/*!
 * jQuery JavaScript Library v1.3.2
 * http://jquery.com/
 *
 * Copyright (c) 2009 John Resig
 * Dual licensed under the MIT and GPL licenses.
 * http://docs.jquery.com/License
 *
 * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
 * Revision: 6246
 */
(function(){

var 
	// Will speed up references to window, and allows munging its name.
	window = this,
	// Will speed up references to undefined, and allows munging its name.
	undefined,
	// Map over jQuery in case of overwrite
	_jQuery = window.jQuery,
	// Map over the $ in case of overwrite
	_$ = window.$,

	jQuery = window.jQuery = window.$ = function( selector, context ) {
		// The jQuery object is actually just the init constructor 'enhanced'
		return new jQuery.fn.init( selector, context );
	},

	// A simple way to check for HTML strings or ID strings
	// (both of which we optimize for)
	quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,
	// Is it a simple selector
	isSimple = /^.[^:#\[\.,]*$/;

jQuery.fn = jQuery.prototype = {
	init: function( selector, context ) {
		// Make sure that a selection was provided
		selector = selector || document;

		// Handle $(DOMElement)
		if ( selector.nodeType ) {
			this[0] = selector;
			this.length = 1;
			this.context = selector;
			return this;
		}
		// Handle HTML strings
		if ( typeof selector === "string" ) {
			// Are we dealing with HTML string or an ID?
			var match = quickExpr.exec( selector );

			// Verify a match, and that no context was specified for #id
			if ( match && (match[1] || !context) ) {

				// HANDLE: $(html) -> $(array)
				if ( match[1] )
					selector = jQuery.clean( [ match[1] ], context );

				// HANDLE: $("#id")
				else {
					var elem = document.getElementById( match[3] );

					// Handle the case where IE and Opera return items
					// by name instead of ID
					if ( elem && elem.id != match[3] )
						return jQuery().find( selector );

					// Otherwise, we inject the element directly into the jQuery object
					var ret = jQuery( elem || [] );
					ret.context = document;
					ret.selector = selector;
					return ret;
				}

			// HANDLE: $(expr, [context])
			// (which is just equivalent to: $(content).find(expr)
			} else
				return jQuery( context ).find( selector );

		// HANDLE: $(function)
		// Shortcut for document ready
		} else if ( jQuery.isFunction( selector ) )
			return jQuery( document ).ready( selector );

		// Make sure that old selector state is passed along
		if ( selector.selector && selector.context ) {
			this.selector = selector.selector;
			this.context = selector.context;
		}

		return this.setArray(jQuery.isArray( selector ) ?
			selector :
			jQuery.makeArray(selector));
	},

	// Start with an empty selector
	selector: "",

	// The current version of jQuery being used
	jquery: "1.3.2",

	// The number of elements contained in the matched element set
	size: function() {
		return this.length;
	},

	// Get the Nth element in the matched element set OR
	// Get the whole matched element set as a clean array
	get: function( num ) {
		return num === undefined ?

			// Return a 'clean' array
			Array.prototype.slice.call( this ) :

			// Return just the object
			this[ num ];
	},

	// Take an array of elements and push it onto the stack
	// (returning the new matched element set)
	pushStack: function( elems, name, selector ) {
		// Build a new jQuery matched element set
		var ret = jQuery( elems );

		// Add the old object onto the stack (as a reference)
		ret.prevObject = this;

		ret.context = this.context;

		if ( name === "find" )
			ret.selector = this.selector + (this.selector ? " " : "") + selector;
		else if ( name )
			ret.selector = this.selector + "." + name + "(" + selector + ")";

		// Return the newly-formed element set
		return ret;
	},

	// Force the current matched set of elements to become
	// the specified array of elements (destroying the stack in the process)
	// You should use pushStack() in order to do this, but maintain the stack
	setArray: function( elems ) {
		// Resetting the length to 0, then using the native Array push
		// is a super-fast way to populate an object with array-like properties
		this.length = 0;
		Array.prototype.push.apply( this, elems );

		return this;
	},

	// Execute a callback for every element in the matched set.
	// (You can seed the arguments with an array of args, but this is
	// only used internally.)
	each: function( callback, args ) {
		return jQuery.each( this, callback, args );
	},

	// Determine the position of an element within
	// the matched set of elements
	index: function( elem ) {
		// Locate the position of the desired element
		return jQuery.inArray(
			// If it receives a jQuery object, the first element is used
			elem && elem.jquery ? elem[0] : elem
		, this );
	},

	attr: function( name, value, type ) {
		var options = name;

		// Look for the case where we're accessing a style value
		if ( typeof name === "string" )
			if ( value === undefined )
				return this[0] && jQuery[ type || "attr" ]( this[0], name );

			else {
				options = {};
				options[ name ] = value;
			}

		// Check to see if we're setting style values
		return this.each(function(i){
			// Set all the styles
			for ( name in options )
				jQuery.attr(
					type ?
						this.style :
						this,
					name, jQuery.prop( this, options[ name ], type, i, name )
				);
		});
	},

	css: function( key, value ) {
		// ignore negative width and height values
		if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
			value = undefined;
		return this.attr( key, value, "curCSS" );
	},

	text: function( text ) {
		if ( typeof text !== "object" && text != null )
			return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );

		var ret = "";

		jQuery.each( text || this, function(){
			jQuery.each( this.childNodes, function(){
				if ( this.nodeType != 8 )
					ret += this.nodeType != 1 ?
						this.nodeValue :
						jQuery.fn.text( [ this ] );
			});
		});

		return ret;
	},

	wrapAll: function( html ) {
		if ( this[0] ) {
			// The elements to wrap the target around
			var wrap = jQuery( html, this[0].ownerDocument ).clone();

			if ( this[0].parentNode )
				wrap.insertBefore( this[0] );

			wrap.map(function(){
				var elem = this;

				while ( elem.firstChild )
					elem = elem.firstChild;

				return elem;
			}).append(this);
		}

		return this;
	},

	wrapInner: function( html ) {
		return this.each(function(){
			jQuery( this ).contents().wrapAll( html );
		});
	},

	wrap: function( html ) {
		return this.each(function(){
			jQuery( this ).wrapAll( html );
		});
	},

	append: function() {
		return this.domManip(arguments, true, function(elem){
			if (this.nodeType == 1)
				this.appendChild( elem );
		});
	},

	prepend: function() {
		return this.domManip(arguments, true, function(elem){
			if (this.nodeType == 1)
				this.insertBefore( elem, this.firstChild );
		});
	},

	before: function() {
		return this.domManip(arguments, false, function(elem){
			this.parentNode.insertBefore( elem, this );
		});
	},

	after: function() {
		return this.domManip(arguments, false, function(elem){
			this.parentNode.insertBefore( elem, this.nextSibling );
		});
	},

	end: function() {
		return this.prevObject || jQuery( [] );
	},

	// For internal use only.
	// Behaves like an Array's method, not like a jQuery method.
	push: [].push,
	sort: [].sort,
	splice: [].splice,

	find: function( selector ) {
		if ( this.length === 1 ) {
			var ret = this.pushStack( [], "find", selector );
			ret.length = 0;
			jQuery.find( selector, this[0], ret );
			return ret;
		} else {
			return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){
				return jQuery.find( selector, elem );
			})), "find", selector );
		}
	},

	clone: function( events ) {
		// Do the clone
		var ret = this.map(function(){
			if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
				// IE copies events bound via attachEvent when
				// using cloneNode. Calling detachEvent on the
				// clone will also remove the events from the orignal
				// In order to get around this, we use innerHTML.
				// Unfortunately, this means some modifications to
				// attributes in IE that are actually only stored
				// as properties will not be copied (such as the
				// the name attribute on an input).
				var html = this.outerHTML;
				if ( !html ) {
					var div = this.ownerDocument.createElement("div");
					div.appendChild( this.cloneNode(true) );
					html = div.innerHTML;
				}

				return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0];
			} else
				return this.cloneNode(true);
		});

		// Copy the events from the original to the clone
		if ( events === true ) {
			var orig = this.find("*").andSelf(), i = 0;

			ret.find("*").andSelf().each(function(){
				if ( this.nodeName !== orig[i].nodeName )
					return;

				var events = jQuery.data( orig[i], "events" );

				for ( var type in events ) {
					for ( var handler in events[ type ] ) {
						jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
					}
				}

				i++;
			});
		}

		// Return the cloned set
		return ret;
	},

	filter: function( selector ) {
		return this.pushStack(
			jQuery.isFunction( selector ) &&
			jQuery.grep(this, function(elem, i){
				return selector.call( elem, i );
			}) ||

			jQuery.multiFilter( selector, jQuery.grep(this, function(elem){
				return elem.nodeType === 1;
			}) ), "filter", selector );
	},

	closest: function( selector ) {
		var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null,
			closer = 0;

		return this.map(function(){
			var cur = this;
			while ( cur && cur.ownerDocument ) {
				if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) {
					jQuery.data(cur, "closest", closer);
					return cur;
				}
				cur = cur.parentNode;
				closer++;
			}
		});
	},

	not: function( selector ) {
		if ( typeof selector === "string" )
			// test special case where just one selector is passed in
			if ( isSimple.test( selector ) )
				return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector );
			else
				selector = jQuery.multiFilter( selector, this );

		var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
		return this.filter(function() {
			return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
		});
	},

	add: function( selector ) {
		return this.pushStack( jQuery.unique( jQuery.merge(
			this.get(),
			typeof selector === "string" ?
				jQuery( selector ) :
				jQuery.makeArray( selector )
		)));
	},

	is: function( selector ) {
		return !!selector && jQuery.multiFilter( selector, this ).length > 0;
	},

	hasClass: function( selector ) {
		return !!selector && this.is( "." + selector );
	},

	val: function( value ) {
		if ( value === undefined ) {			
			var elem = this[0];

			if ( elem ) {
				if( jQuery.nodeName( elem, 'option' ) )
					return (elem.attributes.value || {}).specified ? elem.value : elem.text;
				
				// We need to handle select boxes special
				if ( jQuery.nodeName( elem, "select" ) ) {
					var index = elem.selectedIndex,
						values = [],
						options = elem.options,
						one = elem.type == "select-one";

					// Nothing was selected
					if ( index < 0 )
						return null;

					// Loop through all the selected options
					for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
						var option = options[ i ];

						if ( option.selected ) {
							// Get the specifc value for the option
							value = jQuery(option).val();

							// We don't need an array for one selects
							if ( one )
								return value;

							// Multi-Selects return an array
							values.push( value );
						}
					}

					return values;				
				}

				// Everything else, we just grab the value
				return (elem.value || "").replace(/\r/g, "");

			}

			return undefined;
		}

		if ( typeof value === "number" )
			value += '';

		return this.each(function(){
			if ( this.nodeType != 1 )
				return;

			if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) )
				this.checked = (jQuery.inArray(this.value, value) >= 0 ||
					jQuery.inArray(this.name, value) >= 0);

			else if ( jQuery.nodeName( this, "select" ) ) {
				var values = jQuery.makeArray(value);

				jQuery( "option", this ).each(function(){
					this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
						jQuery.inArray( this.text, values ) >= 0);
				});

				if ( !values.length )
					this.selectedIndex = -1;

			} else
				this.value = value;
		});
	},

	html: function( value ) {
		return value === undefined ?
			(this[0] ?
				this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") :
				null) :
			this.empty().append( value );
	},

	replaceWith: function( value ) {
		return this.after( value ).remove();
	},

	eq: function( i ) {
		return this.slice( i, +i + 1 );
	},

	slice: function() {
		return this.pushStack( Array.prototype.slice.apply( this, arguments ),
			"slice", Array.prototype.slice.call(arguments).join(",") );
	},

	map: function( callback ) {
		return this.pushStack( jQuery.map(this, function(elem, i){
			return callback.call( elem, i, elem );
		}));
	},

	andSelf: function() {
		return this.add( this.prevObject );
	},

	domManip: function( args, table, callback ) {
		if ( this[0] ) {
			var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(),
				scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ),
				first = fragment.firstChild;

			if ( first )
				for ( var i = 0, l = this.length; i < l; i++ )
					callback.call( root(this[i], first), this.length > 1 || i > 0 ?
							fragment.cloneNode(true) : fragment );
		
			if ( scripts )
				jQuery.each( scripts, evalScript );
		}

		return this;
		
		function root( elem, cur ) {
			return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ?
				(elem.getElementsByTagName("tbody")[0] ||
				elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
				elem;
		}
	}
};

// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;

function evalScript( i, elem ) {
	if ( elem.src )
		jQuery.ajax({
			url: elem.src,
			async: false,
			dataType: "script"
		});

	else
		jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );

	if ( elem.parentNode )
		elem.parentNode.removeChild( elem );
}

function now(){
	return +new Date;
}

jQuery.extend = jQuery.fn.extend = function() {
	// copy reference to target object
	var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;

	// Handle a deep copy situation
	if ( typeof target === "boolean" ) {
		deep = target;
		target = arguments[1] || {};
		// skip the boolean and the target
		i = 2;
	}

	// Handle case when target is a string or something (possible in deep copy)
	if ( typeof target !== "object" && !jQuery.isFunction(target) )
		target = {};

	// extend jQuery itself if only one argument is passed
	if ( length == i ) {
		target = this;
		--i;
	}

	for ( ; i < length; i++ )
		// Only deal with non-null/undefined values
		if ( (options = arguments[ i ]) != null )
			// Extend the base object
			for ( var name in options ) {
				var src = target[ name ], copy = options[ name ];

				// Prevent never-ending loop
				if ( target === copy )
					continue;

				// Recurse if we're merging object values
				if ( deep && copy && typeof copy === "object" && !copy.nodeType )
					target[ name ] = jQuery.extend( deep, 
						// Never move original objects, clone them
						src || ( copy.length != null ? [ ] : { } )
					, copy );

				// Don't bring in undefined values
				else if ( copy !== undefined )
					target[ name ] = copy;

			}

	// Return the modified object
	return target;
};

// exclude the following css properties to add px
var	exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
	// cache defaultView
	defaultView = document.defaultView || {},
	toString = Object.prototype.toString;

jQuery.extend({
	noConflict: function( deep ) {
		window.$ = _$;

		if ( deep )
			window.jQuery = _jQuery;

		return jQuery;
	},

	// See test/unit/core.js for details concerning isFunction.
	// Since version 1.3, DOM methods and functions like alert
	// aren't supported. They return false on IE (#2968).
	isFunction: function( obj ) {
		return toString.call(obj) === "[object Function]";
	},

	isArray: function( obj ) {
		return toString.call(obj) === "[object Array]";
	},

	// check if an element is in a (or is an) XML document
	isXMLDoc: function( elem ) {
		return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
			!!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument );
	},

	// Evalulates a script in a global context
	globalEval: function( data ) {
		if ( data && /\S/.test(data) ) {
			// Inspired by code by Andrea Giammarchi
			// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
			var head = document.getElementsByTagName("head")[0] || document.documentElement,
				script = document.createElement("script");

			script.type = "text/javascript";
			if ( jQuery.support.scriptEval )
				script.appendChild( document.createTextNode( data ) );
			else
				script.text = data;

			// Use insertBefore instead of appendChild  to circumvent an IE6 bug.
			// This arises when a base node is used (#2709).
			head.insertBefore( script, head.firstChild );
			head.removeChild( script );
		}
	},

	nodeName: function( elem, name ) {
		return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
	},

	// args is for internal usage only
	each: function( object, callback, args ) {
		var name, i = 0, length = object.length;

		if ( args ) {
			if ( length === undefined ) {
				for ( name in object )
					if ( callback.apply( object[ name ], args ) === false )
						break;
			} else
				for ( ; i < length; )
					if ( callback.apply( object[ i++ ], args ) === false )
						break;

		// A special, fast, case for the most common use of each
		} else {
			if ( length === undefined ) {
				for ( name in object )
					if ( callback.call( object[ name ], name, object[ name ] ) === false )
						break;
			} else
				for ( var value = object[0];
					i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
		}

		return object;
	},

	prop: function( elem, value, type, i, name ) {
		// Handle executable functions
		if ( jQuery.isFunction( value ) )
			value = value.call( elem, i );

		// Handle passing in a number to a CSS property
		return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ?
			value + "px" :
			value;
	},

	className: {
		// internal only, use addClass("class")
		add: function( elem, classNames ) {
			jQuery.each((classNames || "").split(/\s+/), function(i, className){
				if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
					elem.className += (elem.className ? " " : "") + className;
			});
		},

		// internal only, use removeClass("class")
		remove: function( elem, classNames ) {
			if (elem.nodeType == 1)
				elem.className = classNames !== undefined ?
					jQuery.grep(elem.className.split(/\s+/), function(className){
						return !jQuery.className.has( classNames, className );
					}).join(" ") :
					"";
		},

		// internal only, use hasClass("class")
		has: function( elem, className ) {
			return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
		}
	},

	// A method for quickly swapping in/out CSS properties to get correct calculations
	swap: function( elem, options, callback ) {
		var old = {};
		// Remember the old values, and insert the new ones
		for ( var name in options ) {
			old[ name ] = elem.style[ name ];
			elem.style[ name ] = options[ name ];
		}

		callback.call( elem );

		// Revert the old values
		for ( var name in options )
			elem.style[ name ] = old[ name ];
	},

	css: function( elem, name, force, extra ) {
		if ( name == "width" || name == "height" ) {
			var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];

			function getWH() {
				val = name == "width" ? elem.offsetWidth : elem.offsetHeight;

				if ( extra === "border" )
					return;

				jQuery.each( which, function() {
					if ( !extra )
						val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
					if ( extra === "margin" )
						val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
					else
						val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
				});
			}

			if ( elem.offsetWidth !== 0 )
				getWH();
			else
				jQuery.swap( elem, props, getWH );

			return Math.max(0, Math.round(val));
		}

		return jQuery.curCSS( elem, name, force );
	},

	curCSS: function( elem, name, force ) {
		var ret, style = elem.style;

		// We need to handle opacity special in IE
		if ( name == "opacity" && !jQuery.support.opacity ) {
			ret = jQuery.attr( style, "opacity" );

			return ret == "" ?
				"1" :
				ret;
		}

		// Make sure we're using the right name for getting the float value
		if ( name.match( /float/i ) )
			name = styleFloat;

		if ( !force && style && style[ name ] )
			ret = style[ name ];

		else if ( defaultView.getComputedStyle ) {

			// Only "float" is needed here
			if ( name.match( /float/i ) )
				name = "float";

			name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();

			var computedStyle = defaultView.getComputedStyle( elem, null );

			if ( computedStyle )
				ret = computedStyle.getPropertyValue( name );

			// We should always get a number back from opacity
			if ( name == "opacity" && ret == "" )
				ret = "1";

		} else if ( elem.currentStyle ) {
			var camelCase = name.replace(/\-(\w)/g, function(all, letter){
				return letter.toUpperCase();
			});

			ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];

			// From the awesome hack by Dean Edwards
			// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291

			// If we're not dealing with a regular pixel number
			// but a number that has a weird ending, we need to convert it to pixels
			if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
				// Remember the original values
				var left = style.left, rsLeft = elem.runtimeStyle.left;

				// Put in the new values to get a computed value out
				elem.runtimeStyle.left = elem.currentStyle.left;
				style.left = ret || 0;
				ret = style.pixelLeft + "px";

				// Revert the changed values
				style.left = left;
				elem.runtimeStyle.left = rsLeft;
			}
		}

		return ret;
	},

	clean: function( elems, context, fragment ) {
		context = context || document;

		// !context.createElement fails in IE with an error but returns typeof 'object'
		if ( typeof context.createElement === "undefined" )
			context = context.ownerDocument || context[0] && context[0].ownerDocument || document;

		// If a single string is passed in and it's a single tag
		// just do a createElement and skip the rest
		if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) {
			var match = /^<(\w+)\s*\/?>$/.exec(elems[0]);
			if ( match )
				return [ context.createElement( match[1] ) ];
		}

		var ret = [], scripts = [], div = context.createElement("div");

		jQuery.each(elems, function(i, elem){
			if ( typeof elem === "number" )
				elem += '';

			if ( !elem )
				return;

			// Convert html string into DOM nodes
			if ( typeof elem === "string" ) {
				// Fix "XHTML"-style tags in all browsers
				elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
					return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
						all :
						front + "></" + tag + ">";
				});

				// Trim whitespace, otherwise indexOf won't work as expected
				var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase();

				var wrap =
					// option or optgroup
					!tags.indexOf("<opt") &&
					[ 1, "<select multiple='multiple'>", "</select>" ] ||

					!tags.indexOf("<leg") &&
					[ 1, "<fieldset>", "</fieldset>" ] ||

					tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
					[ 1, "<table>", "</table>" ] ||

					!tags.indexOf("<tr") &&
					[ 2, "<table><tbody>", "</tbody></table>" ] ||

				 	// <thead> matched above
					(!tags.indexOf("<td") || !tags.indexOf("<th")) &&
					[ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||

					!tags.indexOf("<col") &&
					[ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||

					// IE can't serialize <link> and <script> tags normally
					!jQuery.support.htmlSerialize &&
					[ 1, "div<div>", "</div>" ] ||

					[ 0, "", "" ];

				// Go to html and back, then peel off extra wrappers
				div.innerHTML = wrap[1] + elem + wrap[2];

				// Move to the right depth
				while ( wrap[0]-- )
					div = div.lastChild;

				// Remove IE's autoinserted <tbody> from table fragments
				if ( !jQuery.support.tbody ) {

					// String was a <table>, *may* have spurious <tbody>
					var hasBody = /<tbody/i.test(elem),
						tbody = !tags.indexOf("<table") && !hasBody ?
							div.firstChild && div.firstChild.childNodes :

						// String was a bare <thead> or <tfoot>
						wrap[1] == "<table>" && !hasBody ?
							div.childNodes :
							[];

					for ( var j = tbody.length - 1; j >= 0 ; --j )
						if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
							tbody[ j ].parentNode.removeChild( tbody[ j ] );

					}

				// IE completely kills leading whitespace when innerHTML is used
				if ( !jQuery.support.leadingWhitespace && /^\s/.test( elem ) )
					div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );
				
				elem = jQuery.makeArray( div.childNodes );
			}

			if ( elem.nodeType )
				ret.push( elem );
			else
				ret = jQuery.merge( ret, elem );

		});

		if ( fragment ) {
			for ( var i = 0; ret[i]; i++ ) {
				if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
					scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
				} else {
					if ( ret[i].nodeType === 1 )
						ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
					fragment.appendChild( ret[i] );
				}
			}
			
			return scripts;
		}

		return ret;
	},

	attr: function( elem, name, value ) {
		// don't set attributes on text and comment nodes
		if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
			return undefined;

		var notxml = !jQuery.isXMLDoc( elem ),
			// Whether we are setting (or getting)
			set = value !== undefined;

		// Try to normalize/fix the name
		name = notxml && jQuery.props[ name ] || name;

		// Only do all the following if this is a node (faster for style)
		// IE elem.getAttribute passes even for style
		if ( elem.tagName ) {

			// These attributes require special treatment
			var special = /href|src|style/.test( name );

			// Safari mis-reports the default selected property of a hidden option
			// Accessing the parent's selectedIndex property fixes it
			if ( name == "selected" && elem.parentNode )
				elem.parentNode.selectedIndex;

			// If applicable, access the attribute via the DOM 0 way
			if ( name in elem && notxml && !special ) {
				if ( set ){
					// We can't allow the type property to be changed (since it causes problems in IE)
					if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
						throw "type property can't be changed";

					elem[ name ] = value;
				}

				// browsers index elements by id/name on forms, give priority to attributes.
				if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) )
					return elem.getAttributeNode( name ).nodeValue;

				// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
				// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
				if ( name == "tabIndex" ) {
					var attributeNode = elem.getAttributeNode( "tabIndex" );
					return attributeNode && attributeNode.specified
						? attributeNode.value
						: elem.nodeName.match(/(button|input|object|select|textarea)/i)
							? 0
							: elem.nodeName.match(/^(a|area)$/i) && elem.href
								? 0
								: undefined;
				}

				return elem[ name ];
			}

			if ( !jQuery.support.style && notxml &&  name == "style" )
				return jQuery.attr( elem.style, "cssText", value );

			if ( set )
				// convert the value to a string (all browsers do this but IE) see #1070
				elem.setAttribute( name, "" + value );

			var attr = !jQuery.support.hrefNormalized && notxml && special
					// Some attributes require a special call on IE
					? elem.getAttribute( name, 2 )
					: elem.getAttribute( name );

			// Non-existent attributes return null, we normalize to undefined
			return attr === null ? undefined : attr;
		}

		// elem is actually elem.style ... set the style

		// IE uses filters for opacity
		if ( !jQuery.support.opacity && name == "opacity" ) {
			if ( set ) {
				// IE has trouble with opacity if it does not have layout
				// Force it by setting the zoom level
				elem.zoom = 1;

				// Set the alpha filter to set the opacity
				elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
					(parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
			}

			return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
				(parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
				"";
		}

		name = name.replace(/-([a-z])/ig, function(all, letter){
			return letter.toUpperCase();
		});

		if ( set )
			elem[ name ] = value;

		return elem[ name ];
	},

	trim: function( text ) {
		return (text || "").replace( /^\s+|\s+$/g, "" );
	},

	makeArray: function( array ) {
		var ret = [];

		if( array != null ){
			var i = array.length;
			// The window, strings (and functions) also have 'length'
			if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )
				ret[0] = array;
			else
				while( i )
					ret[--i] = array[i];
		}

		return ret;
	},

	inArray: function( elem, array ) {
		for ( var i = 0, length = array.length; i < length; i++ )
		// Use === because on IE, window == document
			if ( array[ i ] === elem )
				return i;

		return -1;
	},

	merge: function( first, second ) {
		// We have to loop this way because IE & Opera overwrite the length
		// expando of getElementsByTagName
		var i = 0, elem, pos = first.length;
		// Also, we need to make sure that the correct elements are being returned
		// (IE returns comment nodes in a '*' query)
		if ( !jQuery.support.getAll ) {
			while ( (elem = second[ i++ ]) != null )
				if ( elem.nodeType != 8 )
					first[ pos++ ] = elem;

		} else
			while ( (elem = second[ i++ ]) != null )
				first[ pos++ ] = elem;

		return first;
	},

	unique: function( array ) {
		var ret = [], done = {};

		try {

			for ( var i = 0, length = array.length; i < length; i++ ) {
				var id = jQuery.data( array[ i ] );

				if ( !done[ id ] ) {
					done[ id ] = true;
					ret.push( array[ i ] );
				}
			}

		} catch( e ) {
			ret = array;
		}

		return ret;
	},

	grep: function( elems, callback, inv ) {
		var ret = [];

		// Go through the array, only saving the items
		// that pass the validator function
		for ( var i = 0, length = elems.length; i < length; i++ )
			if ( !inv != !callback( elems[ i ], i ) )
				ret.push( elems[ i ] );

		return ret;
	},

	map: function( elems, callback ) {
		var ret = [];

		// Go through the array, translating each of the items to their
		// new value (or values).
		for ( var i = 0, length = elems.length; i < length; i++ ) {
			var value = callback( elems[ i ], i );

			if ( value != null )
				ret[ ret.length ] = value;
		}

		return ret.concat.apply( [], ret );
	}
});

// Use of jQuery.browser is deprecated.
// It's included for backwards compatibility and plugins,
// although they should work to migrate away.

var userAgent = navigator.userAgent.toLowerCase();

// Figure out what browser is being used
jQuery.browser = {
	version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
	safari: /webkit/.test( userAgent ),
	opera: /opera/.test( userAgent ),
	msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
	mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
};

jQuery.each({
	parent: function(elem){return elem.parentNode;},
	parents: function(elem){return jQuery.dir(elem,"parentNode");},
	next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
	prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
	nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
	prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
	siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
	children: function(elem){return jQuery.sibling(elem.firstChild);},
	contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
}, function(name, fn){
	jQuery.fn[ name ] = function( selector ) {
		var ret = jQuery.map( this, fn );

		if ( selector && typeof selector == "string" )
			ret = jQuery.multiFilter( selector, ret );

		return this.pushStack( jQuery.unique( ret ), name, selector );
	};
});

jQuery.each({
	appendTo: "append",
	prependTo: "prepend",
	insertBefore: "before",
	insertAfter: "after",
	replaceAll: "replaceWith"
}, function(name, original){
	jQuery.fn[ name ] = function( selector ) {
		var ret = [], insert = jQuery( selector );

		for ( var i = 0, l = insert.length; i < l; i++ ) {
			var elems = (i > 0 ? this.clone(true) : this).get();
			jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
			ret = ret.concat( elems );
		}

		return this.pushStack( ret, name, selector );
	};
});

jQuery.each({
	removeAttr: function( name ) {
		jQuery.attr( this, name, "" );
		if (this.nodeType == 1)
			this.removeAttribute( name );
	},

	addClass: function( classNames ) {
		jQuery.className.add( this, classNames );
	},

	removeClass: function( classNames ) {
		jQuery.className.remove( this, classNames );
	},

	toggleClass: function( classNames, state ) {
		if( typeof state !== "boolean" )
			state = !jQuery.className.has( this, classNames );
		jQuery.className[ state ? "add" : "remove" ]( this, classNames );
	},

	remove: function( selector ) {
		if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
			// Prevent memory leaks
			jQuery( "*", this ).add([this]).each(function(){
				jQuery.event.remove(this);
				jQuery.removeData(this);
			});
			if (this.parentNode)
				this.parentNode.removeChild( this );
		}
	},

	empty: function() {
		// Remove element nodes and prevent memory leaks
		jQuery(this).children().remove();

		// Remove any remaining nodes
		while ( this.firstChild )
			this.removeChild( this.firstChild );
	}
}, function(name, fn){
	jQuery.fn[ name ] = function(){
		return this.each( fn, arguments );
	};
});

// Helper function used by the dimensions and offset modules
function num(elem, prop) {
	return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
}
var expando = "jQuery" + now(), uuid = 0, windowData = {};

jQuery.extend({
	cache: {},

	data: function( elem, name, data ) {
		elem = elem == window ?
			windowData :
			elem;

		var id = elem[ expando ];

		// Compute a unique ID for the element
		if ( !id )
			id = elem[ expando ] = ++uuid;

		// Only generate the data cache if we're
		// trying to access or manipulate it
		if ( name && !jQuery.cache[ id ] )
			jQuery.cache[ id ] = {};

		// Prevent overriding the named cache with undefined values
		if ( data !== undefined )
			jQuery.cache[ id ][ name ] = data;

		// Return the named cache data, or the ID for the element
		return name ?
			jQuery.cache[ id ][ name ] :
			id;
	},

	removeData: function( elem, name ) {
		elem = elem == window ?
			windowData :
			elem;

		var id = elem[ expando ];

		// If we want to remove a specific section of the element's data
		if ( name ) {
			if ( jQuery.cache[ id ] ) {
				// Remove the section of cache data
				delete jQuery.cache[ id ][ name ];

				// If we've removed all the data, remove the element's cache
				name = "";

				for ( name in jQuery.cache[ id ] )
					break;

				if ( !name )
					jQuery.removeData( elem );
			}

		// Otherwise, we want to remove all of the element's data
		} else {
			// Clean up the element expando
			try {
				delete elem[ expando ];
			} catch(e){
				// IE has trouble directly removing the expando
				// but it's ok with using removeAttribute
				if ( elem.removeAttribute )
					elem.removeAttribute( expando );
			}

			// Completely remove the data cache
			delete jQuery.cache[ id ];
		}
	},
	queue: function( elem, type, data ) {
		if ( elem ){
	
			type = (type || "fx") + "queue";
	
			var q = jQuery.data( elem, type );
	
			if ( !q || jQuery.isArray(data) )
				q = jQuery.data( elem, type, jQuery.makeArray(data) );
			else if( data )
				q.push( data );
	
		}
		return q;
	},

	dequeue: function( elem, type ){
		var queue = jQuery.queue( elem, type ),
			fn = queue.shift();
		
		if( !type || type === "fx" )
			fn = queue[0];
			
		if( fn !== undefined )
			fn.call(elem);
	}
});

jQuery.fn.extend({
	data: function( key, value ){
		var parts = key.split(".");
		parts[1] = parts[1] ? "." + parts[1] : "";

		if ( value === undefined ) {
			var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);

			if ( data === undefined && this.length )
				data = jQuery.data( this[0], key );

			return data === undefined && parts[1] ?
				this.data( parts[0] ) :
				data;
		} else
			return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
				jQuery.data( this, key, value );
			});
	},

	removeData: function( key ){
		return this.each(function(){
			jQuery.removeData( this, key );
		});
	},
	queue: function(type, data){
		if ( typeof type !== "string" ) {
			data = type;
			type = "fx";
		}

		if ( data === undefined )
			return jQuery.queue( this[0], type );

		return this.each(function(){
			var queue = jQuery.queue( this, type, data );
			
			 if( type == "fx" && queue.length == 1 )
				queue[0].call(this);
		});
	},
	dequeue: function(type){
		return this.each(function(){
			jQuery.dequeue( this, type );
		});
	}
});/*!
 * Sizzle CSS Selector Engine - v0.9.3
 *  Copyright 2009, The Dojo Foundation
 *  Released under the MIT, BSD, and GPL Licenses.
 *  More information: http://sizzlejs.com/
 */
(function(){

var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
	done = 0,
	toString = Object.prototype.toString;

var Sizzle = function(selector, context, results, seed) {
	results = results || [];
	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;
	
	// 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 {
		var ret = seed ?
			{ expr: parts.pop(), set: makeArray(seed) } :
			Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
		set = Sizzle.filter( ret.expr, 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, isXML(context) );
		}
	}

	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.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, context, results, seed );

		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);
					}
				}
			}
		}
	}

	return results;
};

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 ] = 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("form"),
		id = "script" + (new Date).getTime();
	form.innerHTML = "<input 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);
	};

	Sizzle.find = oldSizzle.find;
	Sizzle.filter = oldSizzle.filter;
	Sizzle.selectors = oldSizzle.selectors;
	Sizzle.matches = oldSizzle.matches;
})();

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 && isXML( elem.ownerDocument );
};

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
jQuery.find = Sizzle;
jQuery.filter = Sizzle.filter;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.filters;

Sizzle.selectors.filters.hidden = function(elem){
	return elem.offsetWidth === 0 || elem.offsetHeight === 0;
};

Sizzle.selectors.filters.visible = function(elem){
	return elem.offsetWidth > 0 || elem.offsetHeight > 0;
};

Sizzle.selectors.filters.animated = function(elem){
	return jQuery.grep(jQuery.timers, function(fn){
		return elem === fn.elem;
	}).length;
};

jQuery.multiFilter = function( expr, elems, not ) {
	if ( not ) {
		expr = ":not(" + expr + ")";
	}

	return Sizzle.matches(expr, elems);
};

jQuery.dir = function( elem, dir ){
	var matched = [], cur = elem[dir];
	while ( cur && cur != document ) {
		if ( cur.nodeType == 1 )
			matched.push( cur );
		cur = cur[dir];
	}
	return matched;
};

jQuery.nth = function(cur, result, dir, elem){
	result = result || 1;
	var num = 0;

	for ( ; cur; cur = cur[dir] )
		if ( cur.nodeType == 1 && ++num == result )
			break;

	return cur;
};

jQuery.sibling = function(n, elem){
	var r = [];

	for ( ; n; n = n.nextSibling ) {
		if ( n.nodeType == 1 && n != elem )
			r.push( n );
	}

	return r;
};

return;

window.Sizzle = Sizzle;

})();
/*
 * A number of helper functions used for managing events.
 * Many of the ideas behind this code originated from
 * Dean Edwards' addEvent library.
 */
jQuery.event = {

	// Bind an event to an element
	// Original by Dean Edwards
	add: function(elem, types, handler, data) {
		if ( elem.nodeType == 3 || elem.nodeType == 8 )
			return;

		// For whatever reason, IE has trouble passing the window object
		// around, causing it to be cloned in the process
		if ( elem.setInterval && elem != window )
			elem = window;

		// Make sure that the function being executed has a unique ID
		if ( !handler.guid )
			handler.guid = this.guid++;

		// if data is passed, bind to handler
		if ( data !== undefined ) {
			// Create temporary function pointer to original handler
			var fn = handler;

			// Create unique handler function, wrapped around original handler
			handler = this.proxy( fn );

			// Store data in unique handler
			handler.data = data;
		}

		// Init the element's event structure
		var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
			handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
				// Handle the second event of a trigger and when
				// an event is called after a page has unloaded
				return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
					jQuery.event.handle.apply(arguments.callee.elem, arguments) :
					undefined;
			});
		// Add elem as a property of the handle function
		// This is to prevent a memory leak with non-native
		// event in IE.
		handle.elem = elem;

		// Handle multiple events separated by a space
		// jQuery(...).bind("mouseover mouseout", fn);
		jQuery.each(types.split(/\s+/), function(index, type) {
			// Namespaced event handlers
			var namespaces = type.split(".");
			type = namespaces.shift();
			handler.type = namespaces.slice().sort().join(".");

			// Get the current list of functions bound to this event
			var handlers = events[type];
			
			if ( jQuery.event.specialAll[type] )
				jQuery.event.specialAll[type].setup.call(elem, data, namespaces);

			// Init the event handler queue
			if (!handlers) {
				handlers = events[type] = {};

				// Check for a special event handler
				// Only use addEventListener/attachEvent if the special
				// events handler returns false
				if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem, data, namespaces) === false ) {
					// Bind the global event handler to the element
					if (elem.addEventListener)
						elem.addEventListener(type, handle, false);
					else if (elem.attachEvent)
						elem.attachEvent("on" + type, handle);
				}
			}

			// Add the function to the element's handler list
			handlers[handler.guid] = handler;

			// Keep track of which events have been used, for global triggering
			jQuery.event.global[type] = true;
		});

		// Nullify elem to prevent memory leaks in IE
		elem = null;
	},

	guid: 1,
	global: {},

	// Detach an event or set of events from an element
	remove: function(elem, types, handler) {
		// don't do events on text and comment nodes
		if ( elem.nodeType == 3 || elem.nodeType == 8 )
			return;

		var events = jQuery.data(elem, "events"), ret, index;

		if ( events ) {
			// Unbind all events for the element
			if ( types === undefined || (typeof types === "string" && types.charAt(0) == ".") )
				for ( var type in events )
					this.remove( elem, type + (types || "") );
			else {
				// types is actually an event object here
				if ( types.type ) {
					handler = types.handler;
					types = types.type;
				}

				// Handle multiple events seperated by a space
				// jQuery(...).unbind("mouseover mouseout", fn);
				jQuery.each(types.split(/\s+/), function(index, type){
					// Namespaced event handlers
					var namespaces = type.split(".");
					type = namespaces.shift();
					var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");

					if ( events[type] ) {
						// remove the given handler for the given type
						if ( handler )
							delete events[type][handler.guid];

						// remove all handlers for the given type
						else
							for ( var handle in events[type] )
								// Handle the removal of namespaced events
								if ( namespace.test(events[type][handle].type) )
									delete events[type][handle];
									
						if ( jQuery.event.specialAll[type] )
							jQuery.event.specialAll[type].teardown.call(elem, namespaces);

						// remove generic event handler if no more handlers exist
						for ( ret in events[type] ) break;
						if ( !ret ) {
							if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem, namespaces) === false ) {
								if (elem.removeEventListener)
									elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
								else if (elem.detachEvent)
									elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
							}
							ret = null;
							delete events[type];
						}
					}
				});
			}

			// Remove the expando if it's no longer used
			for ( ret in events ) break;
			if ( !ret ) {
				var handle = jQuery.data( elem, "handle" );
				if ( handle ) handle.elem = null;
				jQuery.removeData( elem, "events" );
				jQuery.removeData( elem, "handle" );
			}
		}
	},

	// bubbling is internal
	trigger: function( event, data, elem, bubbling ) {
		// Event object or event type
		var type = event.type || event;

		if( !bubbling ){
			event = typeof event === "object" ?
				// jQuery.Event object
				event[expando] ? event :
				// Object literal
				jQuery.extend( jQuery.Event(type), event ) :
				// Just the event type (string)
				jQuery.Event(type);

			if ( type.indexOf("!") >= 0 ) {
				event.type = type = type.slice(0, -1);
				event.exclusive = true;
			}

			// Handle a global trigger
			if ( !elem ) {
				// Don't bubble custom events when global (to avoid too much overhead)
				event.stopPropagation();
				// Only trigger if we've ever bound an event for it
				if ( this.global[type] )
					jQuery.each( jQuery.cache, function(){
						if ( this.events && this.events[type] )
							jQuery.event.trigger( event, data, this.handle.elem );
					});
			}

			// Handle triggering a single element

			// don't do events on text and comment nodes
			if ( !elem || elem.nodeType == 3 || elem.nodeType == 8 )
				return undefined;
			
			// Clean up in case it is reused
			event.result = undefined;
			event.target = elem;
			
			// Clone the incoming data, if any
			data = jQuery.makeArray(data);
			data.unshift( event );
		}

		event.currentTarget = elem;

		// Trigger the event, it is assumed that "handle" is a function
		var handle = jQuery.data(elem, "handle");
		if ( handle )
			handle.apply( elem, data );

		// Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
		if ( (!elem[type] || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
			event.result = false;

		// Trigger the native events (except for clicks on links)
		if ( !bubbling && elem[type] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
			this.triggered = true;
			try {
				elem[ type ]();
			// prevent IE from throwing an error for some hidden elements
			} catch (e) {}
		}

		this.triggered = false;

		if ( !event.isPropagationStopped() ) {
			var parent = elem.parentNode || elem.ownerDocument;
			if ( parent )
				jQuery.event.trigger(event, data, parent, true);
		}
	},

	handle: function(event) {
		// returned undefined or false
		var all, handlers;

		event = arguments[0] = jQuery.event.fix( event || window.event );
		event.currentTarget = this;
		
		// Namespaced event handlers
		var namespaces = event.type.split(".");
		event.type = namespaces.shift();

		// Cache this now, all = true means, any handler
		all = !namespaces.length && !event.exclusive;
		
		var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");

		handlers = ( jQuery.data(this, "events") || {} )[event.type];

		for ( var j in handlers ) {
			var handler = handlers[j];

			// Filter the functions by class
			if ( all || namespace.test(handler.type) ) {
				// Pass in a reference to the handler function itself
				// So that we can later remove it
				event.handler = handler;
				event.data = handler.data;

				var ret = handler.apply(this, arguments);

				if( ret !== undefined ){
					event.result = ret;
					if ( ret === false ) {
						event.preventDefault();
						event.stopPropagation();
					}
				}

				if( event.isImmediatePropagationStopped() )
					break;

			}
		}
	},

	props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),

	fix: function(event) {
		if ( event[expando] )
			return event;

		// store a copy of the original event object
		// and "clone" to set read-only properties
		var originalEvent = event;
		event = jQuery.Event( originalEvent );

		for ( var i = this.props.length, prop; i; ){
			prop = this.props[ --i ];
			event[ prop ] = originalEvent[ prop ];
		}

		// Fix target property, if necessary
		if ( !event.target )
			event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either

		// check if target is a textnode (safari)
		if ( event.target.nodeType == 3 )
			event.target = event.target.parentNode;

		// Add relatedTarget, if necessary
		if ( !event.relatedTarget && event.fromElement )
			event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;

		// Calculate pageX/Y if missing and clientX/Y available
		if ( event.pageX == null && event.clientX != null ) {
			var doc = document.documentElement, body = document.body;
			event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
			event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
		}

		// Add which for key events
		if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
			event.which = event.charCode || event.keyCode;

		// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
		if ( !event.metaKey && event.ctrlKey )
			event.metaKey = event.ctrlKey;

		// Add which for click: 1 == left; 2 == middle; 3 == right
		// Note: button is not normalized, so don't use it
		if ( !event.which && event.button )
			event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));

		return event;
	},

	proxy: function( fn, proxy ){
		proxy = proxy || function(){ return fn.apply(this, arguments); };
		// Set the guid of unique handler to the same of original handler, so it can be removed
		proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
		// So proxy can be declared as an argument
		return proxy;
	},

	special: {
		ready: {
			// Make sure the ready event is setup
			setup: bindReady,
			teardown: function() {}
		}
	},
	
	specialAll: {
		live: {
			setup: function( selector, namespaces ){
				jQuery.event.add( this, namespaces[0], liveHandler );
			},
			teardown:  function( namespaces ){
				if ( namespaces.length ) {
					var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
					
					jQuery.each( (jQuery.data(this, "events").live || {}), function(){
						if ( name.test(this.type) )
							remove++;
					});
					
					if ( remove < 1 )
						jQuery.event.remove( this, namespaces[0], liveHandler );
				}
			}
		}
	}
};

jQuery.Event = function( src ){
	// Allow instantiation without the 'new' keyword
	if( !this.preventDefault )
		return new jQuery.Event(src);
	
	// Event object
	if( src && src.type ){
		this.originalEvent = src;
		this.type = src.type;
	// Event type
	}else
		this.type = src;

	// timeStamp is buggy for some events on Firefox(#3843)
	// So we won't rely on the native value
	this.timeStamp = now();
	
	// Mark it as fixed
	this[expando] = true;
};

function returnFalse(){
	return false;
}
function returnTrue(){
	return true;
}

// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
jQuery.Event.prototype = {
	preventDefault: function() {
		this.isDefaultPrevented = returnTrue;

		var e = this.originalEvent;
		if( !e )
			return;
		// if preventDefault exists run it on the original event
		if (e.preventDefault)
			e.preventDefault();
		// otherwise set the returnValue property of the original event to false (IE)
		e.returnValue = false;
	},
	stopPropagation: function() {
		this.isPropagationStopped = returnTrue;

		var e = this.originalEvent;
		if( !e )
			return;
		// if stopPropagation exists run it on the original event
		if (e.stopPropagation)
			e.stopPropagation();
		// otherwise set the cancelBubble property of the original event to true (IE)
		e.cancelBubble = true;
	},
	stopImmediatePropagation:function(){
		this.isImmediatePropagationStopped = returnTrue;
		this.stopPropagation();
	},
	isDefaultPrevented: returnFalse,
	isPropagationStopped: returnFalse,
	isImmediatePropagationStopped: returnFalse
};
// Checks if an event happened on an element within another element
// Used in jQuery.event.special.mouseenter and mouseleave handlers
var withinElement = function(event) {
	// Check if mouse(over|out) are still within the same parent element
	var parent = event.relatedTarget;
	// Traverse up the tree
	while ( parent && parent != this )
		try { parent = parent.parentNode; }
		catch(e) { parent = this; }
	
	if( parent != this ){
		// set the correct event type
		event.type = event.data;
		// handle event if we actually just moused on to a non sub-element
		jQuery.event.handle.apply( this, arguments );
	}
};
	
jQuery.each({ 
	mouseover: 'mouseenter', 
	mouseout: 'mouseleave'
}, function( orig, fix ){
	jQuery.event.special[ fix ] = {
		setup: function(){
			jQuery.event.add( this, orig, withinElement, fix );
		},
		teardown: function(){
			jQuery.event.remove( this, orig, withinElement );
		}
	};			   
});

jQuery.fn.extend({
	bind: function( type, data, fn ) {
		return type == "unload" ? this.one(type, data, fn) : this.each(function(){
			jQuery.event.add( this, type, fn || data, fn && data );
		});
	},

	one: function( type, data, fn ) {
		var one = jQuery.event.proxy( fn || data, function(event) {
			jQuery(this).unbind(event, one);
			return (fn || data).apply( this, arguments );
		});
		return this.each(function(){
			jQuery.event.add( this, type, one, fn && data);
		});
	},

	unbind: function( type, fn ) {
		return this.each(function(){
			jQuery.event.remove( this, type, fn );
		});
	},

	trigger: function( type, data ) {
		return this.each(function(){
			jQuery.event.trigger( type, data, this );
		});
	},

	triggerHandler: function( type, data ) {
		if( this[0] ){
			var event = jQuery.Event(type);
			event.preventDefault();
			event.stopPropagation();
			jQuery.event.trigger( event, data, this[0] );
			return event.result;
		}		
	},

	toggle: function( fn ) {
		// Save reference to arguments for access in closure
		var args = arguments, i = 1;

		// link all the functions, so any of them can unbind this click handler
		while( i < args.length )
			jQuery.event.proxy( fn, args[i++] );

		return this.click( jQuery.event.proxy( fn, function(event) {
			// Figure out which function to execute
			this.lastToggle = ( this.lastToggle || 0 ) % i;

			// Make sure that clicks stop
			event.preventDefault();

			// and execute the function
			return args[ this.lastToggle++ ].apply( this, arguments ) || false;
		}));
	},

	hover: function(fnOver, fnOut) {
		return this.mouseenter(fnOver).mouseleave(fnOut);
	},

	ready: function(fn) {
		// Attach the listeners
		bindReady();

		// If the DOM is already ready
		if ( jQuery.isReady )
			// Execute the function immediately
			fn.call( document, jQuery );

		// Otherwise, remember the function for later
		else
			// Add the function to the wait list
			jQuery.readyList.push( fn );

		return this;
	},
	
	live: function( type, fn ){
		var proxy = jQuery.event.proxy( fn );
		proxy.guid += this.selector + type;

		jQuery(document).bind( liveConvert(type, this.selector), this.selector, proxy );

		return this;
	},
	
	die: function( type, fn ){
		jQuery(document).unbind( liveConvert(type, this.selector), fn ? { guid: fn.guid + this.selector + type } : null );
		return this;
	}
});

function liveHandler( event ){
	var check = RegExp("(^|\\.)" + event.type + "(\\.|$)"),
		stop = true,
		elems = [];

	jQuery.each(jQuery.data(this, "events").live || [], function(i, fn){
		if ( check.test(fn.type) ) {
			var elem = jQuery(event.target).closest(fn.data)[0];
			if ( elem )
				elems.push({ elem: elem, fn: fn });
		}
	});

	elems.sort(function(a,b) {
		return jQuery.data(a.elem, "closest") - jQuery.data(b.elem, "closest");
	});
	
	jQuery.each(elems, function(){
		if ( this.fn.call(this.elem, event, this.fn.data) === false )
			return (stop = false);
	});

	return stop;
}

function liveConvert(type, selector){
	return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "|")].join(".");
}

jQuery.extend({
	isReady: false,
	readyList: [],
	// Handle when the DOM is ready
	ready: function() {
		// Make sure that the DOM is not already loaded
		if ( !jQuery.isReady ) {
			// Remember that the DOM is ready
			jQuery.isReady = true;

			// If there are functions bound, to execute
			if ( jQuery.readyList ) {
				// Execute all of them
				jQuery.each( jQuery.readyList, function(){
					this.call( document, jQuery );
				});

				// Reset the list of functions
				jQuery.readyList = null;
			}

			// Trigger any bound ready events
			jQuery(document).triggerHandler("ready");
		}
	}
});

var readyBound = false;

function bindReady(){
	if ( readyBound ) return;
	readyBound = true;

	// Mozilla, Opera and webkit nightlies currently support this event
	if ( document.addEventListener ) {
		// Use the handy event callback
		document.addEventListener( "DOMContentLoaded", function(){
			document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
			jQuery.ready();
		}, false );

	// If IE event model is used
	} else if ( document.attachEvent ) {
		// ensure firing before onload,
		// maybe late but safe also for iframes
		document.attachEvent("onreadystatechange", function(){
			if ( document.readyState === "complete" ) {
				document.detachEvent( "onreadystatechange", arguments.callee );
				jQuery.ready();
			}
		});

		// If IE and not an iframe
		// continually check to see if the document is ready
		if ( document.documentElement.doScroll && window == window.top ) (function(){
			if ( jQuery.isReady ) return;

			try {
				// If IE is used, use the trick by Diego Perini
				// http://javascript.nwbox.com/IEContentLoaded/
				document.documentElement.doScroll("left");
			} catch( error ) {
				setTimeout( arguments.callee, 0 );
				return;
			}

			// and execute any waiting functions
			jQuery.ready();
		})();
	}

	// A fallback to window.onload, that will always work
	jQuery.event.add( window, "load", jQuery.ready );
}

jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
	"mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave," +
	"change,select,submit,keydown,keypress,keyup,error").split(","), function(i, name){

	// Handle event binding
	jQuery.fn[name] = function(fn){
		return fn ? this.bind(name, fn) : this.trigger(name);
	};
});

// Prevent memory leaks in IE
// And prevent errors on refresh with events like mouseover in other browsers
// Window isn't included so as not to unbind existing unload events
jQuery( window ).bind( 'unload', function(){ 
	for ( var id in jQuery.cache )
		// Skip the window
		if ( id != 1 && jQuery.cache[ id ].handle )
			jQuery.event.remove( jQuery.cache[ id ].handle.elem );
}); 
(function(){

	jQuery.support = {};

	var root = document.documentElement,
		script = document.createElement("script"),
		div = document.createElement("div"),
		id = "script" + (new Date).getTime();

	div.style.display = "none";
	div.innerHTML = '   <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';

	var all = div.getElementsByTagName("*"),
		a = div.getElementsByTagName("a")[0];

	// Can't get basic test support
	if ( !all || !all.length || !a ) {
		return;
	}

	jQuery.support = {
		// IE strips leading whitespace when .innerHTML is used
		leadingWhitespace: div.firstChild.nodeType == 3,
		
		// Make sure that tbody elements aren't automatically inserted
		// IE will insert them into empty tables
		tbody: !div.getElementsByTagName("tbody").length,
		
		// Make sure that you can get all elements in an <object> element
		// IE 7 always returns no results
		objectAll: !!div.getElementsByTagName("object")[0]
			.getElementsByTagName("*").length,
		
		// Make sure that link elements get serialized correctly by innerHTML
		// This requires a wrapper element in IE
		htmlSerialize: !!div.getElementsByTagName("link").length,
		
		// Get the style information from getAttribute
		// (IE uses .cssText insted)
		style: /red/.test( a.getAttribute("style") ),
		
		// Make sure that URLs aren't manipulated
		// (IE normalizes it by default)
		hrefNormalized: a.getAttribute("href") === "/a",
		
		// Make sure that element opacity exists
		// (IE uses filter instead)
		opacity: a.style.opacity === "0.5",
		
		// Verify style float existence
		// (IE uses styleFloat instead of cssFloat)
		cssFloat: !!a.style.cssFloat,

		// Will be defined later
		scriptEval: false,
		noCloneEvent: true,
		boxModel: null
	};
	
	script.type = "text/javascript";
	try {
		script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
	} catch(e){}

	root.insertBefore( script, root.firstChild );
	
	// Make sure that the execution of code works by injecting a script
	// tag with appendChild/createTextNode
	// (IE doesn't support this, fails, and uses .text instead)
	if ( window[ id ] ) {
		jQuery.support.scriptEval = true;
		delete window[ id ];
	}

	root.removeChild( script );

	if ( div.attachEvent && div.fireEvent ) {
		div.attachEvent("onclick", function(){
			// Cloning a node shouldn't copy over any
			// bound event handlers (IE does this)
			jQuery.support.noCloneEvent = false;
			div.detachEvent("onclick", arguments.callee);
		});
		div.cloneNode(true).fireEvent("onclick");
	}

	// Figure out if the W3C box model works as expected
	// document.body must exist before we can do this
	jQuery(function(){
		var div = document.createElement("div");
		div.style.width = div.style.paddingLeft = "1px";

		document.body.appendChild( div );
		jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
		document.body.removeChild( div ).style.display = 'none';
	});
})();

var styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat";

jQuery.props = {
	"for": "htmlFor",
	"class": "className",
	"float": styleFloat,
	cssFloat: styleFloat,
	styleFloat: styleFloat,
	readonly: "readOnly",
	maxlength: "maxLength",
	cellspacing: "cellSpacing",
	rowspan: "rowSpan",
	tabindex: "tabIndex"
};
jQuery.fn.extend({
	// Keep a copy of the old load
	_load: jQuery.fn.load,

	load: function( url, params, callback ) {
		if ( typeof url !== "string" )
			return this._load( url );

		var off = url.indexOf(" ");
		if ( off >= 0 ) {
			var selector = url.slice(off, url.length);
			url = url.slice(0, off);
		}

		// Default to a GET request
		var type = "GET";

		// If the second parameter was provided
		if ( params )
			// If it's a function
			if ( jQuery.isFunction( params ) ) {
				// We assume that it's the callback
				callback = params;
				params = null;

			// Otherwise, build a param string
			} else if( typeof params === "object" ) {
				params = jQuery.param( params );
				type = "POST";
			}

		var self = this;

		// Request the remote document
		jQuery.ajax({
			url: url,
			type: type,
			dataType: "html",
			data: params,
			complete: function(res, status){
				// If successful, inject the HTML into all the matched elements
				if ( status == "success" || status == "notmodified" )
					// See if a selector was specified
					self.html( selector ?
						// Create a dummy div to hold the results
						jQuery("<div/>")
							// inject the contents of the document in, removing the scripts
							// to avoid any 'Permission Denied' errors in IE
							.append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))

							// Locate the specified elements
							.find(selector) :

						// If not, just inject the full result
						res.responseText );

				if( callback )
					self.each( callback, [res.responseText, status, res] );
			}
		});
		return this;
	},

	serialize: function() {
		return jQuery.param(this.serializeArray());
	},
	serializeArray: function() {
		return this.map(function(){
			return this.elements ? jQuery.makeArray(this.elements) : this;
		})
		.filter(function(){
			return this.name && !this.disabled &&
				(this.checked || /select|textarea/i.test(this.nodeName) ||
					/text|hidden|password|search/i.test(this.type));
		})
		.map(function(i, elem){
			var val = jQuery(this).val();
			return val == null ? null :
				jQuery.isArray(val) ?
					jQuery.map( val, function(val, i){
						return {name: elem.name, value: val};
					}) :
					{name: elem.name, value: val};
		}).get();
	}
});

// Attach a bunch of functions for handling common AJAX events
jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
	jQuery.fn[o] = function(f){
		return this.bind(o, f);
	};
});

var jsc = now();

jQuery.extend({
  
	get: function( url, data, callback, type ) {
		// shift arguments if data argument was ommited
		if ( jQuery.isFunction( data ) ) {
			callback = data;
			data = null;
		}

		return jQuery.ajax({
			type: "GET",
			url: url,
			data: data,
			success: callback,
			dataType: type
		});
	},

	getScript: function( url, callback ) {
		return jQuery.get(url, null, callback, "script");
	},

	getJSON: function( url, data, callback ) {
		return jQuery.get(url, data, callback, "json");
	},

	post: function( url, data, callback, type ) {
		if ( jQuery.isFunction( data ) ) {
			callback = data;
			data = {};
		}

		return jQuery.ajax({
			type: "POST",
			url: url,
			data: data,
			success: callback,
			dataType: type
		});
	},

	ajaxSetup: function( settings ) {
		jQuery.extend( jQuery.ajaxSettings, settings );
	},

	ajaxSettings: {
		url: location.href,
		global: true,
		type: "GET",
		contentType: "application/x-www-form-urlencoded",
		processData: true,
		async: true,
		/*
		timeout: 0,
		data: null,
		username: null,
		password: null,
		*/
		// Create the request object; Microsoft failed to properly
		// implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
		// This function can be overriden by calling jQuery.ajaxSetup
		xhr:function(){
			return window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
		},
		accepts: {
			xml: "application/xml, text/xml",
			html: "text/html",
			script: "text/javascript, application/javascript",
			json: "application/json, text/javascript",
			text: "text/plain",
			_default: "*/*"
		}
	},

	// Last-Modified header cache for next request
	lastModified: {},

	ajax: function( s ) {
		// Extend the settings, but re-extend 's' so that it can be
		// checked again later (in the test suite, specifically)
		s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));

		var jsonp, jsre = /=\?(&|$)/g, status, data,
			type = s.type.toUpperCase();

		// convert data if not already a string
		if ( s.data && s.processData && typeof s.data !== "string" )
			s.data = jQuery.param(s.data);

		// Handle JSONP Parameter Callbacks
		if ( s.dataType == "jsonp" ) {
			if ( type == "GET" ) {
				if ( !s.url.match(jsre) )
					s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
			} else if ( !s.data || !s.data.match(jsre) )
				s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
			s.dataType = "json";
		}

		// Build temporary JSONP function
		if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
			jsonp = "jsonp" + jsc++;

			// Replace the =? sequence both in the query string and the data
			if ( s.data )
				s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
			s.url = s.url.replace(jsre, "=" + jsonp + "$1");

			// We need to make sure
			// that a JSONP style response is executed properly
			s.dataType = "script";

			// Handle JSONP-style loading
			window[ jsonp ] = function(tmp){
				data = tmp;
				success();
				complete();
				// Garbage collect
				window[ jsonp ] = undefined;
				try{ delete window[ jsonp ]; } catch(e){}
				if ( head )
					head.removeChild( script );
			};
		}

		if ( s.dataType == "script" && s.cache == null )
			s.cache = false;

		if ( s.cache === false && type == "GET" ) {
			var ts = now();
			// try replacing _= if it is there
			var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
			// if nothing was replaced, add timestamp to the end
			s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
		}

		// If data is available, append data to url for get requests
		if ( s.data && type == "GET" ) {
			s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;

			// IE likes to send both get and post data, prevent this
			s.data = null;
		}

		// Watch for a new set of requests
		if ( s.global && ! jQuery.active++ )
			jQuery.event.trigger( "ajaxStart" );

		// Matches an absolute URL, and saves the domain
		var parts = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url );

		// If we're requesting a remote document
		// and trying to load JSON or Script with a GET
		if ( s.dataType == "script" && type == "GET" && parts
			&& ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){

			var head = document.getElementsByTagName("head")[0];
			var script = document.createElement("script");
			script.src = s.url;
			if (s.scriptCharset)
				script.charset = s.scriptCharset;

			// Handle Script loading
			if ( !jsonp ) {
				var done = false;

				// Attach handlers for all browsers
				script.onload = script.onreadystatechange = function(){
					if ( !done && (!this.readyState ||
							this.readyState == "loaded" || this.readyState == "complete") ) {
						done = true;
						success();
						complete();

						// Handle memory leak in IE
						script.onload = script.onreadystatechange = null;
						head.removeChild( script );
					}
				};
			}

			head.appendChild(script);

			// We handle everything using the script element injection
			return undefined;
		}

		var requestDone = false;

		// Create the request object
		var xhr = s.xhr();

		// Open the socket
		// Passing null username, generates a login popup on Opera (#2865)
		if( s.username )
			xhr.open(type, s.url, s.async, s.username, s.password);
		else
			xhr.open(type, s.url, s.async);

		// Need an extra try/catch for cross domain requests in Firefox 3
		try {
			// Set the correct header, if data is being sent
			if ( s.data )
				xhr.setRequestHeader("Content-Type", s.contentType);

			// Set the If-Modified-Since header, if ifModified mode.
			if ( s.ifModified )
				xhr.setRequestHeader("If-Modified-Since",
					jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );

			// Set header so the called script knows that it's an XMLHttpRequest
			xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");

			// Set the Accepts header for the server, depending on the dataType
			xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
				s.accepts[ s.dataType ] + ", */*" :
				s.accepts._default );
		} catch(e){}

		// Allow custom headers/mimetypes and early abort
		if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
			// Handle the global AJAX counter
			if ( s.global && ! --jQuery.active )
				jQuery.event.trigger( "ajaxStop" );
			// close opended socket
			xhr.abort();
			return false;
		}

		if ( s.global )
			jQuery.event.trigger("ajaxSend", [xhr, s]);

		// Wait for a response to come back
		var onreadystatechange = function(isTimeout){
			// The request was aborted, clear the interval and decrement jQuery.active
			if (xhr.readyState == 0) {
				if (ival) {
					// clear poll interval
					clearInterval(ival);
					ival = null;
					// Handle the global AJAX counter
					if ( s.global && ! --jQuery.active )
						jQuery.event.trigger( "ajaxStop" );
				}
			// The transfer is complete and the data is available, or the request timed out
			} else if ( !requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout") ) {
				requestDone = true;

				// clear poll interval
				if (ival) {
					clearInterval(ival);
					ival = null;
				}

				status = isTimeout == "timeout" ? "timeout" :
					!jQuery.httpSuccess( xhr ) ? "error" :
					s.ifModified && jQuery.httpNotModified( xhr, s.url ) ? "notmodified" :
					"success";

				if ( status == "success" ) {
					// Watch for, and catch, XML document parse errors
					try {
						// process the data (runs the xml through httpData regardless of callback)
						data = jQuery.httpData( xhr, s.dataType, s );
					} catch(e) {
						status = "parsererror";
					}
				}

				// Make sure that the request was successful or notmodified
				if ( status == "success" ) {
					// Cache Last-Modified header, if ifModified mode.
					var modRes;
					try {
						modRes = xhr.getResponseHeader("Last-Modified");
					} catch(e) {} // swallow exception thrown by FF if header is not available

					if ( s.ifModified && modRes )
						jQuery.lastModified[s.url] = modRes;

					// JSONP handles its own success callback
					if ( !jsonp )
						success();
				} else
					jQuery.handleError(s, xhr, status);

				// Fire the complete handlers
				complete();

				if ( isTimeout )
					xhr.abort();

				// Stop memory leaks
				if ( s.async )
					xhr = null;
			}
		};

		if ( s.async ) {
			// don't attach the handler to the request, just poll it instead
			var ival = setInterval(onreadystatechange, 13);

			// Timeout checker
			if ( s.timeout > 0 )
				setTimeout(function(){
					// Check to see if the request is still happening
					if ( xhr && !requestDone )
						onreadystatechange( "timeout" );
				}, s.timeout);
		}

		// Send the data
		try {
			xhr.send(s.data);
		} catch(e) {
			jQuery.handleError(s, xhr, null, e);
		}

		// firefox 1.5 doesn't fire statechange for sync requests
		if ( !s.async )
			onreadystatechange();

		function success(){
			// If a local callback was specified, fire it and pass it the data
			if ( s.success )
				s.success( data, status );

			// Fire the global callback
			if ( s.global )
				jQuery.event.trigger( "ajaxSuccess", [xhr, s] );
		}

		function complete(){
			// Process result
			if ( s.complete )
				s.complete(xhr, status);

			// The request was completed
			if ( s.global )
				jQuery.event.trigger( "ajaxComplete", [xhr, s] );

			// Handle the global AJAX counter
			if ( s.global && ! --jQuery.active )
				jQuery.event.trigger( "ajaxStop" );
		}

		// return XMLHttpRequest to allow aborting the request etc.
		return xhr;
	},

	handleError: function( s, xhr, status, e ) {
		// If a local callback was specified, fire it
		if ( s.error ) s.error( xhr, status, e );

		// Fire the global callback
		if ( s.global )
			jQuery.event.trigger( "ajaxError", [xhr, s, e] );
	},

	// Counter for holding the number of active queries
	active: 0,

	// Determines if an XMLHttpRequest was successful or not
	httpSuccess: function( xhr ) {
		try {
			// IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
			return !xhr.status && location.protocol == "file:" ||
				( xhr.status >= 200 && xhr.status < 300 ) || xhr.status == 304 || xhr.status == 1223;
		} catch(e){}
		return false;
	},

	// Determines if an XMLHttpRequest returns NotModified
	httpNotModified: function( xhr, url ) {
		try {
			var xhrRes = xhr.getResponseHeader("Last-Modified");

			// Firefox always returns 200. check Last-Modified date
			return xhr.status == 304 || xhrRes == jQuery.lastModified[url];
		} catch(e){}
		return false;
	},

	httpData: function( xhr, type, s ) {
		var ct = xhr.getResponseHeader("content-type"),
			xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0,
			data = xml ? xhr.responseXML : xhr.responseText;

		if ( xml && data.documentElement.tagName == "parsererror" )
			throw "parsererror";
			
		// Allow a pre-filtering function to sanitize the response
		// s != null is checked to keep backwards compatibility
		if( s && s.dataFilter )
			data = s.dataFilter( data, type );

		// The filter can actually parse the response
		if( typeof data === "string" ){

			// If the type is "script", eval it in global context
			if ( type == "script" )
				jQuery.globalEval( data );

			// Get the JavaScript object, if JSON is used.
			if ( type == "json" )
				data = window["eval"]("(" + data + ")");
		}
		
		return data;
	},

	// Serialize an array of form elements or a set of
	// key/values into a query string
	param: function( a ) {
		var s = [ ];

		function add( key, value ){
			s[ s.length ] = encodeURIComponent(key) + '=' + encodeURIComponent(value);
		};

		// If an array was passed in, assume that it is an array
		// of form elements
		if ( jQuery.isArray(a) || a.jquery )
			// Serialize the form elements
			jQuery.each( a, function(){
				add( this.name, this.value );
			});

		// Otherwise, assume that it's an object of key/value pairs
		else
			// Serialize the key/values
			for ( var j in a )
				// If the value is an array then the key names need to be repeated
				if ( jQuery.isArray(a[j]) )
					jQuery.each( a[j], function(){
						add( j, this );
					});
				else
					add( j, jQuery.isFunction(a[j]) ? a[j]() : a[j] );

		// Return the resulting serialization
		return s.join("&").replace(/%20/g, "+");
	}

});
var elemdisplay = {},
	timerId,
	fxAttrs = [
		// height animations
		[ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
		// width animations
		[ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
		// opacity animations
		[ "opacity" ]
	];

function genFx( type, num ){
	var obj = {};
	jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function(){
		obj[ this ] = type;
	});
	return obj;
}

jQuery.fn.extend({
	show: function(speed,callback){
		if ( speed ) {
			return this.animate( genFx("show", 3), speed, callback);
		} else {
			for ( var i = 0, l = this.length; i < l; i++ ){
				var old = jQuery.data(this[i], "olddisplay");
				
				this[i].style.display = old || "";
				
				if ( jQuery.css(this[i], "display") === "none" ) {
					var tagName = this[i].tagName, display;
					
					if ( elemdisplay[ tagName ] ) {
						display = elemdisplay[ tagName ];
					} else {
						var elem = jQuery("<" + tagName + " />").appendTo("body");
						
						display = elem.css("display");
						if ( display === "none" )
							display = "block";
						
						elem.remove();
						
						elemdisplay[ tagName ] = display;
					}
					
					jQuery.data(this[i], "olddisplay", display);
				}
			}

			// Set the display of the elements in a second loop
			// to avoid the constant reflow
			for ( var i = 0, l = this.length; i < l; i++ ){
				this[i].style.display = jQuery.data(this[i], "olddisplay") || "";
			}
			
			return this;
		}
	},

	hide: function(speed,callback){
		if ( speed ) {
			return this.animate( genFx("hide", 3), speed, callback);
		} else {
			for ( var i = 0, l = this.length; i < l; i++ ){
				var old = jQuery.data(this[i], "olddisplay");
				if ( !old && old !== "none" )
					jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
			}

			// Set the display of the elements in a second loop
			// to avoid the constant reflow
			for ( var i = 0, l = this.length; i < l; i++ ){
				this[i].style.display = "none";
			}

			return this;
		}
	},

	// Save the old toggle function
	_toggle: jQuery.fn.toggle,

	toggle: function( fn, fn2 ){
		var bool = typeof fn === "boolean";

		return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
			this._toggle.apply( this, arguments ) :
			fn == null || bool ?
				this.each(function(){
					var state = bool ? fn : jQuery(this).is(":hidden");
					jQuery(this)[ state ? "show" : "hide" ]();
				}) :
				this.animate(genFx("toggle", 3), fn, fn2);
	},

	fadeTo: function(speed,to,callback){
		return this.animate({opacity: to}, speed, callback);
	},

	animate: function( prop, speed, easing, callback ) {
		var optall = jQuery.speed(speed, easing, callback);

		return this[ optall.queue === false ? "each" : "queue" ](function(){
		
			var opt = jQuery.extend({}, optall), p,
				hidden = this.nodeType == 1 && jQuery(this).is(":hidden"),
				self = this;
	
			for ( p in prop ) {
				if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
					return opt.complete.call(this);

				if ( ( p == "height" || p == "width" ) && this.style ) {
					// Store display property
					opt.display = jQuery.css(this, "display");

					// Make sure that nothing sneaks out
					opt.overflow = this.style.overflow;
				}
			}

			if ( opt.overflow != null )
				this.style.overflow = "hidden";

			opt.curAnim = jQuery.extend({}, prop);

			jQuery.each( prop, function(name, val){
				var e = new jQuery.fx( self, opt, name );

				if ( /toggle|show|hide/.test(val) )
					e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
				else {
					var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
						start = e.cur(true) || 0;

					if ( parts ) {
						var end = parseFloat(parts[2]),
							unit = parts[3] || "px";

						// We need to compute starting value
						if ( unit != "px" ) {
							self.style[ name ] = (end || 1) + unit;
							start = ((end || 1) / e.cur(true)) * start;
							self.style[ name ] = start + unit;
						}

						// If a +=/-= token was provided, we're doing a relative animation
						if ( parts[1] )
							end = ((parts[1] == "-=" ? -1 : 1) * end) + start;

						e.custom( start, end, unit );
					} else
						e.custom( start, val, "" );
				}
			});

			// For JS strict compliance
			return true;
		});
	},

	stop: function(clearQueue, gotoEnd){
		var timers = jQuery.timers;

		if (clearQueue)
			this.queue([]);

		this.each(function(){
			// go in reverse order so anything added to the queue during the loop is ignored
			for ( var i = timers.length - 1; i >= 0; i-- )
				if ( timers[i].elem == this ) {
					if (gotoEnd)
						// force the next step to be the last
						timers[i](true);
					timers.splice(i, 1);
				}
		});

		// start the next in the queue if the last step wasn't forced
		if (!gotoEnd)
			this.dequeue();

		return this;
	}

});

// Generate shortcuts for custom animations
jQuery.each({
	slideDown: genFx("show", 1),
	slideUp: genFx("hide", 1),
	slideToggle: genFx("toggle", 1),
	fadeIn: { opacity: "show" },
	fadeOut: { opacity: "hide" }
}, function( name, props ){
	jQuery.fn[ name ] = function( speed, callback ){
		return this.animate( props, speed, callback );
	};
});

jQuery.extend({

	speed: function(speed, easing, fn) {
		var opt = typeof speed === "object" ? speed : {
			complete: fn || !fn && easing ||
				jQuery.isFunction( speed ) && speed,
			duration: speed,
			easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
		};

		opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
			jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;

		// Queueing
		opt.old = opt.complete;
		opt.complete = function(){
			if ( opt.queue !== false )
				jQuery(this).dequeue();
			if ( jQuery.isFunction( opt.old ) )
				opt.old.call( this );
		};

		return opt;
	},

	easing: {
		linear: function( p, n, firstNum, diff ) {
			return firstNum + diff * p;
		},
		swing: function( p, n, firstNum, diff ) {
			return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
		}
	},

	timers: [],

	fx: function( elem, options, prop ){
		this.options = options;
		this.elem = elem;
		this.prop = prop;

		if ( !options.orig )
			options.orig = {};
	}

});

jQuery.fx.prototype = {

	// Simple function for setting a style value
	update: function(){
		if ( this.options.step )
			this.options.step.call( this.elem, this.now, this );

		(jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );

		// Set display property to block for height/width animations
		if ( ( this.prop == "height" || this.prop == "width" ) && this.elem.style )
			this.elem.style.display = "block";
	},

	// Get the current size
	cur: function(force){
		if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) )
			return this.elem[ this.prop ];

		var r = parseFloat(jQuery.css(this.elem, this.prop, force));
		return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
	},

	// Start an animation from one number to another
	custom: function(from, to, unit){
		this.startTime = now();
		this.start = from;
		this.end = to;
		this.unit = unit || this.unit || "px";
		this.now = this.start;
		this.pos = this.state = 0;

		var self = this;
		function t(gotoEnd){
			return self.step(gotoEnd);
		}

		t.elem = this.elem;

		if ( t() && jQuery.timers.push(t) && !timerId ) {
			timerId = setInterval(function(){
				var timers = jQuery.timers;

				for ( var i = 0; i < timers.length; i++ )
					if ( !timers[i]() )
						timers.splice(i--, 1);

				if ( !timers.length ) {
					clearInterval( timerId );
					timerId = undefined;
				}
			}, 13);
		}
	},

	// Simple 'show' function
	show: function(){
		// Remember where we started, so that we can go back to it later
		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
		this.options.show = true;

		// Begin the animation
		// Make sure that we start at a small width/height to avoid any
		// flash of content
		this.custom(this.prop == "width" || this.prop == "height" ? 1 : 0, this.cur());

		// Start by showing the element
		jQuery(this.elem).show();
	},

	// Simple 'hide' function
	hide: function(){
		// Remember where we started, so that we can go back to it later
		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
		this.options.hide = true;

		// Begin the animation
		this.custom(this.cur(), 0);
	},

	// Each step of an animation
	step: function(gotoEnd){
		var t = now();

		if ( gotoEnd || t >= this.options.duration + this.startTime ) {
			this.now = this.end;
			this.pos = this.state = 1;
			this.update();

			this.options.curAnim[ this.prop ] = true;

			var done = true;
			for ( var i in this.options.curAnim )
				if ( this.options.curAnim[i] !== true )
					done = false;

			if ( done ) {
				if ( this.options.display != null ) {
					// Reset the overflow
					this.elem.style.overflow = this.options.overflow;

					// Reset the display
					this.elem.style.display = this.options.display;
					if ( jQuery.css(this.elem, "display") == "none" )
						this.elem.style.display = "block";
				}

				// Hide the element if the "hide" operation was done
				if ( this.options.hide )
					jQuery(this.elem).hide();

				// Reset the properties, if the item has been hidden or shown
				if ( this.options.hide || this.options.show )
					for ( var p in this.options.curAnim )
						jQuery.attr(this.elem.style, p, this.options.orig[p]);
					
				// Execute the complete function
				this.options.complete.call( this.elem );
			}

			return false;
		} else {
			var n = t - this.startTime;
			this.state = n / this.options.duration;

			// Perform the easing function, defaults to swing
			this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration);
			this.now = this.start + ((this.end - this.start) * this.pos);

			// Perform the next step of the animation
			this.update();
		}

		return true;
	}

};

jQuery.extend( jQuery.fx, {
	speeds:{
		slow: 600,
 		fast: 200,
 		// Default speed
 		_default: 400
	},
	step: {

		opacity: function(fx){
			jQuery.attr(fx.elem.style, "opacity", fx.now);
		},

		_default: function(fx){
			if ( fx.elem.style && fx.elem.style[ fx.prop ] != null )
				fx.elem.style[ fx.prop ] = fx.now + fx.unit;
			else
				fx.elem[ fx.prop ] = fx.now;
		}
	}
});
if ( document.documentElement["getBoundingClientRect"] )
	jQuery.fn.offset = function() {
		if ( !this[0] ) return { top: 0, left: 0 };
		if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
		var box  = this[0].getBoundingClientRect(), doc = this[0].ownerDocument, body = doc.body, docElem = doc.documentElement,
			clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
			top  = box.top  + (self.pageYOffset || jQuery.boxModel && docElem.scrollTop  || body.scrollTop ) - clientTop,
			left = box.left + (self.pageXOffset || jQuery.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
		return { top: top, left: left };
	};
else 
	jQuery.fn.offset = function() {
		if ( !this[0] ) return { top: 0, left: 0 };
		if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
		jQuery.offset.initialized || jQuery.offset.initialize();

		var elem = this[0], offsetParent = elem.offsetParent, prevOffsetParent = elem,
			doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
			body = doc.body, defaultView = doc.defaultView,
			prevComputedStyle = defaultView.getComputedStyle(elem, null),
			top = elem.offsetTop, left = elem.offsetLeft;

		while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
			computedStyle = defaultView.getComputedStyle(elem, null);
			top -= elem.scrollTop, left -= elem.scrollLeft;
			if ( elem === offsetParent ) {
				top += elem.offsetTop, left += elem.offsetLeft;
				if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.tagName)) )
					top  += parseInt( computedStyle.borderTopWidth,  10) || 0,
					left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
				prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
			}
			if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" )
				top  += parseInt( computedStyle.borderTopWidth,  10) || 0,
				left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
			prevComputedStyle = computedStyle;
		}

		if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" )
			top  += body.offsetTop,
			left += body.offsetLeft;

		if ( prevComputedStyle.position === "fixed" )
			top  += Math.max(docElem.scrollTop, body.scrollTop),
			left += Math.max(docElem.scrollLeft, body.scrollLeft);

		return { top: top, left: left };
	};

jQuery.offset = {
	initialize: function() {
		if ( this.initialized ) return;
		var body = document.body, container = document.createElement('div'), innerDiv, checkDiv, table, td, rules, prop, bodyMarginTop = body.style.marginTop,
			html = '<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';

		rules = { position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' };
		for ( prop in rules ) container.style[prop] = rules[prop];

		container.innerHTML = html;
		body.insertBefore(container, body.firstChild);
		innerDiv = container.firstChild, checkDiv = innerDiv.firstChild, td = innerDiv.nextSibling.firstChild.firstChild;

		this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
		this.doesAddBorderForTableAndCells = (td.offsetTop === 5);

		innerDiv.style.overflow = 'hidden', innerDiv.style.position = 'relative';
		this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);

		body.style.marginTop = '1px';
		this.doesNotIncludeMarginInBodyOffset = (body.offsetTop === 0);
		body.style.marginTop = bodyMarginTop;

		body.removeChild(container);
		this.initialized = true;
	},

	bodyOffset: function(body) {
		jQuery.offset.initialized || jQuery.offset.initialize();
		var top = body.offsetTop, left = body.offsetLeft;
		if ( jQuery.offset.doesNotIncludeMarginInBodyOffset )
			top  += parseInt( jQuery.curCSS(body, 'marginTop',  true), 10 ) || 0,
			left += parseInt( jQuery.curCSS(body, 'marginLeft', true), 10 ) || 0;
		return { top: top, left: left };
	}
};


jQuery.fn.extend({
	position: function() {
		var left = 0, top = 0, results;

		if ( this[0] ) {
			// Get *real* offsetParent
			var offsetParent = this.offsetParent(),

			// Get correct offsets
			offset       = this.offset(),
			parentOffset = /^body|html$/i.test(offsetParent[0].tagName) ? { top: 0, left: 0 } : offsetParent.offset();

			// Subtract element margins
			// note: when an element has margin: auto the offsetLeft and marginLeft 
			// are the same in Safari causing offset.left to incorrectly be 0
			offset.top  -= num( this, 'marginTop'  );
			offset.left -= num( this, 'marginLeft' );

			// Add offsetParent borders
			parentOffset.top  += num( offsetParent, 'borderTopWidth'  );
			parentOffset.left += num( offsetParent, 'borderLeftWidth' );

			// Subtract the two offsets
			results = {
				top:  offset.top  - parentOffset.top,
				left: offset.left - parentOffset.left
			};
		}

		return results;
	},

	offsetParent: function() {
		var offsetParent = this[0].offsetParent || document.body;
		while ( offsetParent && (!/^body|html$/i.test(offsetParent.tagName) && jQuery.css(offsetParent, 'position') == 'static') )
			offsetParent = offsetParent.offsetParent;
		return jQuery(offsetParent);
	}
});


// Create scrollLeft and scrollTop methods
jQuery.each( ['Left', 'Top'], function(i, name) {
	var method = 'scroll' + name;
	
	jQuery.fn[ method ] = function(val) {
		if (!this[0]) return null;

		return val !== undefined ?

			// Set the scroll offset
			this.each(function() {
				this == window || this == document ?
					window.scrollTo(
						!i ? val : jQuery(window).scrollLeft(),
						 i ? val : jQuery(window).scrollTop()
					) :
					this[ method ] = val;
			}) :

			// Return the scroll offset
			this[0] == window || this[0] == document ?
				self[ i ? 'pageYOffset' : 'pageXOffset' ] ||
					jQuery.boxModel && document.documentElement[ method ] ||
					document.body[ method ] :
				this[0][ method ];
	};
});
// Create innerHeight, innerWidth, outerHeight and outerWidth methods
jQuery.each([ "Height", "Width" ], function(i, name){

	var tl = i ? "Left"  : "Top",  // top or left
		br = i ? "Right" : "Bottom", // bottom or right
		lower = name.toLowerCase();

	// innerHeight and innerWidth
	jQuery.fn["inner" + name] = function(){
		return this[0] ?
			jQuery.css( this[0], lower, false, "padding" ) :
			null;
	};

	// outerHeight and outerWidth
	jQuery.fn["outer" + name] = function(margin) {
		return this[0] ?
			jQuery.css( this[0], lower, false, margin ? "margin" : "border" ) :
			null;
	};
	
	var type = name.toLowerCase();

	jQuery.fn[ type ] = function( size ) {
		// Get window width or height
		return this[0] == window ?
			// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
			document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] ||
			document.body[ "client" + name ] :

			// Get document width or height
			this[0] == document ?
				// Either scroll[Width/Height] or offset[Width/Height], whichever is greater
				Math.max(
					document.documentElement["client" + name],
					document.body["scroll" + name], document.documentElement["scroll" + name],
					document.body["offset" + name], document.documentElement["offset" + name]
				) :

				// Get or set width or height on the element
				size === undefined ?
					// Get width or height on the element
					(this.length ? jQuery.css( this[0], type ) : null) :

					// Set the width or height on the element (default to pixels if value is unitless)
					this.css( type, typeof size === "string" ? size : size + "px" );
	};

});
})();
/*
(c) Copyrights 2007 - 2008

Original idea by by Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
 
jQuery Plugin by Tzury Bar Yochay 
tzury.by@gmail.com
http://evalinux.wordpress.com
http://facebook.com/profile.php?id=513676303

Project's sites: 
http://code.google.com/p/js-hotkeys/
http://github.com/tzuryby/hotkeys/tree/master

License: same as jQuery license. 

USAGE:
    // simple usage
    $(document).bind('keydown', 'Ctrl+c', function(){ alert('copy anyone?');});
    
    // special options such as disableInIput
    $(document).bind('keydown', {combi:'Ctrl+x', disableInInput: true} , function() {});
    
Note:
    This plugin wraps the following jQuery methods: $.fn.find, $.fn.bind and $.fn.unbind
*/

(function (jQuery){
    // keep reference to the original $.fn.bind, $.fn.unbind and $.fn.find
    jQuery.fn.__bind__ = jQuery.fn.bind;
    jQuery.fn.__unbind__ = jQuery.fn.unbind;
    jQuery.fn.__find__ = jQuery.fn.find;
    
    var hotkeys = {
        version: '0.7.9',
        override: /keypress|keydown|keyup/g,
        triggersMap: {},
        
        specialKeys: { 27: 'esc', 9: 'tab', 32:'space', 13: 'return', 8:'backspace', 145: 'scroll', 
            20: 'capslock', 144: 'numlock', 19:'pause', 45:'insert', 36:'home', 46:'del',
            35:'end', 33: 'pageup', 34:'pagedown', 37:'left', 38:'up', 39:'right',40:'down', 
            109: '-', 
            112:'f1',113:'f2', 114:'f3', 115:'f4', 116:'f5', 117:'f6', 118:'f7', 119:'f8', 
            120:'f9', 121:'f10', 122:'f11', 123:'f12', 191: '/'},
        
        shiftNums: { "`":"~", "1":"!", "2":"@", "3":"#", "4":"$", "5":"%", "6":"^", "7":"&", 
            "8":"*", "9":"(", "0":")", "-":"_", "=":"+", ";":":", "'":"\"", ",":"<", 
            ".":">",  "/":"?",  "\\":"|" },
        
        newTrigger: function (type, combi, callback) { 
            // i.e. {'keyup': {'ctrl': {cb: callback, disableInInput: false}}}
            var result = {};
            result[type] = {};
            result[type][combi] = {cb: callback, disableInInput: false};
            return result;
        }
    };
    // add firefox num pad char codes
    //if (jQuery.browser.mozilla){
    // add num pad char codes
    hotkeys.specialKeys = jQuery.extend(hotkeys.specialKeys, { 96: '0', 97:'1', 98: '2', 99: 
        '3', 100: '4', 101: '5', 102: '6', 103: '7', 104: '8', 105: '9', 106: '*', 
        107: '+', 109: '-', 110: '.', 111 : '/'
        });
    //}
    
    // a wrapper around of $.fn.find 
    // see more at: http://groups.google.com/group/jquery-en/browse_thread/thread/18f9825e8d22f18d
    jQuery.fn.find = function( selector ) {
        this.query = selector;
        return jQuery.fn.__find__.apply(this, arguments);
	};
    
    jQuery.fn.unbind = function (type, combi, fn){
        if (jQuery.isFunction(combi)){
            fn = combi;
            combi = null;
        }
        if (combi && typeof combi === 'string'){
            var selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString();
            var hkTypes = type.split(' ');
            for (var x=0; x<hkTypes.length; x++){
                delete hotkeys.triggersMap[selectorId][hkTypes[x]][combi];
            }
        }
        // call jQuery original unbind
        return  this.__unbind__(type, fn);
    };
    
    jQuery.fn.bind = function(type, data, fn){
        // grab keyup,keydown,keypress
        var handle = type.match(hotkeys.override);
        
        if (jQuery.isFunction(data) || !handle){
            // call jQuery.bind only
            return this.__bind__(type, data, fn);
        }
        else{
            // split the job
            var result = null,            
            // pass the rest to the original $.fn.bind
            pass2jq = jQuery.trim(type.replace(hotkeys.override, ''));
            
            // see if there are other types, pass them to the original $.fn.bind
            if (pass2jq){
                result = this.__bind__(pass2jq, data, fn);
            }            
            
            if (typeof data === "string"){
                data = {'combi': data};
            }
            if(data.combi){
                for (var x=0; x < handle.length; x++){
                    var eventType = handle[x];
                    var combi = data.combi.toLowerCase(),
                        trigger = hotkeys.newTrigger(eventType, combi, fn),
                        selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString();
                        
                    //trigger[eventType][combi].propagate = data.propagate;
                    trigger[eventType][combi].disableInInput = data.disableInInput;
                    
                    // first time selector is bounded
                    if (!hotkeys.triggersMap[selectorId]) {
                        hotkeys.triggersMap[selectorId] = trigger;
                    }
                    // first time selector is bounded with this type
                    else if (!hotkeys.triggersMap[selectorId][eventType]) {
                        hotkeys.triggersMap[selectorId][eventType] = trigger[eventType];
                    }
                    // make trigger point as array so more than one handler can be bound
                    var mapPoint = hotkeys.triggersMap[selectorId][eventType][combi];
                    if (!mapPoint){
                        hotkeys.triggersMap[selectorId][eventType][combi] = [trigger[eventType][combi]];
                    }
                    else if (mapPoint.constructor !== Array){
                        hotkeys.triggersMap[selectorId][eventType][combi] = [mapPoint];
                    }
                    else {
                        hotkeys.triggersMap[selectorId][eventType][combi][mapPoint.length] = trigger[eventType][combi];
                    }
                    
                    // add attribute and call $.event.add per matched element
                    this.each(function(){
                        // jQuery wrapper for the current element
                        var jqElem = jQuery(this);
                        
                        // element already associated with another collection
                        if (jqElem.attr('hkId') && jqElem.attr('hkId') !== selectorId){
                            selectorId = jqElem.attr('hkId') + ";" + selectorId;
                        }
                        jqElem.attr('hkId', selectorId);
                    });
                    result = this.__bind__(handle.join(' '), data, hotkeys.handler)
                }
            }
            return result;
        }
    };
    // work-around for opera and safari where (sometimes) the target is the element which was last 
    // clicked with the mouse and not the document event it would make sense to get the document
    hotkeys.findElement = function (elem){
        if (!jQuery(elem).attr('hkId')){
            if (jQuery.browser.opera || jQuery.browser.safari){
                while (!jQuery(elem).attr('hkId') && elem.parentNode){
                    elem = elem.parentNode;
                }
            }
        }
        return elem;
    };
    // the event handler
    hotkeys.handler = function(event) {
        var target = hotkeys.findElement(event.currentTarget), 
            jTarget = jQuery(target),
            ids = jTarget.attr('hkId');
        
        if(ids){
            ids = ids.split(';');
            var code = event.which,
                type = event.type,
                special = hotkeys.specialKeys[code],
                // prevent f5 overlapping with 't' (or f4 with 's', etc.)
                character = !special && String.fromCharCode(code).toLowerCase(),
                shift = event.shiftKey,
                ctrl = event.ctrlKey,            
                // patch for jquery 1.2.5 && 1.2.6 see more at:  
                // http://groups.google.com/group/jquery-en/browse_thread/thread/83e10b3bb1f1c32b
                alt = event.altKey || event.originalEvent.altKey,
                mapPoint = null;

            for (var x=0; x < ids.length; x++){
                if (hotkeys.triggersMap[ids[x]][type]){
                    mapPoint = hotkeys.triggersMap[ids[x]][type];
                    break;
                }
            }
            
            //find by: id.type.combi.options            
            if (mapPoint){ 
                var trigger;
                // event type is associated with the hkId
                if(!shift && !ctrl && !alt) { // No Modifiers
                    trigger = mapPoint[special] ||  (character && mapPoint[character]);
                }
                else{
                    // check combinations (alt|ctrl|shift+anything)
                    var modif = '';
                    if(alt) modif +='alt+';
                    if(ctrl) modif+= 'ctrl+';
                    if(shift) modif += 'shift+';
                    // modifiers + special keys or modifiers + character or modifiers + shift character or just shift character
                    trigger = mapPoint[modif+special];
                    if (!trigger){
                        if (character){
                            trigger = mapPoint[modif+character] 
                                || mapPoint[modif+hotkeys.shiftNums[character]]
                                // '$' can be triggered as 'Shift+4' or 'Shift+$' or just '$'
                                || (modif === 'shift+' && mapPoint[hotkeys.shiftNums[character]]);
                        }
                    }
                }
                if (trigger){
                    var result = false;
                    for (var x=0; x < trigger.length; x++){
                        if(trigger[x].disableInInput){
                            // double check event.currentTarget and event.target
                            var elem = jQuery(event.target);
                            if (jTarget.is("input") || jTarget.is("textarea") || jTarget.is("select") 
                                || elem.is("input") || elem.is("textarea") || elem.is("select")) {
                                return true;
                            }
                        }                       
                        // call the registered callback function
                        result = result || trigger[x].cb.apply(this, [event]);
                    }
                    return result;
                }
            }
        }
    };
    // place it under window so it can be extended and overridden by others
    window.hotkeys = hotkeys;
    return jQuery;
})(jQuery);
//==============================================================================
//  PROJECT: ApexLib - The APEX Library (http://apexlib.soureforge.net/)
//
//  DESCRIPTION:
//
//    This script is called directly after defining jQuery and changes it name
//    from "jQuery" to "apexlib.jQuery" to avoid collisions with other jQuery
//    versions included by user code.
//
//  SUPPORTED APEX VERSIONS:
//
//    2.x
//
//  AUTHOR(S):
//
//    Peter Raganitsch (http://www.oracle-and-apex.com/, http://www.oracle-and-apex.com/)
//
//  SVN HEADER:
//
//    $Id: $
//
//==============================================================================
var apexlibJquery = {};
apexlibJquery = jQuery.noConflict(true);
//==============================================================================
//  PROJECT: ApexLib - The APEX Library (http://apexlib.soureforge.net/)
//
//  DESCRIPTION:
//
//    This JavaScript library contains the core methods.
//
//  SUPPORTED APEX VERSIONS:
//
//    2.x
//
//  AUTHOR(S):
//
//    Patrick Wolf (http://inside-apex.blogspot.com/)
//    Peter Raganitsch (http://www.oracle-and-apex.com/, http://blog.oracleapex.at/)
//
//  SVN HEADER:
//
//    $Id: ApexLib_Core.js 383 2009-10-19 19:48:55Z praganitsch $
//
//==============================================================================

//==============================================================================
// Initialize the namespace for ApexLib.
//==============================================================================
if (!window.apexlib) window.apexlib = {};
if (!apexlib.core)   apexlib.core   = {};

//==============================================================================
// Global array used by the different core methods.
//==============================================================================
apexlib.core.sMessageList = new Object(); // index: message name
                                          // value: message text with placeholders

//==============================================================================
// Gets the element identified by the class name.
// Note: This function will stop after the first element with this class name!
//       This function doesn't handle elements with a list of class names!
//==============================================================================
apexlib.core.getElementByClassName = function(pTagName, pClassName, pRootElement)
{
  // get all elements with this tag within the root element or the hole DOM
  var vElementList = null;
  if (!pRootElement)
  {
    vElementList = document.getElementsByTagName(pTagName);
  }
  else
  {
    vElementList = pRootElement.getElementsByTagName(pTagName);
  }
  // find the element with this class name
  for (var i=0; i < vElementList.length; i++)
  {
    if (vElementList[i].className == pClassName) return vElementList[i];
  }
}; // getElementByClassName

//==============================================================================
// Returns a list of elements with a matching class name.
// Note: This function doesn't handle elements with a list of class names!
//==============================================================================
apexlib.core.getElementsByClassName = function(pTagName, pClassName, pRootElement)
{
  // get all elements with this tag within the root element or the hole DOM
  var vElementList       = null;
  var vResultElementList = new Array();
  var vIdx               = 0;
  if (!pRootElement)
  {
    vElementList = document.getElementsByTagName(pTagName);
  }
  else
  {
    vElementList = pRootElement.getElementsByTagName(pTagName);
  }
  // find the element with this class name
  for (var i=0; i < vElementList.length; i++)
  {
    if (vElementList[i].className == pClassName)
    {
      vResultElementList[vIdx] = vElementList[i];
      vIdx++;
    }
  }
  return vResultElementList;
}; // getElementsByClassName

//==============================================================================
// Searches the next parent node where the node name matches.
//==============================================================================
apexlib.core.getParentNode = function(pStartElement, pNodeName)
{
  var vElement = pStartElement.parentNode;
  if ((!vElement) || (vElement.nodeName == pNodeName))
  {
    return vElement;
  }
  else
  {
    return apexlib.core.getParentNode(vElement, pNodeName);
  }
}; // getParentNode

//==============================================================================
// Searches the previous node where the node name matches.
//==============================================================================
apexlib.core.getPreviousNode = function(pStartElement, pNodeName)
{
  var vElement = pStartElement.previousSibling;
  if ((!vElement) || (vElement.nodeName == pNodeName))
  {
    return vElement;
  }
  else
  {
    // if a previous element exist, check that
    if (vElement.previousSibling)
    {
      return apexlib.core.getPreviousNode(vElement, pNodeName);
    }
    else
    {
      // no previous element exists, continue with the parent
      if ((!pStartElement.parentNode) || (pStartElement.parentNode.nodeName == pNodeName))
      {
        return pStartElement.parentNode;
      }
      else
      {
        return apexlib.core.getPreviousNode(pStartElement.parentNode, pNodeName);
      }
    }
  }
}; // getPreviousNode

//==============================================================================
// Searches the next node where the node name matches.
//==============================================================================
apexlib.core.getNextNode = function(pStartElement, pNodeName)
{
  var vElement = pStartElement.nextSibling;
  if ((!vElement) || (vElement.nodeName == pNodeName))
  {
    return vElement;
  }
  else
  {
    // if a next element exist, check that
    if (vElement.nextSibling)
    {
      return apexlib.core.getNextNode(vElement, pNodeName);
    }
    else
    {
      // no previous element exists, continue with the parent
      if ((!pStartElement.parentNode) || (pStartElement.parentNode.nodeName == pNodeName))
      {
        return pStartElement.parentNode;
      }
      else
      {
        return apexlib.core.getNextNode(pStartElement.parentNode, pNodeName);
      }
    }
  }
}; // getNextNode

//==============================================================================
// Adds another event function (pFunction) to an element identified by pElement.
// pEvent is the name of the event, eg onchange, onload, ...
// pElement can be an object like a select list or an item identified by the id.
//
// If there is already an event function registered the new one will be
// executed before or after the existing one, as defined by pAddPosition.
//==============================================================================
apexlib.core.ADD_POSITION = {FIRST: 1, LAST: 2};
apexlib.core.addEvent = function(pEvent, pElement, pFunction, pAddPosition)
{
  // if it's an object, use the object otherwise look it up by id
  var vElement = null;
  switch(typeof(pElement))
  {
    case 'string': vElement = document.getElementById(pElement); break;
    case 'object': vElement = pElement; break;
    default:       vElement = false; break;
  }
  // first check if the element exists
  if (vElement)
  {
    var vOldOnChangeEvent = vElement[pEvent];
    // is there already an onchange event registered?
    if (typeof vOldOnChangeEvent == 'function')
    {
      if (pAddPosition == apexlib.core.ADD_POSITION.FIRST)
      {
        // execute our new one first and the old event one
        vElement[pEvent] = function(pEvent)
                           {
                             if (pFunction.apply(this, [pEvent]) == false) return false;
                             return vOldOnChangeEvent.apply(this, [pEvent]);
                           }
      }
      else
      {
        // first execute the old event and then our new one
        vElement[pEvent] = function(pEvent)
                           {
                             if (vOldOnChangeEvent.apply(this, [pEvent])==false) return false;
                             return pFunction.apply(this, [pEvent]);
                           }
      }
    }
    else
    {
      // no event registered, just assign our event function
      vElement[pEvent] = function(pEvent)
                         {
                           return pFunction.apply(this, [pEvent]);
                         }
    }
  }
  else
  {
    throw "Element '"+pElement+"' doesn't exist or doesn't have a "+pEvent+" handler!";
  }
}; // addEvent

//==============================================================================
// Adds a message to the message store sMessageList.
//==============================================================================
apexlib.core.addMessage = function(pName, pMessage)
{
  apexlib.core.sMessageList[pName.toUpperCase()] = pMessage;
}; // addMessage

//==============================================================================
// Reads a message from the message store sMessageList and replaces the
// placeholders %label and %0 - %4.
//==============================================================================
apexlib.core.getMessage = function(pName, pLabel, p0, p1, p2, p3, p4)
{
  if (!apexlib.core.sMessageList[pName.toUpperCase()]) return "Message "+pName+" not found!";

  var vResult = apexlib.core.sMessageList[pName.toUpperCase()];
  if (pLabel != null) vResult = vResult.replace(/\%label/gi, pLabel);
  if (p0 != null) vResult = vResult.replace(/\%0/g, p0);
  if (p1 != null) vResult = vResult.replace(/\%1/g, p1);
  if (p2 != null) vResult = vResult.replace(/\%2/g, p2);
  if (p3 != null) vResult = vResult.replace(/\%3/g, p3);
  if (p4 != null) vResult = vResult.replace(/\%4/g, p4);
  return vResult;
}; // getMessage

//==============================================================================
// Throws and implementation error.
//==============================================================================
apexlib.core.raiseImplError = function(pMethodName, pError)
{
  throw(pMethodName+": "+pError);
}; // raiseImplError

//==============================================================================
// Copied from htmldb_html_elements.js, to have it available when running
// running against a pre-APEX 2.2
//==============================================================================
if (!window.$x)
{
  window.$x =
    function(pNd)
    {
      try{
        var node;
        switch(typeof (pNd)){
          case 'string':node = document.getElementById(pNd);break;
          case 'object':node = pNd;break;
          default:node = false;break;
        }
        if(node.nodeType == 1){return node;}else{return false;}
      }catch(e){return false;}
    };
}

//==============================================================================
// Replaced APEX methods of htmldb_html_elements.js
//==============================================================================
function doSubmit(r)
{
  var vDoSubmit = true;
  flowSelectAll();
  document.wwv_flow.p_request.value = r;
  // is there a registered onsubmit event? execute it first
  var vFunction = document.wwv_flow.onsubmit; // IE needs a variable
  if (typeof vFunction == "function")
  {
    if (vFunction(this)==false) {vDoSubmit = false;}
  }
  if (vDoSubmit)
  {
    document.wwv_flow.submit();
    return true;
  }
// PR: this doesnt work as expected, browser ends up displaying "false" instead
//     of just ending execution of function doSubmit.
//     -> happens when called in href of a HTML-Button (would work in onclick, though)
//  else
//  {
//    return false;
//  }
}; // doSubmit
//==============================================================================
//  PROJECT: ApexLib - The APEX Library (http://apexlib.soureforge.net/)
//
//  DESCRIPTION:
//
//    This JavaScript library contains method for conversion.
//
//  SUPPORTED APEX VERSIONS:
//
//    2.x
//
//  AUTHOR(S):
//
//    Patrick Wolf (http://inside-apex.blogspot.com/)
//
//  SVN HEADER:
//
//    $Id: ApexLib_Convert.js 383 2009-10-19 19:48:55Z praganitsch $
//
//==============================================================================

//==============================================================================
// Initialize the namespace for ApexLib.
//==============================================================================
if (!window.apexlib)  window.apexlib  = {};
if (!apexlib.convert) apexlib.convert = {};

//==============================================================================
// Global arrays initialized by setNlsData and used by different convert
// methods.
//==============================================================================
apexlib.convert.sMonthList        = {"JAN":{no:1,abbr:"Jan",txt:"January"},"FEB":{no:2,abbr:"Feb",txt:"February"},"MAR":{no:3,abbr:"Mar",txt:"March"},"APR":{no:4,abbr:"Apr",txt:"April"},"MAY":{no:5,abbr:"May",txt:"May"},"JUN":{no:6,abbr:"Jun",txt:"June"},"JUL":{no:7,abbr:"Jul",txt:"July"},"AUG":{no:8,abbr:"Aug",txt:"August"},"SEP":{no:9,abbr:"Sep",txt:"September"},"OCT":{no:10,abbr:"Oct",txt:"October"},"NOV":{no:11,abbr:"Nov",txt:"November"},"DEC":{no:12,abbr:"Dec",txt:"December"}};
apexlib.convert.sWeekdayList      = {"MON":{no:1,abbr:"Mon",txt:"Monday"},"TUE":{no:2,abbr:"Tue",txt:"Tuesday"},"WED":{no:3,abbr:"Wed",txt:"Wednesday"},"THU":{no:4,abbr:"Thu",txt:"Thursday"},"FRI":{no:5,abbr:"Fri",txt:"Friday"},"SAT":{no:6,abbr:"Sat",txt:"Saturday"},"SUN":{no:7,abbr:"Sun",txt:"Sunday"}};
apexlib.convert.sGroupSeperator   = ",";
apexlib.convert.sDecimalSeperator = ".";
apexlib.convert.sCurrencySign     = "$";

//==============================================================================
// Sets the NLS specific values which are used by different convert functions.
// Call this method if the default settings don't work out for your language.
//==============================================================================
apexlib.convert.setNlsData = function
  ( pMonthList
  , pWeekdayList
  , pGroupSeperator
  , pDecimalSeperator
  , pCurrencySign
  )
{
  apexlib.convert.sMonthList        = pMonthList;
  apexlib.convert.sWeekdayList      = pWeekdayList;
  apexlib.convert.sGroupSeperator   = pGroupSeperator;
  apexlib.convert.sDecimalSeperator = pDecimalSeperator;
  apexlib.convert.sCurrencySign     = pCurrencySign;
}; // setNlsData

//==============================================================================
// ltrim's strings
//==============================================================================
apexlib.convert.ltrim = function(pString)
{
  return String(pString).replace(/^\s+/,"");
}; // ltrim

//==============================================================================
// rtrim's strings
//==============================================================================
apexlib.convert.rtrim = function(pString)
{
  return String(pString).replace(/\s+$/,"");
}; // rtrim

//==============================================================================
// lpad's a string.
//==============================================================================
apexlib.convert.lpad = function(pValue, pLength, pChar)
{
  var vChar   = (pChar)?pChar:" ";
  var vResult = String(pValue).toString();
  while (vResult.length<pLength)
  {
    vResult = vChar+vResult;
  }
  return vResult;
}; // lpad

//==============================================================================
// lpad's a string.
//==============================================================================
apexlib.convert.rpad = function(pValue, pLength, pChar)
{
  var vChar   = (pChar)?pChar:" ";
  var vResult = String(pValue).toString();
  while (vResult.length<pLength)
  {
    vResult = vResult+vChar;
  }
  return vResult;
}; // rpad

//==============================================================================
// Replaces a character in a string at the specified position.
//==============================================================================
apexlib.convert.replaceChar = function(pString, pPosition, pChar)
{
  return pString.slice(0, pPosition) + pChar + pString.substr(pPosition+1);
}; // replaceChar

//==============================================================================
// Same as regular string replace function, but starts at specified position.
//==============================================================================
apexlib.convert.replaceAt = function(pString, pPosition, pSearchRegExp, pReplaceBy)
{
  var vString = pString.substr(pPosition);
  return pString.slice(0, pPosition) + vString.replace(pSearchRegExp, pReplaceBy);
}; // replaceAt

//==============================================================================
// Returns true if the pString is null, "" or just contains blanks.
//==============================================================================
apexlib.convert.isBlank = function(pString)
{
  if (!String(pString))                return true;
  if (!apexlib.convert.ltrim(pString)) return true;
  return false;
}; // isBlank

//==============================================================================
// The Oracle TO_DATE function is really flexible and can handle a lot of wrong
// data entered by the user. Let's try at least to handle the most common cases.
// Known restriction: doesn't support years below 1 and BC
//==============================================================================
apexlib.convert.toDate = function(pDateString, pFormatMask)
{
  // change all seperators to an underscore
  function prepareString(pString)
  {
    var vString = pString;
    vString = vString.toUpperCase();
    vString = apexlib.convert.ltrim(apexlib.convert.rtrim(vString));
    vString = vString.replace(/AM/,"_AM");  // just be sure that there is a seperator
    vString = vString.replace(/PM/,"_PM");  // just be sure that there is a seperator
    vString = vString.replace(/\s/g, "_");  // replace space
    vString = vString.replace(/-/g, "_");   // replace -
    vString = vString.replace(/\//g, "_");  // replace /
    vString = vString.replace(/:/g, "_");   // replace :
    vString = vString.replace(/\./g, "_");  // replace .
    vString = vString.replace(/\_+/g, "_"); // replace duplicates
    return vString;
  }; // prepareString

  // checks if the string is integer and verifies that it is in the range specified
  function parseNumber(pString, pMinNumber, pMaxNumber)
  {
    var vNumber = parseInt(pString, 10);
    if (vNumber != pString)   throw("no number");
    if (vNumber < pMinNumber) throw("below min");
    if (vNumber > pMaxNumber) throw("above max");
    return vNumber;
  }; // parseNumber

  //----------------------------------------------------------------------------
  // M A I N
  //----------------------------------------------------------------------------
  // check if we have to do someting
  if ((apexlib.convert.isBlank(pDateString)) ||
      (apexlib.convert.isBlank(pFormatMask))) return null;
  // seperate the values into lists
  var vFormatMaskList = prepareString(pFormatMask).split("_");
  var vDateStringList = prepareString(pDateString).split("_");
  var vDays           = null;
  var vMonths         = null;
  var vYears          = null;
  var vHours          = null;
  var vMinutes        = null;
  var vSeconds        = null;
  // first simple check, entered value can't have more tokens
  if (vDateStringList.length > vFormatMaskList.length) return null;
  // loop through all tokens of the entered value
  for (var i=0; i < vDateStringList.length; i++)
  {
    try
    {
      switch(vFormatMaskList[i])
      {
        case "DD":   vDays    = parseNumber(vDateStringList[i], 1, 31);        break;
        case "MM":   vMonths  = parseNumber(vDateStringList[i], 1, 12);        break;
        case "MON":  if (!apexlib.convert.sMonthList[vDateStringList[i]]) return null; // month doesn't exist
                     vMonths  = apexlib.convert.sMonthList[vDateStringList[i]].no; break;
        case "HH":   vHours   = parseNumber(vDateStringList[i], 1, 12);        break;
        case "HH24": vHours   = parseNumber(vDateStringList[i], 0, 23);        break;
        case "MI":   vMinutes = parseNumber(vDateStringList[i], 0, 59);        break;
        case "SS":   vSeconds = parseNumber(vDateStringList[i], 0, 59);        break;
        default:
          if ((vFormatMaskList[i]=="YYYY") || (vFormatMaskList[i]=="YY") ||
              (vFormatMaskList[i]=="RRRR") || (vFormatMaskList[i]=="RR"))
          {
            // convert to 4-digit year if necessary
            if (vDateStringList[i].length <= 2)
            {
              vYears = parseNumber(vDateStringList[i], 0, 99);
              // a quick and dirty RR function => 1900/2000 shouldn't be hardcoded...
              if (vYears >= 50) { vYears = 1900 + vYears; }
              else { vYears = 2000 + vYears; }
            }
            else
            {
              vYears = parseNumber(vDateStringList[i], 1, 9999);
            }
          }
          else if ((vFormatMaskList[i]=="AM") || (vFormatMaskList[i]=="PM"))
               {
                 if ((vDateStringList[i] != "AM") && (vDateStringList[i] != "PM")) throw("wrong date");
                 if ((vDateStringList[i] == "PM") && (vHours < 12)) vHours=vHours+12;
                 if ((vDateStringList[i] == "AM") && (vHours ==12)) vHours=vHours-12;
               }
               else
               {
                 alert("getDateFromString: unsupported format mask "+vFormatMaskList[i]);
                 return null;
               }
          break;
      }
    }
    catch (vError) { return null; } // a convertion error has occurred
  }
  // check for valid months
  if (vMonths==2)
  {
    // check for leap year
    if (((vYears % 4 == 0) && (vYears % 100 != 0)) || (vYears % 400 == 0))
    { // leap year
      if (vDays > 29) return null;
    }
    else
    {
      if (vDays > 28) return null;
    }
  }
  else if ((vMonths==4)||(vMonths==6)||(vMonths==9)||(vMonths==11))
  {
    if (vDays > 30) return null;
  }
  // Fix for date formats without days; thanks to Marcio Nemec
  if((vMonths>0&&vDays==null)||(vYears>0&&vDays==null))
  {
    vDays = 1;
  }
  // change month to a javascript month (starting with 0)
  vMonths = vMonths-1;
  //
  return new Date(vYears, vMonths, vDays, vHours, vMinutes, vSeconds);
}; // toDate

//==============================================================================
// Returns true if the date string matches to the pFormatMask (has to be a
// valid Oracle date format mask!)
// Returns true if pDateString is null or blank.
//==============================================================================
apexlib.convert.isDate = function(pDateString, pFormatMask)
{
  if (!apexlib.convert.isBlank(pDateString))
  {
    if (apexlib.convert.toDate(pDateString, pFormatMask)) return true;
    return false;
  }
  else
  { // a blank or null date string is considered as valid
    return true;
  }
}; // isDate

//==============================================================================
// The Oracle TO_NUMBER function is really flexible and can handle a lot of wrong
// data entered by the user. Let's try at least to handle the most common cases.
// But the current implementation is very simple, eg. it can't handle if the
// user just enters 123.5 but the format mask is 990D00 => the second decimal
// digit is missing
//==============================================================================
apexlib.convert.toNumber = function(pNumberString, pFormatMask)
{
  // remove some values JavaScript Number convertion can't handle
  function prepareString(pString)
  {
    var vString = pString;
    vString = vString.replace(/\$/,  ""); // remove currency sign
    vString = vString.replace(new RegExp("\\"+apexlib.convert.sCurrencySign),        "");  // remove currency sign
    vString = vString.replace(new RegExp("\\"+apexlib.convert.sGroupSeperator, "g"), "");  // remove group seperator
    vString = vString.replace(new RegExp("\\"+apexlib.convert.sDecimalSeperator),    "."); // change decimal seperator to a point
    vString = apexlib.convert.ltrim(apexlib.convert.rtrim(vString));
    return vString;
  }; // prepareString
  //----------------------------------------------------------------------------
  // M A I N
  //----------------------------------------------------------------------------

  // check if we have to do someting
  if ((apexlib.convert.isBlank(pNumberString)) ||
      (apexlib.convert.isBlank(pFormatMask))) return null;
  // prepare the string
  var vNumberString   = prepareString(pNumberString);
  var vFormatedString = null;
  var vNumber         = null;
  var vPos            = vNumberString.indexOf("-");
  // Ask JavaScript if it's a valid number
  if (vPos == -1)
  {
    vNumber = Number(vNumberString);
  }
  else
  { // JavaScript needs the minus always at the beginning
    vNumberString = "-"+
                    apexlib.convert.ltrim(apexlib.convert.rtrim(
                    apexlib.convert.replaceChar(vNumberString, vPos, "")));
    vNumber = Number(vNumberString);
  }
  if (isNaN(vNumber)) return null;
  // Ok, at least JavaScript thinks it's a valid number, but posibilities are high
  // that it doesn't match our format mask
  vFormatedString = prepareString(apexlib.convert.toString(vNumber, pFormatMask));
  // remove minus sign, because it can be in front or at the end
  vNumberString   = vNumberString.replace(/\-/, "");
  vFormatedString = vFormatedString.replace(/\-/, "");
  // if we have decimal digits, rpad the compare string with the same number of digits
  if (vFormatedString.indexOf(".")>=0)
  {
    vPos = vNumberString.indexOf(".");
    if (vPos==-1)
    {
      // add a trailing decimal point if it doesn't exist yet
      vNumberString = vNumberString+".";
      vPos = vNumberString.length - 1;
    }
    // calculate the number of decimal digits which are required
    var vRequiredDecimalDigits = vFormatedString.length - vFormatedString.indexOf(".") - 1;
    var vHasDecimalDigits      = vNumberString.length - vPos - 1;
    if (vRequiredDecimalDigits > vHasDecimalDigits)
    {
      vNumberString = apexlib.convert.rpad
                        ( vNumberString
                        , vNumberString.length+(vRequiredDecimalDigits-vHasDecimalDigits)
                        , "0"
                        );
    }

  }
  // only if the results are matching, the format seems to be equal
  if (vFormatedString == vNumberString) return vNumber;
  // format mask doesn't match
  return null;
}; // toNumber

//==============================================================================
// Returns true if the number string matches to the pFormatMask (has to be a
// valid Oracle number format mask!)
// Returns true if pNumberString is null or blank.
//==============================================================================
apexlib.convert.isNumber = function(pNumberString, pFormatMask)
{
  if (!apexlib.convert.isBlank(pNumberString))
  {
    if (apexlib.convert.toNumber(pNumberString, pFormatMask) != null) return true;
    return false;
  }
  else
  { // a blank or null number string is considered as valid
    return true;
  }
}; // isNumber

//==============================================================================
// Converts a date or number into a string with the supplied format mask.
// For restrictions see toStringNumber and toStringDate.
//==============================================================================
apexlib.convert.toString = function(pValue, pFormatMask)
{
  if (pValue instanceof Date)
  {
    return apexlib.convert.toStringDate(pValue, pFormatMask);
  }
  else if (typeof(pValue) == "number")
  {
    return apexlib.convert.toStringNumber(pValue, pFormatMask);
  }
  else if (typeof(pValue) == "undefined" || pValue == null)
  {
    return pValue;
  }
  // default object convertion
  return pValue.toString();
}; // toString

//==============================================================================
// Converts a date into a string with the supplied format mask.
//==============================================================================
apexlib.convert.toStringDate = function(pDate, pFormatMask)
{
  var vResult = pFormatMask;
  // check if we have to do someting or
  // no format mask is specified, use JavaScript default toString
  if ((!pDate) ||
      (apexlib.convert.isBlank(pFormatMask))) return pDate.toString();
  var vYear      = String(pDate.getFullYear());
  var vMonth     = pDate.getMonth()+1;
  var vMonthAbbr = null;
  // replace the date placeholders
  vResult = vResult.replace(/DD/i, apexlib.convert.lpad(pDate.getDate(), 2, "0"));
  vResult = vResult.replace(/MM/i, apexlib.convert.lpad(vMonth, 2, "0"));
  if (vResult.search(/MON/i) >= 0)
  {
    for (var vIdx in apexlib.convert.sMonthList)
    {
      if (apexlib.convert.sMonthList[vIdx].no == vMonth)
      {
        var vPos = vResult.search(/MON/i);
        switch (vResult.substr(vPos, 3))
        {
          case "MON": vMonthAbbr = apexlib.convert.sMonthList[vIdx].abbr.toUpperCase(); break;
          case "Mon": vMonthAbbr = apexlib.convert.sMonthList[vIdx].abbr;               break;
          default:    vMonthAbbr = apexlib.convert.sMonthList[vIdx].abbr.toLowerCase(); break;
        }
        vResult = vResult.replace(/MON/i, vMonthAbbr);
      }
    }
  }
  vResult = vResult.replace(/YYYY/i, vYear);
  vResult = vResult.replace(/RRRR/i, vYear);
  vResult = vResult.replace(/YY/i,   vYear.substring(2, 4));
  vResult = vResult.replace(/RR/i,   vYear.substring(2, 4));
  vResult = vResult.replace(/HH24/i, apexlib.convert.lpad(pDate.getHours(), 2, "0"));
  if (pDate.getHours() > 12)
  {
    vResult = vResult.replace(/HH/i, apexlib.convert.lpad(pDate.getHours()-12, 2, "0"));
  }
  else if (pDate.getHours() < 1)
  {
    vResult = vResult.replace(/HH/i, apexlib.convert.lpad(pDate.getHours()+12, 2, "0"));
  }
  else
  {
    vResult = vResult.replace(/HH/i, apexlib.convert.lpad(pDate.getHours(), 2, "0"));
  }
  if (pDate.getHours() >= 12)
  {
    vResult = vResult.replace(/AM/i, "PM");
  }
  else
  {
    vResult = vResult.replace(/PM/i, "AM");
  }
  vResult = vResult.replace(/MI/i,   apexlib.convert.lpad(pDate.getMinutes(), 2, "0"));
  vResult = vResult.replace(/SS/i,   apexlib.convert.lpad(pDate.getSeconds(), 2, "0"));
  return vResult;
}; // toStringDate

//==============================================================================
// Converts a number into a string with the supplied format mask.
// Known restrictions: Number - Doesn't round as Oracle does for decimal digits
//                              where there is no format mask position.
//                              Doesn't handle a leading 0 format masks
//                              with 099 => 000 works.
//==============================================================================
apexlib.convert.toStringNumber = function(pNumber, pFormatMask)
{
  var vResult = pFormatMask;
  // if no format mask is specified, use JavaScript default toString
  if (apexlib.convert.isBlank(pFormatMask)) return pNumber.toString();
  var vNumberString = pNumber.toString();
  var vNumberPos    = null;
  var vResultPos    = null;
  vResult = vResult.replace(/FM/,  ""); // we don't care about FM
  // prepare string for later, to identify 0 and 9 positions
  vResult = vResult.replace(/0/gi,  "?");
  vResult = vResult.replace(/9/gi,  "_");
  // handle this old type of format mask
  vResult = vResult.replace(/\,/gi, "G");
  vResult = vResult.replace(/\./gi, "D");
  // is the number negative?
  if (pNumber < 0)
  {
    if (vResult.search(/MI/gi) >= 0)
    {
      vResult = vResult.replace(/MI/gi, "-");
    }
    else
    {
      vResult = "-" + vResult;
    }
    // we don't want to have the minus for further processing
    vNumberString = (pNumber*-1).toString();
  }
  vResult = vResult.replace(/MI/gi, "");
  //--------------------------------------------------------------------------
  // fill decimal digits
  //--------------------------------------------------------------------------
  vResult    = vResult.replace(/D/gi, apexlib.convert.sDecimalSeperator);
  vNumberPos = vNumberString.indexOf(".");
  vResultPos = vResult.indexOf(apexlib.convert.sDecimalSeperator);
  if ((vNumberPos >= 0) && (vResultPos >= 0))
  {
    for (var i=vNumberPos, j=vResultPos; ((i < vNumberString.length) && (j < vResult.length)); i++, j++)
    {
      if ((vResult.charAt(j)=="?") || (vResult.charAt(j)=="_"))
      {
        vResult = apexlib.convert.replaceChar(vResult, j, vNumberString.charAt(i));
      }
    }
  }
  //--------------------------------------------------------------------------
  // fill digits before decimal point
  //--------------------------------------------------------------------------
  // remove the decimal digits
  if (vNumberPos >=  0) vNumberString = vNumberString.slice(0, vNumberPos);
  if (vResultPos == -1)
  {
    vResultPos = vResult.length;
    // if the last digit is a minus, we have to correct the starting position
    if (vResult.charAt(vResult.length-1)=="-") vResultPos = vResultPos - 1;
  }
  else
  {
    vResult = apexlib.convert.replaceAt(vResult, vResultPos, /\?/gi, "0"); // pad decimal with 0
  }
  // replace digits in format mask
  for (var i=vNumberString.length-1, j=vResultPos-1; ((i >= 0) && (j >= 0)); i--, j--)
  {
    // replace the group seperator
    if (vResult.charAt(j) == "G")
    {
      vResult = apexlib.convert.replaceChar(vResult, j, apexlib.convert.sGroupSeperator);
      j--; // an extra decrement because of the group seperator
    }
    if ((vResult.charAt(j)=="?") || (vResult.charAt(j)=="_"))
    {
      vResult = apexlib.convert.replaceChar(vResult, j, vNumberString.charAt(i));
    } // has the format mask ended before we could replace all values?
    else if (j==0) return vResult = "###";
  }
  // clean up 9, 0 and group seperator placeholders
  vResult = vResult.replace(/\_/gi, "");  // remove 9
  // if there are any 0 placeholders left, we will fill them and the group seperator
  // up, assuming that the format mask is 000G000 and not 999G000 which would
  // lead to a wrong group seperator
  if (vResult.indexOf("?") >= 0)
  {
    vResult = vResult.replace(/\?/gi, "0"); // pad with 0
    vResult = vResult.replace(/G/gi, apexlib.convert.sGroupSeperator);
  }
  vResult = vResult.replace(/G/gi, "");
  // replace the currency sign
  vResult = vResult.replace(/L/i, apexlib.convert.sCurrencySign);
  // finally the result!!!
  return vResult;
}; // toStringNumber
//==============================================================================
//  PROJECT: ApexLib - The APEX Library (http://apexlib.soureforge.net/)
//
//  DESCRIPTION:
//
//    This JavaScript library contains methods for field handling.
//
//  SUPPORTED APEX VERSIONS:
//
//    2.x
//
//  REFERENCED OTHER LIBRARIES:
//
//    ApexLib_Core.js
//
//  CODE USED FROM SOMEWHERE ELSE:
//
//    AJAX and HTML DB: 2. The active form (in german)
//    (http://www.oracle.com/global/de/community/tipps/ajax-02/index.html)
//
//  AUTHOR(S):
//
//    Patrick Wolf (http://inside-apex.blogspot.com/)
//    Peter Raganitsch (http://www.oracle-and-apex.com/, http://blog.oracleapex.at/)
//
//  SVN HEADER:
//
//    $Id: ApexLib_Field.js 383 2009-10-19 19:48:55Z praganitsch $
//
//==============================================================================

//==============================================================================
// Initialize the namespace for ApexLib.
//==============================================================================
if (!window.apexlib) window.apexlib = {};
if (!apexlib.field)  apexlib.field  = {};

//==============================================================================
// Global array used by the different field methods.
//==============================================================================
apexlib.field.sDependingFieldList = new Object(); // index: field id
                                                  // value: another array which is
                                                  // also index by depending field id
apexlib.field.sLovFieldList       = new Object(); // index: the field id using the lov
                                                  // value: object of type lovEntry
apexlib.field.sComputationFieldList= new Object();// index: the field id using the computation
                                                  // value: object of type computationEntry
apexlib.field.sFieldList          = new Object(); // index: field id
                                                  // value: object of type FieldEntry
apexlib.field.sLabelTemplateList  = new Object(); // index: sequence
                                                  // value: object of type LabelTemplateEntry
apexlib.field.sTabFormRowCount    = null;         // number of rows of the tabular form
apexlib.field.sTabFormRowCntArray = null;         // column used to count the tabular form
apexlib.field.sIsManualTabForm    = false;        // a manual tabform has field ids with
                                                  // the pattern f00_0, automatic tabforms
                                                  // have a pattern of f00_0000
//==============================================================================
// Object for a lov entry.
// Contains a reference to the definition of the column in the APEX repository
// and an array of field ids used by this lov in the where-clause.
//==============================================================================
apexlib.field.lovEntry = function
  ( pReferenceType
  , pReferenceId
  , pNullText
  , pNullValue
  , pAlwaysRefresh
  , pUsedFieldList
  )
{
  this.oReferenceType = pReferenceType;
  this.oReferenceId   = pReferenceId;
  this.oNullText      = pNullText;
  this.oNullValue     = pNullValue;
  this.oAlwaysRefresh = pAlwaysRefresh;
  this.oUsedFieldList = pUsedFieldList;
};

//==============================================================================
// Object for a computation entry.
// Contains a reference to the definition of the column in the APEX repository
// and an array of field ids used by this computations code.
//==============================================================================
apexlib.field.computationEntry = function
  ( pReferenceType
  , pReferenceId
  , pUsedFieldList
  )
{
  this.oReferenceType = pReferenceType;
  this.oReferenceId   = pReferenceId;
  this.oUsedFieldList = pUsedFieldList;
};

//==============================================================================
// Object for a field data entry
//==============================================================================
apexlib.field.FieldEntry = function
  ( pName
  , pId
  , pTabFormArray
  , pFieldType
  , pLabel
  , pLabelTemplateIdx
  , pIsRequired
  , pIgnoreValidation
  , pDataType
  , pFormatMask
  , pLovNullValue
  , pMinValue
  , pMaxValue
  , pIsInternal
  )
{
  this.oName             = pName;
  this.oId               = pId;
  this.oTabFormArray     = pTabFormArray;
  this.oFieldType        = pFieldType;
  this.oLabel            = pLabel;
  this.oLabelTemplateIdx = pLabelTemplateIdx;
  this.oIsRequired       = pIsRequired;
  this.oIgnoreValidation = pIgnoreValidation;
  this.oDataType         = pDataType; // S=String, N=Number, D=Date
  this.oFormatMask       = pFormatMask;
  this.oLovNullValue     = pLovNullValue;
  this.oMinValue         = pMinValue;
  this.oMaxValue         = pMaxValue;
  this.oIsInternal       = pIsInternal;
};

//==============================================================================
// Object for a template data entry
//==============================================================================
apexlib.field.LabelTemplateEntry = function
  ( pIndex
  , pBeforeLabel
  , pAfterLabel
  , pOnErrorBeforeLabel
  , pOnErrorAfterLabel
  )
{
  this.oIndex              = pIndex;
  this.oBeforeLabel        = pBeforeLabel;
  this.oAfterLabel         = pAfterLabel;
  this.oOnErrorBeforeLabel = pOnErrorBeforeLabel;
  this.oOnErrorAfterLabel  = pOnErrorAfterLabel;
};

//==============================================================================
// Adds field/column information to the apexlib.field.sFieldList array.
//==============================================================================
apexlib.field.addField = function
  ( pName
  , pId
  , pTabFormArray
  , pFieldType
  , pLabel
  , pLabelTemplateIdx
  , pIsRequired
  , pIgnoreValidation
  , pDataType
  , pFormatMask
  , pLovNullValue
  , pMinValue
  , pMaxValue
  )
{
  apexlib.field.sFieldList[pName] = new apexlib.field.FieldEntry
                                      ( pName
                                      , pId
                                      , pTabFormArray
                                      , pFieldType
                                      , pLabel
                                      , pLabelTemplateIdx
                                      , pIsRequired
                                      , pIgnoreValidation
                                      , pDataType
                                      , pFormatMask
                                      , pLovNullValue
                                      , pMinValue
                                      , pMaxValue
                                      , false // pIsInternal
                                      );
  // if it's a tabular form column we also add an entry which has the
  // tab form array name as indes
  if (pTabFormArray != "")
  {
    apexlib.field.sFieldList['_'+pTabFormArray] = new apexlib.field.FieldEntry
                                                    ( pName
                                                    , pId
                                                    , pTabFormArray
                                                    , pFieldType
                                                    , pLabel
                                                    , pLabelTemplateIdx
                                                    , pIsRequired
                                                    , pIgnoreValidation
                                                    , pDataType
                                                    , pFormatMask
                                                    , pLovNullValue
                                                    , pMinValue
                                                    , pMaxValue
                                                    , true // pIsInternal
                                                    );
    // if the number of rows of the tabular form hasn't been evaluated yet or
    // if it is 0 it's possible that the first column where we did the check
    // was hidden, so do it again
    if ((apexlib.field.sTabFormRowCount == null) || (apexlib.field.sTabFormRowCount == 0))
    {
      var vRow = 1;
      // are the column ids build by APEX or are they manually created
      if ($x(pTabFormArray+"_0001"))
      {
        apexlib.field.sIsManualTabForm = false;
        while ($x(pTabFormArray+"_"+apexlib.convert.lpad(vRow, 4, '0')))
        {
          vRow++;
        }
      }
      else
      {
        // if the tabular form is user generated, the columns will not have
        // leading zeros
        apexlib.field.sIsManualTabForm = true;
        while ($x(pTabFormArray+"_"+vRow))
        {
          vRow++;
        }
      }
      apexlib.field.sTabFormRowCount    = vRow-1;
      apexlib.field.sTabFormRowCntArray = pTabFormArray;
    }
  }
}; // addFieldEntry

//==============================================================================
// Adds field information to the apexlib.field.sFieldList array.
//==============================================================================
apexlib.field.addLabelTemplate = function
  ( pIndex
  , pBeforeLabel
  , pAfterLabel
  , pOnErrorBeforeLabel
  , pOnErrorAfterLabel
  )
{
  apexlib.field.sLabelTemplateList[pIndex] = new apexlib.field.LabelTemplateEntry
                                               ( pIndex
                                               , pBeforeLabel
                                               , pAfterLabel
                                               , pOnErrorBeforeLabel
                                               , pOnErrorAfterLabel
                                               );
}; // addLabelTemplateEntry

//==============================================================================
// Returns the TD node of the label which belongs to pFieldId
//==============================================================================
apexlib.field.getLabelTD = function(pFieldId)
{
  var vField   = $x(pFieldId);
  var vElement = null;
  // only if the field exists and is stored in our array for the known fields
  if ((vField) && (apexlib.field.sFieldList[pFieldId]))
  {
    // get the td element of the label
    return apexlibJquery('label[for='+pFieldId+']').parent()[0];
  }
}; // getLabelTD

//==============================================================================
// Displays the label with an inline error message.
// Or if no inline error text is specified, restores the original label.
// Note: This method only works for page items, not for tabular columns!
//       And the label has to be identified by an label tag. That means no
//       inline error for the templates "- select -" and "No Label"
//==============================================================================
apexlib.field.setLabel = function(pFieldId, pInlineErrorText)
{
  var vTdLabelElement = apexlib.field.getLabelTD(pFieldId);
  var vLabelElement   = null;
  var vField          = $x(pFieldId);
  var vBrElement      = null;
  var vTemplateEntry  = null;
  var vLabelText      = "";
  // not supported for tabular form columns
  if (apexlib.field.isTabFormColumn(pFieldId))
  {
    alert("setLabel not supported for tabular form columns!");
    return;
  }
  // only if the label TD exists and we have a label template
  if ((vTdLabelElement) && (apexlib.field.sFieldList[pFieldId].oLabelTemplateIdx))
  {
    // get the template data
    vTemplateEntry = apexlib.field.sLabelTemplateList[apexlib.field.sFieldList[pFieldId].oLabelTemplateIdx];
    // Create the new label text and replace the placeholders
    vLabelText = vTemplateEntry.oBeforeLabel +
                 apexlib.field.sFieldList[pFieldId].oLabel +
                 vTemplateEntry.oAfterLabel;
    // Add inline error if necessary
    if (pInlineErrorText)
    {
      vLabelText = vTemplateEntry.oOnErrorBeforeLabel +
                   vLabelText                         +
                   vTemplateEntry.oOnErrorAfterLabel;
    }
    vLabelText = vLabelText.replace(/#ERROR_MESSAGE#/gi,     pInlineErrorText);
    vLabelText = vLabelText.replace(/#CURRENT_ITEM_NAME#/gi, pFieldId  );
    vLabelText = vLabelText.replace(/#CURRENT_ITEM_ID#/gi,   apexlib.field.sFieldList[pFieldId].oId);
    // is the label stored in the same TD as the field? eg. if above/below
    // then it gets a little bit more complicated...
    if (vTdLabelElement == apexlib.core.getParentNode(vField, "TD"))
    {
      // is the label above our item?
      vBrElement = apexlib.core.getPreviousNode(vField, "BR");
      if (vBrElement)
      {
        // remove everything before this BR, it belongs to the label
        // Or at least I hope so... ;-)
        while (vBrElement.previousSibling)
        {
          vBrElement.parentNode.removeChild(vBrElement.previousSibling);
        }
        // write the new text to the label. We can not just concatinate the
        // innserHTMLs, because in that case we would loose additionally
        // registered onchange events for the input element!
        vLabelElement = document.createElement("span");
        vLabelElement.innerHTML = vLabelText;
        vTdLabelElement.insertBefore(vLabelElement, vBrElement);
        return;
      }
      // is the label below our item?
      vBrElement = apexlib.core.getNextNode(vField, "BR");
      if (vBrElement)
      {
        // remove everything after this BR, it belongs to the label
        while (vBrElement.nextSibling)
        {
          vBrElement.parentNode.removeChild(vBrElement.nextSibling);
        }
        // write the new text to the label. We can not just concatinate the
        // innserHTMLs, because in that case we would loose additionally
        // registered onchange events for the input element!
        vLabelElement = document.createElement("span");
        vLabelElement.innerHTML = vLabelText;
        vTdLabelElement.appendChild(vLabelElement);
        return;
      }
    }
    else
    {
      // write the new text to the label
      vTdLabelElement.innerHTML = vLabelText;
    }
  }
}; // setLabel

//==============================================================================
// Replaces the placeholder %label with the label of the field/column.
//==============================================================================
apexlib.field.replaceLabelPlaceholder = function(pFieldId, pText)
{
  // return if pText is null or if this field isnt managed by ApexLib
  if (!pText || !apexlib.field.isField(pFieldId)) return pText;
  // Replace the placeholder with the stored label
  return pText.replace(/\%label/gi, apexlib.field.getField(pFieldId).oLabel);
}; // replaceLabelPlaceholder

//==============================================================================
// Sets the value of a field/column. If the field/column is in our array of
// known fields the format mask defined for the field is used to set the value
// defined by pValue. pValue has to be the matching data type for the
// format mask. eg Date or nummber
//==============================================================================
apexlib.field.setValue = function(pFieldId, pValue)
{
  var vField      = $x(pFieldId);
  var vFieldEntry = apexlib.field.getField(pFieldId);
  // does the field exist
  if (!vField)
  {
    apexlib.core.raiseImplError
      ( "setValue"
      , "pFieldId '"+pFieldId+"' does not exist!"
      );
  }
  // set value
  vField.value = apexlib.convert.toString(pValue, vFieldEntry.oFormatMask);
}; // setValue

//==============================================================================
// Returns true if the pFieldId matches to the pattern of a tabular form column.
//==============================================================================
apexlib.field.isTabFormColumn = function(pFieldId)
{
  // did we get a field?
  if (!pFieldId) return false;
  // look for the pattern f00_0000 or f00_0 and just contains this pattern
  if (pFieldId.search(/^f\d{2}\_\d{1,4}$/)==0) return true;
  return false;
}; // isTabFormColumn

//==============================================================================
// Returns the row number of a tabular form field id.
//==============================================================================
apexlib.field.getTabFormRow = function(pFieldId)
{
  if (apexlib.field.isTabFormColumn(pFieldId))
  {
    return parseInt(pFieldId.substr(4), 10);
  }
  return null;
}; // getTabFormRow

//==============================================================================
// Returns the index for accessing the sFieldList array.
// If pFieldId is a tabular form cell, the tabular form array name is returned.
//==============================================================================
apexlib.field.getFieldListIdx = function(pFieldId)
{
  // look for the pattern f00_0000 or f00_0 and just contains this pattern
  if (pFieldId.search(/^f\d{2}\_\d{1,4}$/)==0) return "_"+pFieldId.substr(0, 3);
  return pFieldId;
}; // getFieldListIdx

//==============================================================================
// Returns the field id of a tabular form column, identified by the name and
// the row number.
//==============================================================================
apexlib.field.getTabFormFieldId = function(pColumnName, pRow)
{
  if (apexlib.field.sIsManualTabForm)
  {
    return apexlib.field.sFieldList[pColumnName].oTabFormArray+"_"+pRow;
  }
  else
  {
    return apexlib.field.sFieldList[pColumnName].oTabFormArray+"_"+
           apexlib.convert.lpad(pRow, 4, '0');
  }
}; // getTabFormFieldId

//==============================================================================
// Returns true if pFieldId is a Field managed by ApexLib
//==============================================================================
apexlib.field.isField = function(pFieldId)
{
  return apexlib.field.sFieldList[apexlib.field.getFieldListIdx(pFieldId)]?true:false;
}; // isField

//==============================================================================
// Returns the index for accessing the sFieldList array.
// If pFieldId is a tabular form cell, the tabular form array name is returned.
//==============================================================================
apexlib.field.getField = function(pFieldId)
{
  var vFieldEntry = apexlib.field.sFieldList[apexlib.field.getFieldListIdx(pFieldId)];
  // Has it been a valid field?
  if (!vFieldEntry)
  {
    apexlib.core.raiseImplError
      ( "getField"
      , "pFieldId '"+pFieldId+"' does not exist!"
      );
  }
  return vFieldEntry;
}; // getField

//==============================================================================
// Executes the provided function for each tabular form cell of the specified
// pColumnName. this can be used by the called function to get the current cell
// object.
//==============================================================================
apexlib.field.forEachRow = function(pColumnName, pFunction)
{
  var vFieldEntry = apexlib.field.getField(pColumnName);
  // check if the tabular column has been rendered
  if (vFieldEntry.oTabFormArray=="") return;
  // does the tabular form have at least one row?
  if (apexlib.field.sTabFormRowCount==0) return;
  if (!$x(apexlib.field.getTabFormFieldId(vFieldEntry.oName, 1))) return;
  // execute function for each tabular form cell
  for (var i=1; i<=apexlib.field.sTabFormRowCount; i++)
  {
    pFunction.apply($x(apexlib.field.getTabFormFieldId(vFieldEntry.oName, i)));
  }
}; // forEachRow

//==============================================================================
// Registers a list of fields as dependend from a field.
// A onchange event will be registered to clear and re-populate the depending
// fields if the field changes.
//
// pFieldId is the field id on which the depending fields are depending.
// pDependingFieldList is an array of depending field ids.
//==============================================================================
apexlib.field.registerDependingFields = function (pFieldId, pDependingFieldList)
{
  // only if the field really exists, it's possible that the field isn't
  // rendered because of a condition or authorization
  if (!$x(pFieldId)) return;

  // only create an onchange event for the field if
  // we havn't stored anything for the field yet (we don't wan't to have
  // multiple calls to clearDependingFields
  if (!apexlib.field.sDependingFieldList[pFieldId])
  {
    apexlib.field.sDependingFieldList[pFieldId] = new Array();
    apexlib.core.addEvent
      ( "onchange"
      , pFieldId
      , function()
        { apexlib.field.clearDependingFields(pFieldId);
          apexlib.field.populateDependingLovs(pFieldId);
          apexlib.field.executeDependingComputations(pFieldId);
        }
      , apexlib.core.ADD_POSITION.LAST
      );
  }
  // loop thru all the depending fields and register them
  for (var ii in pDependingFieldList)
  {
    // no dependencies onto itself allowed!
    if (pDependingFieldList[ii] == pFieldId)
    {
      apexlib.core.raiseImplError
        ( "registerDependingFields"
        , "Field '"+pFieldId+"' can't have a dependency to itself!"
        );
    }
    // only if the field hasn't been registered yet
    if (!apexlib.field.sDependingFieldList[pFieldId][pDependingFieldList[ii]])
    {
      apexlib.field.sDependingFieldList[pFieldId][pDependingFieldList[ii]] = pDependingFieldList[ii];
    }
  }
}; // registerDependingFields

//==============================================================================
// Registers a lov field.
// This will also automatically create a dependency between the lov field and
// the used fields. If a used field is changed the lov field will be cleared
// and an AJAX call will be issued to re-populate the lov.
//
// pFieldId is the field id to which the lov is assigned to.
// pReferenceType identifies of which type the pReferenceId is.
// Valid values are "ITEM" or "REP_COLUMN".
//
// pUsedFieldList is an array of field ids the lov is referencing.
//==============================================================================
apexlib.field.registerLovField = function(pFieldId,pReferenceType,pReferenceId,pNullText,pNullValue,pAlwaysRefresh,pUsedFieldList)
{
  // check if it is a valid reference type
  if ((pReferenceType == "ITEM" || pReferenceType == "REP_COLUMN") == false)
  {
    apexlib.core.raiseImplError
      ( "registerLovField"
      , "pReferenceType '"+pReferenceType+"' is a not supported reference type!"
      );
  }
  // only if no lov has been registered for this field yet
  if (!apexlib.field.sLovFieldList[pFieldId])
  {
    apexlib.field.sLovFieldList[pFieldId] = new apexlib.field.lovEntry
                                              ( pReferenceType
                                              , pReferenceId
                                              , pNullText
                                              , pNullValue
                                              , pAlwaysRefresh
                                              , pUsedFieldList
                                              );
    // loop thru all the depending fields and register them
    for (var ii in pUsedFieldList)
    {
      apexlib.field.registerDependingFields(pUsedFieldList[ii], new Array(pFieldId));
    }
  }
}; // registerLovField

//==============================================================================
// Registers a computation field.
// This will also automatically create a dependency between the computed field and
// the used fields. If a used field is changed the field will be cleared
// and an AJAX call will be issued to re-execute the computation.
//
// pFieldId is the field id to which the computation is assigned to.
// pReferenceType identifies of which type the pReferenceId is.
// Valid values are "ITEM" or "REP_COLUMN".
//
// pUsedFieldList is an array of field ids the computation is referencing.
//==============================================================================
apexlib.field.registerComputationField = function(pFieldId,pReferenceType,pReferenceId,pUsedFieldList)
{
  // check if it is a valid reference type
  if ((pReferenceType == "COMPUTATION") == false)
  {
    apexlib.core.raiseImplError
      ( "registerComputationField"
      , "pReferenceType '"+pReferenceType+"' is a not supported reference type!"
      );
  }
  // only if no computation has been registered for this field yet
  if (!apexlib.field.sComputationFieldList[pFieldId])
  {
    apexlib.field.sComputationFieldList[pFieldId] = new apexlib.field.computationEntry
                                              ( pReferenceType
                                              , pReferenceId
                                              , pUsedFieldList
                                              );
    // loop thru all the depending fields and register them
    for (var ii in pUsedFieldList)
    {
      apexlib.field.registerDependingFields(pUsedFieldList[ii], new Array(pFieldId));
    }
  }
}; // registerComputationField

//==============================================================================
// Clears all depending fields (and their depending fields) of pFieldId.
//==============================================================================
apexlib.field.clearDependingFields = function(pFieldId)
{
  // do depending fields exist for this field id?
  if (apexlib.field.sDependingFieldList[pFieldId])
  {
    // loop thru all depending fields
    for (var ii in apexlib.field.sDependingFieldList[pFieldId])
    {
      var vClearFieldId = apexlib.field.sDependingFieldList[pFieldId][ii];
      var vClearField = $x(vClearFieldId);
      // only if the field really exists (may not be rendered because of a
      // condition or authorization
      if (vClearField) vClearField.value = "";
      // do a recursive call so that the depending fields of the cleared
      // field are also cleared
      apexlib.field.clearDependingFields(vClearFieldId);
    }
  }
}; // clearDependingFields

//==============================================================================
// Populate all lovs of the depending fields (and there depending fields)
// of pFieldId.
//==============================================================================
apexlib.field.populateDependingLovs = function(pFieldId)
{
  // do depending fields exist for this field id?
  if (apexlib.field.sDependingFieldList[pFieldId])
  {
    // loop thru all depending fields
    for (var ii in apexlib.field.sDependingFieldList[pFieldId])
    {
      vLovFieldId = apexlib.field.sDependingFieldList[pFieldId][ii];
      // is a lov assigned to this field?
      if (apexlib.field.sLovFieldList[vLovFieldId])
      {
        apexlib.field.populateLovField
          ( vLovFieldId
          , apexlib.field.sLovFieldList[vLovFieldId]
          );
        // do a recursive call so that the depending fields of the cleared
        // field are also cleared
        apexlib.field.populateDependingLovs(vLovFieldId);
      }
    }
  }
}; // populateDependingLovs

//==============================================================================
// execute all computations of the depending fields (and there depending fields)
// of pFieldId.
//==============================================================================
apexlib.field.executeDependingComputations = function(pFieldId)
{
  // do depending fields exist for this field id?
  if (apexlib.field.sDependingFieldList[pFieldId])
  {
    // loop thru all depending fields
    for (var ii in apexlib.field.sDependingFieldList[pFieldId])
    {
      vComputationFieldId = apexlib.field.sDependingFieldList[pFieldId][ii];
      // is a computation assigned to this field?
      if (apexlib.field.sComputationFieldList[vComputationFieldId])
      {
        apexlib.field.executeComputation
          ( vComputationFieldId
          , apexlib.field.sComputationFieldList[vComputationFieldId]
          );
        // do a recursive call so that the depending fields of the cleared
        // field are also cleared
        apexlib.field.executeDependingComputations(vLovFieldId);
      }
    }
  }
}; // executeDependingComputations

//==============================================================================
// Populate the lov of a field by issueing a AJAX call to a generic
// on-demand process which returns the new values for the lov.
// pFieldId is the id of a "select-one" item.
// pLovEntry has to be of type lovEntry.
//==============================================================================
apexlib.field.populateLovField = function(pFieldId, pLovEntry)
{
  var vLovField    = $x(pFieldId);
  var vAjaxRequest = null;
  var vUsedField   = null;
  var vResult      = null;
  var vDoRefresh   = true;
  var vFieldValue  = null;
  // does the lov field really exist? It may have not been rendered because
  // of a condition or authorization
  if (!vLovField) return;

  // Is it ok if we just clear the lov if all used fields are null?
  if (!pLovEntry.oAlwaysRefresh)
  {
    // Check all fields if there is at least on value which is
    // null or equal to the lov null value, because in that case the
    // lov query will return nothing anyway.
    for (var ii in pLovEntry.oUsedFieldList)
    {
      vFieldValue = $x(pLovEntry.oUsedFieldList[ii]).value;
      //
      if ((vFieldValue == "") || (vFieldValue == pLovEntry.oNullValue))
      {
        vDoRefresh = false;
        break;
      }
    }
    // If no full refresh is required, just clear the lov and default it with
    // the null text and null value
    if (vDoRefresh == false && vLovField["type"].search("select") != -1)
    {
      vLovField.length = 0;
      if (pLovEntry.oNullText != "")
      {
        apexlib.field.addSelectOption
          ( vLovField
          , pLovEntry.oNullValue
          , pLovEntry.oNullText
          );
      }
      // we are done
      return;
    }
  }
  // If we are here we have to do an AJAX roundtrip to the database to get
  // the new lov values from there.
  vAjaxRequest = new htmldb_Get
                       ( null
                       , $x("pFlowId").value
                       , "APPLICATION_PROCESS=ApexLib_getGenericResult"
                       , $x("pFlowStepId").value
                       );
  vAjaxRequest.add("APEXLIB_ROUTINE_TYPE",   "LOV"                   );
  vAjaxRequest.add("APEXLIB_REFERENCE_TYPE", pLovEntry.oReferenceType);
  vAjaxRequest.add("APEXLIB_REFERENCE_ID",   pLovEntry.oReferenceId  );
  // send all fields and there current value, because they are used in the
  // where clause of the lov
  for (var ii in pLovEntry.oUsedFieldList)
  {
    vUsedField  = $x(pLovEntry.oUsedFieldList[ii]);
    vFieldValue = vUsedField.value;
    // if it is a multi select list, the selected values are not stored in the
    // value property, they are in the options
    if (vUsedField["type"] == "select-multiple")
    {
      vFieldValue = "";
      for (var jj=0; jj < vUsedField.options.length; jj++)
      {
        if (vUsedField.options[jj].selected)
        {
          vFieldValue = ((vFieldValue=="")?"":vFieldValue+":")+
                        vUsedField.options[jj].value;
        }
      }
    }
    vAjaxRequest.add(pLovEntry.oUsedFieldList[ii], vFieldValue.replace(/\:/g, String.fromCharCode(1)));
  }
  // send AJAX request
  vResult = vAjaxRequest.get("XML");
  if (vResult && vLovField["type"].search("select") != -1)
  {
    // reset the current options of the lov field
    vLovField.length = 0;
    // parse the result and add the values to the lov field
    var vCount = vResult.getElementsByTagName("option").length;
    for(var ii=0;ii<vCount;ii++)
    {
      var vOption = vResult.getElementsByTagName("option")[ii];
      apexlib.field.addSelectOption
        ( vLovField
        , vOption.getAttribute("value")
        , (vOption.firstChild?vOption.firstChild.nodeValue:"") /* IE: necessary if there is just a blank */
        );
    }
  }
}; // populateLovField

//==============================================================================
// Executes a computation by issueing a AJAX call to a generic on-demand process
// which returns the result of the computation, this is result is written to
// the given field.
// pFieldId is the id of the item to be computed.
// pComputationEntry has to be of type computationEntry.
//==============================================================================
apexlib.field.executeComputation = function(pFieldId, pComputationEntry)
{
  var vComputationField = $x(pFieldId);
  var vAjaxRequest      = null;
  var vUsedField        = null;
  var vResult           = null;
  var vDoRefresh        = true;
  var vFieldValue       = null;
  // does the field really exist? It may have not been rendered because
  // of a condition or authorization
  if (!vComputationField) return;

  // If we are here we have to do an AJAX roundtrip to the database to get
  // the new lov values from there.
  vAjaxRequest = new htmldb_Get
                       ( null
                       , $x("pFlowId").value
                       , "APPLICATION_PROCESS=ApexLib_getGenericResult"
                       , $x("pFlowStepId").value
                       );
  vAjaxRequest.add("APEXLIB_ROUTINE_TYPE",   "COMPUTATION"                   );
  vAjaxRequest.add("APEXLIB_REFERENCE_TYPE", pComputationEntry.oReferenceType);
  vAjaxRequest.add("APEXLIB_REFERENCE_ID",   pComputationEntry.oReferenceId  );
  // send all fields and their current value, because they are used in the
  // computation code
  for (var ii in pComputationEntry.oUsedFieldList)
  {
    vUsedField  = $x(pComputationEntry.oUsedFieldList[ii]);
    vFieldValue = vUsedField.value;
    // if it is a multi select list, the selected values are not stored in the
    // value property, they are in the options
    if (vUsedField["type"] == "select-multiple")
    {
      vFieldValue = "";
      for (var jj=0; jj < vUsedField.options.length; jj++)
      {
        if (vUsedField.options[jj].selected)
        {
          vFieldValue = ((vFieldValue=="")?"":vFieldValue+":")+
                        vUsedField.options[jj].value;
        }
      }
    }
    vAjaxRequest.add(pComputationEntry.oUsedFieldList[ii], vFieldValue.replace(/\:/g, String.fromCharCode(1)));
  }
  // send AJAX request
  vResult = vAjaxRequest.get("XML");
  if (vResult)
  {
    // parse the result and set the computed field to the new value
    var vCount = vResult.getElementsByTagName("result").length;
    for(var ii=0;ii<vCount;ii++)
    {
      var vComputationResult = vResult.getElementsByTagName("result")[ii];
      $s(pFieldId,(vComputationResult.firstChild?vComputationResult.firstChild.nodeValue:""));
    }
    // parse the result and check for exceptions
    var vCount = vResult.getElementsByTagName("exception").length;
    for(var ii=0;ii<vCount;ii++)
    {
      var vComputationError = vResult.getElementsByTagName("exception")[ii];
      // display error if we found one
      if (vComputationError.firstChild)
      {
        apexlib.error.showError
          ( "Computation"
          , pFieldId
          , vComputationError.firstChild.nodeValue
          );
      }
    }
  }
}; // executeComputation

//==============================================================================
// Adds a select option to a select field.
// pLovField references already the object of a "select-one" item.
//==============================================================================
apexlib.field.addSelectOption = function(pLovField, pInternalValue, pDisplayValue)
{
  var vNewOption   = document.createElement("option");
  vNewOption.value = pInternalValue;
  // special handling for IE
  if (document.all)
  {
    pLovField.options.add(vNewOption);
    vNewOption.innerText = pDisplayValue;
  }
  else
  {
    vNewOption.appendChild(document.createTextNode(pDisplayValue));
    pLovField.appendChild(vNewOption);
  }
}; // addSelectOption
//==============================================================================
//  PROJECT: ApexLib - The APEX Library (http://apexlib.soureforge.net/)
//
//  DESCRIPTION:
//
//    This JavaScript library contains methods for button handling.
//
//  SUPPORTED APEX VERSIONS:
//
//    2.x, 3.x
//
//  REFERENCED OTHER LIBRARIES:
//
//    -
//
//  CODE USED FROM SOMEWHERE ELSE:
//
//    -
//
//  AUTHOR(S):
//
//    Peter Raganitsch (http://www.oracle-and-apex.com/, http://blog.oracleapex.at/)
//
//  SVN HEADER:
//
//    $Id: $
//
//==============================================================================

//==============================================================================
// Initialize the namespace for ApexLib.
//==============================================================================
if (!window.apexlib) window.apexlib = {};
if (!apexlib.button) apexlib.button = {};

//==============================================================================
// Global array used by the different field methods.
//==============================================================================
//

//==============================================================================
// Registers a Hotkey/Shortcut for pressing a button.
// This function will add a given hotkey for a button, when user presses the
// Hotkey the button is clicked. This works for image buttons as well as for
// real HTML Buttons.
//
// pButtonName is the Name of the button
// pButtonId   is the ID of the button
// pHotkey     is a key or key combination, e.g. Ctrl+g or F1 or Shift+Alt+f
//
// Pre-Requirement is that Button can be found using Name or Id, that means
// the Template has to set an ID.
//
// If found element is a HTML-Link-Tag we add a span with a onclick event to be
// able to bind the Hotkey.
//==============================================================================
apexlib.button.registerHotkey = function(pButtonName,pButtonId,pHotkey)
{
  var vButton = apexlibJquery("#"+pButtonName+", #"+pButtonId);
  if (vButton.length==0)
  {
    apexlib.core.raiseImplError
      ( "registerHotkey"
      , "Button '"+pButtonName+"' not found, check your template if it contains an ID-Attribute!"
      );
  }
  if (vButton[0].tagName == "A")
  {
    vButton.parent().html("<span onclick=\""
                           +vButton.attr("href")//.replace("javascript:","")
                           +"\">"
                           +vButton.parent().html()
                           +"</span>");
//    vButton.bind("click",vButton.attr("href").replace("javascript:",""));
    vButton = apexlibJquery("#"+pButtonName+", #"+pButtonId).parent();
  }
  // bind hotkey on element and trigger "click"
  apexlibJquery(document).bind('keydown',pHotkey,function(){vButton.click();});
}; // registerLovField

//==============================================================================
//  PROJECT: ApexLib - The APEX Library (http://apexlib.soureforge.net/)
//
//  DESCRIPTION:
//
//    This JavaScript library contains method for error handling.
//
//  SUPPORTED APEX VERSIONS:
//
//    2.x
//
//  REFERENCED OTHER LIBRARIES:
//
//    ApexLib_Core.js
//    ApexLib_Field.js
//
//  AUTHOR(S):
//
//    Patrick Wolf (http://inside-apex.blogspot.com/)
//    Peter Raganitsch (http://www.oracle-and-apex.com/, http://blog.oracleapex.at/)
//
//  SVN HEADER:
//
//    $Id: ApexLib_Error.js 383 2009-10-19 19:48:55Z praganitsch $
//
//==============================================================================

//==============================================================================
// Initialize the namespace for ApexLib.
//==============================================================================
if (!window.apexlib) window.apexlib = {};
if (!apexlib.error)  apexlib.error  = {};

//==============================================================================
// Default error messages required by this module
// Overwrite them in Shared Components\Text Messages
//==============================================================================
apexlib.core.addMessage("APEXLIB_TABFORM_ROW_PREFIX", "Row \%0:");

//==============================================================================
// Global variables used by the different error handling methods.
//==============================================================================
apexlib.error.sMessagesClassName      = null;
apexlib.error.sNotificationClassName  = null;
apexlib.error.sTabFormBackgroundColor = null;
apexlib.error.sTabFormForegroundColor = null;
apexlib.error.sDisplayLocation        = null;

apexlib.error.sHighlightFieldList = new Object(); // index: field id
                                                  // value: original style info
apexlib.error.sRestoreCounter = 0;
apexlib.error.sRestoreList    = new Array(); // index: sequence
                                             // value: error name

apexlib.error.DISPLAY_LOCATION = { FIELD_AND_NOTIFICATION:1
                                 , FIELD:2
                                 , NOTIFICATION:3
                                 };
//==============================================================================
// Initializes the necessary global variables which are required by the
// apexlib.error module.
// For pMessagesClassName and pNotificationClassName the corresponding theme
// classes have to be set. eg: t12Messages and t12Notification
// pTabularFormBackgroundColor and pTabularFormForegroundColor are used to set
// the colors which are used if an error is displayed for a tabular form cell.
// pDisplayLocation is of type apexlib.error.DISPLAY_LOCATION
//==============================================================================
apexlib.error.init = function
  ( pMessagesClassName
  , pNotificationClassName
  , pTabularFormBackgroundColor
  , pTabularFormForegroundColor
  , pDisplayLocation
  )
{
  //----------------------------------------------------------------------------
  // Store the parameters for later usage in other methods
  //----------------------------------------------------------------------------
  apexlib.error.sMessagesClassName      = pMessagesClassName;
  apexlib.error.sNotificationClassName  = pNotificationClassName;
  apexlib.error.sTabFormBackgroundColor = pTabularFormBackgroundColor;
  apexlib.error.sTabFormForegroundColor = pTabularFormForegroundColor;
  apexlib.error.sDisplayLocation        = pDisplayLocation;
}; // initErrorHandling

//==============================================================================
// Extracts the error messages, stores them in a cookie and redirects to the
// previous displayed page. On that page the handleErrorPage is called
// in a onload event to process the error messages.
//==============================================================================
apexlib.error.extractErrorPage = function()
{
  var vElementList = null;
  var vErrorStack  = null;
  //----------------------------------------------------------------------------
  // Find our ErrorPageMessage and ApexLibErrorMessage div elements.
  // Extract and store them in a cookie
  //----------------------------------------------------------------------------
  vElementList = window.document.getElementsByTagName("div");
  for (var i=0; i < vElementList.length; i++)
  {
    if ( (vElementList[i].className == "ErrorPageMessage")   ||
         (vElementList[i].className == "ApexLibErrorMessage")
       )
    {
      vErrorStack = (vErrorStack == null?"":vErrorStack+"<br />")+
                     vElementList[i].innerHTML;
    }
  }
  // store the error message in a cookie
 var vCookieCount   = 0;
 var vCookieContent = null;
 while (vErrorStack != "")
 {
   vCookieCount++;
   vCookieContent = vErrorStack.substr(0, 4000);
   vErrorStack    = vErrorStack.substr(3999);
   document.cookie = "ApexLibErrorStack"+vCookieCount+"="+encodeURIComponent(vCookieContent);
 }
 //----------------------------------------------------------------------------
 // now go back the the previous page, on that page an onload event will check
 // if the cookie exists and paste it into the page.
 //----------------------------------------------------------------------------
 window.history.go(-1);
}; // extractErrorPage

apexlib.error.DISPLAY_LOCATION = { FIELD_AND_NOTIFICATION:1
                                 , FIELD:2
                                 , NOTIFICATION:3
                                 };
//==============================================================================
// Tries to read an error message from the cookie ApexLibErrorStack* which
// has been set by extractErrorMessage before.
// If it exists, it will paste the text into messages div.
//==============================================================================
apexlib.error.handleErrorPage = function()
{
  var vCookieList   = document.cookie;
  var vCookieCount  = 1;
  var vErrorStack   = "";
  var vErrorName    = null;
  var vErrorMessage = null;
  var vInlineError  = null;
  var vFieldId      = null;
  var vRow          = null;
  var vPos          = null;
  var vStart        = null;
  var vEnd          = null;
  //----------------------------------------------------------------------------
  // search for our ApexLibErrorMessage cookie
  //----------------------------------------------------------------------------
  vPos = vCookieList.indexOf("ApexLibErrorStack"+vCookieCount+"=");
  // Search as long as there are cookies with the name ApexLibErrorStack*=
  while (vPos != -1)
  {
    //
    vStart = vCookieList.indexOf("=", vPos)+1;
    vEnd   = vCookieList.indexOf(";", vStart);
    if (vEnd == -1) vEnd = vCookieList.length;
    vErrorStack = vErrorStack+decodeURIComponent(vCookieList.substring(vStart, vEnd));
    // remove the cookie
    document.cookie = "ApexLibErrorStack"+vCookieCount+"=; max-age=0";
    // is there another cookie?
    vCookieCount++;
    vPos = vCookieList.indexOf("ApexLibErrorStack"+vCookieCount+"=");
  }
  if (vErrorStack == "") return;

  // search for our message section by id or classname (eg. t7Messages)
  var vMessageArea = apexlibJquery('#'+apexlib.error.sMessagesClassName+',.'+apexlib.error.sMessagesClassName);
  if (vMessageArea[0])
  {
    vMessageArea.html('<div class="'+apexlib.error.sNotificationClassName+'">'+
                             vErrorStack+"</div>");
  }
  else
  {
    // no apexlib.error.sMessagesClassName div found?
    alert("Message section "+apexlib.error.sMessagesClassName+" not found. Error: "+vErrorStack);
    return;
  }
  //----------------------------------------------------------------------------
  // search for our just created ApexLibError spans, so that we are able to
  // extract them.
  //----------------------------------------------------------------------------
  var vErrorList = apexlib.core.getElementsByClassName("span", "ApexLibError", vMessageArea[0]);
  for (var i=0; i < vErrorList.length; i++)
  {
    vErrorMessage = vErrorList[i].innerHTML;
    vErrorName    = vErrorList[i].id;
    vFieldId      = vErrorList[i].getAttribute("apexlib:fieldid");
    vRow          = vErrorList[i].getAttribute("apexlib:row");
    vInlineError  = vErrorList[i].getAttribute("apexlib:inlineError");
    if (vInlineError == "") vInlineError = vErrorMessage;
    // Is it a single record field?
    if (!vRow)
    {
      // hide error in message area if the error should only be displayed
      // as inline field error. We are doing an extra call here, because the
      // the following call to apexlib.error.showError has pRemoveOldErrors=false
      // => if it's true we may would change the order of the error messages
      if (apexlib.error.sDisplayLocation == apexlib.error.DISPLAY_LOCATION.FIELD)
      {
        apexlib.error.removeError(vErrorName);
      }
      // show the error message as inline error message
      if (apexlib.error.sDisplayLocation != apexlib.error.DISPLAY_LOCATION.NOTIFICATION)
      {
        // pRemoveOldErrors is false, to be able to show multiple errors for
        // a field
        apexlib.error.showError(vErrorName, vFieldId, null, vInlineError, false);
      }
    }
    else
    {
      apexlib.error.showError(vErrorName, vFieldId, null, null, false);
    }
  }
}; // handleErrorPage

//==============================================================================
// Sets the color of a field and registers an onchange event to restore the
// original color when the field is changed
//==============================================================================
apexlib.error.highlightField = function
  ( pFieldId
  , pBackgroundColor
  , pForegroundColor
  )
{
  var vField = $x(pFieldId);
  // only if the item exists
  if (vField)
  {
    // store current value, but only if there is no pending highlighting for
    // this field, otherwise we would store the error columns!
    if (!apexlib.error.sHighlightFieldList[pFieldId])
    {
      apexlib.error.sHighlightFieldList[pFieldId] = vField.style.cssText;
      if (!apexlib.error.sHighlightFieldList[pFieldId])
      {
        apexlib.error.sHighlightFieldList[pFieldId] = "default";
      }
    }
    // change color of item
    vField.style.cssText = "background-color:"+pBackgroundColor+"; color:"+pForegroundColor;
  }
}; // highlightField

//==============================================================================
// Called by onchange to restore the original style infos of the item.
//==============================================================================
apexlib.error.restoreHighlightField = function(pFieldId)
{
  if (apexlib.error.sHighlightFieldList[pFieldId])
  {
    $x(pFieldId).style.cssText = apexlib.error.sHighlightFieldList[pFieldId];
    delete apexlib.error.sHighlightFieldList[pFieldId];
  }
}; // restoreHighlightField

//==============================================================================
// Displays an error in the message area (appends the error), but only if
// pShowErrorText is true and pErrorText is not null.
// If pInlineErrorText is not null, the error will also be displayed as
// inline error message for the field identified by pFieldId.
// pErrorName is a marker for the error in the message area and used to remove
// the error message later on when the user changes the field.
// Note: The registered on change event still be there if the error message
//       has been removed, because we can't restore the original onchange event
//       => someone could have added other functions.
//       But that shouldn't harm the system.
//==============================================================================
apexlib.error.showError = function(pErrorName, pFieldId, pErrorText, pInlineErrorText, pRemoveOldErrors)
{
  var vErrorName       = (!pErrorName)?"Error_"+pFieldId:pErrorName;
  var vErrorText       = apexlib.field.replaceLabelPlaceholder(pFieldId, pErrorText);
  var vInlineErrorText = apexlib.field.replaceLabelPlaceholder(pFieldId, pInlineErrorText);
  // remove old errors first
  if (pRemoveOldErrors != false) apexlib.error.removeError(vErrorName, pFieldId, null);
  //----------------------------------------------------------------------------
  // If an error text has been specified, display the error in the message area
  // But only if we havn't the setting "display inline error only" -> if we
  // have no pFieldId in that case we will still show it in the message area.
  //----------------------------------------------------------------------------
  if ((vErrorText) && ((apexlib.field.isTabFormColumn(pFieldId)) || (apexlib.error.sDisplayLocation != apexlib.error.DISPLAY_LOCATION.FIELD)))
  {
    var vErrorSection = '<span class="ApexLibError" id="'+vErrorName+'">';
    // search for our message section by id or classname (eg. t7Messages)
    var vMessageArea = apexlibJquery('#'+apexlib.error.sMessagesClassName+',.'+apexlib.error.sMessagesClassName);
    // add the row prefix if it's a tabular form error.
    if (apexlib.field.isTabFormColumn(pFieldId))
    {
      vErrorSection = vErrorSection +
                      apexlib.core.getMessage
                        ( "APEXLIB_TABFORM_ROW_PREFIX"
                        , null
                        , apexlib.field.getTabFormRow(pFieldId)
                        )+" ";
    }
    vErrorSection = vErrorSection + vErrorText + '</span>';
    // show error in dialog box if message area doesn't exist
    if (!vMessageArea[0])
    {
      // no pMessagesClassName div found! Show at least the error.
      alert("Message section "+apexlib.error.sMessagesClassName+" not found. Error: "+vErrorText);
      return;
    }
    // search for an existing notification area
    var vNotificationArea = apexlib.core.getElementByClassName
                              ( "div"
                              , apexlib.error.sNotificationClassName
                              , vMessageArea[0]
                              );
    // Does it already exist? Append error message
    if (vNotificationArea)
    {
      vNotificationArea.innerHTML = vNotificationArea.innerHTML +
                                    "<br />" +
                                    vErrorSection;
    }
    else
    {
      // Doesn't exist yet, let's create the notification area
      vMessageArea.html('<div class="'+apexlib.error.sNotificationClassName+'">'+
                               vErrorSection+"</div>");
    }
  }
  //----------------------------------------------------------------------------
  // Show an inline error message or highlight the tabular form cell.
  //----------------------------------------------------------------------------
  if (pFieldId)
  {
    // Is it a tabular form cell?
    if (apexlib.field.isTabFormColumn(pFieldId))
    {
      // it's a tabular form column, highlight the cell
      apexlib.error.highlightField
        ( pFieldId
        , apexlib.error.sTabFormBackgroundColor
        , apexlib.error.sTabFormForegroundColor
        );
    }
    // Is it one of our stored fields and are we allowed to
    // display inline error messages?
    else if ((apexlib.field.sFieldList[pFieldId]) &&
             (vInlineErrorText) &&
             (apexlib.error.sDisplayLocation != apexlib.error.DISPLAY_LOCATION.NOTIFICATION)
            )
    {
      apexlib.field.setLabel(pFieldId, vInlineErrorText);
    }
    // Register on change to remove error when the field is changed
    //
    // because we are unable to remove the code from the onchange after we
    // have removed the error, we have to remember if we have already cleared
    // the error, otherwise we would clear errors which the user hasn't seen yet.
    // eg. onchange event stack could be validateOnChange/removeError/...
    // if the removeError was registered before the validateOnChange, which is
    // possible if an error occurs on the server and is redirected.
    // Or in the case a manual removeError is called...
    apexlib.error.sRestoreCounter++;
    // we need a local version so that the anonymous function remembers the value
    var vRestoreCounter = apexlib.error.sRestoreCounter;
    apexlib.error.sRestoreList[vRestoreCounter] = vErrorName;

    apexlib.core.addEvent
      ( "onchange"
      , pFieldId
      , function()
          {
            apexlib.error.removeError(vErrorName, pFieldId, vRestoreCounter);
          }
      , apexlib.core.ADD_POSITION.FIRST
      );
  }
}; // showError

//==============================================================================
// Removes an error from the message area and restores the label or background
// color of the specified field.
//==============================================================================
apexlib.error.removeError = function(pErrorName, pFieldId, pRestoreCounter)
{
  // only if we havn't already cleared that error
  if ((pRestoreCounter) && (!apexlib.error.sRestoreList[pRestoreCounter])) return;
  //----------------------------------------------------------------------------
  // Couldn't use the name instead of the id attribute for the span, because IE
  // doesn't support that. With name we could have used getElementsByName...
  //----------------------------------------------------------------------------
  var vErrorName    = (!pErrorName)?"Error_"+pFieldId:pErrorName;
  var vErrorMessage = document.getElementById(vErrorName);
  // loop through all errors with that name and delete them
  while (vErrorMessage)
  {
    // remove line break if it exists
    if ((vErrorMessage.nextSibling) && (vErrorMessage.nextSibling.tagName == "BR"))
    {
      vErrorMessage.parentNode.removeChild(vErrorMessage.nextSibling);
      // sometimes there is a second line break, remove it too. Havn't analyzed
      // this bug yet $$$
      if ((vErrorMessage.nextSibling) && (vErrorMessage.nextSibling.tagName == "BR"))
      {
        vErrorMessage.parentNode.removeChild(vErrorMessage.nextSibling);
      }
    }
    // remove the error span
    vErrorMessage.parentNode.removeChild(vErrorMessage);
    // search again for an error message with that id
    vErrorMessage = document.getElementById(vErrorName);
  }
  // delete all "handlers" which have this error message(s) we just cleared
  for (var i=0; i < apexlib.error.sRestoreList.length; i++)
  {
    if (apexlib.error.sRestoreList[i] == vErrorName) delete apexlib.error.sRestoreList[i];
  }
  //----------------------------------------------------------------------------
  // check if it was the last error in the notification area
  //----------------------------------------------------------------------------
  var vNotificationArea = apexlib.core.getElementByClassName
                            ( "div"
                            , apexlib.error.sNotificationClassName
                            );
  if (vNotificationArea)
  {
    // if it was the last message, remove the complete div.
    if (vNotificationArea.childNodes.length == 0)
    {
      vNotificationArea.parentNode.removeChild(vNotificationArea);
    }
  }
  //----------------------------------------------------------------------------
  // Has a field been specified and is it one of our stored fields?
  //----------------------------------------------------------------------------
  if (!pFieldId) return;

  if (apexlib.field.isTabFormColumn(pFieldId))
  {
    // it's a tabular column cell, restore the original background color
    apexlib.error.restoreHighlightField(pFieldId);
  }
  else if (apexlib.field.sFieldList[pFieldId])
  {
    // remove inline error message
    apexlib.field.setLabel(pFieldId);
  }
}; // removeError
//==============================================================================
//  PROJECT: ApexLib - The APEX Library (http://apexlib.soureforge.net/)
//
//  DESCRIPTION:
//
//    This JavaScript library contains method for validation handling.
//
//  SUPPORTED APEX VERSIONS:
//
//    2.x, 3.0
//
//  REFERENCED OTHER LIBRARIES:
//
//    ApexLib_Core.js
//    ApexLib_Convert.js
//    ApexLib_Field.js
//    ApexLib_Error.js
//
//  AUTHOR(S):
//
//    Patrick Wolf (http://inside-apex.blogspot.com/)
//    Peter Raganitsch (http://www.oracle-and-apex.com/, http://blog.oracleapex.at/)
//
//  SVN HEADER:
//
//    $Id: ApexLib_Validation.js 383 2009-10-19 19:48:55Z praganitsch $
//
//==============================================================================

//==============================================================================
// Initialize the namespace for ApexLib.
//==============================================================================
if (!window.apexlib)     window.apexlib     = {};
if (!apexlib.validation) apexlib.validation = {};

//==============================================================================
// Global array used by the different field methods.
//==============================================================================
apexlib.validation.sIgnoreRequestList   = new Object(); // index: request
                                                        // value: ignored
apexlib.validation.sShowRequiredWarning = true;
//==============================================================================
// Default error messages required by this module
// Overwrite them in Shared Components\Text Messages
//==============================================================================
apexlib.core.addMessage("APEXLIB_ITEM_REQUIRED", "\%label is required.");
apexlib.core.addMessage("APEXLIB_ITEM_REQUIRED_INLINE", "Is required");
apexlib.core.addMessage("APEXLIB_ITEM_INVALID_FORMAT", "\%label doesn\'t match to the format \"%0\" (eg \%1).");
apexlib.core.addMessage("APEXLIB_ITEM_INVALID_FORMAT_INLINE", "Format \"\%0\"<br>Eg \%1");
apexlib.core.addMessage("APEXLIB_ITEM_GREATER_OR_EQUAL", "\%label has to be greater or equal to %0.");
apexlib.core.addMessage("APEXLIB_ITEM_GREATER_OR_EQUAL_INLINE", "Has to be greater or<br>equal to %0");
apexlib.core.addMessage("APEXLIB_ITEM_LESS_OR_EQUAL", "\%label has to be less or equal to %0.");
apexlib.core.addMessage("APEXLIB_ITEM_LESS_OR_EQUAL_INLINE", "Has to be less or<br>equal to %0");
apexlib.core.addMessage("APEXLIB_ITEM_BETWEEN", "\%label has to be between %0 and %1.");
apexlib.core.addMessage("APEXLIB_ITEM_BETWEEN_INLINE", "Has to be between<br>%0 and %1");
apexlib.core.addMessage("APEXLIB_TABFORM_COLUMN_REQUIRED", "\%label is required.");
apexlib.core.addMessage("APEXLIB_TABFORM_INVALID_FORMAT", "\%label doesn\'t match to the format \"\%0\" (eg \%1).");
apexlib.core.addMessage("APEXLIB_TABFORM_GREATER_OR_EQUAL", "\%label has to be greater or equal to %0.");
apexlib.core.addMessage("APEXLIB_TABFORM_LESS_OR_EQUAL", "\%label has to be less or equal to %0.");
apexlib.core.addMessage("APEXLIB_TABFORM_BETWEEN", "\%label has to be between %0 and %1.");

//==============================================================================
// Asserts that the field has a value, if not an error is displayed and the
// function returns false
//==============================================================================
apexlib.validation.assertNotNull = function(pFieldId)
{
  var vField = $x(pFieldId);
  // is it blank or equal to the lov null value?
  if ((apexlib.convert.isBlank(vField.value)) ||
      (vField.value == apexlib.field.getField(pFieldId).oLovNullValue))
  {
    if (apexlib.field.isTabFormColumn(pFieldId))
    {
      apexlib.error.showError
        ( null
        , pFieldId
        , apexlib.core.getMessage("APEXLIB_TABFORM_COLUMN_REQUIRED")
        );
    }
    else
    {
      apexlib.error.showError
        ( null
        , pFieldId
        , apexlib.core.getMessage("APEXLIB_ITEM_REQUIRED")
        , apexlib.core.getMessage("APEXLIB_ITEM_REQUIRED_INLINE")
        );
    }
    return false;
  }
  return true;
}; // assertNotNull

//==============================================================================
// Asserts that the field has a valid value which matches to the format mask,
// if not, an error is displayed and the function returns false.
//==============================================================================
apexlib.validation.assertValidFormat = function(pFieldId)
{
  var vField        = $x(pFieldId);
  var vFieldEntry   = apexlib.field.getField(pFieldId);
  var vFormatMask   = vFieldEntry.oFormatMask;
  var vValue        = null;
  var vExampleValue = null;
  // is it blank or equal to the lov null value?
  if (!apexlib.convert.isBlank(vField.value) && (vFormatMask))
  {
    switch(vFieldEntry.oDataType)
    {
      case "N":
        vValue        = apexlib.convert.toNumber(vField.value, vFormatMask);
        // create an example, if this value is too long, use a shorter one
        vExampleValue = apexlib.convert.toString(1251.49, vFormatMask);
        if (vExampleValue.indexOf("#") >= 0) vExampleValue = apexlib.convert.toString(3.49, vFormatMask);
        break;
      case "D":
        vValue        = apexlib.convert.toDate(vField.value, vFormatMask);
        vExampleValue = apexlib.convert.toString(new Date(2006, 11, 31, 14, 05, 11), vFormatMask);
        break;
      default: return true; break; // no format mask check for other types then Number and Date
    }
    // was the convert successful?
    if (vValue != null)
    {
      // let's re-convert the value to show the value in the correct format
      // eg (adding decimal 0's or group seperator or adding currency symbol
      apexlib.field.setValue(pFieldId, vValue);
    }
    else
    {
      if (apexlib.field.isTabFormColumn(pFieldId))
      {
        apexlib.error.showError
          ( null
          , pFieldId
          , apexlib.core.getMessage("APEXLIB_TABFORM_INVALID_FORMAT", null, vFormatMask, vExampleValue)
          );
      }
      else
      {
        apexlib.error.showError
          ( null
          , pFieldId
          , apexlib.core.getMessage("APEXLIB_ITEM_INVALID_FORMAT", null, vFormatMask, vExampleValue)
          , apexlib.core.getMessage("APEXLIB_ITEM_INVALID_FORMAT_INLINE", null, vFormatMask, vExampleValue)
          );
      }
      vField.value = vField.value+""; // that's for IE so that he thinks the field has changed
                                      // otherwise the onChange isn't fired again if the user
                                      // restores the original value
      return false;
    }
  }
  return true;
}; // assertValidFormat

//==============================================================================
// Asserts that the field value is within min- and max value.
// If not, an error is displayed and the function returns false.
//==============================================================================
apexlib.validation.assertMinMaxValue = function(pFieldId)
{
  var vField      = $x(pFieldId);
  var vFieldEntry = apexlib.field.getField(pFieldId);
  var vFormatMask = vFieldEntry.oFormatMask;
  var vMinValue   = vFieldEntry.oMinValue;
  var vMaxValue   = vFieldEntry.oMaxValue;
  var vValue      = vFieldEntry.value;
  var vError      = null;
  var vParam1     = null;
  var vParam2     = null;
  //
  function replaceItemRefs(pValue)
  {
    if ( pValue != null && String(pValue).search(/^#[\w\s]*#$/) == 0) // eg. #P4_TEST# or #TEST#
    {
      if (apexlib.field.isTabFormColumn(pFieldId))
      {
        return $x(apexlib.field.getTabFormFieldId
                   ( String(pValue).substr(1, String(pValue).length-2)
                   , apexlib.field.getTabFormRow(pFieldId)
                   )
                 ).value;
      }
      else
      {
        return $x(String(pValue).substr(1, String(pValue).length-2)).value;
      }
    }
    return pValue;
  }; // replaceItemRefs
  //
  // Don't proceed if it's a blank value or if no min/max value is defined
  if (!apexlib.convert.isBlank(vField.value) && (vMinValue || vMaxValue))
  {
    vMinValue = replaceItemRefs(vMinValue);
    vMaxValue = replaceItemRefs(vMaxValue);

    switch(vFieldEntry.oDataType)
    {
      case "N":
        vMinValue = apexlib.convert.toNumber(vMinValue,    vFormatMask);
        vMaxValue = apexlib.convert.toNumber(vMaxValue,    vFormatMask);
        vValue    = apexlib.convert.toNumber(vField.value, vFormatMask);
        break;
      case "D":
        vMinValue = apexlib.convert.toDate(vMinValue,    vFormatMask);
        vMaxValue = apexlib.convert.toDate(vMaxValue,    vFormatMask);
        vValue    = apexlib.convert.toDate(vField.value, vFormatMask);
        break;
      default: return true; break; // other types will use string
    }
    // only proceed if we have valid min/max values
    if ((vMinValue != null) && (vMaxValue != null))
    {
      if (vValue < vMinValue || vValue > vMaxValue)
      {
        vError  = "BETWEEN";
        vParam1 = vMinValue;
        vParam2 = vMaxValue;
      }
    }
    else if (vMinValue != null)
    {
      if (vValue < vMinValue)
      {
        vError  = "GREATER_OR_EQUAL";
        vParam1 = vMinValue;
        vParam2 = null;
      }
    }
    else if (vMaxValue != null)
    {
      if (vValue > vMaxValue)
      {
        vError  = "LESS_OR_EQUAL";
        vParam1 = vMaxValue;
        vParam2 = null;
      }
    }
    // has an error occurred?
    if (vError)
    {
      if (apexlib.field.isTabFormColumn(pFieldId))
      {
        apexlib.error.showError
          ( null
          , pFieldId
          , apexlib.core.getMessage
              ( "APEXLIB_TABFORM_"+vError
              , null
              , apexlib.convert.toString(vParam1, vFormatMask)
              , apexlib.convert.toString(vParam2, vFormatMask)
              )
          );
      }
      else
      {
        apexlib.error.showError
          ( null
          , pFieldId
          , apexlib.core.getMessage
              ( "APEXLIB_ITEM_"+vError
              , null
              , apexlib.convert.toString(vParam1, vFormatMask)
              , apexlib.convert.toString(vParam2, vFormatMask)
              )
          , apexlib.core.getMessage
              ( "APEXLIB_ITEM_"+vError+"_INLINE"
              , null
              , apexlib.convert.toString(vParam1, vFormatMask)
              , apexlib.convert.toString(vParam2, vFormatMask)
              )
          );
      }
      vField.value = vField.value+""; // that's for IE so that he thinks the field has changed
                                      // otherwise the onChange isn't fired again if the user
                                      // restores the original value
      return false;
    }
  }
  return true;
}; // assertMinMaxValue

//==============================================================================
// ApexLib code which should be run when the value has been changed.
//==============================================================================
apexlib.validation.validateOnChange = function()
{
  var vField = $x(this.id);
  // Does the field exist?
  if (!vField) return true;
  // check format mask and min-/max value
  if ( apexlib.validation.assertValidFormat(vField.id) &&
       apexlib.validation.assertMinMaxValue(vField.id)
     )
  {
    return true;
  }
  return false;
}; // validateOnChange

//==============================================================================
// ApexLib code which should be run when leaving the field.
//==============================================================================
apexlib.validation.validateOnBlur = function()
{
  // Is the field required?
  if (apexlib.field.getField(this.id).oIsRequired)
  {
    apexlib.validation.assertNotNull(this.id);
  }
}; // validateOnBlur

//==============================================================================
// ApexLib code which should be run when the page is submitted.
//==============================================================================
apexlib.validation.validateOnSubmit = function(pThis)
{
  // do we have to check the values for the submitted request?
  // @ALL@ means, that the is a before computation branch with no restriction
  // at all.
  if (apexlib.validation.sIgnoreRequestList["@ALL@"]) return true;
  if (apexlib.validation.sIgnoreRequestList[document.wwv_flow.p_request.value]) return true;

  var vFieldEntry = null;
  var vFieldId    = null;
  var vField      = null;
  var vSuccessful = true;
  // loop through our field repository, but we only process page items,
  // no tabular form columns
  for (var i in apexlib.field.sFieldList)
  {
    vFieldEntry = apexlib.field.sFieldList[i];
    // is it just a internal field?
    if (vFieldEntry.oIsInternal) continue; // next one
    // do we have to care about it?
    if (vFieldEntry.oIgnoreValidation) continue; // next one
    // tabular forms are checked in the second loop
    if (vFieldEntry.oTabFormArray != "") continue; // next one
    // does the field really exist (in case of a condition, ...)
    vField = $x(vFieldEntry.oName);
    if (!vField) continue; // next one
    // remove all error messages for this field
    apexlib.error.removeError(null, vFieldEntry.oName);
    // check format mask, ... first
    vSuccessful = (apexlib.validation.validateOnChange.apply(vField) && vSuccessful);
    // Is the field required?
    if (vFieldEntry.oIsRequired)
    {
      vSuccessful = (apexlib.validation.assertNotNull(i) && vSuccessful);
    }
  }
  // we have a seperate loop for the tabular form columns, because we want to
  // have all error messages for a row. The other loop isn't used because it is
  // driven by the column.
  for (var vRow=1; vRow<=apexlib.field.sTabFormRowCount; vRow++)
  {
    if (!apexlib.validation.isTabFormRowCheckRequired(vRow)) continue; // next row
    // loop through our field repository, but we only process tabular form columns
    for (var i in apexlib.field.sFieldList)
    {
      vFieldEntry = apexlib.field.sFieldList[i];
      // is it just a internal field?
      if (vFieldEntry.oIsInternal) continue; // next one
      // do we have to care about it?
      if (vFieldEntry.oIgnoreValidation) continue; // next one
      // has to be a tabular form column
      if (vFieldEntry.oTabFormArray == "") continue; // next one
      // does the field really exist (in case of a condition, ...)
      vFieldId = apexlib.field.getTabFormFieldId(vFieldEntry.oName, vRow);
      vField   = $x(vFieldId);
      if (!vField) continue; // next one
      // remove all error messages for this column cell
      apexlib.error.removeError(null, vFieldId);
      // check format mask, ... first
      vSuccessful = (apexlib.validation.validateOnChange.apply(vField) && vSuccessful);
      // Is the field required?
      if (vFieldEntry.oIsRequired)
      {
        vSuccessful = (apexlib.validation.assertNotNull(vFieldId) && vSuccessful);
      }
    }
  }
  return vSuccessful;
}; // validateOnSubmit

//==============================================================================
// Checks if for the specified row a check is required.
// A check is required if any of the fields has a value or the fsc (checksum)
// field is not null.
//==============================================================================
apexlib.validation.isTabFormRowCheckRequired = function(pRow)
{
  if (pRow > apexlib.field.sTabFormRowCount) return false;
  // the problem is that APEX doesn't generate unique field ids for hidden items
  // that's why we have to use a trick to get all values.
  var vField = $x(apexlib.field.getTabFormFieldId("_"+apexlib.field.sTabFormRowCntArray, pRow));
  // get the tr element of the field
  var vElement = apexlib.core.getParentNode(vField, "TR");
  if (!vElement) return false;
  // get all labels within this row
  var vInputList = vElement.getElementsByTagName("input");
  for (var i=0; i < vInputList.length; i++)
  {
    if ((vInputList[i].name=="fcs") &&
        (vInputList[i].value == "A884FA378C851786DDFE3A33709CB23C")) continue; // checksum for null record

    if (vInputList[i].type == "checkbox")
    {
      if (vInputList[i].checked) return true;
    }
    else
    {
      if (vInputList[i].value != "") return true;
    }
  }
  return false;
}; // isTabFormRowCheckRequired

//==============================================================================
// The ignore request array contains all the request names where no validation
// should occur if the page is submitted. eg GET_NEXT_... for navigation
//==============================================================================
apexlib.validation.addIgnoreRequest = function(pRequest)
{
  apexlib.validation.sIgnoreRequestList[pRequest] = pRequest;
}; // addIgnoreRequest

//==============================================================================
// Uses the information stored in apexlib.field.sFieldList to automatically
// register some client side checks, like date picker, number, required.
//==============================================================================
apexlib.validation.init = function()
{
  // First correct HTML buttons generated by Oracle APEX. Otherwise they would
  // stay disabled if a validation fails. That's why we replace the APEX code
  // "this.disabled=true; doSubmit('XXX');" with
  // "this.disabled=true; if (!doSubmit('XXX')) {this.disabled=false;}"
  apexlibJquery("input[type=button]")
    .filter
      ( function()
        {
          var vFunction = String(apexlibJquery(this).attr("onclick"));
          if (vFunction.match(/this.disabled=true; doSubmit\(/)) { return true; }
          return false;
        }
      )
    .each
      ( function()
        {
          var vFunction = String(apexlibJquery(this).attr("onclick"));
          var vRequest  = vFunction.match(/doSubmit\(".*"\)/);
          // IE converts the string delimiter to a single quote
          if (!vRequest) { vRequest = vFunction.match(/doSubmit\('.*'\)/); }
          this.onclick = new Function
                               ( 'this.disabled=true; if (!'+
                                 vRequest+
                                 '){this.disabled=false;}'
                               );
        }
      );
  // Now the ApexLib validation initialization
  var vFieldEntry = null;
  var vForm       = $x("wwvFlowForm");
  // does a form exist, or is it a read only page?
  if (!vForm) return;
  // loop through our field repository
  for (var i in apexlib.field.sFieldList)
  {
    vFieldEntry = apexlib.field.sFieldList[i];
    // is it just a internal field?
    if (vFieldEntry.oIsInternal) continue; // next one
    // do we have to care about it?
    if (vFieldEntry.oIgnoreValidation) continue; // next one
    // does the field really exist (in case of a condition, ...)
    if (vFieldEntry.oTabFormArray=="")
    {
      if (!$x(vFieldEntry.oName)) continue; // next one
    }
    else
    {
      if (apexlib.field.sTabFormRowCount==0) continue; // next one
      // has this column been rendered? In case of a condition
      if (!$x(apexlib.field.getTabFormFieldId(vFieldEntry.oName, 1))) continue; // next one
    }
    // register a "hard" format mask check
    // as first event, so that the following code can be sure it is a date/number.
    // The idea was to abort the onchange event so that the user stays in the field.
    // But according to the JS spec, there is no way to abort the onchange
    // event so that the user isn't able to leave the field :-(, but at least
    // the following functions are not called.
    // Lucky us, IE doesn't comply to the spec and aborts onchange if false is
    // returned! So at least IE shows the desired behavior.
    if (vFieldEntry.oFormatMask != "")
    {
      if (vFieldEntry.oTabFormArray=="")
      {
        apexlib.core.addEvent
          ( "onchange"
          , vFieldEntry.oName
          , apexlib.validation.validateOnChange
          , apexlib.core.ADD_POSITION.FIRST
          );
      }
      else
      {
        // register for each tabular form cell
        apexlib.field.forEachRow
          ( vFieldEntry.oName
          , function()
            { apexlib.core.addEvent
                ( "onchange"
                , this.id
                , apexlib.validation.validateOnChange
                , apexlib.core.ADD_POSITION.FIRST
                );
            }
          );
      }
    }
    // register a "soft" required check when leaving the field
    // but as last check, so that other code can correct/set the value when
    // leaving the field
    if (vFieldEntry.oIsRequired && apexlib.validation.sShowRequiredWarning)
    {
      if (vFieldEntry.oTabFormArray=="")
      {
        apexlib.core.addEvent
          ( "onblur"
          , vFieldEntry.oName
          , apexlib.validation.validateOnBlur
          , apexlib.core.ADD_POSITION.LAST
          );
      }
      else
      {
        // register for each tabular form cell
        apexlib.field.forEachRow
          ( vFieldEntry.oName
          , function()
            { apexlib.core.addEvent
                ( "onblur"
                , this.id
                , apexlib.validation.validateOnBlur
                , apexlib.core.ADD_POSITION.FIRST
                );
            }
          );
      }
    }
  }
  // register the same checks when the user submits the page, we want to
  // be first, because that are the basic checks so that the other userdefined
  // checks can rely on that the values are correct.
  apexlib.core.addEvent
    ( "onsubmit"
    , vForm
    , apexlib.validation.validateOnSubmit
    , apexlib.core.ADD_POSITION.FIRST
    );
}; // init

//==============================================================================
// Registers the init method with an onload event, so that we are sure that
// we are the last ones modifying the onchange events of the fields.
//==============================================================================
apexlib.validation.registerInit = function(pShowRequiredWarning)
{
  apexlib.validation.sShowRequiredWarning = pShowRequiredWarning;
  apexlib.core.addEvent("onload", window, apexlib.validation.init, apexlib.core.ADD_POSITION.LAST);
}; // registerInit
//==============================================================================
//  PROJECT: ApexLib - The APEX Library (http://apexlib.soureforge.net/)
//
//  DESCRIPTION:
//
//    This JavaScript library contains method for UI improvements.
//
//  SUPPORTED APEX VERSIONS:
//
//    2.x
//
//  CODE USED FROM SOMEWHERE ELSE:
//
//    Some code snippets of the JavaScript library of www.drupal.org
//
//  AUTHOR(S):
//
//    Patrick Wolf (http://inside-apex.blogspot.com/)
//    Peter Raganitsch (http://www.oracle-and-apex.com/, http://blog.oracleapex.at/)
//
//  SVN HEADER:
//
//    $Id: ApexLib_UI.js 383 2009-10-19 19:48:55Z praganitsch $
//
//==============================================================================

//==============================================================================
// Initialize the namespace for ApexLib.
//==============================================================================
if (!window.apexlib) window.apexlib = {};
if (!apexlib.UI)     apexlib.UI     = {};

//==============================================================================
// Returns the position of the mouse cursor based on the event object passed.
//==============================================================================
apexlib.UI.getMousePosition = function(pEvent)
{
  return { x: pEvent.clientX + document.documentElement.scrollLeft
         , y: pEvent.clientY + document.documentElement.scrollTop
         };
}; // getMousePosition

//==============================================================================
// Asserts that the field has a value, if not an error is displayed and the
// function returns false
//==============================================================================
apexlib.UI.setVerticalResizeable = function(pTextArea)
{
  if (!pTextArea) return;

  // static variables used by startResize and performResize
  var sOffset   = null;
  var sTextArea = apexlibJquery(pTextArea);

  // wrap the textarea with a div, so that it is resizeable and add a
  // size-grip for resizing.
  sTextArea
    .wrap('<div class="apexlib-v-resizable-textarea"></div>')
    .parent()
    .append(apexlibJquery('<div class="size-grip"></div>').mousedown(startResize));

  // Get our size-grip div and do some style modifications
  var vSizeGrip = apexlibJquery('div.size-grip', sTextArea.parent())[0];
  vSizeGrip.style.marginRight = (vSizeGrip.offsetWidth - pTextArea.offsetWidth) +'px';

  // Function called when the mouse button has been pressed in the size-grip div
  function startResize(pEvent)
  {
    sOffset = sTextArea.height() - apexlib.UI.getMousePosition(pEvent).y;
    sTextArea.css('opacity', 0.25);
    apexlibJquery(document).mousemove(performResize).mouseup(endResize);
    return false;
  };

  // Function called when the mouse is moved while the button is pressed in
  // the size-grip div
  function performResize(pEvent)
  {
    sTextArea.height
      ( Math.max(32, sOffset + apexlib.UI.getMousePosition(pEvent).y) + 'px'
      );
    return false;
  };

  // Function called when the mouse button is released in the size-grip div
  // this will de-register the performResize and endResize methods and
  // restore the opacity of the textarea.
  function endResize(pEvent)
  {
    apexlibJquery(document).unbind("mousemove", performResize).unbind("mouseup", endResize);
    sTextArea.css('opacity', 1);
  };
}; // setVerticalResizeable

//==============================================================================
// This method is called with pElement equal to an date picker link or lov link
// and will set the focus to the attached field of the icon.
//==============================================================================
apexlib.UI.setFocusToAttachedField = function(pElement)
{
  // set focus but only if input item isnt disabled, otherwise IE is complaining
  if (apexlib.core.getPreviousNode(pElement, "INPUT").disabled==false)
  {
    apexlib.core.getPreviousNode(pElement, "INPUT").focus();
  }
}; // setFocusToAttachedField

//==============================================================================
// This method is called for a keydown event and checks if the
// keys ALT+down or Apple+down have been pressed.
// If that's the case the date picker/lov link assigned to this field is
// activated to open the popup.
//==============================================================================
apexlib.UI.checkLovKey = function(pEvent)
{
  // only if the cursor is on a input field
  if (pEvent.target.nodeName != "INPUT") return;
  // if ALT+down or Apple+down has been pressed
  if (((pEvent.altKey ==true) && (pEvent.keyCode==40)) ||
      ((pEvent.metaKey==true) && (pEvent.keyCode==40))
     )
  {
    // activate the attached link, but only if it's an item of a lov span
    var vLovSpan = apexlib.core.getParentNode(pEvent.target, "SPAN");
    if ((vLovSpan) && (vLovSpan.className="lov"))
    {
      // activate the link
      apexlibJquery(apexlib.core.getNextNode(pEvent.target, "IMG")).click();
    }
  }
}; // checkLovKey

//==============================================================================
// This method is called for a keydown event and checks if the
// keys ALT+down or Apple+down have been pressed.
// If that's the case the date picker/lov link assigned to this field is
// activated to open the popup.
//==============================================================================
apexlib.UI.checkUpDownKey = function(pEvent)
{
  // only if the cursor is on a input field
  if (pEvent.target.nodeName != "INPUT") return;
  // if up or down has been pressed and no other key
  if ((pEvent.ctrlKey ==false) &&
      (pEvent.shiftKey==false) &&
      (pEvent.altKey  ==false) &&
      ((pEvent.metaKey==undefined) || (pEvent.metaKey==false)) &&
      ((pEvent.keyCode==38) || (pEvent.keyCode==40)) // up and down key
     )
  {
    // Is the current item a tabular form field?
    if (apexlib.field.isTabFormColumn(pEvent.target.id))
    {
      // get next row depending on up/down key
      var vRow = apexlib.field.getTabFormRow(pEvent.target.id)+((pEvent.keyCode==38)?-1:1);
      // get field of next row.
      // Note: Take care if it's a manual tabular form (no leading 0), because
      //       in that case use no leading 0s for the new vRow
      var vField = $x(pEvent.target.id.substr(0, 4)+
                      (($x(pEvent.target.id.substr(0, 4)+"1"))?vRow:apexlib.convert.lpad(vRow, 4, "0"))
                     );
      // only if it exists (in case we are on the first or last record)
      if (vField)
      {
        var vAutoComplete = vField.autocomplete;
        apexlibJquery(vField).attr("autocomplete", "off");
        vField.focus();
        apexlibJquery(vField).attr("autocomplete", vAutoComplete);
      }
    }
  }
}; // checkUpDown

//==============================================================================
// This method removes the surrounding link of an image and adds the action
// of the link (a href) as onclick event to the image. This method is used to
// make the APEX icons non-navigable.
//==============================================================================
apexlib.UI.removeImageLink = function(pLinkElement)
{
  var vHref = pLinkElement.href; // closure
  // Get the image within the link, remove it and add it before the link,
  // register an onclick event (usage of window.location is better than eval
  // here, because it will also work if it's a normal url)
  // and remove the link afterwards.
  apexlibJquery("img", pLinkElement)
    .insertBefore(pLinkElement)
    .bind("click", function(){window.location=vHref;})
    .css("cursor", "pointer");
  apexlibJquery(pLinkElement).remove();
}; // removeImageLink
