/*!
 * jQuery JavaScript Library v1.4.2
 * http://jquery.com/
 *
 * Copyright 2010, John Resig
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * Includes Sizzle.js
 * http://sizzlejs.com/
 * Copyright 2010, The Dojo Foundation
 * Released under the MIT, BSD, and GPL Licenses.
 *
 * Date: Sat Feb 13 22:33:48 2010 -0500
 */
(function( window, undefined ) {

// Define a local copy of jQuery
var jQuery = function( selector, context ) {
		// The jQuery object is actually just the init constructor 'enhanced'
		return new jQuery.fn.init( selector, context );
	},

	// Map over jQuery in case of overwrite
	_jQuery = window.jQuery,

	// Map over the $ in case of overwrite
	_$ = window.$,

	// Use the correct document accordingly with window argument (sandbox)
	document = window.document,

	// A central reference to the root jQuery(document)
	rootjQuery,

	// A simple way to check for HTML strings or ID strings
	// (both of which we optimize for)
	quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,

	// Is it a simple selector
	isSimple = /^.[^:#\[\.,]*$/,

	// Check if a string has a non-whitespace character in it
	rnotwhite = /\S/,

	// Used for trimming whitespace
	rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g,

	// Match a standalone tag
	rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,

	// Keep a UserAgent string for use with jQuery.browser
	userAgent = navigator.userAgent,

	// For matching the engine and version of the browser
	browserMatch,
	
	// Has the ready events already been bound?
	readyBound = false,
	
	// The functions to execute on DOM ready
	readyList = [],

	// The ready event handler
	DOMContentLoaded,

	// Save a reference to some core methods
	toString = Object.prototype.toString,
	hasOwnProperty = Object.prototype.hasOwnProperty,
	push = Array.prototype.push,
	slice = Array.prototype.slice,
	indexOf = Array.prototype.indexOf;

jQuery.fn = jQuery.prototype = {
	init: function( selector, context ) {
		var match, elem, ret, doc;

		// Handle $(""), $(null), or $(undefined)
		if ( !selector ) {
			return this;
		}

		// Handle $(DOMElement)
		if ( selector.nodeType ) {
			this.context = this[0] = selector;
			this.length = 1;
			return this;
		}
		
		// The body element only exists once, optimize finding it
		if ( selector === "body" && !context ) {
			this.context = document;
			this[0] = document.body;
			this.selector = "body";
			this.length = 1;
			return this;
		}

		// Handle HTML strings
		if ( typeof selector === "string" ) {
			// Are we dealing with HTML string or an ID?
			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] ) {
					doc = (context ? context.ownerDocument || context : document);

					// If a single string is passed in and it's a single tag
					// just do a createElement and skip the rest
					ret = rsingleTag.exec( selector );

					if ( ret ) {
						if ( jQuery.isPlainObject( context ) ) {
							selector = [ document.createElement( ret[1] ) ];
							jQuery.fn.attr.call( selector, context, true );

						} else {
							selector = [ doc.createElement( ret[1] ) ];
						}

					} else {
						ret = buildFragment( [ match[1] ], [ doc ] );
						selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
					}
					
					return jQuery.merge( this, selector );
					
				// HANDLE: $("#id")
				} else {
					elem = document.getElementById( match[2] );

					if ( elem ) {
						// Handle the case where IE and Opera return items
						// by name instead of ID
						if ( elem.id !== match[2] ) {
							return rootjQuery.find( selector );
						}

						// Otherwise, we inject the element directly into the jQuery object
						this.length = 1;
						this[0] = elem;
					}

					this.context = document;
					this.selector = selector;
					return this;
				}

			// HANDLE: $("TAG")
			} else if ( !context && /^\w+$/.test( selector ) ) {
				this.selector = selector;
				this.context = document;
				selector = document.getElementsByTagName( selector );
				return jQuery.merge( this, selector );

			// HANDLE: $(expr, $(...))
			} else if ( !context || context.jquery ) {
				return (context || rootjQuery).find( selector );

			// HANDLE: $(expr, context)
			// (which is just equivalent to: $(context).find(expr)
			} else {
				return jQuery( context ).find( selector );
			}

		// HANDLE: $(function)
		// Shortcut for document ready
		} else if ( jQuery.isFunction( selector ) ) {
			return rootjQuery.ready( selector );
		}

		if (selector.selector !== undefined) {
			this.selector = selector.selector;
			this.context = selector.context;
		}

		return jQuery.makeArray( selector, this );
	},

	// Start with an empty selector
	selector: "",

	// The current version of jQuery being used
	jquery: "1.4.2",

	// The default length of a jQuery object is 0
	length: 0,

	// The number of elements contained in the matched element set
	size: function() {
		return this.length;
	},

	toArray: function() {
		return slice.call( this, 0 );
	},

	// 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 == null ?

			// Return a 'clean' array
			this.toArray() :

			// Return just the object
			( num < 0 ? this.slice(num)[ 0 ] : 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();

		if ( jQuery.isArray( elems ) ) {
			push.apply( ret, elems );
		
		} else {
			jQuery.merge( ret, 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;
	},

	// 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 );
	},
	
	ready: function( fn ) {
		// Attach the listeners
		jQuery.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 if ( readyList ) {
			// Add the function to the wait list
			readyList.push( fn );
		}

		return this;
	},
	
	eq: function( i ) {
		return i === -1 ?
			this.slice( i ) :
			this.slice( i, +i + 1 );
	},

	first: function() {
		return this.eq( 0 );
	},

	last: function() {
		return this.eq( -1 );
	},

	slice: function() {
		return this.pushStack( slice.apply( this, arguments ),
			"slice", slice.call(arguments).join(",") );
	},

	map: function( callback ) {
		return this.pushStack( jQuery.map(this, function( elem, i ) {
			return callback.call( elem, i, elem );
		}));
	},
	
	end: function() {
		return this.prevObject || jQuery(null);
	},

	// For internal use only.
	// Behaves like an Array's method, not like a jQuery method.
	push: push,
	sort: [].sort,
	splice: [].splice
};

// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;

jQuery.extend = jQuery.fn.extend = function() {
	// copy reference to target object
	var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;

	// 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 ( name in options ) {
				src = target[ name ];
				copy = options[ name ];

				// Prevent never-ending loop
				if ( target === copy ) {
					continue;
				}

				// Recurse if we're merging object literal values or arrays
				if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) {
					var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src
						: jQuery.isArray(copy) ? [] : {};

					// Never move original objects, clone them
					target[ name ] = jQuery.extend( deep, clone, copy );

				// Don't bring in undefined values
				} else if ( copy !== undefined ) {
					target[ name ] = copy;
				}
			}
		}
	}

	// Return the modified object
	return target;
};

jQuery.extend({
	noConflict: function( deep ) {
		window.$ = _$;

		if ( deep ) {
			window.jQuery = _jQuery;
		}

		return jQuery;
	},
	
	// Is the DOM ready to be used? Set to true once it occurs.
	isReady: false,
	
	// Handle when the DOM is ready
	ready: function() {
		// Make sure that the DOM is not already loaded
		if ( !jQuery.isReady ) {
			// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
			if ( !document.body ) {
				return setTimeout( jQuery.ready, 13 );
			}

			// Remember that the DOM is ready
			jQuery.isReady = true;

			// If there are functions bound, to execute
			if ( readyList ) {
				// Execute all of them
				var fn, i = 0;
				while ( (fn = readyList[ i++ ]) ) {
					fn.call( document, jQuery );
				}

				// Reset the list of functions
				readyList = null;
			}

			// Trigger any bound ready events
			if ( jQuery.fn.triggerHandler ) {
				jQuery( document ).triggerHandler( "ready" );
			}
		}
	},
	
	bindReady: function() {
		if ( readyBound ) {
			return;
		}

		readyBound = true;

		// Catch cases where $(document).ready() is called after the
		// browser event has already occurred.
		if ( document.readyState === "complete" ) {
			return jQuery.ready();
		}

		// Mozilla, Opera and webkit nightlies currently support this event
		if ( document.addEventListener ) {
			// Use the handy event callback
			document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
			
			// A fallback to window.onload, that will always work
			window.addEventListener( "load", 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", DOMContentLoaded);
			
			// A fallback to window.onload, that will always work
			window.attachEvent( "onload", jQuery.ready );

			// If IE and not a frame
			// continually check to see if the document is ready
			var toplevel = false;

			try {
				toplevel = window.frameElement == null;
			} catch(e) {}

			if ( document.documentElement.doScroll && toplevel ) {
				doScrollCheck();
			}
		}
	},

	// 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]";
	},

	isPlainObject: function( obj ) {
		// Must be an Object.
		// Because of IE, we also have to check the presence of the constructor property.
		// Make sure that DOM nodes and window objects don't pass through, as well
		if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) {
			return false;
		}
		
		// Not own constructor property must be Object
		if ( obj.constructor
			&& !hasOwnProperty.call(obj, "constructor")
			&& !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) {
			return false;
		}
		
		// Own properties are enumerated firstly, so to speed up,
		// if last one is own, then all properties are own.
	
		var key;
		for ( key in obj ) {}
		
		return key === undefined || hasOwnProperty.call( obj, key );
	},

	isEmptyObject: function( obj ) {
		for ( var name in obj ) {
			return false;
		}
		return true;
	},
	
	error: function( msg ) {
		throw msg;
	},
	
	parseJSON: function( data ) {
		if ( typeof data !== "string" || !data ) {
			return null;
		}

		// Make sure leading/trailing whitespace is removed (IE can't handle it)
		data = jQuery.trim( data );
		
		// Make sure the incoming data is actual JSON
		// Logic borrowed from http://json.org/json2.js
		if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
			.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
			.replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) {

			// Try to use the native JSON parser first
			return window.JSON && window.JSON.parse ?
				window.JSON.parse( data ) :
				(new Function("return " + data))();

		} else {
			jQuery.error( "Invalid JSON: " + data );
		}
	},

	noop: function() {},

	// Evalulates a script in a global context
	globalEval: function( data ) {
		if ( data && rnotwhite.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,
			isObj = length === undefined || jQuery.isFunction(object);

		if ( args ) {
			if ( isObj ) {
				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 ( isObj ) {
				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;
	},

	trim: function( text ) {
		return (text || "").replace( rtrim, "" );
	},

	// results is for internal usage only
	makeArray: function( array, results ) {
		var ret = results || [];

		if ( array != null ) {
			// The window, strings (and functions) also have 'length'
			// The extra typeof function check is to prevent crashes
			// in Safari 2 (See: #3039)
			if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) {
				push.call( ret, array );
			} else {
				jQuery.merge( ret, array );
			}
		}

		return ret;
	},

	inArray: function( elem, array ) {
		if ( array.indexOf ) {
			return array.indexOf( elem );
		}

		for ( var i = 0, length = array.length; i < length; i++ ) {
			if ( array[ i ] === elem ) {
				return i;
			}
		}

		return -1;
	},

	merge: function( first, second ) {
		var i = first.length, j = 0;

		if ( typeof second.length === "number" ) {
			for ( var l = second.length; j < l; j++ ) {
				first[ i++ ] = second[ j ];
			}
		
		} else {
			while ( second[j] !== undefined ) {
				first[ i++ ] = second[ j++ ];
			}
		}

		first.length = i;

		return first;
	},

	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;
	},

	// arg is for internal usage only
	map: function( elems, callback, arg ) {
		var ret = [], value;

		// 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++ ) {
			value = callback( elems[ i ], i, arg );

			if ( value != null ) {
				ret[ ret.length ] = value;
			}
		}

		return ret.concat.apply( [], ret );
	},

	// A global GUID counter for objects
	guid: 1,

	proxy: function( fn, proxy, thisObject ) {
		if ( arguments.length === 2 ) {
			if ( typeof proxy === "string" ) {
				thisObject = fn;
				fn = thisObject[ proxy ];
				proxy = undefined;

			} else if ( proxy && !jQuery.isFunction( proxy ) ) {
				thisObject = proxy;
				proxy = undefined;
			}
		}

		if ( !proxy && fn ) {
			proxy = function() {
				return fn.apply( thisObject || this, arguments );
			};
		}

		// Set the guid of unique handler to the same of original handler, so it can be removed
		if ( fn ) {
			proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
		}

		// So proxy can be declared as an argument
		return proxy;
	},

	// Use of jQuery.browser is frowned upon.
	// More details: http://docs.jquery.com/Utilities/jQuery.browser
	uaMatch: function( ua ) {
		ua = ua.toLowerCase();

		var match = /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
			/(opera)(?:.*version)?[ \/]([\w.]+)/.exec( ua ) ||
			/(msie) ([\w.]+)/.exec( ua ) ||
			!/compatible/.test( ua ) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec( ua ) ||
		  	[];

		return { browser: match[1] || "", version: match[2] || "0" };
	},

	browser: {}
});

browserMatch = jQuery.uaMatch( userAgent );
if ( browserMatch.browser ) {
	jQuery.browser[ browserMatch.browser ] = true;
	jQuery.browser.version = browserMatch.version;
}

// Deprecated, use jQuery.browser.webkit instead
if ( jQuery.browser.webkit ) {
	jQuery.browser.safari = true;
}

if ( indexOf ) {
	jQuery.inArray = function( elem, array ) {
		return indexOf.call( array, elem );
	};
}

// All jQuery objects should point back to these
rootjQuery = jQuery(document);

// Cleanup functions for the document ready method
if ( document.addEventListener ) {
	DOMContentLoaded = function() {
		document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
		jQuery.ready();
	};

} else if ( document.attachEvent ) {
	DOMContentLoaded = function() {
		// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
		if ( document.readyState === "complete" ) {
			document.detachEvent( "onreadystatechange", DOMContentLoaded );
			jQuery.ready();
		}
	};
}

// The DOM ready check for Internet Explorer
function doScrollCheck() {
	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( doScrollCheck, 1 );
		return;
	}

	// and execute any waiting functions
	jQuery.ready();
}

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 );
	}
}

// Mutifunctional method to get and set values to a collection
// The value/s can be optionally by executed if its a function
function access( elems, key, value, exec, fn, pass ) {
	var length = elems.length;
	
	// Setting many attributes
	if ( typeof key === "object" ) {
		for ( var k in key ) {
			access( elems, k, key[k], exec, fn, value );
		}
		return elems;
	}
	
	// Setting one attribute
	if ( value !== undefined ) {
		// Optionally, function values get executed if exec is true
		exec = !pass && exec && jQuery.isFunction(value);
		
		for ( var i = 0; i < length; i++ ) {
			fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
		}
		
		return elems;
	}
	
	// Getting an attribute
	return length ? fn( elems[0], key ) : undefined;
}

function now() {
	return (new Date).getTime();
}
(function() {

	jQuery.support = {};

	var root = document.documentElement,
		script = document.createElement("script"),
		div = document.createElement("div"),
		id = "script" + now();

	div.style.display = "none";
	div.innerHTML = "   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";

	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 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)
		// Use a regex to work around a WebKit issue. See #5145
		opacity: /^0.55$/.test( a.style.opacity ),

		// Verify style float existence
		// (IE uses styleFloat instead of cssFloat)
		cssFloat: !!a.style.cssFloat,

		// Make sure that if no value is specified for a checkbox
		// that it defaults to "on".
		// (WebKit defaults to "" instead)
		checkOn: div.getElementsByTagName("input")[0].value === "on",

		// Make sure that a selected-by-default option has a working selected property.
		// (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
		optSelected: document.createElement("select").appendChild( document.createElement("option") ).selected,

		parentNode: div.removeChild( div.appendChild( document.createElement("div") ) ).parentNode === null,

		// Will be defined later
		deleteExpando: true,
		checkClone: false,
		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 ];
	}

	// Test to see if it's possible to delete an expando from an element
	// Fails in Internet Explorer
	try {
		delete script.test;
	
	} catch(e) {
		jQuery.support.deleteExpando = false;
	}

	root.removeChild( script );

	if ( div.attachEvent && div.fireEvent ) {
		div.attachEvent("onclick", function click() {
			// Cloning a node shouldn't copy over any
			// bound event handlers (IE does this)
			jQuery.support.noCloneEvent = false;
			div.detachEvent("onclick", click);
		});
		div.cloneNode(true).fireEvent("onclick");
	}

	div = document.createElement("div");
	div.innerHTML = "<input type='radio' name='radiotest' checked='checked'/>";

	var fragment = document.createDocumentFragment();
	fragment.appendChild( div.firstChild );

	// WebKit doesn't clone checked state correctly in fragments
	jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;

	// 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';

		div = null;
	});

	// Technique from Juriy Zaytsev
	// http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
	var eventSupported = function( eventName ) { 
		var el = document.createElement("div"); 
		eventName = "on" + eventName; 

		var isSupported = (eventName in el); 
		if ( !isSupported ) { 
			el.setAttribute(eventName, "return;"); 
			isSupported = typeof el[eventName] === "function"; 
		} 
		el = null; 

		return isSupported; 
	};
	
	jQuery.support.submitBubbles = eventSupported("submit");
	jQuery.support.changeBubbles = eventSupported("change");

	// release memory in IE
	root = script = div = all = a = null;
})();

jQuery.props = {
	"for": "htmlFor",
	"class": "className",
	readonly: "readOnly",
	maxlength: "maxLength",
	cellspacing: "cellSpacing",
	rowspan: "rowSpan",
	colspan: "colSpan",
	tabindex: "tabIndex",
	usemap: "useMap",
	frameborder: "frameBorder"
};
var expando = "jQuery" + now(), uuid = 0, windowData = {};

jQuery.extend({
	cache: {},
	
	expando:expando,

	// The following elements throw uncatchable exceptions if you
	// attempt to add expando properties to them.
	noData: {
		"embed": true,
		"object": true,
		"applet": true
	},

	data: function( elem, name, data ) {
		if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
			return;
		}

		elem = elem == window ?
			windowData :
			elem;

		var id = elem[ expando ], cache = jQuery.cache, thisCache;

		if ( !id && typeof name === "string" && data === undefined ) {
			return null;
		}

		// Compute a unique ID for the element
		if ( !id ) { 
			id = ++uuid;
		}

		// Avoid generating a new cache unless none exists and we
		// want to manipulate it.
		if ( typeof name === "object" ) {
			elem[ expando ] = id;
			thisCache = cache[ id ] = jQuery.extend(true, {}, name);

		} else if ( !cache[ id ] ) {
			elem[ expando ] = id;
			cache[ id ] = {};
		}

		thisCache = cache[ id ];

		// Prevent overriding the named cache with undefined values
		if ( data !== undefined ) {
			thisCache[ name ] = data;
		}

		return typeof name === "string" ? thisCache[ name ] : thisCache;
	},

	removeData: function( elem, name ) {
		if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
			return;
		}

		elem = elem == window ?
			windowData :
			elem;

		var id = elem[ expando ], cache = jQuery.cache, thisCache = cache[ id ];

		// If we want to remove a specific section of the element's data
		if ( name ) {
			if ( thisCache ) {
				// Remove the section of cache data
				delete thisCache[ name ];

				// If we've removed all the data, remove the element's cache
				if ( jQuery.isEmptyObject(thisCache) ) {
					jQuery.removeData( elem );
				}
			}

		// Otherwise, we want to remove all of the element's data
		} else {
			if ( jQuery.support.deleteExpando ) {
				delete elem[ jQuery.expando ];

			} else if ( elem.removeAttribute ) {
				elem.removeAttribute( jQuery.expando );
			}

			// Completely remove the data cache
			delete cache[ id ];
		}
	}
});

jQuery.fn.extend({
	data: function( key, value ) {
		if ( typeof key === "undefined" && this.length ) {
			return jQuery.data( this[0] );

		} else if ( typeof key === "object" ) {
			return this.each(function() {
				jQuery.data( this, key );
			});
		}

		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 );
		});
	}
});
jQuery.extend({
	queue: function( elem, type, data ) {
		if ( !elem ) {
			return;
		}

		type = (type || "fx") + "queue";
		var q = jQuery.data( elem, type );

		// Speed up dequeue by getting out quickly if this is just a lookup
		if ( !data ) {
			return q || [];
		}

		if ( !q || jQuery.isArray(data) ) {
			q = jQuery.data( elem, type, jQuery.makeArray(data) );

		} else {
			q.push( data );
		}

		return q;
	},

	dequeue: function( elem, type ) {
		type = type || "fx";

		var queue = jQuery.queue( elem, type ), fn = queue.shift();

		// If the fx queue is dequeued, always remove the progress sentinel
		if ( fn === "inprogress" ) {
			fn = queue.shift();
		}

		if ( fn ) {
			// Add a progress sentinel to prevent the fx queue from being
			// automatically dequeued
			if ( type === "fx" ) {
				queue.unshift("inprogress");
			}

			fn.call(elem, function() {
				jQuery.dequeue(elem, type);
			});
		}
	}
});

jQuery.fn.extend({
	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( i, elem ) {
			var queue = jQuery.queue( this, type, data );

			if ( type === "fx" && queue[0] !== "inprogress" ) {
				jQuery.dequeue( this, type );
			}
		});
	},
	dequeue: function( type ) {
		return this.each(function() {
			jQuery.dequeue( this, type );
		});
	},

	// Based off of the plugin by Clint Helfers, with permission.
	// http://blindsignals.com/index.php/2009/07/jquery-delay/
	delay: function( time, type ) {
		time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
		type = type || "fx";

		return this.queue( type, function() {
			var elem = this;
			setTimeout(function() {
				jQuery.dequeue( elem, type );
			}, time );
		});
	},

	clearQueue: function( type ) {
		return this.queue( type || "fx", [] );
	}
});
var rclass = /[\n\t]/g,
	rspace = /\s+/,
	rreturn = /\r/g,
	rspecialurl = /href|src|style/,
	rtype = /(button|input)/i,
	rfocusable = /(button|input|object|select|textarea)/i,
	rclickable = /^(a|area)$/i,
	rradiocheck = /radio|checkbox/;

jQuery.fn.extend({
	attr: function( name, value ) {
		return access( this, name, value, true, jQuery.attr );
	},

	removeAttr: function( name, fn ) {
		return this.each(function(){
			jQuery.attr( this, name, "" );
			if ( this.nodeType === 1 ) {
				this.removeAttribute( name );
			}
		});
	},

	addClass: function( value ) {
		if ( jQuery.isFunction(value) ) {
			return this.each(function(i) {
				var self = jQuery(this);
				self.addClass( value.call(this, i, self.attr("class")) );
			});
		}

		if ( value && typeof value === "string" ) {
			var classNames = (value || "").split( rspace );

			for ( var i = 0, l = this.length; i < l; i++ ) {
				var elem = this[i];

				if ( elem.nodeType === 1 ) {
					if ( !elem.className ) {
						elem.className = value;

					} else {
						var className = " " + elem.className + " ", setClass = elem.className;
						for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
							if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
								setClass += " " + classNames[c];
							}
						}
						elem.className = jQuery.trim( setClass );
					}
				}
			}
		}

		return this;
	},

	removeClass: function( value ) {
		if ( jQuery.isFunction(value) ) {
			return this.each(function(i) {
				var self = jQuery(this);
				self.removeClass( value.call(this, i, self.attr("class")) );
			});
		}

		if ( (value && typeof value === "string") || value === undefined ) {
			var classNames = (value || "").split(rspace);

			for ( var i = 0, l = this.length; i < l; i++ ) {
				var elem = this[i];

				if ( elem.nodeType === 1 && elem.className ) {
					if ( value ) {
						var className = (" " + elem.className + " ").replace(rclass, " ");
						for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
							className = className.replace(" " + classNames[c] + " ", " ");
						}
						elem.className = jQuery.trim( className );

					} else {
						elem.className = "";
					}
				}
			}
		}

		return this;
	},

	toggleClass: function( value, stateVal ) {
		var type = typeof value, isBool = typeof stateVal === "boolean";

		if ( jQuery.isFunction( value ) ) {
			return this.each(function(i) {
				var self = jQuery(this);
				self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
			});
		}

		return this.each(function() {
			if ( type === "string" ) {
				// toggle individual class names
				var className, i = 0, self = jQuery(this),
					state = stateVal,
					classNames = value.split( rspace );

				while ( (className = classNames[ i++ ]) ) {
					// check each className given, space seperated list
					state = isBool ? state : !self.hasClass( className );
					self[ state ? "addClass" : "removeClass" ]( className );
				}

			} else if ( type === "undefined" || type === "boolean" ) {
				if ( this.className ) {
					// store className if set
					jQuery.data( this, "__className__", this.className );
				}

				// toggle whole className
				this.className = this.className || value === false ? "" : jQuery.data( this, "__className__" ) || "";
			}
		});
	},

	hasClass: function( selector ) {
		var className = " " + selector + " ";
		for ( var i = 0, l = this.length; i < l; i++ ) {
			if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
				return true;
			}
		}

		return false;
	},

	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;
				}

				// Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
				if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
					return elem.getAttribute("value") === null ? "on" : elem.value;
				}
				

				// Everything else, we just grab the value
				return (elem.value || "").replace(rreturn, "");

			}

			return undefined;
		}

		var isFunction = jQuery.isFunction(value);

		return this.each(function(i) {
			var self = jQuery(this), val = value;

			if ( this.nodeType !== 1 ) {
				return;
			}

			if ( isFunction ) {
				val = value.call(this, i, self.val());
			}

			// Typecast each time if the value is a Function and the appended
			// value is therefore different each time.
			if ( typeof val === "number" ) {
				val += "";
			}

			if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {
				this.checked = jQuery.inArray( self.val(), val ) >= 0;

			} else if ( jQuery.nodeName( this, "select" ) ) {
				var values = jQuery.makeArray(val);

				jQuery( "option", this ).each(function() {
					this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
				});

				if ( !values.length ) {
					this.selectedIndex = -1;
				}

			} else {
				this.value = val;
			}
		});
	}
});

jQuery.extend({
	attrFn: {
		val: true,
		css: true,
		html: true,
		text: true,
		data: true,
		width: true,
		height: true,
		offset: true
	},
		
	attr: function( elem, name, value, pass ) {
		// don't set attributes on text and comment nodes
		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
			return undefined;
		}

		if ( pass && name in jQuery.attrFn ) {
			return jQuery(elem)[name](value);
		}

		var notxml = elem.nodeType !== 1 || !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)
		if ( elem.nodeType === 1 ) {
			// These attributes require special treatment
			var special = rspecialurl.test( name );

			// Safari mis-reports the default selected property of an option
			// Accessing the parent's selectedIndex property fixes it
			if ( name === "selected" && !jQuery.support.optSelected ) {
				var parent = elem.parentNode;
				if ( parent ) {
					parent.selectedIndex;
	
					// Make sure that it also works with optgroups, see #5701
					if ( parent.parentNode ) {
						parent.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" && rtype.test( elem.nodeName ) && elem.parentNode ) {
						jQuery.error( "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 :
						rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
							0 :
							undefined;
				}

				return elem[ name ];
			}

			if ( !jQuery.support.style && notxml && name === "style" ) {
				if ( set ) {
					elem.style.cssText = "" + value;
				}

				return elem.style.cssText;
			}

			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
		// Using attr for specific style information is now deprecated. Use style instead.
		return jQuery.style( elem, name, value );
	}
});
var rnamespaces = /\.(.*)$/,
	fcleanup = function( nm ) {
		return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
			return "\\" + ch;
		});
	};

/*
 * 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.frameElement ) ) {
			elem = window;
		}

		var handleObjIn, handleObj;

		if ( handler.handler ) {
			handleObjIn = handler;
			handler = handleObjIn.handler;
		}

		// Make sure that the function being executed has a unique ID
		if ( !handler.guid ) {
			handler.guid = jQuery.guid++;
		}

		// Init the element's event structure
		var elemData = jQuery.data( elem );

		// If no elemData is found then we must be trying to bind to one of the
		// banned noData elements
		if ( !elemData ) {
			return;
		}

		var events = elemData.events = elemData.events || {},
			eventHandle = elemData.handle, eventHandle;

		if ( !eventHandle ) {
			elemData.handle = eventHandle = 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( eventHandle.elem, arguments ) :
					undefined;
			};
		}

		// Add elem as a property of the handle function
		// This is to prevent a memory leak with non-native events in IE.
		eventHandle.elem = elem;

		// Handle multiple events separated by a space
		// jQuery(...).bind("mouseover mouseout", fn);
		types = types.split(" ");

		var type, i = 0, namespaces;

		while ( (type = types[ i++ ]) ) {
			handleObj = handleObjIn ?
				jQuery.extend({}, handleObjIn) :
				{ handler: handler, data: data };

			// Namespaced event handlers
			if ( type.indexOf(".") > -1 ) {
				namespaces = type.split(".");
				type = namespaces.shift();
				handleObj.namespace = namespaces.slice(0).sort().join(".");

			} else {
				namespaces = [];
				handleObj.namespace = "";
			}

			handleObj.type = type;
			handleObj.guid = handler.guid;

			// Get the current list of functions bound to this event
			var handlers = events[ type ],
				special = jQuery.event.special[ type ] || {};

			// 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 ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
					// Bind the global event handler to the element
					if ( elem.addEventListener ) {
						elem.addEventListener( type, eventHandle, false );

					} else if ( elem.attachEvent ) {
						elem.attachEvent( "on" + type, eventHandle );
					}
				}
			}
			
			if ( special.add ) { 
				special.add.call( elem, handleObj ); 

				if ( !handleObj.handler.guid ) {
					handleObj.handler.guid = handler.guid;
				}
			}

			// Add the function to the element's handler list
			handlers.push( handleObj );

			// 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;
	},

	global: {},

	// Detach an event or set of events from an element
	remove: function( elem, types, handler, pos ) {
		// don't do events on text and comment nodes
		if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
			return;
		}

		var ret, type, fn, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
			elemData = jQuery.data( elem ),
			events = elemData && elemData.events;

		if ( !elemData || !events ) {
			return;
		}

		// types is actually an event object here
		if ( types && types.type ) {
			handler = types.handler;
			types = types.type;
		}

		// Unbind all events for the element
		if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
			types = types || "";

			for ( type in events ) {
				jQuery.event.remove( elem, type + types );
			}

			return;
		}

		// Handle multiple events separated by a space
		// jQuery(...).unbind("mouseover mouseout", fn);
		types = types.split(" ");

		while ( (type = types[ i++ ]) ) {
			origType = type;
			handleObj = null;
			all = type.indexOf(".") < 0;
			namespaces = [];

			if ( !all ) {
				// Namespaced event handlers
				namespaces = type.split(".");
				type = namespaces.shift();

				namespace = new RegExp("(^|\\.)" + 
					jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)")
			}

			eventType = events[ type ];

			if ( !eventType ) {
				continue;
			}

			if ( !handler ) {
				for ( var j = 0; j < eventType.length; j++ ) {
					handleObj = eventType[ j ];

					if ( all || namespace.test( handleObj.namespace ) ) {
						jQuery.event.remove( elem, origType, handleObj.handler, j );
						eventType.splice( j--, 1 );
					}
				}

				continue;
			}

			special = jQuery.event.special[ type ] || {};

			for ( var j = pos || 0; j < eventType.length; j++ ) {
				handleObj = eventType[ j ];

				if ( handler.guid === handleObj.guid ) {
					// remove the given handler for the given type
					if ( all || namespace.test( handleObj.namespace ) ) {
						if ( pos == null ) {
							eventType.splice( j--, 1 );
						}

						if ( special.remove ) {
							special.remove.call( elem, handleObj );
						}
					}

					if ( pos != null ) {
						break;
					}
				}
			}

			// remove generic event handler if no more handlers exist
			if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
				if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
					removeEvent( elem, type, elemData.handle );
				}

				ret = null;
				delete events[ type ];
			}
		}

		// Remove the expando if it's no longer used
		if ( jQuery.isEmptyObject( events ) ) {
			var handle = elemData.handle;
			if ( handle ) {
				handle.elem = null;
			}

			delete elemData.events;
			delete elemData.handle;

			if ( jQuery.isEmptyObject( elemData ) ) {
				jQuery.removeData( elem );
			}
		}
	},

	// bubbling is internal
	trigger: function( event, data, elem /*, bubbling */ ) {
		// Event object or event type
		var type = event.type || event,
			bubbling = arguments[3];

		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 ( jQuery.event.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 );
		}

		var parent = elem.parentNode || elem.ownerDocument;

		// Trigger an inline bound script
		try {
			if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
				if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
					event.result = false;
				}
			}

		// prevent IE from throwing an error for some elements with some event types, see #3533
		} catch (e) {}

		if ( !event.isPropagationStopped() && parent ) {
			jQuery.event.trigger( event, data, parent, true );

		} else if ( !event.isDefaultPrevented() ) {
			var target = event.target, old,
				isClick = jQuery.nodeName(target, "a") && type === "click",
				special = jQuery.event.special[ type ] || {};

			if ( (!special._default || special._default.call( elem, event ) === false) && 
				!isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {

				try {
					if ( target[ type ] ) {
						// Make sure that we don't accidentally re-trigger the onFOO events
						old = target[ "on" + type ];

						if ( old ) {
							target[ "on" + type ] = null;
						}

						jQuery.event.triggered = true;
						target[ type ]();
					}

				// prevent IE from throwing an error for some elements with some event types, see #3533
				} catch (e) {}

				if ( old ) {
					target[ "on" + type ] = old;
				}

				jQuery.event.triggered = false;
			}
		}
	},

	handle: function( event ) {
		var all, handlers, namespaces, namespace, events;

		event = arguments[0] = jQuery.event.fix( event || window.event );
		event.currentTarget = this;

		// Namespaced event handlers
		all = event.type.indexOf(".") < 0 && !event.exclusive;

		if ( !all ) {
			namespaces = event.type.split(".");
			event.type = namespaces.shift();
			namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
		}

		var events = jQuery.data(this, "events"), handlers = events[ event.type ];

		if ( events && handlers ) {
			// Clone the handlers to prevent manipulation
			handlers = handlers.slice(0);

			for ( var j = 0, l = handlers.length; j < l; j++ ) {
				var handleObj = handlers[ j ];

				// Filter the functions by class
				if ( all || namespace.test( handleObj.namespace ) ) {
					// Pass in a reference to the handler function itself
					// So that we can later remove it
					event.handler = handleObj.handler;
					event.data = handleObj.data;
					event.handleObj = handleObj;
	
					var ret = handleObj.handler.apply( this, arguments );

					if ( ret !== undefined ) {
						event.result = ret;
						if ( ret === false ) {
							event.preventDefault();
							event.stopPropagation();
						}
					}

					if ( event.isImmediatePropagationStopped() ) {
						break;
					}
				}
			}
		}

		return event.result;
	},

	props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY 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 && doc.clientLeft || body && body.clientLeft || 0);
			event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.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 !== undefined ) {
			event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
		}

		return event;
	},

	// Deprecated, use jQuery.guid instead
	guid: 1E8,

	// Deprecated, use jQuery.proxy instead
	proxy: jQuery.proxy,

	special: {
		ready: {
			// Make sure the ready event is setup
			setup: jQuery.bindReady,
			teardown: jQuery.noop
		},

		live: {
			add: function( handleObj ) {
				jQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) ); 
			},

			remove: function( handleObj ) {
				var remove = true,
					type = handleObj.origType.replace(rnamespaces, "");
				
				jQuery.each( jQuery.data(this, "events").live || [], function() {
					if ( type === this.origType.replace(rnamespaces, "") ) {
						remove = false;
						return false;
					}
				});

				if ( remove ) {
					jQuery.event.remove( this, handleObj.origType, liveHandler );
				}
			}

		},

		beforeunload: {
			setup: function( data, namespaces, eventHandle ) {
				// We only want to do this special case on windows
				if ( this.setInterval ) {
					this.onbeforeunload = eventHandle;
				}

				return false;
			},
			teardown: function( namespaces, eventHandle ) {
				if ( this.onbeforeunload === eventHandle ) {
					this.onbeforeunload = null;
				}
			}
		}
	}
};

var removeEvent = document.removeEventListener ?
	function( elem, type, handle ) {
		elem.removeEventListener( type, handle, false );
	} : 
	function( elem, type, handle ) {
		elem.detachEvent( "on" + type, handle );
	};

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;

	// Firefox sometimes assigns relatedTarget a XUL element
	// which we cannot access the parentNode property of
	try {
		// Traverse up the tree
		while ( parent && parent !== this ) {
			parent = parent.parentNode;
		}

		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 );
		}

	// assuming we've left the element since we most likely mousedover a xul element
	} catch(e) { }
},

// In case of event delegation, we only need to rename the event.type,
// liveHandler will take care of the rest.
delegate = function( event ) {
	event.type = event.data;
	jQuery.event.handle.apply( this, arguments );
};

// Create mouseenter and mouseleave events
jQuery.each({
	mouseenter: "mouseover",
	mouseleave: "mouseout"
}, function( orig, fix ) {
	jQuery.event.special[ orig ] = {
		setup: function( data ) {
			jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
		},
		teardown: function( data ) {
			jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
		}
	};
});

// submit delegation
if ( !jQuery.support.submitBubbles ) {

	jQuery.event.special.submit = {
		setup: function( data, namespaces ) {
			if ( this.nodeName.toLowerCase() !== "form" ) {
				jQuery.event.add(this, "click.specialSubmit", function( e ) {
					var elem = e.target, type = elem.type;

					if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
						return trigger( "submit", this, arguments );
					}
				});
	 
				jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
					var elem = e.target, type = elem.type;

					if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
						return trigger( "submit", this, arguments );
					}
				});

			} else {
				return false;
			}
		},

		teardown: function( namespaces ) {
			jQuery.event.remove( this, ".specialSubmit" );
		}
	};

}

// change delegation, happens here so we have bind.
if ( !jQuery.support.changeBubbles ) {

	var formElems = /textarea|input|select/i,

	changeFilters,

	getVal = function( elem ) {
		var type = elem.type, val = elem.value;

		if ( type === "radio" || type === "checkbox" ) {
			val = elem.checked;

		} else if ( type === "select-multiple" ) {
			val = elem.selectedIndex > -1 ?
				jQuery.map( elem.options, function( elem ) {
					return elem.selected;
				}).join("-") :
				"";

		} else if ( elem.nodeName.toLowerCase() === "select" ) {
			val = elem.selectedIndex;
		}

		return val;
	},

	testChange = function testChange( e ) {
		var elem = e.target, data, val;

		if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
			return;
		}

		data = jQuery.data( elem, "_change_data" );
		val = getVal(elem);

		// the current data will be also retrieved by beforeactivate
		if ( e.type !== "focusout" || elem.type !== "radio" ) {
			jQuery.data( elem, "_change_data", val );
		}
		
		if ( data === undefined || val === data ) {
			return;
		}

		if ( data != null || val ) {
			e.type = "change";
			return jQuery.event.trigger( e, arguments[1], elem );
		}
	};

	jQuery.event.special.change = {
		filters: {
			focusout: testChange, 

			click: function( e ) {
				var elem = e.target, type = elem.type;

				if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
					return testChange.call( this, e );
				}
			},

			// Change has to be called before submit
			// Keydown will be called before keypress, which is used in submit-event delegation
			keydown: function( e ) {
				var elem = e.target, type = elem.type;

				if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
					(e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
					type === "select-multiple" ) {
					return testChange.call( this, e );
				}
			},

			// Beforeactivate happens also before the previous element is blurred
			// with this event you can't trigger a change event, but you can store
			// information/focus[in] is not needed anymore
			beforeactivate: function( e ) {
				var elem = e.target;
				jQuery.data( elem, "_change_data", getVal(elem) );
			}
		},

		setup: function( data, namespaces ) {
			if ( this.type === "file" ) {
				return false;
			}

			for ( var type in changeFilters ) {
				jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
			}

			return formElems.test( this.nodeName );
		},

		teardown: function( namespaces ) {
			jQuery.event.remove( this, ".specialChange" );

			return formElems.test( this.nodeName );
		}
	};

	changeFilters = jQuery.event.special.change.filters;
}

function trigger( type, elem, args ) {
	args[0].type = type;
	return jQuery.event.handle.apply( elem, args );
}

// Create "bubbling" focus and blur events
if ( document.addEventListener ) {
	jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
		jQuery.event.special[ fix ] = {
			setup: function() {
				this.addEventListener( orig, handler, true );
			}, 
			teardown: function() { 
				this.removeEventListener( orig, handler, true );
			}
		};

		function handler( e ) { 
			e = jQuery.event.fix( e );
			e.type = fix;
			return jQuery.event.handle.call( this, e );
		}
	});
}

jQuery.each(["bind", "one"], function( i, name ) {
	jQuery.fn[ name ] = function( type, data, fn ) {
		// Handle object literals
		if ( typeof type === "object" ) {
			for ( var key in type ) {
				this[ name ](key, data, type[key], fn);
			}
			return this;
		}
		
		if ( jQuery.isFunction( data ) ) {
			fn = data;
			data = undefined;
		}

		var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
			jQuery( this ).unbind( event, handler );
			return fn.apply( this, arguments );
		}) : fn;

		if ( type === "unload" && name !== "one" ) {
			this.one( type, data, fn );

		} else {
			for ( var i = 0, l = this.length; i < l; i++ ) {
				jQuery.event.add( this[i], type, handler, data );
			}
		}

		return this;
	};
});

jQuery.fn.extend({
	unbind: function( type, fn ) {
		// Handle object literals
		if ( typeof type === "object" && !type.preventDefault ) {
			for ( var key in type ) {
				this.unbind(key, type[key]);
			}

		} else {
			for ( var i = 0, l = this.length; i < l; i++ ) {
				jQuery.event.remove( this[i], type, fn );
			}
		}

		return this;
	},
	
	delegate: function( selector, types, data, fn ) {
		return this.live( types, data, fn, selector );
	},
	
	undelegate: function( selector, types, fn ) {
		if ( arguments.length === 0 ) {
				return this.unbind( "live" );
		
		} else {
			return this.die( types, null, fn, selector );
		}
	},
	
	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.proxy( fn, args[ i++ ] );
		}

		return this.click( jQuery.proxy( fn, function( event ) {
			// Figure out which function to execute
			var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
			jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );

			// Make sure that clicks stop
			event.preventDefault();

			// and execute the function
			return args[ lastToggle ].apply( this, arguments ) || false;
		}));
	},

	hover: function( fnOver, fnOut ) {
		return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
	}
});

var liveMap = {
	focus: "focusin",
	blur: "focusout",
	mouseenter: "mouseover",
	mouseleave: "mouseout"
};

jQuery.each(["live", "die"], function( i, name ) {
	jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
		var type, i = 0, match, namespaces, preType,
			selector = origSelector || this.selector,
			context = origSelector ? this : jQuery( this.context );

		if ( jQuery.isFunction( data ) ) {
			fn = data;
			data = undefined;
		}

		types = (types || "").split(" ");

		while ( (type = types[ i++ ]) != null ) {
			match = rnamespaces.exec( type );
			namespaces = "";

			if ( match )  {
				namespaces = match[0];
				type = type.replace( rnamespaces, "" );
			}

			if ( type === "hover" ) {
				types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
				continue;
			}

			preType = type;

			if ( type === "focus" || type === "blur" ) {
				types.push( liveMap[ type ] + namespaces );
				type = type + namespaces;

			} else {
				type = (liveMap[ type ] || type) + namespaces;
			}

			if ( name === "live" ) {
				// bind live handler
				context.each(function(){
					jQuery.event.add( this, liveConvert( type, selector ),
						{ data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
				});

			} else {
				// unbind live handler
				context.unbind( liveConvert( type, selector ), fn );
			}
		}
		
		return this;
	}
});

function liveHandler( event ) {
	var stop, elems = [], selectors = [], args = arguments,
		related, match, handleObj, elem, j, i, l, data,
		events = jQuery.data( this, "events" );

	// Make sure we avoid non-left-click bubbling in Firefox (#3861)
	if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
		return;
	}

	event.liveFired = this;

	var live = events.live.slice(0);

	for ( j = 0; j < live.length; j++ ) {
		handleObj = live[j];

		if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
			selectors.push( handleObj.selector );

		} else {
			live.splice( j--, 1 );
		}
	}

	match = jQuery( event.target ).closest( selectors, event.currentTarget );

	for ( i = 0, l = match.length; i < l; i++ ) {
		for ( j = 0; j < live.length; j++ ) {
			handleObj = live[j];

			if ( match[i].selector === handleObj.selector ) {
				elem = match[i].elem;
				related = null;

				// Those two events require additional checking
				if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
					related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
				}

				if ( !related || related !== elem ) {
					elems.push({ elem: elem, handleObj: handleObj });
				}
			}
		}
	}

	for ( i = 0, l = elems.length; i < l; i++ ) {
		match = elems[i];
		event.currentTarget = match.elem;
		event.data = match.handleObj.data;
		event.handleObj = match.handleObj;

		if ( match.handleObj.origHandler.apply( match.elem, args ) === false ) {
			stop = false;
			break;
		}
	}

	return stop;
}

function liveConvert( type, selector ) {
	return "live." + (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
}

jQuery.each( ("blur focus focusin focusout 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 );
	};

	if ( jQuery.attrFn ) {
		jQuery.attrFn[ name ] = true;
	}
});

// Prevent memory leaks in IE
// Window isn't included so as not to unbind existing unload events
// More info:
//  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
if ( window.attachEvent && !window.addEventListener ) {
	window.attachEvent("onunload", function() {
		for ( var id in jQuery.cache ) {
			if ( jQuery.cache[ id ].handle ) {
				// Try/Catch is to handle iframes being unloaded, see #4280
				try {
					jQuery.event.remove( jQuery.cache[ id ].handle.elem );
				} catch(e) {}
			}
		}
	});
}
/*!
 * Sizzle CSS Selector Engine - v1.0
 *  Copyright 2009, The Dojo Foundation
 *  Released under the MIT, BSD, and GPL Licenses.
 *  More information: http://sizzlejs.com/
 */
(function(){

var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
	done = 0,
	toString = Object.prototype.toString,
	hasDuplicate = false,
	baseHasDuplicate = true;

// Here we check if the JavaScript engine is using some sort of
// optimization where it does not always call our comparision
// function. If that is the case, discard the hasDuplicate value.
//   Thus far that includes Google Chrome.
[0, 0].sort(function(){
	baseHasDuplicate = false;
	return 0;
});

var Sizzle = function(selector, context, results, seed) {
	results = results || [];
	var origContext = context = context || document;

	if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
		return [];
	}
	
	if ( !selector || typeof selector !== "string" ) {
		return results;
	}

	var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context),
		soFar = selector;
	
	// Reset the position of the chunker regexp (start from head)
	while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
		soFar = m[3];
		
		parts.push( m[1] );
		
		if ( m[2] ) {
			extra = m[3];
			break;
		}
	}

	if ( parts.length > 1 && origPOS.exec( selector ) ) {
		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
			set = posProcess( parts[0] + parts[1], context );
		} else {
			set = Expr.relative[ parts[0] ] ?
				[ context ] :
				Sizzle( parts.shift(), context );

			while ( parts.length ) {
				selector = parts.shift();

				if ( Expr.relative[ selector ] ) {
					selector += parts.shift();
				}
				
				set = posProcess( selector, set );
			}
		}
	} else {
		// Take a shortcut and set the context if the root selector is an ID
		// (but not if it'll be faster if the inner selector is an ID)
		if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
				Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
			var ret = Sizzle.find( parts.shift(), context, contextXML );
			context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
		}

		if ( context ) {
			var ret = seed ?
				{ expr: parts.pop(), set: makeArray(seed) } :
				Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
			set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;

			if ( parts.length > 0 ) {
				checkSet = makeArray(set);
			} else {
				prune = false;
			}

			while ( parts.length ) {
				var cur = parts.pop(), pop = cur;

				if ( !Expr.relative[ cur ] ) {
					cur = "";
				} else {
					pop = parts.pop();
				}

				if ( pop == null ) {
					pop = context;
				}

				Expr.relative[ cur ]( checkSet, pop, contextXML );
			}
		} else {
			checkSet = parts = [];
		}
	}

	if ( !checkSet ) {
		checkSet = set;
	}

	if ( !checkSet ) {
		Sizzle.error( cur || selector );
	}

	if ( toString.call(checkSet) === "[object Array]" ) {
		if ( !prune ) {
			results.push.apply( results, checkSet );
		} else if ( context && context.nodeType === 1 ) {
			for ( var i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
					results.push( set[i] );
				}
			}
		} else {
			for ( var i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
					results.push( set[i] );
				}
			}
		}
	} else {
		makeArray( checkSet, results );
	}

	if ( extra ) {
		Sizzle( extra, origContext, results, seed );
		Sizzle.uniqueSort( results );
	}

	return results;
};

Sizzle.uniqueSort = function(results){
	if ( sortOrder ) {
		hasDuplicate = baseHasDuplicate;
		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.leftMatch[ type ].exec( expr )) ) {
			var left = match[1];
			match.splice(1,1);

			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.leftMatch[ type ].exec( expr )) != null && match[2] ) {
				var filter = Expr.filter[ type ], found, item, left = match[1];
				anyFound = false;

				match.splice(1,1);

				if ( left.substr( left.length - 1 ) === "\\" ) {
					continue;
				}

				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 ) {
				Sizzle.error( expr );
			} else {
				break;
			}
		}

		old = expr;
	}

	return curLoop;
};

Sizzle.error = function( msg ) {
	throw "Syntax error, unrecognized expression: " + msg;
};

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\))?/
	},
	leftMatch: {},
	attrMap: {
		"class": "className",
		"for": "htmlFor"
	},
	attrHandle: {
		href: function(elem){
			return elem.getAttribute("href");
		}
	},
	relative: {
		"+": function(checkSet, part){
			var isPartStr = typeof part === "string",
				isTag = isPartStr && !/\W/.test(part),
				isPartStrNotTag = isPartStr && !isTag;

			if ( isTag ) {
				part = part.toLowerCase();
			}

			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.toLowerCase() === part ?
						elem || false :
						elem === part;
				}
			}

			if ( isPartStrNotTag ) {
				Sizzle.filter( part, checkSet, true );
			}
		},
		">": function(checkSet, part){
			var isPartStr = typeof part === "string";

			if ( isPartStr && !/\W/.test(part) ) {
				part = part.toLowerCase();

				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
					var elem = checkSet[i];
					if ( elem ) {
						var parent = elem.parentNode;
						checkSet[i] = parent.nodeName.toLowerCase() === 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 ( typeof part === "string" && !/\W/.test(part) ) {
				var nodeCheck = part = part.toLowerCase();
				checkFn = dirNodeCheck;
			}

			checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
		},
		"~": function(checkSet, part, isXML){
			var doneName = done++, checkFn = dirCheck;

			if ( typeof part === "string" && !/\W/.test(part) ) {
				var nodeCheck = part = part.toLowerCase();
				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){
			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 + " ").replace(/[\t\n]/g, " ").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){
			return match[1].toLowerCase();
		},
		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 ( ( chunker.exec(match[3]) || "" ).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.toLowerCase() === "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 || getText([ elem ]) || "").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;
			} else {
				Sizzle.error( "Syntax error, unrecognized expression: " + name );
			}
		},
		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.toLowerCase() === match;
		},
		CLASS: function(elem, match){
			return (" " + (elem.className || elem.getAttribute("class")) + " ")
				.indexOf( match ) > -1;
		},
		ATTR: function(elem, match){
			var name = match[1],
				result = Expr.attrHandle[ name ] ?
					Expr.attrHandle[ name ]( elem ) :
					elem[ name ] != null ?
						elem[ name ] :
						elem.getAttribute( name ),
				value = result + "",
				type = match[2],
				check = match[4];

			return result == null ?
				type === "!=" :
				type === "=" ?
				value === check :
				type === "*=" ?
				value.indexOf(check) >= 0 :
				type === "~=" ?
				(" " + value + " ").indexOf(check) >= 0 :
				!check ?
				value && result !== false :
				type === "!=" ?
				value !== check :
				type === "^=" ?
				value.indexOf(check) === 0 :
				type === "$=" ?
				value.substr(value.length - check.length) === check :
				type === "|=" ?
				value === check || value.substr(0, check.length + 1) === check + "-" :
				false;
		},
		POS: function(elem, match, i, array){
			var name = match[2], filter = Expr.setFilters[ name ];

			if ( filter ) {
				return filter( elem, i, match, array );
			}
		}
	}
};

var origPOS = Expr.match.POS;

for ( var type in Expr.match ) {
	Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
	Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, function(all, num){
		return "\\" + (num - 0 + 1);
	}));
}

var makeArray = function(array, results) {
	array = Array.prototype.slice.call( array, 0 );

	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.
// Also verifies that the returned array holds DOM nodes
// (which is not the case in the Blackberry browser)
try {
	Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;

// 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 ) {
		if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
			if ( a == b ) {
				hasDuplicate = true;
			}
			return a.compareDocumentPosition ? -1 : 1;
		}

		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 ) {
		if ( !a.sourceIndex || !b.sourceIndex ) {
			if ( a == b ) {
				hasDuplicate = true;
			}
			return a.sourceIndex ? -1 : 1;
		}

		var ret = a.sourceIndex - b.sourceIndex;
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
} else if ( document.createRange ) {
	sortOrder = function( a, b ) {
		if ( !a.ownerDocument || !b.ownerDocument ) {
			if ( a == b ) {
				hasDuplicate = true;
			}
			return a.ownerDocument ? -1 : 1;
		}

		var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
		aRange.setStart(a, 0);
		aRange.setEnd(a, 0);
		bRange.setStart(b, 0);
		bRange.setEnd(b, 0);
		var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
}

// Utility function for retreiving the text value of an array of DOM nodes
function getText( elems ) {
	var ret = "", elem;

	for ( var i = 0; elems[i]; i++ ) {
		elem = elems[i];

		// Get the text from text nodes and CDATA nodes
		if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
			ret += elem.nodeValue;

		// Traverse everything else, except comment nodes
		} else if ( elem.nodeType !== 8 ) {
			ret += getText( elem.childNodes );
		}
	}

	return ret;
}

// Check to see if the browser returns elements by name when
// querying by getElementById (and provide a workaround)
(function(){
	// We're going to inject a fake input element with a specified name
	var form = document.createElement("div"),
		id = "script" + (new Date).getTime();
	form.innerHTML = "<a name='" + id + "'/>";

	// Inject it into the root element, check its status, and remove it quickly
	var root = document.documentElement;
	root.insertBefore( form, root.firstChild );

	// The workaround has to do additional checks after a getElementById
	// Which slows things down for other browsers (hence the branching)
	if ( document.getElementById( id ) ) {
		Expr.find.ID = function(match, context, isXML){
			if ( typeof context.getElementById !== "undefined" && !isXML ) {
				var m = context.getElementById(match[1]);
				return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
			}
		};

		Expr.filter.ID = function(elem, match){
			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
			return elem.nodeType === 1 && node && node.nodeValue === match;
		};
	}

	root.removeChild( form );
	root = form = null; // release memory in IE
})();

(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);
		};
	}

	div = null; // release memory in IE
})();

if ( document.querySelectorAll ) {
	(function(){
		var oldSizzle = Sizzle, div = document.createElement("div");
		div.innerHTML = "<p class='TEST'></p>";

		// Safari can't handle uppercase or unicode characters when
		// in quirks mode.
		if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
			return;
		}
	
		Sizzle = function(query, context, extra, seed){
			context = context || document;

			// Only use querySelectorAll on non-XML documents
			// (ID selectors don't work in non-HTML documents)
			if ( !seed && context.nodeType === 9 && !isXML(context) ) {
				try {
					return makeArray( context.querySelectorAll(query), extra );
				} catch(e){}
			}
		
			return oldSizzle(query, context, extra, seed);
		};

		for ( var prop in oldSizzle ) {
			Sizzle[ prop ] = oldSizzle[ prop ];
		}

		div = null; // release memory in IE
	})();
}

(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)
	// Also, make sure that getElementsByClassName actually exists
	if ( !div.getElementsByClassName || 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]);
		}
	};

	div = null; // release memory in IE
})();

function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
		var elem = checkSet[i];
		if ( elem ) {
			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.toLowerCase() === cur ) {
					match = elem;
					break;
				}

				elem = elem[dir];
			}

			checkSet[i] = match;
		}
	}
}

function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
		var elem = checkSet[i];
		if ( elem ) {
			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){
	// documentElement is verified for cases where it doesn't yet exist
	// (such as loading iframes in IE - #4833) 
	var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
	return documentElement ? documentElement.nodeName !== "HTML" : false;
};

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.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.filters;
jQuery.unique = Sizzle.uniqueSort;
jQuery.text = getText;
jQuery.isXMLDoc = isXML;
jQuery.contains = contains;

return;

window.Sizzle = Sizzle;

})();
var runtil = /Until$/,
	rparentsprev = /^(?:parents|prevUntil|prevAll)/,
	// Note: This RegExp should be improved, or likely pulled from Sizzle
	rmultiselector = /,/,
	slice = Array.prototype.slice;

// Implement the identical functionality for filter and not
var winnow = function( elements, qualifier, keep ) {
	if ( jQuery.isFunction( qualifier ) ) {
		return jQuery.grep(elements, function( elem, i ) {
			return !!qualifier.call( elem, i, elem ) === keep;
		});

	} else if ( qualifier.nodeType ) {
		return jQuery.grep(elements, function( elem, i ) {
			return (elem === qualifier) === keep;
		});

	} else if ( typeof qualifier === "string" ) {
		var filtered = jQuery.grep(elements, function( elem ) {
			return elem.nodeType === 1;
		});

		if ( isSimple.test( qualifier ) ) {
			return jQuery.filter(qualifier, filtered, !keep);
		} else {
			qualifier = jQuery.filter( qualifier, filtered );
		}
	}

	return jQuery.grep(elements, function( elem, i ) {
		return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
	});
};

jQuery.fn.extend({
	find: function( selector ) {
		var ret = this.pushStack( "", "find", selector ), length = 0;

		for ( var i = 0, l = this.length; i < l; i++ ) {
			length = ret.length;
			jQuery.find( selector, this[i], ret );

			if ( i > 0 ) {
				// Make sure that the results are unique
				for ( var n = length; n < ret.length; n++ ) {
					for ( var r = 0; r < length; r++ ) {
						if ( ret[r] === ret[n] ) {
							ret.splice(n--, 1);
							break;
						}
					}
				}
			}
		}

		return ret;
	},

	has: function( target ) {
		var targets = jQuery( target );
		return this.filter(function() {
			for ( var i = 0, l = targets.length; i < l; i++ ) {
				if ( jQuery.contains( this, targets[i] ) ) {
					return true;
				}
			}
		});
	},

	not: function( selector ) {
		return this.pushStack( winnow(this, selector, false), "not", selector);
	},

	filter: function( selector ) {
		return this.pushStack( winnow(this, selector, true), "filter", selector );
	},
	
	is: function( selector ) {
		return !!selector && jQuery.filter( selector, this ).length > 0;
	},

	closest: function( selectors, context ) {
		if ( jQuery.isArray( selectors ) ) {
			var ret = [], cur = this[0], match, matches = {}, selector;

			if ( cur && selectors.length ) {
				for ( var i = 0, l = selectors.length; i < l; i++ ) {
					selector = selectors[i];

					if ( !matches[selector] ) {
						matches[selector] = jQuery.expr.match.POS.test( selector ) ? 
							jQuery( selector, context || this.context ) :
							selector;
					}
				}

				while ( cur && cur.ownerDocument && cur !== context ) {
					for ( selector in matches ) {
						match = matches[selector];

						if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) {
							ret.push({ selector: selector, elem: cur });
							delete matches[selector];
						}
					}
					cur = cur.parentNode;
				}
			}

			return ret;
		}

		var pos = jQuery.expr.match.POS.test( selectors ) ? 
			jQuery( selectors, context || this.context ) : null;

		return this.map(function( i, cur ) {
			while ( cur && cur.ownerDocument && cur !== context ) {
				if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selectors) ) {
					return cur;
				}
				cur = cur.parentNode;
			}
			return null;
		});
	},
	
	// Determine the position of an element within
	// the matched set of elements
	index: function( elem ) {
		if ( !elem || typeof elem === "string" ) {
			return jQuery.inArray( this[0],
				// If it receives a string, the selector is used
				// If it receives nothing, the siblings are used
				elem ? jQuery( elem ) : this.parent().children() );
		}
		// Locate the position of the desired element
		return jQuery.inArray(
			// If it receives a jQuery object, the first element is used
			elem.jquery ? elem[0] : elem, this );
	},

	add: function( selector, context ) {
		var set = typeof selector === "string" ?
				jQuery( selector, context || this.context ) :
				jQuery.makeArray( selector ),
			all = jQuery.merge( this.get(), set );

		return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
			all :
			jQuery.unique( all ) );
	},

	andSelf: function() {
		return this.add( this.prevObject );
	}
});

// A painfully simple check to see if an element is disconnected
// from a document (should be improved, where feasible).
function isDisconnected( node ) {
	return !node || !node.parentNode || node.parentNode.nodeType === 11;
}

jQuery.each({
	parent: function( elem ) {
		var parent = elem.parentNode;
		return parent && parent.nodeType !== 11 ? parent : null;
	},
	parents: function( elem ) {
		return jQuery.dir( elem, "parentNode" );
	},
	parentsUntil: function( elem, i, until ) {
		return jQuery.dir( elem, "parentNode", until );
	},
	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" );
	},
	nextUntil: function( elem, i, until ) {
		return jQuery.dir( elem, "nextSibling", until );
	},
	prevUntil: function( elem, i, until ) {
		return jQuery.dir( elem, "previousSibling", until );
	},
	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( until, selector ) {
		var ret = jQuery.map( this, fn, until );
		
		if ( !runtil.test( name ) ) {
			selector = until;
		}

		if ( selector && typeof selector === "string" ) {
			ret = jQuery.filter( selector, ret );
		}

		ret = this.length > 1 ? jQuery.unique( ret ) : ret;

		if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
			ret = ret.reverse();
		}

		return this.pushStack( ret, name, slice.call(arguments).join(",") );
	};
});

jQuery.extend({
	filter: function( expr, elems, not ) {
		if ( not ) {
			expr = ":not(" + expr + ")";
		}

		return jQuery.find.matches(expr, elems);
	},
	
	dir: function( elem, dir, until ) {
		var matched = [], cur = elem[dir];
		while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
			if ( cur.nodeType === 1 ) {
				matched.push( cur );
			}
			cur = cur[dir];
		}
		return matched;
	},

	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;
	},

	sibling: function( n, elem ) {
		var r = [];

		for ( ; n; n = n.nextSibling ) {
			if ( n.nodeType === 1 && n !== elem ) {
				r.push( n );
			}
		}

		return r;
	}
});
var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
	rleadingWhitespace = /^\s+/,
	rxhtmlTag = /(<([\w:]+)[^>]*?)\/>/g,
	rselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,
	rtagName = /<([\w:]+)/,
	rtbody = /<tbody/i,
	rhtml = /<|&#?\w+;/,
	rnocache = /<script|<object|<embed|<option|<style/i,
	rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,  // checked="checked" or checked (html5)
	fcloseTag = function( all, front, tag ) {
		return rselfClosing.test( tag ) ?
			all :
			front + "></" + tag + ">";
	},
	wrapMap = {
		option: [ 1, "<select multiple='multiple'>", "</select>" ],
		legend: [ 1, "<fieldset>", "</fieldset>" ],
		thead: [ 1, "<table>", "</table>" ],
		tr: [ 2, "<table><tbody>", "</tbody></table>" ],
		td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
		col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
		area: [ 1, "<map>", "</map>" ],
		_default: [ 0, "", "" ]
	};

wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;

// IE can't serialize <link> and <script> tags normally
if ( !jQuery.support.htmlSerialize ) {
	wrapMap._default = [ 1, "div<div>", "</div>" ];
}

jQuery.fn.extend({
	text: function( text ) {
		if ( jQuery.isFunction(text) ) {
			return this.each(function(i) {
				var self = jQuery(this);
				self.text( text.call(this, i, self.text()) );
			});
		}

		if ( typeof text !== "object" && text !== undefined ) {
			return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
		}

		return jQuery.text( this );
	},

	wrapAll: function( html ) {
		if ( jQuery.isFunction( html ) ) {
			return this.each(function(i) {
				jQuery(this).wrapAll( html.call(this, i) );
			});
		}

		if ( this[0] ) {
			// The elements to wrap the target around
			var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);

			if ( this[0].parentNode ) {
				wrap.insertBefore( this[0] );
			}

			wrap.map(function() {
				var elem = this;

				while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
					elem = elem.firstChild;
				}

				return elem;
			}).append(this);
		}

		return this;
	},

	wrapInner: function( html ) {
		if ( jQuery.isFunction( html ) ) {
			return this.each(function(i) {
				jQuery(this).wrapInner( html.call(this, i) );
			});
		}

		return this.each(function() {
			var self = jQuery( this ), contents = self.contents();

			if ( contents.length ) {
				contents.wrapAll( html );

			} else {
				self.append( html );
			}
		});
	},

	wrap: function( html ) {
		return this.each(function() {
			jQuery( this ).wrapAll( html );
		});
	},

	unwrap: function() {
		return this.parent().each(function() {
			if ( !jQuery.nodeName( this, "body" ) ) {
				jQuery( this ).replaceWith( this.childNodes );
			}
		}).end();
	},

	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() {
		if ( this[0] && this[0].parentNode ) {
			return this.domManip(arguments, false, function( elem ) {
				this.parentNode.insertBefore( elem, this );
			});
		} else if ( arguments.length ) {
			var set = jQuery(arguments[0]);
			set.push.apply( set, this.toArray() );
			return this.pushStack( set, "before", arguments );
		}
	},

	after: function() {
		if ( this[0] && this[0].parentNode ) {
			return this.domManip(arguments, false, function( elem ) {
				this.parentNode.insertBefore( elem, this.nextSibling );
			});
		} else if ( arguments.length ) {
			var set = this.pushStack( this, "after", arguments );
			set.push.apply( set, jQuery(arguments[0]).toArray() );
			return set;
		}
	},
	
	// keepData is for internal use only--do not document
	remove: function( selector, keepData ) {
		for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
			if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
				if ( !keepData && elem.nodeType === 1 ) {
					jQuery.cleanData( elem.getElementsByTagName("*") );
					jQuery.cleanData( [ elem ] );
				}

				if ( elem.parentNode ) {
					 elem.parentNode.removeChild( elem );
				}
			}
		}
		
		return this;
	},

	empty: function() {
		for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
			// Remove element nodes and prevent memory leaks
			if ( elem.nodeType === 1 ) {
				jQuery.cleanData( elem.getElementsByTagName("*") );
			}

			// Remove any remaining nodes
			while ( elem.firstChild ) {
				elem.removeChild( elem.firstChild );
			}
		}
		
		return this;
	},

	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, ownerDocument = this.ownerDocument;
				if ( !html ) {
					var div = ownerDocument.createElement("div");
					div.appendChild( this.cloneNode(true) );
					html = div.innerHTML;
				}

				return jQuery.clean([html.replace(rinlinejQuery, "")
					// Handle the case in IE 8 where action=/test/> self-closes a tag
					.replace(/=([^="'>\s]+\/)>/g, '="$1">')
					.replace(rleadingWhitespace, "")], ownerDocument)[0];
			} else {
				return this.cloneNode(true);
			}
		});

		// Copy the events from the original to the clone
		if ( events === true ) {
			cloneCopyEvent( this, ret );
			cloneCopyEvent( this.find("*"), ret.find("*") );
		}

		// Return the cloned set
		return ret;
	},

	html: function( value ) {
		if ( value === undefined ) {
			return this[0] && this[0].nodeType === 1 ?
				this[0].innerHTML.replace(rinlinejQuery, "") :
				null;

		// See if we can take a shortcut and just use innerHTML
		} else if ( typeof value === "string" && !rnocache.test( value ) &&
			(jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
			!wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {

			value = value.replace(rxhtmlTag, fcloseTag);

			try {
				for ( var i = 0, l = this.length; i < l; i++ ) {
					// Remove element nodes and prevent memory leaks
					if ( this[i].nodeType === 1 ) {
						jQuery.cleanData( this[i].getElementsByTagName("*") );
						this[i].innerHTML = value;
					}
				}

			// If using innerHTML throws an exception, use the fallback method
			} catch(e) {
				this.empty().append( value );
			}

		} else if ( jQuery.isFunction( value ) ) {
			this.each(function(i){
				var self = jQuery(this), old = self.html();
				self.empty().append(function(){
					return value.call( this, i, old );
				});
			});

		} else {
			this.empty().append( value );
		}

		return this;
	},

	replaceWith: function( value ) {
		if ( this[0] && this[0].parentNode ) {
			// Make sure that the elements are removed from the DOM before they are inserted
			// this can help fix replacing a parent with child elements
			if ( jQuery.isFunction( value ) ) {
				return this.each(function(i) {
					var self = jQuery(this), old = self.html();
					self.replaceWith( value.call( this, i, old ) );
				});
			}

			if ( typeof value !== "string" ) {
				value = jQuery(value).detach();
			}

			return this.each(function() {
				var next = this.nextSibling, parent = this.parentNode;

				jQuery(this).remove();

				if ( next ) {
					jQuery(next).before( value );
				} else {
					jQuery(parent).append( value );
				}
			});
		} else {
			return this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value );
		}
	},

	detach: function( selector ) {
		return this.remove( selector, true );
	},

	domManip: function( args, table, callback ) {
		var results, first, value = args[0], scripts = [], fragment, parent;

		// We can't cloneNode fragments that contain checked, in WebKit
		if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
			return this.each(function() {
				jQuery(this).domManip( args, table, callback, true );
			});
		}

		if ( jQuery.isFunction(value) ) {
			return this.each(function(i) {
				var self = jQuery(this);
				args[0] = value.call(this, i, table ? self.html() : undefined);
				self.domManip( args, table, callback );
			});
		}

		if ( this[0] ) {
			parent = value && value.parentNode;

			// If we're in a fragment, just use that instead of building a new one
			if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
				results = { fragment: parent };

			} else {
				results = buildFragment( args, this, scripts );
			}
			
			fragment = results.fragment;
			
			if ( fragment.childNodes.length === 1 ) {
				first = fragment = fragment.firstChild;
			} else {
				first = fragment.firstChild;
			}

			if ( first ) {
				table = table && jQuery.nodeName( first, "tr" );

				for ( var i = 0, l = this.length; i < l; i++ ) {
					callback.call(
						table ?
							root(this[i], first) :
							this[i],
						i > 0 || results.cacheable || this.length > 1  ?
							fragment.cloneNode(true) :
							fragment
					);
				}
			}

			if ( scripts.length ) {
				jQuery.each( scripts, evalScript );
			}
		}

		return this;

		function root( elem, cur ) {
			return jQuery.nodeName(elem, "table") ?
				(elem.getElementsByTagName("tbody")[0] ||
				elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
				elem;
		}
	}
});

function cloneCopyEvent(orig, ret) {
	var i = 0;

	ret.each(function() {
		if ( this.nodeName !== (orig[i] && orig[i].nodeName) ) {
			return;
		}

		var oldData = jQuery.data( orig[i++] ), curData = jQuery.data( this, oldData ), events = oldData && oldData.events;

		if ( events ) {
			delete curData.handle;
			curData.events = {};

			for ( var type in events ) {
				for ( var handler in events[ type ] ) {
					jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
				}
			}
		}
	});
}

function buildFragment( args, nodes, scripts ) {
	var fragment, cacheable, cacheresults,
		doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);

	// Only cache "small" (1/2 KB) strings that are associated with the main document
	// Cloning options loses the selected state, so don't cache them
	// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
	// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
	if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
		!rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {

		cacheable = true;
		cacheresults = jQuery.fragments[ args[0] ];
		if ( cacheresults ) {
			if ( cacheresults !== 1 ) {
				fragment = cacheresults;
			}
		}
	}

	if ( !fragment ) {
		fragment = doc.createDocumentFragment();
		jQuery.clean( args, doc, fragment, scripts );
	}

	if ( cacheable ) {
		jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
	}

	return { fragment: fragment, cacheable: cacheable };
}

jQuery.fragments = {};

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 ),
			parent = this.length === 1 && this[0].parentNode;
		
		if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
			insert[ original ]( this[0] );
			return this;
			
		} else {
			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, insert.selector );
		}
	};
});

jQuery.extend({
	clean: function( elems, context, fragment, scripts ) {
		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;
		}

		var ret = [];

		for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
			if ( typeof elem === "number" ) {
				elem += "";
			}

			if ( !elem ) {
				continue;
			}

			// Convert html string into DOM nodes
			if ( typeof elem === "string" && !rhtml.test( elem ) ) {
				elem = context.createTextNode( elem );

			} else if ( typeof elem === "string" ) {
				// Fix "XHTML"-style tags in all browsers
				elem = elem.replace(rxhtmlTag, fcloseTag);

				// Trim whitespace, otherwise indexOf won't work as expected
				var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
					wrap = wrapMap[ tag ] || wrapMap._default,
					depth = wrap[0],
					div = context.createElement("div");

				// Go to html and back, then peel off extra wrappers
				div.innerHTML = wrap[1] + elem + wrap[2];

				// Move to the right depth
				while ( depth-- ) {
					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 = rtbody.test(elem),
						tbody = tag === "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 && rleadingWhitespace.test( elem ) ) {
					div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
				}

				elem = div.childNodes;
			}

			if ( elem.nodeType ) {
				ret.push( elem );
			} else {
				ret = jQuery.merge( ret, elem );
			}
		}

		if ( fragment ) {
			for ( var i = 0; ret[i]; i++ ) {
				if ( scripts && 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 ret;
	},
	
	cleanData: function( elems ) {
		var data, id, cache = jQuery.cache,
			special = jQuery.event.special,
			deleteExpando = jQuery.support.deleteExpando;
		
		for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
			id = elem[ jQuery.expando ];
			
			if ( id ) {
				data = cache[ id ];
				
				if ( data.events ) {
					for ( var type in data.events ) {
						if ( special[ type ] ) {
							jQuery.event.remove( elem, type );

						} else {
							removeEvent( elem, type, data.handle );
						}
					}
				}
				
				if ( deleteExpando ) {
					delete elem[ jQuery.expando ];

				} else if ( elem.removeAttribute ) {
					elem.removeAttribute( jQuery.expando );
				}
				
				delete cache[ id ];
			}
		}
	}
});
// exclude the following css properties to add px
var rexclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
	ralpha = /alpha\([^)]*\)/,
	ropacity = /opacity=([^)]*)/,
	rfloat = /float/i,
	rdashAlpha = /-([a-z])/ig,
	rupper = /([A-Z])/g,
	rnumpx = /^-?\d+(?:px)?$/i,
	rnum = /^-?\d/,

	cssShow = { position: "absolute", visibility: "hidden", display:"block" },
	cssWidth = [ "Left", "Right" ],
	cssHeight = [ "Top", "Bottom" ],

	// cache check for defaultView.getComputedStyle
	getComputedStyle = document.defaultView && document.defaultView.getComputedStyle,
	// normalize float css property
	styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat",
	fcamelCase = function( all, letter ) {
		return letter.toUpperCase();
	};

jQuery.fn.css = function( name, value ) {
	return access( this, name, value, true, function( elem, name, value ) {
		if ( value === undefined ) {
			return jQuery.curCSS( elem, name );
		}
		
		if ( typeof value === "number" && !rexclude.test(name) ) {
			value += "px";
		}

		jQuery.style( elem, name, value );
	});
};

jQuery.extend({
	style: function( elem, name, value ) {
		// don't set styles on text and comment nodes
		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
			return undefined;
		}

		// ignore negative width and height values #1599
		if ( (name === "width" || name === "height") && parseFloat(value) < 0 ) {
			value = undefined;
		}

		var style = elem.style || elem, set = value !== undefined;

		// 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
				style.zoom = 1;

				// Set the alpha filter to set the opacity
				var opacity = parseInt( value, 10 ) + "" === "NaN" ? "" : "alpha(opacity=" + value * 100 + ")";
				var filter = style.filter || jQuery.curCSS( elem, "filter" ) || "";
				style.filter = ralpha.test(filter) ? filter.replace(ralpha, opacity) : opacity;
			}

			return style.filter && style.filter.indexOf("opacity=") >= 0 ?
				(parseFloat( ropacity.exec(style.filter)[1] ) / 100) + "":
				"";
		}

		// Make sure we're using the right name for getting the float value
		if ( rfloat.test( name ) ) {
			name = styleFloat;
		}

		name = name.replace(rdashAlpha, fcamelCase);

		if ( set ) {
			style[ name ] = value;
		}

		return style[ name ];
	},

	css: function( elem, name, force, extra ) {
		if ( name === "width" || name === "height" ) {
			var val, props = cssShow, which = name === "width" ? cssWidth : cssHeight;

			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, filter;

		// IE uses filters for opacity
		if ( !jQuery.support.opacity && name === "opacity" && elem.currentStyle ) {
			ret = ropacity.test(elem.currentStyle.filter || "") ?
				(parseFloat(RegExp.$1) / 100) + "" :
				"";

			return ret === "" ?
				"1" :
				ret;
		}

		// Make sure we're using the right name for getting the float value
		if ( rfloat.test( name ) ) {
			name = styleFloat;
		}

		if ( !force && style && style[ name ] ) {
			ret = style[ name ];

		} else if ( getComputedStyle ) {

			// Only "float" is needed here
			if ( rfloat.test( name ) ) {
				name = "float";
			}

			name = name.replace( rupper, "-$1" ).toLowerCase();

			var defaultView = elem.ownerDocument.defaultView;

			if ( !defaultView ) {
				return null;
			}
      
			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(rdashAlpha, fcamelCase);

			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 ( !rnumpx.test( ret ) && rnum.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 = camelCase === "fontSize" ? "1em" : (ret || 0);
				ret = style.pixelLeft + "px";

				// Revert the changed values
				style.left = left;
				elem.runtimeStyle.left = rsLeft;
			}
		}

		return ret;
	},

	// 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 ];
		}
	}
});

if ( jQuery.expr && jQuery.expr.filters ) {
	jQuery.expr.filters.hidden = function( elem ) {
		var width = elem.offsetWidth, height = elem.offsetHeight,
			skip = elem.nodeName.toLowerCase() === "tr";

		return width === 0 && height === 0 && !skip ?
			true :
			width > 0 && height > 0 && !skip ?
				false :
				jQuery.curCSS(elem, "display") === "none";
	};

	jQuery.expr.filters.visible = function( elem ) {
		return !jQuery.expr.filters.hidden( elem );
	};
}
var jsc = now(),
	rscript = /<script(.|\s)*?\/script>/gi,
	rselectTextarea = /select|textarea/i,
	rinput = /color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,
	jsre = /=\?(&|$)/,
	rquery = /\?/,
	rts = /(\?|&)_=.*?(&|$)/,
	rurl = /^(\w+:)?\/\/([^\/?#]+)/,
	r20 = /%20/g,

	// Keep a copy of the old load method
	_load = jQuery.fn.load;

jQuery.fn.extend({
	load: function( url, params, callback ) {
		if ( typeof url !== "string" ) {
			return _load.call( this, url );

		// Don't do a request if no elements are being requested
		} else if ( !this.length ) {
			return this;
		}

		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, jQuery.ajaxSettings.traditional );
				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(rscript, ""))

							// 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 || rselectTextarea.test(this.nodeName) ||
					rinput.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);
	};
});

jQuery.extend({

	get: function( url, data, callback, type ) {
		// shift arguments if data argument was omited
		if ( jQuery.isFunction( data ) ) {
			type = type || callback;
			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 ) {
		// shift arguments if data argument was omited
		if ( jQuery.isFunction( data ) ) {
			type = type || callback;
			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,
		traditional: false,
		*/
		// Create the request object; Microsoft failed to properly
		// implement the XMLHttpRequest in IE7 (can't request local files),
		// so we use the ActiveXObject when it is available
		// This function can be overriden by calling jQuery.ajaxSetup
		xhr: window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject) ?
			function() {
				return new window.XMLHttpRequest();
			} :
			function() {
				try {
					return new window.ActiveXObject("Microsoft.XMLHTTP");
				} catch(e) {}
			},
		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: {},
	etag: {},

	ajax: function( origSettings ) {
		var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings);
		
		var jsonp, status, data,
			callbackContext = origSettings && origSettings.context || s,
			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, s.traditional );
		}

		// Handle JSONP Parameter Callbacks
		if ( s.dataType === "jsonp" ) {
			if ( type === "GET" ) {
				if ( !jsre.test( s.url ) ) {
					s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
				}
			} else if ( !s.data || !jsre.test(s.data) ) {
				s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
			}
			s.dataType = "json";
		}

		// Build temporary JSONP function
		if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
			jsonp = s.jsonpCallback || ("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 ] = 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(rts, "$1_=" + ts + "$2");

			// if nothing was replaced, add timestamp to the end
			s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
		}

		// If data is available, append data to url for get requests
		if ( s.data && type === "GET" ) {
			s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
		}

		// 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 = rurl.exec( s.url ),
			remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);

		// If we're requesting a remote document
		// and trying to load JSON or Script with a GET
		if ( s.dataType === "script" && type === "GET" && remote ) {
			var head = document.getElementsByTagName("head")[0] || document.documentElement;
			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;
						if ( head && script.parentNode ) {
							head.removeChild( script );
						}
					}
				};
			}

			// Use insertBefore instead of appendChild  to circumvent an IE6 bug.
			// This arises when a base node is used (#2709 and #4378).
			head.insertBefore( script, head.firstChild );

			// We handle everything using the script element injection
			return undefined;
		}

		var requestDone = false;

		// Create the request object
		var xhr = s.xhr();

		if ( !xhr ) {
			return;
		}

		// 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 || origSettings && origSettings.contentType ) {
				xhr.setRequestHeader("Content-Type", s.contentType);
			}

			// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
			if ( s.ifModified ) {
				if ( jQuery.lastModified[s.url] ) {
					xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url]);
				}

				if ( jQuery.etag[s.url] ) {
					xhr.setRequestHeader("If-None-Match", jQuery.etag[s.url]);
				}
			}

			// Set header so the called script knows that it's an XMLHttpRequest
			// Only send the header if it's not a remote XHR
			if ( !remote ) {
				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.call(callbackContext, 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 ) {
			trigger("ajaxSend", [xhr, s]);
		}

		// Wait for a response to come back
		var onreadystatechange = xhr.onreadystatechange = function( isTimeout ) {
			// The request was aborted
			if ( !xhr || xhr.readyState === 0 || isTimeout === "abort" ) {
				// Opera doesn't call onreadystatechange before this point
				// so we simulate the call
				if ( !requestDone ) {
					complete();
				}

				requestDone = true;
				if ( xhr ) {
					xhr.onreadystatechange = jQuery.noop;
				}

			// 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;
				xhr.onreadystatechange = jQuery.noop;

				status = isTimeout === "timeout" ?
					"timeout" :
					!jQuery.httpSuccess( xhr ) ?
						"error" :
						s.ifModified && jQuery.httpNotModified( xhr, s.url ) ?
							"notmodified" :
							"success";

				var errMsg;

				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(err) {
						status = "parsererror";
						errMsg = err;
					}
				}

				// Make sure that the request was successful or notmodified
				if ( status === "success" || status === "notmodified" ) {
					// JSONP handles its own success callback
					if ( !jsonp ) {
						success();
					}
				} else {
					jQuery.handleError(s, xhr, status, errMsg);
				}

				// Fire the complete handlers
				complete();

				if ( isTimeout === "timeout" ) {
					xhr.abort();
				}

				// Stop memory leaks
				if ( s.async ) {
					xhr = null;
				}
			}
		};

		// Override the abort handler, if we can (IE doesn't allow it, but that's OK)
		// Opera doesn't fire onreadystatechange at all on abort
		try {
			var oldAbort = xhr.abort;
			xhr.abort = function() {
				if ( xhr ) {
					oldAbort.call( xhr );
				}

				onreadystatechange( "abort" );
			};
		} catch(e) { }

		// Timeout checker
		if ( s.async && 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( type === "POST" || type === "PUT" || type === "DELETE" ? s.data : null );
		} catch(e) {
			jQuery.handleError(s, xhr, null, e);
			// Fire the complete handlers
			complete();
		}

		// 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.call( callbackContext, data, status, xhr );
			}

			// Fire the global callback
			if ( s.global ) {
				trigger( "ajaxSuccess", [xhr, s] );
			}
		}

		function complete() {
			// Process result
			if ( s.complete ) {
				s.complete.call( callbackContext, xhr, status);
			}

			// The request was completed
			if ( s.global ) {
				trigger( "ajaxComplete", [xhr, s] );
			}

			// Handle the global AJAX counter
			if ( s.global && ! --jQuery.active ) {
				jQuery.event.trigger( "ajaxStop" );
			}
		}
		
		function trigger(type, args) {
			(s.context ? jQuery(s.context) : jQuery.event).trigger(type, args);
		}

		// 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.call( s.context || s, xhr, status, e );
		}

		// Fire the global callback
		if ( s.global ) {
			(s.context ? jQuery(s.context) : 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:" ||
				// Opera returns 0 when status is 304
				( xhr.status >= 200 && xhr.status < 300 ) ||
				xhr.status === 304 || xhr.status === 1223 || xhr.status === 0;
		} catch(e) {}

		return false;
	},

	// Determines if an XMLHttpRequest returns NotModified
	httpNotModified: function( xhr, url ) {
		var lastModified = xhr.getResponseHeader("Last-Modified"),
			etag = xhr.getResponseHeader("Etag");

		if ( lastModified ) {
			jQuery.lastModified[url] = lastModified;
		}

		if ( etag ) {
			jQuery.etag[url] = etag;
		}

		// Opera returns 0 when status is 304
		return xhr.status === 304 || xhr.status === 0;
	},

	httpData: function( xhr, type, s ) {
		var ct = xhr.getResponseHeader("content-type") || "",
			xml = type === "xml" || !type && ct.indexOf("xml") >= 0,
			data = xml ? xhr.responseXML : xhr.responseText;

		if ( xml && data.documentElement.nodeName === "parsererror" ) {
			jQuery.error( "parsererror" );
		}

		// Allow a pre-filtering function to sanitize the response
		// s 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" ) {
			// Get the JavaScript object, if JSON is used.
			if ( type === "json" || !type && ct.indexOf("json") >= 0 ) {
				data = jQuery.parseJSON( data );

			// If the type is "script", eval it in global context
			} else if ( type === "script" || !type && ct.indexOf("javascript") >= 0 ) {
				jQuery.globalEval( data );
			}
		}

		return data;
	},

	// Serialize an array of form elements or a set of
	// key/values into a query string
	param: function( a, traditional ) {
		var s = [];
		
		// Set traditional to true for jQuery <= 1.3.2 behavior.
		if ( traditional === undefined ) {
			traditional = jQuery.ajaxSettings.traditional;
		}
		
		// 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 );
			});
			
		} else {
			// If traditional, encode the "old" way (the way 1.3.2 or older
			// did it), otherwise encode params recursively.
			for ( var prefix in a ) {
				buildParams( prefix, a[prefix] );
			}
		}

		// Return the resulting serialization
		return s.join("&").replace(r20, "+");

		function buildParams( prefix, obj ) {
			if ( jQuery.isArray(obj) ) {
				// Serialize array item.
				jQuery.each( obj, function( i, v ) {
					if ( traditional || /\[\]$/.test( prefix ) ) {
						// Treat each array item as a scalar.
						add( prefix, v );
					} else {
						// If array item is non-scalar (array or object), encode its
						// numeric index to resolve deserialization ambiguity issues.
						// Note that rack (as of 1.0.0) can't currently deserialize
						// nested arrays properly, and attempting to do so may cause
						// a server error. Possible fixes are to modify rack's
						// deserialization algorithm or to provide an option or flag
						// to force array serialization to be shallow.
						buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v );
					}
				});
					
			} else if ( !traditional && obj != null && typeof obj === "object" ) {
				// Serialize object item.
				jQuery.each( obj, function( k, v ) {
					buildParams( prefix + "[" + k + "]", v );
				});
					
			} else {
				// Serialize scalar item.
				add( prefix, obj );
			}
		}

		function add( key, value ) {
			// If value is a function, invoke it and return its value
			value = jQuery.isFunction(value) ? value() : value;
			s[ s.length ] = encodeURIComponent(key) + "=" + encodeURIComponent(value);
		}
	}
});
var elemdisplay = {},
	rfxtypes = /toggle|show|hide/,
	rfxnum = /^([+-]=)?([\d+-.]+)(.*)$/,
	timerId,
	fxAttrs = [
		// height animations
		[ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
		// width animations
		[ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
		// opacity animations
		[ "opacity" ]
	];

jQuery.fn.extend({
	show: function( speed, callback ) {
		if ( speed || speed === 0) {
			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 nodeName = this[i].nodeName, display;

					if ( elemdisplay[ nodeName ] ) {
						display = elemdisplay[ nodeName ];

					} else {
						var elem = jQuery("<" + nodeName + " />").appendTo("body");

						display = elem.css("display");

						if ( display === "none" ) {
							display = "block";
						}

						elem.remove();

						elemdisplay[ nodeName ] = display;
					}

					jQuery.data(this[i], "olddisplay", display);
				}
			}

			// Set the display of the elements in a second loop
			// to avoid the constant reflow
			for ( var j = 0, k = this.length; j < k; j++ ) {
				this[j].style.display = jQuery.data(this[j], "olddisplay") || "";
			}

			return this;
		}
	},

	hide: function( speed, callback ) {
		if ( speed || speed === 0 ) {
			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 j = 0, k = this.length; j < k; j++ ) {
				this[j].style.display = "none";
			}

			return this;
		}
	},

	// Save the old toggle function
	_toggle: jQuery.fn.toggle,

	toggle: function( fn, fn2 ) {
		var bool = typeof fn === "boolean";

		if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
			this._toggle.apply( this, arguments );

		} else if ( fn == null || bool ) {
			this.each(function() {
				var state = bool ? fn : jQuery(this).is(":hidden");
				jQuery(this)[ state ? "show" : "hide" ]();
			});

		} else {
			this.animate(genFx("toggle", 3), fn, fn2);
		}

		return this;
	},

	fadeTo: function( speed, to, callback ) {
		return this.filter(":hidden").css("opacity", 0).show().end()
					.animate({opacity: to}, speed, callback);
	},

	animate: function( prop, speed, easing, callback ) {
		var optall = jQuery.speed(speed, easing, callback);

		if ( jQuery.isEmptyObject( prop ) ) {
			return this.each( optall.complete );
		}

		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 ) {
				var name = p.replace(rdashAlpha, fcamelCase);

				if ( p !== name ) {
					prop[ name ] = prop[ p ];
					delete prop[ p ];
					p = name;
				}

				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 ( jQuery.isArray( prop[p] ) ) {
					// Create (if needed) and add to specialEasing
					(opt.specialEasing = opt.specialEasing || {})[p] = prop[p][1];
					prop[p] = prop[p][0];
				}
			}

			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 ( rfxtypes.test(val) ) {
					e[ val === "toggle" ? hidden ? "show" : "hide" : val ]( prop );

				} else {
					var parts = rfxnum.exec(val),
						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 = speed && 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(jQuery.fx.tick, 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.style( this.elem, 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.style( this.elem, 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(), done = true;

		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;

			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
					var old = jQuery.data(this.elem, "olddisplay");
					this.elem.style.display = old ? old : 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.style(this.elem, 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
			var specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];
			var defaultEasing = this.options.easing || (jQuery.easing.swing ? "swing" : "linear");
			this.pos = jQuery.easing[specialEasing || defaultEasing](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, {
	tick: function() {
		var timers = jQuery.timers;

		for ( var i = 0; i < timers.length; i++ ) {
			if ( !timers[i]() ) {
				timers.splice(i--, 1);
			}
		}

		if ( !timers.length ) {
			jQuery.fx.stop();
		}
	},
		
	stop: function() {
		clearInterval( timerId );
		timerId = null;
	},
	
	speeds: {
		slow: 600,
 		fast: 200,
 		// Default speed
 		_default: 400
	},

	step: {
		opacity: function( fx ) {
			jQuery.style(fx.elem, "opacity", fx.now);
		},

		_default: function( fx ) {
			if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
				fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
			} else {
				fx.elem[ fx.prop ] = fx.now;
			}
		}
	}
});

if ( jQuery.expr && jQuery.expr.filters ) {
	jQuery.expr.filters.animated = function( elem ) {
		return jQuery.grep(jQuery.timers, function( fn ) {
			return elem === fn.elem;
		}).length;
	};
}

function genFx( type, num ) {
	var obj = {};

	jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
		obj[ this ] = type;
	});

	return obj;
}
if ( "getBoundingClientRect" in document.documentElement ) {
	jQuery.fn.offset = function( options ) {
		var elem = this[0];

		if ( options ) { 
			return this.each(function( i ) {
				jQuery.offset.setOffset( this, options, i );
			});
		}

		if ( !elem || !elem.ownerDocument ) {
			return null;
		}

		if ( elem === elem.ownerDocument.body ) {
			return jQuery.offset.bodyOffset( elem );
		}

		var box = elem.getBoundingClientRect(), doc = elem.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.support.boxModel && docElem.scrollTop  || body.scrollTop ) - clientTop,
			left = box.left + (self.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;

		return { top: top, left: left };
	};

} else {
	jQuery.fn.offset = function( options ) {
		var elem = this[0];

		if ( options ) { 
			return this.each(function( i ) {
				jQuery.offset.setOffset( this, options, i );
			});
		}

		if ( !elem || !elem.ownerDocument ) {
			return null;
		}

		if ( elem === elem.ownerDocument.body ) {
			return jQuery.offset.bodyOffset( elem );
		}

		jQuery.offset.initialize();

		var offsetParent = elem.offsetParent, prevOffsetParent = elem,
			doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
			body = doc.body, defaultView = doc.defaultView,
			prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
			top = elem.offsetTop, left = elem.offsetLeft;

		while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
			if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
				break;
			}

			computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
			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.nodeName)) ) {
					top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
					left += parseFloat( computedStyle.borderLeftWidth ) || 0;
				}

				prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
			}

			if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
				top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
				left += parseFloat( computedStyle.borderLeftWidth ) || 0;
			}

			prevComputedStyle = computedStyle;
		}

		if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
			top  += body.offsetTop;
			left += body.offsetLeft;
		}

		if ( jQuery.offset.supportsFixedPosition && 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() {
		var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.curCSS(body, "marginTop", true) ) || 0,
			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>";

		jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );

		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);

		checkDiv.style.position = "fixed", checkDiv.style.top = "20px";
		// safari subtracts parent border width here which is 5px
		this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
		checkDiv.style.position = checkDiv.style.top = "";

		innerDiv.style.overflow = "hidden", innerDiv.style.position = "relative";
		this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);

		this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);

		body.removeChild( container );
		body = container = innerDiv = checkDiv = table = td = null;
		jQuery.offset.initialize = jQuery.noop;
	},

	bodyOffset: function( body ) {
		var top = body.offsetTop, left = body.offsetLeft;

		jQuery.offset.initialize();

		if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
			top  += parseFloat( jQuery.curCSS(body, "marginTop",  true) ) || 0;
			left += parseFloat( jQuery.curCSS(body, "marginLeft", true) ) || 0;
		}

		return { top: top, left: left };
	},
	
	setOffset: function( elem, options, i ) {
		// set position first, in-case top/left are set even on static elem
		if ( /static/.test( jQuery.curCSS( elem, "position" ) ) ) {
			elem.style.position = "relative";
		}
		var curElem   = jQuery( elem ),
			curOffset = curElem.offset(),
			curTop    = parseInt( jQuery.curCSS( elem, "top",  true ), 10 ) || 0,
			curLeft   = parseInt( jQuery.curCSS( elem, "left", true ), 10 ) || 0;

		if ( jQuery.isFunction( options ) ) {
			options = options.call( elem, i, curOffset );
		}

		var props = {
			top:  (options.top  - curOffset.top)  + curTop,
			left: (options.left - curOffset.left) + curLeft
		};
		
		if ( "using" in options ) {
			options.using.call( elem, props );
		} else {
			curElem.css( props );
		}
	}
};


jQuery.fn.extend({
	position: function() {
		if ( !this[0] ) {
			return null;
		}

		var elem = this[0],

		// Get *real* offsetParent
		offsetParent = this.offsetParent(),

		// Get correct offsets
		offset       = this.offset(),
		parentOffset = /^body|html$/i.test(offsetParent[0].nodeName) ? { 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  -= parseFloat( jQuery.curCSS(elem, "marginTop",  true) ) || 0;
		offset.left -= parseFloat( jQuery.curCSS(elem, "marginLeft", true) ) || 0;

		// Add offsetParent borders
		parentOffset.top  += parseFloat( jQuery.curCSS(offsetParent[0], "borderTopWidth",  true) ) || 0;
		parentOffset.left += parseFloat( jQuery.curCSS(offsetParent[0], "borderLeftWidth", true) ) || 0;

		// Subtract the two offsets
		return {
			top:  offset.top  - parentOffset.top,
			left: offset.left - parentOffset.left
		};
	},

	offsetParent: function() {
		return this.map(function() {
			var offsetParent = this.offsetParent || document.body;
			while ( offsetParent && (!/^body|html$/i.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
				offsetParent = offsetParent.offsetParent;
			}
			return offsetParent;
		});
	}
});


// Create scrollLeft and scrollTop methods
jQuery.each( ["Left", "Top"], function( i, name ) {
	var method = "scroll" + name;

	jQuery.fn[ method ] = function(val) {
		var elem = this[0], win;
		
		if ( !elem ) {
			return null;
		}

		if ( val !== undefined ) {
			// Set the scroll offset
			return this.each(function() {
				win = getWindow( this );

				if ( win ) {
					win.scrollTo(
						!i ? val : jQuery(win).scrollLeft(),
						 i ? val : jQuery(win).scrollTop()
					);

				} else {
					this[ method ] = val;
				}
			});
		} else {
			win = getWindow( elem );

			// Return the scroll offset
			return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
				jQuery.support.boxModel && win.document.documentElement[ method ] ||
					win.document.body[ method ] :
				elem[ method ];
		}
	};
});

function getWindow( elem ) {
	return ("scrollTo" in elem && elem.document) ?
		elem :
		elem.nodeType === 9 ?
			elem.defaultView || elem.parentWindow :
			false;
}
// Create innerHeight, innerWidth, outerHeight and outerWidth methods
jQuery.each([ "Height", "Width" ], function( i, name ) {

	var type = name.toLowerCase();

	// innerHeight and innerWidth
	jQuery.fn["inner" + name] = function() {
		return this[0] ?
			jQuery.css( this[0], type, false, "padding" ) :
			null;
	};

	// outerHeight and outerWidth
	jQuery.fn["outer" + name] = function( margin ) {
		return this[0] ?
			jQuery.css( this[0], type, false, margin ? "margin" : "border" ) :
			null;
	};

	jQuery.fn[ type ] = function( size ) {
		// Get window width or height
		var elem = this[0];
		if ( !elem ) {
			return size == null ? null : this;
		}
		
		if ( jQuery.isFunction( size ) ) {
			return this.each(function( i ) {
				var self = jQuery( this );
				self[ type ]( size.call( this, i, self[ type ]() ) );
			});
		}

		return ("scrollTo" in elem && elem.document) ? // does it walk and quack like a window?
			// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
			elem.document.compatMode === "CSS1Compat" && elem.document.documentElement[ "client" + name ] ||
			elem.document.body[ "client" + name ] :

			// Get document width or height
			(elem.nodeType === 9) ? // is it a document
				// Either scroll[Width/Height] or offset[Width/Height], whichever is greater
				Math.max(
					elem.documentElement["client" + name],
					elem.body["scroll" + name], elem.documentElement["scroll" + name],
					elem.body["offset" + name], elem.documentElement["offset" + name]
				) :

				// Get or set width or height on the element
				size === undefined ?
					// Get width or height on the element
					jQuery.css( elem, type ) :

					// Set the width or height on the element (default to pixels if value is unitless)
					this.css( type, typeof size === "string" ? size : size + "px" );
	};

});
// Expose jQuery to the global object
window.jQuery = window.$ = jQuery;

})(window);
/**
 * Ajax Queue Plugin
 * 
 * Homepage: http://jquery.com/plugins/project/ajaxqueue
 * Documentation: http://docs.jquery.com/AjaxQueue
 */

/**

<script>
$(function(){
	jQuery.ajaxQueue({
		url: "test.php",
		success: function(html){ jQuery("ul").append(html); }
	});
	jQuery.ajaxQueue({
		url: "test.php",
		success: function(html){ jQuery("ul").append(html); }
	});
	jQuery.ajaxSync({
		url: "test.php",
		success: function(html){ jQuery("ul").append("<b>"+html+"</b>"); }
	});
	jQuery.ajaxSync({
		url: "test.php",
		success: function(html){ jQuery("ul").append("<b>"+html+"</b>"); }
	});
});
</script>
<ul style="position: absolute; top: 5px; right: 5px;"></ul>

 */
/*
 * Queued Ajax requests.
 * A new Ajax request won't be started until the previous queued 
 * request has finished.
 */

/*
 * Synced Ajax requests.
 * The Ajax request will happen as soon as you call this method, but
 * the callbacks (success/error/complete) won't fire until all previous
 * synced requests have been completed.
 */


(function($) {
	
	var ajax = $.ajax;
	
	var pendingRequests = {};
	
	var synced = [];
	var syncedData = [];
	
	$.ajax = function(settings) {
		// create settings for compatibility with ajaxSetup
		settings = jQuery.extend(settings, jQuery.extend({}, jQuery.ajaxSettings, settings));
		
		var port = settings.port;
		
		switch(settings.mode) {
		case "abort": 
			if ( pendingRequests[port] ) {
				pendingRequests[port].abort();
			}
			return pendingRequests[port] = ajax.apply(this, arguments);
		case "queue": 
			var _old = settings.complete;
			settings.complete = function(){
				if ( _old )
					_old.apply( this, arguments );
				jQuery([ajax]).dequeue("ajax" + port );;
			};
		
			jQuery([ ajax ]).queue("ajax" + port, function(){
				ajax( settings );
			});
			return;
		case "sync":
			var pos = synced.length;
	
			synced[ pos ] = {
				error: settings.error,
				success: settings.success,
				complete: settings.complete,
				done: false
			};
		
			syncedData[ pos ] = {
				error: [],
				success: [],
				complete: []
			};
		
			settings.error = function(){ syncedData[ pos ].error = arguments; };
			settings.success = function(){ syncedData[ pos ].success = arguments; };
			settings.complete = function(){
				syncedData[ pos ].complete = arguments;
				synced[ pos ].done = true;
		
				if ( pos == 0 || !synced[ pos-1 ] )
					for ( var i = pos; i < synced.length && synced[i].done; i++ ) {
						if ( synced[i].error ) synced[i].error.apply( jQuery, syncedData[i].error );
						if ( synced[i].success ) synced[i].success.apply( jQuery, syncedData[i].success );
						if ( synced[i].complete ) synced[i].complete.apply( jQuery, syncedData[i].complete );
		
						synced[i] = null;
						syncedData[i] = null;
					}
			};
		}
		return ajax.apply(this, arguments);
	};
	
})(jQuery);
/*
 * jQuery Asynchronous Plugin 1.0 RC1
 *
 * Copyright (c) 2008 Vincent Robert (genezys.net)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 */
(function($){

// opts.delay : (default 10) delay between async call in ms
// opts.bulk : (default 500) delay during which the loop can continue synchronously without yielding the CPU
// opts.test : (default true) function to test in the while test part
// opts.loop : (default empty) function to call in the while loop part
// opts.end : (default empty) function to call at the end of the while loop
$.whileAsync = function(opts)
{
	var delay = Math.abs(opts.delay) || 10,
		bulk = isNaN(opts.bulk) ? 500 : Math.abs(opts.bulk),
		test = opts.test || function(){ return true; },
		loop = opts.loop || function(){},
		end  = opts.end  || function(){};
  
  // extra configuration information or data to be used in loop, test, or end functions
  // this is local to whileAsync, so will be available in closure defined below
  var cfg = opts.cfg || {};
	
	(function(){ // closure

		var t = false, 
			begin = new Date();
			
		while( t = test(cfg) )
		{
			loop(cfg);
			if( bulk === 0 || (new Date() - begin) > bulk )
			{
				break;
			}
		}
		if( t )
		{
			setTimeout(arguments.callee, delay);
		}
		else
		{
			end(cfg);
		}
		
	})();
};

// opts.delay : (default 10) delay between async call in ms
// opts.bulk : (default 500) delay during which the loop can continue synchronously without yielding the CPU
// opts.loop : (default empty) function to call in the each loop part, signature: function(index, value) this = value
// opts.end : (default empty) function to call at the end of the each loop
$.eachAsync = function(array, opts)
{
	var i = 0, 
		l = array.length, 
		loop = opts.loop || function(){};
	
	$.whileAsync(
		$.extend(opts, {
			test: function(){ return i < l; },
			loop: function()
			{ 
				var val = array[i];
				return loop.call(val, i++, val);
			}
		})
	);
};

$.fn.eachAsync = function(opts)
{
	$.eachAsync(this, opts);
	return this;
};


})(jQuery);
/*
 * Autocomplete - jQuery plugin 1.0
 *
 * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, JĂ¶rn Zaefferer
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Revision: $Id: jquery.autocomplete.js 5329 2008-04-27 13:07:34Z joern.zaefferer $
 *
 */

;(function($) {
  
$.fn.extend({
  autocomplete: function(urlOrData, options) {
    var isUrl = typeof urlOrData == "string";
    options = $.extend({}, $.Autocompleter.defaults, {
      url: isUrl ? urlOrData : null,
      data: isUrl ? null : urlOrData,
      delay: isUrl ? $.Autocompleter.defaults.delay : 10,
      max: options && !options.scroll ? 10 : 150
    }, options);
    
    // if highlight is set to false, replace it with a do-nothing function
    options.highlight = options.highlight || function(value) { return value; };
    
    // if the formatMatch option is not specified, then use formatItem for backwards compatibility
    options.formatMatch = options.formatMatch || options.formatItem;
    
    return this.each(function() {
      new $.Autocompleter(this, options);
    });
  },
  result: function(handler) {
    return this.bind("result", handler);
  },
  search: function(handler) {
    return this.trigger("search", [handler]);
  },
  flushCache: function() {
    return this.trigger("flushCache");
  },
  setOptions: function(options){
    return this.trigger("setOptions", [options]);
  },
  unautocomplete: function() {
    return this.trigger("unautocomplete");
  }
});

$.Autocompleter = function(input, options) {

  var KEY = {
    UP: 38,
    DOWN: 40,
    DEL: 46,
    TAB: 9,
    RETURN: 13,
    ESC: 27,
    COMMA: 188,
    PAGEUP: 33,
    PAGEDOWN: 34,
    BACKSPACE: 8
  };

  // Create $ object for input element
  var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);

  var timeout;
  var previousValue = "";
  var cache = $.Autocompleter.Cache(options);
  var hasFocus = 0;
  var lastKeyPressCode;
  var config = {
    mouseDownOnSelect: false
  };
  var select = $.Autocompleter.Select(options, input, selectCurrent, config);
  
  $input.keydown(function(event) {
    // track last key pressed
    lastKeyPressCode = event.keyCode;
    switch(event.keyCode) {
    
      case KEY.UP:
        event.preventDefault();
        if ( select.visible() ) {
          select.prev();
        } else {
          onChange(0, true);
        }
        break;
        
      case KEY.DOWN:
        event.preventDefault();
        if ( select.visible() ) {
          select.next();
        } else {
          onChange(0, true);
        }
        break;
        
      case KEY.PAGEUP:
        event.preventDefault();
        if ( select.visible() ) {
          select.pageUp();
        } else {
          onChange(0, true);
        }
        break;
        
      case KEY.PAGEDOWN:
        event.preventDefault();
        if ( select.visible() ) {
          select.pageDown();
        } else {
          onChange(0, true);
        }
        break;
      
      // matches also semicolon
      case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
      //case KEY.TAB:
      case KEY.RETURN:
        // MOD : only select item from list if list is visible
        // if list not visible, run lookup immediately
        //if (typeof (event.which) != 'undefined'){
          event.preventDefault();
          if ( select.visible() ){
            selectCurrent();
          }
          else {
            onChange();
          }
          this.select();
        // }
        // else {
          // fblog(event.which);
          // select.hide();
        // }
        // MOD END

        // if( selectCurrent() ){
          // // make sure to blur off the current field
          // //if( !options.multiple )
            // //$input.blur();
          // event.preventDefault();
        // }
        break;
        
      case KEY.ESC:
        select.hide();
        break;
        
      default:
      
        // MOD : filter which keys should trigger onChange event
        var k = event.keyCode;
        if (
          ((48 <= k) && (k <= 90)) ||         // alphanumeric keys
          ((96 <= k) && (k <= 111)) ||        // numeric keypad
          ((186 <= k) && (k <= 222)) ||       // symbols
          k == 8 ||                           // backspace
          k == 46 ||                          // delete
          k == 32                             // space
        ){
          clearTimeout(timeout);
          timeout = setTimeout(onChange, options.delay);
        }
        // else {
          // console.log('uncaught key ' + event.keyCode);
        // }
        // MOD END

        // clearTimeout(timeout);
        // timeout = setTimeout(onChange, options.delay);
        break;
    }
  }).keypress(function() {
    // having fun with opera - remove this binding and Opera submits the form when we select an entry via return
  }).focus(function(){
    // track whether the field has focus, we shouldn't process any
    // results if the field no longer has focus
    hasFocus++;
  }).blur(function() {
    hasFocus = 0;
    // MOD : always hide results on blur
    if (!config.mouseDownOnSelect) {
      hideResults();
    }
  }).click(function() {
    // show select when clicking in a focused field
    if ( hasFocus++ > 1 && !select.visible() ) {
      onChange(0, true);
    }
  }).bind("lookup", function(){
    onChange(0, true);
  }).bind("search", function() {
    // TODO why not just specifying both arguments?
    var fn = (arguments.length > 1) ? arguments[1] : null;
    function findValueCallback(q, data) {
      var result;
      if( data && data.length ) {
        for (var i=0; i < data.length; i++) {
          if( data[i].result.toLowerCase() == q.toLowerCase() ) {
            result = data[i];
            break;
          }
        }
      }
      if( typeof fn == "function" ) {
        fn(result);
      }
      else {
        $input.trigger("result", result && [result.data, result.value]);
      }
    }
    $.each(trimWords($input.val()), function(i, value) {
      request(value, findValueCallback, findValueCallback);
    });
  }).bind("flushCache", function() {
    cache.flush();
  }).bind("setOptions", function() {
    $.extend(options, arguments[1]);
    // if we've updated the data, repopulate
    if ( "data" in arguments[1] )
      cache.populate();
  }).bind("unautocomplete", function() {
    select.unbind();
    $input.unbind();
  });
  
  
  function selectCurrent() {
    var selected = select.selected();
    if( !selected )
      return false;
    
    var v = selected.result;
    previousValue = v;
    
    if ( options.multiple ) {
      var words = trimWords($input.val());
      if ( words.length > 1 ) {
        v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v;
      }
      v += options.multipleSeparator;
    }
    
    // MOD : option to prevent changing content of input field after selecting from results
    if (!options.persistInput){ // MOD
      $input.val(v);
    } // MOD END
    //$input.val(v);
    hideResultsNow();
    
    $input.addClass(options.selectedClass);
    
    $input.trigger("result", [selected.data, selected.value]);
    return true;
  }
  
  function onChange(crap, skipPrevCheck) {
    // MOD : commented out to allow onChange to execute even if DEL key pressed
    // if( lastKeyPressCode == KEY.DEL ) {
      // select.hide();
      // return;
    // }
    // MOD END
    
    var currentValue = $input.val();
    
    // if ( !skipPrevCheck && currentValue == previousValue )
      // return;
    
    previousValue = currentValue;
    
    currentValue = lastWord(currentValue);
    
    if (options.trim){
      currentValue = $.trim(currentValue);
    }
    
    $input.removeClass(options.inputInvalidClass);
    $input.removeClass(options.selectedClass);
      
    if ( currentValue.length >= options.minChars) {
      $input.addClass(options.loadingClass);
      if (options.loadingProc && typeof options.loadingProc == 'function'){
        if (!options.loadingProc($input, true)){
          stopLoading();
        }
      }
      if (!options.matchCase)
        currentValue = currentValue.toLowerCase();
        
      // MOD : allow changes to be made to content of query before sending
      if (options.preparse){
        for (token in options.preparse){
          currentValue = currentValue.replace(new RegExp(token, 'gi'), options.preparse[token]);
        }
      }
      // MOD END
      
      setTimeout(function(){
        request(currentValue, receiveData, notFound);
      }, 25);
      
      // MOD support custom failure routine
      // request(
        // currentValue, 
        // function(term, data, element){
          // element.removeClass(options.inputInvalidClass);
          // receiveData(term, data);
        // },
        // function(term, element){
          // hideResultsNow();
        
          // // fblog('failure');
          // // fbdir(element);
          // element.addClass(options.inputInvalidClass);
        // }
      // );
      // MOD
      
    } else {
      stopLoading();
      select.hide();
    }
  };
  
  function trimWords(value) {
    if ( !value ) {
      return [""];
    }
    var words = value.split( options.multipleSeparator );
    var result = [];
    $.each(words, function(i, value) {
      if ( $.trim(value) )
        result[i] = $.trim(value);
    });
    return result;
  }
  
  function lastWord(value) {
    if ( !options.multiple )
      return value;
    var words = trimWords(value);
    return words[words.length - 1];
  }
  
  // fills in the input box w/the first match (assumed to be the best match)
  // q: the term entered
  // sValue: the first matching result
  function autoFill(q, sValue){
    // autofill in the complete box w/the first match as long as the user hasn't entered in more data
    // if the last user key pressed was backspace, don't autofill
    if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
      // fill in the value (keep the case the user has typed)
      $input.val($input.val() + sValue.substring(lastWord(previousValue).length));
      // select the portion of the value not typed by the user (so the next character will erase)
      $.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length);
    }
  };

  function hideResults() {
    clearTimeout(timeout);
    timeout = setTimeout(hideResultsNow, 200);
  };

  function notFound() {
    if ($input.val().length >= options.minChars){
      //console.log('input invalid');
      $input.addClass(options.inputInvalidClass);
    }
    hideResultsNow();
  }
  
  function hideResultsNow() {
    select.hide();
    clearTimeout(timeout);
    stopLoading();
    // if ($input.val().length >= options.minChars){
      // console.log('input invalid');
      // $input.addClass(options.inputInvalidClass);
    // }
    // else {
      // $input.removeClass(options.inputInvalidClass);
    // }
    if (options.mustMatch) {
      // call search and run callback
      $input.search(
        function (result){
          // if no value found, clear the input box
          if( !result ) $input.val("");
        }
      );
    }
  };

  function receiveData(q, data) {
    if ( data && data.length && hasFocus ) {
      stopLoading();
      select.display(data, q);
      autoFill(q, data[0].value);
      select.show();
    } else {
      hideResultsNow();
    }
  };

  function request(term, success, failure) {
    if (!options.matchCase)
      term = term.toLowerCase();
    var data = cache.load(term);
    // recieve the cached data
    if (data && data.length) {
      success(term, data, $input);
    // if an AJAX url has been supplied, try loading the data now
    } else if( (typeof options.url == "string") && (options.url.length > 0) ){
      
      var extraParams = {
        timestamp: +new Date()
      };
      $.each(options.extraParams, function(key, param) {
        // MOD : pass input control to param function for scope
        extraParams[key] = typeof param == "function" ? param($input) : param;
        // MOD end
        //extraParams[key] = typeof param == "function" ? param() : param;
      });
      
      $.ajax({
        // try to leverage ajaxQueue plugin to abort previous requests
        mode: "abort",
        // limit abortion to this input
        port: "autocomplete" + input.name,
        dataType: options.dataType,
        url: options.url,
        data: $.extend({
          q: lastWord(term),
          limit: options.max
        }, extraParams),
        success: function(data) {
          var parsed = options.parse && options.parse(data) || parse(data);
          cache.add(term, parsed);
          success(term, parsed);
        }
      });
    } else {
      // if we have a failure, we need to empty the list -- this prevents the [TAB] key from selecting the last successful match
      select.emptyList();
      failure(term, $input);
    }
  };
  
  function parse(data) {
    var parsed = [];
    var rows = data.split("\n");
    for (var i=0; i < rows.length; i++) {
      var row = $.trim(rows[i]);
      if (row) {
        row = row.split("|");
        parsed[parsed.length] = {
          data: row,
          value: row[0],
          result: options.formatResult && options.formatResult(row, row[0]) || row[0]
        };
      }
    }
    return parsed;
  };

  function stopLoading() {
    $input.removeClass(options.loadingClass);
    if (options.loadingProc && typeof options.loadingProc == 'function'){
      options.loadingProc($input, false);
    }
  };

};

$.Autocompleter.defaults = {
  inputClass: "ac_input",
  inputInvalidClass: "",
  resultsClass: "ac_results",
  loadingClass: '', //"ac_loading",
  selectedClass: '',
  loadingProc: null,
  minChars: 1,
  trim: false,
  preparse: null,
  delay: 400,
  matchCase: false,
  matchSubset: true,
  matchContains: false,
  cacheLength: 10,
  max: 100,
  mustMatch: false,
  extraParams: {},
  selectFirst: true,
  formatItem: function(row) { return row[0]; },
  formatMatch: null,
  autoFill: false,
  persistInput: false,
  width: 0,
  multiple: false,
  multipleSeparator: ", ",
  highlight: function(value, term){
    return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
  },
  scroll: true,
  scrollHeight: 180
};

$.Autocompleter.Cache = function(options) {

  var data = {};
  var length = 0;
  
  function matchSubset(s, sub) {
    var wordMatch = false;
    if (sub.length == 1 && !isNaN(Number(sub[0]))){
      wordMatch = true;
    }
    
    if (!options.matchCase) 
      s = s.toLowerCase();
      
    // MOD : allow changes to be made to content of query before sending
    if (options.preparse){
      for (token in options.preparse){
        s = s.replace(new RegExp(token, 'gi'), options.preparse[token]);
      }
    }
    // MOD END
      
      
    // MOD treat sub as an array of query terms
    var i;
    for (var j in sub){
      // MOD add support for whole word matches
      if (wordMatch){
        var words = s.split(' ');
        i = $.inArray(sub[j], words);
      }
      else {
        i = s.indexOf(sub[j]);
      }
      // END MOD
      if (i == -1) return false;
    }
    // END MOD
    
    return i == 0 || options.matchContains;
  };
  
  function add(q, value) {
    if (length > options.cacheLength){
      flush();
    }
    if (!data[q]){ 
      length++;
    }
    data[q] = value;
  }
  
  function populate(){
    if( !options.data ) return false;
    
    // track the matches
    var stMatchSets = {},
      nullData = 0;

    // no url was specified, we need to adjust the cache length to make sure it fits the local data store
    if( !options.url ) options.cacheLength = 1;
    
    // track all options for minChars = 0
    stMatchSets[""] = [];
    
    // loop through the array and create a lookup structure
    for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
      var rawValue = options.data[i];
      // if rawValue is a string, make an array otherwise just reference the array
      rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
      
      var value = options.formatMatch(rawValue, i+1, options.data.length);
      if ( value === false )
        continue;
        
      var firstChar = value.charAt(0).toLowerCase();
      
      // if no lookup array for this character exists, look it up now
      if( !stMatchSets[firstChar] ) 
        stMatchSets[firstChar] = [];

      // if the match is a string
      var row = {
        value: value,
        data: rawValue,
        result: options.formatResult && options.formatResult(rawValue) || value
      };
      
      // push the current match into the set list
      stMatchSets[firstChar].push(row);

      // keep track of minChars zero items
      if ( nullData++ < options.max ) {
        stMatchSets[""].push(row);
      }
    };

    // add the data items to the cache
    $.each(stMatchSets, function(i, value) {
      // increase the cache size
      options.cacheLength++;
      // add to the cache
      add(i, value);
    });
    
  }
  
  // populate any existing data
  setTimeout(populate, 25);
  
  function flush(){
    data = {};
    length = 0;
  }
  
  return {
    flush: flush,
    add: add,
    populate: populate,
    load: function(q) {
    
      // MOD split query by spaces to search for multiple terms
      // this will be passed into modified matchSubset() function
      q = q.split(' ');
      // END MOD
      
      if (!options.cacheLength || !length)
        return null;
      /* 
       * if dealing w/local data and matchContains than we must make sure
       * to loop through all the data collections looking for matches
       */
      if( !options.url && options.matchContains ){
      
        // track all matches
        var csub = [];
        
        
        // MOD ensure object properties are called in alpha order
        var names = [];
        var i;
        
        for (i in data){
          if (data.hasOwnProperty(i) && typeof data[i] != 'function'){
            names.push(i);
          }
        }
        
        names.sort();
        // END MOD
        
        
        // loop through all the data grids for matches
        // for (var k in data){
        var k;
        for (i = 0; i < names.length; i++){
          k = names[i];
          // console.log(['this data = ', k].join(''));
          // don't search through the stMatchSets[""] (minChars: 0) cache
          // this prevents duplicates
          if( k.length > 0 ){
            var c = data[k];
            $.each(c, function(i, x) {
              // if we've got a match, add it to the array
              if (matchSubset(x.value, q)) {
                csub.push(x);
              }
            });
          }
        }        
        return csub;
      } else 
      // if the exact item exists, use it
      // if (data[q]){
        // return data[q];
      // } else
      if (options.matchSubset) {
        for (var i = q.length - 1; i >= options.minChars; i--) {
          var c = data[q.substr(0, i)];
          if (c) {
            var csub = [];
            $.each(c, function(i, x) {
              if (matchSubset(x.value, q)) {
                csub[csub.length] = x;
              }
            });
            return csub;
          }
        }
      }
      return null;
    }
  };
};

$.Autocompleter.Select = function (options, input, select, config) {
  var CLASSES = {
    ACTIVE: "ac_over"
  };
  
  var listItems,
    active = -1,
    data,
    term = "",
    needsInit = true,
    element,
    list;
  
  // Create results
  function init() {
    if (!needsInit)
      return;
    element = $("<div/>")
    .hide()
    .addClass(options.resultsClass)
    .css("position", "absolute")
    .appendTo(document.body);
    
    
    
    // MOD - add close link to corner of results DIV
    // since IE doesn't correctly support blur event for scrollbar in DIV, it will stay visible until a result is selected
    // this lets you force the results to close without selecting anything
    closeButton = $('<a class="ac_closeButton"></a>').appendTo(element).click(function(event){
      //hideResultsNow();
      $(event.target).parent().hide();
      //fblog('ac_closeButton clicked');
      //fbdir($(event.target).parent());
    });
    // MOD end
    
    
  
    list = $("<ul>").appendTo(element).mouseover( function(event) {
      if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
              active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
          $(target(event)).addClass(CLASSES.ACTIVE);            
          }
    }).click(function(event) {
      $(target(event)).addClass(CLASSES.ACTIVE);
      select();
      input.focus();
      return false;
    }).mousedown(function() {
      config.mouseDownOnSelect = true;
    }).mouseup(function() {
      config.mouseDownOnSelect = false;
    });
    
    if( options.width > 0 )
      element.css("width", options.width);
      
    needsInit = false;
  } 
  
  function target(event) {
    var element = event.target;
    while(element && element.tagName != "LI")
      element = element.parentNode;
    // more fun with IE, sometimes event.target is empty, just ignore it then
    if(!element)
      return [];
    return element;
  }

  function moveSelect(step) {
    listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
    movePosition(step);
        var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
        if(options.scroll) {
            var offset = 0;
            listItems.slice(0, active).each(function() {
                offset += this.offsetHeight;
            });
            
            var offsetDiff = Math.ceil(listItems.length / 15);
            if((offset + activeItem[0].offsetHeight - list.scrollTop() + offsetDiff) > list[0].clientHeight) {
                list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight() + offsetDiff);
            } else if(offset < list.scrollTop()) {
                list.scrollTop(offset);
            }
        }
  };
  
  function movePosition(step) {
    active += step;
    if (active < 0) {
      active = listItems.size() - 1;
    } else if (active >= listItems.size()) {
      active = 0;
    }
  }
  
  function limitNumberOfItems(available) {
    return options.max && options.max < available
      ? options.max
      : available;
  }
  
  function fillList() {
    list.empty();
    var max = limitNumberOfItems(data.length);
    
    for (var i=0; i < max; i++) {
      if (!data[i])
        continue;
      
      // MOD : moved highlight before format call to ensure highlighting only applies to returned value
      var highlighted = options.highlight(data[i].value, term); // MOD
      var formatted = options.formatItem(data[i].data, i+1, max, highlighted, term);
      //var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
      if ( formatted === false )
        continue;
      var li = $("<li>").html( formatted ).addClass(i%2 == 0 ? "ac_event" : "ac_odd").appendTo(list)[0]; // MOD
      //var li = $("<li>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_event" : "ac_odd").appendTo(list)[0];
      $.data(li, "ac_data", data[i]);
    }
    listItems = list.find("li");
    if ( options.selectFirst ) {
      listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
      active = 0;
    }
    list.bgiframe();
  }
  
  return {
    display: function(d, q) {
      init();
      data = d;
      term = q;
      fillList();
    },
    next: function() {
      moveSelect(1);
    },
    prev: function() {
      moveSelect(-1);
    },
    pageUp: function() {
      if (active != 0 && active - 8 < 0) {
        moveSelect( -active );
      } else {
        moveSelect(-8);
      }
    },
    pageDown: function() {
      if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
        moveSelect( listItems.size() - 1 - active );
      } else {
        moveSelect(8);
      }
    },
    hide: function() {
      element && element.hide();
      active = -1;
    },
    visible : function() {
      return element && element.is(":visible");
    },
    current: function() {
      return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
    },
    show: function() {
      var offset = $(input).offset();
      element.css({
        width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
        top: offset.top + $(input).outerHeight(),
        left: offset.left
      }).show();
            if(options.scroll) {
                list.scrollTop(0);
                list.css({
          maxHeight: options.scrollHeight,
          overflow: 'auto'
        });
        
                if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
          var listHeight = 0;
          listItems.each(function() {
            listHeight += this.offsetHeight;
          });
          var scrollbarsVisible = listHeight > options.scrollHeight;
                    list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
          if (!scrollbarsVisible) {
            // IE doesn't recalculate width when scrollbar disappears
            listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
          }
                }
                
            }
    },
    selected: function() {
      var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
      return selected && selected.length && $.data(selected[0], "ac_data");
    },
    emptyList: function (){
      list && list.empty();
    },
    unbind: function() {
      element && element.remove();
    }
  };
};

$.Autocompleter.Selection = function(field, start, end) {
  if( field.createTextRange ){
    var selRange = field.createTextRange();
    selRange.collapse(true);
    selRange.moveStart("character", start);
    selRange.moveEnd("character", end);
    selRange.select();
  } else if( field.setSelectionRange ){
    field.setSelectionRange(start, end);
  } else {
    if( field.selectionStart ){
      field.selectionStart = start;
      field.selectionEnd = end;
    }
  }
  field.focus();
};

})(jQuery);
/*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net)
 * Licensed under the MIT License (LICENSE.txt).
 *
 * Version 2.1.2
 */

(function($){

$.fn.bgiframe = ($.browser.msie && /msie 6\.0/i.test(navigator.userAgent) ? function(s) {
    s = $.extend({
        top     : 'auto', // auto == .currentStyle.borderTopWidth
        left    : 'auto', // auto == .currentStyle.borderLeftWidth
        width   : 'auto', // auto == offsetWidth
        height  : 'auto', // auto == offsetHeight
        opacity : true,
        src     : 'javascript:false;'
    }, s);
    var html = '<iframe class="bgiframe"frameborder="0"tabindex="-1"src="'+s.src+'"'+
                   'style="display:block;position:absolute;z-index:-1;'+
                       (s.opacity !== false?'filter:Alpha(Opacity=\'0\');':'')+
                       'top:'+(s.top=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderTopWidth)||0)*-1)+\'px\')':prop(s.top))+';'+
                       'left:'+(s.left=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderLeftWidth)||0)*-1)+\'px\')':prop(s.left))+';'+
                       'width:'+(s.width=='auto'?'expression(this.parentNode.offsetWidth+\'px\')':prop(s.width))+';'+
                       'height:'+(s.height=='auto'?'expression(this.parentNode.offsetHeight+\'px\')':prop(s.height))+';'+
                '"/>';
    return this.each(function() {
        if ( $(this).children('iframe.bgiframe').length === 0 )
            this.insertBefore( document.createElement(html), this.firstChild );
    });
} : function() { return this; });

// old alias
$.fn.bgIframe = $.fn.bgiframe;

function prop(n) {
    return n && n.constructor === Number ? n + 'px' : n;
}

})(jQuery);
ď»ż/*!
 * jQuery blockUI plugin
 * Version 2.33 (29-MAR-2010)
 * @requires jQuery v1.2.3 or later
 *
 * Examples at: http://malsup.com/jquery/block/
 * Copyright (c) 2007-2008 M. Alsup
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 * Thanks to Amir-Hossein Sobhi for some excellent contributions!
 */

;(function($) {

if (/1\.(0|1|2)\.(0|1|2)/.test($.fn.jquery) || /^1.1/.test($.fn.jquery)) {
	alert('blockUI requires jQuery v1.2.3 or later!  You are using v' + $.fn.jquery);
	return;
}

$.fn._fadeIn = $.fn.fadeIn;

var noOp = function() {};

// this bit is to ensure we don't call setExpression when we shouldn't (with extra muscle to handle
// retarded userAgent strings on Vista)
var mode = document.documentMode || 0;
var setExpr = $.browser.msie && (($.browser.version < 8 && !mode) || mode < 8);
var ie6 = $.browser.msie && /MSIE 6.0/.test(navigator.userAgent) && !mode;

// global $ methods for blocking/unblocking the entire page
$.blockUI   = function(opts) { install(window, opts); };
$.unblockUI = function(opts) { remove(window, opts); };

// convenience method for quick growl-like notifications  (http://www.google.com/search?q=growl)
$.growlUI = function(title, message, timeout, onClose) {
	var $m = $('<div class="growlUI"></div>');
	if (title) $m.append('<h1>'+title+'</h1>');
	if (message) $m.append('<h2>'+message+'</h2>');
	if (timeout == undefined) timeout = 3000;
	$.blockUI({
		message: $m, fadeIn: 700, fadeOut: 1000, centerY: false,
		timeout: timeout, showOverlay: false,
		onUnblock: onClose, 
		css: $.blockUI.defaults.growlCSS
	});
};

// plugin method for blocking element content
$.fn.block = function(opts) {
	return this.unblock({ fadeOut: 0 }).each(function() {
		if ($.css(this,'position') == 'static')
			this.style.position = 'relative';
		if ($.browser.msie)
			this.style.zoom = 1; // force 'hasLayout'
		install(this, opts);
	});
};

// plugin method for unblocking element content
$.fn.unblock = function(opts) {
	return this.each(function() {
		remove(this, opts);
	});
};

$.blockUI.version = 2.33; // 2nd generation blocking at no extra cost!

// override these in your code to change the default behavior and style
$.blockUI.defaults = {
	// message displayed when blocking (use null for no message)
	message:  '<h1>Please wait...</h1>',

	title: null,	  // title string; only used when theme == true
	draggable: true,  // only used when theme == true (requires jquery-ui.js to be loaded)
	
	theme: false, // set to true to use with jQuery UI themes
	
	// styles for the message when blocking; if you wish to disable
	// these and use an external stylesheet then do this in your code:
	// $.blockUI.defaults.css = {};
	css: {
		padding:	0,
		margin:		0,
		width:		'30%',
		top:		'40%',
		left:		'35%',
		textAlign:	'center',
		color:		'#000',
		border:		'3px solid #aaa',
		backgroundColor:'#fff',
		cursor:		'wait'
	},
	
	// minimal style set used when themes are used
	themedCSS: {
		width:	'30%',
		top:	'40%',
		left:	'35%'
	},

	// styles for the overlay
	overlayCSS:  {
		backgroundColor: '#000',
		opacity:	  	 0.6,
		cursor:		  	 'wait'
	},

	// styles applied when using $.growlUI
	growlCSS: {
		width:  	'350px',
		top:		'10px',
		left:   	'',
		right:  	'10px',
		border: 	'none',
		padding:	'5px',
		opacity:	0.6,
		cursor: 	'default',
		color:		'#fff',
		backgroundColor: '#000',
		'-webkit-border-radius': '10px',
		'-moz-border-radius':	 '10px',
		'border-radius': 		 '10px'
	},
	
	// IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w
	// (hat tip to Jorge H. N. de Vasconcelos)
	iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank',

	// force usage of iframe in non-IE browsers (handy for blocking applets)
	forceIframe: false,

	// z-index for the blocking overlay
	baseZ: 1000,

	// set these to true to have the message automatically centered
	centerX: true, // <-- only effects element blocking (page block controlled via css above)
	centerY: true,

	// allow body element to be stetched in ie6; this makes blocking look better
	// on "short" pages.  disable if you wish to prevent changes to the body height
	allowBodyStretch: true,

	// enable if you want key and mouse events to be disabled for content that is blocked
	bindEvents: true,

	// be default blockUI will supress tab navigation from leaving blocking content
	// (if bindEvents is true)
	constrainTabKey: true,

	// fadeIn time in millis; set to 0 to disable fadeIn on block
	fadeIn:  200,

	// fadeOut time in millis; set to 0 to disable fadeOut on unblock
	fadeOut:  400,

	// time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock
	timeout: 0,

	// disable if you don't want to show the overlay
	showOverlay: true,

	// if true, focus will be placed in the first available input field when
	// page blocking
	focusInput: true,

	// suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity)
	applyPlatformOpacityRules: true,
	
	// callback method invoked when fadeIn has completed and blocking message is visible
	onBlock: null,

	// callback method invoked when unblocking has completed; the callback is
	// passed the element that has been unblocked (which is the window object for page
	// blocks) and the options that were passed to the unblock call:
	//	 onUnblock(element, options)
	onUnblock: null,

	// don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493
	quirksmodeOffsetHack: 4
};

// private data and functions follow...

var pageBlock = null;
var pageBlockEls = [];

function install(el, opts) {
	var full = (el == window);
	var msg = opts && opts.message !== undefined ? opts.message : undefined;
	opts = $.extend({}, $.blockUI.defaults, opts || {});
	opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {});
	var css = $.extend({}, $.blockUI.defaults.css, opts.css || {});
	var themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {});
	msg = msg === undefined ? opts.message : msg;

	// remove the current block (if there is one)
	if (full && pageBlock)
		remove(window, {fadeOut:0});

	// if an existing element is being used as the blocking content then we capture
	// its current place in the DOM (and current display style) so we can restore
	// it when we unblock
	if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) {
		var node = msg.jquery ? msg[0] : msg;
		var data = {};
		$(el).data('blockUI.history', data);
		data.el = node;
		data.parent = node.parentNode;
		data.display = node.style.display;
		data.position = node.style.position;
		if (data.parent)
			data.parent.removeChild(node);
	}

	var z = opts.baseZ;

	// blockUI uses 3 layers for blocking, for simplicity they are all used on every platform;
	// layer1 is the iframe layer which is used to supress bleed through of underlying content
	// layer2 is the overlay layer which has opacity and a wait cursor (by default)
	// layer3 is the message content that is displayed while blocking

	var lyr1 = ($.browser.msie || opts.forceIframe) 
		? $('<iframe class="blockUI" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;position:absolute;width:100%;height:100%;top:0;left:0" src="'+opts.iframeSrc+'"></iframe>')
		: $('<div class="blockUI" style="display:none"></div>');
	var lyr2 = $('<div class="blockUI blockOverlay" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;width:100%;height:100%;top:0;left:0"></div>');
	
	var lyr3, s;
	if (opts.theme && full) {
		s = '<div class="blockUI blockMsg blockPage ui-dialog ui-widget ui-corner-all" style="z-index:'+z+';display:none;position:fixed">' +
				'<div class="ui-widget-header ui-dialog-titlebar blockTitle">'+(opts.title || '&nbsp;')+'</div>' +
				'<div class="ui-widget-content ui-dialog-content"></div>' +
			'</div>';
	}
	else if (opts.theme) {
		s = '<div class="blockUI blockMsg blockElement ui-dialog ui-widget ui-corner-all" style="z-index:'+z+';display:none;position:absolute">' +
				'<div class="ui-widget-header ui-dialog-titlebar blockTitle">'+(opts.title || '&nbsp;')+'</div>' +
				'<div class="ui-widget-content ui-dialog-content"></div>' +
			'</div>';
	}
	else if (full) {
		s = '<div class="blockUI blockMsg blockPage" style="z-index:'+z+';display:none;position:fixed"></div>';
	}			
	else {
		s = '<div class="blockUI blockMsg blockElement" style="z-index:'+z+';display:none;position:absolute"></div>';
	}
	lyr3 = $(s);

	// if we have a message, style it
	if (msg) {
		if (opts.theme) {
			lyr3.css(themedCSS);
			lyr3.addClass('ui-widget-content');
		}
		else 
			lyr3.css(css);
	}

	// style the overlay
	if (!opts.applyPlatformOpacityRules || !($.browser.mozilla && /Linux/.test(navigator.platform)))
		lyr2.css(opts.overlayCSS);
	lyr2.css('position', full ? 'fixed' : 'absolute');

	// make iframe layer transparent in IE
	if ($.browser.msie || opts.forceIframe)
		lyr1.css('opacity',0.0);

	//$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el);
	var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el);
	$.each(layers, function() {
		this.appendTo($par);
	});
	
	if (opts.theme && opts.draggable && $.fn.draggable) {
		lyr3.draggable({
			handle: '.ui-dialog-titlebar',
			cancel: 'li'
		});
	}

	// ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling)
	var expr = setExpr && (!$.boxModel || $('object,embed', full ? null : el).length > 0);
	if (ie6 || expr) {
		// give body 100% height
		if (full && opts.allowBodyStretch && $.boxModel)
			$('html,body').css('height','100%');

		// fix ie6 issue when blocked element has a border width
		if ((ie6 || !$.boxModel) && !full) {
			var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth');
			var fixT = t ? '(0 - '+t+')' : 0;
			var fixL = l ? '(0 - '+l+')' : 0;
		}

		// simulate fixed position
		$.each([lyr1,lyr2,lyr3], function(i,o) {
			var s = o[0].style;
			s.position = 'absolute';
			if (i < 2) {
				full ? s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"')
					 : s.setExpression('height','this.parentNode.offsetHeight + "px"');
				full ? s.setExpression('width','jQuery.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"')
					 : s.setExpression('width','this.parentNode.offsetWidth + "px"');
				if (fixL) s.setExpression('left', fixL);
				if (fixT) s.setExpression('top', fixT);
			}
			else if (opts.centerY) {
				if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"');
				s.marginTop = 0;
			}
			else if (!opts.centerY && full) {
				var top = (opts.css && opts.css.top) ? parseInt(opts.css.top) : 0;
				var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"';
				s.setExpression('top',expression);
			}
		});
	}

	// show the message
	if (msg) {
		if (opts.theme)
			lyr3.find('.ui-widget-content').append(msg);
		else
			lyr3.append(msg);
		if (msg.jquery || msg.nodeType)
			$(msg).show();
	}

	if (($.browser.msie || opts.forceIframe) && opts.showOverlay)
		lyr1.show(); // opacity is zero
	if (opts.fadeIn) {
		var cb = opts.onBlock ? opts.onBlock : noOp;
		var cb1 = (opts.showOverlay && !msg) ? cb : noOp;
		var cb2 = msg ? cb : noOp;
		if (opts.showOverlay)
			lyr2._fadeIn(opts.fadeIn, cb1);
		if (msg)
			lyr3._fadeIn(opts.fadeIn, cb2);
	}
	else {
		if (opts.showOverlay)
			lyr2.show();
		if (msg)
			lyr3.show();
		if (opts.onBlock)
			opts.onBlock();
	}

	// bind key and mouse events
	bind(1, el, opts);

	if (full) {
		pageBlock = lyr3[0];
		pageBlockEls = $(':input:enabled:visible',pageBlock);
		if (opts.focusInput)
			setTimeout(focus, 20);
	}
	else
		center(lyr3[0], opts.centerX, opts.centerY);

	if (opts.timeout) {
		// auto-unblock
		var to = setTimeout(function() {
			full ? $.unblockUI(opts) : $(el).unblock(opts);
		}, opts.timeout);
		$(el).data('blockUI.timeout', to);
	}
};

// remove the block
function remove(el, opts) {
	var full = (el == window);
	var $el = $(el);
	var data = $el.data('blockUI.history');
	var to = $el.data('blockUI.timeout');
	if (to) {
		clearTimeout(to);
		$el.removeData('blockUI.timeout');
	}
	opts = $.extend({}, $.blockUI.defaults, opts || {});
	bind(0, el, opts); // unbind events
	
	var els;
	if (full) // crazy selector to handle odd field errors in ie6/7
		els = $('body').children().filter('.blockUI').add('body > .blockUI');
	else
		els = $('.blockUI', el);

	if (full)
		pageBlock = pageBlockEls = null;

	if (opts.fadeOut) {
		els.fadeOut(opts.fadeOut);
		setTimeout(function() { reset(els,data,opts,el); }, opts.fadeOut);
	}
	else
		reset(els, data, opts, el);
};

// move blocking element back into the DOM where it started
function reset(els,data,opts,el) {
	els.each(function(i,o) {
		// remove via DOM calls so we don't lose event handlers
		if (this.parentNode)
			this.parentNode.removeChild(this);
	});

	if (data && data.el) {
		data.el.style.display = data.display;
		data.el.style.position = data.position;
		if (data.parent)
			data.parent.appendChild(data.el);
		$(el).removeData('blockUI.history');
	}

	if (typeof opts.onUnblock == 'function')
		opts.onUnblock(el,opts);
};

// bind/unbind the handler
function bind(b, el, opts) {
	var full = el == window, $el = $(el);

	// don't bother unbinding if there is nothing to unbind
	if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked')))
		return;
	if (!full)
		$el.data('blockUI.isBlocked', b);

	// don't bind events when overlay is not in use or if bindEvents is false
	if (!opts.bindEvents || (b && !opts.showOverlay)) 
		return;

	// bind anchors and inputs for mouse and key events
	var events = 'mousedown mouseup keydown keypress';
	b ? $(document).bind(events, opts, handler) : $(document).unbind(events, handler);

// former impl...
//	   var $e = $('a,:input');
//	   b ? $e.bind(events, opts, handler) : $e.unbind(events, handler);
};

// event handler to suppress keyboard/mouse events when blocking
function handler(e) {
	// allow tab navigation (conditionally)
	if (e.keyCode && e.keyCode == 9) {
		if (pageBlock && e.data.constrainTabKey) {
			var els = pageBlockEls;
			var fwd = !e.shiftKey && e.target == els[els.length-1];
			var back = e.shiftKey && e.target == els[0];
			if (fwd || back) {
				setTimeout(function(){focus(back)},10);
				return false;
			}
		}
	}
	// allow events within the message content
	if ($(e.target).parents('div.blockMsg').length > 0)
		return true;

	// allow events for content that is not being blocked
	return $(e.target).parents().children().filter('div.blockUI').length == 0;
};

function focus(back) {
	if (!pageBlockEls)
		return;
	var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0];
	if (e)
		e.focus();
};

function center(el, x, y) {
	var p = el.parentNode, s = el.style;
	var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth');
	var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth');
	if (x) s.left = l > 0 ? (l+'px') : '0';
	if (y) s.top  = t > 0 ? (t+'px') : '0';
};

function sz(el, p) {
	return parseInt($.css(el,p))||0;
};

})(jQuery);

/*
 * ContextMenu - jQuery plugin for right-click context menus
 *
 * Author: Chris Domigan
 * Contributors: Dan G. Switzer, II
 * Parts of this plugin are inspired by Joern Zaefferer's Tooltip plugin
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Version: r2
 * Date: 16 July 2007
 *
 * For documentation visit http://www.trendskitchens.co.nz/jquery/contextmenu/
 *
 */

(function($) {

 	var menu, shadow, trigger, content, hash, currentTarget;
  var defaults = {
    menuStyle: {
      listStyle: 'none',
      padding: '1px',
      margin: '0px',
      backgroundColor: '#fff',
      border: '1px solid #999',
      width: '100px'
    },
    itemStyle: {
      margin: '0px',
      color: '#000',
      display: 'block',
      cursor: 'default',
      padding: '3px',
      border: '1px solid #fff',
      backgroundColor: 'transparent'
    },
    itemHoverStyle: {
      border: '1px solid #0a246a',
      backgroundColor: '#b6bdd2'
    },
    eventPosX: 'pageX',
    eventPosY: 'pageY',
    shadow : true,
    onContextMenu: null,
    onShowMenu: null,
    includeOnly: false
 	};

  $.fn.contextMenu = function(id, options) {
    if (!menu) {                                      // Create singleton menu
      menu = $('<div id="jqContextMenu"></div>')
               .hide()
               .css({position:'absolute', zIndex:'500'})
               .appendTo('body')
               .bind('click', function(e) {
                 e.stopPropagation();
               })
               .bind('contextmenu', function(e) {
                 return false;
               });
    }
    if (!shadow) {
      shadow = $('<div></div>')
                 .css({backgroundColor:'#000',position:'absolute',opacity:0.2,zIndex:499})
                 .appendTo('body')
                 .hide();
    }
    hash = hash || [];
    hash.push({
      id : id,
      menuStyle: $.extend({}, defaults.menuStyle, options.menuStyle || {}),
      itemStyle: $.extend({}, defaults.itemStyle, options.itemStyle || {}),
      itemHoverStyle: $.extend({}, defaults.itemHoverStyle, options.itemHoverStyle || {}),
      bindings: options.bindings || {},
      includeOnly: options.includeOnly || defaults.includeOnly,
      shadow: options.shadow || options.shadow === false ? options.shadow : defaults.shadow,
      onContextMenu: options.onContextMenu || defaults.onContextMenu,
      onShowMenu: options.onShowMenu || defaults.onShowMenu,
      eventPosX: options.eventPosX || defaults.eventPosX,
      eventPosY: options.eventPosY || defaults.eventPosY
    });

    var index = hash.length - 1;
    $(this).bind('contextmenu', function(e) {
      var i, included = true;
      var target = $(e.target);
      
      if (hash[index].includeOnly){
        included = false;
        
        for (i = 0; i < hash[index].includeOnly.length; i++){
          if (target.is(hash[index].includeOnly[i])){
            included = true;
          }
        }
      }
      
      if (included){
    
        var context = {target: true, event: null};
        
        // Check if onContextMenu() defined
        if (!!hash[index].onContextMenu){
          // onContextMenu returns FALSE to not show menu,
          // TRUE to show menu,
          // or jQuery element (such as that which was clicked) to be provided to menu selection handler
          context.target = hash[index].onContextMenu(e);
          context.event = e;
        }
        // var bShowContext = (!!hash[index].onContextMenu) ? hash[index].onContextMenu(e) : true;
        var bShowContext = !(context.target.length) ? false : true;
        if (bShowContext) display(index, this, e, options, context);

        return false;
      }
      
    });
    return this;
  };

  function display(index, trigger, e, options, context) {
    var cur = hash[index];
    content = $('#'+cur.id).find('ul:first').clone(true);
    content.css(cur.menuStyle).find('li').css(cur.itemStyle).hover(
      function() {
        $(this).css(cur.itemHoverStyle);
      },
      function(){
        $(this).css(cur.itemStyle);
      }
    ).find('img').css({verticalAlign:'middle',paddingRight:'2px'});

    // Send the content to the menu
    menu.html(content);

    // if there's an onShowMenu, run it now -- must run after content has been added
		// if you try to alter the content variable before the menu.html(), IE6 has issues
		// updating the content
    if (!!cur.onShowMenu) menu = cur.onShowMenu(e, menu);

    $.each(cur.bindings, function(id, func) {
      $('#'+id, menu).bind('click', function(e) {
        hide();
        func(trigger, context);
      });
    });

    menu.css({'left':e[cur.eventPosX],'top':e[cur.eventPosY]}).show();
    if (cur.shadow) shadow.css({width:menu.width(),height:menu.height(),left:e.pageX+2,top:e.pageY+2}).show();
    $(document).one('click', hide);
  }

  function hide() {
    menu.hide();
    shadow.hide();
  }

  // Apply defaults
  $.contextMenu = {
    defaults : function(userDefaults) {
      $.each(userDefaults, function(i, val) {
        if (typeof val == 'object' && defaults[i]) {
          $.extend(defaults[i], val);
        }
        else defaults[i] = val;
      });
    }
  };

})(jQuery);

$(function() {
  $('div.contextMenu').hide();
});
 /****************************************************************
  *                                                              *
  *  JQuery Curvy Corners by Mike Jolley                         *
  *	 http://blue-anvil.com                                       *
  *  ------------                                                *
  *  Version 1.81                                                 *
  *                                                              *
  *  Origionaly by: Cameron Cooke and Tim Hutchison.             *
  *  Website: http://www.curvycorners.net                        *
  *                                                              *
  *  This library is free software; you can redistribute         *
  *  it and/or modify it under the terms of the GNU              *
  *  Lesser General Public License as published by the           *
  *  Free Software Foundation; either version 2.1 of the         *
  *  License, or (at your option) any later version.             *
  *                                                              *
  *  This library is distributed in the hope that it will        *
  *  be useful, but WITHOUT ANY WARRANTY; without even the       *
  *  implied warranty of MERCHANTABILITY or FITNESS FOR A        *
  *  PARTICULAR PURPOSE. See the GNU Lesser General Public       *
  *  License for more details.                                   *
  *                                                              *
  *  You should have received a copy of the GNU Lesser           *
  *  General Public License along with this library;             *
  *  Inc., 59 Temple Place, Suite 330, Boston,                   *
  *  MA 02111-1307 USA                                           *
  *                                                              *
  ****************************************************************/
(function($) {
$.fn.corner = function(options) {

	function BlendColour(Col1, Col2, Col1Fraction) {
		var red1 = parseInt(Col1.substr(1,2),16);
		var green1 = parseInt(Col1.substr(3,2),16);
		var blue1 = parseInt(Col1.substr(5,2),16);
		var red2 = parseInt(Col2.substr(1,2),16);
		var green2 = parseInt(Col2.substr(3,2),16);
		var blue2 = parseInt(Col2.substr(5,2),16);
		if(Col1Fraction > 1 || Col1Fraction < 0) Col1Fraction = 1;
		var endRed = Math.round((red1 * Col1Fraction) + (red2 * (1 - Col1Fraction)));
		if(endRed > 255) endRed = 255;
		if(endRed < 0) endRed = 0;
		var endGreen = Math.round((green1 * Col1Fraction) + (green2 * (1 - Col1Fraction)));
		if(endGreen > 255) endGreen = 255;
		if(endGreen < 0) endGreen = 0;
		var endBlue = Math.round((blue1 * Col1Fraction) + (blue2 * (1 - Col1Fraction)));
		if(endBlue > 255) endBlue = 255;
		if(endBlue < 0) endBlue = 0;
		return "#" + IntToHex(endRed)+ IntToHex(endGreen)+ IntToHex(endBlue);
	}
	
	function IntToHex(strNum) {
		base = strNum / 16;
		rem = strNum % 16;
		base = base - (rem / 16);
		baseS = MakeHex(base);
		remS = MakeHex(rem);
		return baseS + '' + remS;
	}
	
	function MakeHex(x) {
		if((x >= 0) && (x <= 9)) {
			return x; 
		} else {
			switch(x) {
				case 10: return "A";
				case 11: return "B";
				case 12: return "C";
				case 13: return "D";
				case 14: return "E";
				case 15: return "F";
			};
			return "F";
		};
	}
	
	function pixelFraction(x, y, r) {
		var pixelfraction = 0;
		var xvalues = new Array(1);
		var yvalues = new Array(1);
		var point = 0;
		var whatsides = "";
		var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x,2)));
		if ((intersect >= y) && (intersect < (y+1))) {
			whatsides = "Left";
			xvalues[point] = 0;
			yvalues[point] = intersect - y;
			point = point + 1;
		};
		var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y+1,2)));
		if ((intersect >= x) && (intersect < (x+1))) {
			whatsides = whatsides + "Top";
			xvalues[point] = intersect - x;
			yvalues[point] = 1;
			point = point + 1;
		};
		var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x+1,2)));
		if ((intersect >= y) && (intersect < (y+1))) {
			whatsides = whatsides + "Right";
			xvalues[point] = 1;
			yvalues[point] = intersect - y;
			point = point + 1;
		};
		var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y,2)));
		if ((intersect >= x) && (intersect < (x+1))) {
			whatsides = whatsides + "Bottom";
			xvalues[point] = intersect - x;
			yvalues[point] = 0;
		};
		switch (whatsides) {
			case "LeftRight":
			pixelfraction = Math.min(yvalues[0],yvalues[1]) + ((Math.max(yvalues[0],yvalues[1]) - Math.min(yvalues[0],yvalues[1]))/2);
			break;
			case "TopRight":
			pixelfraction = 1-(((1-xvalues[0])*(1-yvalues[1]))/2);
			break;
			case "TopBottom":
			pixelfraction = Math.min(xvalues[0],xvalues[1]) + ((Math.max(xvalues[0],xvalues[1]) - Math.min(xvalues[0],xvalues[1]))/2);
			break;
			case "LeftBottom":
			pixelfraction = (yvalues[0]*xvalues[1])/2;
			break;
			default:
			pixelfraction = 1;
		};
		return pixelfraction;
	}
	
	function rgb2Hex(rgbColour) {
		try {
			var rgbArray = rgb2Array(rgbColour);
			var red = parseInt(rgbArray[0]);
			var green = parseInt(rgbArray[1]);
			var blue = parseInt(rgbArray[2]);
			var hexColour = "#" + IntToHex(red) + IntToHex(green) + IntToHex(blue);
		} catch(e) {
			alert("There was an error converting the RGB value to Hexadecimal in function rgb2Hex");
		};
		return hexColour;
	}
	
	function rgb2Array(rgbColour) {
		var rgbValues = rgbColour.substring(4, rgbColour.indexOf(")"));
		var rgbArray = rgbValues.split(", ");
		return rgbArray;
	}
	
	function format_colour(colour) {
		var returnColour = "transparent";
		if(colour != "" && colour != "transparent")
		{
			if(colour.substr(0, 3) == "rgb")
			{
				returnColour = rgb2Hex(colour);
			}
			else if(colour.length == 4)
			{
				returnColour = "#" + colour.substring(1, 2) + colour.substring(1, 2) + colour.substring(2, 3) + colour.substring(2, 3) + colour.substring(3, 4) + colour.substring(3, 4);
			}
			else
			{
				returnColour = colour;
			};
		};
		return returnColour;
	};
	function strip_px(value) {
		return parseInt((( value != "auto" && value.indexOf("%") == -1 && value != "" && value.indexOf("px") !== -1)? value.slice(0, value.indexOf("px")) : 0))	
	}

    function drawPixel(box,intx, inty, colour, transAmount, height, newCorner, image, bgImage, cornerRadius, isBorder, borderWidth, boxWidth, settings) {
    	var $$ = $(box);
        var pixel = document.createElement("div");
        $(pixel).css({	height:height,width:"1px",position:"absolute","font-size":"1px",overflow:"hidden"	});
        //var topMaxRadius = Math.max(settings["tr"].radius, settings["tl"].radius);
        var topMaxRadius = Math.max(settings.tl ? settings.tl.radius : 0, settings.tr ? settings.tr.radius : 0);
        // Dont apply background image to border pixels
        if(image == -1 && bgImage != "") {
			if(topMaxRadius>0)
				$(pixel).css("background-position","-" + ((boxWidth - cornerRadius - borderWidth)+ intx) + "px -" + (($$.height() + topMaxRadius  - borderWidth )-inty) + "px");
			else 
				$(pixel).css("background-position","-" + ((boxWidth - cornerRadius - borderWidth)+ intx) + "px -" + (($$.height() )-inty) + "px");	
			$(pixel).css({
						 "background-image":bgImage,
						 "background-repeat":$$.css("background-repeat"),
						 "background-color":colour						 
			});
        }
        else
        {
			if (!isBorder) $(pixel).css("background-color",colour).addClass('hasBackgroundColor');
			else $(pixel).css("background-color",colour);
        };
        
        if (transAmount != 100)
        	setOpacity(pixel, transAmount);
        	//$(pixel).css('opacity',transAmount/100);
		$(pixel).css({top:inty + "px",left:intx + "px"});
        return pixel;
    };
    
	function setOpacity(obj, opacity) {
	  opacity = (opacity == 100)?99.999:opacity;
	
	  if($.browser.safari && obj.tagName != "IFRAME")
	  {
	      // Get array of RGB values
	      var rgbArray = rgb2Array(obj.style.backgroundColor);
	
	      // Get RGB values
	      var red   = parseInt(rgbArray[0]);
	      var green = parseInt(rgbArray[1]);
	      var blue  = parseInt(rgbArray[2]);
	
	      // Safari using RGBA support
	      obj.style.backgroundColor = "rgba(" + red + ", " + green + ", " + blue + ", " + opacity/100 + ")";
	  }
	  else if(typeof(obj.style.opacity) != "undefined")
	  {
	      // W3C
	      obj.style.opacity = opacity/100;
	  }
	  else if(typeof(obj.style.MozOpacity) != "undefined")
	  {
	      // Older Mozilla
	      obj.style.MozOpacity = opacity/100;
	  }
	  else if(typeof(obj.style.filter) != "undefined")
	  {
	      // IE
	      obj.style.filter = "alpha(opacity:" + opacity + ")";
	  }
	  else if(typeof(obj.style.KHTMLOpacity) != "undefined")
	  {
	      // Older KHTML Based Browsers
	      obj.style.KHTMLOpacity = opacity/100;
	  }
	}
		
	// Apply the corners
	function applyCorners(box,settings) {
	
		var $$ = $(box); 

		// Get CSS of box and define vars
		var thebgImage 			= $$.css("backgroundImage");
		var topContainer = null;
		var bottomContainer = null;
		var masterCorners = new Array();
		var contentDIV = null;
		var boxHeight 			= strip_px($$.css("height")) ? strip_px($$.css("height")) : box.scrollHeight; 
		var boxWidth 			= strip_px($$.css("width")) ? strip_px($$.css("width")) : box.scrollWidth; 
		var borderWidth     	= strip_px($$.css("borderTopWidth")) ? strip_px($$.css("borderTopWidth")) : 0; 
		var boxPaddingTop 		= strip_px($$.css("paddingTop"));
		var boxPaddingBottom 	= strip_px($$.css("paddingBottom"));
		var boxPaddingLeft 	= strip_px($$.css("paddingLeft"));
		
		var boxPaddingRight 	= strip_px($$.css("paddingRight"));
		var boxColour 			= format_colour($$.css("backgroundColor"));
		var bgImage 			= (thebgImage != "none" && thebgImage!="initial") ? thebgImage : "";
		//var boxContent 		= $$.html();
		var borderColour 		= format_colour($$.css("borderTopColor")); 
		var borderString 		= borderWidth + "px" + " solid " + borderColour;
		
		var topMaxRadius = Math.max(settings.tl ? settings.tl.radius : 0, settings.tr ? settings.tr.radius : 0);
		var botMaxRadius = Math.max(settings.bl ? settings.bl.radius : 0, settings.br ? settings.br.radius : 0);
		
		$$.addClass('hasCorners').css({"padding":"0", "borderColor":box.style.borderColour, 'overflow':'visible'});
		if(box.style.position != "absolute") $$.css("position","relative");
		if(($.browser.msie)) {
			if($.browser.version==6 && box.style.width == "auto" && box.style.height == "auto") $$.css("width","100%");
			$$.css("zoom","1");
			$($$ +" *").css("zoom","normal");
		}		
		
		for(var t = 0;t < 2;t++) {
			switch(t) {
				case 0:
				if(settings.tl || settings.tr) {
					var newMainContainer 	= document.createElement("div");
					topContainer		= box.appendChild(newMainContainer);
					$(topContainer).css({ width:"100%", "font-size":"1px", overflow:"hidden", position:"absolute", "padding-left":borderWidth, "padding-right":borderWidth, height:topMaxRadius + "px",top:0 - topMaxRadius + "px",left:0 - borderWidth + "px"}).addClass('topContainer');
				};
				break;
				case 1:
				if(settings.bl || settings.br) {
					var newMainContainer 	= document.createElement("div");
					bottomContainer	= box.appendChild(newMainContainer);
					$(bottomContainer).css({ width:"100%", "font-size":"1px", overflow:"hidden", position:"absolute", "padding-left":borderWidth, "padding-right":borderWidth, height:botMaxRadius,bottom:0 - botMaxRadius + "px",left:0 - borderWidth + "px"}).addClass('bottomContainer');
				};
				break;
			};
		};
		
		if(settings.autoPad == true) {
			//$$.html("");
			var contentContainer = document.createElement("div");
			var contentContainer2 = document.createElement("div");
			var clearDiv = document.createElement("div");
			
			$(contentContainer2).css({ margin:"0","padding-bottom":boxPaddingBottom,"padding-top":boxPaddingTop,"padding-left":boxPaddingLeft,"padding-right":boxPaddingRight, 'overflow':'visible'}).addClass('hasBackgroundColor content_container');

			$(contentContainer).css({position:"relative", 'float':"left",width:"100%", "margin-top":"-" + (topMaxRadius-borderWidth) + "px", "margin-bottom":"-" + (botMaxRadius-borderWidth) + "px"}).addClass = "autoPadDiv";
			
			$(clearDiv).css("clear","both");

			contentContainer2.appendChild(contentContainer);
			contentContainer2.appendChild(clearDiv);
			$$.wrapInner(contentContainer2);
		};
		
		if(topContainer) $$.css("border-top",0);
		if(bottomContainer) $$.css("border-bottom",0);
		var corners = ["tr", "tl", "br", "bl"];
		for(var i in corners) {
			if(i > -1 < 4) {
				var cc = corners[i];
				if(!settings[cc]) {

					if(((cc == "tr" || cc == "tl") && topContainer != null) || ((cc == "br" || cc == "bl") && bottomContainer != null)) {
						var newCorner = document.createElement("div");
						$(newCorner).css({position:"relative","font-size":"1px", overflow:"hidden"});
						
						if(bgImage == "")
							$(newCorner).css("background-color",boxColour);
						else
							$(newCorner).css("background-image",bgImage).css("background-color",boxColour);;

						switch(cc)
						{
							case "tl":							
								$(newCorner).css({height:topMaxRadius - borderWidth,"margin-right":settings.tr.radius - (borderWidth*2), "border-left":borderString,"border-top":borderString,left:-borderWidth + "px", "background-repeat":$$.css("background-repeat"), "background-position":borderWidth + "px 0px"});
							break;
							case "tr":
								$(newCorner).css({height:topMaxRadius - borderWidth,"margin-left":settings.tl.radius - (borderWidth*2), "border-right":borderString,"border-top":borderString,left:borderWidth + "px", "background-repeat":$$.css("background-repeat"), "background-position":"-" + (topMaxRadius + borderWidth) + "px 0px"});
							break;
							case "bl":
								if(topMaxRadius>0)
									$(newCorner).css({height:botMaxRadius - borderWidth,"margin-right":settings.br.radius - (borderWidth*2), "border-left":borderString,"border-bottom":borderString,left:-borderWidth + "px", "background-repeat":$$.css("background-repeat"), "background-position":"0px -" + ($$.height() + topMaxRadius  - borderWidth +1) + "px" });
								else
									$(newCorner).css({height:botMaxRadius - borderWidth,"margin-right":settings.br.radius - (borderWidth*2), "border-left":borderString,"border-bottom":borderString,left:-borderWidth + "px", "background-repeat":$$.css("background-repeat"), "background-position":"0px -" + ($$.height()) + "px" });
							break;
							case "br":
								if(topMaxRadius>0)
									$(newCorner).css({height:botMaxRadius - borderWidth,"margin-left":settings.bl.radius - (borderWidth*2), "border-right":borderString,"border-bottom":borderString,left:borderWidth + "px", "background-repeat":$$.css("background-repeat"),  "background-position":"-" + settings.bl.radius + borderWidth + "px -" + ($$.height() + topMaxRadius  - borderWidth + 1) + "px" });
								else
									$(newCorner).css({height:botMaxRadius - borderWidth,"margin-left":settings.bl.radius - (borderWidth*2), "border-right":borderString,"border-bottom":borderString,left:borderWidth + "px", "background-repeat":$$.css("background-repeat"),  "background-position":"-" + settings.bl.radius + borderWidth + "px -" + ($$.height()) + "px" });
							break;
						};
					};
				} else {
					if(masterCorners[settings[cc].radius]) {
						var newCorner = masterCorners[settings[cc].radius].cloneNode(true);
					} else {
						var newCorner = document.createElement("DIV");
						$(newCorner).css({	height:settings[cc].radius,width:settings[cc].radius,position:"absolute","font-size":"1px",overflow:"hidden"	});
						var borderRadius = parseInt(settings[cc].radius - borderWidth);
						for(var intx = 0, j = settings[cc].radius; intx < j; intx++) {
							if((intx +1) >= borderRadius)
								var y1 = -1;
							else
								var y1 = (Math.floor(Math.sqrt(Math.pow(borderRadius, 2) - Math.pow((intx+1), 2))) - 1);
							if(borderRadius != j) {
								if((intx) >= borderRadius)
								var y2 = -1;
								else
								var y2 = Math.ceil(Math.sqrt(Math.pow(borderRadius,2) - Math.pow(intx, 2)));
								if((intx+1) >= j)
								var y3 = -1;
								else
								var y3 = (Math.floor(Math.sqrt(Math.pow(j ,2) - Math.pow((intx+1), 2))) - 1);
							};
							if((intx) >= j)
								var y4 = -1;
							else
								var y4 = Math.ceil(Math.sqrt(Math.pow(j ,2) - Math.pow(intx, 2)));
							if(y1 > -1) newCorner.appendChild(drawPixel(box,intx, 0, boxColour, 100, (y1+1), newCorner, -1, bgImage, settings[cc].radius, 0, borderWidth, boxWidth, settings));
							if(borderRadius != j) {
								for(var inty = (y1 + 1); inty < y2; inty++) {
									if(settings.antiAlias) {
										if(bgImage != "") {
											var borderFract = (pixelFraction(intx, inty, borderRadius) * 100);
											if(borderFract < 30) {
												newCorner.appendChild(drawPixel(box,intx, inty, borderColour, 100, 1, newCorner, 0, bgImage, settings[cc].radius, 1, borderWidth, boxWidth, settings));
											} else {
												newCorner.appendChild(drawPixel(box,intx, inty, borderColour, 100, 1, newCorner, -1, bgImage, settings[cc].radius, 1, borderWidth, boxWidth, settings));
											};
										} else {
											var pixelcolour = BlendColour(boxColour, borderColour, pixelFraction(intx, inty, borderRadius));
											newCorner.appendChild(drawPixel(box,intx, inty, pixelcolour, 100, 1, newCorner, 0, bgImage, settings[cc].radius, cc, 1, borderWidth, boxWidth, settings));
										};
									};
								};
								if(settings.antiAlias) {
									  if(y3 >= y2)
									  {
										 if (y2 == -1) y2 = 0;
										 newCorner.appendChild(drawPixel(box,intx, y2, borderColour, 100, (y3 - y2 + 1), newCorner, 0,bgImage, 0, 1, borderWidth, boxWidth, settings));
									  }
								} else {
									  if(y3 >= y1)
									  {
										  newCorner.appendChild(drawPixel(box,intx, (y1 + 1), borderColour, 100, (y3 - y1), newCorner, 0,bgImage, 0, 1, borderWidth, boxWidth, settings));
									  }
								};
								var outsideColour = borderColour;
							} else {
								var outsideColour = boxColour;
								var y3 = y1;
							};
							if(settings.antiAlias) {
								for(var inty = (y3 + 1); inty < y4; inty++) {
									newCorner.appendChild(drawPixel(box,intx, inty, outsideColour, (pixelFraction(intx, inty , j) * 100), 1, newCorner, ((borderWidth > 0)? 0 : -1),bgImage, settings[cc].radius, 1, borderWidth, boxWidth, settings));
								};                                
							};
						};
						masterCorners[settings[cc].radius] = newCorner.cloneNode(true);                                           
					};                    
					if(cc != "br") {
						for(var t = 0, k = newCorner.childNodes.length; t < k; t++) {
							var pixelBar 			= newCorner.childNodes[t];
							var pixelBarTop 		= strip_px($(pixelBar).css("top"));
							var pixelBarLeft 		= strip_px($(pixelBar).css("left"));
							var pixelBarHeight 		= strip_px($(pixelBar).css("height"));
							
							if(cc == "tl" || cc == "bl") {
								$(pixelBar).css("left",settings[cc].radius -pixelBarLeft -1 + "px");
							};
							
							if(cc == "tr" || cc == "tl") {
								$(pixelBar).css("top", settings[cc].radius -pixelBarHeight -pixelBarTop + "px");
							};
							
							switch(cc) {
								case "tr":
									$(pixelBar).css("background-position","-" + Math.abs((boxWidth - settings[cc].radius + borderWidth) + pixelBarLeft) + "px -" + Math.abs(settings[cc].radius -pixelBarHeight -pixelBarTop - borderWidth) + "px");
								break;
								case "tl":
									$(pixelBar).css("background-position","-" + Math.abs((settings[cc].radius -pixelBarLeft -1) - borderWidth) + "px -" + Math.abs(settings[cc].radius -pixelBarHeight -pixelBarTop - borderWidth) + "px");
								break;
								case "bl":
									if(topMaxRadius>0)
										$(pixelBar).css("background-position", "-" + Math.abs((settings[cc].radius -pixelBarLeft -1) - borderWidth) + "px -" + Math.abs(($$.height() + topMaxRadius - borderWidth + 1)) + "px");
									else
										$(pixelBar).css("background-position", "-" + Math.abs((settings[cc].radius -pixelBarLeft -1) - borderWidth) + "px -" + Math.abs(($$.height())) + "px");
								break;
							};
						};
					};
				};
				
				if(newCorner) {
					switch(cc) {
						case "tl":
						if($(newCorner).css("position") == "absolute") $(newCorner).css("top","0");
						if($(newCorner).css("position") == "absolute") $(newCorner).css("left","0");
						if(topContainer) topContainer.appendChild(newCorner);
						break;
						case "tr":
						if($(newCorner).css("position") == "absolute") $(newCorner).css("top","0");
						if($(newCorner).css("position") == "absolute") $(newCorner).css("right","0");
						if(topContainer) topContainer.appendChild(newCorner);
						break;
						case "bl":
						if($(newCorner).css("position") == "absolute") $(newCorner).css("bottom","0");
						if(newCorner.style.position == "absolute") $(newCorner).css("left","0");
						if(bottomContainer) bottomContainer.appendChild(newCorner);
						break;
						case "br":
						if($(newCorner).css("position") == "absolute") $(newCorner).css("bottom","0");
						if($(newCorner).css("position") == "absolute") $(newCorner).css("right","0");
						if(bottomContainer) bottomContainer.appendChild(newCorner);
						break;
					};                    
				};
			};
		};
		
		var radiusDiff = new Array();
		radiusDiff["t"] = Math.abs(settings.tl.radius - settings.tr.radius);
		radiusDiff["b"] = Math.abs(settings.bl.radius - settings.br.radius);
		for(z in radiusDiff) {
			if(z == "t" || z == "b") {
				if(radiusDiff[z]) {
					var smallerCornerType = ((settings[z + "l"].radius < settings[z + "r"].radius)? z +"l" : z +"r");
					var newFiller = document.createElement("div");
					$(newFiller).css({	height:radiusDiff[z],width:settings[smallerCornerType].radius+ "px",position:"absolute","font-size":"1px",overflow:"hidden","background-color":boxColour,"background-image":bgImage	});
				  switch(smallerCornerType)
				  {
					  case "tl":
						$(newFiller).css({"bottom":"0","left":"0","border-left":borderString,"background-position":"0px -" + (settings[smallerCornerType].radius - borderWidth )});
						topContainer.appendChild(newFiller);
						break;
	
					  case "tr":
						$(newFiller).css({"bottom":"0","right":"0","border-right":borderString,"background-position":"0px -" + (settings[smallerCornerType].radius - borderWidth ) + "px"});
						topContainer.appendChild(newFiller);
						break;
	
					  case "bl":
						$(newFiller).css({"top":"0","left":"0","border-left":borderString,"background-position":"0px -" + ($$.height() + settings[smallerCornerType].radius - borderWidth )});
						bottomContainer.appendChild(newFiller);
						break;
	
					  case "br":
						$(newFiller).css({"top":"0","right":"0","border-right":borderString,"background-position":"0px -" + ($$.height() + settings[smallerCornerType].radius - borderWidth )});
						bottomContainer.appendChild(newFiller);
						
						break;
				  }
			};
				
			var newFillerBar = document.createElement("div");
			$(newFillerBar).css({	position:"relative","font-size":"1px",overflow:"hidden","background-color":boxColour,"background-image":bgImage,"background-repeat":$$.css("background-repeat")});
			switch(z) {
					case "t":
					if(topContainer) {
						if(settings.tl.radius && settings.tr.radius) {
							$(newFillerBar).css({
												height:topMaxRadius - borderWidth + "px",
												"margin-left":settings.tl.radius - borderWidth  + "px",
												"margin-right":settings.tr.radius - borderWidth  + "px",
												"border-top":borderString
							}).addClass('hasBackgroundColor');
						
							if(bgImage != "")
								$(newFillerBar).css("background-position","-" + (topMaxRadius + borderWidth) + "px 0px");
							
							topContainer.appendChild(newFillerBar);
							
						}; 
						$$.css("background-position", "0px -" + (topMaxRadius - borderWidth +1) + "px"); 
					}; 
					break;
					case "b":
					if(bottomContainer) {
						if(settings.bl.radius && settings.br.radius) {
							$(newFillerBar).css({	
												height:botMaxRadius - borderWidth + "px",
												"margin-left":settings.bl.radius - borderWidth + "px",
												"margin-right":settings.br.radius - borderWidth + "px",
												"border-bottom":borderString
							});
						
							if(bgImage != "" && topMaxRadius>0)
								$(newFillerBar).css("background-position","-" + (settings.bl.radius - borderWidth) + "px -" + ($$.height() + topMaxRadius - borderWidth + 1) + "px");
							else
								$(newFillerBar).css("background-position","-" + (settings.bl.radius - borderWidth) + "px -" + ($$.height() ) + "px").addClass('hasBackgroundColor');
							
							bottomContainer.appendChild(newFillerBar);
						};
					};
					break;
				};
			};
		};
		$$.prepend(topContainer);
		$$.prepend(bottomContainer);
	}

	var settings = {
	  tl: { radius: 8 },
	  tr: { radius: 8 },
	  bl: { radius: 8 },
	  br: { radius: 8 },
	  antiAlias: true,
	  autoPad: true,
	  validTags: ["div"] };
	if ( options && typeof(options) != 'string' )
		$.extend(settings, options);
            
	return this.each(function() {
		if (!$(this).is('.hasCorners')) {
			applyCorners(this, settings);				
		}
		
	}); 
			
};
})(jQuery);
/* http://keith-wood.name/datepick.html
   Datepicker for jQuery 3.6.1.
   Written by Marc Grabanski (m@marcgrabanski.com) and
              Keith Wood (kbwood{at}iinet.com.au).
   Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and 
   MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. 
   Please attribute the authors if you use it. */

(function($) { // Hide the namespace

var PROP_NAME = 'datepick';

/* Date picker manager.
   Use the singleton instance of this class, $.datepick, to interact with the date picker.
   Settings for (groups of) date pickers are maintained in an instance object,
   allowing multiple different settings on the same page. */

function Datepick() {
  this._uuid = new Date().getTime(); // Unique identifier seed
  this._curInst = null; // The current instance in use
  this._keyEvent = false; // If the last event was a key event
  this._disabledInputs = []; // List of date picker inputs that have been disabled
  this._datepickerShowing = false; // True if the popup picker is showing , false if not
  this._inDialog = false; // True if showing within a "dialog", false if not
  this.regional = []; // Available regional settings, indexed by language code
  this.regional[''] = { // Default regional settings
    clearText: 'Clear', // Display text for clear link
    clearStatus: 'Erase the current date', // Status text for clear link
    closeText: 'Close', // Display text for close link
    closeStatus: 'Close without change', // Status text for close link
    prevText: '&#x3c;Prev', // Display text for previous month link
    prevStatus: 'Show the previous month', // Status text for previous month link
    prevBigText: '&#x3c;&#x3c;', // Display text for previous year link
    prevBigStatus: 'Show the previous year', // Status text for previous year link
    nextText: 'Next&#x3e;', // Display text for next month link
    nextStatus: 'Show the next month', // Status text for next month link
    nextBigText: '&#x3e;&#x3e;', // Display text for next year link
    nextBigStatus: 'Show the next year', // Status text for next year link
    currentText: 'Today', // Display text for current month link
    currentStatus: 'Show the current month', // Status text for current month link
    monthNames: ['January','February','March','April','May','June',
      'July','August','September','October','November','December'], // Names of months for drop-down and formatting
    monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
    monthStatus: 'Show a different month', // Status text for selecting a month
    yearStatus: 'Show a different year', // Status text for selecting a year
    weekHeader: 'Wk', // Header for the week of the year column
    weekStatus: 'Week of the year', // Status text for the week of the year column
    dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting
    dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting
    dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday
    dayStatus: 'Set DD as first week day', // Status text for the day of the week selection
    dateStatus: 'Select DD, M d', // Status text for the date selection
    dateFormat: 'mm/dd/yy', // See format options on parseDate
    firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
    initStatus: 'Select a date', // Initial Status text on opening
    isRTL: false, // True if right-to-left language, false if left-to-right
    showMonthAfterYear: false, // True if the year select precedes month, false for month then year
    yearSuffix: '' // Additional text to append to the year in the month headers
  };
  this._defaults = { // Global defaults for all the date picker instances
    showOn: 'focus', // 'focus' for popup on focus,
      // 'button' for trigger button, or 'both' for either
    showAnim: 'show', // Name of jQuery animation for popup
    showOptions: {}, // Options for enhanced animations
    duration: 'normal', // Duration of display/closure
    buttonText: '...', // Text for trigger button
    buttonImage: '', // URL for trigger button image
    buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
    defaultDate: null, // Used when field is blank: actual date,
      // +/-number for offset from today, null for today
    showDefault: false, // True to populate field with the default date
    appendText: '', // Display text following the input box, e.g. showing the format
    closeAtTop: true, // True to have the clear/close at the top,
      // false to have them at the bottom
    mandatory: false, // True to hide the Clear link, false to include it
    hideIfNoPrevNext: false, // True to hide next/previous month links
      // if not applicable, false to just disable them
    navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
    showBigPrevNext: false, // True to show big prev/next links
    stepMonths: 1, // Number of months to step back/forward
    stepBigMonths: 12, // Number of months to step back/forward for the big links
    stepYears: 1, // Number of years to step back/forward in month selector
    stepBigYears: 5, // Number of years to step back/forward in month selector
    gotoCurrent: false, // True if today link goes back to current selection instead
    changeMonth: true, // True if month can be selected directly, false if only prev/next
    changeYear: true, // True if year can be selected directly, false if only prev/next
    yearRange: '-10:+10', // Range of years to display in drop-down,
      // either relative to current year (-nn:+nn) or absolute (nnnn:nnnn)
    changeFirstDay: false, // True to click on day name to change, false to remain as set
    showOtherMonths: false, // True to show dates in other months, false to leave blank
    selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
    highlightWeek: false, // True to highlight the selected week
    showWeeks: false, // True to show week of the year, false to omit
    calculateWeek: this.iso8601Week, // How to calculate the week of the year,
      // takes a Date and returns the number of the week for it
    shortYearCutoff: '+10', // Short year values < this are in the current century,
      // > this are in the previous century, string value starting with '+'
      // for current year + value, -1 for no change
    showStatus: false, // True to show status bar at bottom, false to not show it
    statusForDate: this.dateStatus, // Function to provide status text for a date -
      // takes date and instance as parameters, returns display text
    minDate: null, // The earliest selectable date, or null for no limit
    maxDate: null, // The latest selectable date, or null for no limit
    numberOfMonths: 1, // Number of months to show at a time
    showCurrentAtPos: 0, // The position in multiple months at which to show the current month (starting at 0)
    rangeSelect: false, // Allows for selecting a date range on one date picker
    rangeSeparator: ' - ', // Text between two dates in a range
    multiSelect: 0, // Maximum number of selectable dates
    multiSeparator: ',', // Text between multiple dates
    beforeShow: null, // Function that takes an input field and
      // returns a set of custom settings for the date picker
    beforeShowDay: null, // Function that takes a date and returns an array with
      // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or '',
      // [2] = cell title (optional), e.g. $.datepick.noWeekends
    onChangeMonthYear: null, // Define a callback function when the month or year is changed
    onHover: null, // Define a callback function when hovering over a day
    onSelect: null, // Define a callback function when a date is selected
    onClose: null, // Define a callback function when the datepicker is closed
    altField: '', // Selector for an alternate field to store selected dates into
    altFormat: '', // The date format to use for the alternate field
    constrainInput: true, // The input is constrained by the current date format
    monthSelect: false
  };
  $.extend(this._defaults, this.regional['']);
  this.dpDiv = $(['<div id="', this._mainDivId, '" style="display: none;"></div>'].join(''));
}

$.extend(Datepick.prototype, {
  version: '3.6.0', // Current version
  
  /* Class name added to elements to indicate already configured with a date picker. */
  markerClassName: 'hasDatepick',

  _mainDivId: 'datepick-div', // The ID of the main datepicker division
  _inlineClass: 'datepick-inline', // The name of the inline marker class
  _appendClass: 'datepick-append', // The name of the append marker class
  _triggerClass: 'datepick-trigger', // The name of the trigger marker class
  _dialogClass: 'datepick-dialog', // The name of the dialog marker class
  _promptClass: 'datepick-prompt', // The name of the dialog prompt marker class
  _disableClass: 'datepick-disabled', // The name of the disabled covering marker class
  _oneMonthClass: 'datepick-one-month', // The name of the single month marker class
  _unselectableClass: 'datepick-unselectable', // The name of the unselectable cell marker class
  _currentClass: 'datepick-current-day', // The name of the current day marker class
  _dayOverClass: 'datepick-days-cell-over', // The name of the day hover marker class
  _weekOverClass: 'datepick-week-over', // The name of the week hover marker class
  _coverClass: 'datepick-cover', // The name of the IE6- iframe marker class

  /* Override the default settings for all instances of the date picker.
     @param  settings  (object) the new settings to use as defaults (anonymous object)
     @return  (Datepick) the manager object */
  setDefaults: function(settings) {
    extendRemove(this._defaults, settings || {});
    return this;
  },

  /* Attach the date picker to a jQuery selection.
     @param  target    (element) the target input field or division or span
     @param  settings  (object) the new settings to use for this date picker instance */
  _attachDatepick: function(target, settings) {
    // Check for settings on the control itself - in namespace 'date:'
    var inlineSettings = null;
    for (var attrName in this._defaults) {
      var attrValue = target.getAttribute(['date:', attrName].join(''));
      if (attrValue) {
        inlineSettings = inlineSettings || {};
        try {
          inlineSettings[attrName] = eval(attrValue);
        } catch (err) {
          inlineSettings[attrName] = attrValue;
        }
      }
    }
    var nodeName = target.nodeName.toLowerCase();
    var inline = (nodeName == 'div' || nodeName == 'span');
    if (!target.id)
      target.id = ['dp', (++this._uuid)].join('');
    var inst = this._newInst($(target), inline);
    inst.settings = $.extend({}, settings || {}, inlineSettings || {});
    if (nodeName == 'input')
      this._connectDatepick(target, inst);
    else if (inline)
      this._inlineDatepick(target, inst);
  },

  /* Create a new instance object.
     @param  target  (jQuery) the target input field or division or span
     @param  inline  (boolean) true if this datepicker appears inline */
  _newInst: function(target, inline) {
    var id = target[0].id.replace(/([:\[\]\.])/g, '\\\\$1'); // Escape jQuery meta chars
    return {id: id, input: target, // Associated target
      cursorDate: this._daylightSavingAdjust(new Date()), // Current position
      drawMonth: 0, drawYear: 0, // Month being drawn
      dates: [], // Selected dates
      inline: inline, // Is datepicker inline or not
      dpDiv: (!inline ? this.dpDiv : // presentation div
      $(['<div class="', this._inlineClass, '"></div>'].join(''))),
      siblings: $([])}; // Created siblings (trigger/append)
  },

  /* Attach the date picker to an input field.
     @param  target  (element) the target input field or division or span
     @param  inst    (object) the instance settings for this datepicker */
  _connectDatepick: function(target, inst) {
    var input = $(target);
    if (input.hasClass(this.markerClassName))
      return;
    var appendText = this._get(inst, 'appendText');
    var isRTL = this._get(inst, 'isRTL');
    if (appendText) {
      var append = $(['<span class="', this._appendClass, '">', appendText, '</span>'].join(''));
      input[isRTL ? 'before' : 'after'](append);
      inst.siblings = inst.siblings.add(append);
    }
    var showOn = this._get(inst, 'showOn');
    if (showOn == 'focus' || showOn == 'both') // Pop-up date picker when in the marked field
      input.focus(this._showDatepick);
    if (showOn == 'button' || showOn == 'both') { // Pop-up date picker when button clicked
      var buttonText = this._get(inst, 'buttonText');
      var buttonImage = this._get(inst, 'buttonImage');
      var trigger = $(this._get(inst, 'buttonImageOnly') ?
        $('<img/>').addClass(this._triggerClass).
          attr({src: buttonImage, alt: buttonText, title: buttonText}) :
        $('<button type="button"></button>').addClass(this._triggerClass).
          html(buttonImage == '' ? buttonText : $('<img/>').attr(
          {src: buttonImage, alt: buttonText, title: buttonText})));
      input[isRTL ? 'before' : 'after'](trigger);
      inst.siblings = inst.siblings.add(trigger);
      trigger.click(function() {
        if ($.datepick._datepickerShowing && $.datepick._lastInput == target)
          $.datepick._hideDatepick();
        else
          $.datepick._showDatepick(target);
        return false;
      });
    }
    input.addClass(this.markerClassName).keydown(this._doKeyDown).
      keypress(this._doKeyPress).keyup(this._doKeyUp);
    if (this._get(inst, 'showDefault') && !inst.input.val()) {
      inst.dates = [this._getDefaultDate(inst)];
      this._showDate(inst);
    }
    $.data(target, PROP_NAME, inst);
  },

  /* Attach an inline date picker to a div.
     @param  target  (element) the target input field or division or span
     @param  inst    (object) the instance settings for this datepicker */
  _inlineDatepick: function(target, inst) {
    var divSpan = $(target);
    if (divSpan.hasClass(this.markerClassName))
      return;
    divSpan.addClass(this.markerClassName);
    $.data(target, PROP_NAME, inst);
    inst.drawMonth = inst.cursorDate.getMonth();
    inst.drawYear = inst.cursorDate.getFullYear();
    $('body').append(inst.dpDiv);
    this._updateDatepick(inst);
    // Fix width for dynamic number of date pickers
    // inst.dpDiv.width(this._getNumberOfMonths(inst)[1] *
      // $('.' + this._oneMonthClass, inst.dpDiv)[0].offsetWidth);
    divSpan.append(inst.dpDiv);
    this._updateAlternate(inst);
  },

  /* Pop-up the date picker in a "dialog" box.
     @param  input     (element) ignored
     @param  dateText  (string) the initial date to display (in the current format)
     @param  onSelect  (function) the function(dateText) to call when a date is selected
     @param  settings  (object) update the dialog date picker instance's settings (anonymous object)
     @param  pos       (int[2]) coordinates for the dialog's position within the screen or
                       (event) with x/y coordinates or
                       leave empty for default (screen centre) */
  _dialogDatepick: function(input, dateText, onSelect, settings, pos) {
    var inst = this._dialogInst; // Internal instance
    if (!inst) {
      var id = ['dp', (++this._uuid)].join('');
      this._dialogInput = $(['<input type="text" id="', id,
        '" size="1" style="position: absolute; top: -100px;"/>'].join(''));
      this._dialogInput.keydown(this._doKeyDown);
      $('body').append(this._dialogInput);
      inst = this._dialogInst = this._newInst(this._dialogInput, false);
      inst.settings = {};
      $.data(this._dialogInput[0], PROP_NAME, inst);
    }
    extendRemove(inst.settings, settings || {});
    this._dialogInput.val(dateText);

    this._pos = (pos ? (isArray(pos) ? pos : [pos.pageX, pos.pageY]) : null);
    if (!this._pos) {
      var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
      var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
      this._pos = // Should use actual width/height below
        [(document.documentElement.clientWidth / 2) - 100 + scrollX,
        (document.documentElement.clientHeight / 2) - 150 + scrollY];
    }

    // Move input on screen for focus, but hidden behind dialog
    this._dialogInput.css('left', [this._pos[0], 'px'].join('')).css('top', [this._pos[1], 'px'].join(''));
    inst.settings.onSelect = onSelect;
    this._inDialog = true;
    this.dpDiv.addClass(this._dialogClass);
    this._showDatepick(this._dialogInput[0]);
    if ($.blockUI)
      $.blockUI(this.dpDiv);
    $.data(this._dialogInput[0], PROP_NAME, inst);
  },

  /* Detach a datepicker from its control.
     @param  target  (element) the target input field or division or span */
  _destroyDatepick: function(target) {
    var $target = $(target);
    if (!$target.hasClass(this.markerClassName)) {
      return;
    }
    var nodeName = target.nodeName.toLowerCase();
    var inst = $.data(target, PROP_NAME);
    $.removeData(target, PROP_NAME);
    if (nodeName == 'input') {
      $(inst.siblings).remove();
      $target.removeClass(this.markerClassName).
        unbind('focus', this._showDatepick).unbind('keydown', this._doKeyDown).
        unbind('keypress', this._doKeyPress).unbind('keyup', this._doKeyUp);
    }
    else if (nodeName == 'div' || nodeName == 'span')
      $target.removeClass(this.markerClassName).empty();
  },

  /* Enable the date picker to a jQuery selection.
     @param  target  (element) the target input field or division or span */
  _enableDatepick: function(target) {
    var $target = $(target);
    if (!$target.hasClass(this.markerClassName)) {
      return;
    }
    var nodeName = target.nodeName.toLowerCase();
    var inst = $.data(target, PROP_NAME);
    if (nodeName == 'input') {
      target.disabled = false;
      inst.siblings.filter(['button.', this._triggerClass].join('')).
        each(function() { this.disabled = false; }).end().
        filter(['img.', this._triggerClass].join('')).
        css({opacity: '1.0', cursor: ''});
    }
    else if (nodeName == 'div' || nodeName == 'span') {
      $target.children(['.', this._disableClass].join('')).remove().end().
        find('select').attr('disabled', '');
    }
    this._disabledInputs = $.map(this._disabledInputs,
      function(value) { return (value == target ? null : value); }); // Delete entry
  },

  /* Disable the date picker to a jQuery selection.
     @param  target  (element) the target input field or division or span */
  _disableDatepick: function(target) {
    var $target = $(target);
    if (!$target.hasClass(this.markerClassName)) {
      return;
    }
    var nodeName = target.nodeName.toLowerCase();
    var inst = $.data(target, PROP_NAME);
    if (nodeName == 'input') {
      target.disabled = true;
      inst.siblings.filter(['button.', this._triggerClass].join('')).
        each(function() { this.disabled = true; }).end().
        filter(['img.', this._triggerClass].join('')).
        css({opacity: '0.5', cursor: 'default'});
    }
    else if (nodeName == 'div' || nodeName == 'span') {
      var inline = $target.children(['.', this._inlineClass].join(''));
      var offset = inline.offset();
      var relOffset = {left: 0, top: 0};
      inline.parents().each(function() {
        if ($(this).css('position') == 'relative') {
          relOffset = $(this).offset();
          return false;
        }
      });
      $target.prepend(['<div class="', this._disableClass, '" style="',
        'width: ', inline.width(), 'px; height: ', inline.height(),
        'px; left: ', (offset.left - relOffset.left),
        'px; top: ', (offset.top - relOffset.top), 'px;"></div>'].join('')).
        find('select').attr('disabled', 'disabled');
    }
    this._disabledInputs = $.map(this._disabledInputs,
      function(value) { return (value == target ? null : value); }); // Delete entry
    this._disabledInputs.push(target);
  },

  /* Is the first field in a jQuery collection disabled as a datepicker?
     @param  target  (element) the target input field or division or span
     @return  (boolean) true if disabled, false if enabled */
  _isDisabledDatepick: function(target) {
    return (!target ? false : $.inArray(target, this._disabledInputs) > -1);
  },

  /* Retrieve the instance data for the target control.
     @param  target  (element) the target input field or division or span
     @return  (object) the associated instance data
     @throws  error if a jQuery problem getting data */
  _getInst: function(target) {
    try {
      return $.data(target, PROP_NAME);
    }
    catch (err) {
      throw 'Missing instance data for this datepicker';
    }
  },

  /* Update or retrieve the settings for a date picker attached to an input field or division.
     @param  target  (element) the target input field or division or span
     @param  name    (object) the new settings to update or
                     (string) the name of the setting to change or retrieve,
                     when retrieving also 'all' for all instance settings or
                     'defaults' for all global defaults
     @param  value   (any) the new value for the setting
                     (omit if above is an object or to retrieve value) */
  _optionDatepick: function(target, name, value) {
    var inst = this._getInst(target);
    if (arguments.length == 2 && typeof name == 'string') {
      return (name == 'defaults' ? $.extend({}, $.datepick._defaults) :
        (inst ? (name == 'all' ? $.extend({}, inst.settings) :
        this._get(inst, name)) : null));
    }
    var settings = name || {};
    if (typeof name == 'string') {
      settings = {};
      settings[name] = value;
    }
    if (inst) {
      if (this._curInst == inst) {
        this._hideDatepick(null);
      }
      var dates = this._getDateDatepick(target);
      extendRemove(inst.settings, settings);
      extendRemove(inst, {dates: []});
      var blank = (!dates || isArray(dates));
      if (isArray(dates))
        for (var i = 0; i < dates.length; i++)
          if (dates[i]) {
            blank = false;
            break;
          }
      if (!blank)
        this._setDateDatepick(target, dates);
      this._updateDatepick(inst);
    }
  },

  // Change method deprecated
  _changeDatepick: function(target, name, value) {
    this._optionDatepick(target, name, value);
  },

  /* Redraw the date picker attached to an input field or division.
     @param  target  (element) the target input field or division or span */
  _refreshDatepick: function(target) {
    var inst = this._getInst(target);
    if (inst) {
      this._updateDatepick(inst);
    }
  },

  /* Set the dates for a jQuery selection.
     @param  target   (element) the target input field or division or span
     @param  date     (Date) the new date
     @param  endDate  (Date) the new end date for a range (optional) */
  _setDateDatepick: function(target, date, endDate) {
    var inst = this._getInst(target);
    if (inst) {
      this._setDate(inst, date, endDate);
      this._updateDatepick(inst);
      this._updateAlternate(inst);
    }
  },

  /* Get the date(s) for the first entry in a jQuery selection.
     @param  target  (element) the target input field or division or span
     @return (Date) the current date or
             (Date[2]) the current dates for a range */
  _getDateDatepick: function(target) {
    var inst = this._getInst(target);
    if (inst && !inst.inline)
      this._setDateFromField(inst);
    return (inst ? this._getDate(inst) : null);
  },

  /* Handle keystrokes.
     @param  event  (KeyEvent) the keystroke details
     @return  (boolean) true to continue, false to discard */
  _doKeyDown: function(event) {
    var inst = $.datepick._getInst(event.target);
    inst._keyEvent = true;
    var handled = true;
    var isRTL = $.datepick._get(inst, 'isRTL');
    if ($.datepick._datepickerShowing)
      switch (event.keyCode) {
        case 9:  $.datepick._hideDatepick(null, '');
            break; // Hide on tab out
        case 13: var sel = $(['td.', $.datepick._dayOverClass].join(''), inst.dpDiv);
            if (sel.length == 0)
              sel = $(['td.', $.datepick._currentClass, ':first'].join(''), inst.dpDiv);
            if (sel[0])
              $.datepick._selectDay(event.target, inst.cursorDate.getTime(), sel[0]);
            else
              $.datepick._hideDatepick(null, $.datepick._get(inst, 'duration'));
            break; // Select the value on enter
        case 27: $.datepick._hideDatepick(null, $.datepick._get(inst, 'duration'));
            break; // Hide on escape
        case 33: $.datepick._adjustDate(event.target, (event.ctrlKey ?
              -$.datepick._get(inst, 'stepBigMonths') :
              -$.datepick._get(inst, 'stepMonths')), 'M');
            break; // Previous month/year on page up/+ ctrl
        case 34: $.datepick._adjustDate(event.target, (event.ctrlKey ?
              +$.datepick._get(inst, 'stepBigMonths') :
              +$.datepick._get(inst, 'stepMonths')), 'M');
            break; // Next month/year on page down/+ ctrl
        case 35: if (event.ctrlKey || event.metaKey)
              $.datepick._clearDate(event.target);
            handled = event.ctrlKey || event.metaKey;
            break; // Clear on ctrl or command + end
        case 36: if (event.ctrlKey || event.metaKey)
              $.datepick._gotoToday(event.target);
            handled = event.ctrlKey || event.metaKey;
            break; // Current on ctrl or command + home
        case 37: if (event.ctrlKey || event.metaKey)
              $.datepick._adjustDate(event.target, (isRTL ? +1 : -1), 'D');
            handled = event.ctrlKey || event.metaKey;
            // -1 day on ctrl or command + left
            if (event.originalEvent.altKey)
              $.datepick._adjustDate(event.target,
                (event.ctrlKey ? -$.datepick._get(inst, 'stepBigMonths') :
                -$.datepick._get(inst, 'stepMonths')), 'M');
            // Next month/year on alt + left/+ ctrl
            break;
        case 38: if (event.ctrlKey || event.metaKey)
              $.datepick._adjustDate(event.target, -7, 'D');
            handled = event.ctrlKey || event.metaKey;
            break; // -1 week on ctrl or command + up
        case 39: if (event.ctrlKey || event.metaKey)
              $.datepick._adjustDate(event.target, (isRTL ? -1 : +1), 'D');
            handled = event.ctrlKey || event.metaKey;
            // +1 day on ctrl or command + right
            if (event.originalEvent.altKey)
              $.datepick._adjustDate(event.target,
                (event.ctrlKey ? +$.datepick._get(inst, 'stepBigMonths') :
                +$.datepick._get(inst, 'stepMonths')), 'M');
            // Next month/year on alt + right/+ ctrl
            break;
        case 40: if (event.ctrlKey || event.metaKey)
              $.datepick._adjustDate(event.target, +7, 'D');
            handled = event.ctrlKey || event.metaKey;
            break; // +1 week on ctrl or command + down
        default: handled = false;
      }
    else if (event.keyCode == 36 && event.ctrlKey) // Display the date picker on ctrl+home
      $.datepick._showDatepick(this);
    else
      handled = false;
    if (handled) {
      event.preventDefault();
      event.stopPropagation();
    }
    return !handled;
  },

  /* Filter entered characters - based on date format.
     @param  event  (KeyEvent) the keystroke details
     @return  (boolean) true to continue, false to discard */
  _doKeyPress: function(event) {
    var inst = $.datepick._getInst(event.target);
    if ($.datepick._get(inst, 'constrainInput')) {
      var chars = $.datepick._possibleChars(inst);
      var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
      return event.ctrlKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
    }
  },

  /* Synchronise manual entry and field/alternate field.
     @param  event  (KeyEvent) the keystroke details
     @return  (boolean) true to continue */
  _doKeyUp: function(event) {
    var inst = $.datepick._getInst(event.target);
    try {
      var separator = ($.datepick._get(inst, 'rangeSelect') ?
        $.datepick._get(inst, 'rangeSeparator') :
        ($.datepick._get(inst, 'multiSelect') ?
        $.datepick._get(inst, 'multiSeparator') : ''));
      var dates = (inst.input ? inst.input.val() : '');
      dates = (separator ? dates.split(separator) : [dates]);
      var ok = true;
      for (var i = 0; i < dates.length; i++) {
        if (!$.datepick.parseDate($.datepick._get(inst, 'dateFormat'),
            dates[i], $.datepick._getFormatConfig(inst))) {
          ok = false;
          break;
        }
      }
      if (ok) { // Only if valid
        $.datepick._setDateFromField(inst);
        $.datepick._updateAlternate(inst);
        $.datepick._updateDatepick(inst);
      }
    }
    catch (event) {
      // Ignore
    }
    return true;
  },

  /* Extract all possible characters from the date format.
     @param  inst  (object) the instance settings for this datepicker
     @return  (string) the set of characters allowed by this format */
  _possibleChars: function (inst) {
    var dateFormat = $.datepick._get(inst, 'dateFormat');
    var chars = ($.datepick._get(inst, 'rangeSelect') ?
      $.datepick._get(inst, 'rangeSeparator') :
      ($.datepick._get(inst, 'multiSelect') ?
      $.datepick._get(inst, 'multiSeparator') : ''));
    var literal = false;
    for (var iFormat = 0; iFormat < dateFormat.length; iFormat++)
      if (literal)
        if (dateFormat.charAt(iFormat) == "'" && !lookAhead("'"))
          literal = false;
        else
          chars = [chars, dateFormat.charAt(iFormat)].join('');
      else
        switch (dateFormat.charAt(iFormat)) {
          case 'd': case 'm': case 'y': case '@':
            chars = [chars, '0123456789'].join('');
            break;
          case 'D': case 'M':
            return null; // Accept anything
          case "'":
            if (lookAhead("'"))
              chars = [chars, "'"].join('');
            else
              literal = true;
            break;
          default:
            chars = [chars, dateFormat.charAt(iFormat)].join('');
        }
    return chars;
  },

  /* Pop-up the date picker for a given input field.
     @param  input  (element) the input field attached to the date picker or
                    (event) if triggered by focus */
  _showDatepick: function(input) {
    input = input.target || input;
    if (input.nodeName.toLowerCase() != 'input') // Find from button/image trigger
      input = $('input', input.parentNode)[0];
    if ($.datepick._isDisabledDatepick(input) || $.datepick._lastInput == input) // Already here
      return;
    var inst = $.datepick._getInst(input);
    var beforeShow = $.datepick._get(inst, 'beforeShow');
    extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
    $.datepick._hideDatepick(null, '');
    $.datepick._lastInput = input;
    $.datepick._setDateFromField(inst);
    if ($.datepick._inDialog) // Hide cursor
      input.value = '';
    if (!$.datepick._pos) { // Position below input
      $.datepick._pos = $.datepick._findPos(input);
      $.datepick._pos[1] += input.offsetHeight; // Add the height
    }
    var isFixed = false;
    $(input).parents().each(function() {
      isFixed |= $(this).css('position') == 'fixed';
      return !isFixed;
    });
    if (isFixed && $.browser.opera) { // Correction for Opera when fixed and scrolled
      $.datepick._pos[0] -= document.documentElement.scrollLeft;
      $.datepick._pos[1] -= document.documentElement.scrollTop;
    }
    var offset = {left: $.datepick._pos[0], top: $.datepick._pos[1]};
    $.datepick._pos = null;
    // Determine sizing offscreen
    inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'});
    $.datepick._updateDatepick(inst);
    // Fix width for dynamic number of date pickers
    inst.dpDiv.width($.datepick._getNumberOfMonths(inst)[1] *
      $(['.', $.datepick._oneMonthClass].join(''), inst.dpDiv).width());
    // And adjust position before showing
    offset = $.datepick._checkOffset(inst, offset, isFixed);
    inst.dpDiv.css({position: ($.datepick._inDialog && $.blockUI ?
      'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
      left: [offset.left, 'px'].join(''), top: [offset.top, 'px'].join('')});
    if (!inst.inline) {
      var showAnim = $.datepick._get(inst, 'showAnim') || 'show';
      var duration = $.datepick._get(inst, 'duration');
      var postProcess = function() {
        $.datepick._datepickerShowing = true;
        var borders = $.datepick._getBorders(inst.dpDiv);
        inst.dpDiv.find(['iframe.', $.datepick._coverClass].join('')). // IE6- only
          css({left: -borders[0], top: -borders[1],
            width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()});
      };
      if ($.effects && $.effects[showAnim])
        inst.dpDiv.show(showAnim, $.datepick._get(inst, 'showOptions'), duration, postProcess);
      else
        inst.dpDiv[showAnim](duration, postProcess);
      if (duration == '')
        postProcess();
      if (inst.input[0].type != 'hidden')
        inst.input.focus();
      $.datepick._curInst = inst;
    }
  },

  /* Generate the date picker content.
     @param  inst  (object) the instance settings for this datepicker */
  _updateDatepick: function(inst) {
    var borders = this._getBorders(inst.dpDiv);
    inst.dpDiv.empty().append(this._generateHTML(inst)).
      find(['iframe.', this._coverClass].join('')). // IE6- only
      css({left: -borders[0], top: -borders[1],
        width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()});
    var numMonths = this._getNumberOfMonths(inst);
    inst.dpDiv[[(numMonths[0] != 1 || numMonths[1] != 1 ?
      'add' : 'remove'), 'Class'].join('')]('datepick-multi');
    inst.dpDiv[[(this._get(inst, 'isRTL') ?
      'add' : 'remove'), 'Class'].join('')]('datepick-rtl');
    if (inst.input && inst.input[0].type != 'hidden' && inst == $.datepick._curInst)
      $(inst.input).focus();
  },

  /* Retrieve the size of left and top borders for an element.
     @param  elem  (jQuery object) the element of interest
     @return  (number[2]) the left and top borders */
  _getBorders: function(elem) {
    var convert = function(value) {
      var extra = ($.browser.msie ? 1 : 0);
      return {thin: 1 + extra, medium: 3 + extra, thick: 5 + extra}[value] || value;
    };
    return [parseFloat(convert(elem.css('border-left-width'))),
      parseFloat(convert(elem.css('border-top-width')))];
  },

  /* Check positioning to remain on the screen.
     @param  inst     (object) the instance settings for this datepicker
     @param  offset   (object) the offset of the attached field
     @param  isFixed  (boolean) true if control or a parent is 'fixed' in position
     @return  (object) the updated offset for the datepicker */
  _checkOffset: function(inst, offset, isFixed) {
    var pos = inst.input ? this._findPos(inst.input[0]) : null;
    var browserWidth = document.documentElement.clientWidth;
    var browserHeight = document.documentElement.clientHeight;
    if (browserWidth == 0)
      return offset;
    var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
    var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
    // Reposition date picker horizontally if outside the browser window
    if (this._get(inst, 'isRTL') ||
        (offset.left + inst.dpDiv.outerWidth() - scrollX) > browserWidth)
      offset.left = Math.max((isFixed ? 0 : scrollX),
        pos[0] + (inst.input ? inst.input.outerWidth() : 0) -
        (isFixed ? scrollX : 0) - inst.dpDiv.outerWidth() -
        (isFixed && $.browser.opera ? document.documentElement.scrollLeft : 0));
    else
      offset.left -= (isFixed ? scrollX : 0);
    // Reposition date picker vertically if outside the browser window
    if ((offset.top + inst.dpDiv.outerHeight() - scrollY) > browserHeight)
      offset.top = Math.max((isFixed ? 0 : scrollY),
        pos[1] - (isFixed ? scrollY : 0) -
        (this._inDialog ? 0 : inst.dpDiv.outerHeight()) -
        (isFixed && $.browser.opera ? document.documentElement.scrollTop : 0));
    else
      offset.top -= (isFixed ? scrollY : 0);
    return offset;
  },

  /* Find an element's position on the screen.
     @param  elem  (element) the element to check
     @return  (number[2]) the x- and y-coordinates for the object */
  _findPos: function(elem) {
        while (elem && (elem.type == 'hidden' || elem.nodeType != 1)) {
            elem = elem.nextSibling;
        }
        var position = $(elem).offset();
      return [position.left, position.top];
  },

  /* Hide the date picker from view.
     @param  input     (element) the input field attached to the date picker
     @param  duration  (string) the duration over which to close the date picker */
  _hideDatepick: function(input, duration) {
    var inst = this._curInst;
    if (!inst || (input && inst != $.data(input, PROP_NAME)))
      return false;
    var rangeSelect = this._get(inst, 'rangeSelect');
    if (rangeSelect && inst.stayOpen)
      this._updateInput(['#', inst.id].join(''));
    inst.stayOpen = false;
    if (this._datepickerShowing) {
      duration = (duration != null ? duration : this._get(inst, 'duration'));
      var showAnim = this._get(inst, 'showAnim');
      var postProcess = function() {
        $.datepick._tidyDialog(inst);
      };
      if (duration != '' && $.effects && $.effects[showAnim])
        inst.dpDiv.hide(showAnim, $.datepick._get(inst, 'showOptions'),
          duration, postProcess);
      else
        inst.dpDiv[(duration == '' ? 'hide' : (showAnim == 'slideDown' ? 'slideUp' :
          (showAnim == 'fadeIn' ? 'fadeOut' : 'hide')))](duration, postProcess);
      if (duration == '')
        this._tidyDialog(inst);
      var onClose = this._get(inst, 'onClose');
      if (onClose)  // Trigger custom callback
        onClose.apply((inst.input ? inst.input[0] : null),
          [(inst.input ? inst.input.val() : ''), this._getDate(inst), inst]);
      this._datepickerShowing = false;
      this._lastInput = null;
      inst.settings.prompt = null;
      if (this._inDialog) {
        this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
        if ($.blockUI) {
          $.unblockUI();
          $('body').append(this.dpDiv);
        }
      }
      this._inDialog = false;
    }
    this._curInst = null;
    return false;
  },

  /* Tidy up after a dialog display.
     @param  inst  (object) the instance settings for this datepicker */
  _tidyDialog: function(inst) {
    inst.dpDiv.removeClass(this._dialogClass).unbind('.datepick');
    $(['.', this._promptClass].join(''), inst.dpDiv).remove();
  },

  /* Close date picker if clicked elsewhere.
     @param  event  (MouseEvent) the mouse click to check */
  _checkExternalClick: function(event) {
    if (!$.datepick._curInst)
      return;
    var $target = $(event.target);
    if (!$target.parents().andSelf().is(['#', $.datepick._mainDivId].join('')) &&
        !$target.hasClass($.datepick.markerClassName) &&
        !$target.parents().andSelf().hasClass($.datepick._triggerClass) &&
        $.datepick._datepickerShowing && !($.datepick._inDialog && $.blockUI))
      $.datepick._hideDatepick(null, '');
  },

  /* Adjust one of the date sub-fields.
     @param  id      (string) the ID of the target field
     @param  offset  (number) the amount to change by
     @param  period  (string) 'D' for days, 'M' for months, 'Y' for years */
  _adjustDate: function(id, offset, period) {
    var inst = this._getInst($(id)[0]);
    this._adjustInstDate(inst, offset +
      (period == 'M' ? this._get(inst, 'showCurrentAtPos') : 0), // Undo positioning
      period);
    this._updateDatepick(inst);
    return false;
  },

  /* Show the month for today or the current selection.
     @param  id  (string) the ID of the target field */
  _gotoToday: function(id) {
    var target = $(id);
    var inst = this._getInst(target[0]);
    if (this._get(inst, 'gotoCurrent') && inst.dates[0])
      inst.cursorDate = new Date(inst.dates[0].getTime());
    else
      inst.cursorDate = this._daylightSavingAdjust(new Date());
    inst.drawMonth = inst.cursorDate.getMonth();
    inst.drawYear = inst.cursorDate.getFullYear();
    this._notifyChange(inst);
    this._adjustDate(target);
    return false;
  },

  /* Selecting a new month/year.
     @param  id      (string) the ID of the target field
     @param  select  (element) the select being chosen from
     @param  period  (string) 'M' for month, 'Y' for year */
  _selectMonthYear: function(id, select, period) {
    var target = $(id);
    var inst = this._getInst(target[0]);
    inst._selectingMonthYear = false;
    var value = parseInt(select.options[select.selectedIndex].value, 10);
    inst[['selected', (period == 'M' ? 'Month' : 'Year')].join('')] =
    inst[['draw', (period == 'M' ? 'Month' : 'Year')].join('')] = value;
    inst.cursorDate.setDate(Math.min(inst.cursorDate.getDate(),
      $.datepick._getDaysInMonth(inst.drawYear, inst.drawMonth)));
    inst.cursorDate[['set', (period == 'M' ? 'Month' : 'FullYear')].join('')](value);
    this._notifyChange(inst);
    this._adjustDate(target);
  },

  /* Restore input focus after not changing month/year.
     @param  id  (string) the ID of the target field */
  _clickMonthYear: function(id) {
    var inst = this._getInst($(id)[0]);
    if (inst.input && inst._selectingMonthYear && !$.browser.msie)
      inst.input.focus();
    inst._selectingMonthYear = !inst._selectingMonthYear;
  },

  /* Action for changing the first week day.
     @param  id   (string) the ID of the target field
     @param  day  (number) the number of the first day, 0 = Sun, 1 = Mon, ... */
  _changeFirstDay: function(id, day) {
    var inst = this._getInst($(id)[0]);
    inst.settings.firstDay = day;
    this._updateDatepick(inst);
    return false;
  },

  /* Hover over a particular day.
     @param  id     (string) the ID of the target field
     @param  year   (number) the year for this day
     @param  month  (number) the month for this day
     @param  td     (element) the table cell containing the selection */
  _doHover: function(id, year, month, td) {
    if ($(td).hasClass(this._unselectableClass))
      return;
    var inst = this._getInst($(id)[0]);
    var onHover = this._get(inst, 'onHover');
    var date = (year ?
      this._daylightSavingAdjust(new Date(year, month, $(td).text())) : null);
    onHover.apply((inst.input ? inst.input[0] : null),
      [(date ? this._formatDate(inst, date) : ''), date, inst]);
  },

  /* Select a particular day.
     @param  id         (string) the ID of the target field
     @param  timestamp  (number) the timestamp for this day
     @param  td         (element) the table cell containing the selection */
  _selectDay: function(id, timestamp, td) {
    if ($(td).hasClass(this._unselectableClass))
      return false;
    var inst = this._getInst($(id)[0]);
    var rangeSelect = this._get(inst, 'rangeSelect');
    var multiSelect = this._get(inst, 'multiSelect');
    if (rangeSelect)
      inst.stayOpen = !inst.stayOpen;
    else if (multiSelect)
      inst.stayOpen = true;
    if (inst.stayOpen) {
      $('.datepick td', inst.dpDiv).removeClass(this._currentClass);
      $(td).addClass(this._currentClass);
    }
    inst.cursorDate = this._daylightSavingAdjust(new Date(timestamp));
    var date = new Date(inst.cursorDate.getTime());
    if (rangeSelect && !inst.stayOpen)
      inst.dates[1] = date;
    else if (multiSelect) {
      var pos = -1;
      for (var i = 0; i < inst.dates.length; i++)
        if (inst.dates[i] && date.getTime() == inst.dates[i].getTime()) {
          pos = i;
          break;
        }
      if (pos > -1)
        inst.dates.splice(pos, 1);
      else if (inst.dates.length < multiSelect) {
        if (inst.dates[0])
          inst.dates.push(date);
        else
          inst.dates = [date];
        inst.stayOpen = (inst.dates.length != multiSelect);
      }
    }
    else
      inst.dates = [date];
    this._updateInput(id);
    if (inst.stayOpen)
      this._updateDatepick(inst);
    else if ((rangeSelect || multiSelect) && inst.inline)
      this._updateDatepick(inst);
    return false;
  },

  /* Erase the input field and hide the date picker.
     @param  id  (string) the ID of the target field */
  _clearDate: function(id) {
    var target = $(id);
    var inst = this._getInst(target[0]);
    if (this._get(inst, 'mandatory'))
      return false;
    inst.stayOpen = false;
    inst.dates = (this._get(inst, 'showDefault') ?
      [this._getDefaultDate(inst)] : []);
    this._updateInput(target);
    return false;
  },

  /* Update the input field with the selected date.
     @param  id       (string) the ID of the target field or
                      (element) the target object */
  _updateInput: function(id) {
    var inst = this._getInst($(id)[0]);
    var dateStr = this._showDate(inst);
    this._updateAlternate(inst);
    var onSelect = this._get(inst, 'onSelect');
    if (onSelect)
      onSelect.apply((inst.input ? inst.input[0] : null),
        [dateStr, this._getDate(inst), inst]);  // Trigger custom callback
    else if (inst.input)
      inst.input.trigger('change'); // Fire the change event
    if (inst.inline)
      this._updateDatepick(inst);
    else if (!inst.stayOpen) {
      this._hideDatepick(null, this._get(inst, 'duration'));
      this._lastInput = inst.input[0];
      if (typeof(inst.input[0]) != 'object')
        inst.input.focus(); // Restore focus
      this._lastInput = null;
    }
    return false;
  },

  /* Update the input field with the current date(s).
     @param  inst  (object) the instance settings for this datepicker
     @return  (string) the formatted date(s) */
  _showDate: function(inst) {
    var dateStr = '';
    if (inst.input) {
      dateStr = (inst.dates.length == 0 ? '' : this._formatDate(inst, inst.dates[0]));
      if (dateStr) {
        if (this._get(inst, 'rangeSelect'))
          dateStr = [dateStr, this._get(inst, 'rangeSeparator'),
            this._formatDate(inst, inst.dates[1] || inst.dates[0])].join('');
        else if (this._get(inst, 'multiSelect'))
          for (var i = 1; i < inst.dates.length; i++)
            dateStr = [dateStr, this._get(inst, 'multiSeparator'),
              this._formatDate(inst, inst.dates[i])].join('');
      }
      inst.input.val(dateStr);
    }
    return dateStr;
  },

  /* Update any alternate field to synchronise with the main field.
     @param  inst  (object) the instance settings for this datepicker */
  _updateAlternate: function(inst) {
    var altField = this._get(inst, 'altField');
    if (altField) { // Update alternate field too
      var altFormat = this._get(inst, 'altFormat') || this._get(inst, 'dateFormat');
      var settings = this._getFormatConfig(inst);
      var dateStr = this.formatDate(altFormat, inst.dates[0], settings);
      if (dateStr && this._get(inst, 'rangeSelect'))
        dateStr = [dateStr, this._get(inst, 'rangeSeparator'), this.formatDate(
          altFormat, inst.dates[1] || inst.dates[0], settings)].join('');
      else if (this._get(inst, 'multiSelect'))
        for (var i = 1; i < inst.dates.length; i++)
          dateStr = [dateStr, this._get(inst, 'multiSeparator'),
            this.formatDate(altFormat, inst.dates[i], settings)].join('');
      $(altField).val(dateStr);
    }
  },

  /* Set as beforeShowDay function to prevent selection of weekends.
     @param  date  (Date) the date to customise
     @return  ([boolean, string]) is this date selectable?, what is its CSS class? */
  noWeekends: function(date) {
    return [(date.getDay() || 7) < 6, ''];
  },

  /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
     @param  date  (Date) the date to get the week for
     @return  (number) the number of the week within the year that contains this date */
  iso8601Week: function(date) {
    var checkDate = new Date(date.getTime());
    // Find Thursday of this week starting on Monday
    checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
    var time = checkDate.getTime();
    checkDate.setMonth(0); // Compare with Jan 1
    checkDate.setDate(1);
    return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
  },

  /* Provide status text for a particular date.
     @param  date  (Date) the date to get the status for
     @param  inst  (object) the current datepicker instance
     @return  (string) the status display text for this date */
  dateStatus: function(date, inst) {
    return $.datepick.formatDate($.datepick._get(inst, 'dateStatus'),
      date, $.datepick._getFormatConfig(inst));
  },

  /* Parse a string value into a date object.
     See formatDate below for the possible formats.
     @param  format    (string) the expected format of the date
     @param  value     (string) the date in the above format
     @param  settings  (object) attributes include:
                       shortYearCutoff  (number) the cutoff year for determining the century (optional)
                       dayNamesShort    (string[7]) abbreviated names of the days from Sunday (optional)
                       dayNames         (string[7]) names of the days from Sunday (optional)
                       monthNamesShort  (string[12]) abbreviated names of the months (optional)
                       monthNames       (string[12]) names of the months (optional)
     @return  (Date) the extracted date value or null if value is blank */
  parseDate: function (format, value, settings) {
    if (format == null || value == null)
      throw 'Invalid arguments';
    value = (typeof value == 'object' ? value.toString() : [value, ''].join(''));
    if (value == '')
      return null;
    settings = settings || {};
    var shortYearCutoff = settings.shortYearCutoff || this._defaults.shortYearCutoff;
    shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
      new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
    var dayNamesShort = settings.dayNamesShort || this._defaults.dayNamesShort;
    var dayNames = settings.dayNames || this._defaults.dayNames;
    var monthNamesShort = settings.monthNamesShort || this._defaults.monthNamesShort;
    var monthNames = settings.monthNames || this._defaults.monthNames;
    var year = -1;
    var month = -1;
    var day = -1;
    var doy = -1;
    var literal = false;
    // Check whether a format character is doubled
    var lookAhead = function(match) {
      var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
      if (matches)
        iFormat++;
      return matches;
    };
    // Extract a number from the string value
    var getNumber = function(match) {
      lookAhead(match);
      var size = (match == '@' ? 14 : (match == '!' ? 20 :
        (match == 'y' ? 4 : (match == 'o' ? 3 : 2))));
      var digits = new RegExp(['^\\d{1,', size, '}'].join(''));
      var num = value.substring(iValue).match(digits);
      if (!num)
        throw ['Missing number at position ', iValue].join('');
      iValue += num[0].length;
      return parseInt(num[0], 10);
    };
    // Extract a name from the string value and convert to an index
    var getName = function(match, shortNames, longNames) {
      var names = (lookAhead(match) ? longNames : shortNames);
      for (var i = 0; i < names.length; i++) {
        if (value.substr(iValue, names[i].length) == names[i]) {
          iValue += names[i].length;
          return i + 1;
        }
      }
      throw ['Unknown name at position ', iValue].join('');
    };
    // Confirm that a literal character matches the string value
    var checkLiteral = function() {
      if (value.charAt(iValue) != format.charAt(iFormat))
        throw ['Unexpected literal at position ', iValue].join('');
      iValue++;
    };
    var iValue = 0;
    for (var iFormat = 0; iFormat < format.length; iFormat++) {
      if (literal)
        if (format.charAt(iFormat) == "'" && !lookAhead("'"))
          literal = false;
        else
          checkLiteral();
      else
        switch (format.charAt(iFormat)) {
          case 'd':
            day = getNumber('d');
            break;
          case 'D':
            getName('D', dayNamesShort, dayNames);
            break;
          case 'o':
            doy = getNumber('o');
            break;
          case 'm':
            month = getNumber('m');
            break;
          case 'M':
            month = getName('M', monthNamesShort, monthNames);
            break;
          case 'y':
            year = getNumber('y');
            break;
          case '@':
            var date = new Date(getNumber('@'));
            year = date.getFullYear();
            month = date.getMonth() + 1;
            day = date.getDate();
            break;
          case '!':
            var date = new Date((getNumber('!') - this._ticksTo1970) / 10000);
            year = date.getFullYear();
            month = date.getMonth() + 1;
            day = date.getDate();
            break;
          case "'":
            if (lookAhead("'"))
              checkLiteral();
            else
              literal = true;
            break;
          default:
            checkLiteral();
        }
    }
    if (iValue < value.length)
      throw 'Additional text found at end';
    if (year == -1)
      year = new Date().getFullYear();
    else if (year < 100)
      year += (shortYearCutoff == -1 ? 1900 : new Date().getFullYear() -
        new Date().getFullYear() % 100 - (year <= shortYearCutoff ? 0 : 100));
    if (doy > -1) {
      month = 1;
      day = doy;
      do {
        var dim = this._getDaysInMonth(year, month - 1);
        if (day <= dim)
          break;
        month++;
        day -= dim;
      } while (true);
    }
    var date = this._daylightSavingAdjust(new Date(year, month - 1, day));
    if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day)
      throw 'Invalid date'; // E.g. 31/02/*
    return date;
  },

  /* Standard date formats. */
  ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601)
  COOKIE: 'D, dd M yy',
  ISO_8601: 'yy-mm-dd',
  RFC_822: 'D, d M y',
  RFC_850: 'DD, dd-M-y',
  RFC_1036: 'D, d M y',
  RFC_1123: 'D, d M yy',
  RFC_2822: 'D, d M yy',
  RSS: 'D, d M y', // RFC 822
  TICKS: '!',
  TIMESTAMP: '@',
  W3C: 'yy-mm-dd', // ISO 8601

  _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
    Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),

  /* Format a date object into a string value.
     The format can be combinations of the following:
     d  - day of month (no leading zero)
     dd - day of month (two digit)
     o  - day of year (no leading zeros)
     oo - day of year (three digit)
     D  - day name short
     DD - day name long
     m  - month of year (no leading zero)
     mm - month of year (two digit)
     M  - month name short
     MM - month name long
     y  - year (two digit)
     yy - year (four digit)
     @ - Unix timestamp (ms since 01/01/1970)
     ! - Windows ticks (100ns since 01/01/0001)
     '...' - literal text
     '' - single quote
     @param  format    (string) the desired format of the date
     @param  date      (Date) the date value to format
     @param  settings  (object) attributes include:
                       dayNamesShort    (string[7]) abbreviated names of the days from Sunday (optional)
                       dayNames         (string[7]) names of the days from Sunday (optional)
                       monthNamesShort  (string[12]) abbreviated names of the months (optional)
                       monthNames       (string[12]) names of the months (optional)
     @return  (string) the date in the above format */
  formatDate: function (format, date, settings) {
    if (!date)
      return '';
    var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
    var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
    var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
    var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
    // Check whether a format character is doubled
    var lookAhead = function(match) {
      var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
      if (matches)
        iFormat++;
      return matches;
    };
    // Format a number, with leading zero if necessary
    var formatNumber = function(match, value, len) {
      var num = ['', value].join('');
      if (lookAhead(match))
        while (num.length < len)
          num = ['0', num].join('');
      return num;
    };
    // Format a name, short or long as requested
    var formatName = function(match, value, shortNames, longNames) {
      return (lookAhead(match) ? longNames[value] : shortNames[value]);
    };
    var output = '';
    var literal = false;
    if (date)
      for (var iFormat = 0; iFormat < format.length; iFormat++) {
        if (literal)
          if (format.charAt(iFormat) == "'" && !lookAhead("'"))
            literal = false;
          else
            output = [output, format.charAt(iFormat)].join('');
        else
          switch (format.charAt(iFormat)) {
            case 'd':
              output = [output, formatNumber('d', date.getDate(), 2)].join('');
              break;
            case 'D':
              output = [output, formatName('D', date.getDay(), dayNamesShort, dayNames)].join('');
              break;
            case 'o':
              output = [output, formatNumber('o',
                (date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000, 3)].join('');
              break;
            case 'm':
              output = [output, formatNumber('m', date.getMonth() + 1, 2)].join('');
              break;
            case 'M':
              output = [output, formatName('M', date.getMonth(), monthNamesShort, monthNames)].join('');
              break;
            case 'y':
              output = [output, (lookAhead('y') ? date.getFullYear() :
                (date.getFullYear() % 100 < 10 ? '0' : '') + date.getFullYear() % 100)].join('');
              break;
            case '@':
              output = [output, date.getTime()].join('');
              break;
            case '!':
              output = [output, date.getTime() * 10000 + this._ticksTo1970].join('');
              break;
            case "'":
              if (lookAhead("'"))
                output = [output, "'"].join('');
              else
                literal = true;
              break;
            default:
              output = [output, format.charAt(iFormat)].join('');
          }
      }
    return output;
  },

  /* Get a setting value, defaulting if necessary.
     @param  inst  (object) the instance settings for this datepicker
     @param  name  (string) the name of the property
     @return  (any) the property's value */
  _get: function(inst, name) {
    return inst.settings[name] !== undefined ?
      inst.settings[name] : this._defaults[name];
  },

  /* Parse existing date and initialise date picker.
     @param  inst  (object) the instance settings for this datepicker */
  _setDateFromField: function(inst) {
    var dateFormat = this._get(inst, 'dateFormat');
    var rangeSelect = this._get(inst, 'rangeSelect');
    var multiSelect = this._get(inst, 'multiSelect');
    var dates = (inst.input ? inst.input.val() : '');
    dates = (rangeSelect ? dates.split(this._get(inst, 'rangeSeparator')) :
      (multiSelect ? dates.split(this._get(inst, 'multiSeparator')) : [dates]));
    inst.dates = [];
    var settings = this._getFormatConfig(inst);
    for (var i = 0; i < dates.length; i++)
      try {
        inst.dates[i] = this.parseDate(dateFormat, dates[i], settings);
      }
      catch (event) {
        inst.dates[i] = null;
      }
    for (var i = inst.dates.length - 1; i >= 0; i--)
      if (!inst.dates[i])
        inst.dates.splice(i, 1);
    if (rangeSelect && inst.dates.length < 2)
      inst.dates[1] = inst.dates[0];
    if (multiSelect && inst.dates.length > multiSelect)
      inst.dates.splice(multiSelect, inst.dates.length);
    inst.cursorDate = new Date((inst.dates[0] || this._getDefaultDate(inst)).getTime());
    inst.drawMonth = inst.cursorDate.getMonth();
    inst.drawYear = inst.cursorDate.getFullYear();
    this._adjustInstDate(inst);
  },

  /* Retrieve the default date shown on opening.
     @param  inst  (object) the instance settings for this datepicker
     @return  (Date) the default date */
  _getDefaultDate: function(inst) {
    return this._restrictMinMax(inst,
      this._determineDate(this._get(inst, 'defaultDate'), new Date()));
  },

  /* A date may be specified as an exact value or a relative one.
     @param  date         (Date or number or string) the date or offset
     @param  defaultDate  (Date) the date to use if no other supplied
     @return  (Date) the decoded date */
  _determineDate: function(date, defaultDate) {
    var offsetNumeric = function(offset) {
      var date = new Date();
      date.setDate(date.getDate() + offset);
      return date;
    };
    var offsetString = function(offset) {
      var date = new Date();
      var year = date.getFullYear();
      var month = date.getMonth();
      var day = date.getDate();
      var pattern = /([+-]?[0-9]+)\s*(d|w|m|y)?/g;
      var matches = pattern.exec(offset.toLowerCase());
      while (matches) {
        switch (matches[2] || 'd') {
          case 'd':
            day += parseInt(matches[1], 10); break;
          case 'w':
            day += parseInt(matches[1], 10) * 7; break;
          case 'm':
            month += parseInt(matches[1], 10);
            day = Math.min(day, $.datepick._getDaysInMonth(year, month));
            break;
          case 'y':
            year += parseInt(matches[1], 10);
            day = Math.min(day, $.datepick._getDaysInMonth(year, month));
            break;
        }
        matches = pattern.exec(offset.toLowerCase());
      }
      return new Date(year, month, day);
    };
    date = (date == null ? defaultDate : (typeof date == 'string' ? offsetString(date) :
      (typeof date == 'number' ? (isNaN(date) || date == Infinity || date == -Infinity ?
      defaultDate : offsetNumeric(date)) : date)));
    date = (date && (date.toString() == 'Invalid Date' ||
      date.toString() == 'NaN') ? defaultDate : date);
    if (date) {
      date.setHours(0);
      date.setMinutes(0);
      date.setSeconds(0);
      date.setMilliseconds(0);
    }
    return this._daylightSavingAdjust(date);
  },

  /* Handle switch to/from daylight saving.
     Hours may be non-zero on daylight saving cut-over:
     > 12 when midnight changeover, but then cannot generate
     midnight datetime, so jump to 1AM, otherwise reset.
     @param  date  (Date) the date to check
     @return  (Date) the corrected date */
  _daylightSavingAdjust: function(date) {
    if (!date) return null;
    date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
    return date;
  },

  /* Set the date(s) directly.
     @param  inst     (object) the instance settings for this datepicker
     @param  date     (Date or Date[] or number or string) the new date or start of a range
     @param  endDate  (Date or number or string) the end of a range */
  _setDate: function(inst, date, endDate) {
    date = (!date ? [] : (isArray(date) ? date : [date]));
    if (endDate)
      date.push(endDate);
    var clear = (date.length == 0);
    var origMonth = inst.cursorDate.getMonth();
    var origYear = inst.cursorDate.getFullYear();
    inst.dates = [];
    inst.dates[0] = this._restrictMinMax(inst, this._determineDate(date[0], new Date()));
    inst.cursorDate = new Date(inst.dates[0].getTime());
    inst.drawMonth = inst.cursorDate.getMonth();
    inst.drawYear = inst.cursorDate.getFullYear();
    if (this._get(inst, 'rangeSelect'))
      inst.dates[1] = (date.length < 1 ? inst.dates[0] :
        this._restrictMinMax(inst, this._determineDate(date[1], null)));
    else if (this._get(inst, 'multiSelect'))
      for (var i = 1; i < date.length; i++)
        inst.dates[i] = this._restrictMinMax(inst, this._determineDate(date[i], null));
    if (origMonth != inst.cursorDate.getMonth() || origYear != inst.cursorDate.getFullYear())
      this._notifyChange(inst);
    this._adjustInstDate(inst);
    this._showDate(inst);
  },

  /* Retrieve the date(s) directly.
     @param  inst  (object) the instance settings for this datepicker
     @return  (Date or Date[2] or Date[]) the current date or dates
              (for a range or multiples) */
  _getDate: function(inst) {
    var startDate = (inst.input && inst.input.val() == '' ? null : inst.dates[0]);
    if (this._get(inst, 'rangeSelect'))
      return (startDate ? [inst.dates[0], inst.dates[1] || inst.dates[0]] : [null, null]);
    else if (this._get(inst, 'multiSelect'))
      return inst.dates.slice(0, inst.dates.length);
    else
      return startDate;
  },

  /* Generate the HTML for the current state of the date picker.
     @param  inst  (object) the instance settings for this datepicker
     @return  (string) the new HTML for the datepicker */
  _generateHTML: function(inst) {
    var monthSelect = this._get(inst, 'monthSelect');
  
    var today = new Date();
    today = this._daylightSavingAdjust(
      new Date(today.getFullYear(), today.getMonth(), today.getDate())); // Clear time
    var showStatus = this._get(inst, 'showStatus');
    var initStatus = this._get(inst, 'initStatus') || '&#xa0;';
    var isRTL = this._get(inst, 'isRTL');
    // Build the date picker HTML
    var clear = (this._get(inst, 'mandatory') ? '' :
      ['<div class="datepick-clear"><a href="javascript:void(0)" onclick="jQuery.datepick._clearDate(\'#', inst.id, '\');"',
      this._addStatus(showStatus, inst.id, this._get(inst, 'clearStatus'), initStatus), '>',
      this._get(inst, 'clearText'), '</a></div>'].join(''));
    var controls = ['<div class="datepick-control">', (isRTL ? '' : clear),
      '<div class="datepick-close"><a href="javascript:void(0)" onclick="jQuery.datepick._hideDatepick();"',
      this._addStatus(showStatus, inst.id, this._get(inst, 'closeStatus'), initStatus), '>',
      this._get(inst, 'closeText'), '</a></div>', (isRTL ? clear : ''), '</div>'].join('');
    var prompt = this._get(inst, 'prompt');
    var closeAtTop = this._get(inst, 'closeAtTop');
    var hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext');
    var navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat');
    var showBigPrevNext = this._get(inst, 'showBigPrevNext');
    var numMonths = this._getNumberOfMonths(inst);
    var showCurrentAtPos = this._get(inst, 'showCurrentAtPos');
    var stepMonths = this._get(inst, 'stepMonths');
    var stepBigMonths = this._get(inst, 'stepBigMonths');
    var stepYears = this._get(inst, 'stepYears');
    var stepBigYears = this._get(inst, 'stepBigYears');
    var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
    var minDate = this._getMinMaxDate(inst, 'min', true);
    var maxDate = this._getMinMaxDate(inst, 'max');
    var drawMonth = inst.drawMonth - showCurrentAtPos;
    var drawYear = inst.drawYear;
    if (drawMonth < 0) {
      drawMonth += 12;
      drawYear--;
    }
    if (maxDate) { // Don't show past maximum unless also restricted by minimum
      var maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
        maxDate.getMonth() - numMonths[1] + 1, maxDate.getDate()));
      maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
      while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
        drawMonth--;
        if (drawMonth < 0) {
          drawMonth = 11;
          drawYear--;
        }
      }
    }
    inst.drawMonth = drawMonth;
    inst.drawYear = drawYear;
    // Controls and links
    var prevText = this._get(inst, 'prevText');
    prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
      this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
      this._getFormatConfig(inst)));
    var prevBigText = (showBigPrevNext ? this._get(inst, 'prevBigText') : '');
    prevBigText = (!navigationAsDateFormat ? prevBigText : this.formatDate(prevBigText,
      this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepBigMonths, 1)),
      this._getFormatConfig(inst)));
    var prev = ['<div class="datepick-prev">', (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
      [(showBigPrevNext ? ['<a href="javascript:void(0)" onclick="jQuery.datepick._adjustDate(\'#', inst.id, '\', -', (monthSelect ? stepBigYears * 12 : stepBigMonths), ', \'M\');"',
      this._addStatus(showStatus, inst.id, this._get(inst, 'prevBigStatus'), initStatus), '>', prevBigText, '</a>'].join('') : ''),
      '<a href="javascript:void(0)" onclick="jQuery.datepick._adjustDate(\'#', inst.id, '\', -', (monthSelect ? stepYears * 12 : stepMonths), ', \'M\');"',
      this._addStatus(showStatus, inst.id, this._get(inst, 'prevStatus'), initStatus), '>', prevText, '</a>'].join('') :
      (hideIfNoPrevNext ? '&#xa0;' : [(showBigPrevNext ? ['<label>', prevBigText, '</label>'].join('') : ''),
      '<label>', prevText, '</label>'].join(''))), '</div>'].join('');
    var nextText = this._get(inst, 'nextText');
    nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
      this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
      this._getFormatConfig(inst)));
    var nextBigText = (showBigPrevNext ? this._get(inst, 'nextBigText') : '');
    nextBigText = (!navigationAsDateFormat ? nextBigText : this.formatDate(nextBigText,
      this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepBigMonths, 1)),
      this._getFormatConfig(inst)));
    var next = ['<div class="datepick-next">', (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
      ['<a href="javascript:void(0)" onclick="jQuery.datepick._adjustDate(\'#', inst.id, '\', +', (monthSelect ? stepYears * 12 : stepMonths), ', \'M\');"',
      this._addStatus(showStatus, inst.id, this._get(inst, 'nextStatus'), initStatus), '>', nextText, '</a>',
      (showBigPrevNext ? ['<a href="javascript:void(0)" onclick="jQuery.datepick._adjustDate(\'#', inst.id, '\', +', (monthSelect ? stepBigYears * 12 : stepBigMonths), ', \'M\');"',
      this._addStatus(showStatus, inst.id, this._get(inst, 'nextBigStatus'), initStatus), '>', nextBigText, '</a>'].join('') : '')].join('') :
      (hideIfNoPrevNext ? '&#xa0;' : ['<label>', nextText, '</label>',
      (showBigPrevNext ? ['<label>', nextBigText, '</label>'].join('') : '')].join(''))), '</div>'].join('');
    var currentText = this._get(inst, 'currentText');
    var gotoDate = (this._get(inst, 'gotoCurrent') && inst.dates[0] ? inst.dates[0] : today);
    currentText = (!navigationAsDateFormat ? currentText :
      this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
    var html = [(closeAtTop && !inst.inline ? controls : ''),
      '<div class="datepick-links">', (isRTL ? next : prev),
      (this._isInRange(inst, gotoDate) ? ['<div class="datepick-current">',
      '<a href="javascript:void(0)" onclick="jQuery.datepick._gotoToday(\'#', inst.id, '\');"',
      this._addStatus(showStatus, inst.id, this._get(inst, 'currentStatus'), initStatus), '>',
      currentText, '</a></div>'].join('') : ''), (isRTL ? prev : next), '</div>',
      (prompt ? ['<div class="', this._promptClass, '"><span>', prompt, '</span></div>'].join('') : '')].join('');
    var firstDay = parseInt(this._get(inst, 'firstDay'), 10);
    firstDay = (isNaN(firstDay) ? 0 : firstDay);
    var changeFirstDay = this._get(inst, 'changeFirstDay');
    var dayNames = this._get(inst, 'dayNames');
    var dayNamesShort = this._get(inst, 'dayNamesShort');
    var dayNamesMin = this._get(inst, 'dayNamesMin');
    var monthNames = this._get(inst, 'monthNames');
    var monthNamesShort = this._get(inst, 'monthNamesShort');
    var beforeShowDay = this._get(inst, 'beforeShowDay');
    var highlightWeek = this._get(inst, 'highlightWeek');
    var showOtherMonths = this._get(inst, 'showOtherMonths');
    var selectOtherMonths = this._get(inst, 'selectOtherMonths');
    var showWeeks = this._get(inst, 'showWeeks');
    var calculateWeek = this._get(inst, 'calculateWeek') || this.iso8601Week;
    var weekStatus = this._get(inst, 'weekStatus');
    var status = (showStatus ? this._get(inst, 'dayStatus') || initStatus : '');
    var dateStatus = this._get(inst, 'statusForDate') || this.dateStatus;
    var onHover = this._get(inst, 'onHover');
    var defaultDate = this._getDefaultDate(inst);
    
    for (var row = 0; row < numMonths[0]; row++) {
      for (var col = 0; col < numMonths[1]; col++) {
        var cursorDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.cursorDate.getDate()));
        html = [html, '<div class="', this._oneMonthClass, (col == 0 ? ' datepick-new-row' : ''), '">',
          this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
          cursorDate, row > 0 || col > 0, showStatus, initStatus, monthNames), // Draw month headers
          '<table class="datepick" cellpadding="0" cellspacing="0">'].join('');
          
        if (!monthSelect){
          html = [html, '<thead>',
            '<tr class="datepick-title-row">',
            (showWeeks ? ['<th', this._addStatus(showStatus, inst.id, weekStatus, initStatus), '>',
            this._get(inst, 'weekHeader'), '</th>'].join('') : '')].join('');
          
          for (var dow = 0; dow < 7; dow++) { // Days of the week
            var day = (dow + firstDay) % 7;
            var dayStatus = (!showStatus || !changeFirstDay ? '' :
              status.replace(/DD/, dayNames[day]).replace(/D/, dayNamesShort[day]));
            html = [html, '<th', ((dow + firstDay + 6) % 7 < 5 ? '' : ' class="datepick-week-end-cell"'), '>',
              (!changeFirstDay ? ['<span', this._addStatus(showStatus, inst.id, dayNames[day], initStatus)].join('') :
              ['<a href="javascript:void(0)" onclick="jQuery.datepick._changeFirstDay(\'#', inst.id, '\', ', day, ');"',
              this._addStatus(showStatus, inst.id, dayStatus, initStatus)].join('')), ' title="', dayNames[day], '">',
              dayNamesMin[day], (changeFirstDay ? '</a>' : '</span>'), '</th>'].join('');
          }

          html = [html, '</tr></thead>'].join('');
        }
        
        html = [html, '<tbody>'].join('');
        
        // view full year (selectable months)
        if (monthSelect){
        
          for (var i = 0; i < 3; i++){
            html = [html, '<tr class="datepick-days-row">'].join('');
            
            for (var j = 0; j < 4; j++){ 
              var index = i + (j * 3);
              var printDate = this._daylightSavingAdjust(new Date(drawYear, index, 1));
              var unselectable = (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
              
              // console.log(printDate);
              html = [html, '<td class="datepick-days-cell',
                (index == today.getMonth() && drawYear == today.getFullYear() ? ' datepick-today' : ''),
                '"',
                
                ' onmouseover="',
                (
                  unselectable
                  ?
                  ''
                  :
                  [
                    'jQuery(this).parents(\'tbody\').',
                    'find(\'td\').removeClass(\'', this._dayOverClass, '\').end().end().',
                    'addClass(\'', this._dayOverClass, '\');'
                  ].join('')
                ), // Cursor
                '"',
                
                ' onmouseout="',
                (
                  unselectable
                  ?
                  ''
                  :
                  [
                    'jQuery(this).removeClass(\'', this._dayOverClass, '\');'
                  ].join('')
                ), // Remove cursor
                
                '"',
                
                (
                  unselectable
                  ?
                  ''
                  :
                  [
                    ' onclick="jQuery.datepick._selectDay(\'#', // Select
                    inst.id, '\'', ',', printDate.getTime(), ',this);"'
                  ].join('')
                ),
                
                '">',
                (
                  unselectable
                  ?
                  monthNamesShort[index]
                  :
                  [
                    '<a>', monthNamesShort[index], '</a>'
                  ].join('')
                ),
                '</td>'
              ].join(''); // Display for this month
            }
            
            html = [html, '</tr>'].join('');
              
          }
        }
        
        // view single month (selectable days)
        else {
    
          var daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
          if (drawYear == inst.cursorDate.getFullYear() && drawMonth == inst.cursorDate.getMonth())
            inst.cursorDate.setDate(Math.min(inst.cursorDate.getDate(), daysInMonth));
          var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
          var numRows = (isMultiMonth ? 6 : Math.ceil((leadDays + daysInMonth) / 7)); // Calculate the number of rows to generate
          var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
          for (var dRow = 0; dRow < numRows; dRow++) { // Create date picker rows
            html = [html, '<tr class="datepick-days-row">',
              (showWeeks ? ['<td class="datepick-week-col"',
              this._addStatus(showStatus, inst.id, weekStatus, initStatus), '>',
              calculateWeek(printDate), '</td>'].join('') : '')].join('');
            for (var dow = 0; dow < 7; dow++) { // Create date picker days
              var daySettings = (beforeShowDay ?
                beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']);
              var otherMonth = (printDate.getMonth() != drawMonth);
              var unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
                (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
              var current = (this._get(inst, 'rangeSelect') && inst.dates[0] &&
                printDate.getTime() >= inst.dates[0].getTime() &&
                printDate.getTime() <= (inst.dates[1] || inst.dates[0]).getTime());
              for (var i = 0; i < inst.dates.length; i++)
                current = current || (inst.dates[i] &&
                  printDate.getTime() == inst.dates[i].getTime());
              var empty = otherMonth && !showOtherMonths;
              html = 
                [
                  html, '<td class="datepick-days-cell',
                  (
                    (dow + firstDay + 6) % 7 >= 5 ? ' datepick-week-end-cell' : ''
                  ), // Highlight weekends
                  (otherMonth ? ' datepick-other-month' : ''), // Highlight days from other months
                  (
                    (printDate.getTime() == cursorDate.getTime() && drawMonth == inst.cursorDate.getMonth() && inst._keyEvent)
                    || // User pressed key
                    (defaultDate.getTime() == printDate.getTime() && defaultDate.getTime() == cursorDate.getTime())
                    ?
                    // Or defaultDate is current printedDate and defaultDate is cursorDate
                    [' ', $.datepick._dayOverClass].join('')
                    :
                    ''
                  ), // Highlight selected day
                  (unselectable ? [' ', this._unselectableClass].join('') : ''),  // Highlight unselectable days
                  (
                    empty
                    ?
                    ''
                    : 
                    [
                      ' ', daySettings[1], // Highlight custom dates
                      (current ? [' ', this._currentClass].join('') : ''), // Currently selected
                      (printDate.getTime() == today.getTime() ? ' datepick-today' : '')
                    ].join('')
                  ),
                  '"', // Highlight today (if different)
                  (!empty && daySettings[2] ? [' title="', daySettings[2], '"'].join('') : ''), // Cell title
                  ' onmouseover="',
                  (
                    unselectable
                    ?
                    ''
                    :
                    [
                      'jQuery(this).parents(\'tbody\').',
                      'find(\'td\').removeClass(\'', this._dayOverClass, '\').end().end().',
                      'addClass(\'', this._dayOverClass, '\');'
                    ].join('')
                  ), // Cursor
                  (
                    highlightWeek
                    ?
                    [
                      'jQuery(this).parent().parent().',
                      'find(\'tr\').removeClass(\'', this._weekOverClass, '\').end().end().',
                      'addClass(\'', this._weekOverClass, '\');'
                    ].join('')
                    :
                    ''
                  ), // Highlight week
                  (
                    !showStatus
                    ||
                    empty
                    ?
                    ''
                    :
                    [
                      'jQuery(\'#datepick-status-', inst.id, '\').html(\'', // Show status
                      (
                        dateStatus.apply((inst.input ? inst.input[0] : null), [printDate, inst]) || initStatus
                      ), '\');'
                    ].join('')
                  ),
                  (
                    onHover && !empty
                    ?
                    [
                      'jQuery.datepick._doHover(\'#', // onHover
                      inst.id, '\',', printDate.getFullYear(), ',', printDate.getMonth(), ', this);'
                    ].join('')
                    :
                    ''
                  ),
                  '"',
                  ' onmouseout="',
                  (
                    unselectable
                    ?
                    ''
                    :
                    [
                      'jQuery(this).removeClass(\'', this._dayOverClass, '\');'
                    ].join('')
                  ), // Remove cursor
                  (
                    highlightWeek
                    ?
                    [
                      'jQuery(this).parent().parent().',
                      'find(\'tr\').removeClass(\'', this._weekOverClass, '\');'
                    ].join('')
                    :
                    ''
                  ), // Remove week highlight
                  (
                    !showStatus || empty
                    ?
                    ''
                    :
                    [
                      'jQuery(\'#datepick-status-',
                      inst.id, '\').', 'html(\'', initStatus, '\');'
                    ].join('')
                  ),
                  (
                    onHover && !empty
                    ?
                    [
                      'jQuery.datepick._doHover(\'#', inst.id, '\');'
                    ].join('')
                    :
                    ''
                  ),
                  '"', // onHover
                  (
                    unselectable
                    ?
                    ''
                    :
                    [
                      ' onclick="jQuery.datepick._selectDay(\'#', // Select
                      inst.id, '\'', ',', printDate.getTime(), ',this);"'
                    ].join('')
                  ),
                  '>',
                  (
                    empty
                    ?
                    '&#xa0;'
                    : // Not showing other months
                    (
                      unselectable
                      ?
                      printDate.getDate()
                      :
                      [
                        '<a>', printDate.getDate(), '</a>'
                      ].join('')
                    )
                  ),
                  '</td>'
                ].join(''); // Display for this month
              printDate.setDate(printDate.getDate() + 1);
              printDate = this._daylightSavingAdjust(printDate);
            }
            html = [html, '</tr>'].join('');
          }
          drawMonth++;
          if (drawMonth > 11) {
            drawMonth = 0;
            drawYear++;
          }
          
        }
        
        html = [html, '</tbody></table></div>'].join('');
      }
    }
    
    
    html = [html, (showStatus ? ['<div style="clear: both;"></div><div id="datepick-status-', inst.id,
      '" class="datepick-status">', initStatus, '</div>'].join('') : ''),
      (!closeAtTop && !inst.inline ? controls : ''),
      '<div style="clear: both;"></div>',
      ($.browser.msie && parseInt($.browser.version, 10) < 7 && !inst.inline ?
      ['<iframe src="javascript:false;" class="', this._coverClass, '"></iframe>'].join('') : '')].join('');
      
    //////////  string concatenations optimized up to this point
          
    inst._keyEvent = false;
    return html;
  },

  /* Generate the month and year header.
     @param  inst  (object) the instance settings for this datepicker
     @param  drawMonth  (number) the current month
     @param  drawYear   (number) the current year
     @param  minDate    (Date) the minimum allowed date or null if none
     @param  maxDate    (Date) the maximum allowed date or null if none
     @return  (string) the HTML for the month and year */
  _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
      cursorDate, secondary, showStatus, initStatus, monthNames) {
    var minDraw = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1));
    minDate = (minDate < minDraw ? minDate : minDraw);
    var changeMonth = this._get(inst, 'changeMonth');
    var changeYear = this._get(inst, 'changeYear');
    var showMonthAfterYear = this._get(inst, 'showMonthAfterYear');
    var html = '<div class="datepick-header">';
    var monthHtml = '';
    var monthSelect = this._get(inst, 'monthSelect');
    
    if (!monthSelect){
      // Month selection
      if (secondary || !changeMonth)
        monthHtml = [monthHtml, '<span>', monthNames[drawMonth], '</span>'].join('');
      else {
        var inMinYear = (minDate && minDate.getFullYear() == drawYear);
        var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
        monthHtml = [monthHtml, '<select class="datepick-new-month" ',
          'onchange="jQuery.datepick._selectMonthYear(\'#', inst.id, '\', this, \'M\');" ',
          'onclick="jQuery.datepick._clickMonthYear(\'#', inst.id, '\');"',
          this._addStatus(showStatus, inst.id, this._get(inst, 'monthStatus'), initStatus), '>'].join('');
        for (var month = 0; month < 12; month++) {
          if ((!inMinYear || month >= minDate.getMonth()) &&
              (!inMaxYear || month <= maxDate.getMonth()))
            monthHtml = [monthHtml, '<option value="', month, '"',
              (month == drawMonth ? ' selected="selected"' : ''),
              '>', monthNames[month], '</option>'].join('');
        }
        monthHtml = [monthHtml, '</select>'].join('');
      }
      
      if (!showMonthAfterYear)
        html = [html, monthHtml, (secondary || !changeMonth || !changeYear ? '&#xa0;' : '')].join('');
    }
      
    // Year selection
    if (secondary || !changeYear)
      html = [html, '<span>', drawYear, '</span>'].join('');
    else {
      // Determine range of years to display
      var years = this._get(inst, 'yearRange').split(':');
      var year = 0;
      var endYear = 0;
      if (years.length != 2) {
        year = drawYear - 10;
        endYear = drawYear + 10;
      } else if (years[0].charAt(0) == '+' || years[0].charAt(0) == '-') {
        year = drawYear + parseInt(years[0], 10);
        endYear = drawYear + parseInt(years[1], 10);
      } else {
        year = parseInt(years[0], 10);
        endYear = parseInt(years[1], 10);
      }
      year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
      endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
      html = [html, '<select class="datepick-new-year" ',
        'onchange="jQuery.datepick._selectMonthYear(\'#', inst.id, '\', this, \'Y\');" ',
        'onclick="jQuery.datepick._clickMonthYear(\'#', inst.id, '\');"',
        this._addStatus(showStatus, inst.id, this._get(inst, 'yearStatus'), initStatus), '>'].join('');
      for (; year <= endYear; year++) {
        html = [html, '<option value="', year, '"',
          (year == drawYear ? ' selected="selected"' : ''),
          '>', year, '</option>'].join('');
      }
      html = [html, '</select>'].join('');
    }
    html = [html, this._get(inst, 'yearSuffix')].join('');
    
    if (!monthSelect){
      if (showMonthAfterYear)
        html = [html, (secondary || !changeMonth || !changeYear ? '&#xa0;' : ''),  monthHtml].join('');
    }
      
    html = [html, '</div>'].join(''); // Close datepicker_header
    return html;
  },

  /* Provide code to set and clear the status panel.
     @param  showStatus  (boolean) true if the status bar is shown
     @param  id          (string) the ID of the datepicker instance
     @param  text        (string) the status text to display
     @param  initStatus  (string) the default status message
     @return  (string) hover actions for the status messages */
  _addStatus: function(showStatus, id, text, initStatus) {
    return (showStatus ? [' onmouseover="jQuery(\'#datepick-status-', id,
      '\').html(\'', (text || initStatus), '\');" ',
      'onmouseout="jQuery(\'#datepick-status-', id,
      '\').html(\'', initStatus, '\');"'].join('') : '');
  },

  /* Adjust one of the date sub-fields.
     @param  inst    (object) the instance settings for this datepicker
     @param  offset  (number) the change to apply
     @param  period  (string) 'D' for days, 'M' for months, 'Y' for years */
  _adjustInstDate: function(inst, offset, period) {
    var year = inst.drawYear + (period == 'Y' ? offset : 0);
    var month = inst.drawMonth + (period == 'M' ? offset : 0);
    var day = Math.min(inst.cursorDate.getDate(), this._getDaysInMonth(year, month)) +
      (period == 'D' ? offset : 0);
    inst.cursorDate = this._restrictMinMax(inst,
      this._daylightSavingAdjust(new Date(year, month, day)));
    inst.drawMonth = inst.cursorDate.getMonth();
    inst.drawYear = inst.cursorDate.getFullYear();
    if (period == 'M' || period == 'Y')
      this._notifyChange(inst);
  },

  /* Ensure a date is within any min/max bounds.
     @param  inst  (object) the instance settings for this datepicker
     @param  date  (Date) the date to check
     @return  (Date) the restricted date */
  _restrictMinMax: function(inst, date) {
    var minDate = this._getMinMaxDate(inst, 'min', true);
    var maxDate = this._getMinMaxDate(inst, 'max');
    date = (minDate && date < minDate ? minDate : date);
    date = (maxDate && date > maxDate ? maxDate : date);
    return date;
  },

  /* Notify change of month/year.
     @param  inst  (object) the instance settings for this datepicker */
  _notifyChange: function(inst) {
    var onChange = this._get(inst, 'onChangeMonthYear');
    if (onChange)
      onChange.apply((inst.input ? inst.input[0] : null),
        [inst.cursorDate.getFullYear(), inst.cursorDate.getMonth() + 1,
        this._daylightSavingAdjust(new Date(
        inst.cursorDate.getFullYear(), inst.cursorDate.getMonth(), 1)), inst]);
  },

  /* Determine the number of months to show.
     @param  inst  (object) the instance settings for this datepicker
     @return  (number[2]) the number of rows and columns to display */
  _getNumberOfMonths: function(inst) {
    var numMonths = this._get(inst, 'numberOfMonths');
    return (numMonths == null ? [1, 1] :
      (typeof numMonths == 'number' ? [1, numMonths] : numMonths));
  },

  /* Determine the current minimum/maximum date.
     Ensure no time components are set. May be overridden for a range.
     @param  inst        (object) the instance settings for this datepicker
     @param  minMax      (string) 'min' or 'max' for required date
     @param  checkRange  (boolean) true to allow override for a range minimum
     @return  (Date) the minimum/maximum date or null if none */
  _getMinMaxDate: function(inst, minMax, checkRange) {
    var date = this._determineDate(this._get(inst, [minMax, 'Date'].join('')), null);
    var rangeMin = this._getRangeMin(inst);
    return (checkRange && rangeMin && (!date || rangeMin > date) ? rangeMin : date);
  },

  /* Retrieve the temporary range minimum when in the process of selecting.
     @param  inst  (object) the instance settings for this datepicker
     @return  (Date) the temporary minimum or null */
  _getRangeMin: function(inst) {
    return (this._get(inst, 'rangeSelect') && inst.dates[0] &&
      !inst.dates[1] ? inst.dates[0] : null);
  },

  /* Find the number of days in a given month.
     @param  year   (number) the full year
     @param  month  (number) the month (0 to 11)
     @return  (number) the number of days in this month */
  _getDaysInMonth: function(year, month) {
    return 32 - new Date(year, month, 32).getDate();
  },

  /* Find the day of the week of the first of a month.
     @param  year   (number) the full year
     @param  month  (number) the month (0 to 11)
     @return  (number) 0 = Sunday, 1 = Monday, ... */
  _getFirstDayOfMonth: function(year, month) {
    return new Date(year, month, 1).getDay();
  },

  /* Determines if we should allow a "prev/next" month display change.
     @param  inst      (object) the instance settings for this datepicker
     @param  offset    (number) the number of months to change by
     @param  curYear   (number) the full current year
     @param  curMonth  (number) the current month (0 to 11)
     @return  (boolean) true if prev/next allowed, false if not */
  _canAdjustMonth: function(inst, offset, curYear, curMonth) {
    var numMonths = this._getNumberOfMonths(inst);
    var date = this._daylightSavingAdjust(new Date(
      curYear, curMonth + (offset < 0 ? offset : numMonths[1]), 1));
    if (offset < 0)
      date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
    return this._isInRange(inst, date);
  },

  /* Is the given date in the accepted range?
     @param  inst  (object) the instance settings for this datepicker
     @param  date  (Date) the date to check
     @return  (boolean) true if the date is in the allowed minimum/maximum, false if not */
  _isInRange: function(inst, date) {
    // During range selection, use minimum of selected date and range start
    var minDate = this._getRangeMin(inst) || this._getMinMaxDate(inst, 'min');
    var maxDate = this._getMinMaxDate(inst, 'max');
    return ((!minDate || date >= minDate) && (!maxDate || date <= maxDate));
  },

  /* Provide the configuration settings for formatting/parsing.
     @param  inst  (object) the instance settings for this datepicker
     @return  (object) the settings subset */
  _getFormatConfig: function(inst) {
    return {shortYearCutoff: this._get(inst, 'shortYearCutoff'),
      dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'),
      monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')};
  },

  /* Format the given date for display.
     @param  inst   (object) the instance settings for this datepicker
     @param  year   (number, optional) the full year
     @param  month  (number, optional) the month of the year (0 to 11)
     @param  day    (number, optional) the day of the month
     @return  (string) formatted date */
  _formatDate: function(inst, year, month, day) {
    if (!year)
      inst.dates[0] = new Date(inst.cursorDate.getTime());
    var date = (year ? (typeof year == 'object' ? year :
      this._daylightSavingAdjust(new Date(year, month, day))) : inst.dates[0]);
    return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst));
  }
});

/* jQuery extend now ignores nulls!
   @param  target  (object) the object to extend
   @param  props   (object) the new settings
   @return  (object) the updated object */
function extendRemove(target, props) {
  $.extend(target, props);
  for (var name in props)
    if (props[name] == null || props[name] == undefined)
      target[name] = props[name];
  return target;
};

/* Determine whether an object is an array.
   @param  a  (object) the object to test
   @return  (boolean) true if an array, false if not */
function isArray(a) {
  return (a && a.constructor == Array);
};

/* Invoke the datepicker functionality.
   @param  options  (string) a command, optionally followed by additional parameters or
                    (object) settings for attaching new datepicker functionality
   @return  (jQuery) jQuery object */
$.fn.datepick = function(options){
  var otherArgs = Array.prototype.slice.call(arguments, 1);
  if (typeof options == 'string' && (options == 'isDisabled' ||
      options == 'getDate' || options == 'settings'))
    return $.datepick[['_', options, 'Datepick'].join('')].
      apply($.datepick, [this[0]].concat(otherArgs));
  if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
    return $.datepick[['_', options, 'Datepick'].join('')].
      apply($.datepick, [this[0]].concat(otherArgs));
  return this.each(function() {
    typeof options == 'string' ?
      $.datepick[['_', options, 'Datepick'].join('')].
        apply($.datepick, [this].concat(otherArgs)) :
      $.datepick._attachDatepick(this, options);
  });
};

$.datepick = new Datepick(); // Singleton instance

$(function() {
  $(document).mousedown($.datepick._checkExternalClick).
    find('body').append($.datepick.dpDiv);
});

})(jQuery);

/**
 * .disableTextSelect - Disable Text Select Plugin
 *
 * Version: 1.1
 * Updated: 2007-11-28
 *
 * Used to stop users from selecting text
 *
 * Copyright (c) 2007 James Dempster (letssurf@gmail.com, http://www.jdempster.com/category/jquery/disabletextselect/)
 *
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 **/

/**
 * Requirements:
 * - jQuery (John Resig, http://www.jquery.com/)
 **/
(function($) {
    if ($.browser.mozilla) {
        $.fn.disableTextSelect = function() {
            return this.each(function() {
                $(this).css({
                    'MozUserSelect' : 'none'
                });
            });
        };
        $.fn.enableTextSelect = function() {
            return this.each(function() {
                $(this).css({
                    'MozUserSelect' : ''
                });
            });
        };
    } else if ($.browser.msie) {
        $.fn.disableTextSelect = function() {
            return this.each(function() {
                $(this).bind('selectstart.disableTextSelect', function() {
                    return false;
                });
            });
        };
        $.fn.enableTextSelect = function() {
            return this.each(function() {
                $(this).unbind('selectstart.disableTextSelect');
            });
        };
    } else {
        $.fn.disableTextSelect = function() {
            return this.each(function() {
                $(this).bind('mousedown.disableTextSelect', function() {
                    return false;
                });
            });
        };
        $.fn.enableTextSelect = function() {
            return this.each(function() {
                $(this).unbind('mousedown.disableTextSelect');
            });
        };
    }
})(jQuery);
/*
 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
 *
 * Uses the built in easing capabilities added In jQuery 1.1
 * to offer multiple easing options
 *
 * TERMS OF USE - jQuery Easing
 * 
 * Open source under the BSD License. 
 * 
 * Copyright Â© 2008 George McGinley Smith
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list 
 * of conditions and the following disclaimer in the documentation and/or other materials 
 * provided with the distribution.
 * 
 * Neither the name of the author nor the names of contributors may be used to endorse 
 * or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 *
*/

// t: current time, b: begInnIng value, c: change In value, d: duration
jQuery.easing['jswing'] = jQuery.easing['swing'];

jQuery.extend( jQuery.easing,
{
	def: 'easeOutQuad',
	swing: function (x, t, b, c, d) {
		//alert(jQuery.easing.default);
		return jQuery.easing[jQuery.easing.def](x, t, b, c, d);
	},
	easeInQuad: function (x, t, b, c, d) {
		return c*(t/=d)*t + b;
	},
	easeOutQuad: function (x, t, b, c, d) {
		return -c *(t/=d)*(t-2) + b;
	},
	easeInOutQuad: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t + b;
		return -c/2 * ((--t)*(t-2) - 1) + b;
	},
	easeInCubic: function (x, t, b, c, d) {
		return c*(t/=d)*t*t + b;
	},
	easeOutCubic: function (x, t, b, c, d) {
		return c*((t=t/d-1)*t*t + 1) + b;
	},
	easeInOutCubic: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t + b;
		return c/2*((t-=2)*t*t + 2) + b;
	},
	easeInQuart: function (x, t, b, c, d) {
		return c*(t/=d)*t*t*t + b;
	},
	easeOutQuart: function (x, t, b, c, d) {
		return -c * ((t=t/d-1)*t*t*t - 1) + b;
	},
	easeInOutQuart: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
		return -c/2 * ((t-=2)*t*t*t - 2) + b;
	},
	easeInQuint: function (x, t, b, c, d) {
		return c*(t/=d)*t*t*t*t + b;
	},
	easeOutQuint: function (x, t, b, c, d) {
		return c*((t=t/d-1)*t*t*t*t + 1) + b;
	},
	easeInOutQuint: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
		return c/2*((t-=2)*t*t*t*t + 2) + b;
	},
	easeInSine: function (x, t, b, c, d) {
		return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
	},
	easeOutSine: function (x, t, b, c, d) {
		return c * Math.sin(t/d * (Math.PI/2)) + b;
	},
	easeInOutSine: function (x, t, b, c, d) {
		return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
	},
	easeInExpo: function (x, t, b, c, d) {
		return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
	},
	easeOutExpo: function (x, t, b, c, d) {
		return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
	},
	easeInOutExpo: function (x, t, b, c, d) {
		if (t==0) return b;
		if (t==d) return b+c;
		if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
		return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
	},
	easeInCirc: function (x, t, b, c, d) {
		return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
	},
	easeOutCirc: function (x, t, b, c, d) {
		return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
	},
	easeInOutCirc: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
		return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
	},
	easeInElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
	},
	easeOutElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
	},
	easeInOutElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
		return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
	},
	easeInBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c*(t/=d)*t*((s+1)*t - s) + b;
	},
	easeOutBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
	},
	easeInOutBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158; 
		if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
		return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
	},
	easeInBounce: function (x, t, b, c, d) {
		return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
	},
	easeOutBounce: function (x, t, b, c, d) {
		if ((t/=d) < (1/2.75)) {
			return c*(7.5625*t*t) + b;
		} else if (t < (2/2.75)) {
			return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
		} else if (t < (2.5/2.75)) {
			return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
		} else {
			return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
		}
	},
	easeInOutBounce: function (x, t, b, c, d) {
		if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
		return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
	}
});

/*
 *
 * TERMS OF USE - EASING EQUATIONS
 * 
 * Open source under the BSD License. 
 * 
 * Copyright Â© 2001 Robert Penner
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list 
 * of conditions and the following disclaimer in the documentation and/or other materials 
 * provided with the distribution.
 * 
 * Neither the name of the author nor the names of contributors may be used to endorse 
 * or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 *
 */
/**
 * jQuery.FastTrigger
 * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
 * Dual licensed under MIT and GPL.
 * Date: 4/5/2008
 *
 * @projectDescription Faster event triggering for jQuery.
 *
 * @author Ariel Flesler
 * @version 1.0.0
 */
;(function( $ ){
	
	//this is a fake event object, will stay with us all along, don't break it
	var event = {
		//no need for real data, what data would you expect from a programatic trigger
		pageX:0,
		pageY:0,
		which:0,
		button:0,
		metaKey:false,
		ctrlKey:false,
		charCode:' ',
		keyCode:0,
		//no need for real functions
		preventDefault:function(){},
		stopPropagation:function(){}
	};

	$.fn.fastTrigger = function( type, args ){
		var e = event,
			ns, any = true;//any is the same as "not-exclusive"
		
		if( !args || !args.length )//what if args is a string ? args CAN'T be a string (docs.jquery.com).
			args = null;//args must be an array, or nothing
		else if( args[0].preventDefault )
			e = args[0];		
		else
			args.unshift( e );
		
		if( type.indexOf('!') != -1 ){
			any = false;//exclusive
			type = type.slice(0, -1);
		}
		
		ns = type.split('.');
		e.type = type = ns[0];//ensure the right type
		any &= !(ns = ns[1]);//cache this value, no need to check all each time

		return this.each(function(){
			var 
				handlers = ( $.data(this,'events') || {} )[type],//don't do 2 $.data like jQuery, they are slow
				handler;

			if( handlers ){
				e.target = e.relatedTarget = this;
				for( var i in handlers ){
					handler = handlers[i];
					if(	any || handler.type == ns ){
						e.data = handler.data;
						if( args )//call is slightly faster, thus preferred
							handler.apply( this, args );
						else
							handler.call( this, e );
					}
				}
			}
		});
	};
	
	$.fastTrigger = function( type, args ){
		//the native method is not THAT faster, but still better
		$(document.getElementsByTagName('*')).add([window,document]).fastTrigger( type, args );
	};
	
})( jQuery );
/**
 * Stupid Fixed Header 1.0.3 - jQuery plugins to create a fixed headers
 * 
 * Require: jQuery 1.2.6
 * Author: Jacky See
 * Blog: http://jacky.seezone.net
 * email:  jackysee at gmail dot com
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
*/

(function($){
	
	/* created fixed headers , require jquery dimenions plugins*/
	$.fn.fixedHeader = function(o){
		var s = {adjustWidth: $.fixedHeader.calcWidth};
		if(o) $.extend(s,o);
		
		return this.each(function(){
			var table = $(this); //table
			var tId = this.id;
			
			var scrollBarWidth = $.fixedHeader.getScrollBarWidth();
			var IE6 = $.browser.msie && $.browser.version == '6.0';
			
			//wrap a body container
			var bodyContainer = table.wrap('<div></div>').parent()
				.attr('id', tId + "_body_container")
				.css({
					width: s.width,
					height: s.height,
					overflow: 'auto'
				});
			
			//Wrap with an overall container
			var tableContainer = bodyContainer.wrap('<div></div>').parent()
				.attr('id', tId + '_table_container')
				.css('position','relative');

			//clone the header
			var position = table.position();
			var headerContainer = $(document.createElement('div'))
				.attr('id', tId + '_header_container')
				.css({
					width:  bodyContainer.innerWidth() - scrollBarWidth,
					height: table.find('thead').outerHeight(), 
					overflow: 'hidden',
					top: position.top, left:position.left
				})
				.prependTo(tableContainer);
			
			var headerTable = table.clone(true)
				.find('tbody').remove().end()
				.attr('id',tId + "_header")
				.addClass(s.tableClass || table[0].className)
				.css({
					//width: $.browser.msie? table.outerWidth():table.width(), 
					'table-layout':'fixed',
					position:'absolute',
					top:0, left:0
				})
				.append(table.find('thead').clone(true))
				.appendTo(headerContainer);
			
			//sync header width
			var headThs = headerTable.find('th');
			table.find('th').each(function(i){
				headThs.eq(i).css('width', s.adjustWidth(this));
			})
			
			//sync scroll
			var selects = IE6? table.find("select"): null;
			bodyContainer.scroll(function(){
				if(IE6 && selects.size()>0){
					selects.each(function(i){
						this.style.visibility =
							($(this).offset().top - bodyContainer.offset().top) <= table.find("thead").outerHeight() + 10
							? 'hidden':'visible';
					});
				}
				headerTable.css({
					left: '-' + $(this).scrollLeft() + 'px'
				});
			})
			
			//Move it down
			headerContainer.css({
				'position': 'absolute',
				'top': 0
			});
		});
	}
	
	$.fixedHeader = {
		calcWidth: function(th){
			var w = $(th).width();
			var paddingLeft = $.fixedHeader.getComputedStyleInPx(th,'paddingLeft');
			var paddingRight = $.fixedHeader.getComputedStyleInPx(th,'paddingRight');
			var borderWidth = $.fixedHeader.getComputedStyleInPx(th,'borderRightWidth');			
			if($.browser.msie) w = w+borderWidth;
			if($.browser.opera) w = w+borderWidth;
			if($.browser.safari) w = w+paddingLeft+paddingRight+borderWidth*2;
			if($.browser.mozilla && parseFloat($.browser.version) <= 1.8) w=w+borderWidth; //FF2 still got a border-left missing problem, this is the best I can do.
			return w;
		},
		getComputedStyleInPx: function(elem,style){
			var computedStyle = (typeof elem.currentStyle != 'undefined')
				?elem.currentStyle
				:document.defaultView.getComputedStyle(elem, null);
			var val = computedStyle[style];
			val = val? parseInt(val.replace("px","")):0;
			return (!val || val == 'NaN')?0:val;
		},
		getScrollBarWidth: function() { //calculate or get from global the scroll bar width
			if(!$.fixedHeader.scrollBarWidth){ 
				var inner = $(document.createElement('p')).css({width:'100%',height:'100%'});
				var outer = $(document.createElement('div'))
					.css({
						position:'absolute',
						top: '0px',
						left: '0px',
						visibility: 'hidden',
						width: '200px',
						height: '150px',
						overflow: 'hidden'
					})
					.append(inner)
					.appendTo(document.body);
				
				var w1 = inner[0].offsetWidth;
				outer[0].style.overflow = 'scroll';
				var w2 = inner[0].offsetWidth;
				if (w1 == w2) w2 = outer[0].clientWidth;
				document.body.removeChild (outer[0]);
				$.fixedHeader.scrollBarWidth = (w1 - w2);
			}
			return $.fixedHeader.scrollBarWidth;
		}
	}
	
})(jQuery);

/** jQuery textarea autogrow plugin
 *   jQuery.1.2.6 required
 * 
 * optional parameters: // $(elem).growfield( options ) 
 * * * * * * * * * * * * * * * * * * *
 * auto - bool // default = true // if false, ctrl + up/down are enabled
 * animate - bool // default = true
 * before(jEvent), after(jEvent) - callbacks // 'this' will be the dom object
 * min - integer - height in pixels // default = min-height ? css.min-height || initial height
 * max - integer - height in pixels // maximum size // default = css.max-height || none
 * restore - bool - restore original size on blur (and back to growed on focus) // default = false
 * speed - integer - animation speed. not a jquery parameter // default = 300 (ms)
 * offset - integer - bottom offset // default is calculated for each textarea separately // i don't recommend to touch it :) 
 * 
 * additional functions (public interface):
 * * * * * * * * * * * * * * * * * * *
 * $(..).increase( [step] ), decrease( [step] ), growTo( height ), growToggleAuto( bool ), growToggleRestore( bool ), 
 * growSetMin( integer ), growSetMax( integer )
 * 
 * notes
 * * * * * * * * * * * * * * * * * * *
 * parameters may be set in html attributes // tag attributes have priority 
 * <textarea autogrow=1/0 animate=1/0 speed='' line='' min='' max='' restore='' step=''></textarea>
 * 
 * increase and decrease functions are working only in auto mode
 * 
 * opera:
 * in opera, if you don't have border style for textarea, plugin will set it (opera hides borders in overflow:hidden mode). 
 * in auto mode it won't work at all. More than that, opera returns border:2px solid #00000 even if you don't set it. :(
 * 
 * known problems: 
 * after restore (onfocus) with animation textarea sometimes looses cursor. :( // ff2, ie7xp
 * when reached maximum height, opera will jitter on every keydown :( // opera < 9.5
 * 
 * ctrl + up/down: 
 * in opera and ie this shortcut doesn't work properly. But you may use ctrl + whatever + up/down.
 * 
 * three things that make possible scrollHeight update in opera < 9.5:
 * (actually, we don't need scrollHeight in opera < 9.5, but we need something to be updated when typing)
 * height: auto, toggle overflow to hidden and back to auto, and (!!!) padding >= 4px (am i crazy?!)
 * we need textarea because only in this way we can calculate height with all inherited styles
 * 
 *  @author: johann kuindji (www.kuindji.com, www.stuffedguys.com, www.stuffedtracker.com) jk@kuindji.com
 *  @version 1.1
 *  
 *  example: 
 *  $('textarea').growfield();
 */

(function($){
$.fn.growfield = function( options ) {
    this.each( function() {
        this._growField = true;
        var txt = $(this);        
        if (this.tagName.toLowerCase() != 'textarea') return false;
        if (!options) options = {};
        var th = this;
        this.gf = {
            auto: (typeof(options.auto) != 'undefined' ? options.auto : true),
            animate: (typeof(options.animate) != 'undefined' ? options.animate : true),
            hOffset: (typeof(options.offset) != 'undefined' ? options.offset : 0),
            before: (options.before || null), after: (options.after || null),
            min: (options.min || false),  max: (options.max || false), restore: (options.restore || false), initialH:0,
            busy: false, keysEnabled: false, dummy: null, queue: 0, speed: 300, ms: 15, timeout: false, opera9: false, impossible: false
        };

        this._growInit = function() {
            if (this.gf.before) this._growCallbackBefore = this.gf.before;
            if (this.gf.after) this._growCallbackAfter = this.gf.after;
            this.gf.initialH = txt.get(0).offsetHeight;
            if (typeof(txt.attr('autogrow')) != 'undefined') this.gf.auto = parseInt(txt.attr('autogrow'));
            if (typeof(txt.attr('animate')) != 'undefined') this.gf.animate = parseInt(txt.attr('animate'));
            if (typeof(txt.attr('min')) != 'undefined') this.gf.min = parseInt(txt.attr('min'));
            if (typeof(txt.attr('max')) != 'undefined') this.gf.max = parseInt(txt.attr('max'));
            if (typeof(txt.attr('restore')) != 'undefined') this.gf.restore = parseInt(txt.attr('restore'));
            if (typeof(txt.attr('speed')) != 'undefined') this.gf.speed = parseInt(txt.attr('speed'));
            if (!this.gf.min) this.gf.min = parseInt(txt.css('min-height'));
            if (!this.gf.min) this.gf.min = this.gf.initialH;
            if (!this.gf.max) this.gf.max = parseInt(txt.css('max-height'));
            this.gf.opera9 = ($.browser.opera && $.browser.version < 9.5);
            if (this.gf.restore) { this.gf.restore = false; this._toggleRestore( true );}
            if (this.gf.auto) {
                if (this.gf.initialH == 0) { txt.bind('keyup.growinit', this._afterShowInit); } 
                else { this.gf.auto = false; this._toggleAuto( true ); }
            }
            else this._toggleKeys( true );
            return true;
        };

        this._afterShowInit = function() {
            this.gf.initialH = txt.get(0).offsetHeight;
            if (!this.gf.initialH) return true;
            if (!this.gf.min) this.gf.min = this.gf.initialH;
            txt.unbind('.growinit');
            this.gf.auto = false; 
            this._toggleAuto( true );
            txt.focus();
            return true;
        };

        this._toggleKeys = function( on ) {
            if ( on ) {
                if (this.gf.keysEnabled) return false;
                txt.bind( ($.browser.msie? 'keyup':'keydown')+'.autogrow', function( event ) { 
                    if (!event.ctrlKey || $.inArray(event.keyCode,[38,40])==-1) return true;
                    txt[ event.keyCode==38 ? 'decrease':'increase' ]( false, event );
                    if ($.browser.opera) txt.focus(); // !!
                    if ($.browser.msie || $.browser.opera) return false;
                    return true;
                });
                this.gf.keysEnabled = true;
            } else {
                if (!this.gf.keysEnabled) return false;
                txt.unbind(($.browser.msie? 'keyup':'keydown')+'.autogrow');
                this.gf.keysEnabled = false;
            }
        };

        this._toggleAuto = function( on ) {
            if ($.browser.opera || on ) {
                txt.css('overflow', 'hidden');
                if ($.browser.opera &&  txt.attr('style') && txt.attr('style').indexOf('border') == -1) txt.css('border', '1px solid #ccc'); // see notes
            }
            if ( on ) {
                if (this.gf.auto) return false; 
                this._toggleKeys( false );
                this._createDummy();
                if (this.gf.impossible) { this._growField = false; return false; }
                txt.css('overflow', 'hidden');
                txt.bind('keyup.autogrow', function( event ) { 
                    if (!txt.val()) this._changeSize(this.gf.min, event ); 
                    else return this._changeSize(this._textHeight(), event ); 
                });
                if ( txt.val() ) txt.keyup();
                $(window).bind('resize.autogrow', function( event ) { th.gf.dummy.width( txt.width() );});
                this.gf.auto = true;
            } else {
                if (!this.gf.auto) return false;
                this.gf.dummy.remove();
                this.gf.dummy = null;
                txt.unbind('keyup.autogrow');
                $(window).unbind('resize.autogrow');
                txt.css('overflow', 'auto');
                this.gf.auto = false;
                this._toggleKeys( true );
            }
            return true;
        };

        this._toggleRestore = function( on ) {
            if ( on ) {
                if (this.gf.restore) return false;
                this.gf.restore = true;
                txt.bind('focus.autogrow', function( event, noChange ) {
                    if (!this.gf.auto || noChange || !txt.val()) return true; 
                    else return this._changeSize(this._textHeight(), event );
                });
                txt.bind('blur.autogrow', function( event ) { return this.gf.auto ? this._changeSize( this.gf.min, event ) : true; });
            } else {
                if (!this.gf.restore) return false;
                this.gf.restore = false;
                txt.unbind('focus.autogrow').unbind('blur.autogrow');
                txt.keyup();
            }
        };

        this._changeSize = function( to, event ) {
            if (this.gf.busy) return true;
            if (!event) event = {};
            this._growBefore( event );
            var ovr = txt.css('overflow');

            if (this.gf.max > 0 && to >= this.gf.max) {  // if we have reached the maximum height
                to = this.gf.max;  
                if (ovr == 'hidden') { // if overflow is still hidden, we need to switch it to auto so that user could see the text 
                    txt.css('overflow', 'auto');
                    if (event.type=='keyup') txt.focus();
                    if (event.type=='focus' && this.gf.animate && this.gf.auto) txt.trigger('focus', true); 
                }
            }
            else if (ovr=='auto' && this.gf.auto)  { // if there is a space left (or no maximum is defined) 
                txt.css('overflow', 'hidden');  // we need to switch overflow back to hidden
                if (event.type=='keyup') txt.focus(); // focus event is neccessary 
            } 
            if (this.gf.min > 0 && to <= this.gf.min) to = this.gf.min; // if we have minimum height

            if (to == txt.get(0).offsetHeight) {this.gf.busy=false; return true;}

            return this._animate( txt.get(0).offsetHeight, to, event, ovr);
        };

        this._animate = function( from, to, event, ovr ) {
            if (!this.gf.animate || (ovr == 'auto' && this.gf.auto) ||  event.type == 'init' ) {
                txt.height( to );
                return this._growAfter( event );
            }
            this.gf.queue = Math.floor((this.gf.speed / this.gf.ms) * ( to < from ? -1 : 1));
            this._timeout( from, to );
            return true;
        };

        this._timeout = function( from, to ) {
            if (th.gf.queue==0) return th._growAfter();
            th.gf.queue += (th.gf.queue > 0 ? -1 : 1);
            if ( Math.abs(to-from)<3 ) {
                txt.height( to );
                return th._growAfter();
            }
            from = th.gf.queue == 0 ?  to : from + Math.ceil( (to-from) /2);
            txt.height( from );
            th.gf.timeout = window.setTimeout(function(){ th._timeout(from, to) }, th.gf.ms);
        };

        this._textHeight = function() {
            var val = txt.val();
            if ($.browser.safari && val.substr(-2) == "\n\n") val = val.substring(0,val.length-2)+"\n11"; // empty row fix for safari
            if ($.browser.safari) this.gf.dummy.val(''); // another fix for safari
            this.gf.dummy.val(val); 
            if (this.gf.opera9) this.gf.dummy.css('overflow', 'hidden').css('overflow', 'auto');
            var d = this.gf.dummy.get(0);
            if (this.gf.opera9) return d.clientHeight + this.gf.hOffset;
            if ((d.scrollHeight + ($.browser.safari ? 1 : 0)) > d.clientHeight) {
                return d.scrollHeight +( d.offsetHeight-d.clientHeight) + this.gf.hOffset + ($.browser.safari ? this.gf.hOffset : 0);
            }
            else {
                if ( !txt.val()) return this.gf.min;
                else return d.offsetHeight + this.gf.hOffset;
            } 
        };

        this._createDummy = function() {
            if (this.gf.dummy) { this.gf.dummy.remove(); this.gf.dummy = false; }
            var i = true; var tryPadding = false;
            while (i) {
                this.gf.dummy = txt.clone().css({position:'absolute', left:-9999, top:0, visibility: 'hidden', width: txt.get(0).offsetWidth}).attr('tabindex', -9999);
                this.gf.dummy.attr('id', this.gf.dummy.attr('id') + '_dummy');
                 // in hidden mode opera < 9.5 doesn't update scrollHeight, but if we change overflow every time, it almost works
                if (this.gf.opera9) { 
                    this.gf.dummy.css({overflow:'auto', height:'auto'});
                    var padding = txt.css('padding');
                    if ((padding && padding< 4) || tryPadding) this.gf.dummy.css({padding: '4px'}); // !!!! only with padding textarea will update scrollHeight
                }
                this.gf.dummy.get(0)._growField = false;
                txt.after(this.gf.dummy);
                this.gf.dummy.val('').height(10);
                this.gf.dummy.val("11");
                if (this.gf.opera9) this.gf.dummy.css('overflow', 'hidden').css('overflow', 'auto');
                var s1 = this.gf.dummy.get(0).scrollHeight;
                this.gf.dummy.val("11\n11");
                if (this.gf.opera9) this.gf.dummy.css('overflow', 'hidden').css('overflow', 'auto');
                var s2 = this.gf.dummy.get(0).scrollHeight;
                if (!this.gf.hOffset) {
                    this.gf.hOffset = s2-s1;
                    if ($.browser.opera && !this.gf.opera9) this.gf.hOffset += this.gf.dummy.get(0).offsetHeight - this.gf.dummy.height(); 
                }
                if (this.gf.opera9 && this.gf.hOffset==0) {
                    if (tryPadding) i=false;
                    else { tryPadding=true; this.gf.dummy.remove(); continue; }
                }
                else i=false;
            }
        };

        this._growBefore = function( event ) { this.gf.busy = true; this._growCallbackBefore( event ); };
        this._growAfter = function( event ) { 
            if (this.gf.timeout) { 
                window.clearTimeout(this.gf.timeout); 
                this.gf.timeout = false; 
            }
            this.gf.busy = false; 
            this._growCallbackAfter( event ); 
            return true;
        };
        this._growCallbackBefore = function() {};
        this._growCallbackAfter = function() {};
        $(function() { th._growInit(); });
    });
};

$.fn.increase = function( step, event ) { this.each( function() { 
    if (!this._growField || this.gf.auto) return true;
    this._changeSize( this.offsetHeight + (step ? parseInt(step) : this.gf.hOffset), event);
});};
$.fn.decrease = function( step, event ) { this.each( function() { 
    if (!this._growField || this.gf.auto) return true;
    this._changeSize( this.offsetHeight - (step ? parseInt(step) : this.gf.hOffset), event);
});};
$.fn.growToggleAuto = function( bool ) {
    if (bool && bool != true && bool != false && bool.toLowerCase() != 'on' && bool.toLowerCase() != 'off') delete(bool);
    if (bool && typeof(bool)=='string') bool = bool.toLowerCase()=='on' ? true: false; 
    this.each( function() { 
        if (!this._growField) return true; 
        this._toggleAuto(bool);
});};
$.fn.growToggleAnimation = function( bool ) {
    if (bool && bool != true && bool != false && bool.toLowerCase() != 'on' && bool.toLowerCase() != 'off') delete(bool);
    if (bool && typeof(bool)=='string') bool = bool.toLowerCase()=='on' ? true: false; 
    this.each( function() { 
        if (!this._growField) return true; 
        this.gf.animate = bool;
});};
$.fn.growTo = function( h ) { this.each( function() { 
    if (!this._growField) return true; 
    this._changeSize(h);
});};
$.fn.growSetMin = function( h ) { this.each( function() { 
    if (!this._growField) return true;
    h = parseInt(h);
    if (h < 10 && this.gf.initialH) h = this.gf.initialH;
    if (h < 10) return true;
    this.gf.min = parseInt(h);
    if (this.offsetHeight < this.gf.min) $(this).growTo(this.gf.min);
});};
$.fn.growSetMax = function( h ) {  this.each( function() { 
    if (!this._growField) return true;
    this.gf.max = parseInt(h);
    if (this.offsetHeight > this.gf.max) $(this).growTo(this.gf.max);
});};
$.fn.growToggleRestore = function( bool ) {
    if (bool && bool != true && bool != false && bool.toLowerCase() != 'on' && bool.toLowerCase() != 'off') delete(bool);
    if (bool && typeof(bool)=='string') bool = bool.toLowerCase()=='on' ? true: false;  
    this.each( function() { 
        if (!this._growField) return true;
        this._toggleRestore( bool );
});};
$.fn.growRenewDummy = function() { this.each(function(){
    if (!this._growField) return true; 
    this._createDummy();
});};

})(jQuery);
(function ($){

$.fn.headlock = function(options) {
  
  function fnAdjustTable(){

    var colCount = $('#firstTr>td').length; //get total number of column

    var m = 0;
    var n = 0;
    var brow = 'mozilla';
    
    jQuery.each(jQuery.browser, function(i, val) {
      if(val == true){
        brow = i.toString();
      }
    });
    
    $('.tableHeader').each(function(i){
      if (m < colCount){

        if (brow == 'mozilla'){
          $('#firstTd').css("width",$('.tableFirstCol').innerWidth());//for adjusting first td
          $(this).css('width',$('#table_div td:eq('+m+')').innerWidth());//for assigning width to table Header div
        }
        else if (brow == 'msie'){
          $('#firstTd').css("width",$('.tableFirstCol').width());
          $(this).css('width',$('#table_div td:eq('+m+')').width()-2);//In IE there is difference of 2 px
        }
        else if (brow == 'safari'){
          $('#firstTd').css("width",$('.tableFirstCol').width());
          $(this).css('width',$('#table_div td:eq('+m+')').width());
        }
        else {
          $('#firstTd').css("width",$('.tableFirstCol').width());
          $(this).css('width',$('#table_div td:eq('+m+')').innerWidth());
        }
      }
      m++;
    });

    $('.tableFirstCol').each(function(i){
      if(brow == 'mozilla'){
        $(this).css('height',$('#table_div td:eq('+colCount*n+')').outerHeight());//for providing height using scrollable table column height
      }
      else if(brow == 'msie'){
        $(this).css('height',$('#table_div td:eq('+colCount*n+')').innerHeight()-2);
      }
      else {
        $(this).css('height',$('#table_div td:eq('+colCount*n+')').height());
      }
      n++;
    });

  }

  //function to support scrolling of title and first column
  function fnScroll(){
    $('#divHeader').scrollLeft($('#table_div').scrollLeft());
    $('#firstcol').scrollTop($('#table_div').scrollTop());
  }
  
  function test(param){
    console.log(param);
  }

  var settings = jQuery.extend({
     name: "defaultName",
     size: 5,
     global: true
  }, options);

  return this.each(function(){
    test($(this)[0].className);
  });
  
  
};
  
})(jQuery);
/*

highlight v3

Highlights arbitrary terms.

<http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html>

MIT license.

Johann Burkard
<http://johannburkard.de>
<mailto:jb@eaio.com>

*/

jQuery.fn.highlight = function(pat) {
  var result = [];

  function innerHighlight(node, pat) {
    var skip = 0;
    if (node.nodeType == 3) {
      var pos = node.data.toUpperCase().indexOf(pat);
      if (pos >= 0) {
        var spannode = document.createElement('span');
        spannode.className = 'highlightResult';
        var middlebit = node.splitText(pos);
        var endbit = middlebit.splitText(pat.length);
        var middleclone = middlebit.cloneNode(true);
        spannode.appendChild(middleclone);
        middlebit.parentNode.replaceChild(spannode, middlebit);
        skip = 1;
        result.push(spannode);
        //console.log(count);
      }
    }
    else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
      for (var i = 0; i < node.childNodes.length; ++i) {
        i += innerHighlight(node.childNodes[i], pat);
      }
    }
    return skip;
  }
  
  this.each(function() {
    innerHighlight(this, pat.toUpperCase());
  });
  
  return result;
};

jQuery.fn.removeHighlight = function() {
  return this.find("span.highlightResult").each(function() {
    this.parentNode.firstChild.nodeName;
    with (this.parentNode) {
      replaceChild(this.firstChild, this);
      normalize();
    }
  }).end();
};

/*
 * jQuery idleTimer plugin
 * version 0.7.080609
 * by Paul irish. 
 *   http://github.com/paulirish/yui-misc/tree/
 * MIT license
 
 * adapted from YUI idle timer by nzakas:
 *   http://github.com/nzakas/yui-misc/
 
 
 * Copyright (c) 2009 Nicholas C. Zakas
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

(function($){

$.idleTimer = function(newTimeout){

    //$.idleTimer.tId = -1     //timeout ID

    var idle    = false,        //indicates if the user is idle
        enabled = true,        //indicates if the idle timer is enabled
        timeout = 30000,        //the amount of time (ms) before the user is considered idle
        events  = 'mousemove keydown DOMMouseScroll mousewheel mousedown', // activity is one of these events
        
    /* (intentionally not documented)
     * Toggles the idle state and fires an appropriate event.
     * @return {void}
     */
    toggleIdleState = function(){
    
        //toggle the state
        idle = !idle;
        
        //fire appropriate event
        $(document).trigger((idle ? "idle" : "active") + '.idleTimer');            
    },

    
    /**
     * Indicates if the idle timer is running or not.
     * @return {Boolean} True if the idle timer is running, false if not.
     * @method isRunning
     * @static
     */
    isRunning = function(){
        return enabled;
    },
    
    /**
     * Indicates if the user is idle or not.
     * @return {Boolean} True if the user is idle, false if not.
     * @method isIdle
     * @static
     */        
    isIdle = function(){
        return idle;
    },
    
    /**
     * Stops the idle timer. This removes appropriate event handlers
     * and cancels any pending timeouts.
     * @return {void}
     * @method stop
     * @static
     */         
    stop = function(){
    
        //set to disabled
        enabled = false;
        
        //clear any pending timeouts
        clearTimeout($.idleTimer.tId);
        
        //detach the event handlers
        $(document).unbind('.idleTimer');
    },
    
    
    /* (intentionally not documented)
     * Handles a user event indicating that the user isn't idle.
     * @param {Event} event A DOM2-normalized event object.
     * @return {void}
     */
    handleUserEvent = function(){
    
        //clear any existing timeout
        clearTimeout($.idleTimer.tId);
        
        
        
        //if the idle timer is enabled
        if (enabled){
        
          
            //if it's idle, that means the user is no longer idle
            if (idle){
                toggleIdleState();           
            } 
        
            //set a new timeout
            $.idleTimer.tId = setTimeout(toggleIdleState, timeout);
            
        }    
     };
    
      
    /**
     * Starts the idle timer. This adds appropriate event handlers
     * and starts the first timeout.
     * @param {int} newTimeout (Optional) A new value for the timeout period in ms.
     * @return {void}
     * @method $.idleTimer
     * @static
     */ 
    
    
    
    //assign a new timeout if necessary
    if (typeof newTimeout == "number"){
        timeout = newTimeout;
    } else if (newTimeout === 'destroy') {
        stop();
        return;  
    }
    
    //assign appropriate event handlers
    $(document).bind($.trim((events+' ').split(' ').join('.idleTimer ')),handleUserEvent);
    
    
    //set a timeout to toggle state
    $.idleTimer.tId = setTimeout(toggleIdleState, timeout);
  
    

    
}; // end of $.idleTimer()

    

})(jQuery);
/**
 * @Keith Bentrup
 */
jQuery.fn.css2 = jQuery.fn.css;
jQuery.fn.css = function () {
	if (arguments.length) return jQuery.fn.css2.apply(this,arguments);
	var attr = ['font-family','font-size','font-weight','font-style','color',
		'text-transform','text-decoration','letter-spacing','word-spacing',
		'line-height','text-align','vertical-align','direction','background-color',
		'background-image','background-repeat','background-position',
		'background-attachment','opacity','width','height','top','right','bottom',
		'left','margin-top','margin-right','margin-bottom','margin-left',
		'padding-top','padding-right','padding-bottom','padding-left',
		'border-top-width','border-right-width','border-bottom-width',
		'border-left-width','border-top-color','border-right-color',
		'border-bottom-color','border-left-color','border-top-style',
		'border-right-style','border-bottom-style','border-left-style','position',
		'display','visibility','z-index','overflow-x','overflow-y','white-space',
		'clip','float','clear','cursor','list-style-image','list-style-position',
		'list-style-type','marker-offset'
	];
	var len = attr.length, obj = {}, val;
	for (var i = 0; i < len; i++) obj[attr[i]] = jQuery.fn.css2.call(this,attr[i]);
	return obj;
}

/**
 * jQuery.Listen - Light and fast event handling, using event delegation.
 * Copyright (c) 2007-2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
 * Dual licensed under MIT and GPL.
 * Date: 3/7/2008
 * http://flesler.blogspot.com/2007/10/jquerylisten.html
 * @version 1.0.3
 */
;(function($){var a='indexer',h=$.event,j=h.special,k=$.listen=function(c,d,e,f){if(typeof d!='object'){f=e;e=d;d=document}o(c.split(/\s+/),function(a){a=k.fixes[a]||a;var b=m(d,a)||m(d,a,new n(a,d));b.append(e,f);b.start()})},m=function(b,c,d){return $.data(b,c+'.'+a,d)};$.fn[a]=function(a){return this[0]&&m(this[0],a)||null};$[a]=function(a){return m(document,a)};$.extend(k,{regex:/^((?:\w*?|\*))(?:([#.])([\w-]+))?$/,fixes:{focus:'focusin',blur:'focusout'},cache:function(a){this.caching=a}});$.each(k.fixes,function(a,b){j[b]={setup:function(){if($.browser.msie)return!1;this.addEventListener(a,j[b].handler,!0)},teardown:function(){if($.browser.msie)return!1;this.removeEventListener(a,j[b].handler,!0)},handler:function(e){arguments[0]=e=h.fix(e);e.type=b;return h.handle.apply(this,arguments)}}});$.fn.listen=function(a,b,c){return this.each(function(){k(a,this,b,c)})};function n(a,b){$.extend(this,{ids:{},tags:{},listener:b,event:a});this.id=n.instances.push(this)};n.instances=[];n.prototype={constructor:n,handle:function(e){var a=e.stopPropagation;e.stopPropagation=function(){e.stopped=1;a.apply(this,arguments)};m(this,e.type).parse(e);e.stopPropagation=a;a=e.data=null},on:0,bubbles:0,start:function(){var a=this;if(!a.on){h.add(a.listener,a.event,a.handle);a.on=1}},stop:function(){var a=this;if(a.on){h.remove(a.listener,a.event,a.handle);a.on=0}},cache:function(a,b){return $.data(a,'listenCache_'+this.id,b)},parse:function(e){var z=this,c=e.data||e.target,d=arguments,f;if(!k.caching||!(f=z.cache(c))){f=[];if(c.id&&z.ids[c.id])p(f,z.ids[c.id]);o([c.nodeName,'*'],function(a){var b=z.tags[a];if(b)o((c.className+' *').split(' '),function(a){if(a&&b[a])p(f,b[a])})});if(k.caching)z.cache(c,f)}if(f[0]){o(f,function(a){if(a.apply(c,d)===!1){e.preventDefault();e.stopPropagation()}})}if(!e.stopped&&(c=c.parentNode)&&(c.nodeName=='A'||z.bubbles&&c!=z.listener)){e.data=c;z.parse(e)}f=d=c=null},append:function(f,g){var z=this;o(f.split(/\s*,\s*/),function(a){var b=k.regex.exec(a);if(!b)throw'$.listen > "'+a+'" is not a supported selector.';var c=b[2]=='#'&&b[3],d=b[1].toUpperCase()||'*',e=b[3]||'*';if(c)(z.ids[c]||(z.ids[c]=[])).push(g);else if(d){d=z.tags[d]=z.tags[d]||{};(d[e]||(d[e]=[])).push(g)}})}};function o(a,b,c){for(var i=0,l=a.length;i<l;i++)b.call(c,a[i],i)};function p(a,b){a.push.apply(a,b);return a};$(window).unload(function(){if(typeof n=='function')o(n.instances,function(b){b.stop();$.removeData(b.listener,b.event+'.'+a);b.ids=b.names=b.listener=null})})})(jQuery);
/*
  jQuery multiSelect

  Version 1.0 beta

  Cory S.N. LaViska
  A Beautiful Site (http://abeautifulsite.net/)
  06 April 2008

  Visit http://abeautifulsite.net/notebook.php?article=62 for more information

  Usage: $('#control_id').multiSelect( options, callback )

  Options:  selectAll          - whether or not to display the Select All option; true/false, default = true
            selectAllText      - text to display for selecting/unselecting all options simultaneously
            noneSelected       - text to display when there are no selected items in the list
            oneOrMoreSelected  - text to display when there are one or more selected items in the list
                                 (note: you can use % as a placeholder for the number of items selected).
                                 Use * to show a comma separated list of all selected; default = '% selected'


  MODS (these don't come from the original author):
  
            inputClass         - name of css class to be applied to created INPUT element
                                 this will be used in addition to the standard 'multiSelect' class
                                 useful if you'd like to inherit existing formatting rather than modifying multiselect.css
                                 
            noneSelectedClass  - name of css class to be applied to INPUT element when no options are selected
                                 
            optionsClass       - name of css class to be applied to created options DIV element
                                 this will be used in addition to the standard 'multiSelectOptions' class
                                 
            selectExclusive    - only allow one item to be selected at a time; true/false or array of which items should be exclusive, default = false
                                 array of items to be exclusive should be specified as element values
                                 if array specified, array items will *only* be exclusive with each other, but can still be selected in addition to
                                   other items not listed in array
            
            selectExclusiveAll - only allow one item to be selected at a time; true/false or array of which items should be exclusive, default = false
                                 array of items to be exclusive should be specified as element values
                                 if array specified, array items will be exclusive with *all* other items, not just the other items listed in the array
            
            separator          - override the default ', ' (comma space) separator when more than one item is selected,
                                 for example '; ' or ' + '
                                 
            readOnly           - set to true to prevent INPUT element from accepting input

            readOnlyClass      - name of css class to be applied to created INPUT element when readOnly is true


  Dependencies:  jQuery 1.2 or higher (http://jquery.com/)
                 the jQuery Dimensions plugin (http://plugins.jquery.com/project/dimensions)

  Licensing & Terms of Use

  jQuery File Tree is licensed under a Creative Commons License and is copyrighted (C)2008 by Cory S.N. LaViska.
  For details, visit http://creativecommons.org/licenses/by/3.0/us/
  	
*/
if(jQuery) (function($){
	
	$.extend($.fn, {
		multiSelect: function(o, callback) {
			// Default options
			if( !o ) o = {};
			if( o.selectAll == undefined ) o.selectAll = true;
			if( o.selectAllText == undefined ) o.selectAllText = "Select All";
			if( o.noneSelected == undefined ) o.noneSelected = 'Select options';
			if( o.oneOrMoreSelected == undefined ) o.oneOrMoreSelected = '% selected';
			if( o.inputClass == undefined ) o.inputClass = '';
			if( o.noneSelectedClass == undefined ) o.noneSelectedClass = '';
			if( o.optionsClass == undefined ) o.optionsClass = '';
			if( o.selectExclusive == undefined ) o.selectExclusive = false;
			if( o.selectExclusiveAll == undefined ) o.selectExclusiveAll = false;
			if( o.separator == undefined ) o.separator = ', ';
			if( o.readOnly == undefined ) o.readOnly = false;
			if( o.readOnlyClass == undefined ) o.readOnlyClass = '';
      
			// Initialize each multiSelect
			$(this).each( function() {
				var select = $(this);
				var htmlArray = ['<input type="text" id="', select.attr('id'), '" name="', select.attr('name'), '" unique="', select.attr('unique'), '" readonly="readonly" class="multiSelect', (o.inputClass ? [' ', o.inputClass].join('') : ''), 
          (o.readOnly && o.readOnlyClass ? [' ', o.readOnlyClass].join('') : ''),
          '" value="" />',
          // '<span class="dropdownArrow">test</span>',
          '<div class="multiSelectOptions', (o.inputClass ? [' ', o.inputClass].join('') : ''), '" style="',
          // 'position: absolute; z-index: 9999; ',
          'display: none;">'
        ];
        
				if( o.selectAll ) htmlArray.push('<label class="selectAll"><input type="checkbox" class="selectAll" />', o.selectAllText, '</label>');
				
        select.find('OPTION').each( function() {
          var thisEl = $(this);
          var thisVal = thisEl.val();
          var thisDesc = thisEl.attr('description');
					if( thisVal != '' ) {
						htmlArray.push('<label value="', thisVal, '"><input type="checkbox" name="', select.attr('name'), '" value="', thisVal, '"');
						
            if( thisEl.attr('selected') ) htmlArray.push(' checked="checked"');
						htmlArray.push(' /><span class="name">', thisEl.html(), '</span>');

            if (thisDesc){
              htmlArray.push('<span class="description"> (', thisDesc, ')</span>');
            }
              
            htmlArray.push('</label>');
            
            // create unique element id for this input
            // combines SELECT element id with this option's value, converting all non-alphanumerical characters to hyphens
            // var inputId = ($(select).attr('name') + '_' + $(this).val()).replace(/[^\w]/g, "-");
						// html += '<input type="checkbox" name="' + $(select).attr('name') + '" value="' + $(this).val() + '" id="' + inputId + '"';
						// if( $(this).attr('selected') ) html += ' checked="checked"';
						// html += ' /><label for="' + inputId + '">' + $(this).html() + '</label>';
					}
				});
        
				htmlArray.push('</div>');
        
        // htmlArray.push(
          // '<div class="dropdownArrow hidden" />'
        // );

				select.after(htmlArray.join(''));
        
        // var inputPos = select.nextAll('.multiSelect').eq(0).position();
        // var inputWidth = select.nextAll('.multiSelect').eq(0).width();
        // var dda = select.siblings('.dropdownArrow');
        // dda.css({
          // // 'border-color': '#99f',
          // 'top': inputPos.top,
          // 'left': inputPos.left + inputWidth
        // });
				
        // if (!o.readOnly){
          // Events
          select.nextAll('.multiSelect').eq(0).mouseover( function() {
            $(this).addClass('hover');
          }).mouseout( function() {
            $(this).removeClass('hover');
          }).click( function() {
            var thisEl = $(this);
            // Show/hide on click
            if( thisEl.hasClass('active') ) {
              thisEl.multiSelectOptionsHide();
            } else if (!o.readOnly) {
              thisEl.multiSelectOptionsShow();
            }
            return false;
          }).focus( function() {
            // So it can be styled with CSS
            $(this).addClass('focus');
          }).blur( function() {
            // So it can be styled with CSS
            $(this).removeClass('focus');
          });
        // }
				
				// Determine if Select All should be checked initially
				if( o.selectAll ) {
					var sa = true;
					select.nextAll('.multiSelect').eq(0).next('.multiSelectOptions').find('INPUT:checkbox').not('.selectAll').each( function() {
						if( !$(this).attr('checked') ) sa = false;
					});
					if( sa ) select.nextAll('.multiSelect').eq(0).next('.multiSelectOptions').find('INPUT.selectAll').attr('checked', true).parent().addClass('checked');
				}
				
				// Handle Select All
				select.nextAll('.multiSelect').eq(0).next('.multiSelectOptions').find('INPUT.selectAll').click( function() {
          var thisEl = $(this);
					if( thisEl.attr('checked') == true ){
            thisEl.parent().parent().find('INPUT:checkbox').attr('checked', true).parent().addClass('checked');
          }
          else {
            thisEl.parent().parent().find('INPUT:checkbox').attr('checked', false).parent().removeClass('checked');
          }
				});
				
				// Handle checkboxes
        // $(select).nextAll('.multiSelect').eq(0).next('.multiSelectOptions').find('LABEL').click(function(){
          // Acat.jq.form.fblog(['label clicked = ', $(this).html()].join(''), true);
        // });
				// // Acat.jq.form.fblog(["labels '1' = ", $(select).nextAll('.multiSelect').eq(0).next('.multiSelectOptions').find('LABEL').eq(0).html()].join(''), true);
        
				select.nextAll('.multiSelect').eq(0).next('.multiSelectOptions').find('INPUT:checkbox').click( function() {
          var toBeUnchecked;
          var i;
          var thisEl = $(this);
          var thisGPEl = thisEl.parent().parent(); // grandparent element
        
          if (
            o.selectExclusive === true
            ||
            o.selectExclusiveAll === true
          ){
            $('.multiSelect').multiSelectOptionsHide();
          }
          
          if (
            o.selectExclusive === true
            ||
            (
              o.selectExclusive.length
              &&
              ($.inArray(Number(thisEl.attr('value')), o.selectExclusive) != -1)
            )

            ||
            
            o.selectExclusiveAll === true
            ||
            (
              o.selectExclusiveAll.length
              &&
              ($.inArray(Number(thisEl.attr('value')), o.selectExclusiveAll) != -1)
            )
          ){
            // uncheck any other checked exclusive inputs
            toBeUnchecked = thisEl.parents('.multiSelectOptions').find(['input:checked[value!=', thisEl.attr('value'), ']'].join('')); // .attr('checked', false);
            
            // fblog(toBeUnchecked.length + ' elements before filtering: [ ' + $.map(toBeUnchecked, function(n){
                // return Number(n.value);
            // }).join(', ') + ' ]');
            
            if (
              o.selectExclusive.length
              &&
              !(
                o.selectExclusiveAll.length
                &&
                ($.inArray(Number(thisEl.attr('value')), o.selectExclusiveAll) != -1)
              )
            ){
              toBeUnchecked = thisEl.parents('.multiSelectOptions').find(['input:checked[value!=', thisEl.attr('value'), ']'].join('')).filter(function(i){
                // fblog('filtering ' + Number($(this).attr('value')) + ' from [ ' + o.selectExclusive.join(', ') + ' ] == ' + $.inArray(Number($(this).attr('value')), o.selectExclusive));
                return $.inArray(Number($(this).attr('value')), o.selectExclusive) != -1;
              });
            }
            
            // fblog(toBeUnchecked.length + ' elements after filtering: [ ' + $.map(toBeUnchecked, function(n){
                // return Number(n.value);
            // }).join(', ') + ' ]');
            
            toBeUnchecked.attr('checked', false);
          }
          
          // if new item is not an exclusive, at least uncheck any currently checked exclusives
          else {
            // uncheck any other checked exclusive inputs
            var checked = thisEl.parents('.multiSelectOptions').find('input:checked');
            var toBeUnchecked = [];
            checked.each(function(){
              if ($.inArray(Number($(this).attr('value')), o.selectExclusiveAll) != -1){
                toBeUnchecked.push(this);
              }
            });
            toBeUnchecked = $(toBeUnchecked);
            toBeUnchecked.attr('checked', false);
          }
        
					thisGPEl.multiSelectUpdateSelected(o);
					thisGPEl.find('LABEL').removeClass('checked').find('INPUT:checked').parent().addClass('checked');
          
          // focus input for further keyboard control
					//thisGPEl.prev('.multiSelect').focus();
          
					if( !thisEl.attr('checked') ){
            thisGPEl.find('INPUT:checkbox.selectAll').attr('checked', false).parent().removeClass('checked');
          }
          
					if( callback ) callback(thisEl);
				});
				
				// Initial display
				select.nextAll('.multiSelect').eq(0).next('.multiSelectOptions').each( function() {
          var thisEl = $(this);
					thisEl.multiSelectUpdateSelected(o);
					thisEl.find('INPUT:checked').parent().addClass('checked');
				});
				
				// Handle hovers
				select.nextAll('.multiSelect').eq(0).next('.multiSelectOptions').find('LABEL').mouseover( function() {
          var thisEl = $(this);
					thisEl.parent().find('LABEL').removeClass('hover');
					thisEl.addClass('hover');
				}).mouseout( function() {
					$(this).parent().find('LABEL').removeClass('hover');
				});

        // if (!o.readOnly){

          // Keyboard
          select.nextAll('.multiSelect').eq(0).keydown( function(e) {
            var thisEl = $(this);
            var thisOptionsEl = thisEl.next('.multiSelectOptions');
            var thisOptionsLabels = thisOptionsEl.find('LABEL');
            
            // Is dropdown visible?
            if( thisEl.next('.multiSelectOptions').is(':visible') ) {
              // Dropdown is visible
              // Tab
              if( e.keyCode == 9 ) {
                thisEl.addClass('focus').trigger('click'); // esc, left, right - hide
                thisEl.focus().next(':input').focus();
                return true;
              }
              
              // ESC, Left, Right
              if( e.keyCode == 27 || e.keyCode == 37 || e.keyCode == 39 ) {
                // Hide dropdown
                thisEl.addClass('focus').trigger('click');
              }
              // Down
              if( e.keyCode == 40 ) {
                if( !thisOptionsLabels.hasClass('hover') ) {
                  // Default to first item
                  thisOptionsEl.find('LABEL:first').addClass('hover');
                } else {
                  // Move down, cycle to top if on bottom
                  thisOptionsEl.find('LABEL.hover').removeClass('hover').next('LABEL').addClass('hover');
                  if( !thisOptionsLabels.hasClass('hover') ) {
                    thisOptionsEl.find('LABEL:first').addClass('hover');
                  }
                }
                return false;
              }
              // Up
              if( e.keyCode == 38 ) {
                if( !thisOptionsLabels.hasClass('hover') ) {
                  // Default to first item
                  thisOptionsEl.find('LABEL:first').addClass('hover');
                } else {
                  // Move up, cycle to bottom if on top
                  thisOptionsEl.find('LABEL.hover').removeClass('hover').prev('LABEL').addClass('hover');
                  if( !thisOptionsLabels.hasClass('hover') ) {
                    thisOptionsEl.find('LABEL:last').addClass('hover');
                  }
                }
                return false;
              }
              // Enter, Space
              if( e.keyCode == 13 || e.keyCode == 32 ) {
                // Select All
                if( thisOptionsEl.find('LABEL.hover INPUT:checkbox').hasClass('selectAll') ) {
                  if( thisOptionsEl.find('LABEL.hover INPUT:checkbox').attr('checked') ) {
                    // Uncheck all
                    thisOptionsEl.find('INPUT:checkbox').attr('checked', false).parent().removeClass('checked');
                  } else {
                    // Check all
                    thisOptionsEl.find('INPUT:checkbox').attr('checked', true).parent().addClass('checked');
                  }
                  thisOptionsEl.multiSelectUpdateSelected(o);
                  if( callback ) callback(thisEl);
                  return false;
                }

                if( thisOptionsEl.find('LABEL.hover INPUT:checkbox').attr('checked') ) {
                  // Uncheck
                  thisOptionsEl.find('LABEL.hover INPUT:checkbox').attr('checked', false);
                  thisOptionsEl.multiSelectUpdateSelected(o);
                  thisOptionsEl.find('LABEL').removeClass('checked').find('INPUT:checked').parent().addClass('checked');
                  // Select all status can't be checked at this point
                  thisOptionsEl.find('INPUT:checkbox.selectAll').attr('checked', false).parent().removeClass('checked');
                  if( callback ) callback(thisEl);
                } else {
                  // Other checkboxes

                  if (o.selectExclusive){
                    // uncheck any other checked inputs
                    thisOptionsEl.find('input:checked').attr('checked', false);
                  }
          
                  // Check
                  thisOptionsEl.find('LABEL.hover INPUT:checkbox').attr('checked', true);

                  thisOptionsEl.multiSelectUpdateSelected(o);
                  thisOptionsEl.find('LABEL').removeClass('checked').find('INPUT:checked').parent().addClass('checked');
                  if( callback ) callback(thisEl);
                }
              }
              return false;
            } else {
              // Dropdown is not visible
              if( e.keyCode == 38 || e.keyCode == 40 || e.keyCode == 13 || e.keyCode == 32 ) { // down, enter, space - show
                // Show dropdown
                thisEl.removeClass('focus').trigger('click');
                thisOptionsEl.find('LABEL:first').addClass('hover');
                return false;
              }
              //  Tab key
              if( e.keyCode == 9 ) {
                // Shift focus to next INPUT element on page
                thisEl.focus().next(':input').focus();
                return true;
              }
            }
            // Prevent enter key from submitting form
            if( e.keyCode == 13 ) return false;
          });
          
        // }
        
				//$(select).nextAll('.multiSelect').eq(0).after('<span>test</span>');
        
        // apply bgiframe if available
				// if ( $.fn.bgiframe ) {
          // select.nextAll('.multiSelect').eq(0).next('.multiSelectOptions').bgiframe();
        // }
				
				// Eliminate the original form element
				select.remove();
			});
      
      return o;
			
		},
		
		// Hide the dropdown
		multiSelectOptionsHide: function() {
			$(this).removeClass('active').next('.multiSelectOptions').hide()
      // remove z-index from this and parent container to fix IE6/7 z-index bug
      .css({position: 'static', 'z-index': ''}).parent().css('z-index', '');
      
      // remove focus from input box when hiding dropdown
      //this.blur();
		},
		
		// Show the dropdown
		multiSelectOptionsShow: function() {
      var thisEl = $(this);
      var multiSelectEl = thisEl.next('.multiSelectOptions');
    
			// Hide any open option boxes
			$('.multiSelect').multiSelectOptionsHide();
			multiSelectEl.find('LABEL').removeClass('hover');
			thisEl.addClass('active');
      
      multiSelectEl.show();
			
			// Position it
			// var offset = thisEl.offset();
      var position = thisEl.position();
			multiSelectEl.css({
        top: [position.top + thisEl.outerHeight(), 'px'].join(''),
        left: [position.left, 'px'].join(''),
        position: 'absolute',
        'z-index': 9999
      })
      // increase z-index of parent container to fix IE6/7 z-index bug
      .parent().css('z-index', 9999);
			
			// Disappear on hover out
			multiSelectCurrent = thisEl;
			var timer = '';
			multiSelectEl.hover( function() {
				clearTimeout(timer);
			}, function() {
				timer = setTimeout('$(multiSelectCurrent).multiSelectOptionsHide(); $(multiSelectCurrent).unbind("hover");', 0);
			});
			
		},
    
    // update which options are available (shown)
    multiSelectUpdateOptions: function(o, shown) {
      // step through child elements, showing or hiding as applicable
      $(this).children().each(function(){
        var label = $(this);
        if (shown && shown.length && $.inArray(Number(label.attr('value')), shown) == -1){
          label.hide();
        }
        else {
          label.show();
        }
      });
    },
		
		// Update the textbox with the total number of selected items
		multiSelectUpdateSelected: function(o) {
			var i = 0, s = '';
			$(this).find('INPUT:checkbox:checked').not('.selectAll').each( function() {
				i++;
			})
			if( i == 0 ) {
				$(this).prev('INPUT.multiSelect').val(o.noneSelected).addClass(o.noneSelectedClass);
			} else {
				if( o.oneOrMoreSelected == '*' ) {
					var display = '';
					$(this).find('INPUT:checkbox:checked').each( function() {
            var text = $(this).parent().children('span.name').text();
						if( text != o.selectAllText ) display = [display, text, o.separator].join(''); //', ';
					});
					display = display.substr(0, display.length - 2);
					$(this).prev('INPUT.multiSelect').val( display );
				} else {
					$(this).prev('INPUT.multiSelect').val( o.oneOrMoreSelected.replace('%', i) );
				}
        $(this).prev('INPUT.multiSelect').removeClass(o.noneSelectedClass);
			}
		}
		
	});
	
})(jQuery);
/*-------------------------------------------------------------------- 
 * javascript method: "pxToEm"
 * by:
   Scott Jehl (scott@filamentgroup.com) 
   Maggie Wachs (maggie@filamentgroup.com)
   http://www.filamentgroup.com
 *
 * Copyright (c) 2008 Filament Group
 * Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
 *
 * Description: Extends the native Number and String objects with pxToEm method. pxToEm converts a pixel value to ems depending on inherited font size.  
 * Article: http://www.filamentgroup.com/lab/retaining_scalable_interfaces_with_pixel_to_em_conversion/
 * Demo: http://www.filamentgroup.com/examples/pxToEm/	 	
 *							
 * Options:  	 								
 		scope: string or jQuery selector for font-size scoping
 		reverse: Boolean, true reverses the conversion to em-px
 * Dependencies: jQuery library						  
 * Usage Example: myPixelValue.pxToEm(); or myPixelValue.pxToEm({'scope':'#navigation', reverse: true});
 *
 * Version: 2.0, 08.01.2008 
 * Changelog:
 *		08.02.2007 initial Version 1.0
 *		08.01.2008 - fixed font-size calculation for IE
--------------------------------------------------------------------*/

Number.prototype.pxToEm = String.prototype.pxToEm = function(settings){
	//set defaults
	settings = jQuery.extend({
		scope: 'body',
		reverse: false
	}, settings);
	
	var pxVal = (this == '') ? 0 : parseFloat(this);
	var scopeVal;
	var getWindowWidth = function(){
		var de = document.documentElement;
		return self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
	};	
	
	/* When a percentage-based font-size is set on the body, IE returns that percent of the window width as the font-size. 
		For example, if the body font-size is 62.5% and the window width is 1000px, IE will return 625px as the font-size. 	
		When this happens, we calculate the correct body font-size (%) and multiply it by 16 (the standard browser font size) 
		to get an accurate em value. */
				
	if (settings.scope == 'body' && $.browser.msie && (parseFloat($('body').css('font-size')) / getWindowWidth()).toFixed(1) > 0.0) {
		var calcFontSize = function(){		
			return (parseFloat($('body').css('font-size'))/getWindowWidth()).toFixed(3) * 16;
		};
		scopeVal = calcFontSize();
	}
	else { scopeVal = parseFloat(jQuery(settings.scope).css("font-size")); };
			
	var result = (settings.reverse == true) ? (pxVal * scopeVal).toFixed(2) /* + 'px' */ : (pxVal / scopeVal).toFixed(2) /* + 'em' */;
	return result;
};
/**
 * jQuery.ScrollTo
 * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
 * Dual licensed under MIT and GPL.
 * Date: 5/25/2009
 *
 * @projectDescription Easy element scrolling using jQuery.
 * http://flesler.blogspot.com/2007/10/jqueryscrollto.html
 * Works with jQuery +1.2.6. Tested on FF 2/3, IE 6/7/8, Opera 9.5/6, Safari 3, Chrome 1 on WinXP.
 *
 * @author Ariel Flesler
 * @version 1.4.2
 *
 * @id jQuery.scrollTo
 * @id jQuery.fn.scrollTo
 * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements.
 *	  The different options for target are:
 *		- A number position (will be applied to all axes).
 *		- A string position ('44', '100px', '+=90', etc ) will be applied to all axes
 *		- A jQuery/DOM element ( logically, child of the element to scroll )
 *		- A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc )
 *		- A hash { top:x, left:y }, x and y can be any kind of number/string like above.
*		- A percentage of the container's dimension/s, for example: 50% to go to the middle.
 *		- The string 'max' for go-to-end. 
 * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead.
 * @param {Object,Function} settings Optional set of settings or the onAfter callback.
 *	 @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'.
 *	 @option {Number} duration The OVERALL length of the animation.
 *	 @option {String} easing The easing method for the animation.
 *	 @option {Boolean} margin If true, the margin of the target element will be deducted from the final position.
 *	 @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }.
 *	 @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes.
 *	 @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends.
 *	 @option {Function} onAfter Function to be called after the scrolling ends. 
 *	 @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends.
 * @return {jQuery} Returns the same jQuery object, for chaining.
 *
 * @desc Scroll to a fixed position
 * @example $('div').scrollTo( 340 );
 *
 * @desc Scroll relatively to the actual position
 * @example $('div').scrollTo( '+=340px', { axis:'y' } );
 *
 * @dec Scroll using a selector (relative to the scrolled element)
 * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } );
 *
 * @ Scroll to a DOM element (same for jQuery object)
 * @example var second_child = document.getElementById('container').firstChild.nextSibling;
 *			$('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){
 *				alert('scrolled!!');																   
 *			}});
 *
 * @desc Scroll on both axes, to different values
 * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } );
 */
;(function( $ ){
	
	var $scrollTo = $.scrollTo = function( target, duration, settings ){
		$(window).scrollTo( target, duration, settings );
	};

	$scrollTo.defaults = {
		axis:'xy',
		duration: parseFloat($.fn.jquery) >= 1.3 ? 0 : 1
	};

	// Returns the element that needs to be animated to scroll the window.
	// Kept for backwards compatibility (specially for localScroll & serialScroll)
	$scrollTo.window = function( scope ){
		return $(window)._scrollable();
	};

	// Hack, hack, hack :)
	// Returns the real elements to scroll (supports window/iframes, documents and regular nodes)
	$.fn._scrollable = function(){
		return this.map(function(){
			var elem = this,
				isWin = !elem.nodeName || $.inArray( elem.nodeName.toLowerCase(), ['iframe','#document','html','body'] ) != -1;

				if( !isWin )
					return elem;

			var doc = (elem.contentWindow || elem).document || elem.ownerDocument || elem;
			
			return $.browser.safari || doc.compatMode == 'BackCompat' ?
				doc.body : 
				doc.documentElement;
		});
	};

	$.fn.scrollTo = function( target, duration, settings ){
		if( typeof duration == 'object' ){
			settings = duration;
			duration = 0;
		}
		if( typeof settings == 'function' )
			settings = { onAfter:settings };
			
		if( target == 'max' )
			target = 9e9;
			
		settings = $.extend( {}, $scrollTo.defaults, settings );
		// Speed is still recognized for backwards compatibility
		duration = duration || settings.speed || settings.duration;
		// Make sure the settings are given right
		settings.queue = settings.queue && settings.axis.length > 1;
		
		if( settings.queue )
			// Let's keep the overall duration
			duration /= 2;
		settings.offset = both( settings.offset );
		settings.over = both( settings.over );

		return this._scrollable().each(function(){
			var elem = this,
				$elem = $(elem),
				targ = target, toff, attr = {},
				win = $elem.is('html,body');

			switch( typeof targ ){
				// A number will pass the regex
				case 'number':
				case 'string':
					if( /^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ) ){
						targ = both( targ );
						// We are done
						break;
					}
					// Relative selector, no break!
					targ = $(targ,this);
				case 'object':
					// DOMElement / jQuery
					if( targ.is || targ.style )
						// Get the real position of the target 
						toff = (targ = $(targ)).offset();
			}
			$.each( settings.axis.split(''), function( i, axis ){
				var Pos	= axis == 'x' ? 'Left' : 'Top',
					pos = Pos.toLowerCase(),
					key = 'scroll' + Pos,
					old = elem[key],
					max = $scrollTo.max(elem, axis);

				if( toff ){// jQuery / DOMElement
					attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] );

					// If it's a dom element, reduce the margin
					if( settings.margin ){
						attr[key] -= parseInt(targ.css('margin'+Pos)) || 0;
						attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0;
					}
					
					attr[key] += settings.offset[pos] || 0;
					
					if( settings.over[pos] )
						// Scroll to a fraction of its width/height
						attr[key] += targ[axis=='x'?'width':'height']() * settings.over[pos];
				}else{ 
					var val = targ[pos];
					// Handle percentage values
					attr[key] = val.slice && val.slice(-1) == '%' ? 
						parseFloat(val) / 100 * max
						: val;
				}

				// Number or 'number'
				if( /^\d+$/.test(attr[key]) )
					// Check the limits
					attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max );

				// Queueing axes
				if( !i && settings.queue ){
					// Don't waste time animating, if there's no need.
					if( old != attr[key] )
						// Intermediate animation
						animate( settings.onAfterFirst );
					// Don't animate this axis again in the next iteration.
					delete attr[key];
				}
			});

			animate( settings.onAfter );			

			function animate( callback ){
				$elem.animate( attr, duration, settings.easing, callback && function(){
					callback.call(this, target, settings);
				});
			};

		}).end();
	};
	
	// Max scrolling position, works on quirks mode
	// It only fails (not too badly) on IE, quirks mode.
	$scrollTo.max = function( elem, axis ){
		var Dim = axis == 'x' ? 'Width' : 'Height',
			scroll = 'scroll'+Dim;
		
		if( !$(elem).is('html,body') )
			return elem[scroll] - $(elem)[Dim.toLowerCase()]();
		
		var size = 'client' + Dim,
			html = elem.ownerDocument.documentElement,
			body = elem.ownerDocument.body;

		return Math.max( html[scroll], body[scroll] ) 
			 - Math.min( html[size]  , body[size]   );
			
	};

	function both( val ){
		return typeof val == 'object' ? val : { top:val, left:val };
	};

})( jQuery );
jQuery.fn.supersleight = function(settings) {
	settings = jQuery.extend({
		imgs: true,
		backgrounds: true,
    listImages: true,
		shim: 'x.gif',
		apply_positioning: true
	}, settings);
	
	return this.each(function(){
		if (jQuery.browser.msie && parseInt(jQuery.browser.version, 10) < 7 && parseInt(jQuery.browser.version, 10) > 4) {
			jQuery(this).find('*').andSelf().each(function(i,obj) {
				var self = jQuery(obj);
        
				// background pngs
				if (settings.backgrounds && self.css('background-image').match(/\.png/i) !== null) {
					var bg = self.css('background-image');
					var src = bg.substring(5,bg.length-2);
					var mode = (self.css('background-repeat') == 'no-repeat' ? 'crop' : 'scale');
					var styles = {
						'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizingMethod='" + mode + "')",
						'background-image': 'url('+settings.shim+')'
					};
					self.css(styles);
				};
        
				// image elements
				if (settings.imgs && self.is('img[src$=png]')){
					var styles = {
						'width': self.width() + 'px',
						'height': self.height() + 'px',
						'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + self.attr('src') + "', sizingMethod='scale')"
					};
					self.css(styles).attr('src', settings.shim);
				};
				// apply position to 'active' elements
				if (settings.apply_positioning && self.is('a, input') && (self.css('position') === '' || self.css('position') == 'static')){
					self.css('position', 'relative');
				};
        
        // // list image bullet pngs
				// if (settings.listImages && self.is('ul') && self.css('list-style-image').match(/\.png/i) !== null) {
					// var img = self.css('list-style-image');
					// var src = img.substring(5,img.length-2);
					// var mode = 'crop';
					// var styles = {
						// 'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizingMethod='" + mode + "')",
						// 'list-style-image': 'url('+settings.shim+')'
					// };
					// self.css(styles);
				// };
        
			});
		};
	});
};
/*
 * 
 * TableSorter 2.0 - Client-side table sorting with ease!
 * Version 2.0.3
 * MOD to support:
 *   - child rows that sort & zebra stripe with parent rows
 *   - option for per-column default sort order
 *   - always start sorts with default sort order (fixes sort "memory" effect)
 *   - option to defer widgets (which must then be manually triggered)
 *       can improve performance when combined with pager (widget will only be applied once to visible page rather than whole table)
 *   - trigger for manually fixing column widths
 *   - speed improvements in IE (converted string concatenations to array joins)
 * @requires jQuery v1.2.3
 * 
 * Copyright (c) 2007 Christian Bach
 * Examples and docs at: http://tablesorter.com
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 * 
 */
/**
 *
 * @description Create a sortable table with multi-column sorting capabilitys
 * 
 * @example $('table').tablesorter();
 * @desc Create a simple tablesorter interface.
 *
 * @example $('table').tablesorter({ sortList:[[0,0],[1,0]] });
 * @desc Create a tablesorter interface and sort on the first and secound column in ascending order.
 * 
 * @example $('table').tablesorter({ headers: { 0: { sorter: false}, 1: {sorter: false} } });
 * @desc Create a tablesorter interface and disable the first and second column headers.
 * 
 * @example $('table').tablesorter({ headers: { 1: { order: 1}});
 * @desc Create a tablesorter interface and set the second column's default sort order to descending (that is, the order used when first clicked - overrides sortInitialOrder).
 * 
 * @example $('table').tablesorter({ 0: {sorter:"integer"}, 1: {sorter:"currency"} });
 * @desc Create a tablesorter interface and set a column parser for the first and secound column.
 * 
 * 
 * @param Object settings An object literal containing key/value pairs to provide optional settings.
 * 
 * @option String cssHeader (optional)       A string of the class name to be appended to sortable tr elements in the thead of the table. 
 *                         Default value: "header"
 * 
 * @option String cssAsc (optional)       A string of the class name to be appended to sortable tr elements in the thead on a ascending sort. 
 *                         Default value: "headerSortUp"
 * 
 * @option String cssDesc (optional)       A string of the class name to be appended to sortable tr elements in the thead on a descending sort. 
 *                         Default value: "headerSortDown"
 * 
 * @option String sortInitialOrder (optional)   A string of the inital sorting order can be asc or desc. 
 *                         Default value: "asc"
 * 
 * @option String sortMultisortKey (optional)   A string of the multi-column sort key. 
 *                         Default value: "shiftKey"
 * 
 * @option String textExtraction (optional)   A string of the text-extraction method to use. 
 *                         For complex html structures inside td cell set this option to "complex", 
 *                         on large tables the complex option can be slow. 
 *                         Default value: "simple"
 * 
 * @option Object headers (optional)       An array containing the forces sorting rules. 
 *                         This option let's you specify a default sorting rule. 
 *                         Default value: null
 * 
 * @option Array sortList (optional)       An array containing the forces sorting rules. 
 *                         This option let's you specify a default sorting rule. 
 *                         Default value: null
 * 
 * @option Array sortForce (optional)       An array containing forced sorting rules. 
 *                         This option let's you specify a default sorting rule, which is prepended to user-selected rules.
 *                         Default value: null
 *  
  * @option Array sortAppend (optional)       An array containing forced sorting rules. 
 *                         This option let's you specify a default sorting rule, which is appended to user-selected rules.
 *                         Default value: null
 * 
 * @option Boolean widthFixed (optional)     Boolean flag indicating if tablesorter should apply fixed widths to the table columns.
 *                         This is usefull when using the pager companion plugin.
 *                         This options requires the dimension jquery plugin.
 *                         Default value: false
 *
 * @option Boolean cancelSelection (optional)   Boolean flag indicating if tablesorter should cancel selection of the table headers text.
 *                         Default value: true
 *
 * @option Boolean debug (optional)       Boolean flag indicating if tablesorter should display debuging information usefull for development.
 *
 * @type jQuery
 *
 * @name tablesorter
 * 
 * @cat Plugins/Tablesorter
 * 
 * @author Christian Bach/christian.bach@polyester.se
 */

(function($) {
  $.extend({
    tablesorter: new function() {
      
      var parsers = [], widgets = [];
      
      this.defaults = {
        cssHeader: "header",
        cssAsc: "headerSortUp",
        cssDesc: "headerSortDown",
        cssChildRow: "expand-child",
        sortInitialOrder: "asc",
        sortMultiSortKey: "shiftKey",
        sortForce: null,
        sortAppend: null,
        textExtraction: "simple",
        parsers: {}, 
        widgets: [],    
        deferWidgets: false,
        widgetZebra: {css: ["even","odd"]},
        widgetRepeatHeaders: 5,
        headers: {},
        widthFixed: false,
        cancelSelection: true,
        sortList: [],
        headerList: [],
        dateFormat: "us",
        decimal: '.',
        onRenderHeader: null,
        debug: false
      };
      
      /* debuging utils */
      function benchmark(s,d) {
        log(s + "," + (new Date().getTime() - d.getTime()) + "ms");
      }
      
      this.benchmark = benchmark;
      
      function log(s) {
        if (typeof console != "undefined" && typeof console.debug != "undefined") {
          console.log(s);
        } else {
          alert(s);
        }
      }
            
      /* parsers utils */
      function buildParserCache(table,$headers) {
        
        if(table.config.debug) { var parsersDebug = ""; }
        
        var rows = table.tBodies[0].rows;
        
        if(table.tBodies[0].rows[0]) {

          var list = [], cells = rows[0].cells, l = cells.length;
          
          for (var i=0;i < l; i++) {
            var p = false;
            
            if($.metadata && ($($headers[i]).metadata() && $($headers[i]).metadata().sorter)  ) {
            
              p = getParserById($($headers[i]).metadata().sorter);  
            
            } else if((table.config.headers[i] && table.config.headers[i].sorter)) {
  
              p = getParserById(table.config.headers[i].sorter);
            }
            if(!p) {
              p = detectParserForColumn(table,cells[i]);
            }
  
            if(table.config.debug) { parsersDebug += "column:" + i + " parser:" +p.id + "\n"; }
  
            list.push(p);
          }
        }
        
        if(table.config.debug) { log(parsersDebug); }

        return list;
      };
      
      function detectParserForColumn(table,node) {
        var l = parsers.length;
        for(var i=1; i < l; i++) {
          if(parsers[i].is($.trim(getElementText(table.config,node)),table,node)) {
            return parsers[i];
          }
        }
        // 0 is always the generic parser (text)
        return parsers[0];
      }
      
      function getParserById(name) {
        var l = parsers.length;
        for(var i=0; i < l; i++) {
          if(parsers[i].id.toLowerCase() == name.toLowerCase()) {  
            return parsers[i];
          }
        }
        return false;
      }
      
      /* utils */
      function buildCache(table) {
        
        if(table.config.debug) { var cacheTime = new Date(); }
        
        var totalRows = (table.tBodies[0] && table.tBodies[0].rows.length) || 0,
          totalCells = (table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length) || 0,
          parsers = table.config.parsers, 
          cache = {row: [], normalized: []};
        
          for (var i=0;i < totalRows; ++i) {
            
            /** Add the table data to main data array */
            var c = $(table.tBodies[0].rows[i]), cols = [];
          
            // if this is a child row, add it to the last row's children and continue to the next row
            if( c.hasClass(table.config.cssChildRow) ){
              cache.row[cache.row.length-1] = cache.row[cache.row.length-1].add(c);
              // go to the next for loop
              continue;
            }
          
            cache.row.push(c);
            
            for(var j=0; j < totalCells; ++j) {
              cols.push(parsers[j].format(getElementText(table.config,c[0].cells[j]),table,c[0].cells[j]));  
            }
                        
            cols.push(cache.normalized.length); // add position for rowCache
            cache.normalized.push(cols);
            cols = null;
          };
        
        if(table.config.debug) { benchmark("Building cache for " + totalRows + " rows:", cacheTime); }

        return cache;
      };
      
      function getElementText(config,node) {
        
        if(!node) return "";
                
        var t = "";
        
        if(config.textExtraction == "simple") {
          if(node.childNodes[0] && node.childNodes[0].hasChildNodes()) {
            t = node.childNodes[0].innerHTML;
          } else {
            t = node.innerHTML;
          }
        } else {
          if(typeof(config.textExtraction) == "function") {
            t = config.textExtraction(node);
          } else { 
            t = $(node).text();
          }  
        }
        return t;
      }
      
      function appendToTable(table,cache) {
        
        if(table.config.debug) {var appendTime = new Date()}
        
        var c = cache, 
          r = c.row, 
          n= c.normalized, 
          totalRows = n.length, 
          checkCell = (n[0].length-1), 
          tableBody = $(table.tBodies[0]),
          rows = [];
      
        for (var i=0;i < totalRows; i++) {
          var pos = n[i][checkCell];
          rows.push(r[pos]);  
          if(!table.config.appender) {
            
            var o = r[pos];
            var l = o.length;
            for(var j=0; j < l; j++) {
              tableBody[0].appendChild(o[j]);
            }

            //tableBody.append(r[n[i][checkCell]]);
          }
        }  
        
        if(table.config.appender) {
        
          table.config.appender(table,rows);  
        }
        
        rows = null;
        
        if(table.config.debug) { benchmark("Rebuilt table:", appendTime); }
          
        if (!table.config.deferWidgets){
          //apply table widgets
          applyWidget(table);
        }
        
        // trigger sortend
        setTimeout(function() {
          $(table).trigger("sortEnd");  
        },0);
        
      };
      
      function buildHeaders(table) {
        
        if(table.config.debug) { var time = new Date(); }
        
        var meta = ($.metadata) ? true : false, tableHeadersRows = [];
      
        for(var i = 0; i < table.tHead.rows.length; i++) { tableHeadersRows[i]=0; };
        
        var $tableHeaders = $("thead th",table);
    
        $tableHeaders.each(function(index) {
          this.count = 0;
          this.column = index;
          this.order = formatSortingOrder(table.config.sortInitialOrder);
          
          if(checkHeaderMetadata(this) || checkHeaderOptions(table,index)) this.sortDisabled = true;
          
          if(!this.sortDisabled) {
            var $th = $(this).addClass(table.config.cssHeader);
            if( table.config.onRenderHeader ) table.config.onRenderHeader.apply($th);
          }
          
          // add cell to headerList
          table.config.headerList[index]= this;
        });
        
        if(table.config.debug) { benchmark("Built headers:", time); log($tableHeaders); }
        
        return $tableHeaders;
        
      };
            
      function checkCellColSpan(table, rows, row) {
        var arr = [], r = table.tHead.rows, c = r[row].cells;
        
        for(var i=0; i < c.length; i++) {
          var cell = c[i];
          
          if ( cell.colSpan > 1) { 
            arr = arr.concat(checkCellColSpan(table, headerArr,row++));
          } else  {
            if(table.tHead.length == 1 || (cell.rowSpan > 1 || !r[row+1])) {
              arr.push(cell);
            }
            //headerArr[row] = (i+row);
          }
        }
        return arr;
      };
      
      function checkHeaderMetadata(cell) {
        if(($.metadata) && ($(cell).metadata().sorter === false)) { return true; };
        return false;
      }
      
      function checkHeaderOptions(table,i) {  
        if((table.config.headers[i]) && (table.config.headers[i].sorter === false)) { return true; };
        return false;
      }
      
      function applyWidget(table) {
        var c = table.config.widgets;
        var l = c.length;
        for(var i=0; i < l; i++) {
          
          getWidgetById(c[i]).format(table);
        }
        
      }
      
      function getWidgetById(name) {
        var l = widgets.length;
        for(var i=0; i < l; i++) {
          if(widgets[i].id.toLowerCase() == name.toLowerCase() ) {
            return widgets[i]; 
          }
        }
      };
      
      function formatSortingOrder(v) {
        
        if(typeof(v) != "Number") {
          i = (v.toLowerCase() == "desc") ? 1 : 0;
        } else {
          i = (v == (0 || 1)) ? v : 0;
        }
        return i;
      }
      
      function isValueInArray(v, a) {
        var l = a.length;
        for(var i=0; i < l; i++) {
          if(a[i][0] == v) {
            return true;  
          }
        }
        return false;
      }
        
      function setHeadersCss(table,$headers, list, css) {
        // remove all header information
        $headers.removeClass(css[0]).removeClass(css[1]);
        
        var h = [];
        $headers.each(function(offset) {
            if(!this.sortDisabled) {
              h[this.column] = $(this);
            }
        });
        
        var l = list.length; 
        for(var i=0; i < l; i++) {
          h[list[i][0]].addClass(css[list[i][1]]);
        }
      }
      
      
      
      // MOD START
      // call with force parameter set to TRUE to fix column widths just on this call
      // useful when fixing column widths after first creating the tablesorter
      function fixColumnWidth(table,$headers,force) {
        var c = table.config;
        if(c.widthFixed || force) {
          var colgroup = $('<colgroup>');
          $("tr:first td",table.tBodies[0]).each(function() {
            colgroup.append($('<col>').css('width',$(this).width()));
          });
          $(table).prepend(colgroup);
        };
      }
      // MOD END
      
      
      
      function updateHeaderSortCount(table,sortList) {
        var c = table.config, l = sortList.length;
        for(var i=0; i < l; i++) {
          var s = sortList[i], o = c.headerList[s[0]];
          o.count = s[1];
          o.count++;
        }
      }
      
      /* sorting methods */
      function multisort(table,sortList,cache) {
        
        if(table.config.debug) { var sortTime = new Date(); }
        
        var dynamicExp = "var sortWrapper = function(a,b) {", l = sortList.length;
          
        for(var i=0; i < l; i++) {
          
          var c = sortList[i][0];
          var order = sortList[i][1];
          var s = (getCachedSortType(table.config.parsers,c) == "text") ? ((order == 0) ? "sortText" : "sortTextDesc") : ((order == 0) ? "sortNumeric" : "sortNumericDesc");
          
          var e = ["e", i].join('');
          
          dynamicExp = [dynamicExp,
            "var ", e, " = ", s, "(a[", c, "],b[", c, "]); ",
            "if(", e, ") { return ", e, "; } ",
            "else { "].join('');
        }
        
        // if value is the same keep orignal order  
        var orgOrderCol = cache.normalized[0].length - 1;
        dynamicExp = [dynamicExp, "return a[", orgOrderCol, "]-b[", orgOrderCol, "];"].join('');
            
        for(var i=0; i < l; i++) {
          dynamicExp = [dynamicExp, "}; "].join('');
        }
        
        dynamicExp = [dynamicExp, "return 0; ", "}; "].join('');  
        
        eval(dynamicExp);
        
        cache.normalized.sort(sortWrapper);
        
        if(table.config.debug) { benchmark("Sorting on " + sortList.toString() + " and dir " + order+ " time:", sortTime); }
        
        return cache;
      };
      
      function sortText(a,b) {
        return ((a < b) ? -1 : ((a > b) ? 1 : 0));
      };
      
      function sortTextDesc(a,b) {
        return ((b < a) ? -1 : ((b > a) ? 1 : 0));
      };  
      
       function sortNumeric(a,b) {
        return a-b;
      };
      
      function sortNumericDesc(a,b) {
        return b-a;
      };
      
      function getCachedSortType(parsers,i) {
        return parsers[i].type;
      };
      
      /* public methods */
      this.construct = function(settings) {

        return this.each(function() {
          
          if(!this.tHead || !this.tBodies) return;
          
          var $this, $document,$headers, cache, config, shiftDown = 0, sortOrder;
          
          this.config = {};
          
          config = $.extend(this.config, $.tablesorter.defaults, settings);
          
          // store common expression for speed          
          $this = $(this);
          
          // save the settings where they read
          $.data(this, "tablesorter", config);
          
          // build headers
          $headers = buildHeaders(this);
          
          // try to auto detect column type, and store in tables config
          this.config.parsers = buildParserCache(this,$headers);
          
          
          // build the cache for the tbody cells
          cache = buildCache(this);
          
          // get the css class names, could be done else where.
          var sortCSS = [config.cssDesc,config.cssAsc];
          
          // fixate columns if the users supplies the fixedWidth option
          fixColumnWidth(this);
          
          // apply event handling to headers
          // this is to big, perhaps break it out?
          $headers.click(function(e) {
            
            $this.trigger("sortStart");
            
            var totalRows = ($this[0].tBodies[0] && $this[0].tBodies[0].rows.length) || 0;
            
            if(!this.sortDisabled && totalRows > 0) {
              
              
              // store exp, for speed
              var $cell = $(this);
  
              // get current column index
              var i = this.column;

              // get current column sort order
              //this.order = this.count++ % 2;
              
              
              
              // MOD START
              // first see if this column has a default sort order defined in config.headers
              if (config.headers[i] && $.inArray(config.headers[i].order, [0,1]) != -1){
                this.order = config.headers[i].order;
              }
              // if not, use global default sort order
              else {
                this.order = formatSortingOrder(config.sortInitialOrder);
              }
              
              // then, step through sortList to see if this column is already sorted
              // and if so, reverse current sort order
              for (var k = 0; k < config.sortList.length; k++){
                if (config.sortList[k][0] == i){
                  this.order = Number(!config.sortList[k][1]);
                }
              }
              // MOD END
              
              
              
              // user only wants to sort on one column
              if(!e[config.sortMultiSortKey]) {
                // flush the sort list
                config.sortList = [];
                
                if(config.sortForce != null) {
                  var a = config.sortForce; 
                  for(var j=0; j < a.length; j++) {
                    if(a[j][0] != i) {
                      config.sortList.push(a[j]);
                    }
                  }
                }
                
                // add column to sort list
                config.sortList.push([i,this.order]);
              
              // multi column sorting
              } else {
                // the user has clicked on an all ready sortet column.
                if(isValueInArray(i,config.sortList)) {   
                  
                  // revers the sorting direction for all tables.
                  for(var j=0; j < config.sortList.length; j++) {
                    var s = config.sortList[j], o = config.headerList[s[0]];
                    if(s[0] == i) {
                      o.count = s[1];
                      o.count++;
                      s[1] = o.count % 2;
                    }
                  }  
                } else {
                  // add column to sort list array
                  config.sortList.push([i,this.order]);
                }
              };
              setTimeout(function() {
                //set css for headers
                setHeadersCss($this[0],$headers,config.sortList,sortCSS);
                appendToTable($this[0],multisort($this[0],config.sortList,cache));
              },1);
              // stop normal event by returning false
              return false;
            }
          // cancel selection  
          }).mousedown(function() {
            if(config.cancelSelection) {
              this.onselectstart = function() {return false};
              return false;
            }
          });
          
          // apply easy methods that trigger binded events
          $this.bind("update",function() {
            
            // rebuild parsers.
            this.config.parsers = buildParserCache(this,$headers);
            
            // rebuild the cache map
            cache = buildCache(this);
            
          })
          .bind("sorton",function(e,list) {
            
            $(this).trigger("sortStart");
            
            config.sortList = list;
            
            // update and store the sortlist
            var sortList = config.sortList;
            
            // update header count index
            updateHeaderSortCount(this,sortList);
            
            //set css for headers
            setHeadersCss(this,$headers,sortList,sortCSS);
            
            
            // sort the table and append it to the dom
            appendToTable(this,multisort(this,sortList,cache));
            
            return false;

          })
          .bind("appendCache",function() {
            
            appendToTable(this,cache);
          
          })
          .bind("applyWidgetId",function(e,id) {
            
            getWidgetById(id).format(this);
            
          })
          .bind("applyWidgets",function() {
            // apply widgets
            applyWidget(this);
          })
          
          
          
          // MOD START
          // create trigger to allow fixing column widths on demand
          .bind("fixColumnWidths", function(){
            fixColumnWidth(this, null, true);
          });
          // MOD END
          
          
          
          if($.metadata && ($(this).metadata() && $(this).metadata().sortlist)) {
            config.sortList = $(this).metadata().sortlist;
          }
          // if user has supplied a sort list to constructor.
          if(config.sortList.length > 0) {
            $this.trigger("sorton",[config.sortList]);  
          }
          
          
          
          // MOD START
          // if deferWidgets is TRUE, prevent calling widgets during initial rendering
          // prevents unecessary render time for large tables that will be paged
          // in this case, must manually trigger "applyWidgets" after tablesorter() call 
          
          if (!config.deferWidgets){
            // apply widgets
            //applyWidget(this);
          }
          // MOD END
          
          
          
        });
      };
      
      this.addParser = function(parser) {
        var l = parsers.length, a = true;
        for(var i=0; i < l; i++) {
          if(parsers[i].id.toLowerCase() == parser.id.toLowerCase()) {
            a = false;
          }
        }
        if(a) { parsers.push(parser); };
      };
      
      this.addWidget = function(widget) {
        widgets.push(widget);
      };
      
      this.formatFloat = function(s) {
        var i = parseFloat(s);
        return (isNaN(i)) ? 0 : i;
      };
      this.formatInt = function(s) {
        var i = parseInt(s);
        return (isNaN(i)) ? 0 : i;
      };
      
      this.isDigit = function(s,config) {
        var DECIMAL = ['\\', config.decimal].join('');
        var exp = ['/(^[+]?0(', DECIMAL, '0+)?$)|(^([-+]?[1-9][0-9]*)$)|(^([-+]?((0?|[1-9][0-9]*)', DECIMAL, '(0*[1-9][0-9]*)))$)|(^[-+]?[1-9]+[0-9]*', DECIMAL, '0+$)/'].join('');
        return RegExp(exp).test($.trim(s));
      };
      
      this.clearTableBody = function(table) {
        if($.browser.msie) {
          function empty() {
            while ( this.firstChild ) this.removeChild( this.firstChild );
          }
          empty.apply(table.tBodies[0]);
        } else {
          table.tBodies[0].innerHTML = "";
        }
      };
    }
  });
  
  // extend plugin scope
  $.fn.extend({
        tablesorter: $.tablesorter.construct
  });
  
  var ts = $.tablesorter;
  
  // add default parsers
  ts.addParser({
    id: "text",
    is: function(s) {
      return true;
    },
    format: function(s) {
      return $.trim(s.toLowerCase());
    },
    type: "text"
  });
  
  ts.addParser({
    id: "digit",
    is: function(s,table) {
      var c = table.config;
      return $.tablesorter.isDigit(s,c);
    },
    format: function(s) {
      return $.tablesorter.formatFloat(s);
    },
    type: "numeric"
  });
  
  ts.addParser({
    id: "currency",
    is: function(s) {
      return /^[ÂŁ$â‚¬?.]/.test(s);
    },
    format: function(s) {
      return $.tablesorter.formatFloat(s.replace(new RegExp(/[^0-9.]/g),""));
    },
    type: "numeric"
  });
  
  ts.addParser({
    id: "ipAddress",
    is: function(s) {
      return /^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);
    },
    format: function(s) {
      var a = s.split("."), r = "", l = a.length;
      for(var i = 0; i < l; i++) {
        var item = a[i];
        if(item.length == 2) {
          r = [r, "0", item].join('');
        } else {
          r = [r, item].join('');
        }
      }
      return $.tablesorter.formatFloat(r);
    },
    type: "numeric"
  });
  
  ts.addParser({
    id: "url",
    is: function(s) {
      return /^(https?|ftp|file):\/\/$/.test(s);
    },
    format: function(s) {
      return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));
    },
    type: "text"
  });
  
  ts.addParser({
    id: "isoDate",
    is: function(s) {
      return /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);
    },
    format: function(s) {
      return $.tablesorter.formatFloat((s != "") ? new Date(s.replace(new RegExp(/-/g),"/")).getTime() : "0");
    },
    type: "numeric"
  });
    
  ts.addParser({
    id: "percent",
    is: function(s) { 
      return /\%$/.test($.trim(s));
    },
    format: function(s) {
      return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));
    },
    type: "numeric"
  });

  ts.addParser({
    id: "usLongDate",
    is: function(s) {
      return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));
    },
    format: function(s) {
      return $.tablesorter.formatFloat(new Date(s).getTime());
    },
    type: "numeric"
  });

  ts.addParser({
    id: "shortDate",
    is: function(s) {
      return /\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);
    },
    format: function(s,table) {
      var c = table.config;
      s = s.replace(/\-/g,"/");
      if(c.dateFormat == "us") {
        // reformat the string in ISO format
        s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$1/$2");
      } else if(c.dateFormat == "uk") {
        //reformat the string in ISO format
        s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$2/$1");
      } else if(c.dateFormat == "dd/mm/yy" || c.dateFormat == "dd-mm-yy") {
        s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/, "$1/$2/$3");  
      }
      return $.tablesorter.formatFloat(new Date(s).getTime());
    },
    type: "numeric"
  });

  ts.addParser({
      id: "time",
      is: function(s) {
          return /^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);
      },
      format: function(s) {
          return $.tablesorter.formatFloat(new Date(["2000/01/01 ", s].join('')).getTime());
      },
    type: "numeric"
  });
  
  
  ts.addParser({
      id: "metadata",
      is: function(s) {
          return false;
      },
      format: function(s,table,cell) {
      var c = table.config, p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName;
          return $(cell).metadata()[p];
      },
    type: "numeric"
  });
  
  // add default widgets
  ts.addWidget({
    id: "zebra",
    format: function(table) {
      if(table.config.debug) { var time = new Date(); }
      var $tr, row = -1, odd;
      // loop through the visible rows
      var rows = $(table.tBodies[0]).children("tr"); // MOD only include direct children of this table body (ignore subtables)
      rows.each(function (i){
        $tr = $(this);
        if (
          $tr.css("display") !== "none"
          &&
          $tr.css("visibility") !== "hidden"
        ){ 
          // style children rows the same way the parent row was styled
          if( !$tr.hasClass(table.config.cssChildRow) ) row++;
          odd = (row%2 == 0);
          $tr.removeClass(table.config.widgetZebra.css[odd?0:1]).addClass(table.config.widgetZebra.css[odd?1:0])
        }
      });
      if(table.config.debug) { $.tablesorter.benchmark("Applying Zebra widget", time); }
    }
  });  
  
  // add new widget called repeatHeaders
  ts.addWidget({
    // give the widget a id
    id: 'repeatHeaders',
    // format is called when the on init and when a sorting has finished
    format: function(table) {
      // cache and collect all TH headers
      if(!this.headers) {
        var h = this.headers = []; 
        $('thead th',table).each(function() {
          h.push(
            '<th class="' + $(this)[0].className + '">' + $(this).html() + '</th>'
          );
          
        });
      }
      
      // remove appended headers by classname.
      var removed = $('tr.repeated-header', table).remove();
      var removedCount = 0;
      
      // loop all tr elements and insert a copy of the "headers"	
      var total = $('tbody tr:not(.expand-child)', table).length;
      for (var i = total; i > 0; i--) {
        // insert a copy of the table header every Nth row
        if( i % table.config.widgetRepeatHeaders == 0) {
          if (removed.length){
            $('tbody tr:not(.expand-child)', table).eq(i).before(
              removed[removed.length - 1]
            );
            removed.splice(removed.length - 1, 1);
          }
          else {
            $('<tr class="repeated-header">' + this.headers.join('') + '</tr>').insertBefore(
              $('tbody tr:not(.expand-child)', table).eq(i)
            );
            
            
            
          }
        }
      }
    }
  });  
})(jQuery);
(function($) {
	$.extend({
		tablesorterPager: new function() {
			
			function updatePageDisplay(c) {
      
      
      
        // MOD START
        // update current/total page display
        // supports INPUT or non-INPUT elements as target
        // applies cssDisabled class to backward or forward links when at beginning or end (respectively)
        var el = $(c.cssPageDisplay,c.container);
        var disabledClass = c.cssDisabled.replace('.', '');
        var linksForward = $([c.cssNext, ', ', c.cssLast].join(''),c.container);
        var linksBackward = $([c.cssPrev, ', ', c.cssFirst].join(''),c.container);
        var s = [(c.page+1), c.seperator, c.totalPages].join('');
        
        if (el.is('input')){
          el.val(s);	
        }
        else {
          el.html(['<strong>', s, '</strong>'].join(''));
        }
        
        if (c.page+1 == c.totalPages){
          linksForward.addClass(disabledClass);
        }
        else {
          linksForward.removeClass(disabledClass);
        }
        
        if (c.page+1 == 1){
          linksBackward.addClass(disabledClass);
        }
        else {
          linksBackward.removeClass(disabledClass);
        }
        // MOD END
        
        
        
			}
			
			function setPageSize(table,size) {
				var c = table.config;
				c.size = size;
				c.totalPages = Math.ceil(c.totalRows / c.size);
				c.pagerPositionSet = false;
				moveToPage(table);
				fixPosition(table);
			}
			
			function fixPosition(table) {
				var c = table.config;
				if(!c.pagerPositionSet && c.positionFixed) {
					var c = table.config, o = $(table);
					if(o.offset) {
						c.container.css({
							top: [o.offset().top, o.height(), 'px'].join(''),
							position: 'absolute'
						});
					}
					c.pagerPositionSet = true;
				}
			}
			
			function moveToFirstPage(table) {
				var c = table.config;
				c.page = 0;
				moveToPage(table);
			}
			
			function moveToLastPage(table) {
				var c = table.config;
				c.page = (c.totalPages-1);
				moveToPage(table);
			}
			
			function moveToNextPage(table) {
				var c = table.config;
				c.page++;
				if(c.page >= (c.totalPages-1)) {
					c.page = (c.totalPages-1);
				}
				moveToPage(table);
			}
			
			function moveToPrevPage(table) {
				var c = table.config;
				c.page--;
				if(c.page <= 0) {
					c.page = 0;
				}
				moveToPage(table);
			}
						
			
			function moveToPage(table) {
				var c = table.config;
				if(c.page < 0 || c.page > (c.totalPages-1)) {
					c.page = 0;
				}
				
				renderTable(table,c.rowsCopy);
			}
			
			function renderTable(table,rows) {
				
				var c = table.config;
				var l = rows.length;
				var s = (c.page * c.size);
				var e = (s + c.size);
				if(e > rows.length ) {
					e = rows.length;
				}
				
				
				var tableBody = $(table.tBodies[0]);
				
				// clear the table body
				
				$.tablesorter.clearTableBody(table);
				
				for(var i = s; i < e; i++) {
					
					//tableBody.append(rows[i]);
					
					var o = rows[i];
					var l = o.length;
					for(var j=0; j < l; j++) {
						
						tableBody[0].appendChild(o[j]);

					}
				}
				
				fixPosition(table,tableBody);
				
				$(table).trigger("applyWidgets");
				
				if( c.page >= c.totalPages ) {
        			moveToLastPage(table);
				}
				
				updatePageDisplay(c);
			}
			
			this.appender = function(table,rows) {
				
				var c = table.config;
				
				c.rowsCopy = rows;
				c.totalRows = rows.length;
				c.totalPages = Math.ceil(c.totalRows / c.size);
				
				renderTable(table,rows);
			};
			
			this.defaults = {
				size: 10,
				offset: 0,
				page: 0,
				totalRows: 0,
				totalPages: 0,
				container: null,
				cssNext: '.next',
				cssPrev: '.prev',
				cssFirst: '.first',
				cssLast: '.last',
        cssDisabled: '.disabled',
				cssPageDisplay: '.pagedisplay',
				cssPageSize: '.pagesize',
				seperator: "/",
				positionFixed: true,
				appender: this.appender
			};
			
			this.construct = function(settings) {
				
				return this.each(function() {	
					
					config = $.extend(this.config, $.tablesorterPager.defaults, settings);
					
					var table = this, pager = config.container;
				
					$(this).trigger("appendCache");
					
					config.size = parseInt($(".pagesize",pager).val());
					
					$(config.cssFirst,pager).click(function() {
						moveToFirstPage(table);
						return false;
					});
					$(config.cssNext,pager).click(function() {
						moveToNextPage(table);
						return false;
					});
					$(config.cssPrev,pager).click(function() {
						moveToPrevPage(table);
						return false;
					});
					$(config.cssLast,pager).click(function() {
						moveToLastPage(table);
						return false;
					});
          
          
          
          // MOD START
          // prevent selection of links
          $([config.cssFirst, ', ', config.cssNext, ', ', config.cssPrev, ', ', config.cssLast].join(''), pager).mousedown(function() {
            this.onselectstart = function() {return false};
            return false;
          });
          // MOD END
          

          
					$(config.cssPageSize,pager).change(function() {
						setPageSize(table,parseInt($(this).val()));
						return false;
					});
				});
			};
			
		}
	});
	// extend plugin scope
	$.fn.extend({
        tablesorterPager: $.tablesorterPager.construct
	});
	
})(jQuery);				
ď»ż/**
 * jQuery.timers - Timer abstractions for jQuery
 * Written by Blair Mitchelmore (blair DOT mitchelmore AT gmail DOT com)
 * Licensed under the WTFPL (http://sam.zoy.org/wtfpl/).
 * Date: 2009/08/13
 *
 * @author Blair Mitchelmore
 * @version 1.1.3
 *
 **/

jQuery.fn.extend({
	everyTime: function(interval, label, fn, times, belay) {
		return this.each(function() {
			jQuery.timer.add(this, interval, label, fn, times, belay);
		});
	},
	oneTime: function(interval, label, fn) {
		return this.each(function() {
			jQuery.timer.add(this, interval, label, fn, 1);
		});
	},
	stopTime: function(label, fn) {
		return this.each(function() {
			jQuery.timer.remove(this, label, fn);
		});
	}
});

jQuery.extend({
	timer: {
		global: [],
		guid: 1,
		dataKey: "jQuery.timer",
		regex: /^([0-9]+(?:\.[0-9]*)?)\s*(.*s)?$/,
		powers: {
			// Yeah this is major overkill...
			'ms': 1,
			'cs': 10,
			'ds': 100,
			's': 1000,
			'das': 10000,
			'hs': 100000,
			'ks': 1000000
		},
		timeParse: function(value) {
			if (value == undefined || value == null)
				return null;
			var result = this.regex.exec(jQuery.trim(value.toString()));
			if (result[2]) {
				var num = parseFloat(result[1]);
				var mult = this.powers[result[2]] || 1;
				return num * mult;
			} else {
				return value;
			}
		},
		add: function(element, interval, label, fn, times, belay) {
			var counter = 0;
			
			if (jQuery.isFunction(label)) {
				if (!times) 
					times = fn;
				fn = label;
				label = interval;
			}
			
			interval = jQuery.timer.timeParse(interval);

			if (typeof interval != 'number' || isNaN(interval) || interval <= 0)
				return;

			if (times && times.constructor != Number) {
				belay = !!times;
				times = 0;
			}
			
			times = times || 0;
			belay = belay || false;
			
			var timers = jQuery.data(element, this.dataKey) || jQuery.data(element, this.dataKey, {});
			
			if (!timers[label])
				timers[label] = {};
			
			fn.timerID = fn.timerID || this.guid++;
			
			var handler = function() {
				if (belay && this.inProgress) 
					return;
				this.inProgress = true;
				if ((++counter > times && times !== 0) || fn.call(element, counter) === false)
					jQuery.timer.remove(element, label, fn);
				this.inProgress = false;
			};
			
			handler.timerID = fn.timerID;
			
			if (!timers[label][fn.timerID])
				timers[label][fn.timerID] = window.setInterval(handler,interval);
			
			this.global.push( element );
			
		},
		remove: function(element, label, fn) {
			var timers = jQuery.data(element, this.dataKey), ret;
			
			if ( timers ) {
				
				if (!label) {
					for ( label in timers )
						this.remove(element, label, fn);
				} else if ( timers[label] ) {
					if ( fn ) {
						if ( fn.timerID ) {
							window.clearInterval(timers[label][fn.timerID]);
							delete timers[label][fn.timerID];
						}
					} else {
						for ( var fn in timers[label] ) {
							window.clearInterval(timers[label][fn]);
							delete timers[label][fn];
						}
					}
					
					for ( ret in timers[label] ) break;
					if ( !ret ) {
						ret = null;
						delete timers[label];
					}
				}
				
				for ( ret in timers ) break;
				if ( !ret ) 
					jQuery.removeData(element, this.dataKey);
			}
		}
	}
});

jQuery(window).bind("unload", function() {
	jQuery.each(jQuery.timer.global, function(index, item) {
		jQuery.timer.remove(item);
	});
});
/*
 * jQuery Tooltip plugin 1.3
 *
 * http://bassistance.de/jquery-plugins/jquery-plugin-tooltip/
 * http://docs.jquery.com/Plugins/Tooltip
 *
 * Copyright (c) 2006 - 2008 JĂ¶rn Zaefferer
 *
 * $Id: jquery.tooltip.js 5741 2008-06-21 15:22:16Z joern.zaefferer $
 * 
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */
 
;(function($) {
	
		// the tooltip element
	var helper = {},
		// the current tooltipped element
		current,
		// the title of the current element, used for restoring
		title,
		// timeout id for delayed tooltips
		tID,
		// IE 5.5 or 6
		IE = $.browser.msie && /MSIE\s(5\.5|6\.)/.test(navigator.userAgent),
		// flag for mouse tracking
		track = false;
	
	$.tooltip = {
		blocked: false,
		defaults: {
			delay: 200,
			fade: false,
			showURL: true,
			extraClass: "",
			top: 15,
			left: 15,
			id: "tooltip"
		},
		block: function() {
			$.tooltip.blocked = !$.tooltip.blocked;
		}
	};
	
	$.fn.extend({
		tooltip: function(settings) {
			settings = $.extend({}, $.tooltip.defaults, settings);
			createHelper(settings);
			return this.each(function() {
					$.data(this, "tooltip", settings);
					//this.tOpacity = helper.parent.css("opacity");
					// copy tooltip into its own expando and remove the title
					this.tooltipText = this.title;
					$(this).removeAttr("title");
					// also remove alt attribute to prevent default tooltip in IE
					this.alt = "";
				})
				.mouseover(save)
				.mouseout(hide)
				.click(hide);
		},
		fixPNG: IE ? function() {
			return this.each(function () {
				var image = $(this).css('backgroundImage');
				if (image.match(/^url\(["']?(.*\.png)["']?\)$/i)) {
					image = RegExp.$1;
					$(this).css({
						'backgroundImage': 'none',
						'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')"
					}).each(function () {
						var position = $(this).css('position');
						if (position != 'absolute' && position != 'relative')
							$(this).css('position', 'relative');
					});
				}
			});
		} : function() { return this; },
		unfixPNG: IE ? function() {
			return this.each(function () {
				$(this).css({'filter': '', backgroundImage: ''});
			});
		} : function() { return this; },
		hideWhenEmpty: function() {
			return this.each(function() {
				$(this)[ $(this).html() ? "show" : "hide" ]();
			});
		},
		url: function() {
			return this.attr('href') || this.attr('src');
		}
	});
	
	function createHelper(settings) {
		// there can be only one tooltip helper
		if( helper.parent )
			return;
		// create the helper, h3 for title, div for url
		helper.parent = $('<div id="' + settings.id + '"><h3></h3><div class="body"></div><div class="url"></div></div>')
			// add to document
			.appendTo(document.body)
			// hide it at first
			.hide();
			
		// apply bgiframe if available
		if ( $.fn.bgiframe )
			helper.parent.bgiframe();
		
		// save references to title and url elements
		helper.title = $('h3', helper.parent);
		helper.body = $('div.body', helper.parent);
		helper.url = $('div.url', helper.parent);
	}
	
	function settings(element) {
		return $.data(element, "tooltip");
	}
	
	// main event handler to start showing tooltips
	function handle(event) {
		// show helper, either with timeout or on instant
		if( settings(this).delay )
			tID = setTimeout(show, settings(this).delay);
		else
			show();
		
		// if selected, update the helper position when the mouse moves
		track = !!settings(this).track;
		$(document.body).bind('mousemove', update);
			
		// update at least once
		update(event);
	}
	
	// save elements title before the tooltip is displayed
	function save() {
		// if this is the current source, or it has no title (occurs with click event), stop
		if ( $.tooltip.blocked || this == current || (!this.tooltipText && !settings(this).bodyHandler) )
			return;

		// save current
		current = this;
		title = this.tooltipText;
		
		if ( settings(this).bodyHandler ) {
			helper.title.hide();
			var bodyContent = settings(this).bodyHandler.call(this);
  		if (bodyContent.nodeType || bodyContent.jquery) {
				helper.body.empty().append(bodyContent)
			} else {
        // helper.body.text(bodyContent);
				helper.body.html('<div>' + bodyContent + '</div>');
			}
			helper.body.show();
		} else if ( settings(this).showBody ) {
			var parts = title.split(settings(this).showBody);
			helper.title.html(parts.shift()).show();
			helper.body.empty();
			for(var i = 0, part; (part = parts[i]); i++) {
				if(i > 0)
					helper.body.append("<br/>");
				helper.body.append(part);
			}
			helper.body.hideWhenEmpty();
		} else {
			helper.title.html(title).show();
			helper.body.hide();
		}
		
		// if element has href or src, add and show it, otherwise hide it
		if( settings(this).showURL && $(this).url() )
			helper.url.html( $(this).url().replace('http://', '') ).show();
		else 
			helper.url.hide();
		
		// add an optional class for this tip
		helper.parent.addClass(settings(this).extraClass);

		// fix PNG background for IE
		if (settings(this).fixPNG )
			helper.parent.fixPNG();
			
		handle.apply(this, arguments);
	}
	
	// delete timeout and show helper
	function show() {
		tID = null;
		if ((!IE || !$.fn.bgiframe) && settings(current).fade) {
			if (helper.parent.is(":animated"))
				helper.parent.stop().show(); //.fadeTo(settings(current).fade, current.tOpacity);
			//else
				//helper.parent.is(':visible') ? helper.parent.fadeTo(settings(current).fade, current.tOpacity) : helper.parent.fadeIn(settings(current).fade);
		} else {
			helper.parent.show();
		}
		update();
	}
	
	/**
	 * callback for mousemove
	 * updates the helper position
	 * removes itself when no current element
	 */
	function update(event)	{
		if($.tooltip.blocked)
			return;
		
		if (event && event.target.tagName == "OPTION") {
			return;
		}
		
		// stop updating when tracking is disabled and the tooltip is visible
		if ( !track && helper.parent.is(":visible")) {
			$(document.body).unbind('mousemove', update)
		}
		
		// if no current element is available, remove this listener
		if( current == null ) {
			$(document.body).unbind('mousemove', update);
			return;	
		}
		
		// remove position helper classes
		helper.parent.removeClass("viewport-right").removeClass("viewport-bottom");
		
		var left = helper.parent[0].offsetLeft;
		var top = helper.parent[0].offsetTop;
		if (event) {
			// position the helper 15 pixel to bottom right, starting from mouse position
			left = event.pageX + settings(current).left;
			top = event.pageY + settings(current).top;
			var right='auto';
			if (settings(current).positionLeft) {
				right = $(window).width() - left;
				left = 'auto';
			}
			helper.parent.css({
				left: left,
				right: right,
				top: top
			});
		}
		
		var v = viewport(),
			h = helper.parent[0];
		// check horizontal position
		if (v.x + v.cx < h.offsetLeft + h.offsetWidth) {
			left -= h.offsetWidth + 20 + settings(current).left;
			helper.parent.css({left: left + 'px'}).addClass("viewport-right");
		}
		// check vertical position
		if (v.y + v.cy < h.offsetTop + h.offsetHeight) {
			top -= h.offsetHeight + 20 + settings(current).top;
			helper.parent.css({top: top + 'px'}).addClass("viewport-bottom");
		}
	}
	
	function viewport() {
		return {
			x: $(window).scrollLeft(),
			y: $(window).scrollTop(),
			cx: $(window).width(),
			cy: $(window).height()
		};
	}
	
	// hide helper and restore added classes and the title
	function hide(event) {
		if($.tooltip.blocked)
			return;
		// clear timeout if possible
		if(tID)
			clearTimeout(tID);
		// no more current element
		current = null;
		
		var tsettings = settings(this);
		function complete() {
			helper.parent.removeClass( tsettings.extraClass ).hide(); //.css("opacity", "");
		}
		if ((!IE || !$.fn.bgiframe) && tsettings.fade) {
			if (helper.parent.is(':animated'))
				helper.parent.stop().fadeTo(tsettings.fade, 0, complete);
			else
				helper.parent.stop().fadeOut(tsettings.fade, complete);
		} else
			complete();
		
		if( settings(this).fixPNG )
			helper.parent.unfixPNG();
	}
	
})(jQuery);

Date.CultureInfo = {
	/* Culture Name */
    name: "en-US",
    englishName: "English (United States)",
    nativeName: "English (United States)",
    
    /* Day Name Strings */
    dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
    abbreviatedDayNames: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
    shortestDayNames: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
    firstLetterDayNames: ["S", "M", "T", "W", "T", "F", "S"],
    
    /* Month Name Strings */
    monthNames: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
    abbreviatedMonthNames: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],

	/* AM/PM Designators */
    amDesignator: "am",
    pmDesignator: "pm",

    firstDayOfWeek: 0,
    twoDigitYearMax: 2029,
    
    /**
     * The dateElementOrder is based on the order of the 
     * format specifiers in the formatPatterns.DatePattern. 
     *
     * Example:
     <pre>
     shortDatePattern    dateElementOrder
     ------------------  ---------------- 
     "M/d/yyyy"          "mdy"
     "dd/MM/yyyy"        "dmy"
     "yyyy-MM-dd"        "ymd"
     </pre>
     *
     * The correct dateElementOrder is required by the parser to
     * determine the expected order of the date elements in the
     * string being parsed.
     */
    dateElementOrder: "mdy",
    
    /* Standard date and time format patterns */
    formatPatterns: {
        shortDate: "M/d/yyyy",
        longDate: "dddd, MMMM dd, yyyy",
        shortTime: "h:mm tt",
        longTime: "h:mm:ss tt",
        fullDateTime: "dddd, MMMM dd, yyyy h:mm:ss tt",
        sortableDateTime: "yyyy-MM-ddTHH:mm:ss",
        universalSortableDateTime: "yyyy-MM-dd HH:mm:ssZ",
        rfc1123: "ddd, dd MMM yyyy HH:mm:ss GMT",
        monthDay: "MMMM dd",
        yearMonth: "MMMM, yyyy"
    },

    /**
     * NOTE: If a string format is not parsing correctly, but
     * you would expect it parse, the problem likely lies below. 
     * 
     * The following regex patterns control most of the string matching
     * within the parser.
     * 
     * The Month name and Day name patterns were automatically generated
     * and in general should be (mostly) correct. 
     *
     * Beyond the month and day name patterns are natural language strings.
     * Example: "next", "today", "months"
     *
     * These natural language string may NOT be correct for this culture. 
     * If they are not correct, please translate and edit this file
     * providing the correct regular expression pattern. 
     *
     * If you modify this file, please post your revised CultureInfo file
     * to the Datejs Forum located at http://www.datejs.com/forums/.
     *
     * Please mark the subject of the post with [CultureInfo]. Example:
     *    Subject: [CultureInfo] Translated "da-DK" Danish(Denmark)
     * 
     * We will add the modified patterns to the master source files.
     *
     * As well, please review the list of "Future Strings" section below. 
     */	
    regexPatterns: {
        jan: /^jan(uary)?/i,
        feb: /^feb(ruary)?/i,
        mar: /^mar(ch)?/i,
        apr: /^apr(il)?/i,
        may: /^may/i,
        jun: /^jun(e)?/i,
        jul: /^jul(y)?/i,
        aug: /^aug(ust)?/i,
        sep: /^sep(t(ember)?)?/i,
        oct: /^oct(ober)?/i,
        nov: /^nov(ember)?/i,
        dec: /^dec(ember)?/i,

        sun: /^su(n(day)?)?/i,
        mon: /^mo(n(day)?)?/i,
        tue: /^tu(e(s(day)?)?)?/i,
        wed: /^we(d(nesday)?)?/i,
        thu: /^th(u(r(s(day)?)?)?)?/i,
        fri: /^fr(i(day)?)?/i,
        sat: /^sa(t(urday)?)?/i,

        future: /^next/i,
        past: /^last|past|prev(ious)?/i,
        add: /^(\+|aft(er)?|from|hence)/i,
        subtract: /^(\-|bef(ore)?|ago)/i,
        
        yesterday: /^yes(terday)?/i,
        today: /^t(od(ay)?)?/i,
        tomorrow: /^tom(orrow)?/i,
        now: /^n(ow)?/i,
        
        millisecond: /^ms|milli(second)?s?/i,
        second: /^sec(ond)?s?/i,
        minute: /^mn|min(ute)?s?/i,
		hour: /^h(our)?s?/i,
		week: /^w(eek)?s?/i,
        month: /^m(onth)?s?/i,
        day: /^d(ay)?s?/i,
        year: /^y(ear)?s?/i,
		
        shortMeridian: /^(a|p)/i,
        longMeridian: /^(a\.?m?\.?|p\.?m?\.?)/i,
        timezone: /^((e(s|d)t|c(s|d)t|m(s|d)t|p(s|d)t)|((gmt)?\s*(\+|\-)\s*\d\d\d\d?)|gmt|utc)/i,
        ordinalSuffix: /^\s*(st|nd|rd|th)/i,
        timeContext: /^\s*(\:|a(?!u|p)|p)/i
    },

	timezones: [{name:"UTC", offset:"-000"}, {name:"GMT", offset:"-000"}, {name:"EST", offset:"-0500"}, {name:"EDT", offset:"-0400"}, {name:"CST", offset:"-0600"}, {name:"CDT", offset:"-0500"}, {name:"MST", offset:"-0700"}, {name:"MDT", offset:"-0600"}, {name:"PST", offset:"-0800"}, {name:"PDT", offset:"-0700"}]
};

/********************
 ** Future Strings **
 ********************
 * 
 * The following list of strings may not be currently being used, but 
 * may be incorporated into the Datejs library later. 
 *
 * We would appreciate any help translating the strings below.
 * 
 * If you modify this file, please post your revised CultureInfo file
 * to the Datejs Forum located at http://www.datejs.com/forums/.
 *
 * Please mark the subject of the post with [CultureInfo]. Example:
 *    Subject: [CultureInfo] Translated "da-DK" Danish(Denmark)b
 *
 * English Name        Translated
 * ------------------  -----------------
 * about               about
 * ago                 ago
 * date                date
 * time                time
 * calendar            calendar
 * show                show
 * hourly              hourly
 * daily               daily
 * weekly              weekly
 * bi-weekly           bi-weekly
 * fortnight           fortnight
 * monthly             monthly
 * bi-monthly          bi-monthly
 * quarter             quarter
 * quarterly           quarterly
 * yearly              yearly
 * annual              annual
 * annually            annually
 * annum               annum
 * again               again
 * between             between
 * after               after
 * from now            from now
 * repeat              repeat
 * times               times
 * per                 per
 * min (abbrev minute) min
 * morning             morning
 * noon                noon
 * night               night
 * midnight            midnight
 * mid-night           mid-night
 * evening             evening
 * final               final
 * future              future
 * spring              spring
 * summer              summer
 * fall                fall
 * winter              winter
 * end of              end of
 * end                 end
 * long                long
 * short               short
 */
/**
 * @version: 1.0 Alpha-1
 * @author: Coolite Inc. http://www.coolite.com/
 * @date: 2008-04-13
 * @copyright: Copyright (c) 2006-2008, Coolite Inc. (http://www.coolite.com/). All rights reserved.
 * @license: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/. 
 * @website: http://www.datejs.com/
 */
 
(function () {
    var $D = Date, 
        $P = $D.prototype, 
        $C = $D.CultureInfo,
        p = function (s, l) {
            if (!l) {
                l = 2;
            }
            return ("000" + s).slice(l * -1);
        };
            
    /**
     * Resets the time of this Date object to 12:00 AM (00:00), which is the start of the day.
     * @param {Boolean}  .clone() this date instance before clearing Time
     * @return {Date}    this
     */
    $P.clearTime = function () {
        this.setHours(0);
        this.setMinutes(0);
        this.setSeconds(0);
        this.setMilliseconds(0);
        return this;
    };

    /**
     * Resets the time of this Date object to the current time ('now').
     * @return {Date}    this
     */
    $P.setTimeToNow = function () {
        var n = new Date();
        this.setHours(n.getHours());
        this.setMinutes(n.getMinutes());
        this.setSeconds(n.getSeconds());
        this.setMilliseconds(n.getMilliseconds());
        return this;
    };

    /** 
     * Gets a date that is set to the current date. The time is set to the start of the day (00:00 or 12:00 AM).
     * @return {Date}    The current date.
     */
    $D.today = function () {
        return new Date().clearTime();
    };

    /**
     * Compares the first date to the second date and returns an number indication of their relative values.  
     * @param {Date}     First Date object to compare [Required].
     * @param {Date}     Second Date object to compare to [Required].
     * @return {Number}  -1 = date1 is lessthan date2. 0 = values are equal. 1 = date1 is greaterthan date2.
     */
    $D.compare = function (date1, date2) {
        if (isNaN(date1) || isNaN(date2)) { 
            throw new Error(date1 + " - " + date2); 
        } else if (date1 instanceof Date && date2 instanceof Date) {
            return (date1 < date2) ? -1 : (date1 > date2) ? 1 : 0;
        } else { 
            throw new TypeError(date1 + " - " + date2); 
        }
    };
    
    /**
     * Compares the first Date object to the second Date object and returns true if they are equal.  
     * @param {Date}     First Date object to compare [Required]
     * @param {Date}     Second Date object to compare to [Required]
     * @return {Boolean} true if dates are equal. false if they are not equal.
     */
    $D.equals = function (date1, date2) { 
        return (date1.compareTo(date2) === 0); 
    };

    /**
     * Gets the day number (0-6) if given a CultureInfo specific string which is a valid dayName, abbreviatedDayName or shortestDayName (two char).
     * @param {String}   The name of the day (eg. "Monday, "Mon", "tuesday", "tue", "We", "we").
     * @return {Number}  The day number
     */
    $D.getDayNumberFromName = function (name) {
        var n = $C.dayNames, m = $C.abbreviatedDayNames, o = $C.shortestDayNames, s = name.toLowerCase();
        for (var i = 0; i < n.length; i++) { 
            if (n[i].toLowerCase() == s || m[i].toLowerCase() == s || o[i].toLowerCase() == s) { 
                return i; 
            }
        }
        return -1;  
    };
    
    /**
     * Gets the month number (0-11) if given a Culture Info specific string which is a valid monthName or abbreviatedMonthName.
     * @param {String}   The name of the month (eg. "February, "Feb", "october", "oct").
     * @return {Number}  The day number
     */
    $D.getMonthNumberFromName = function (name) {
        var n = $C.monthNames, m = $C.abbreviatedMonthNames, s = name.toLowerCase();
        for (var i = 0; i < n.length; i++) {
            if (n[i].toLowerCase() == s || m[i].toLowerCase() == s) { 
                return i; 
            }
        }
        return -1;
    };

    /**
     * Determines if the current date instance is within a LeapYear.
     * @param {Number}   The year.
     * @return {Boolean} true if date is within a LeapYear, otherwise false.
     */
    $D.isLeapYear = function (year) { 
        return ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0); 
    };

    /**
     * Gets the number of days in the month, given a year and month value. Automatically corrects for LeapYear.
     * @param {Number}   The year.
     * @param {Number}   The month (0-11).
     * @return {Number}  The number of days in the month.
     */
    $D.getDaysInMonth = function (year, month) {
        return [31, ($D.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
    };
 
    $D.getTimezoneAbbreviation = function (offset) {
        var z = $C.timezones, p;
        for (var i = 0; i < z.length; i++) {
            if (z[i].offset === offset) {
                return z[i].name;
            }
        }
        return null;
    };
    
    $D.getTimezoneOffset = function (name) {
        var z = $C.timezones, p;
        for (var i = 0; i < z.length; i++) {
            if (z[i].name === name.toUpperCase()) {
                return z[i].offset;
            }
        }
        return null;
    };

    /**
     * Returns a new Date object that is an exact date and time copy of the original instance.
     * @return {Date}    A new Date instance
     */
    $P.clone = function () {
        return new Date(this.getTime()); 
    };

    /**
     * Compares this instance to a Date object and returns an number indication of their relative values.  
     * @param {Date}     Date object to compare [Required]
     * @return {Number}  -1 = this is lessthan date. 0 = values are equal. 1 = this is greaterthan date.
     */
    $P.compareTo = function (date) {
        return Date.compare(this, date);
    };

    /**
     * Compares this instance to another Date object and returns true if they are equal.  
     * @param {Date}     Date object to compare. If no date to compare, new Date() [now] is used.
     * @return {Boolean} true if dates are equal. false if they are not equal.
     */
    $P.equals = function (date) {
        return Date.equals(this, date || new Date());
    };

    /**
     * Determines if this instance is between a range of two dates or equal to either the start or end dates.
     * @param {Date}     Start of range [Required]
     * @param {Date}     End of range [Required]
     * @return {Boolean} true is this is between or equal to the start and end dates, else false
     */
    $P.between = function (start, end) {
        return this.getTime() >= start.getTime() && this.getTime() <= end.getTime();
    };

    /**
     * Determines if this date occurs after the date to compare to.
     * @param {Date}     Date object to compare. If no date to compare, new Date() ("now") is used.
     * @return {Boolean} true if this date instance is greater than the date to compare to (or "now"), otherwise false.
     */
    $P.isAfter = function (date) {
        return this.compareTo(date || new Date()) === 1;
    };

    /**
     * Determines if this date occurs before the date to compare to.
     * @param {Date}     Date object to compare. If no date to compare, new Date() ("now") is used.
     * @return {Boolean} true if this date instance is less than the date to compare to (or "now").
     */
    $P.isBefore = function (date) {
        return (this.compareTo(date || new Date()) === -1);
    };

    /**
     * Determines if the current Date instance occurs today.
     * @return {Boolean} true if this date instance is 'today', otherwise false.
     */
    
    /**
     * Determines if the current Date instance occurs on the same Date as the supplied 'date'. 
     * If no 'date' to compare to is provided, the current Date instance is compared to 'today'. 
     * @param {date}     Date object to compare. If no date to compare, the current Date ("now") is used.
     * @return {Boolean} true if this Date instance occurs on the same Day as the supplied 'date'.
     */
    $P.isToday = $P.isSameDay = function (date) {
        return this.clone().clearTime().equals((date || new Date()).clone().clearTime());
    };
    
    /**
     * Adds the specified number of milliseconds to this instance. 
     * @param {Number}   The number of milliseconds to add. The number can be positive or negative [Required]
     * @return {Date}    this
     */
    $P.addMilliseconds = function (value) {
        this.setMilliseconds(this.getMilliseconds() + value * 1);
        return this;
    };

    /**
     * Adds the specified number of seconds to this instance. 
     * @param {Number}   The number of seconds to add. The number can be positive or negative [Required]
     * @return {Date}    this
     */
    $P.addSeconds = function (value) { 
        return this.addMilliseconds(value * 1000); 
    };

    /**
     * Adds the specified number of seconds to this instance. 
     * @param {Number}   The number of seconds to add. The number can be positive or negative [Required]
     * @return {Date}    this
     */
    $P.addMinutes = function (value) { 
        return this.addMilliseconds(value * 60000); /* 60*1000 */
    };

    /**
     * Adds the specified number of hours to this instance. 
     * @param {Number}   The number of hours to add. The number can be positive or negative [Required]
     * @return {Date}    this
     */
    $P.addHours = function (value) { 
        return this.addMilliseconds(value * 3600000); /* 60*60*1000 */
    };

    /**
     * Adds the specified number of days to this instance. 
     * @param {Number}   The number of days to add. The number can be positive or negative [Required]
     * @return {Date}    this
     */
    $P.addDays = function (value) {
        this.setDate(this.getDate() + value * 1);
        return this;
    };

    /**
     * Adds the specified number of weeks to this instance. 
     * @param {Number}   The number of weeks to add. The number can be positive or negative [Required]
     * @return {Date}    this
     */
    $P.addWeeks = function (value) { 
        return this.addDays(value * 7);
    };

    /**
     * Adds the specified number of months to this instance. 
     * @param {Number}   The number of months to add. The number can be positive or negative [Required]
     * @return {Date}    this
     */
    $P.addMonths = function (value) {
        var n = this.getDate();
        this.setDate(1);
        this.setMonth(this.getMonth() + value * 1);
        this.setDate(Math.min(n, $D.getDaysInMonth(this.getFullYear(), this.getMonth())));
        return this;
    };

    /**
     * Adds the specified number of years to this instance. 
     * @param {Number}   The number of years to add. The number can be positive or negative [Required]
     * @return {Date}    this
     */
    $P.addYears = function (value) {
        return this.addMonths(value * 12);
    };

    /**
     * Adds (or subtracts) to the value of the years, months, weeks, days, hours, minutes, seconds, milliseconds of the date instance using given configuration object. Positive and Negative values allowed.
     * Example
    <pre><code>
    Date.today().add( { days: 1, months: 1 } )
     
    new Date().add( { years: -1 } )
    </code></pre> 
     * @param {Object}   Configuration object containing attributes (months, days, etc.)
     * @return {Date}    this
     */
    $P.add = function (config) {
        if (typeof config == "number") {
            this._orient = config;
            return this;    
        }
        
        var x = config;
        
        if (x.milliseconds) { 
            this.addMilliseconds(x.milliseconds); 
        }
        if (x.seconds) { 
            this.addSeconds(x.seconds); 
        }
        if (x.minutes) { 
            this.addMinutes(x.minutes); 
        }
        if (x.hours) { 
            this.addHours(x.hours); 
        }
        if (x.weeks) { 
            this.addWeeks(x.weeks); 
        }    
        if (x.months) { 
            this.addMonths(x.months); 
        }
        if (x.years) { 
            this.addYears(x.years); 
        }
        if (x.days) {
            this.addDays(x.days); 
        }
        return this;
    };
    
    var $y, $m, $d;
    
    /**
     * Get the week number. Week one (1) is the week which contains the first Thursday of the year. Monday is considered the first day of the week.
     * This algorithm is a JavaScript port of the work presented by Claus Třndering at http://www.tondering.dk/claus/cal/node8.html#SECTION00880000000000000000
     * .getWeek() Algorithm Copyright (c) 2008 Claus Tondering.
     * The .getWeek() function does NOT convert the date to UTC. The local datetime is used. Please use .getISOWeek() to get the week of the UTC converted date.
     * @return {Number}  1 to 53
     */
    $P.getWeek = function () {
        var a, b, c, d, e, f, g, n, s, w;
        
        $y = (!$y) ? this.getFullYear() : $y;
        $m = (!$m) ? this.getMonth() + 1 : $m;
        $d = (!$d) ? this.getDate() : $d;

        if ($m <= 2) {
            a = $y - 1;
            b = (a / 4 | 0) - (a / 100 | 0) + (a / 400 | 0);
            c = ((a - 1) / 4 | 0) - ((a - 1) / 100 | 0) + ((a - 1) / 400 | 0);
            s = b - c;
            e = 0;
            f = $d - 1 + (31 * ($m - 1));
        } else {
            a = $y;
            b = (a / 4 | 0) - (a / 100 | 0) + (a / 400 | 0);
            c = ((a - 1) / 4 | 0) - ((a - 1) / 100 | 0) + ((a - 1) / 400 | 0);
            s = b - c;
            e = s + 1;
            f = $d + ((153 * ($m - 3) + 2) / 5) + 58 + s;
        }
        
        g = (a + b) % 7;
        d = (f + g - e) % 7;
        n = (f + 3 - d) | 0;

        if (n < 0) {
            w = 53 - ((g - s) / 5 | 0);
        } else if (n > 364 + s) {
            w = 1;
        } else {
            w = (n / 7 | 0) + 1;
        }
        
        $y = $m = $d = null;
        
        return w;
    };
    
    /**
     * Get the ISO 8601 week number. Week one ("01") is the week which contains the first Thursday of the year. Monday is considered the first day of the week.
     * The .getISOWeek() function does convert the date to it's UTC value. Please use .getWeek() to get the week of the local date.
     * @return {String}  "01" to "53"
     */
    $P.getISOWeek = function () {
        $y = this.getUTCFullYear();
        $m = this.getUTCMonth() + 1;
        $d = this.getUTCDate();
        return p(this.getWeek());
    };

    /**
     * Moves the date to Monday of the week set. Week one (1) is the week which contains the first Thursday of the year.
     * @param {Number}   A Number (1 to 53) that represents the week of the year.
     * @return {Date}    this
     */    
    $P.setWeek = function (n) {
        return this.moveToDayOfWeek(1).addWeeks(n - this.getWeek());
    };

    // private
    var validate = function (n, min, max, name) {
        if (typeof n == "undefined") {
            return false;
        } else if (typeof n != "number") {
            throw new TypeError(n + " is not a Number."); 
        } else if (n < min || n > max) {
            throw new RangeError(n + " is not a valid value for " + name + "."); 
        }
        return true;
    };

    /**
     * Validates the number is within an acceptable range for milliseconds [0-999].
     * @param {Number}   The number to check if within range.
     * @return {Boolean} true if within range, otherwise false.
     */
    $D.validateMillisecond = function (value) {
        return validate(value, 0, 999, "millisecond");
    };

    /**
     * Validates the number is within an acceptable range for seconds [0-59].
     * @param {Number}   The number to check if within range.
     * @return {Boolean} true if within range, otherwise false.
     */
    $D.validateSecond = function (value) {
        return validate(value, 0, 59, "second");
    };

    /**
     * Validates the number is within an acceptable range for minutes [0-59].
     * @param {Number}   The number to check if within range.
     * @return {Boolean} true if within range, otherwise false.
     */
    $D.validateMinute = function (value) {
        return validate(value, 0, 59, "minute");
    };

    /**
     * Validates the number is within an acceptable range for hours [0-23].
     * @param {Number}   The number to check if within range.
     * @return {Boolean} true if within range, otherwise false.
     */
    $D.validateHour = function (value) {
        return validate(value, 0, 23, "hour");
    };

    /**
     * Validates the number is within an acceptable range for the days in a month [0-MaxDaysInMonth].
     * @param {Number}   The number to check if within range.
     * @return {Boolean} true if within range, otherwise false.
     */
    $D.validateDay = function (value, year, month) {
        return validate(value, 1, $D.getDaysInMonth(year, month), "day");
    };

    /**
     * Validates the number is within an acceptable range for months [0-11].
     * @param {Number}   The number to check if within range.
     * @return {Boolean} true if within range, otherwise false.
     */
    $D.validateMonth = function (value) {
        return validate(value, 0, 11, "month");
    };

    /**
     * Validates the number is within an acceptable range for years.
     * @param {Number}   The number to check if within range.
     * @return {Boolean} true if within range, otherwise false.
     */
    $D.validateYear = function (value) {
        return validate(value, 0, 9999, "year");
    };

    /**
     * Set the value of year, month, day, hour, minute, second, millisecond of date instance using given configuration object.
     * Example
    <pre><code>
    Date.today().set( { day: 20, month: 1 } )

    new Date().set( { millisecond: 0 } )
    </code></pre>
     * 
     * @param {Object}   Configuration object containing attributes (month, day, etc.)
     * @return {Date}    this
     */
    $P.set = function (config) {
        if ($D.validateMillisecond(config.millisecond)) {
            this.addMilliseconds(config.millisecond - this.getMilliseconds()); 
        }
        
        if ($D.validateSecond(config.second)) {
            this.addSeconds(config.second - this.getSeconds()); 
        }
        
        if ($D.validateMinute(config.minute)) {
            this.addMinutes(config.minute - this.getMinutes()); 
        }
        
        if ($D.validateHour(config.hour)) {
            this.addHours(config.hour - this.getHours()); 
        }
        
        if ($D.validateMonth(config.month)) {
            this.addMonths(config.month - this.getMonth()); 
        }

        if ($D.validateYear(config.year)) {
            this.addYears(config.year - this.getFullYear()); 
        }
        
	    /* day has to go last because you can't validate the day without first knowing the month */
        if ($D.validateDay(config.day, this.getFullYear(), this.getMonth())) {
            this.addDays(config.day - this.getDate()); 
        }
        
        if (config.timezone) { 
            this.setTimezone(config.timezone); 
        }
        
        if (config.timezoneOffset) { 
            this.setTimezoneOffset(config.timezoneOffset); 
        }

        if (config.week && validate(config.week, 0, 53, "week")) {
            this.setWeek(config.week);
        }
        
        return this;   
    };

    /**
     * Moves the date to the first day of the month.
     * @return {Date}    this
     */
    $P.moveToFirstDayOfMonth = function () {
        return this.set({ day: 1 });
    };

    /**
     * Moves the date to the last day of the month.
     * @return {Date}    this
     */
    $P.moveToLastDayOfMonth = function () { 
        return this.set({ day: $D.getDaysInMonth(this.getFullYear(), this.getMonth())});
    };

    /**
     * Moves the date to the next n'th occurrence of the dayOfWeek starting from the beginning of the month. The number (-1) is a magic number and will return the last occurrence of the dayOfWeek in the month.
     * @param {Number}   The dayOfWeek to move to
     * @param {Number}   The n'th occurrence to move to. Use (-1) to return the last occurrence in the month
     * @return {Date}    this
     */
    $P.moveToNthOccurrence = function (dayOfWeek, occurrence) {
        var shift = 0;
        if (occurrence > 0) {
            shift = occurrence - 1;
        }
        else if (occurrence === -1) {
            this.moveToLastDayOfMonth();
            if (this.getDay() !== dayOfWeek) {
                this.moveToDayOfWeek(dayOfWeek, -1);
            }
            return this;
        }
        return this.moveToFirstDayOfMonth().addDays(-1).moveToDayOfWeek(dayOfWeek, +1).addWeeks(shift);
    };

    /**
     * Move to the next or last dayOfWeek based on the orient value.
     * @param {Number}   The dayOfWeek to move to
     * @param {Number}   Forward (+1) or Back (-1). Defaults to +1. [Optional]
     * @return {Date}    this
     */
    $P.moveToDayOfWeek = function (dayOfWeek, orient) {
        var diff = (dayOfWeek - this.getDay() + 7 * (orient || +1)) % 7;
        return this.addDays((diff === 0) ? diff += 7 * (orient || +1) : diff);
    };

    /**
     * Move to the next or last month based on the orient value.
     * @param {Number}   The month to move to. 0 = January, 11 = December
     * @param {Number}   Forward (+1) or Back (-1). Defaults to +1. [Optional]
     * @return {Date}    this
     */
    $P.moveToMonth = function (month, orient) {
        var diff = (month - this.getMonth() + 12 * (orient || +1)) % 12;
        return this.addMonths((diff === 0) ? diff += 12 * (orient || +1) : diff);
    };

    /**
     * Get the Ordinal day (numeric day number) of the year, adjusted for leap year.
     * @return {Number} 1 through 365 (366 in leap years)
     */
    $P.getOrdinalNumber = function () {
        return Math.ceil((this.clone().clearTime() - new Date(this.getFullYear(), 0, 1)) / 86400000) + 1;
    };

    /**
     * Get the time zone abbreviation of the current date.
     * @return {String} The abbreviated time zone name (e.g. "EST")
     */
    $P.getTimezone = function () {
        return $D.getTimezoneAbbreviation(this.getUTCOffset());
    };

    $P.setTimezoneOffset = function (offset) {
        var here = this.getTimezoneOffset(), there = Number(offset) * -6 / 10;
        return this.addMinutes(there - here); 
    };

    $P.setTimezone = function (offset) { 
        return this.setTimezoneOffset($D.getTimezoneOffset(offset)); 
    };

    /**
     * Indicates whether Daylight Saving Time is observed in the current time zone.
     * @return {Boolean} true|false
     */
    $P.hasDaylightSavingTime = function () { 
        return (Date.today().set({month: 0, day: 1}).getTimezoneOffset() !== Date.today().set({month: 6, day: 1}).getTimezoneOffset());
    };
    
    /**
     * Indicates whether this Date instance is within the Daylight Saving Time range for the current time zone.
     * @return {Boolean} true|false
     */
    $P.isDaylightSavingTime = function () {
        return Date.today().set({month: 0, day: 1}).getTimezoneOffset() != this.getTimezoneOffset();
    };

    /**
     * Get the offset from UTC of the current date.
     * @return {String} The 4-character offset string prefixed with + or - (e.g. "-0500")
     */
    $P.getUTCOffset = function () {
        var n = this.getTimezoneOffset() * -10 / 6, r;
        if (n < 0) { 
            r = (n - 10000).toString(); 
            return r.charAt(0) + r.substr(2); 
        } else { 
            r = (n + 10000).toString();  
            return "+" + r.substr(1); 
        }
    };

    /**
     * Returns the number of milliseconds between this date and date.
     * @param {Date} Defaults to now
     * @return {Number} The diff in milliseconds
     */
    $P.getElapsed = function (date) {
        return (date || new Date()) - this;
    };

    if (!$P.toISOString) {
        /**
         * Converts the current date instance into a string with an ISO 8601 format. The date is converted to it's UTC value.
         * @return {String}  ISO 8601 string of date
         */
        $P.toISOString = function () {
            // From http://www.json.org/json.js. Public Domain. 
            function f(n) {
                return n < 10 ? '0' + n : n;
            }

            return '"' + this.getUTCFullYear()   + '-' +
                f(this.getUTCMonth() + 1) + '-' +
                f(this.getUTCDate())      + 'T' +
                f(this.getUTCHours())     + ':' +
                f(this.getUTCMinutes())   + ':' +
                f(this.getUTCSeconds())   + 'Z"';
        };
    }
    
    // private
    $P._toString = $P.toString;

    /**
     * Converts the value of the current Date object to its equivalent string representation.
     * Format Specifiers
    <pre>
    CUSTOM DATE AND TIME FORMAT STRINGS
    Format  Description                                                                  Example
    ------  ---------------------------------------------------------------------------  -----------------------
     s      The seconds of the minute between 0-59.                                      "0" to "59"
     ss     The seconds of the minute with leading zero if required.                     "00" to "59"
     
     m      The minute of the hour between 0-59.                                         "0"  or "59"
     mm     The minute of the hour with leading zero if required.                        "00" or "59"
     
     h      The hour of the day between 1-12.                                            "1"  to "12"
     hh     The hour of the day with leading zero if required.                           "01" to "12"
     
     H      The hour of the day between 0-23.                                            "0"  to "23"
     HH     The hour of the day with leading zero if required.                           "00" to "23"
     
     d      The day of the month between 1 and 31.                                       "1"  to "31"
     dd     The day of the month with leading zero if required.                          "01" to "31"
     ddd    Abbreviated day name. $C.abbreviatedDayNames.                                "Mon" to "Sun" 
     dddd   The full day name. $C.dayNames.                                              "Monday" to "Sunday"
     
     M      The month of the year between 1-12.                                          "1" to "12"
     MM     The month of the year with leading zero if required.                         "01" to "12"
     MMM    Abbreviated month name. $C.abbreviatedMonthNames.                            "Jan" to "Dec"
     MMMM   The full month name. $C.monthNames.                                          "January" to "December"

     yy     The year as a two-digit number.                                              "99" or "08"
     yyyy   The full four digit year.                                                    "1999" or "2008"
     
     t      Displays the first character of the A.M./P.M. designator.                    "A" or "P"
            $C.amDesignator or $C.pmDesignator
     tt     Displays the A.M./P.M. designator.                                           "AM" or "PM"
            $C.amDesignator or $C.pmDesignator
     
     S      The ordinal suffix ("st, "nd", "rd" or "th") of the current day.            "st, "nd", "rd" or "th"

|| *Format* || *Description* || *Example* ||
|| d      || The CultureInfo shortDate Format Pattern                                     || "M/d/yyyy" ||
|| D      || The CultureInfo longDate Format Pattern                                      || "dddd, MMMM dd, yyyy" ||
|| F      || The CultureInfo fullDateTime Format Pattern                                  || "dddd, MMMM dd, yyyy h:mm:ss tt" ||
|| m      || The CultureInfo monthDay Format Pattern                                      || "MMMM dd" ||
|| r      || The CultureInfo rfc1123 Format Pattern                                       || "ddd, dd MMM yyyy HH:mm:ss GMT" ||
|| s      || The CultureInfo sortableDateTime Format Pattern                              || "yyyy-MM-ddTHH:mm:ss" ||
|| t      || The CultureInfo shortTime Format Pattern                                     || "h:mm tt" ||
|| T      || The CultureInfo longTime Format Pattern                                      || "h:mm:ss tt" ||
|| u      || The CultureInfo universalSortableDateTime Format Pattern                     || "yyyy-MM-dd HH:mm:ssZ" ||
|| y      || The CultureInfo yearMonth Format Pattern                                     || "MMMM, yyyy" ||
     

    STANDARD DATE AND TIME FORMAT STRINGS
    Format  Description                                                                  Example ("en-US")
    ------  ---------------------------------------------------------------------------  -----------------------
     xqd      The CultureInfo shortDate Format Pattern                                     "M/d/yyyy"
     xqD      The CultureInfo longDate Format Pattern                                      "dddd, MMMM dd, yyyy"
     xqF      The CultureInfo fullDateTime Format Pattern                                  "dddd, MMMM dd, yyyy h:mm:ss tt"
     xqm      The CultureInfo monthDay Format Pattern                                      "MMMM dd"
     xqr      The CultureInfo rfc1123 Format Pattern                                       "ddd, dd MMM yyyy HH:mm:ss GMT"
     xqs      The CultureInfo sortableDateTime Format Pattern                              "yyyy-MM-ddTHH:mm:ss"
     xqt      The CultureInfo shortTime Format Pattern                                     "h:mm tt"
     xqT      The CultureInfo longTime Format Pattern                                      "h:mm:ss tt"
     xqu      The CultureInfo universalSortableDateTime Format Pattern                     "yyyy-MM-dd HH:mm:ssZ"
     xqy      The CultureInfo yearMonth Format Pattern                                     "MMMM, yyyy"
    </pre>
     * @param {String}   A format string consisting of one or more format spcifiers [Optional].
     * @return {String}  A string representation of the current Date object.
     */
    $P.toString = function (format) {
        var x = this;
        
        // Standard Date and Time Format Strings. Formats pulled from CultureInfo file and
        // may vary by culture. 
        if (format && format.length == 1) {
            var c = $C.formatPatterns;
            x.t = x.toString;
            switch (format) {
            case "xqd": 
                return x.t(c.shortDate);
            case "xqD":
                return x.t(c.longDate);
            case "xqF":
                return x.t(c.fullDateTime);
            case "xqm":
                return x.t(c.monthDay);
            case "xqr":
                return x.t(c.rfc1123);
            case "xqs":
                return x.t(c.sortableDateTime);
            case "xqt":
                return x.t(c.shortTime);
            case "xqT":
                return x.t(c.longTime);
            case "xqu":
                return x.t(c.universalSortableDateTime);
            case "xqy":
                return x.t(c.yearMonth);
            }    
        }
        
        var ord = function (n) {
                switch (n * 1) {
                case 1: 
                case 21: 
                case 31: 
                    return "st";
                case 2: 
                case 22: 
                    return "nd";
                case 3: 
                case 23: 
                    return "rd";
                default: 
                    return "th";
                }
            };
        
        return format ? format.replace(/(\\)?(dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|ff?f?|tt?|S)/g, 
        function (m) {
            if (m.charAt(0) === "\\") {
                return m.replace("\\", "");
            }
            x.h = x.getHours;
            switch (m) {
            case "hh":
                return p(x.h() < 13 ? (x.h() === 0 ? 12 : x.h()) : (x.h() - 12));
            case "h":
                return x.h() < 13 ? (x.h() === 0 ? 12 : x.h()) : (x.h() - 12);
            case "HH":
                return p(x.h());
            case "H":
                return x.h();
            case "mm":
                return p(x.getMinutes());
            case "m":
                return x.getMinutes();
            case "ss":
                return p(x.getSeconds());
            case "s":
                return x.getSeconds();
            case "fff":
                return p(x.getMilliseconds(), 3);
            case "f":
                return x.getMilliseconds();
            case "yyyy":
                return p(x.getFullYear(), 4);
            case "yy":
                return p(x.getFullYear());
            case "dddd":
                return $C.dayNames[x.getDay()];
            case "ddd":
                return $C.abbreviatedDayNames[x.getDay()];
            case "dd":
                return p(x.getDate());
            case "d":
                return x.getDate();
            case "MMMM":
                return $C.monthNames[x.getMonth()];
            case "MMM":
                return $C.abbreviatedMonthNames[x.getMonth()];
            case "MM":
                return p((x.getMonth() + 1));
            case "M":
                return x.getMonth() + 1;
            case "t":
                return x.h() < 12 ? $C.amDesignator.substring(0, 1) : $C.pmDesignator.substring(0, 1);
            case "tt":
                return x.h() < 12 ? $C.amDesignator : $C.pmDesignator;
            case "S":
                return ord(x.getDate());
            default: 
                return m;
            }
        }
        ) : this._toString();
    };
}());    
/**
 * @version: 1.0 Alpha-1
 * @author: Coolite Inc. http://www.coolite.com/
 * @date: 2008-04-13
 * @copyright: Copyright (c) 2006-2008, Coolite Inc. (http://www.coolite.com/). All rights reserved.
 * @license: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/. 
 * @website: http://www.datejs.com/
 */
 
(function () {
    Date.Parsing = {
        Exception: function (s) {
            this.message = "Parse error at '" + s.substring(0, 10) + " ...'"; 
        }
    };
    
    var $P = Date.Parsing; 
    var _ = $P.Operators = {
        //
        // Tokenizers
        //
        rtoken: function (r) { // regex token
            return function (s) {
                var mx = s.match(r);
                if (mx) { 
                    return ([ mx[0], s.substring(mx[0].length) ]); 
                } else { 
                    throw new $P.Exception(s); 
                }
            };
        },
        token: function (s) { // whitespace-eating token
            return function (s) {
                return _.rtoken(new RegExp("^\s*" + s + "\s*"))(s);
                // Removed .strip()
                // return _.rtoken(new RegExp("^\s*" + s + "\s*"))(s).strip();
            };
        },
        stoken: function (s) { // string token
            return _.rtoken(new RegExp("^" + s)); 
        },

        //
        // Atomic Operators
        // 

        until: function (p) {
            return function (s) {
                var qx = [], rx = null;
                while (s.length) { 
                    try { 
                        rx = p.call(this, s); 
                    } catch (e) { 
                        qx.push(rx[0]); 
                        s = rx[1]; 
                        continue; 
                    }
                    break;
                }
                return [ qx, s ];
            };
        },
        many: function (p) {
            return function (s) {
                var rx = [], r = null; 
                while (s.length) { 
                    try { 
                        r = p.call(this, s); 
                    } catch (e) { 
                        return [ rx, s ]; 
                    }
                    rx.push(r[0]); 
                    s = r[1];
                }
                return [ rx, s ];
            };
        },

        // generator operators -- see below
        optional: function (p) {
            return function (s) {
                var r = null; 
                try { 
                    r = p.call(this, s); 
                } catch (e) { 
                    return [ null, s ]; 
                }
                return [ r[0], r[1] ];
            };
        },
        not: function (p) {
            return function (s) {
                try { 
                    p.call(this, s); 
                } catch (e) { 
                    return [null, s]; 
                }
                throw new $P.Exception(s);
            };
        },
        ignore: function (p) {
            return p ? 
            function (s) { 
                var r = null; 
                r = p.call(this, s); 
                return [null, r[1]]; 
            } : null;
        },
        product: function () {
            var px = arguments[0], 
            qx = Array.prototype.slice.call(arguments, 1), rx = [];
            for (var i = 0 ; i < px.length ; i++) {
                rx.push(_.each(px[i], qx));
            }
            return rx;
        },
        cache: function (rule) { 
            var cache = {}, r = null; 
            return function (s) {
                try { 
                    r = cache[s] = (cache[s] || rule.call(this, s)); 
                } catch (e) { 
                    r = cache[s] = e; 
                }
                if (r instanceof $P.Exception) { 
                    throw r; 
                } else { 
                    return r; 
                }
            };
        },
    	  
        // vector operators -- see below
        any: function () {
            var px = arguments;
            return function (s) { 
                var r = null;
                for (var i = 0; i < px.length; i++) { 
                    if (px[i] == null) { 
                        continue; 
                    }
                    try { 
                        r = (px[i].call(this, s)); 
                    } catch (e) { 
                        r = null; 
                    }
                    if (r) { 
                        return r; 
                    }
                } 
                throw new $P.Exception(s);
            };
        },
        each: function () { 
            var px = arguments;
            return function (s) { 
                var rx = [], r = null;
                for (var i = 0; i < px.length ; i++) { 
                    if (px[i] == null) { 
                        continue; 
                    }
                    try { 
                        r = (px[i].call(this, s)); 
                    } catch (e) { 
                        throw new $P.Exception(s); 
                    }
                    rx.push(r[0]); 
                    s = r[1];
                }
                return [ rx, s]; 
            };
        },
        all: function () { 
            var px = arguments, _ = _; 
            return _.each(_.optional(px)); 
        },

        // delimited operators
        sequence: function (px, d, c) {
            d = d || _.rtoken(/^\s*/);  
            c = c || null;
            
            if (px.length == 1) { 
                return px[0]; 
            }
            return function (s) {
                var r = null, q = null;
                var rx = []; 
                for (var i = 0; i < px.length ; i++) {
                    try { 
                        r = px[i].call(this, s); 
                    } catch (e) { 
                        break; 
                    }
                    rx.push(r[0]);
                    try { 
                        q = d.call(this, r[1]); 
                    } catch (ex) { 
                        q = null; 
                        break; 
                    }
                    s = q[1];
                }
                if (!r) { 
                    throw new $P.Exception(s); 
                }
                if (q) { 
                    throw new $P.Exception(q[1]); 
                }
                if (c) {
                    try { 
                        r = c.call(this, r[1]);
                    } catch (ey) { 
                        throw new $P.Exception(r[1]); 
                    }
                }
                return [ rx, (r?r[1]:s) ];
            };
        },
    		
	    //
	    // Composite Operators
	    //
    		
        between: function (d1, p, d2) { 
            d2 = d2 || d1; 
            var _fn = _.each(_.ignore(d1), p, _.ignore(d2));
            return function (s) { 
                var rx = _fn.call(this, s); 
                return [[rx[0][0], r[0][2]], rx[1]]; 
            };
        },
        list: function (p, d, c) {
            d = d || _.rtoken(/^\s*/);  
            c = c || null;
            return (p instanceof Array ?
                _.each(_.product(p.slice(0, -1), _.ignore(d)), p.slice(-1), _.ignore(c)) :
                _.each(_.many(_.each(p, _.ignore(d))), px, _.ignore(c)));
        },
        set: function (px, d, c) {
            d = d || _.rtoken(/^\s*/); 
            c = c || null;
            return function (s) {
                // r is the current match, best the current 'best' match
                // which means it parsed the most amount of input
                var r = null, p = null, q = null, rx = null, best = [[], s], last = false;

                // go through the rules in the given set
                for (var i = 0; i < px.length ; i++) {

                    // last is a flag indicating whether this must be the last element
                    // if there is only 1 element, then it MUST be the last one
                    q = null; 
                    p = null; 
                    r = null; 
                    last = (px.length == 1); 

                    // first, we try simply to match the current pattern
                    // if not, try the next pattern
                    try { 
                        r = px[i].call(this, s);
                    } catch (e) { 
                        continue; 
                    }

                    // since we are matching against a set of elements, the first
                    // thing to do is to add r[0] to matched elements
                    rx = [[r[0]], r[1]];

                    // if we matched and there is still input to parse and 
                    // we don't already know this is the last element,
                    // we're going to next check for the delimiter ...
                    // if there's none, or if there's no input left to parse
                    // than this must be the last element after all ...
                    if (r[1].length > 0 && ! last) {
                        try { 
                            q = d.call(this, r[1]); 
                        } catch (ex) { 
                            last = true; 
                        }
                    } else { 
                        last = true; 
                    }

				    // if we parsed the delimiter and now there's no more input,
				    // that means we shouldn't have parsed the delimiter at all
				    // so don't update r and mark this as the last element ...
                    if (!last && q[1].length === 0) { 
                        last = true; 
                    }


				    // so, if this isn't the last element, we're going to see if
				    // we can get any more matches from the remaining (unmatched)
				    // elements ...
                    if (!last) {

                        // build a list of the remaining rules we can match against,
                        // i.e., all but the one we just matched against
                        var qx = []; 
                        for (var j = 0; j < px.length ; j++) { 
                            if (i != j) { 
                                qx.push(px[j]); 
                            }
                        }

                        // now invoke recursively set with the remaining input
                        // note that we don't include the closing delimiter ...
                        // we'll check for that ourselves at the end
                        p = _.set(qx, d).call(this, q[1]);

                        // if we got a non-empty set as a result ...
                        // (otw rx already contains everything we want to match)
                        if (p[0].length > 0) {
                            // update current result, which is stored in rx ...
                            // basically, pick up the remaining text from p[1]
                            // and concat the result from p[0] so that we don't
                            // get endless nesting ...
                            rx[0] = rx[0].concat(p[0]); 
                            rx[1] = p[1]; 
                        }
                    }

				    // at this point, rx either contains the last matched element
				    // or the entire matched set that starts with this element.

				    // now we just check to see if this variation is better than
				    // our best so far, in terms of how much of the input is parsed
                    if (rx[1].length < best[1].length) { 
                        best = rx; 
                    }

				    // if we've parsed all the input, then we're finished
                    if (best[1].length === 0) { 
                        break; 
                    }
                }

			    // so now we've either gone through all the patterns trying them
			    // as the initial match; or we found one that parsed the entire
			    // input string ...

			    // if best has no matches, just return empty set ...
                if (best[0].length === 0) { 
                    return best; 
                }

			    // if a closing delimiter is provided, then we have to check it also
                if (c) {
                    // we try this even if there is no remaining input because the pattern
                    // may well be optional or match empty input ...
                    try { 
                        q = c.call(this, best[1]); 
                    } catch (ey) { 
                        throw new $P.Exception(best[1]); 
                    }

                    // it parsed ... be sure to update the best match remaining input
                    best[1] = q[1];
                }

			    // if we're here, either there was no closing delimiter or we parsed it
			    // so now we have the best match; just return it!
                return best;
            };
        },
        forward: function (gr, fname) {
            return function (s) { 
                return gr[fname].call(this, s); 
            };
        },

        //
        // Translation Operators
        //
        replace: function (rule, repl) {
            return function (s) { 
                var r = rule.call(this, s); 
                return [repl, r[1]]; 
            };
        },
        process: function (rule, fn) {
            return function (s) {  
                var r = rule.call(this, s); 
                return [fn.call(this, r[0]), r[1]]; 
            };
        },
        min: function (min, rule) {
            return function (s) {
                var rx = rule.call(this, s); 
                if (rx[0].length < min) { 
                    throw new $P.Exception(s); 
                }
                return rx;
            };
        }
    };
	

	// Generator Operators And Vector Operators

	// Generators are operators that have a signature of F(R) => R,
	// taking a given rule and returning another rule, such as 
	// ignore, which parses a given rule and throws away the result.

	// Vector operators are those that have a signature of F(R1,R2,...) => R,
	// take a list of rules and returning a new rule, such as each.

	// Generator operators are converted (via the following _generator
	// function) into functions that can also take a list or array of rules
	// and return an array of new rules as though the function had been
	// called on each rule in turn (which is what actually happens).

	// This allows generators to be used with vector operators more easily.
	// Example:
	// each(ignore(foo, bar)) instead of each(ignore(foo), ignore(bar))

	// This also turns generators into vector operators, which allows
	// constructs like:
	// not(cache(foo, bar))
	
    var _generator = function (op) {
        return function () {
            var args = null, rx = [];
            if (arguments.length > 1) {
                args = Array.prototype.slice.call(arguments);
            } else if (arguments[0] instanceof Array) {
                args = arguments[0];
            }
            if (args) { 
                for (var i = 0, px = args.shift() ; i < px.length ; i++) {
                    args.unshift(px[i]); 
                    rx.push(op.apply(null, args)); 
                    args.shift();
                    return rx;
                } 
            } else { 
                return op.apply(null, arguments); 
            }
        };
    };
    
    var gx = "optional not ignore cache".split(/\s/);
    
    for (var i = 0 ; i < gx.length ; i++) { 
        _[gx[i]] = _generator(_[gx[i]]); 
    }

    var _vector = function (op) {
        return function () {
            if (arguments[0] instanceof Array) { 
                return op.apply(null, arguments[0]); 
            } else { 
                return op.apply(null, arguments); 
            }
        };
    };
    
    var vx = "each any all".split(/\s/);
    
    for (var j = 0 ; j < vx.length ; j++) { 
        _[vx[j]] = _vector(_[vx[j]]); 
    }
	
}());

(function () {
    var $D = Date, $P = $D.prototype, $C = $D.CultureInfo;

    var flattenAndCompact = function (ax) { 
        var rx = []; 
        for (var i = 0; i < ax.length; i++) {
            if (ax[i] instanceof Array) {
                rx = rx.concat(flattenAndCompact(ax[i]));
            } else { 
                if (ax[i]) { 
                    rx.push(ax[i]); 
                }
            }
        }
        return rx;
    };
    
    $D.Grammar = {};
	
    $D.Translator = {
        hour: function (s) { 
            return function () { 
                this.hour = Number(s); 
            }; 
        },
        minute: function (s) { 
            return function () { 
                this.minute = Number(s); 
            }; 
        },
        second: function (s) { 
            return function () { 
                this.second = Number(s); 
            }; 
        },
        meridian: function (s) { 
            return function () { 
                this.meridian = s.slice(0, 1).toLowerCase(); 
            }; 
        },
        timezone: function (s) {
            return function () {
                var n = s.replace(/[^\d\+\-]/g, "");
                if (n.length) { 
                    this.timezoneOffset = Number(n); 
                } else { 
                    this.timezone = s.toLowerCase(); 
                }
            };
        },
        day: function (x) { 
            var s = x[0];
            return function () { 
                this.day = Number(s.match(/\d+/)[0]); 
            };
        }, 
        month: function (s) {
            return function () {
                this.month = (s.length == 3) ? "jan feb mar apr may jun jul aug sep oct nov dec".indexOf(s)/4 : Number(s) - 1;
            };
        },
        year: function (s) {
            return function () {
                var n = Number(s);
                this.year = ((s.length > 2) ? n : 
                    (n + (((n + 2000) < $C.twoDigitYearMax) ? 2000 : 1900))); 
            };
        },
        rday: function (s) { 
            return function () {
                switch (s) {
                case "yesterday": 
                    this.days = -1;
                    break;
                case "tomorrow":  
                    this.days = 1;
                    break;
                case "today": 
                    this.days = 0;
                    break;
                case "now": 
                    this.days = 0; 
                    this.now = true; 
                    break;
                }
            };
        },
        finishExact: function (x) {  
            x = (x instanceof Array) ? x : [ x ]; 

            for (var i = 0 ; i < x.length ; i++) { 
                if (x[i]) { 
                    x[i].call(this); 
                }
            }
            
            var now = new Date();
            
            if ((this.hour || this.minute) && (!this.month && !this.year && !this.day)) {
                this.day = now.getDate();
            }

            if (!this.year) {
                this.year = now.getFullYear();
            }
            
            if (!this.month && this.month !== 0) {
                this.month = now.getMonth();
            }
            
            if (!this.day) {
                this.day = 1;
            }
            
            if (!this.hour) {
                this.hour = 0;
            }
            
            if (!this.minute) {
                this.minute = 0;
            }

            if (!this.second) {
                this.second = 0;
            }

            if (this.meridian && this.hour) {
                if (this.meridian == "p" && this.hour < 12) {
                    this.hour = this.hour + 12;
                } else if (this.meridian == "a" && this.hour == 12) {
                    this.hour = 0;
                }
            }
            
            if (this.day > $D.getDaysInMonth(this.year, this.month)) {
                throw new RangeError(this.day + " is not a valid value for days.");
            }

            var r = new Date(this.year, this.month, this.day, this.hour, this.minute, this.second);

            if (this.timezone) { 
                r.set({ timezone: this.timezone }); 
            } else if (this.timezoneOffset) { 
                r.set({ timezoneOffset: this.timezoneOffset }); 
            }
            
            return r;
        },			
        finish: function (x) {
            x = (x instanceof Array) ? flattenAndCompact(x) : [ x ];

            if (x.length === 0) { 
                return null; 
            }

            for (var i = 0 ; i < x.length ; i++) { 
                if (typeof x[i] == "function") {
                    x[i].call(this); 
                }
            }
            
            var today = $D.today();
            
            if (this.now && !this.unit && !this.operator) { 
                return new Date(); 
            } else if (this.now) {
                today = new Date();
            }
            
            var expression = !!(this.days && this.days !== null || this.orient || this.operator);
            
            var gap, mod, orient;
            orient = ((this.orient == "past" || this.operator == "subtract") ? -1 : 1);
            
            if(!this.now && "hour minute second".indexOf(this.unit) != -1) {
                today.setTimeToNow();
            }

            if (this.month || this.month === 0) {
                if ("year day hour minute second".indexOf(this.unit) != -1) {
                    this.value = this.month + 1;
                    this.month = null;
                    expression = true;
                }
            }
            
            if (!expression && this.weekday && !this.day && !this.days) {
                var temp = Date[this.weekday]();
                this.day = temp.getDate();
                if (!this.month) {
                    this.month = temp.getMonth();
                }
                this.year = temp.getFullYear();
            }
            
            if (expression && this.weekday && this.unit != "month") {
                this.unit = "day";
                gap = ($D.getDayNumberFromName(this.weekday) - today.getDay());
                mod = 7;
                this.days = gap ? ((gap + (orient * mod)) % mod) : (orient * mod);
            }
            
            if (this.month && this.unit == "day" && this.operator) {
                this.value = (this.month + 1);
                this.month = null;
            }
       
            if (this.value != null && this.month != null && this.year != null) {
                this.day = this.value * 1;
            }
     
            if (this.month && !this.day && this.value) {
                today.set({ day: this.value * 1 });
                if (!expression) {
                    this.day = this.value * 1;
                }
            }

            if (!this.month && this.value && this.unit == "month" && !this.now) {
                this.month = this.value;
                expression = true;
            }

            if (expression && (this.month || this.month === 0) && this.unit != "year") {
                this.unit = "month";
                gap = (this.month - today.getMonth());
                mod = 12;
                this.months = gap ? ((gap + (orient * mod)) % mod) : (orient * mod);
                this.month = null;
            }

            if (!this.unit) { 
                this.unit = "day"; 
            }
            
            if (!this.value && this.operator && this.operator !== null && this[this.unit + "s"] && this[this.unit + "s"] !== null) {
                this[this.unit + "s"] = this[this.unit + "s"] + ((this.operator == "add") ? 1 : -1) + (this.value||0) * orient;
            } else if (this[this.unit + "s"] == null || this.operator != null) {
                if (!this.value) {
                    this.value = 1;
                }
                this[this.unit + "s"] = this.value * orient;
            }

            if (this.meridian && this.hour) {
                if (this.meridian == "p" && this.hour < 12) {
                    this.hour = this.hour + 12;
                } else if (this.meridian == "a" && this.hour == 12) {
                    this.hour = 0;
                }
            }
            
            if (this.weekday && !this.day && !this.days) {
                var temp = Date[this.weekday]();
                this.day = temp.getDate();
                if (temp.getMonth() !== today.getMonth()) {
                    this.month = temp.getMonth();
                }
            }
            
            if ((this.month || this.month === 0) && !this.day) { 
                this.day = 1; 
            }
            
            if (!this.orient && !this.operator && this.unit == "week" && this.value && !this.day && !this.month) {
                return Date.today().setWeek(this.value);
            }

            if (expression && this.timezone && this.day && this.days) {
                this.day = this.days;
            }
            
            return (expression) ? today.add(this) : today.set(this);
        }
    };

    var _ = $D.Parsing.Operators, g = $D.Grammar, t = $D.Translator, _fn;

    g.datePartDelimiter = _.rtoken(/^([\s\-\.\,\/\x27]+)/); 
    g.timePartDelimiter = _.stoken(":");
    g.whiteSpace = _.rtoken(/^\s*/);
    g.generalDelimiter = _.rtoken(/^(([\s\,]|at|@|on)+)/);
  
    var _C = {};
    g.ctoken = function (keys) {
        var fn = _C[keys];
        if (! fn) {
            var c = $C.regexPatterns;
            var kx = keys.split(/\s+/), px = []; 
            for (var i = 0; i < kx.length ; i++) {
                px.push(_.replace(_.rtoken(c[kx[i]]), kx[i]));
            }
            fn = _C[keys] = _.any.apply(null, px);
        }
        return fn;
    };
    g.ctoken2 = function (key) { 
        return _.rtoken($C.regexPatterns[key]);
    };

    // hour, minute, second, meridian, timezone
    g.h = _.cache(_.process(_.rtoken(/^(0[0-9]|1[0-2]|[1-9])/), t.hour));
    g.hh = _.cache(_.process(_.rtoken(/^(0[0-9]|1[0-2])/), t.hour));
    g.H = _.cache(_.process(_.rtoken(/^([0-1][0-9]|2[0-3]|[0-9])/), t.hour));
    g.HH = _.cache(_.process(_.rtoken(/^([0-1][0-9]|2[0-3])/), t.hour));
    g.m = _.cache(_.process(_.rtoken(/^([0-5][0-9]|[0-9])/), t.minute));
    g.mm = _.cache(_.process(_.rtoken(/^[0-5][0-9]/), t.minute));
    g.s = _.cache(_.process(_.rtoken(/^([0-5][0-9]|[0-9])/), t.second));
    g.ss = _.cache(_.process(_.rtoken(/^[0-5][0-9]/), t.second));
    g.hms = _.cache(_.sequence([g.H, g.m, g.s], g.timePartDelimiter));
  
    // _.min(1, _.set([ g.H, g.m, g.s ], g._t));
    g.t = _.cache(_.process(g.ctoken2("shortMeridian"), t.meridian));
    g.tt = _.cache(_.process(g.ctoken2("longMeridian"), t.meridian));
    g.z = _.cache(_.process(_.rtoken(/^((\+|\-)\s*\d\d\d\d)|((\+|\-)\d\d\:?\d\d)/), t.timezone));
    g.zz = _.cache(_.process(_.rtoken(/^((\+|\-)\s*\d\d\d\d)|((\+|\-)\d\d\:?\d\d)/), t.timezone));
    
    g.zzz = _.cache(_.process(g.ctoken2("timezone"), t.timezone));
    g.timeSuffix = _.each(_.ignore(g.whiteSpace), _.set([ g.tt, g.zzz ]));
    g.time = _.each(_.optional(_.ignore(_.stoken("T"))), g.hms, g.timeSuffix);
    	  
    // days, months, years
    g.d = _.cache(_.process(_.each(_.rtoken(/^([0-2]\d|3[0-1]|\d)/), 
        _.optional(g.ctoken2("ordinalSuffix"))), t.day));
    g.dd = _.cache(_.process(_.each(_.rtoken(/^([0-2]\d|3[0-1])/), 
        _.optional(g.ctoken2("ordinalSuffix"))), t.day));
    g.ddd = g.dddd = _.cache(_.process(g.ctoken("sun mon tue wed thu fri sat"), 
        function (s) { 
            return function () { 
                this.weekday = s; 
            }; 
        }
    ));
    g.M = _.cache(_.process(_.rtoken(/^(1[0-2]|0\d|\d)/), t.month));
    g.MM = _.cache(_.process(_.rtoken(/^(1[0-2]|0\d)/), t.month));
    g.MMM = g.MMMM = _.cache(_.process(
        g.ctoken("jan feb mar apr may jun jul aug sep oct nov dec"), t.month));
    g.y = _.cache(_.process(_.rtoken(/^(\d\d?)/), t.year));
    g.yy = _.cache(_.process(_.rtoken(/^(\d\d)/), t.year));
    g.yyy = _.cache(_.process(_.rtoken(/^(\d\d?\d?\d?)/), t.year));
    g.yyyy = _.cache(_.process(_.rtoken(/^(\d\d\d\d)/), t.year));
	
	// rolling these up into general purpose rules
    _fn = function () { 
        return _.each(_.any.apply(null, arguments), _.not(g.ctoken2("timeContext")));
    };
    
    g.day = _fn(g.d, g.dd); 
    g.month = _fn(g.M, g.MMM); 
    g.year = _fn(g.yyyy, g.yy);

    // relative date / time expressions
    g.orientation = _.process(g.ctoken("past future"), 
        function (s) { 
            return function () { 
                this.orient = s; 
            }; 
        }
    );
    g.operator = _.process(g.ctoken("add subtract"), 
        function (s) { 
            return function () { 
                this.operator = s; 
            }; 
        }
    );  
    g.rday = _.process(g.ctoken("yesterday tomorrow today now"), t.rday);
    g.unit = _.process(g.ctoken("second minute hour day week month year"), 
        function (s) { 
            return function () { 
                this.unit = s; 
            }; 
        }
    );
    g.value = _.process(_.rtoken(/^\d\d?(st|nd|rd|th)?/), 
        function (s) { 
            return function () { 
                this.value = s.replace(/\D/g, ""); 
            }; 
        }
    );
    g.expression = _.set([ g.rday, g.operator, g.value, g.unit, g.orientation, g.ddd, g.MMM ]);

    // pre-loaded rules for different date part order preferences
    _fn = function () { 
        return  _.set(arguments, g.datePartDelimiter); 
    };
    g.mdy = _fn(g.ddd, g.month, g.day, g.year);
    g.ymd = _fn(g.ddd, g.year, g.month, g.day);
    g.dmy = _fn(g.ddd, g.day, g.month, g.year);
    g.date = function (s) { 
        return ((g[$C.dateElementOrder] || g.mdy).call(this, s));
    }; 

    // parsing date format specifiers - ex: "h:m:s tt" 
    // this little guy will generate a custom parser based
    // on the format string, ex: g.format("h:m:s tt")
    g.format = _.process(_.many(
        _.any(
        // translate format specifiers into grammar rules
        _.process(
        _.rtoken(/^(dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|zz?z?)/), 
        function (fmt) { 
        if (g[fmt]) { 
            return g[fmt]; 
        } else { 
            throw $D.Parsing.Exception(fmt); 
        }
    }
    ),
    // translate separator tokens into token rules
    _.process(
    _.rtoken(/^[^dMyhHmstz]+/), // all legal separators 
        function (s) { 
            return _.ignore(_.stoken(s)); 
        } 
    )
    )), 
        // construct the parser ...
        function (rules) { 
            return _.process(_.each.apply(null, rules), t.finishExact); 
        }
    );
    
    var _F = {
		//"M/d/yyyy": function (s) { 
		//	var m = s.match(/^([0-2]\d|3[0-1]|\d)\/(1[0-2]|0\d|\d)\/(\d\d\d\d)/);
		//	if (m!=null) { 
		//		var r =  [ t.month.call(this,m[1]), t.day.call(this,m[2]), t.year.call(this,m[3]) ];
		//		r = t.finishExact.call(this,r);
		//		return [ r, "" ];
		//	} else {
		//		throw new Date.Parsing.Exception(s);
		//	}
		//}
		//"M/d/yyyy": function (s) { return [ new Date(Date._parse(s)), ""]; }
	}; 
    var _get = function (f) { 
        return _F[f] = (_F[f] || g.format(f)[0]);      
    };
  
    g.formats = function (fx) {
        if (fx instanceof Array) {
            var rx = []; 
            for (var i = 0 ; i < fx.length ; i++) {
                rx.push(_get(fx[i])); 
            }
            return _.any.apply(null, rx);
        } else { 
            return _get(fx); 
        }
    };

	// check for these formats first
    g._formats = g.formats([
        "\"yyyy-MM-ddTHH:mm:ssZ\"",
        "yyyy-MM-ddTHH:mm:ssZ",
        "yyyy-MM-ddTHH:mm:ssz",
        "yyyy-MM-ddTHH:mm:ss",
        "yyyy-MM-ddTHH:mmZ",
        "yyyy-MM-ddTHH:mmz",
        "yyyy-MM-ddTHH:mm",
        "ddd, MMM dd, yyyy H:mm:ss tt",
        "ddd MMM d yyyy HH:mm:ss zzz",
        "MMddyyyy",
        "ddMMyyyy",
        "Mddyyyy",
        "ddMyyyy",
        "Mdyyyy",
        "dMyyyy",
        "yyyy",
        "Mdyy",
        "dMyy",
        "d"
    ]);

	// starting rule for general purpose grammar
    g._start = _.process(_.set([ g.date, g.time, g.expression ], 
        g.generalDelimiter, g.whiteSpace), t.finish);
	
	// real starting rule: tries selected formats first, 
	// then general purpose rule
    g.start = function (s) {
        try { 
            var r = g._formats.call({}, s); 
            if (r[1].length === 0) {
                return r; 
            }
        } catch (e) {}
        return g._start.call({}, s);
    };
	
	$D._parse = $D.parse;

    /**
     * Converts the specified string value into its JavaScript Date equivalent using CultureInfo specific format information.
     * 
     * Example
    <pre><code>
    ///////////
    // Dates //
    ///////////

    // 15-Oct-2004
    var d1 = Date.parse("10/15/2004");

    // 15-Oct-2004
    var d1 = Date.parse("15-Oct-2004");

    // 15-Oct-2004
    var d1 = Date.parse("2004.10.15");

    //Fri Oct 15, 2004
    var d1 = Date.parse("Fri Oct 15, 2004");

    ///////////
    // Times //
    ///////////

    // Today at 10 PM.
    var d1 = Date.parse("10 PM");

    // Today at 10:30 PM.
    var d1 = Date.parse("10:30 P.M.");

    // Today at 6 AM.
    var d1 = Date.parse("06am");

    /////////////////////
    // Dates and Times //
    /////////////////////

    // 8-July-2004 @ 10:30 PM
    var d1 = Date.parse("July 8th, 2004, 10:30 PM");

    // 1-July-2004 @ 10:30 PM
    var d1 = Date.parse("2004-07-01T22:30:00");

    ////////////////////
    // Relative Dates //
    ////////////////////

    // Returns today's date. The string "today" is culture specific.
    var d1 = Date.parse("today");

    // Returns yesterday's date. The string "yesterday" is culture specific.
    var d1 = Date.parse("yesterday");

    // Returns the date of the next thursday.
    var d1 = Date.parse("Next thursday");

    // Returns the date of the most previous monday.
    var d1 = Date.parse("last monday");

    // Returns today's day + one year.
    var d1 = Date.parse("next year");

    ///////////////
    // Date Math //
    ///////////////

    // Today + 2 days
    var d1 = Date.parse("t+2");

    // Today + 2 days
    var d1 = Date.parse("today + 2 days");

    // Today + 3 months
    var d1 = Date.parse("t+3m");

    // Today - 1 year
    var d1 = Date.parse("today - 1 year");

    // Today - 1 year
    var d1 = Date.parse("t-1y"); 


    /////////////////////////////
    // Partial Dates and Times //
    /////////////////////////////

    // July 15th of this year.
    var d1 = Date.parse("July 15");

    // 15th day of current day and year.
    var d1 = Date.parse("15");

    // July 1st of current year at 10pm.
    var d1 = Date.parse("7/1 10pm");
    </code></pre>
     *
     * @param {String}   The string value to convert into a Date object [Required]
     * @return {Date}    A Date object or null if the string cannot be converted into a Date.
     */
    $D.parse = function (s) {
        var r = null; 
        if (!s) { 
            return null; 
        }
        if (s instanceof Date) {
            return s;
        }
        try { 
            r = $D.Grammar.start.call({}, s.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1")); 
        } catch (e) { 
            return null; 
        }
        return ((r[1].length === 0) ? r[0] : null);
    };

    $D.getParseFunction = function (fx) {
        var fn = $D.Grammar.formats(fx);
        return function (s) {
            var r = null;
            try { 
                r = fn.call({}, s); 
            } catch (e) { 
                return null; 
            }
            return ((r[1].length === 0) ? r[0] : null);
        };
    };
    
    /**
     * Converts the specified string value into its JavaScript Date equivalent using the specified format {String} or formats {Array} and the CultureInfo specific format information.
     * The format of the string value must match one of the supplied formats exactly.
     * 
     * Example
    <pre><code>
    // 15-Oct-2004
    var d1 = Date.parseExact("10/15/2004", "M/d/yyyy");

    // 15-Oct-2004
    var d1 = Date.parse("15-Oct-2004", "M-ddd-yyyy");

    // 15-Oct-2004
    var d1 = Date.parse("2004.10.15", "yyyy.MM.dd");

    // Multiple formats
    var d1 = Date.parseExact("10/15/2004", ["M/d/yyyy", "MMMM d, yyyy"]);
    </code></pre>
     *
     * @param {String}   The string value to convert into a Date object [Required].
     * @param {Object}   The expected format {String} or an array of expected formats {Array} of the date string [Required].
     * @return {Date}    A Date object or null if the string cannot be converted into a Date.
     */
    $D.parseExact = function (s, fx) { 
        return $D.getParseFunction(fx)(s); 
    };	
}());
/**
 * @version: 1.0 Alpha-1
 * @author: Coolite Inc. http://www.coolite.com/
 * @date: 2008-04-13
 * @copyright: Copyright (c) 2006-2008, Coolite Inc. (http://www.coolite.com/). All rights reserved.
 * @license: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/. 
 * @website: http://www.datejs.com/
 */

/**
 **************************************************************
 ** SugarPak - Domain Specific Language -  Syntactical Sugar **
 **************************************************************
 */
 
(function () {
    var $D = Date, $P = $D.prototype, $C = $D.CultureInfo, $N = Number.prototype;

    // private
    $P._orient = +1;

    // private
    $P._nth = null;

    // private
    $P._is = false;

    // private
    $P._same = false;
    
    // private
    $P._isSecond = false;

    // private
    $N._dateElement = "day";

    /** 
     * Moves the date to the next instance of a date as specified by the subsequent date element function (eg. .day(), .month()), month name function (eg. .january(), .jan()) or day name function (eg. .friday(), fri()).
     * Example
    <pre><code>
    Date.today().next().friday();
    Date.today().next().fri();
    Date.today().next().march();
    Date.today().next().mar();
    Date.today().next().week();
    </code></pre>
     * 
     * @return {Date}    date
     */
    $P.next = function () {
        this._orient = +1;
        return this;
    };

    /** 
     * Creates a new Date (Date.today()) and moves the date to the next instance of the date as specified by the subsequent date element function (eg. .day(), .month()), month name function (eg. .january(), .jan()) or day name function (eg. .friday(), fri()).
     * Example
    <pre><code>
    Date.next().friday();
    Date.next().fri();
    Date.next().march();
    Date.next().mar();
    Date.next().week();
    </code></pre>
     * 
     * @return {Date}    date
     */    
    $D.next = function () {
        return $D.today().next();
    };

    /** 
     * Moves the date to the previous instance of a date as specified by the subsequent date element function (eg. .day(), .month()), month name function (eg. .january(), .jan()) or day name function (eg. .friday(), fri()).
     * Example
    <pre><code>
    Date.today().last().friday();
    Date.today().last().fri();
    Date.today().last().march();
    Date.today().last().mar();
    Date.today().last().week();
    </code></pre>
     *  
     * @return {Date}    date
     */
    $P.last = $P.prev = $P.previous = function () {
        this._orient = -1;
        return this;
    };

    /** 
     * Creates a new Date (Date.today()) and moves the date to the previous instance of the date as specified by the subsequent date element function (eg. .day(), .month()), month name function (eg. .january(), .jan()) or day name function (eg. .friday(), fri()).
     * Example
    <pre><code>
    Date.last().friday();
    Date.last().fri();
    Date.previous().march();
    Date.prev().mar();
    Date.last().week();
    </code></pre>
     *  
     * @return {Date}    date
     */
    $D.last = $D.prev = $D.previous = function () {
        return $D.today().last();
    };    

    /** 
     * Performs a equality check when followed by either a month name, day name or .weekday() function.
     * Example
    <pre><code>
    Date.today().is().friday(); // true|false
    Date.today().is().fri();
    Date.today().is().march();
    Date.today().is().mar();
    </code></pre>
     *  
     * @return {Boolean}    true|false
     */
    $P.is = function () { 
        this._is = true; 
        return this; 
    };

    /** 
     * Determines if two date objects occur on/in exactly the same instance of the subsequent date part function.
     * The function .same() must be followed by a date part function (example: .day(), .month(), .year(), etc).
     *
     * An optional Date can be passed in the date part function. If now date is passed as a parameter, 'Now' is used. 
     *
     * The following example demonstrates how to determine if two dates fall on the exact same day.
     *
     * Example
    <pre><code>
    var d1 = Date.today(); // today at 00:00
    var d2 = new Date();   // exactly now.

    // Do they occur on the same day?
    d1.same().day(d2); // true
    
     // Do they occur on the same hour?
    d1.same().hour(d2); // false, unless d2 hour is '00' (midnight).
    
    // What if it's the same day, but one year apart?
    var nextYear = Date.today().add(1).year();

    d1.same().day(nextYear); // false, because the dates must occur on the exact same day. 
    </code></pre>
     *
     * Scenario: Determine if a given date occurs during some week period 2 months from now. 
     *
     * Example
    <pre><code>
    var future = Date.today().add(2).months();
    return someDate.same().week(future); // true|false;
    </code></pre>
     *  
     * @return {Boolean}    true|false
     */    
    $P.same = function () { 
        this._same = true;
        this._isSecond = false;
        return this; 
    };

    /** 
     * Determines if the current date/time occurs during Today. Must be preceded by the .is() function.
     * Example
    <pre><code>
    someDate.is().today();    // true|false
    new Date().is().today();  // true
    Date.today().is().today();// true
    Date.today().add(-1).day().is().today(); // false
    </code></pre>
     *  
     * @return {Boolean}    true|false
     */    
    $P.today = function () {
        return this.same().day();
    };

    /** 
     * Determines if the current date is a weekday. This function must be preceded by the .is() function.
     * Example
    <pre><code>
    Date.today().is().weekday(); // true|false
    </code></pre>
     *  
     * @return {Boolean}    true|false
     */
    $P.weekday = function () {
        if (this._is) { 
            this._is = false;
            return (!this.is().sat() && !this.is().sun());
        }
        return false;
    };

    /** 
     * Sets the Time of the current Date instance. A string "6:15 pm" or config object {hour:18, minute:15} are accepted.
     * Example
    <pre><code>
    // Set time to 6:15pm with a String
    Date.today().at("6:15pm");

    // Set time to 6:15pm with a config object
    Date.today().at({hour:18, minute:15});
    </code></pre>
     *  
     * @return {Date}    date
     */
    $P.at = function (time) {
        return (typeof time === "string") ? $D.parse(this.toString("d") + " " + time) : this.set(time);
    }; 
        
    /** 
     * Creates a new Date() and adds this (Number) to the date based on the preceding date element function (eg. second|minute|hour|day|month|year).
     * Example
    <pre><code>
    // Undeclared Numbers must be wrapped with parentheses. Requirment of JavaScript.
    (3).days().fromNow();
    (6).months().fromNow();

    // Declared Number variables do not require parentheses. 
    var n = 6;
    n.months().fromNow();
    </code></pre>
     *  
     * @return {Date}    A new Date instance
     */
    $N.fromNow = $N.after = function (date) {
        var c = {};
        c[this._dateElement] = this;
        return ((!date) ? new Date() : date.clone()).add(c);
    };

    /** 
     * Creates a new Date() and subtract this (Number) from the date based on the preceding date element function (eg. second|minute|hour|day|month|year).
     * Example
    <pre><code>
    // Undeclared Numbers must be wrapped with parentheses. Requirment of JavaScript.
    (3).days().ago();
    (6).months().ago();

    // Declared Number variables do not require parentheses. 
    var n = 6;
    n.months().ago();
    </code></pre>
     *  
     * @return {Date}    A new Date instance
     */
    $N.ago = $N.before = function (date) {
        var c = {};
        c[this._dateElement] = this * -1;
        return ((!date) ? new Date() : date.clone()).add(c);
    };

    // Do NOT modify the following string tokens. These tokens are used to build dynamic functions.
    // All culture-specific strings can be found in the CultureInfo files. See /trunk/src/globalization/.
    var dx = ("sunday monday tuesday wednesday thursday friday saturday").split(/\s/),
        mx = ("january february march april may june july august september october november december").split(/\s/),
        px = ("Millisecond Second Minute Hour Day Week Month Year").split(/\s/),
        pxf = ("Milliseconds Seconds Minutes Hours Date Week Month FullYear").split(/\s/),
		nth = ("final first second third fourth fifth").split(/\s/),
        de;

   /** 
     * Returns an object literal of all the date parts.
     * Example
    <pre><code>
	var o = new Date().toObject();
	
	// { year: 2008, month: 4, week: 20, day: 13, hour: 18, minute: 9, second: 32, millisecond: 812 }
	
	// The object properties can be referenced directly from the object.
	
	alert(o.day);  // alerts "13"
	alert(o.year); // alerts "2008"
    </code></pre>
     *  
     * @return {Date}    An object literal representing the original date object.
     */
    $P.toObject = function () {
        var o = {};
        for (var i = 0; i < px.length; i++) {
            o[px[i].toLowerCase()] = this["get" + pxf[i]]();
        }
        return o;
    }; 
   
   /** 
     * Returns a date created from an object literal. Ignores the .week property if set in the config. 
     * Example
    <pre><code>
	var o = new Date().toObject();
	
	return Date.fromObject(o); // will return the same date. 

    var o2 = {month: 1, day: 20, hour: 18}; // birthday party!
    Date.fromObject(o2);
    </code></pre>
     *  
     * @return {Date}    An object literal representing the original date object.
     */    
    $D.fromObject = function(config) {
        config.week = null;
        return Date.today().set(config);
    };
        
    // Create day name functions and abbreviated day name functions (eg. monday(), friday(), fri()).
    var df = function (n) {
        return function () { 
            if (this._is) { 
                this._is = false; 
                return this.getDay() == n; 
            }
            if (this._nth !== null) {
                // If the .second() function was called earlier, remove the _orient 
                // from the date, and then continue.
                // This is required because 'second' can be used in two different context.
                // 
                // Example
                //
                //   Date.today().add(1).second();
                //   Date.march().second().monday();
                // 
                // Things get crazy with the following...
                //   Date.march().add(1).second().second().monday(); // but it works!!
                //  
                if (this._isSecond) {
                    this.addSeconds(this._orient * -1);
                }
                // make sure we reset _isSecond
                this._isSecond = false;

                var ntemp = this._nth;
                this._nth = null;
                var temp = this.clone().moveToLastDayOfMonth();
                this.moveToNthOccurrence(n, ntemp);
                if (this > temp) {
                    throw new RangeError($D.getDayName(n) + " does not occur " + ntemp + " times in the month of " + $D.getMonthName(temp.getMonth()) + " " + temp.getFullYear() + ".");
                }
                return this;
            }			
            return this.moveToDayOfWeek(n, this._orient);
        };
    };
    
    var sdf = function (n) {
        return function () {
            var t = $D.today(), shift = n - t.getDay();
            if (n === 0 && $C.firstDayOfWeek === 1 && t.getDay() !== 0) {
                shift = shift + 7;
            }
            return t.addDays(shift);
        };
    };
	
    for (var i = 0; i < dx.length; i++) {
        // Create constant static Day Name variables. Example: Date.MONDAY or Date.MON
        $D[dx[i].toUpperCase()] = $D[dx[i].toUpperCase().substring(0, 3)] = i;

        // Create Day Name functions. Example: Date.monday() or Date.mon()
        $D[dx[i]] = $D[dx[i].substring(0, 3)] = sdf(i);

        // Create Day Name instance functions. Example: Date.today().next().monday()
        $P[dx[i]] = $P[dx[i].substring(0, 3)] = df(i);
    }
    
    // Create month name functions and abbreviated month name functions (eg. january(), march(), mar()).
    var mf = function (n) { 
        return function () {
            if (this._is) { 
                this._is = false; 
                return this.getMonth() === n; 
            }
            return this.moveToMonth(n, this._orient); 
        };
    };
    
    var smf = function (n) {
        return function () {
            return $D.today().set({ month: n, day: 1 });
        };
    };
    
    for (var j = 0; j < mx.length; j++) {
        // Create constant static Month Name variables. Example: Date.MARCH or Date.MAR
        $D[mx[j].toUpperCase()] = $D[mx[j].toUpperCase().substring(0, 3)] = j;

        // Create Month Name functions. Example: Date.march() or Date.mar()
        $D[mx[j]] = $D[mx[j].substring(0, 3)] = smf(j);

        // Create Month Name instance functions. Example: Date.today().next().march()
        $P[mx[j]] = $P[mx[j].substring(0, 3)] = mf(j);
    }
    
    // Create date element functions and plural date element functions used with Date (eg. day(), days(), months()).
    var ef = function (j) {
        return function () {
            // if the .second() function was called earlier, the _orient 
            // has alread been added. Just return this and reset _isSecond.
            if (this._isSecond) {
                this._isSecond = false;
                return this;
            }

            if (this._same) {
                this._same = this._is = false; 
                var o1 = this.toObject(),
                    o2 = (arguments[0] || new Date()).toObject(),
                    v = "",
                    k = j.toLowerCase();
                    
                for (var m = (px.length - 1); m > -1; m--) {
                    v = px[m].toLowerCase();
                    if (o1[v] != o2[v]) {
                        return false;
                    }
                    if (k == v) {
                        break;
                    }
                }
                return true;
            }
            
            if (j.substring(j.length - 1) != "s") {
                j += "s"; 
            }
            return this["add" + j](this._orient);
        };
    };
    
    
    var nf = function (n) {
        return function () {
            this._dateElement = n;
            return this;
        };
    };
   
    for (var k = 0; k < px.length; k++) {
        de = px[k].toLowerCase();
    
        // Create date element functions and plural date element functions used with Date (eg. day(), days(), months()).
        $P[de] = $P[de + "s"] = ef(px[k]);
        
        // Create date element functions and plural date element functions used with Number (eg. day(), days(), months()).
        $N[de] = $N[de + "s"] = nf(de);
    }
    
    $P._ss = ef("Second");
	
    var nthfn = function (n) {
        return function (dayOfWeek) {
            if (this._same) {
                return this._ss(arguments[0]);
            }
            if (dayOfWeek || dayOfWeek === 0) {
                return this.moveToNthOccurrence(dayOfWeek, n);
            }
            this._nth = n;

            // if the operator is 'second' add the _orient, then deal with it later...
            if (n === 2 && (dayOfWeek === undefined || dayOfWeek === null)) {
                this._isSecond = true;
                return this.addSeconds(this._orient);
            }
            return this;
        };
    };

    for (var l = 0; l < nth.length; l++) {
        $P[nth[l]] = (l === 0) ? nthfn(-1) : nthfn(l);
    }
}());
/*
    http://www.JSON.org/json2.js
    2009-08-17

    Public Domain.

    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.

    See http://www.JSON.org/js.html

    This file creates a global JSON object containing two methods: stringify
    and parse.

        JSON.stringify(value, replacer, space)
            value       any JavaScript value, usually an object or array.

            replacer    an optional parameter that determines how object
                        values are stringified for objects. It can be a
                        function or an array of strings.

            space       an optional parameter that specifies the indentation
                        of nested structures. If it is omitted, the text will
                        be packed without extra whitespace. If it is a number,
                        it will specify the number of spaces to indent at each
                        level. If it is a string (such as '\t' or '&nbsp;'),
                        it contains the characters used to indent at each level.

            This method produces a JSON text from a JavaScript value.

            When an object value is found, if the object contains a toJSON
            method, its toJSON method will be called and the result will be
            stringified. A toJSON method does not serialize: it returns the
            value represented by the name/value pair that should be serialized,
            or undefined if nothing should be serialized. The toJSON method
            will be passed the key associated with the value, and this will be
            bound to the value

            For example, this would serialize Dates as ISO strings.

                Date.prototype.toJSON = function (key) {
                    function f(n) {
                        // Format integers to have at least two digits.
                        return n < 10 ? '0' + n : n;
                    }

                    return this.getUTCFullYear()   + '-' +
                         f(this.getUTCMonth() + 1) + '-' +
                         f(this.getUTCDate())      + 'T' +
                         f(this.getUTCHours())     + ':' +
                         f(this.getUTCMinutes())   + ':' +
                         f(this.getUTCSeconds())   + 'Z';
                };

            You can provide an optional replacer method. It will be passed the
            key and value of each member, with this bound to the containing
            object. The value that is returned from your method will be
            serialized. If your method returns undefined, then the member will
            be excluded from the serialization.

            If the replacer parameter is an array of strings, then it will be
            used to select the members to be serialized. It filters the results
            such that only members with keys listed in the replacer array are
            stringified.

            Values that do not have JSON representations, such as undefined or
            functions, will not be serialized. Such values in objects will be
            dropped; in arrays they will be replaced with null. You can use
            a replacer function to replace those with JSON values.
            JSON.stringify(undefined) returns undefined.

            The optional space parameter produces a stringification of the
            value that is filled with line breaks and indentation to make it
            easier to read.

            If the space parameter is a non-empty string, then that string will
            be used for indentation. If the space parameter is a number, then
            the indentation will be that many spaces.

            Example:

            text = JSON.stringify(['e', {pluribus: 'unum'}]);
            // text is '["e",{"pluribus":"unum"}]'


            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'

            text = JSON.stringify([new Date()], function (key, value) {
                return this[key] instanceof Date ?
                    'Date(' + this[key] + ')' : value;
            });
            // text is '["Date(---current time---)"]'


        JSON.parse(text, reviver)
            This method parses a JSON text to produce an object or array.
            It can throw a SyntaxError exception.

            The optional reviver parameter is a function that can filter and
            transform the results. It receives each of the keys and values,
            and its return value is used instead of the original value.
            If it returns what it received, then the structure is not modified.
            If it returns undefined then the member is deleted.

            Example:

            // Parse the text. Values that look like ISO date strings will
            // be converted to Date objects.

            myData = JSON.parse(text, function (key, value) {
                var a;
                if (typeof value === 'string') {
                    a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
                    if (a) {
                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
                            +a[5], +a[6]));
                    }
                }
                return value;
            });

            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
                var d;
                if (typeof value === 'string' &&
                        value.slice(0, 5) === 'Date(' &&
                        value.slice(-1) === ')') {
                    d = new Date(value.slice(5, -1));
                    if (d) {
                        return d;
                    }
                }
                return value;
            });


    This is a reference implementation. You are free to copy, modify, or
    redistribute.

    This code should be minified before deployment.
    See http://javascript.crockford.com/jsmin.html

    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
    NOT CONTROL.
*/

/*jslint evil: true */

/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
    lastIndex, length, parse, prototype, push, replace, slice, stringify,
    test, toJSON, toString, valueOf
*/

"use strict";

// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.

if (!this.JSON) {
    this.JSON = {};
}

(function () {

    function f(n) {
        // Format integers to have at least two digits.
        return n < 10 ? '0' + n : n;
    }

    if (typeof Date.prototype.toJSON !== 'function') {

        Date.prototype.toJSON = function (key) {

            return isFinite(this.valueOf()) ?
                   this.getUTCFullYear()   + '-' +
                 f(this.getUTCMonth() + 1) + '-' +
                 f(this.getUTCDate())      + 'T' +
                 f(this.getUTCHours())     + ':' +
                 f(this.getUTCMinutes())   + ':' +
                 f(this.getUTCSeconds())   + 'Z' : null;
        };

        String.prototype.toJSON =
        Number.prototype.toJSON =
        Boolean.prototype.toJSON = function (key) {
            return this.valueOf();
        };
    }

    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        rep;


    function quote(string) {

// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.

        escapable.lastIndex = 0;
        return escapable.test(string) ?
            '"' + string.replace(escapable, function (a) {
                var c = meta[a];
                return typeof c === 'string' ? c :
                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
            }) + '"' :
            '"' + string + '"';
    }


    function str(key, holder) {

// Produce a string from holder[key].

        var i,          // The loop counter.
            k,          // The member key.
            v,          // The member value.
            length,
            mind = gap,
            partial,
            value = holder[key];

// If the value has a toJSON method, call it to obtain a replacement value.

        if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }

// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.

        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }

// What happens next depends on the value's type.

        switch (typeof value) {
        case 'string':
            return quote(value);

        case 'number':

// JSON numbers must be finite. Encode non-finite numbers as null.

            return isFinite(value) ? String(value) : 'null';

        case 'boolean':
        case 'null':

// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.

            return String(value);

// If the type is 'object', we might be dealing with an object or an array or
// null.

        case 'object':

// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.

            if (!value) {
                return 'null';
            }

// Make an array to hold the partial results of stringifying this object value.

            gap += indent;
            partial = [];

// Is the value an array?

            if (Object.prototype.toString.apply(value) === '[object Array]') {

// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.

                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || 'null';
                }

// Join all of the elements together, separated with commas, and wrap them in
// brackets.

                v = partial.length === 0 ? '[]' :
                    gap ? '[\n' + gap +
                            partial.join(',\n' + gap) + '\n' +
                                mind + ']' :
                          '[' + partial.join(',') + ']';
                gap = mind;
                return v;
            }

// If the replacer is an array, use it to select the members to be stringified.

            if (rep && typeof rep === 'object') {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    k = rep[i];
                    if (typeof k === 'string') {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            } else {

// Otherwise, iterate through all of the keys in the object.

                for (k in value) {
                    if (Object.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            }

// Join all of the member texts together, separated with commas,
// and wrap them in braces.

            v = partial.length === 0 ? '{}' :
                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
                        mind + '}' : '{' + partial.join(',') + '}';
            gap = mind;
            return v;
        }
    }

// If the JSON object does not yet have a stringify method, give it one.

    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {

// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.

            var i;
            gap = '';
            indent = '';

// If the space parameter is a number, make an indent string containing that
// many spaces.

            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }

// If the space parameter is a string, it will be used as the indent string.

            } else if (typeof space === 'string') {
                indent = space;
            }

// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.

            rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                    (typeof replacer !== 'object' ||
                     typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }

// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.

            return str('', {'': value});
        };
    }


// If the JSON object does not yet have a parse method, give it one.

    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {

// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.

            var j;

            function walk(holder, key) {

// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.

                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }


// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.

            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }

// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.

// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.

            if (/^[\],:{}\s]*$/.
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.

                j = eval('(' + text + ')');

// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.

                return typeof reviver === 'function' ?
                    walk({'': j}, '') : j;
            }

// If the text is not JSON parseable, then a SyntaxError is thrown.

            throw new SyntaxError('JSON.parse');
        };
    }
}());
sfHover = function() {
  var navMenu = document.getElementById("nav_menu");
  if (navMenu){
  	var sfEls = navMenu.getElementsByTagName("LI");
    if (sfEls){
    	for (var i=0; i<sfEls.length; i++) {
    		sfEls[i].onmouseover=function() {
    			this.className = [this.className, " sfhover"].join('');
    		}
    		sfEls[i].onmouseout=function() {
    			this.className=this.className.replace(new RegExp(" sfhover\\b"), "");
    		}
    	}
    }
  }
}

if (window.attachEvent) window.attachEvent("onload", sfHover);
// Underscore.js
// (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the terms of the MIT license.
// Portions of Underscore are inspired by or borrowed from Prototype.js,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore

(function() {
  // ------------------------- Baseline setup ---------------------------------

  // Establish the root object, "window" in the browser, or "global" on the server.
  var root = this;

  // Save the previous value of the "_" variable.
  var previousUnderscore = root._;

  // Establish the object that gets thrown to break out of a loop iteration.
  var breaker = typeof StopIteration !== 'undefined' ? StopIteration : '__break__';

  // Quick regexp-escaping function, because JS doesn't have RegExp.escape().
  var escapeRegExp = function(s) { return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1'); };

  // Save bytes in the minified (but not gzipped) version:
  var ArrayProto = Array.prototype, ObjProto = Object.prototype;

  // Create quick reference variables for speed access to core prototypes.
  var slice                 = ArrayProto.slice,
      unshift               = ArrayProto.unshift,
      toString              = ObjProto.toString,
      hasOwnProperty        = ObjProto.hasOwnProperty,
      propertyIsEnumerable  = ObjProto.propertyIsEnumerable;

  // All ECMA5 native implementations we hope to use are declared here.
  var
    nativeForEach      = ArrayProto.forEach,
    nativeMap          = ArrayProto.map,
    nativeReduce       = ArrayProto.reduce,
    nativeReduceRight  = ArrayProto.reduceRight,
    nativeFilter       = ArrayProto.filter,
    nativeEvery        = ArrayProto.every,
    nativeSome         = ArrayProto.some,
    nativeIndexOf      = ArrayProto.indexOf,
    nativeLastIndexOf  = ArrayProto.lastIndexOf,
    nativeIsArray      = Array.isArray,
    nativeKeys         = Object.keys;

  // Create a safe reference to the Underscore object for use below.
  var _ = function(obj) { return new wrapper(obj); };

  // Export the Underscore object for CommonJS.
  if (typeof exports !== 'undefined') exports._ = _;

  // Export underscore to global scope.
  root._ = _;

  // Current version.
  _.VERSION = '1.0.1';

  // ------------------------ Collection Functions: ---------------------------

  // The cornerstone, an each implementation.
  // Handles objects implementing forEach, arrays, and raw objects.
  // Delegates to JavaScript 1.6's native forEach if available.
  var each = _.forEach = function(obj, iterator, context) {
    try {
      if (nativeForEach && obj.forEach === nativeForEach) {
        obj.forEach(iterator, context);
      } else if (_.isNumber(obj.length)) {
        for (var i = 0, l = obj.length; i < l; i++) iterator.call(context, obj[i], i, obj);
      } else {
        for (var key in obj) {
          if (hasOwnProperty.call(obj, key)) iterator.call(context, obj[key], key, obj);
        }
      }
    } catch(e) {
      if (e != breaker) throw e;
    }
    return obj;
  };

  // Return the results of applying the iterator to each element.
  // Delegates to JavaScript 1.6's native map if available.
  _.map = function(obj, iterator, context) {
    if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
    var results = [];
    each(obj, function(value, index, list) {
      results.push(iterator.call(context, value, index, list));
    });
    return results;
  };

  // Reduce builds up a single result from a list of values, aka inject, or foldl.
  // Delegates to JavaScript 1.8's native reduce if available.
  _.reduce = function(obj, memo, iterator, context) {
    if (nativeReduce && obj.reduce === nativeReduce) return obj.reduce(_.bind(iterator, context), memo);
    each(obj, function(value, index, list) {
      memo = iterator.call(context, memo, value, index, list);
    });
    return memo;
  };

  // The right-associative version of reduce, also known as foldr. Uses
  // Delegates to JavaScript 1.8's native reduceRight if available.
  _.reduceRight = function(obj, memo, iterator, context) {
    if (nativeReduceRight && obj.reduceRight === nativeReduceRight) return obj.reduceRight(_.bind(iterator, context), memo);
    var reversed = _.clone(_.toArray(obj)).reverse();
    return _.reduce(reversed, memo, iterator, context);
  };

  // Return the first value which passes a truth test.
  _.detect = function(obj, iterator, context) {
    var result;
    each(obj, function(value, index, list) {
      if (iterator.call(context, value, index, list)) {
        result = value;
        _.breakLoop();
      }
    });
    return result;
  };

  // Return all the elements that pass a truth test.
  // Delegates to JavaScript 1.6's native filter if available.
  _.filter = function(obj, iterator, context) {
    if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
    var results = [];
    each(obj, function(value, index, list) {
      iterator.call(context, value, index, list) && results.push(value);
    });
    return results;
  };

  // Return all the elements for which a truth test fails.
  _.reject = function(obj, iterator, context) {
    var results = [];
    each(obj, function(value, index, list) {
      !iterator.call(context, value, index, list) && results.push(value);
    });
    return results;
  };

  // Determine whether all of the elements match a truth test.
  // Delegates to JavaScript 1.6's native every if available.
  _.every = function(obj, iterator, context) {
    iterator = iterator || _.identity;
    if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
    var result = true;
    each(obj, function(value, index, list) {
      if (!(result = result && iterator.call(context, value, index, list))) _.breakLoop();
    });
    return result;
  };

  // Determine if at least one element in the object matches a truth test.
  // Delegates to JavaScript 1.6's native some if available.
  _.some = function(obj, iterator, context) {
    iterator = iterator || _.identity;
    if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
    var result = false;
    each(obj, function(value, index, list) {
      if (result = iterator.call(context, value, index, list)) _.breakLoop();
    });
    return result;
  };

  // Determine if a given value is included in the array or object using '==='.
  _.include = function(obj, target) {
    if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
    var found = false;
    each(obj, function(value) {
      if (found = value === target) _.breakLoop();
    });
    return found;
  };

  // Invoke a method with arguments on every item in a collection.
  _.invoke = function(obj, method) {
    var args = _.rest(arguments, 2);
    return _.map(obj, function(value) {
      return (method ? value[method] : value).apply(value, args);
    });
  };

  // Convenience version of a common use case of map: fetching a property.
  _.pluck = function(obj, key) {
    return _.map(obj, function(value){ return value[key]; });
  };

  // Return the maximum item or (item-based computation).
  _.max = function(obj, iterator, context) {
    if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
    var result = {computed : -Infinity};
    each(obj, function(value, index, list) {
      var computed = iterator ? iterator.call(context, value, index, list) : value;
      computed >= result.computed && (result = {value : value, computed : computed});
    });
    return result.value;
  };

  // Return the minimum element (or element-based computation).
  _.min = function(obj, iterator, context) {
    if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
    var result = {computed : Infinity};
    each(obj, function(value, index, list) {
      var computed = iterator ? iterator.call(context, value, index, list) : value;
      computed < result.computed && (result = {value : value, computed : computed});
    });
    return result.value;
  };

  // Sort the object's values by a criterion produced by an iterator.
  _.sortBy = function(obj, iterator, context) {
    return _.pluck(_.map(obj, function(value, index, list) {
      return {
        value : value,
        criteria : iterator.call(context, value, index, list)
      };
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }), 'value');
  };

  // Use a comparator function to figure out at what index an object should
  // be inserted so as to maintain order. Uses binary search.
  _.sortedIndex = function(array, obj, iterator) {
    iterator = iterator || _.identity;
    var low = 0, high = array.length;
    while (low < high) {
      var mid = (low + high) >> 1;
      iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
    }
    return low;
  };

  // Convert anything iterable into a real, live array.
  _.toArray = function(iterable) {
    if (!iterable)                return [];
    if (iterable.toArray)         return iterable.toArray();
    if (_.isArray(iterable))      return iterable;
    if (_.isArguments(iterable))  return slice.call(iterable);
    return _.values(iterable);
  };

  // Return the number of elements in an object.
  _.size = function(obj) {
    return _.toArray(obj).length;
  };

  // -------------------------- Array Functions: ------------------------------

  // Get the first element of an array. Passing "n" will return the first N
  // values in the array. Aliased as "head". The "guard" check allows it to work
  // with _.map.
  _.first = function(array, n, guard) {
    return n && !guard ? slice.call(array, 0, n) : array[0];
  };

  // Returns everything but the first entry of the array. Aliased as "tail".
  // Especially useful on the arguments object. Passing an "index" will return
  // the rest of the values in the array from that index onward. The "guard"
   //check allows it to work with _.map.
  _.rest = function(array, index, guard) {
    return slice.call(array, _.isUndefined(index) || guard ? 1 : index);
  };

  // Get the last element of an array.
  _.last = function(array) {
    return array[array.length - 1];
  };

  // Trim out all falsy values from an array.
  _.compact = function(array) {
    return _.filter(array, function(value){ return !!value; });
  };

  // Return a completely flattened version of an array.
  _.flatten = function(array) {
    return _.reduce(array, [], function(memo, value) {
      if (_.isArray(value)) return memo.concat(_.flatten(value));
      memo.push(value);
      return memo;
    });
  };

  // Return a version of the array that does not contain the specified value(s).
  _.without = function(array) {
    var values = _.rest(arguments);
    return _.filter(array, function(value){ return !_.include(values, value); });
  };

  // Produce a duplicate-free version of the array. If the array has already
  // been sorted, you have the option of using a faster algorithm.
  _.uniq = function(array, isSorted) {
    return _.reduce(array, [], function(memo, el, i) {
      if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo.push(el);
      return memo;
    });
  };

  // Produce an array that contains every item shared between all the
  // passed-in arrays.
  _.intersect = function(array) {
    var rest = _.rest(arguments);
    return _.filter(_.uniq(array), function(item) {
      return _.every(rest, function(other) {
        return _.indexOf(other, item) >= 0;
      });
    });
  };

  // Zip together multiple lists into a single array -- elements that share
  // an index go together.
  _.zip = function() {
    var args = _.toArray(arguments);
    var length = _.max(_.pluck(args, 'length'));
    var results = new Array(length);
    for (var i = 0; i < length; i++) results[i] = _.pluck(args, String(i));
    return results;
  };

  // If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
  // we need this function. Return the position of the first occurence of an
  // item in an array, or -1 if the item is not included in the array.
  // Delegates to JavaScript 1.8's native indexOf if available.
  _.indexOf = function(array, item) {
    if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
    for (var i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
    return -1;
  };


  // Delegates to JavaScript 1.6's native lastIndexOf if available.
  _.lastIndexOf = function(array, item) {
    if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
    var i = array.length;
    while (i--) if (array[i] === item) return i;
    return -1;
  };

  // Generate an integer Array containing an arithmetic progression. A port of
  // the native Python range() function. See:
  // http://docs.python.org/library/functions.html#range
  _.range = function(start, stop, step) {
    var a     = _.toArray(arguments);
    var solo  = a.length <= 1;
    var start = solo ? 0 : a[0], stop = solo ? a[0] : a[1], step = a[2] || 1;
    var len   = Math.ceil((stop - start) / step);
    if (len <= 0) return [];
    var range = new Array(len);
    for (var i = start, idx = 0; true; i += step) {
      if ((step > 0 ? i - stop : stop - i) >= 0) return range;
      range[idx++] = i;
    }
  };

  // ----------------------- Function Functions: ------------------------------

  // Create a function bound to a given object (assigning 'this', and arguments,
  // optionally). Binding with arguments is also known as 'curry'.
  _.bind = function(func, obj) {
    var args = _.rest(arguments, 2);
    return function() {
      return func.apply(obj || {}, args.concat(_.toArray(arguments)));
    };
  };

  // Bind all of an object's methods to that object. Useful for ensuring that
  // all callbacks defined on an object belong to it.
  _.bindAll = function(obj) {
    var funcs = _.rest(arguments);
    if (funcs.length == 0) funcs = _.functions(obj);
    each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
    return obj;
  };

  // Delays a function for the given number of milliseconds, and then calls
  // it with the arguments supplied.
  _.delay = function(func, wait) {
    var args = _.rest(arguments, 2);
    return setTimeout(function(){ return func.apply(func, args); }, wait);
  };

  // Defers a function, scheduling it to run after the current call stack has
  // cleared.
  _.defer = function(func) {
    return _.delay.apply(_, [func, 1].concat(_.rest(arguments)));
  };

  // Returns the first function passed as an argument to the second,
  // allowing you to adjust arguments, run code before and after, and
  // conditionally execute the original function.
  _.wrap = function(func, wrapper) {
    return function() {
      var args = [func].concat(_.toArray(arguments));
      return wrapper.apply(wrapper, args);
    };
  };

  // Returns a function that is the composition of a list of functions, each
  // consuming the return value of the function that follows.
  _.compose = function() {
    var funcs = _.toArray(arguments);
    return function() {
      var args = _.toArray(arguments);
      for (var i=funcs.length-1; i >= 0; i--) {
        args = [funcs[i].apply(this, args)];
      }
      return args[0];
    };
  };

  // ------------------------- Object Functions: ------------------------------

  // Retrieve the names of an object's properties.
  // Delegates to ECMA5's native Object.keys
  _.keys = nativeKeys || function(obj) {
    if (_.isArray(obj)) return _.range(0, obj.length);
    var keys = [];
    for (var key in obj) if (hasOwnProperty.call(obj, key)) keys.push(key);
    return keys;
  };

  // Retrieve the values of an object's properties.
  _.values = function(obj) {
    return _.map(obj, _.identity);
  };

  // Return a sorted list of the function names available on the object.
  _.functions = function(obj) {
    return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort();
  };

  // Extend a given object with all the properties in passed-in object(s).
  _.extend = function(obj) {
    each(_.rest(arguments), function(source) {
      for (var prop in source) obj[prop] = source[prop];
    });
    return obj;
  };

  // Create a (shallow-cloned) duplicate of an object.
  _.clone = function(obj) {
    if (_.isArray(obj)) return obj.slice(0);
    return _.extend({}, obj);
  };

  // Invokes interceptor with the obj, and then returns obj.
  // The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
  _.tap = function(obj, interceptor) {
    interceptor(obj);
    return obj;
  };

  // Perform a deep comparison to check if two objects are equal.
  _.isEqual = function(a, b) {
    // Check object identity.
    if (a === b) return true;
    // Different types?
    var atype = typeof(a), btype = typeof(b);
    if (atype != btype) return false;
    // Basic equality test (watch out for coercions).
    if (a == b) return true;
    // One is falsy and the other truthy.
    if ((!a && b) || (a && !b)) return false;
    // One of them implements an isEqual()?
    if (a.isEqual) return a.isEqual(b);
    // Check dates' integer values.
    if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
    // Both are NaN?
    if (_.isNaN(a) && _.isNaN(b)) return true;
    // Compare regular expressions.
    if (_.isRegExp(a) && _.isRegExp(b))
      return a.source     === b.source &&
             a.global     === b.global &&
             a.ignoreCase === b.ignoreCase &&
             a.multiline  === b.multiline;
    // If a is not an object by this point, we can't handle it.
    if (atype !== 'object') return false;
    // Check for different array lengths before comparing contents.
    if (a.length && (a.length !== b.length)) return false;
    // Nothing else worked, deep compare the contents.
    var aKeys = _.keys(a), bKeys = _.keys(b);
    // Different object sizes?
    if (aKeys.length != bKeys.length) return false;
    // Recursive comparison of contents.
    for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false;
    return true;
  };

  // Is a given array or object empty?
  _.isEmpty = function(obj) {
    if (_.isArray(obj)) return obj.length === 0;
    for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
    return true;
  };

  // Is a given value a DOM element?
  _.isElement = function(obj) {
    return !!(obj && obj.nodeType == 1);
  };

  // Is a given value an array?
  // Delegates to ECMA5's native Array.isArray
  _.isArray = nativeIsArray || function(obj) {
    return !!(obj && obj.concat && obj.unshift);
  };

  // Is a given variable an arguments object?
  _.isArguments = function(obj) {
    return obj && _.isNumber(obj.length) && !obj.concat && !obj.substr && !obj.apply && !propertyIsEnumerable.call(obj, 'length');
  };

  // Is a given value a function?
  _.isFunction = function(obj) {
    return !!(obj && obj.constructor && obj.call && obj.apply);
  };

  // Is a given value a string?
  _.isString = function(obj) {
    return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
  };

  // Is a given value a number?
  _.isNumber = function(obj) {
    return (obj === +obj) || (toString.call(obj) === '[object Number]');
  };

  // Is a given value a boolean?
  _.isBoolean = function(obj) {
    return obj === true || obj === false;
  };

  // Is a given value a date?
  _.isDate = function(obj) {
    return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
  };

  // Is the given value a regular expression?
  _.isRegExp = function(obj) {
    return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
  };

  // Is the given value NaN -- this one is interesting. NaN != NaN, and
  // isNaN(undefined) == true, so we make sure it's a number first.
  _.isNaN = function(obj) {
    return _.isNumber(obj) && isNaN(obj);
  };

  // Is a given value equal to null?
  _.isNull = function(obj) {
    return obj === null;
  };

  // Is a given variable undefined?
  _.isUndefined = function(obj) {
    return typeof obj == 'undefined';
  };

  // -------------------------- Utility Functions: ----------------------------

  // Run Underscore.js in noConflict mode, returning the '_' variable to its
  // previous owner. Returns a reference to the Underscore object.
  _.noConflict = function() {
    root._ = previousUnderscore;
    return this;
  };

  // Keep the identity function around for default iterators.
  _.identity = function(value) {
    return value;
  };

  // Run a function n times.
  _.times = function (n, iterator, context) {
    for (var i = 0; i < n; i++) iterator.call(context, i);
  };

  // Break out of the middle of an iteration.
  _.breakLoop = function() {
    throw breaker;
  };

  // Add your own custom functions to the Underscore object, ensuring that
  // they're correctly added to the OOP wrapper as well.
  _.mixin = function(obj) {
    each(_.functions(obj), function(name){
      addToWrapper(name, _[name] = obj[name]);
    });
  };

  // Generate a unique integer id (unique within the entire client session).
  // Useful for temporary DOM ids.
  var idCounter = 0;
  _.uniqueId = function(prefix) {
    var id = idCounter++;
    return prefix ? prefix + id : id;
  };

  // By default, Underscore uses ERB-style template delimiters, change the
  // following template settings to use alternative delimiters.
  _.templateSettings = {
    start       : '<%',
    end         : '%>',
    interpolate : /<%=(.+?)%>/g
  };

  // JavaScript templating a-la ERB, pilfered from John Resig's
  // "Secrets of the JavaScript Ninja", page 83.
  // Single-quote fix from Rick Strahl's version.
  // With alterations for arbitrary delimiters.
  _.template = function(str, data) {
    var c  = _.templateSettings;
    var endMatch = new RegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g");
    var fn = new Function('obj',
      'var p=[],print=function(){p.push.apply(p,arguments);};' +
      'with(obj){p.push(\'' +
      str.replace(/[\r\t\n]/g, " ")
         .replace(endMatch,"\t")
         .split("'").join("\\'")
         .split("\t").join("'")
         .replace(c.interpolate, "',$1,'")
         .split(c.start).join("');")
         .split(c.end).join("p.push('")
         + "');}return p.join('');");
    return data ? fn(data) : fn;
  };

  // ------------------------------- Aliases ----------------------------------

  _.each     = _.forEach;
  _.foldl    = _.inject       = _.reduce;
  _.foldr    = _.reduceRight;
  _.select   = _.filter;
  _.all      = _.every;
  _.any      = _.some;
  _.head     = _.first;
  _.tail     = _.rest;
  _.methods  = _.functions;

  // ------------------------ Setup the OOP Wrapper: --------------------------

  // If Underscore is called as a function, it returns a wrapped object that
  // can be used OO-style. This wrapper holds altered versions of all the
  // underscore functions. Wrapped objects may be chained.
  var wrapper = function(obj) { this._wrapped = obj; };

  // Helper function to continue chaining intermediate results.
  var result = function(obj, chain) {
    return chain ? _(obj).chain() : obj;
  };

  // A method to easily add functions to the OOP wrapper.
  var addToWrapper = function(name, func) {
    wrapper.prototype[name] = function() {
      var args = _.toArray(arguments);
      unshift.call(args, this._wrapped);
      return result(func.apply(_, args), this._chain);
    };
  };

  // Add all of the Underscore functions to the wrapper object.
  _.mixin(_);

  // Add all mutator Array functions to the wrapper.
  each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
    var method = ArrayProto[name];
    wrapper.prototype[name] = function() {
      method.apply(this._wrapped, arguments);
      return result(this._wrapped, this._chain);
    };
  });

  // Add all accessor Array functions to the wrapper.
  each(['concat', 'join', 'slice'], function(name) {
    var method = ArrayProto[name];
    wrapper.prototype[name] = function() {
      return result(method.apply(this._wrapped, arguments), this._chain);
    };
  });

  // Start chaining a wrapped Underscore object.
  wrapper.prototype.chain = function() {
    this._chain = true;
    return this;
  };

  // Extracts the result from a wrapped and chained object.
  wrapper.prototype.value = function() {
    return this._wrapped;
  };

})();


// Acat.array.js
// general purpose array functions
// part of Acat namespace


// create namespace
if (!window.Acat){
  Acat = {};
}
if (!Acat.array){
  Acat.array = {};
}


// extend namespace
(function (AcatArray) {

  return $.extend(AcatArray, {


  // STABLE implementation of quick sort to replace unstable Array.sort method in Firefox
  // (equivalent values will maintain original order)
  // if sorting an array of objects, key = name of object property to compare
  // otherwise leave key undefined
  quickSort: function(arr, key) {

    // return if array is unsortable
    if (arr.length <= 1){
      return arr;
    }

    var less = [], greater = [];
    
    // select and remove a pivot value pivot from array
    // a pivot value closer to median of the dataset may result in better performance
    var pivotIndex = Math.floor(arr.length / 2);
    var pivot = arr.splice(pivotIndex, 1)[0];
    
    // step through all array elements
    for (var x = 0; x < arr.length; x++){
    
      // if (current value is less than pivot),
      // OR if (current value is the same as pivot AND this index is less than the index of the pivot in the original array)
      // then push onto end of less array
      if (
        (
          !key  // no object property name passed
          &&
          (
            (arr[x] < pivot)
            ||
            (arr[x] == pivot && x < pivotIndex)  // this maintains the original order of values equal to the pivot
          )
        )
        ||
        (
          key  // object property name passed
          &&
          (
            (arr[x][key] < pivot[key])
            ||
            (arr[x][key] == pivot[key] && x < pivotIndex)  // this maintains the original order of values equal to the pivot
          )
        )
      ){
        less.push(arr[x]);
      }
      
      // if (current value is greater than pivot),
      // OR if (current value is the same as pivot AND this index is greater than or equal to the index of the pivot in the original array)
      // then push onto end of greater array
      else {
        greater.push(arr[x]);
      }
    }
    
    // concatenate less+pivot+greater arrays
    return this.quickSort(less, key).concat([pivot], this.quickSort(greater, key));
  },

  



  
  
  // sort array numerically
  sortNum: function(arr){
    arr.sort(function(a,b){
      if (typeof a == 'number' && typeof b == 'number'){
        return a - b;
      }
      else {
        return 0;
      }
    });
    
    return arr;
  },


  
  
  // sort array of objects by passed property
  sortByKey: function(arr, key, subkey, direction) {

    if (subkey){
      return arr.sort(function sortNumericSubKey(a,b) {
        if (direction=="desc") {
          if (a[subkey][key] < b[subkey][key])
            return 1;
          else if (a[subkey][key] > b[subkey][key])
            return -1;
          else
            return 0;
        } else {
          if (a[subkey][key] < b[subkey][key])
            return -1;
          else if (a[subkey][key] > b[subkey][key])
            return 1;
          else
            return 0;
        }
      });
    }
    
    else {
      return arr.sort(function sortNumeric(a,b) {
        if (direction=="desc") {
          if (a[key] < b[key])
            return 1;
          else if (a[key] > b[key])
            return -1;
          else
            return 0;
        } else {
          if (a[key] < b[key]){
            return -1;
          }
          else if (a[key] == b[key]){
            return 0;
          }
          else if (a[key] > b[key]){
            return 1;
          }
        }
      });
    }

    
  },
  
  
  
  
  // alternate implementation of array.forEach functionality
  // equivalent functionality to javascript >= v1.6, plus:
  // if a non-array object is passed, attempt to iterate through object properties in ALPHABETICAL order, as if this were an associative array
  //   (to give consistent, predictable output, since property order is undefined in Javascript spec, and may vary by browser and property definition sequence)
  // avoids throwing errors unless passed function is invalid (indicating a design bug) - if passed a non-object, do nothing
  
  // USAGE
  
  // var arr = [20,10,30];
  // var obj = {a:10, c:30, b:20};
  // var func = function(value, index, collection){ console.log(index + ' = ' + value); };
  
  // forEach(arr, func);    // OUTPUTS "0 = 20, 1 = 10, 2 = 30"
  
  // forEach(obj, func);    // OUTPUTS "a = 10, b = 20, c = 30"
  
  forEach: function(array, fun) {
    if (typeof fun != "function") {
      throw new TypeError();
    }

    if (typeof array == 'object'){
      var thisp = arguments[1];
        
      if (undefined == array.length){ // passed object is not an array
        
        // get list of properties
        var names = [];
        var nameAsNum;
        var i;
        
        for (i in array){
          if (array.hasOwnProperty(i) && typeof array[i] != 'function'){
            // attempt to add numbers as numbers, not as strings, to preserve numeric sort order
            nameAsNum = Number(i);
            if (!isNaN(nameAsNum)){
              names.push(nameAsNum);
            }
            else {
              names.push(i);
            }
          }
        }
        
        // alphabetize
        //names.sort();
        names = this.quickSort(names);
        
        // step through according to property list
        for (i = 0; i < names.length; i++){
          fun.call(thisp, array[names[i]], names[i], array);
        }
      }
      else { // passed object is an array
        var len = array.length;

        for (var i = 0; i < len; i++) {
          if (i in array) {
            fun.call(thisp, array[i], i, array);
          }
        }
      }
    }
  },



  

  // search for the existence of a passed value in an array

  // USAGE

  // var myArray = [11,42,89];
  // existsIn(myArray, 42); // returns true

  existsIn: function(array, search_term, strict) {
    if (array == undefined || !array.length || search_term == undefined){
      return false;
    }
    else {
      var h;
      // var log = false;
      
      if (!(search_term instanceof Array)){
        search_term = [search_term];
      }
      // else {
        // log = true;
      // }
      for (h = 0; h < search_term.length; h++){
        var i = array.length;
        if (i > 0) {
      	  while (i--) {
            if (typeof search_term[h] == 'object'){
              if (this.objectCompare(search_term[h], array[i], !strict)){
                return true;
              }
            }
            else {
              if (strict && array[i] === search_term[h]) { // use strict type checking
                return true;
              }
              else if (!strict && array[i] == search_term[h]) { // use loose type checking
                return true;
              }
            }
      	  }
        }
      }
      
      return false;
    }
  },


  
  keyExists: function(obj, key){
    if (obj.hasOwnProperty(key)){
      return true;
    }
    return false;
  },

  
  
  // push all passed values onto end of passed array IF they are Boolean(TRUE)
  // accepts any number of arguments after array argument
  // only returns false if no items added
  pushIf: function(array){
    var args = Array.prototype.slice.call(arguments);
    var added = false;
    
    if (array === undefined || !(array instanceof Array) || args.length < 2){
      return false;
    }
    
    var i;
    for (i = 1; i < args.length; i++){
      if (args[i]){
        added = true;
        array.push(args[i]);
      }
    }
    
    return added;
  },
  
  
  
  // return array of all keys in passed array (or object)
  getKeys: function(obj){
    var keys = [];
    for (var i in obj){
      if (obj.hasOwnProperty(i) && typeof obj[i] != 'function'){
        keys.push(i);
      }
    }
    return keys;
  },
  
  
  // return array of all values in passed array (or object)
  // useful to reindex array with index gaps,
  // or convert associative array (object) to indexed array
  getValues: function(obj){
    var values = [];
    for (var i in obj){
      if (obj.hasOwnProperty(i) && typeof obj[i] != 'function'){
        values.push(obj[i]);
      }
    }
    return values;
  },
  
  
  
  
  // return array of all values in passed obj that are >= min AND <= max
  // if exclusive is true, match must be > min AND < max, but NOT equal
  getRange: function(obj, min, max, exclusive){
    var results = [];
    
    for (var i in obj){
      if (
        !exclusive
        &&
        (
          obj[i] >= min
          &&
          obj[i] <= max
        )
      ){
        results.push(obj[i]);
      }

      else if (
        obj[i] > min
        &&
        obj[i] < max
      ){
        results.push(obj[i]);
      }
    }
      
    return results; 
  },

  

  
  
  // returns property of an object based on location within object
  
  // WARNING: there is no "correct" way to order properties - that is, recently defined properties
  // won't necessarily follow previously defined properties, so results may vary by browser
  
  // target is zero-based "index" of desired value
  // target can be passed as an integer, or a matrix of integers for multidimensional objects
  
  // USAGE
  
  // var o1 = {
  //    a1: {
  //      a2a: 'test a2a',
  //      a2b: 'test a2b'
  //    },
  //    b1: {
  //      b2a: 'test b2a',
  //      b2b: 'test b2b'
  //    }
  // };

  // var result = getPropertyByIndex(o1, [1,0]));    // returns 'test b2a', first child-property of second parent-property
  
  getPropertyByIndex: function(obj, targetIndex){
    var currentIndex = -1;
    if (!(targetIndex instanceof Array)){
      targetIndex = [targetIndex];
    }
    
    for (var key in obj){
      currentIndex++;
      
      fblog(['currentIndex = ', currentIndex, ', targetIndex = [', targetIndex.join(','), ']'].join(''));
      
      if (currentIndex == targetIndex[0]){
      
        fblog(['currentIndex == targetIndex[0] (', targetIndex[0], ')'].join(''));
        
        if (targetIndex.length > 1){
        
          fblog(['targetIndex.length > 1 (', targetIndex.join(','), ')'].join(''));
        
          targetIndex.splice(0,1);
          return this.getPropertyByIndex(obj[key], targetIndex);
        }
        else {
        
          fblog(['found ', var_dump(obj[key])].join(''));
          return obj[key];
        }
      }
    }
    
    return false;
  },
  
  
  
  
  
  
  
  // returns a list of all properties of passed object
  // doesn't include inherited properties or functions
  
  getPropertyNames: function (obj){
    var names = [];
    var i;
    
    for (i in obj){
      if (obj.hasOwnProperty(i) && typeof obj[i] != 'function'){
        names.push(i);
      }
    }
    
    return names;
  },
  
  
  
  
  
  
  
  // push value or object onto end of array only if not already present
  // if key passed, try to match objects by this key name
  // if key matches, extend present object with passed object

  // USAGE

  // var myArray = [11,42,89];
  // pushUnique(myArray, 42); // returns false, doesn't touch myArray
  // pushUnique(myArray, 37); // return true, myArray is now = [11,42,89,37]
  
  pushUnique: function(array, value, key) {
  
    var index;
    
    // if key passed, try to match objects by this key name
    if (key){
      var i, length = array.length, found = false;
      for (i = 0; i < length; i++){
        if (array[i][key] == value[key]){
          found = true;
          _.extend(array[i], value);
        }
      }
      
      if (!found){
        array.push(value);
      }
    }
    
    // just try to match values
    else {
      index = this.indexOf(array, value);
      
      if (index == -1){
        array.push(value);
      }
      
      return index;
      
      // if (!this.existsIn(array, value)){
        // array.push(value);
        // return true;
      // }
      // else {
        // return false;
      // }
    }
  },
  
  
  
  concatUnique: function(array1, array2, key){
    if (!(array1 instanceof Array)){
      return;
    }
    if (!(array2 instanceof Array)){
      array2 = [array2];
    }
    
    for (var i = 0, length = array2.length; i < length; i++){
      this.pushUnique(array1, array2[i], key);
    }
  },






  // remove a known value (with unknown index) from an array

  // USAGE

  // myArray = [1,2,3,4];
  // remove(myArray, 3);  // now myArray = [1,2,4];

  removeByValue: function(array, s){
    if (!(s instanceof Array)){
      s = [s];
    }
    
    var i, j;
    
    for (i in s){
      
      if (this.existsIn(array, s[i])){
        for (j = array.length - 1; j >= 0;){
          if (s[i] == array[j]){
            array.splice(j, 1);
          }
          j--;
        }
      }
      
    }
  },



  // ------------



  // toggle value in an array
  // if value doesn't exist in array, add it to the end
  // if value DOES exist in array, remove it

  // USAGE

  // myArray = [2,3,5,7]
  // toggle(myArray, 4); // now myArray = [2,3,5,7,4];
  // toggle(myArray, 3); // now myArray = [2,5,7,4];

  toggle: function(array, s){
    if (this.existsIn(array, s)){
      this.removeByValue(array, s);
    }
    else {
      array.push(s);
    }
  },



  // ------------

  
  
  // cycle through an array of values
  // returns next element in passed array after passed current element
  // if no current element passed, returns first array element
  
  cycle: function(array, current){
    if (!(array instanceof Array)){
      return false;
    }

    if (current == undefined){
      return array[0];
    }
    
    if (!this.existsIn(array, current)){
      return false;
    }
    
    var index = this.indexOf(array, current);
    if (index + 1 != array.length){
      return array[index + 1];
    }
    else {
      return array[0];
    }
  },
  


  // ------------



  // compare passed array with this
  // compares by index, so the following is true:
  //   [2,3,5,7] == [2,3,5,7]
  // while the following is false:
  //   [2,7,3,5] == [2,3,5,7]

  // to compare contained values regardless of order,
  // sort() compared arrays first

  // USAGE

  // myArrayA = [2,3,5,7]
  // myArrayB = [2,7,3,5]
  // sameAs(myArrayA, myArrayB); // false
  // sameAs(myArrayA.sort(), myArrayB.sort()); // true

  sameAs: function(target, candidate) {
    if (target.length != candidate.length) return false;
    //this = this.sort();
    //arr = arr.sort();
    
    var i, length = candidate.length;
    
    for (i = 0; i < length; i++) {
      if (target[i] instanceof Array) { // possibly nested array
        if (!this.sameAs(target[i], candidate[i])) return false;
        else continue;
      }
      if (target[i] != candidate[i]) {
        return false;
      }
    }
    return true;
  },


  
  
  
  
  
  // define missing indexOf function in IE
  indexOf: function(array, elt, from){
    var len = array.length;

    from = Number(from) || 0;
    from = (from < 0)
         ? Math.ceil(from)
         : Math.floor(from);
    if (from < 0) {
      from += len;
    }

    for (; from < len; from++){
      if (from in array && array[from] === elt)
        return from;
    }
    return -1;
  },
  
  
  

  // ------------



  // search an array of objects for a specific object id
  // offset is starting position
  // //returns zero-based index, or -1 if not found
  // return array of all found indexes (or empty array if none found)

  // USAGE

  // myArray = [ {id: 23, value: "exampleA"},
  //             {id: 34, value: "exampleB"} ];
  // indexOfObjectByKey(myArray, 34, 'id'); // returns [1]
  // indexOfObjectByKey(myArray, 57, 'id'); // returns []

  indexOfObjectByKey: function(array, value, keyName, offset) {
    if (!keyName){
      keyName = 'id';
    }
    
    var found = [];
    
    var i, length = array.length;
    for (i = offset || 0; i < length; i++) {
      if (array[i][keyName] == value) {
        found.push(i);
      }
    }

    return found;
  },



  
  // ------------




  keyOfObject: function(object, value, keyName){
    var found = [];
    
    for (var i in object){
      if (object[i][keyName] == value){
        found.push(i);
      }
    }
    
    return found;
  },
  
  
  
  
  
  /* ------------

  search an array of objects for a specific object id
  returns object associated with id, or empty array if not found

  USAGE

  myArray = [ {id: 23, value: "exampleA"},
              {id: 34, value: "exampleB"} ];
  findObjectByKey(myArray, 34, 'id'); // returns [{id: 34, value: "exampleB"}]
  findObjectByKey(myArray, 34, 'id', ['value']); // returns [{value: "exampleB"}]
  findObjectByKey(myArray, 57, 'id'); // returns []

  NOTES
  
  returnKeys is an array of keys (properties) from found object to be returned

  value can be an object, containing:
  
    value: single value to be tested
    
    start & end: lower and upper bounds (inclusive) of range to be tested
    
    opposite: should be set to true to return all NON-matching results
    
    callback: custom function to call instead of built-in testing
              this will be passed two parameters:
              
                array element to compare against
              
                passed value to test
  
  single should be set to TRUE to only return (first found element, or FALSE if no elements found)
  
  */

  findObjectByKey: function(array, value, keyName, returnKeys, single) {
    if (
      !array
      ||
      !array.length
      ||
      !(typeof array == 'object' && "length" in array && array.length)
      ||
      value == null
    ){
      return (single ? false : []);
    }
  
    if (!keyName){
      keyName = 'id';
    }

    var i;
    var range = false;
    var callback = false;
    var opposite = false;
    var valueObject = {};
    var found = [];
    
    if (value instanceof Array){
      for (i = 0; i < value.length; i++){
        found = found.concat(this.findObjectByKey(array, value[i], keyName, returnKeys));
      }
      return (single ? (found.length ? found[0] : false) : found);
    }
    else if (typeof value == 'object'){
      valueObject = value;
      
      if (valueObject.start && valueObject.end){
        range = true;
      }
      else if (valueObject.value){
        value = valueObject.value;
      }
      
      if (valueObject.callback && typeof valueObject.callback == 'function'){
        callback = valueObject.callback;
      }
      
      if (valueObject.opposite){
        opposite = true;
      }
    }
    
    // evals are very time-consuming, so only use if keyName contains a subkey (e.g., 'parent.key', rather than just 'key')
    
    if (callback){
      
      if (keyName.indexOf('.') != -1){
        var keys = keyName.split('.');
        if (keys.length == 2){
          for (i = 0; i < array.length; i++) {
            if (callback(array[i][keys[0]][keys[1]], value)){
              found.push(array[i]);
            }
          }
        }
        else if (keys.length == 3){
          for (i = 0; i < array.length; i++) {
            if (callback(array[i][keys[0]][keys[1]][keys[2]], value)){
              found.push(array[i]);
            }
          }
        }
        else {
          for (i = 0; i < array.length; i++) {
            if (eval(['array[i].', keyName, ' == value'].join(''))) {
              found.push(array[i]);
            }
          }
        }
      }
      else {
        for (i = 0; i < array.length; i++){
          if (callback(array[i][keyName], value)){
            found.push(array[i]);
          }
        }
      }
      
    } // end if callback
    
    
    else { // if not callback

      if (!opposite){
        
        if (range){
          if (keyName.indexOf('.') != -1){
            var keys = keyName.split('.');
            if (keys.length == 2){
              for (i = 0; i < array.length; i++) {
                if (array[i][keys[0]][keys[1]] >= value.start && array[i][keys[0]][keys[1]] <= value.end){
                  found.push(array[i]);
                }
              }
            }
            else if (keys.length == 3){
              for (i = 0; i < array.length; i++) {
                if (array[i][keys[0]][keys[1]][keys[2]] >= value.start && array[i][keys[0]][keys[1]][keys[2]] <= value.end){
                  found.push(array[i]);
                }
              }
            }
            else {
              for (i = 0; i < array.length; i++) {
                if(eval(['array[i].', keyName, ' >= value.start && array[i].', keyName, ' <= value.end'].join(''))) {
                  found.push(array[i]);
                }
              }
            }
          }
          else {
            for (i = 0; i < array.length; i++) {
              if (array[i][keyName] >= value.start && array[i][keyName] <= value.end){
                found.push(array[i]);
              }
            }
          }
        }
        else {
          if (keyName.indexOf('.') != -1){
            var keys = keyName.split('.');
            if (keys.length == 2){
              for (i = 0; i < array.length; i++) {
                if (array[i][keys[0]][keys[1]] == value){
                  found.push(array[i]);
                }
              }
            }
            else if (keys.length == 3){
              for (i = 0; i < array.length; i++) {
                if (array[i][keys[0]][keys[1]][keys[2]] == value){
                  found.push(array[i]);
                }
              }
            }
            else {
              for (i = 0; i < array.length; i++) {
                if (eval(['array[i].', keyName, ' == value'].join(''))) {
                  found.push(array[i]);
                }
              }
            }
          }
          else {
            for (i = 0; i < array.length; i++) {
              if (
                array[i]
                &&
                (
                  array[i][keyName] == value
                  ||
                  (array[i][keyName] instanceof Array && this.existsIn(array[i][keyName], value))
                )
              ){
                found.push(array[i]);
              }
            }
          }
        }
      }
        
      
      else { // if opposite

        if (range){
          if (keyName.indexOf('.') != -1){
            var keys = keyName.split('.');
            if (keys.length == 2){
              for (i = 0; i < array.length; i++) {
                if (array[i][keys[0]][keys[1]] < value.start || array[i][keys[0]][keys[1]] > value.end){
                  found.push(array[i]);
                }
              }
            }
            else if (keys.length == 3){
              for (i = 0; i < array.length; i++) {
                if (array[i][keys[0]][keys[1]][keys[2]] < value.start || array[i][keys[0]][keys[1]][keys[2]] > value.end){
                  found.push(array[i]);
                }
              }
            }
            else {
              for (i = 0; i < array.length; i++) {
                if(eval(['array[i].', keyName, ' < value.start || array[i].', keyName, ' > value.end'].join(''))) {
                  found.push(array[i]);
                }
              }
            }
          }
          else {
            for (i = 0; i < array.length; i++) {
              if (array[i][keyName] < value.start || array[i][keyName] > value.end){
                found.push(array[i]);
              }
            }
          }
        }
        else { // if not range
          if (keyName.indexOf('.') != -1){
            var keys = keyName.split('.');
            if (keys.length == 2){
              for (i = 0; i < array.length; i++) {
                if (array[i][keys[0]][keys[1]] != value){
                  found.push(array[i]);
                }
              }
            }
            else if (keys.length == 3){
              for (i = 0; i < array.length; i++) {
                if (array[i][keys[0]][keys[1]][keys[2]] != value){
                  found.push(array[i]);
                }
              }
            }
            else {
              for (i = 0; i < array.length; i++) {
                if (eval(['array[i].', keyName, ' != value'].join(''))) {
                  found.push(array[i]);
                }
              }
            }
          }
          else {
            for (i = 0; i < array.length; i++) {
              if (
                array[i][keyName] != value
                ||
                (array[i][keyName] instanceof Array && !this.existsIn(array[i][keyName], value))
              ){
                found.push(array[i]);
              }
            }
          }
        }
      }
      

      
    } // end if not callback
    
    if (returnKeys && returnKeys.length){
      var processed = [];
      var j;

      for (i = 0; i < found.length; i++){
        processed[i] = {};
        for (j = 0; j < returnKeys.length; j++){
          processed[i][returnKeys[j]] = found[i].data[returnKeys[j]];
        }
      }
      
      return (single ? (processed.length ? processed[0] : false) : processed);
    }
    else {
      return (single ? (found.length ? found[0] : false) : found);
    }
  },


  
  
  
  
  
  
  
  // ------------



  
  
  getSubset: function(arr, returnKeys, subKey){
    if (returnKeys && returnKeys.length){
      var processed = [];
      var i, j, keyIndex;

      if (subKey){
        subKey = subKey.split('.');
        var thisItem;
        for (i = 0; i < arr.length; i++){
          thisItem = arr[i];
          for (keyIndex = 0; keyIndex < subKey.length; keyIndex++){
            thisItem = thisItem[subKey[keyIndex]];
          }
          processed[i] = {};
          for (j = 0; j < returnKeys.length; j++){
            processed[i][returnKeys[j]] = thisItem[returnKeys[j]];
          }
        }
      }
      else {
        for (i = 0; i < arr.length; i++){
          processed[i] = {};
          for (j = 0; j < returnKeys.length; j++){
            processed[i][returnKeys[j]] = arr[i][returnKeys[j]];
          }
        }
      }
      
      return processed;
    }
    else {
      return arr;
    }
  },
  
  
  
  
  
  
  
  
  // ------------



  
  
  // remove an object from an array by specified key
  // will not change indexes

  removeObjectByKey: function(array, value, keyName) {

    var indexes = this.indexOfObjectByKey(array, value, keyName);
    
    if (indexes.length){
      for (var i = indexes.length - 1; i >= 0; i--){
        array.splice(indexes[i], 1);
      }
    }
    
    return indexes.length;
  },
  
  
  
  
  
  // ------------



  
  
  // remove an object from an array by specified key
  // will not change indexes

  removeObjectByKeys: function(array, config) {
    
    var indexes = this.indexOfObjectByKeys(array, config);
    
    if (indexes.length){
      for (var i = indexes.length - 1; i >= 0; i--){
        array.splice(indexes[i], 1);
      }
    }
    
    return indexes.length;
  },

  
  


  // ------------


  
  // returns number of member properties of passed object
  // same as number of elements in associative array
  
  objectLength: function(obj){
    var count = 0;
    
    for (var i in obj){
      if (obj.hasOwnProperty(i) && typeof obj[i] != 'function'){
        count ++;
      }
    }
    
    return count;
  },
  
  
  
  // ------------



  // search an array of objects by multiple keys
  // returns array of object(s) associated with keys, or empty array if not found
  // if 'or' parameter set to true, only one of the passed search terms must be found
  // otherwise all matches must be found

  // USAGE

  // myArray = [ {id: 1, category: 'foo', value: "exampleA"},
  //             {id: 2, category: 'foo', value: "exampleA"},
  //             {id: 3, category: 'bar', value: "exampleB"},
  //             {id: 4, category: 'bar', value: "exampleC"} ];
  // findObjectByKeys(myArray, {category: 'foo', value: 'exampleA'} ); // returns [{id: 1, category: 'foo', value: 'exampleA'}, {id: 2, category: 'foo', value: 'exampleA'}]
  // findObjectByKeys(myArray, {category: 'bar', value: 'exampleZ'} ); // returns []

  findObjectByKeys: function(array, search, or) {
    var found = [];
    var i, j, k;
    var match;
    
    // all terms must be found
    for (i = 0; i < array.length; i++) {
      match = false;
      
      loopfor:
      for (j in search){
        // if passed search parameter is an object containing 'value' and 'rel' properties,
        // evaluate as a relative comparison
        // if (typeof search[j] == 'object' && !(search[j] instanceof String) && search[j].value && search[j].rel){
        if (search[j] != null && typeof search[j] == 'object' && search[j].value && search[j].rel){
          switch (search[j].rel){
            case 'eq':
              if (array[i][j] == search[j].value) {
                match = true;
              }
              else if (!or){
                match = false;
                break loopfor;
              }
              continue loopfor;
              
            case 'neq':
              if (array[i][j] != search[j].value) {
                match = true;
              }
              else if (!or){
                match = false;
                break loopfor;
              }
              continue loopfor;

            case 'gt':
              if (array[i][j] > search[j].value) {
                match = true;
              }
              else if (!or){
                match = false;
                break loopfor;
              }
              continue loopfor;
            
            case 'gte':
              if (array[i][j] >= search[j].value) {
                match = true;
              }
              else if (!or){
                match = false;
                break loopfor;
              }
              continue loopfor;

            case 'lt':
              if (array[i][j] < search[j].value) {
                match = true;
              }
              else if (!or){
                match = false;
                break loopfor;
              }
              continue loopfor;
              
            case 'lte':
              if (array[i][j] <= search[j].value) {
                match = true;
              }
              else if (!or){
                match = false;
                break loopfor;
              }
              continue loopfor;
              
            // in array of search terms?
            case 'in':
              if (this.existsIn(search[j].value, array[i][j])){
                match = true;
              }
              else if (!or){
                match = false;
                break loopfor;
              }
              continue loopfor;
              
            case 'notin':
              if (!this.existsIn(search[j].value, array[i][j])){
                match = true;
              }
              else if (!or){
                match = false;
                break loopfor;
              }
              continue loopfor;
              
            default:
              
          }
        }
        
        else if (typeof search[j] == 'function'){
          if (search[j](array[i][j])){
            match = true;
          }
          else if (!or){
            match = false;
            break loopfor;
          }
        }
      
        else if (j.indexOf('.') != -1){
          var keys = j.split('.');
          if (keys.length == 2){
            if (array[i][keys[0]][keys[1]] == search[j]){
              match = true;
            }
          }
          else if (keys.length == 3){
            if (array[i][keys[0]][keys[1]][keys[2]] == search[j]){
              match = true;
            }
          }
          else {
            if (eval(['array[i].', keyName, ' == search[j]'].join(''))) {
              match = true;
            }
          }
        }
            
        else if (array[i][j] == search[j]) {
          match = true;
        }
        
        else if (!or) {
          match = false;
          break loopfor;
        }
      }

      if (match){
        found.push(array[i]);
      }
    }
    
    // // any terms may be found
    // else {
      // for (i = 0; i < array.length; i++) {
        // match = false;
        // for (j in search){
          // if (array[i][j] == search[j]) {
            // match = true;
            // break;
          // }
          // else {
            // //match = false;
            // //break;
          // }
        // }

        // if (match){
          // found.push(array[i]);
        // }
      // }
    // }

    return found;
  },
  
  
  
  
  
  // ------------



  
  
  indexOfObjectByKeys: function(array, search, or){
    var found = [];
    var match;
    var i, j;
    
    // all terms must be found
    // if (!or){
      for (i = 0; i < array.length; i++) {
        match = false;
        
        loopfor:
        for (j in search){
          // if (array[i][j] != search[j]) {
            // match = false;
            // break;
          // }

          if (j.indexOf('.') != -1){
            var keys = j.split('.');
            if (keys.length == 2){
              if (array[i][keys[0]][keys[1]] == search[j]){
                match = true;
              }
            }
            else if (keys.length == 3){
              if (array[i][keys[0]][keys[1]][keys[2]] == search[j]){
                match = true;
              }
            }
            else {
              if (eval(['array[i].', keyName, ' == search[j]'].join(''))) {
                match = true;
              }
            }
          }
              
          else if (array[i][j] == search[j]) {
            match = true;
          }
          else if (!or) {
            match = false;
            break loopfor;
          }
        }

        if (match){
          found.push(i);
        }
      }
    // }
    
    // // any terms may be found
    // else {
      // for (i = 0; i < array.length; i++) {
        // var match = false;
        // for (j = 0; j < search.length; j++){
          // if (array[i][j] == search[j]) {
            // match = true;
            // break;
          // }
        // }

        // if (match){
          // found.push(i);
        // }
      // }
    // }

    return found;
  },
  
  
  
  
  
  
  
  // ------------
  
  
  
  
  // clone (make new instance of) an object
  
  // USAGE
  
  // obj1 = {'key1': 1, 'key2': 2};
  // obj2 = new objectClone(obj1);

  objectClone: function(obj) {
    if (
      typeof obj == 'string'
      ||
      typeof obj == 'boolean'
      ||
      typeof obj == 'number'
    ){
      return obj;
    }
    
    var newObj = {};
    
    for (i in obj) {
      if (obj.hasOwnProperty(i)){
        if (obj[i] == null){
          newObj[i] = null;
        }  
        else if (typeof obj[i] == 'object') {
          if (obj[i] instanceof Date){
            newObj[i] = obj[i].clone();
          }
          else {
            newObj[i] = this.objectClone(obj[i]);
          }
        }
        else {
          newObj[i] = obj[i];
        }
      }
    }
    
    return newObj;
    
    
    // for (i in obj) {
      // if (typeof obj[i] == 'object') {
          // this[i] = new objectClone(obj[i]);
      // }
      // else {
          // this[i] = obj[i];
      // }
    // }

  },
  
  
  



  // ------------



  // compare two objects recursively
  // doesn't rely on specifics of object creation or property order
  // performs strict type comparison on values by default, unless loose is set to TRUE
  
  // USAGE
  
  // obj1 = {'key1': 1, 'key2': 2};
  // obj2 = {'key2': 2, 'key1': 1};
  // objectCompare(obj1, obj2); // returns true
  
  // obj1 = {'a': 0, 'b': ''};
  // obj2 = {'b': null}; // don't even declare an 'a' property, so it will be undefined - note that number of properties is ignored when doing loose comparison
  // objectCompare(obj1, obj2, true); // returns true... for 'a', 0 == undefined, and for 'b', '' == null
  
  // will even compare arrays:
  // obj1 = [0, 1];
  // obj2 = [null, '1'];
  // objectCompare(obj1, obj2, true); // returns true
  
  // or even arrays of objects:
  // obj1 = [{'a': 1}, {'b': '0'}];
  // obj2 = [{'a': '1'}, {'b': null}];
  // objectCompare(obj1, obj2, true); // returns true
  
  // note that arrays are compared by index, so out-of-sequence arrays should be sorted before comparing

  objectCompare: function(obj1, obj2, loose) {
  
    // if loose is NOT TRUE, first do a quick comparison of lengths
    // quick & dirty attempt to avoid comparing all property values on large or complex objects
    var count1 = 0, count2 = 0;
    if (!loose){
      for (i in obj1){
        count1++;
      }
      for (i in obj2){
        count2++;
      }
    }
    
    if (count1 != count2){ // lengths are different, so no need to compare values
      // Acat.jq.fblog('objectCompare(): different lengths');
      return false;
    }
    else { // lengths are the same (or loose is TRUE), so continue to compare individual property values
      for (i in obj1) {
        if (typeof obj1[i] == 'object' && typeof obj2[i] == 'object') { // both items are objects
          if (!this.objectCompare(obj1[i], obj2[i], loose)){ // see if objects match
            return false;
          }
        }
        else if (
          loose // loose comparison, 42 == "42", null == '' == undefined
          &&
          !(
            (obj1[i] || '') == (obj2[i] || '') // this will work in all cases EXCEPT ("0" ?= 0|null|undefined), which will evaluate to ("0" ?= ""), which is of course FALSE
            ||
            ( // if either value is a number, try to convert both to numbers and compare - this will work around above exception
              (typeof obj1[i] == 'number' || typeof obj2[i] == 'number')
              &&
              Number(obj1[i]) == Number(obj2[i])
            )
          )
        ){ // only one item is an object, or objects don't match
          // Acat.jq.fblog(['objectCompare(', i, '): |', obj1[i], '| != |', obj2[i], '| (loose match)'].join(''));
          return false;
        }
        else if (
          !loose // strict comparison, 42 != "42", null != '' != undefined
          &&
          !(obj1[i] === obj2[i])
        ){
          // Acat.jq.fblog(['objectCompare(', i, '): |', obj1[i], '| !== |', obj2[i], '| (strict match)'].join(''));
          return false;
        }
      }
    }
    
    return true; // all tests passed, objects are equivalent
  },

  
  
  
  
  
  // update properties of target with same properties of source
  // if target property doesn't exist, create with value of source property
  // if source property doesn't exist, retain target property
  
  objectMerge: function(target, source){
    if (target && source){
      var i;
      
      for (i in source) {
        if (typeof source[i] == 'object'){
          if (
            target[i] === undefined
            ||
            typeof target[i] != 'object'
          ){
            target[i] = new this.objectClone(source[i]);
          }
          else {
            this.objectMerge(target[i], source[i]);
          }
        }
        else {
          target[i] = source[i];
        }
        
      }
      
    }
    
  },
  
  
  
  
  
  
  isset: function() {
    // http://kevin.vanzonneveld.net
    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: FremyCompany
    // *     example 1: isset( undefined, true);
    // *     returns 1: false
    // *     example 2: isset( 'Kevin van Zonneveld' );
    // *     returns 2: true
 
    var a=arguments; var l=a.length; var i=0;
    
    while ( i!=l ) {
      if (typeof(a[i])=='undefined') { 
        return false; 
      } 
      else { 
        i++; 
      }
    }
    
    return true;
  },
  
  
  
  // approximate equivalent to PHP's isset function
  // major difference is that the NAME of the object must be passed,
  // rather than the object itself (which may not even exist)
  isset2: function(objName){
    try {
      if (eval(objName) != undefined){
        return true;
      }
      else {
        return false;
      }
    }
    catch(e){
      return false;
    }
  },
  
  
  
  
  // return contents of object as JSON-ish string
  // set addwhitespace = 'html' to return with HTML-formatted indents and linebreaks
  var_dump: function(data, addwhitespace, safety, level, objName) {
    var rtrn = '';
    var dt,it,spaces = '';
    if(!level) {level = 1;}
    
    for(var i=0; i<level; i++) {
      spaces = [spaces, '    '].join('');
    }//end for i<level
    
    if(typeof(data) != 'object' || data === null) {
      dt = data;
      if(typeof(data) == 'string') {
        if(addwhitespace == 'html') {
          dt = dt.replace(/&/g,'&amp;');
          dt = dt.replace(/>/g,'&gt;');
          dt = dt.replace(/</g,'&lt;');
        }//end if addwhitespace == html
        dt = dt.replace(/\"/g,'\"');
        dt = ['"', dt, '"'].join('');
      }//end if typeof == string
      if(typeof(data) == 'function'){ // && addwhitespace) {
        dt = '&lt;function&gt;';
        // dt = new String(dt).replace(/\n/g, ["\n", spaces].join(''));
        // if(addwhitespace == 'html') {
          // dt = dt.replace(/&/g,'&amp;');
          // dt = dt.replace(/>/g,'&gt;');
          // dt = dt.replace(/</g,'&lt;');
        // }//end if addwhitespace == html
      }//end if typeof == function
      if(data === undefined) {
        dt = '&lt;undefined&gt;';
      }//end if typeof == undefined
      if (data === null){
        dt = '&lt;null&gt;';
      }
      if(addwhitespace == 'html') {
        if(typeof(dt) != 'string') {
          dt = new String(dt);
        }//end typeof != string
        dt = dt.replace(/ /g,"&nbsp;").replace(/\n/g,"<br>");
      }//end if addwhitespace == html
      return dt;
    }//end if typeof != object && != array
    
    var propertyNames = [];
    for (var x in data){
      propertyNames.push(x);
    }
    
    propertyNames.sort();
    
    // for (var x in data) {
    for (var i = 0; i < propertyNames.length; i++) {
      var x = propertyNames[i];
      if(safety && (level > safety)) {
        dt = (data[x].length ? ['(', data[x].length, ' items)'].join('') : '*RECURSION*');
      } else {
        try {
          dt = this.var_dump(data[x],addwhitespace,safety,level+1);
        } catch (e) {continue;}
      }//end if-else level > safety
      it = x; //var_dump(x,addwhitespace,safety,level+1);
      rtrn = [rtrn, it, ': ', dt, ','].join('');
      if(addwhitespace) {
        rtrn = [rtrn, '\n', spaces].join('');
      }//end if addwhitespace
    }//end for...in
    
    if(addwhitespace) {
      rtrn = [[objName ? [objName, ' (Object) = '].join('') : ''], ['{\n', spaces, rtrn.substr(0,rtrn.length-(2+(level*4))), '\n', spaces.substr(0,spaces.length-4), '}'].join('')];
      if(addwhitespace == 'html') {
        rtrn[1] = rtrn[1].replace(/ /g,"&nbsp;").replace(/\n/g,"<br>");
      }//end if addwhitespace == html
      rtrn = rtrn.join('');
    } else {
      temp1 = ['{', rtrn.substr(0,rtrn.length-1), '}'].join('');
    }//end if-else addwhitespace
    
    
    
    
    return rtrn;
  },  //end function var_dump  
  
  
  
  
  
  // return name of class of passed object
  // "String", "Number", "Array", etc.
  classOf: function(o) {
    if (undefined === o) return "Undefined";
    if (null === o) return "Null";
    return {}.toString.call(o).slice(8, -1);
  },
  
  
  
  
  isArray: function(o) {
    // return (classOf(o) == 'Array');
    return (undefined !== o.length);
  },
  
  
  
  // convert passed value, object, or array-like object to a true array
  // for objects, only converts numerically indexed properties
  toArray: function(o) {
  
    // if this is an object
    if (typeof o == 'object'){
      // if this is an array-like object
      if (o.length){
        return Array.prototype.slice.call(o, 0);
      }
      // otherwise step through properties, adding numerically indexed properties to new array
      else {
        var count = 0;
        var arr = [];
        for (var i in o){
          if (!isNaN(Number(i))){
            arr[Number(i)] = o[i];
            count++;
          }
        }
        return arr;
      }
    }
    else if (!o){
      return [];
    }
    // if this is not an object, just create an array with this as its only element
    else {
      return [o];
    }


  }

  
  
  
  }); // end return statement

})(Acat.array);

// Acat.date.js
// general purpose date/time functions
// part of Acat namespace


// create namespace
if (!window.Acat){
  Acat = {};
}
if (!Acat.date){
  Acat.date = {};
}


// extend namespace
(function (AcatDate) {

  return $.extend(AcatDate, {
  
  // simple profiler mechanism
  // tracks 
  stopwatchStart: function(profileName){
    if (!this.stopwatches){
      this.stopwatches = [];
    }
    return this.stopwatches[profileName] = new Date().valueOf();
  },
  
  // return difference of existing timestamp and current time,
  // set timestamp to current time
  stopwatchSegment: function(profileName){
    var time = new Date().valueOf();
    var seg = time - this.stopwatches[profileName];
    this.stopwatches[profileName] = time;
    return seg;
  },

  stopwatchEnd: function(profileName){
    return new Date().valueOf() - this.stopwatches[profileName];
  },


  // convert MySQL DATETIME value (such as "1999-12-31 01:23:45")
  // to a javascript Date object
  
  mysqlToJS: function(timestamp) {
    var regex=/^([0-9]{2,4})-([0-1][0-9])-([0-3][0-9]) (?:([0-2][0-9]):([0-5][0-9]):([0-5][0-9]))?$/;
    var parts=timestamp.replace(regex,"$1 $2 $3 $4 $5 $6").split(' ');
    return new Date(parts[0],parts[1]-1,parts[2],parts[3],parts[4],parts[5]);
  },
  
  
  
  
  
  sqlGetDate: function(timestamp){
    var pieces = timestamp.split(' ');
    if (pieces && pieces.length){
      return pieces[0];
    }
    
    return timestamp;
  },
  
  sqlGetYear: function(timestamp){
    var pieces = timestamp.split(' ');
    if (pieces && pieces.length){
      var pieces2 = pieces[0].split('-');
      if (pieces2 && pieces2.length){
        return pieces2[0];
      }
    }

    return false;
  },

  sqlGetYearShort: function(timestamp){
    var pieces = timestamp.split(' ');
    if (pieces && pieces.length){
      var pieces2 = pieces[0].split('-');
      if (pieces2 && pieces2.length){
        return pieces2[0].substring(2);
      }
    }

    return false;
  },

  sqlGetMonth: function(timestamp){
    var pieces = timestamp.split(' ');
    if (pieces && pieces.length){
      var pieces2 = pieces[0].split('-');
      if (pieces2 && pieces2.length){
        return pieces2[1];
      }
    }

    return false;
  },

  sqlGetDay: function(timestamp){
    var pieces = timestamp.split(' ');
    if (pieces && pieces.length){
      var pieces2 = pieces[0].split('-');
      if (pieces2 && pieces2.length){
        return pieces2[2];
      }
    }

    return false;
  },

  sqlGetTime: function(timestamp){
    var pieces = timestamp.split(' ');
    if (pieces && pieces.length && pieces.length > 1){
      return pieces[1];
    }
    
    return timestamp;
  },
  
  // get hour from mysql timestamp (i.e., '2008-01-02 15:59:00')
  sqlGetHour: function(timestamp){
    var pieces = timestamp.split(':');
    if (pieces && pieces.length){
      var pieces2 = pieces[0].split(' ');
      if (pieces2 && pieces2.length && pieces2.length > 1){
        return pieces2[1];
      }
    }

    return false;
  },

  
  // get hour from mysql timestamp (i.e., '2008-01-02 15:59:00')
  sqlSetHour: function(timestamp, hour){
    var pieces = timestamp.split(':');
    if (pieces && pieces.length){
      var pieces2 = pieces[0].split(' ');
      if (pieces2 && pieces2.length && pieces2.length > 1){
        var output = [pieces2[0], ' ', Acat.string.zeroPad(hour,2), ':', pieces[1], ':', pieces[2]].join('');
        return output;
      }
    }

    return false;
  },
  
  
  sqlClearTime: function(timestamp){
    var pieces = timestamp.split(' ');
    if (pieces && pieces.length){
      return [pieces[0], ' 00:00:00'].join('');
    }
    
    return false;
  },
  
  
  
  
  // convert integer seconds to formatted string
  // e.g.: "2 days, 3 hours, 14 minutes, 12 seconds"
  
  // to drop trailing hours, minutes, or seconds (and any less significant elements)
  //   params.roundTo = days|hours|minutes
  // note that this doesn't truly round off, it only drops the remainder, so "1 day, 23 hours, 59 minutes, 59 seconds" with roundTo = "day" will become "1 day"
  
  secondsToString: function(seconds, params){
    var rem, d = 0, h = 0, m = 0, s = 0, strArr = [], force = false;
    
    rem = seconds % 86400;
    d = (seconds - rem) / 86400;
    seconds = rem;
    rem = rem % 3600;
    h = (seconds - rem) / 3600;
    seconds = rem;
    rem = rem % 60;
    m = (seconds - rem) / 60;
    s = rem % 60;
    
    if (d){
      force = true;
      strArr.push([d, ' day', d == 1 ? '' : 's'].join(''));
    }
    if ((h || force)  && !(params.roundTo == 'days')){
      force = true;
      strArr.push([h, ' hour', h == 1 ? '' : 's'].join(''));
    }
    if ((m || force) && !((params.roundTo == 'days') || (params.roundTo == 'hours'))){
      force = true;
      strArr.push([m, ' minute', m == 1 ? '' : 's'].join(''));
    }
    if ((s || force) && !((params.roundTo == 'days') || (params.roundTo == 'hours') || (params.roundTo == 'minutes'))){
      strArr.push([s, ' second', s == 1 ? '' : 's'].join(''));
    }
    
    return strArr.join(', ');
  },
  
  
  
  // return timestamp string (no date) for the current time
  timestamp: function(){
    return new Date().toString('HH:mm:ss.fff');
  }

  
  
  }); // end return
  
})(Acat.date);


// dropshadow support functions

if (!window.Acat){
  Acat = {};
}

Acat.dropShadow = function () {


  // fill array with all elements matching className
  getElementsByClass = function(className, tagType) {
    // if no tagType specified, default to any
    if (!tagType) {
      tagType = "*";
    }
    
    var all = document.all ? document.all : document.getElementsByTagName(tagType);
    var elements = new Array();
    
    // create new regexp object to allow incorporating passed class name
    var re = new RegExp(['\\b', className, '\\b'].join(''), 'i');
    
    for (var e = 0; e < all.length; e++) {
      // test regular expression against this element's class name
      if (re.test(all[e].className)) {
        elements.push(all[e]);
      }
    }
    
    return elements;
  };

  // adjust any oversize captions to same width as associated images
  sizeCaptionsToImages = function() {
    var captions = getElementsByClass("caption", "div");

    for (var i = 0; i < captions.length; i++) {
      // if this caption DIV immediately follows an IMG
      if (captions[i].previousSibling && captions[i].previousSibling.tagName == "IMG") {
        // set caption DIV width to same as adjacent IMG width
        captions[i].style.width = [captions[i].previousSibling.clientWidth, "px"].join('');
        var temp = captions[i].previousSibling.style.display;
        captions[i].previousSibling.style.display = "none";
        captions[i].previousSibling.style.display = "block";
        //captions[i].previousSibling.style.display = temp;
        //captions[i].parentNode.style.width = "50px";
      }
    }
  };
  
  return {
    getElementsByClass: getElementsByClass,
    sizeCaptionsToImages: sizeCaptionsToImages
  };
  
}(); // execute immediately


// Acat.fire.js
// helper functions to call firebug when available
// prevents script errors in IE


// create namespace
if (!window.Acat){
  Acat = {};
}
if (!Acat.fire){
  Acat.fire = {};
}


// extend namespace
(function (AcatFire) {

  if (window.loadFirebugConsole){
    window.loadFirebugConsole();
  }
  
  return $.extend(AcatFire, {
  
  // safely call firebug only if available
  log: function(string){
    if (!(undefined === window.console)){
      try {
        window.console.log(string);
      }
      catch (e) {
      }
    }
  },
  
  dir: function(variable){
    if (!(undefined === window.console)){
      // must use exception handling for Chrome
      // console.dir does not seem to be defined correctly
      try {
        window.console.dir(variable);
      }
      catch (e){
      }
    }
  }
  
  
  });
})(Acat.fire);
    
  

// Acat.footlinks.js
// convert document link URLs to print-only footnotes
// part of Acat namespace


// create namespace
if (!window.Acat){
  Acat = {};
}
if (!Acat.footLinks){
  Acat.footLinks = {};
}


// extend namespace
(function (AcatFootLinks) {

  return $.extend(AcatFootLinks, {


  
  // run footnote creator
  
  // config = {
  //   linkContainerInclude/ExcludeSelector: css selector of DOM object(s) which contains links to be converted/ignored
  //   linkInclude/ExcludeClass: class name for links to be converted/ignored
  //   appendTo: jQuery object, or selector of DOM object, to which to append footnotes
  //   notesReferenceClass: class name to apply to reference numbers within content
  //   notesContainerClass: class name to apply to footnotes container DIV
  //   notesTitleClass: class name to apply to footnotes title paragraph
  //   notesTitle: text to use as footnotes title
  //   
  // }
  
  init: function(config){

    // initialize config parameter
    if (!config){
      config = {};
    }
    
    $.extend({
      linkContainerIncludeSelector: '',
      linkContainerExcludeSelector: '',
      linkIncludeClass: '',
      linkExcludeClass: '',
      
      appendTo: 'body',
      
      notesReferenceClass: '',
      
      notesContainerClass: '',
      notesTitleClass: '',
      notesTitle: '',
      notesListClass: '',
      notesItemClass: ''
    }, config);
    
    
    // get all links meeting include/exclude restrictions
    var linksSelector = [
      config.linkContainerIncludeSelector,
      ' a',
      (config.linkIncludeClass ? ['.', config.linkIncludeClass].join('') : ''),
      '[href]:not([href^=#],[href^=mailto]',
      (config.linkContainerExcludeSelector ? [',', config.linkContainerExcludeSelector, ' a'].join('') : ''),
      (config.linkExcludeClass ? [',[class~=', config.linkExcludeClass, ']'].join('') : ''),
      ')'
    ].join('');
    
    // $('body').append(linksSelector);
    
    var links = $(linksSelector);
    
    if (!links.length){
      return;
    }
    
    
    // append screen-only css style to document head which will be applied to footnotes and reference numbers
    // this will ensure they are only visible on printed version
    $('head').append('<style type="text/css" media="screen">.footlinksPrintonly { display: none; }</style>');

    
    var output = [];
    var urls = [];
    var noteCount = 0;
    
    // step through found links
    $.each(links, function(i){
      var linkText = $(this).text();
      var linkValue = $(this).attr('href');
      if (
        linkValue.substring(0,5) !== 'http:'
      ){
        linkValue = ['http://', document.location.host, (linkValue.substring(0,1) !== '/' ? '/' : ''), linkValue].join('');
      }
      
      var index = Acat.array.pushUnique(urls, linkValue);
      
      if (index == -1){
        noteCount++;
      }
      
      // append link reference number to link element
      $(['<span class="footlinksPrintonly ', config.notesReferenceClass, '">', ' [', ((index == -1) ? noteCount : (index + 1)), ']', '</span>'].join('')).appendTo($(this));

      if (index == -1){
        output.push('<li class="', config.notesItemClass, '">', linkValue, '</li>');
      }
      
    });    
    
    
    // build complete output string around footnote list
    output = [
      '<div class="footlinksPrintonly ', config.notesContainerClass, '">',
      (config.notesTitle ? ['<p class="', config.notesTitleClass, '">', config.notesTitle, '</p>'].join('') : ''),
      '<ol class="', config.notesListClass, '">'
    ].concat(output, ['</ol></div>']);
    
    
    // append output string to DOM
    if (typeof config.appendTo == 'string'){
      config.appendTo = $(config.appendTo);
    }
    
    if (config.appendTo instanceof $ && config.appendTo.length){
      config.appendTo.append(output.join(''));
    }
    
  }
  
  }); // end return
  
})(Acat.footLinks);

// Acat.math.js
// general purpose math functions
// part of Acat namespace


// create namespace
if (!window.Acat){
  Acat = {};
}
if (!Acat.math){
  Acat.math = {};
}


// extend namespace
(function (AcatMath) {

  return $.extend(AcatMath, {
  
  
  round: function(val, precision) {
    if (!precision){
      precision = 1;
    }
    else {
      precision = Math.pow(10, precision);
    }
    
    return Math.round(val * precision) / precision;
  }

  
  }); // end return statement

})(Acat.math);

// Acat.string.js
// general purpose string functions
// part of Acat namespace


// define namespace
if (!window.Acat){
  Acat = {};
}
if (!Acat.string){
  Acat.string = {};
}


// extend namespace
(function (AcatString) {

  return $.extend(AcatString, {



  // search for the existence of a passed value in an array

  // USAGE

  // var myArray = [11,42,89];
  // existsIn(myArray, 42); // returns true

  echoIf: function(str, prepend, append, alternative, startPos, endPos) {
    var newstr;
    startPos = Number(startPos);
    endPos = Number(endPos);
    
    if (str){
      if (!prepend){
        prepend = '';
      }
      if (!append){
        append = '';
      }
      newstr = [prepend, str, append].join('');
    }
    else {
      if (!alternative){
        alternative = '';
      }
      newstr = alternative;
    }
    
    if (startPos){
      if (endPos){
        newstr = newstr.slice(startPos, endPos);
      }
      else {
        newstr = newstr.slice(startPos);
      }
    }
    else if (endPos){
      newstr = newstr.slice(0, endPos);
    }
    
    return newstr;
  },


  
  pushIf: function (arr, str, prepend, append, alternative){
    if (!str || !arr){
      return;
    }
    
    var i, newStr = '';

    if (!prepend){
      prepend = '';
    }
    if (!append){
      append = '';
    }

    if (typeof str == 'string'){
      str = [str];
    }

    for (i = 0; i < str.length; i++){

      if (str[i]){
        newStr = str[i];
      }
      else if (alternative){
        newStr = alternative[i];
      }
      else {
        continue;
      }
      
      if (newStr){
          
        arr.push([prepend, newStr, append].join(''));
        newStr = '';
      }
    }
  },
  
  
  
  
  /* To Title Case 1.1.1
   * David Gouch <http://individed.com>
   * 23 May 2008
   * License: http://individed.com/code/to-title-case/license.txt
   *
   * In response to John Gruber's call for a Javascript version of his script: 
   * http://daringfireball.net/2008/05/title_case
   */

  toTitleCase: function(string){
    if (!string){
      return '';
    }
    
    return string.replace(/([\w&`'‘’"“.@:\/\{\(\[<>_]+-? *)/g, function(match, p1, index, title) {
      if (index > 0 && title.charAt(index - 2) !== ":" &&
        match.search(/^(a(nd?|s|t)?|b(ut|y)|en|for|i[fn]|o[fnr]|t(he|o)|vs?\.?|via)[ \-]/i) > -1)
          return match.toLowerCase();
      if (title.substring(index - 1, index + 1).search(/['"_{(\[]/) > -1)
          return match.charAt(0) + match.charAt(1).toUpperCase() + match.substr(2);
      if (match.substr(1).search(/[A-Z]+|&|[\w]+[._][\w]+/) > -1 || 
        title.substring(index - 1, index + 1).search(/[\])}]/) > -1)
          return match;
      return [match.charAt(0).toUpperCase(), match.substr(1)].join('');
    });
  },  
  
  
  
  // returns string with first character of each sentence capitalized
  toSentenceCase: function(string){
    return [string.charAt(0).toUpperCase(), string.substr(1)].join('');
  },
  
  
  
  toUpperCase: function(string){
    return string.toUpperCase();
  },
  



  // returns version of passed string with any non-alphanumeric characters
  // changed to hyphens
  // useful for safe DOM element id names
  hyphenize: function(string){
    return string.replace(/[^\w]/g, "-");
  },
  
  
  
  // replace all instances of needle in haystack with replacement
  // removes needle if no replacement passed
  replaceAll: function(haystack, needle, replacement){
    if (!replacement){
      replacement = '';
    }
    var re = new RegExp(needle, 'g');
    return haystack.replace(re, replacement);
  },
  
  
  
  // searchFormat: function(haystack, needle, ignored, prepend, append){
    // var found = [];
    // var offset;
    // var i;
    // var regex;
    
    // haystack = $.trim(haystack);
    // needle = $.trim(needle);
    
    // var ignoredHaystack = haystack, ignoredNeedle = needle;
    // var ignoredRemoved = [];
    
    // if (!haystack || !needle){
      // return false;
    // }
    
    // if (!ignored){
      // ignored = [];
    // }
    
    // offset = 0;
    // for (i = 0; i < ignored.length; i++){
      // regex = new RegExp(ignored[i]);
      // ignoredHaystack = haystack.substr(offset);
      // i = haystack.search(regex);
      
      // while (i != -1){ // get index of next match, -1 if no match
        // ignoredRemoved[i] = 
        // offset += ignored[i].length;
        // i = haystack.search(regex);
      // }
    // }
    
    
    
    
    
    
    
  // }
  
  
  
  // determine if passed needle is a whole word in haystack
  // for haystack "I like food", will return true if needle = 'food',
  // but false if needle = 'foo'
  wordIn: function(haystack, needle, delimiter){
    if (haystack && needle){
    
      if (!delimiter){
        delimiter = ' ';
      }
      
      var arr = haystack.split(delimiter);
      if (Acat.array.existsIn(arr, needle)){
        return true;
      }

    }
    
    return false;
  },
  
  
  
  addSlashes: function (str) {
    str=str.replace(/\\/g,'\\\\');
    str=str.replace(/\'/g,'\\\'');
    str=str.replace(/\"/g,'\\"');
    str=str.replace(/\0/g,'\\0');
    return str;
  },

  stripSlashes: function(str) {
    str=str.replace(/\\0/g,'\0');
    str=str.replace(/\\"/g,'"');
    str=str.replace(/\\'/g,'\'');
    str=str.replace(/\\\\/g,'\\');
    return str;
  },

  
  
  
  // add thousands separator to passed numerical string
  
  // USAGE
  
  // var num = 1234;
  // var withCommas = '$' + addCommas(num.toFixed(2));  // results in "$1,234.00"
  
  addCommas: function(str){
    var sRegExp = new RegExp('(-?[0-9]+)([0-9]{3})');

    while(sRegExp.test(str)) {
      str = str.replace(sRegExp, '$1,$2');
    }
    return str;
  },
  
  
  
  
  // escape special characters in RegExp string
  // useful before converting to RegExp
  escapeRegExp: function(s) {
    return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1')
  },

  
  
  
  
  
  
  urlEncode: function (string) {
    string = string.replace(/\r\n/g,"\n");
    var utftextArray = [];

    for (var n = 0; n < string.length; n++) {

      var c = string.charCodeAt(n);

      if (c < 128) {
          utftextArray.push(String.fromCharCode(c));
      }
      else if((c > 127) && (c < 2048)) {
          utftextArray.push(String.fromCharCode((c >> 6) | 192));
          utftextArray.push(String.fromCharCode((c & 63) | 128));
      }
      else {
          utftextArray.push(String.fromCharCode((c >> 12) | 224));
          utftextArray.push(String.fromCharCode(((c >> 6) & 63) | 128));
          utftextArray.push(String.fromCharCode((c & 63) | 128));
      }

    }

    return escape(utftextArray.join('').replace(/ /g,"+"));
  },


  urlDecode: function (utftext) {
    utftext = unescape(utftext);
    utftext = utftext.replace(/\+/g," ");

    var stringArray = [];
    var i = 0;
    var c = c1 = c2 = 0;

    while ( i < utftext.length ) {

        c = utftext.charCodeAt(i);

        if (c < 128) {
            stringArray.push(String.fromCharCode(c));
            i++;
        }
        else if((c > 191) && (c < 224)) {
            c2 = utftext.charCodeAt(i+1);
            stringArray.push(String.fromCharCode(((c & 31) << 6) | (c2 & 63)));
            i += 2;
        }
        else {
            c2 = utftext.charCodeAt(i+1);
            c3 = utftext.charCodeAt(i+2);
            stringArray.push(String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)));
            i += 3;
        }

    }

    return stringArray.join('');
  },
  
  
  
  
  htmlEncode: function (html){
    return html
      .replace(/&/g,'&amp;')
      .replace(/</g,'&lt;')
      .replace(/>/g,'&gt;')
      .replace(/'/g,'&#039;')
      .replace(/"/g,'&quot;'); 
  },
  
  
  htmlDecode: function (plaintext){
    return plaintext
      .replace(/&amp;/g,'&')
      .replace(/&lt;/g,'<')
      .replace(/&gt;/g,'>')
      .replace(/&apos;/g,"'")
      .replace(/&#039;/g,"'")
      .replace(/&quot;/g,'"');
  },
  
  
  
  
  zeroPad: function(n, width){
  	n = n.toString();
    var padArray = [];
  	while (n.length + padArray.length < width) {
  		padArray.push('0');
  	}
    padArray.push(n);
  	return padArray.join('');
  }
  
  
  
  });

})(Acat.string);
/**
 * AJAX Upload ( http://valums.com/ajax-upload/ ) 
 * Copyright (c) Andris Valums
 * Licensed under the MIT license ( http://valums.com/mit-license/ )
 * Thanks to Gary Haran, David Mark, Corey Burns and others for contributions 
 */
(function () {
    /* global window */
    /* jslint browser: true, devel: true, undef: true, nomen: true, bitwise: true, regexp: true, newcap: true, immed: true */
    
    /**
     * Wrapper for FireBug's console.log
     */
    function log(){
        if (typeof(console) != 'undefined' && typeof(console.log) == 'function'){            
            Array.prototype.unshift.call(arguments, '[Ajax Upload]');
            console.log( Array.prototype.join.call(arguments, ' '));
        }
    } 

    /**
     * Attaches event to a dom element.
     * @param {Element} el
     * @param type event name
     * @param fn callback This refers to the passed element
     */
    function addEvent(el, type, fn){
        if (el.addEventListener) {
            el.addEventListener(type, fn, false);
        } else if (el.attachEvent) {
            el.attachEvent('on' + type, function(){
                fn.call(el);
	        });
	    } else {
            throw new Error('not supported or DOM not loaded');
        }
    }   
    
    /**
     * Attaches resize event to a window, limiting
     * number of event fired. Fires only when encounteres
     * delay of 100 after series of events.
     * 
     * Some browsers fire event multiple times when resizing
     * http://www.quirksmode.org/dom/events/resize.html
     * 
     * @param fn callback This refers to the passed element
     */
    function addResizeEvent(fn){
        var timeout;
               
	    addEvent(window, 'resize', function(){
            if (timeout){
                clearTimeout(timeout);
            }
            timeout = setTimeout(fn, 100);                        
        });
    }    
    
    // Needs more testing, will be rewriten for next version        
    // getOffset function copied from jQuery lib (http://jquery.com/)
    if (document.documentElement.getBoundingClientRect){
        // Get Offset using getBoundingClientRect
        // http://ejohn.org/blog/getboundingclientrect-is-awesome/
        var getOffset = function(el){
            var box = el.getBoundingClientRect();
            var doc = el.ownerDocument;
            var body = doc.body;
            var docElem = doc.documentElement; // for ie 
            var clientTop = docElem.clientTop || body.clientTop || 0;
            var clientLeft = docElem.clientLeft || body.clientLeft || 0;
             
            // In Internet Explorer 7 getBoundingClientRect property is treated as physical,
            // while others are logical. Make all logical, like in IE8.	
            var zoom = 1;            
            if (body.getBoundingClientRect) {
                var bound = body.getBoundingClientRect();
                zoom = (bound.right - bound.left) / body.clientWidth;
            }
            
            if (zoom > 1) {
                clientTop = 0;
                clientLeft = 0;
            }
            
            var top = box.top / zoom + (window.pageYOffset || docElem && docElem.scrollTop / zoom || body.scrollTop / zoom) - clientTop, left = box.left / zoom + (window.pageXOffset || docElem && docElem.scrollLeft / zoom || body.scrollLeft / zoom) - clientLeft;
            
            return {
                top: top,
                left: left
            };
        };        
    } else {
        // Get offset adding all offsets 
        var getOffset = function(el){
            var top = 0, left = 0;
            do {
                top += el.offsetTop || 0;
                left += el.offsetLeft || 0;
                el = el.offsetParent;
            } while (el);
            
            return {
                left: left,
                top: top
            };
        };
    }
    
    /**
     * Returns left, top, right and bottom properties describing the border-box,
     * in pixels, with the top-left relative to the body
     * @param {Element} el
     * @return {Object} Contains left, top, right,bottom
     */
    function getBox(el){
        var left, right, top, bottom;
        var offset = getOffset(el);
        left = offset.left;
        top = offset.top;
        
        right = left + el.offsetWidth;
        bottom = top + el.offsetHeight;
        
        return {
            left: left,
            right: right,
            top: top,
            bottom: bottom
        };
    }
    
    /**
     * Helper that takes object literal
     * and add all properties to element.style
     * @param {Element} el
     * @param {Object} styles
     */
    function addStyles(el, styles){
        for (var name in styles) {
            if (styles.hasOwnProperty(name)) {
                el.style[name] = styles[name];
            }
        }
    }
        
    /**
     * Function places an absolutely positioned
     * element on top of the specified element
     * copying position and dimentions.
     * @param {Element} from
     * @param {Element} to
     */    
    function copyLayout(from, to){
	    var box = getBox(from);
        
        addStyles(to, {
	        position: 'absolute',                    
	        left : box.left + 'px',
	        top : box.top + 'px',
	        width : from.offsetWidth + 'px',
	        height : from.offsetHeight + 'px'
	    });        
    }

    /**
    * Creates and returns element from html chunk
    * Uses innerHTML to create an element
    */
    var toElement = (function(){
        var div = document.createElement('div');
        return function(html){
            div.innerHTML = html;
            var el = div.firstChild;
            return div.removeChild(el);
        };
    })();
            
    /**
     * Function generates unique id
     * @return unique id 
     */
    var getUID = (function(){
        var id = 0;
        return function(){
            return 'ValumsAjaxUpload' + id++;
        };
    })();        
 
    /**
     * Get file name from path
     * @param {String} file path to file
     * @return filename
     */  
    function fileFromPath(file){
        return file.replace(/.*(\/|\\)/, "");
    }
    
    /**
     * Get file extension lowercase
     * @param {String} file name
     * @return file extenstion
     */    
    function getExt(file){
        return (-1 !== file.indexOf('.')) ? file.replace(/.*[.]/, '') : '';
    }

    function hasClass(el, name){        
        var re = new RegExp('\\b' + name + '\\b');        
        return re.test(el.className);
    }    
    function addClass(el, name){
        if ( ! hasClass(el, name)){   
            el.className += ' ' + name;
        }
    }    
    function removeClass(el, name){
        var re = new RegExp('\\b' + name + '\\b');                
        el.className = el.className.replace(re, '');        
    }
    
    function removeNode(el){
        el.parentNode.removeChild(el);
    }

    /**
     * Easy styling and uploading
     * @constructor
     * @param button An element you want convert to 
     * upload button. Tested dimentions up to 500x500px
     * @param {Object} options See defaults below.
     */
    window.AjaxUpload = function(button, options){
        this._settings = {
            // Location of the server-side upload script
            action: 'upload.php',
            // File upload name
            name: 'userfile',
            // Additional data to send
            data: {},
            // Submit file as soon as it's selected
            autoSubmit: true,
            // The type of data that you're expecting back from the server.
            // html and xml are detected automatically.
            // Only useful when you are using json data as a response.
            // Set to "json" in that case. 
            responseType: false,
            // Class applied to button when mouse is hovered
            hoverClass: 'hover',
            // Class applied to button when AU is disabled
            disabledClass: 'disabled',            
            // When user selects a file, useful with autoSubmit disabled
            // You can return false to cancel upload			
            onChange: function(file, extension){
            },
            // Callback to fire before file is uploaded
            // You can return false to cancel upload
            onSubmit: function(file, extension){
            },
            // Fired when file upload is completed
            // WARNING! DO NOT USE "FALSE" STRING AS A RESPONSE!
            onComplete: function(file, response){
            }
        };
                        
        // Merge the users options with our defaults
        for (var i in options) {
            if (options.hasOwnProperty(i)){
                this._settings[i] = options[i];
            }
        }
                
        // button isn't necessary a dom element
        if (button.jquery){
            // jQuery object was passed
            button = button[0];
        } else if (typeof button == "string") {
            if (/^#.*/.test(button)){
                // If jQuery user passes #elementId don't break it					
                button = button.slice(1);                
            }
            
            button = document.getElementById(button);
        }
        
        if ( ! button || button.nodeType !== 1){
            throw new Error("Please make sure that you're passing a valid element"); 
        }
                
        if ( button.nodeName.toUpperCase() == 'A'){
            // disable link                       
            addEvent(button, 'click', function(e){
                if (e && e.preventDefault){
                    e.preventDefault();
                } else if (window.event){
                    window.event.returnValue = false;
                }
            });
        }
                    
        // DOM element
        this._button = button;        
        // DOM element                 
        this._input = null;
        // If disabled clicking on button won't do anything
        this._disabled = false;
        
        // if the button was disabled before refresh if will remain
        // disabled in FireFox, let's fix it
        this.enable();        
        
        this._rerouteClicks();
    };
    
    // assigning methods to our class
    AjaxUpload.prototype = {
        setData: function(data){
            this._settings.data = data;
        },
        disable: function(){            
            addClass(this._button, this._settings.disabledClass);
            this._disabled = true;
            
            var nodeName = this._button.nodeName.toUpperCase();            
            if (nodeName == 'INPUT' || nodeName == 'BUTTON'){
                this._button.setAttribute('disabled', 'disabled');
            }            
            
            // hide input
            if (this._input){
                // We use visibility instead of display to fix problem with Safari 4
                // The problem is that the value of input doesn't change if it 
                // has display none when user selects a file           
                this._input.parentNode.style.visibility = 'hidden';
            }
        },
        enable: function(){
            removeClass(this._button, this._settings.disabledClass);
            this._button.removeAttribute('disabled');
            this._disabled = false;
            
        },
        /**
         * Creates invisible file input 
         * that will hover above the button
         * <div><input type='file' /></div>
         */
        _createInput: function(){ 
            var self = this;
                        
            var input = document.createElement("input");
            input.setAttribute('type', 'file');
            input.setAttribute('name', this._settings.name);
            
            addStyles(input, {
                'position' : 'absolute',
                // in Opera only 'browse' button
                // is clickable and it is located at
                // the right side of the input
                'right' : 0,
                'margin' : 0,
                'padding' : 0,
                'fontSize' : '480px',                
                'cursor' : 'pointer'
            });            

            var div = document.createElement("div");                        
            addStyles(div, {
                'display' : 'block',
                'position' : 'absolute',
                'overflow' : 'hidden',
                'margin' : 0,
                'padding' : 0,                
                'opacity' : 0,
                // Make sure browse button is in the right side
                // in Internet Explorer
                'direction' : 'ltr',
                //Max zIndex supported by Opera 9.0-9.2
                'zIndex': 2147483583
            });
            
            // Make sure that element opacity exists.
            // Otherwise use IE filter            
            if ( div.style.opacity !== "0") {
                if (typeof(div.filters) == 'undefined'){
                    throw new Error('Opacity not supported by the browser');
                }
                div.style.filter = "alpha(opacity=0)";
            }            
            
            addEvent(input, 'change', function(){
                 
                if ( ! input || input.value === ''){                
                    return;                
                }
                            
                // Get filename from input, required                
                // as some browsers have path instead of it          
                var file = fileFromPath(input.value);
                                
                if (false === self._settings.onChange.call(self, file, getExt(file))){
                    self._clearInput();                
                    return;
                }
                
                // Submit form when value is changed
                if (self._settings.autoSubmit) {
                    self.submit();
                }
            });            

            addEvent(input, 'mouseover', function(){
                addClass(self._button, self._settings.hoverClass);
            });
            
            addEvent(input, 'mouseout', function(){
                removeClass(self._button, self._settings.hoverClass);
                
                // We use visibility instead of display to fix problem with Safari 4
                // The problem is that the value of input doesn't change if it 
                // has display none when user selects a file           
                input.parentNode.style.visibility = 'hidden';

            });   
                        
	        div.appendChild(input);
            document.body.appendChild(div);
              
            this._input = input;
        },
        _clearInput : function(){
            if (!this._input){
                return;
            }            
                             
            // this._input.value = ''; Doesn't work in IE6                               
            removeNode(this._input.parentNode);
            this._input = null;                                                                   
            this._createInput();
            
            removeClass(this._button, this._settings.hoverClass);
        },
        /**
         * Function makes sure that when user clicks upload button,
         * the this._input is clicked instead
         */
        _rerouteClicks: function(){
            var self = this;
            
            // IE will later display 'access denied' error
            // if you use using self._input.click()
            // other browsers just ignore click()

            addEvent(self._button, 'mouseover', function(){
                if (self._disabled){
                    return;
                }
                                
                if ( ! self._input){
	                self._createInput();
                }
                
                var div = self._input.parentNode;                            
                copyLayout(self._button, div);
                div.style.visibility = 'visible';
                                
            });
            
            
            // commented because we now hide input on mouseleave
            /**
             * When the window is resized the elements 
             * can be misaligned if button position depends
             * on window size
             */
            //addResizeEvent(function(){
            //    if (self._input){
            //        copyLayout(self._button, self._input.parentNode);
            //    }
            //});            
                                         
        },
        /**
         * Creates iframe with unique name
         * @return {Element} iframe
         */
        _createIframe: function(){
            // We can't use getTime, because it sometimes return
            // same value in safari :(
            var id = getUID();            
             
            // We can't use following code as the name attribute
            // won't be properly registered in IE6, and new window
            // on form submit will open
            // var iframe = document.createElement('iframe');
            // iframe.setAttribute('name', id);                        
 
            var iframe = toElement('<iframe src="javascript:false;" name="' + id + '" />');
            // src="javascript:false; was added
            // because it possibly removes ie6 prompt 
            // "This page contains both secure and nonsecure items"
            // Anyway, it doesn't do any harm.            
            iframe.setAttribute('id', id);
            
            iframe.style.display = 'none';
            document.body.appendChild(iframe);
            
            return iframe;
        },
        /**
         * Creates form, that will be submitted to iframe
         * @param {Element} iframe Where to submit
         * @return {Element} form
         */
        _createForm: function(iframe){
            var settings = this._settings;
                        
            // We can't use the following code in IE6
            // var form = document.createElement('form');
            // form.setAttribute('method', 'post');
            // form.setAttribute('enctype', 'multipart/form-data');
            // Because in this case file won't be attached to request                    
            var form = toElement('<form method="post" enctype="multipart/form-data"></form>');
                        
            form.setAttribute('action', settings.action);
            form.setAttribute('target', iframe.name);                                   
            form.style.display = 'none';
            document.body.appendChild(form);
            
            // Create hidden input element for each data key
            for (var prop in settings.data) {
                if (settings.data.hasOwnProperty(prop)){
                    var el = document.createElement("input");
                    el.setAttribute('type', 'hidden');
                    el.setAttribute('name', prop);
                    el.setAttribute('value', settings.data[prop]);
                    form.appendChild(el);
                }
            }
            return form;
        },
        /**
         * Gets response from iframe and fires onComplete event when ready
         * @param iframe
         * @param file Filename to use in onComplete callback 
         */
        _getResponse : function(iframe, file){            
            // getting response
            var toDeleteFlag = false, self = this, settings = this._settings;   
               
            addEvent(iframe, 'load', function(){                
                
                if (// For Safari 
                    iframe.src == "javascript:'%3Chtml%3E%3C/html%3E';" ||
                    // For FF, IE
                    iframe.src == "javascript:'<html></html>';"){                                                                        
                        // First time around, do not delete.
                        // We reload to blank page, so that reloading main page
                        // does not re-submit the post.
                        
                        if (toDeleteFlag) {
                            // Fix busy state in FF3
                            setTimeout(function(){
                                removeNode(iframe);
                            }, 0);
                        }
                                                
                        return;
                }
                
                var doc = iframe.contentDocument ? iframe.contentDocument : window.frames[iframe.id].document;
                
                // fixing Opera 9.26,10.00
                if (doc.readyState && doc.readyState != 'complete') {
                   // Opera fires load event multiple times
                   // Even when the DOM is not ready yet
                   // this fix should not affect other browsers
                   return;
                }
                
                // fixing Opera 9.64
                if (doc.body && doc.body.innerHTML == "false") {
                    // In Opera 9.64 event was fired second time
                    // when body.innerHTML changed from false 
                    // to server response approx. after 1 sec
                    return;
                }
                
                var response;
                
                if (doc.XMLDocument) {
                    // response is a xml document Internet Explorer property
                    response = doc.XMLDocument;
                } else if (doc.body){
                    // response is html document or plain text
                    response = doc.body.innerHTML;
                    
                    if (settings.responseType && settings.responseType.toLowerCase() == 'json') {
                        // If the document was sent as 'application/javascript' or
                        // 'text/javascript', then the browser wraps the text in a <pre>
                        // tag and performs html encoding on the contents.  In this case,
                        // we need to pull the original text content from the text node's
                        // nodeValue property to retrieve the unmangled content.
                        // Note that IE6 only understands text/html
                        if (doc.body.firstChild && doc.body.firstChild.nodeName.toUpperCase() == 'PRE') {
                            response = doc.body.firstChild.firstChild.nodeValue;
                        }
                        
                        if (response) {
                            response = eval("(" + response + ")");
                        } else {
                            response = {};
                        }
                    }
                } else {
                    // response is a xml document
                    response = doc;
                }
                
                settings.onComplete.call(self, file, response);
                
                // Reload blank page, so that reloading main page
                // does not re-submit the post. Also, remember to
                // delete the frame
                toDeleteFlag = true;
                
                // Fix IE mixed content issue
                iframe.src = "javascript:'<html></html>';";
            });            
        },        
        /**
         * Upload file contained in this._input
         */
        submit: function(){                        
            var self = this, settings = this._settings;
            
            if ( ! this._input || this._input.value === ''){                
                return;                
            }
                                    
            var file = fileFromPath(this._input.value);
            
            // user returned false to cancel upload
            if (false === settings.onSubmit.call(this, file, getExt(file))){
                this._clearInput();                
                return;
            }
            
            // sending request    
            var iframe = this._createIframe();
            var form = this._createForm(iframe);
            
            // assuming following structure
            // div -> input type='file'
            removeNode(this._input.parentNode);            
            removeClass(self._button, self._settings.hoverClass);
                        
            form.appendChild(this._input);
                        
            form.submit();

            // request set, clean up                
            removeNode(form); form = null;                          
            removeNode(this._input); this._input = null;
            
            // Get response from iframe and fire onComplete event when ready
            this._getResponse(iframe, file);            

            // get ready for next request            
            this._createInput();
        }
    };
})(); 

/*
CSS Browser Selector v0.3.4 (Sep 29, 2009)
Rafael Lima (http://rafael.adm.br)
http://rafael.adm.br/css_browser_selector
License: http://creativecommons.org/licenses/by/2.5/
Contributors: http://rafael.adm.br/css_browser_selector#contributors
*/
function css_browser_selector(u){var ua = u.toLowerCase(),is=function(t){return ua.indexOf(t)>-1;},g='gecko',w='webkit',s='safari',o='opera',h=document.getElementsByTagName('html')[0],b=[(!(/opera|webtv/i.test(ua))&&/msie\s(\d)/.test(ua))?('ie ie'+RegExp.$1):is('firefox/2')?g+' ff2':is('firefox/3.5')?g+' ff3 ff3_5':is('firefox/3')?g+' ff3':is('gecko/')?g:is('opera')?o+(/version\/(\d+)/.test(ua)?' '+o+RegExp.$1:(/opera(\s|\/)(\d+)/.test(ua)?' '+o+RegExp.$2:'')):is('konqueror')?'konqueror':is('chrome')?w+' chrome':is('iron')?w+' iron':is('applewebkit/')?w+' '+s+(/version\/(\d+)/.test(ua)?' '+s+RegExp.$1:''):is('mozilla/')?g:'',is('j2me')?'mobile':is('iphone')?'iphone':is('ipod')?'ipod':is('mac')?'mac':is('darwin')?'mac':is('webtv')?'webtv':is('win')?'win':is('freebsd')?'freebsd':(is('x11')||is('linux'))?'linux':'','js']; c = b.join(' '); h.className += ' '+c; return c;}; css_browser_selector(navigator.userAgent);
/**
 * jQuery lightBox plugin
 * This jQuery plugin was inspired and based on Lightbox 2 by Lokesh Dhakar (http://www.huddletogether.com/projects/lightbox2/)
 * and adapted to me for use like a plugin from jQuery.
 * @name jquery-lightbox-0.5.js
 * @author Leandro Vieira Pinho - http://leandrovieira.com
 * @version 0.5
 * @date April 11, 2008
 * @category jQuery plugin
 * @copyright (c) 2008 Leandro Vieira Pinho (leandrovieira.com)
 * @license CC Attribution-No Derivative Works 2.5 Brazil - http://creativecommons.org/licenses/by-nd/2.5/br/deed.en_US
 * @example Visit http://leandrovieira.com/projects/jquery/lightbox/ for more informations about this jQuery plugin
 */

// Offering a Custom Alias suport - More info: http://docs.jquery.com/Plugins/Authoring#Custom_Alias
(function($) {
	/**
	 * $ is an alias to jQuery object
	 *
	 */
	$.fn.lightBox = function(settings) {
		// Settings to configure the jQuery lightBox plugin how you like
		settings = jQuery.extend({
			// Configuration related to overlay
			overlayBgColor: 		'#000',		// (string) Background color to overlay; inform a hexadecimal value like: #RRGGBB. Where RR, GG, and BB are the hexadecimal values for the red, green, and blue values of the color.
			overlayOpacity:			0.8,		// (integer) Opacity value to overlay; inform: 0.X. Where X are number from 0 to 9
			// Configuration related to navigation
			fixedNavigation:		false,		// (boolean) Boolean that informs if the navigation (next and prev button) will be fixed or not in the interface.
			// Configuration related to images
			imageLoading:			'images/lightbox-ico-loading.gif',		// (string) Path and the name of the loading icon
			imageBtnPrev:			'images/lightbox-btn-prev.gif',			// (string) Path and the name of the prev button image
			imageBtnNext:			'images/lightbox-btn-next.gif',			// (string) Path and the name of the next button image
			imageBtnClose:			'images/lightbox-btn-close.gif',		// (string) Path and the name of the close btn
			imageBlank:				'images/lightbox-blank.gif',			// (string) Path and the name of a blank image (one pixel)
			// Configuration related to container image box
			containerBorderSize:	10,			// (integer) If you adjust the padding in the CSS for the container, #lightbox-container-image-box, you will need to update this value
			containerResizeSpeed:	400,		// (integer) Specify the resize duration of container image. These number are miliseconds. 400 is default.
			// Configuration related to texts in caption. For example: Image 2 of 8. You can alter either "Image" and "of" texts.
			txtImage:				'Image',	// (string) Specify text "Image"
			txtOf:					'of',		// (string) Specify text "of"
			// Configuration related to keyboard navigation
			keyToClose:				'c',		// (string) (c = close) Letter to close the jQuery lightBox interface. Beyond this letter, the letter X and the SCAPE key is used to.
			keyToPrev:				'p',		// (string) (p = previous) Letter to show the previous image
			keyToNext:				'n',		// (string) (n = next) Letter to show the next image.
			// Don´t alter these variables in any way
			imageArray:				[],
			activeImage:			0
		},settings);
		// Caching the jQuery object with all elements matched
		var jQueryMatchedObj = this; // This, in this context, refer to jQuery object
		/**
		 * Initializing the plugin calling the start function
		 *
		 * @return boolean false
		 */
		function _initialize() {
			_start(this,jQueryMatchedObj); // This, in this context, refer to object (link) which the user have clicked
			return false; // Avoid the browser following the link
		}
		/**
		 * Start the jQuery lightBox plugin
		 *
		 * @param object objClicked The object (link) whick the user have clicked
		 * @param object jQueryMatchedObj The jQuery object with all elements matched
		 */
		function _start(objClicked,jQueryMatchedObj) {
			// Hime some elements to avoid conflict with overlay in IE. These elements appear above the overlay.
			$('embed, object, select').css({ 'visibility' : 'hidden' });
			// Call the function to create the markup structure; style some elements; assign events in some elements.
			_set_interface();
			// Unset total images in imageArray
			settings.imageArray.length = 0;
			// Unset image active information
			settings.activeImage = 0;
			// We have an image set? Or just an image? Let´s see it.
			if ( jQueryMatchedObj.length == 1 ) {
				settings.imageArray.push(new Array(objClicked.getAttribute('href'),objClicked.getAttribute('title')));
			} else {
				// Add an Array (as many as we have), with href and title atributes, inside the Array that storage the images references		
				for ( var i = 0; i < jQueryMatchedObj.length; i++ ) {
					settings.imageArray.push(new Array(jQueryMatchedObj[i].getAttribute('href'),jQueryMatchedObj[i].getAttribute('title')));
				}
			}
			while ( settings.imageArray[settings.activeImage][0] != objClicked.getAttribute('href') ) {
				settings.activeImage++;
			}
			// Call the function that prepares image exibition
			_set_image_to_view();
		}
		/**
		 * Create the jQuery lightBox plugin interface
		 *
		 * The HTML markup will be like that:
			<div id="jquery-overlay"></div>
			<div id="jquery-lightbox">
				<div id="lightbox-container-image-box">
					<div id="lightbox-container-image">
						<img src="../fotos/XX.jpg" id="lightbox-image">
						<div id="lightbox-nav">
							<a href="#" id="lightbox-nav-btnPrev"></a>
							<a href="#" id="lightbox-nav-btnNext"></a>
						</div>
						<div id="lightbox-loading">
							<a href="#" id="lightbox-loading-link">
								<img src="../images/lightbox-ico-loading.gif">
							</a>
						</div>
					</div>
				</div>
				<div id="lightbox-container-image-data-box">
					<div id="lightbox-container-image-data">
						<div id="lightbox-image-details">
							<span id="lightbox-image-details-caption"></span>
							<span id="lightbox-image-details-currentNumber"></span>
						</div>
						<div id="lightbox-secNav">
							<a href="#" id="lightbox-secNav-btnClose">
								<img src="../images/lightbox-btn-close.gif">
							</a>
						</div>
					</div>
				</div>
			</div>
		 *
		 */
		function _set_interface() {
			// Apply the HTML markup into body tag
			$('body').append('<div id="jquery-overlay"></div><div id="jquery-lightbox"><div id="lightbox-container-image-box"><div id="lightbox-container-image"><img id="lightbox-image"><div style="" id="lightbox-nav"><a href="#" id="lightbox-nav-btnPrev"></a><a href="#" id="lightbox-nav-btnNext"></a></div><div id="lightbox-loading"><a href="#" id="lightbox-loading-link"><img src="' + settings.imageLoading + '"></a></div></div></div><div id="lightbox-container-image-data-box"><div id="lightbox-container-image-data"><div id="lightbox-image-details"><span id="lightbox-image-details-caption"></span><span id="lightbox-image-details-currentNumber"></span></div><div id="lightbox-secNav"><a href="#" id="lightbox-secNav-btnClose"><img src="' + settings.imageBtnClose + '"></a></div></div></div></div>');	
			// Get page sizes
			var arrPageSizes = ___getPageSize();
			// Style overlay and show it
			$('#jquery-overlay').css({
				backgroundColor:	settings.overlayBgColor,
				opacity:			settings.overlayOpacity,
				width:				arrPageSizes[0],
				height:				arrPageSizes[1]
			}).fadeIn();
			// Get page scroll
			var arrPageScroll = ___getPageScroll();
			// Calculate top and left offset for the jquery-lightbox div object and show it
			$('#jquery-lightbox').css({
				top:	arrPageScroll[1] + (arrPageSizes[3] / 10),
				left:	arrPageScroll[0]
			}).show();
			// Assigning click events in elements to close overlay
			$('#jquery-overlay,#jquery-lightbox').click(function() {
				_finish();									
			});
			// Assign the _finish function to lightbox-loading-link and lightbox-secNav-btnClose objects
			$('#lightbox-loading-link,#lightbox-secNav-btnClose').click(function() {
				_finish();
				return false;
			});
			// If window was resized, calculate the new overlay dimensions
			$(window).resize(function() {
				// Get page sizes
				var arrPageSizes = ___getPageSize();
				// Style overlay and show it
				$('#jquery-overlay').css({
					width:		arrPageSizes[0],
					height:		arrPageSizes[1]
				});
				// Get page scroll
				var arrPageScroll = ___getPageScroll();
				// Calculate top and left offset for the jquery-lightbox div object and show it
				$('#jquery-lightbox').css({
					top:	arrPageScroll[1] + (arrPageSizes[3] / 10),
					left:	arrPageScroll[0]
				});
			});
		}
		/**
		 * Prepares image exibition; doing a image´s preloader to calculate it´s size
		 *
		 */
		function _set_image_to_view() { // show the loading
			// Show the loading
			$('#lightbox-loading').show();
			if ( settings.fixedNavigation ) {
				$('#lightbox-image,#lightbox-container-image-data-box,#lightbox-image-details-currentNumber').hide();
			} else {
				// Hide some elements
				$('#lightbox-image,#lightbox-nav,#lightbox-nav-btnPrev,#lightbox-nav-btnNext,#lightbox-container-image-data-box,#lightbox-image-details-currentNumber').hide();
			}
			// Image preload process
			var objImagePreloader = new Image();
			objImagePreloader.onload = function() {
				$('#lightbox-image').attr('src',settings.imageArray[settings.activeImage][0]);
				// Perfomance an effect in the image container resizing it
				_resize_container_image_box(objImagePreloader.width,objImagePreloader.height);
				//	clear onLoad, IE behaves irratically with animated gifs otherwise
				objImagePreloader.onload=function(){};
			};
			objImagePreloader.src = settings.imageArray[settings.activeImage][0];
		};
		/**
		 * Perfomance an effect in the image container resizing it
		 *
		 * @param integer intImageWidth The image´s width that will be showed
		 * @param integer intImageHeight The image´s height that will be showed
		 */
		function _resize_container_image_box(intImageWidth,intImageHeight) {
			// Get current width and height
			var intCurrentWidth = $('#lightbox-container-image-box').width();
			var intCurrentHeight = $('#lightbox-container-image-box').height();
			// Get the width and height of the selected image plus the padding
			var intWidth = (intImageWidth + (settings.containerBorderSize * 2)); // Plus the image´s width and the left and right padding value
			var intHeight = (intImageHeight + (settings.containerBorderSize * 2)); // Plus the image´s height and the left and right padding value
			// Diferences
			var intDiffW = intCurrentWidth - intWidth;
			var intDiffH = intCurrentHeight - intHeight;
			// Perfomance the effect
			$('#lightbox-container-image-box').animate({ width: intWidth, height: intHeight },settings.containerResizeSpeed,function() { _show_image(); });
			if ( ( intDiffW == 0 ) && ( intDiffH == 0 ) ) {
				if ( $.browser.msie ) {
					___pause(250);
				} else {
					___pause(100);	
				}
			} 
			$('#lightbox-container-image-data-box').css({ width: intImageWidth });
			$('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({ height: intImageHeight + (settings.containerBorderSize * 2) });
		};
		/**
		 * Show the prepared image
		 *
		 */
		function _show_image() {
			$('#lightbox-loading').hide();
			$('#lightbox-image').fadeIn(function() {
				_show_image_data();
				_set_navigation();
			});
			_preload_neighbor_images();
		};
		/**
		 * Show the image information
		 *
		 */
		function _show_image_data() {
			$('#lightbox-container-image-data-box').slideDown('fast');
			$('#lightbox-image-details-caption').hide();
			if ( settings.imageArray[settings.activeImage][1] ) {
				$('#lightbox-image-details-caption').html(settings.imageArray[settings.activeImage][1]).show();
			}
			// If we have a image set, display 'Image X of X'
			if ( settings.imageArray.length > 1 ) {
				$('#lightbox-image-details-currentNumber').html(settings.txtImage + ' ' + ( settings.activeImage + 1 ) + ' ' + settings.txtOf + ' ' + settings.imageArray.length).show();
			}		
		}
		/**
		 * Display the button navigations
		 *
		 */
		function _set_navigation() {
			$('#lightbox-nav').show();

			// Instead to define this configuration in CSS file, we define here. And it´s need to IE. Just.
			$('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({ 'background' : 'transparent url(' + settings.imageBlank + ') no-repeat' });
			
			// Show the prev button, if not the first image in set
			if ( settings.activeImage != 0 ) {
				if ( settings.fixedNavigation ) {
					$('#lightbox-nav-btnPrev').css({ 'background' : 'url(' + settings.imageBtnPrev + ') left 15% no-repeat' })
						.unbind()
						.bind('click',function() {
							settings.activeImage = settings.activeImage - 1;
							_set_image_to_view();
							return false;
						});
				} else {
					// Show the images button for Next buttons
					$('#lightbox-nav-btnPrev').unbind().hover(function() {
						$(this).css({ 'background' : 'url(' + settings.imageBtnPrev + ') left 15% no-repeat' });
					},function() {
						$(this).css({ 'background' : 'transparent url(' + settings.imageBlank + ') no-repeat' });
					}).show().bind('click',function() {
						settings.activeImage = settings.activeImage - 1;
						_set_image_to_view();
						return false;
					});
				}
			}
			
			// Show the next button, if not the last image in set
			if ( settings.activeImage != ( settings.imageArray.length -1 ) ) {
				if ( settings.fixedNavigation ) {
					$('#lightbox-nav-btnNext').css({ 'background' : 'url(' + settings.imageBtnNext + ') right 15% no-repeat' })
						.unbind()
						.bind('click',function() {
							settings.activeImage = settings.activeImage + 1;
							_set_image_to_view();
							return false;
						});
				} else {
					// Show the images button for Next buttons
					$('#lightbox-nav-btnNext').unbind().hover(function() {
						$(this).css({ 'background' : 'url(' + settings.imageBtnNext + ') right 15% no-repeat' });
					},function() {
						$(this).css({ 'background' : 'transparent url(' + settings.imageBlank + ') no-repeat' });
					}).show().bind('click',function() {
						settings.activeImage = settings.activeImage + 1;
						_set_image_to_view();
						return false;
					});
				}
			}
			// Enable keyboard navigation
			_enable_keyboard_navigation();
		}
		/**
		 * Enable a support to keyboard navigation
		 *
		 */
		function _enable_keyboard_navigation() {
			$(document).keydown(function(objEvent) {
				_keyboard_action(objEvent);
			});
		}
		/**
		 * Disable the support to keyboard navigation
		 *
		 */
		function _disable_keyboard_navigation() {
			$(document).unbind();
		}
		/**
		 * Perform the keyboard actions
		 *
		 */
		function _keyboard_action(objEvent) {
			// To ie
			if ( objEvent == null ) {
				keycode = event.keyCode;
				escapeKey = 27;
			// To Mozilla
			} else {
				keycode = objEvent.keyCode;
				escapeKey = objEvent.DOM_VK_ESCAPE;
			}
			// Get the key in lower case form
			key = String.fromCharCode(keycode).toLowerCase();
			// Verify the keys to close the ligthBox
			if ( ( key == settings.keyToClose ) || ( key == 'x' ) || ( keycode == escapeKey ) ) {
				_finish();
			}
			// Verify the key to show the previous image
			if ( ( key == settings.keyToPrev ) || ( keycode == 37 ) ) {
				// If we´re not showing the first image, call the previous
				if ( settings.activeImage != 0 ) {
					settings.activeImage = settings.activeImage - 1;
					_set_image_to_view();
					_disable_keyboard_navigation();
				}
			}
			// Verify the key to show the next image
			if ( ( key == settings.keyToNext ) || ( keycode == 39 ) ) {
				// If we´re not showing the last image, call the next
				if ( settings.activeImage != ( settings.imageArray.length - 1 ) ) {
					settings.activeImage = settings.activeImage + 1;
					_set_image_to_view();
					_disable_keyboard_navigation();
				}
			}
		}
		/**
		 * Preload prev and next images being showed
		 *
		 */
		function _preload_neighbor_images() {
			if ( (settings.imageArray.length -1) > settings.activeImage ) {
				objNext = new Image();
				objNext.src = settings.imageArray[settings.activeImage + 1][0];
			}
			if ( settings.activeImage > 0 ) {
				objPrev = new Image();
				objPrev.src = settings.imageArray[settings.activeImage -1][0];
			}
		}
		/**
		 * Remove jQuery lightBox plugin HTML markup
		 *
		 */
		function _finish() {
			$('#jquery-lightbox').remove();
			$('#jquery-overlay').fadeOut(function() { $('#jquery-overlay').remove(); });
			// Show some elements to avoid conflict with overlay in IE. These elements appear above the overlay.
			$('embed, object, select').css({ 'visibility' : 'visible' });
		}
		/**
		 / THIRD FUNCTION
		 * getPageSize() by quirksmode.com
		 *
		 * @return Array Return an array with page width, height and window width, height
		 */
		function ___getPageSize() {
			var xScroll, yScroll;
			if (window.innerHeight && window.scrollMaxY) {	
				xScroll = window.innerWidth + window.scrollMaxX;
				yScroll = window.innerHeight + window.scrollMaxY;
			} else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
				xScroll = document.body.scrollWidth;
				yScroll = document.body.scrollHeight;
			} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
				xScroll = document.body.offsetWidth;
				yScroll = document.body.offsetHeight;
			}
			var windowWidth, windowHeight;
			if (self.innerHeight) {	// all except Explorer
				if(document.documentElement.clientWidth){
					windowWidth = document.documentElement.clientWidth; 
				} else {
					windowWidth = self.innerWidth;
				}
				windowHeight = self.innerHeight;
			} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
				windowWidth = document.documentElement.clientWidth;
				windowHeight = document.documentElement.clientHeight;
			} else if (document.body) { // other Explorers
				windowWidth = document.body.clientWidth;
				windowHeight = document.body.clientHeight;
			}	
			// for small pages with total height less then height of the viewport
			if(yScroll < windowHeight){
				pageHeight = windowHeight;
			} else { 
				pageHeight = yScroll;
			}
			// for small pages with total width less then width of the viewport
			if(xScroll < windowWidth){	
				pageWidth = xScroll;		
			} else {
				pageWidth = windowWidth;
			}
			arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight);
			return arrayPageSize;
		};
		/**
		 / THIRD FUNCTION
		 * getPageScroll() by quirksmode.com
		 *
		 * @return Array Return an array with x,y page scroll values.
		 */
		function ___getPageScroll() {
			var xScroll, yScroll;
			if (self.pageYOffset) {
				yScroll = self.pageYOffset;
				xScroll = self.pageXOffset;
			} else if (document.documentElement && document.documentElement.scrollTop) {	 // Explorer 6 Strict
				yScroll = document.documentElement.scrollTop;
				xScroll = document.documentElement.scrollLeft;
			} else if (document.body) {// all other Explorers
				yScroll = document.body.scrollTop;
				xScroll = document.body.scrollLeft;	
			}
			arrayPageScroll = new Array(xScroll,yScroll);
			return arrayPageScroll;
		};
		 /**
		  * Stop the code execution from a escified time in milisecond
		  *
		  */
		 function ___pause(ms) {
			var date = new Date(); 
			curDate = null;
			do { var curDate = new Date(); }
			while ( curDate - date < ms);
		 };
		// Return the jQuery object for chaining. The unbind method is used to avoid click conflict when the plugin is called more than once
		return this.unbind('click').click(_initialize);
	};
})(jQuery); // Call and execute the function immediately passing the jQuery object
