/* Initialization file for Q Framework */
// Add option to prefix a custom string to all Object prototype extensions

// for more info on recursion / stack problems
//http://support.microsoft.com/kb/175500
(function(gc, gv) {
    //gc - global context of execution
    //gv - global var name framework - defaults to 'q'

    var q = gc[gv] = {}; //Entrypoint into Q Library
        q.gc = gc; //global execution cotext
        q.installpath = "c:\\usr\\dev\\que\\";
        q.libpath = "C:\\usr\\dev\\que\\lib\\";
        q.server_addr = "localhost";
        q.onloads = [];
        q.unloads = [];
        
        
        //Wrapping ActiveXObject creation - since windows won't
        //report what object failed to load.  This is also a good
        //place to failsafe on invalid or unauthorized
        //objects created in the browser
        q.activex = ActiveXObject;
        
        gc.ActiveXObject = function(obj) {
            try {
                return new q.activex(obj); 
            } catch(e) {
                alert("Unable to create " + obj + "\r\n" + e.toString());
            }
        };
        
        q.toArray = function(obj) {
            //Iterates over obj.length and pushes each index to an array
            //Used for Object Collections like document.all or WScript.arguments
            var arry = [];
            try {
                var e = new Enumerator(obj);
                for (; !e.atEnd(); e.moveNext()) {
                    arry.push(e.item());
                }
                return(arry);
            } catch(e) {
                try {
                    return Array.prototype.slice.apply(obj);
                } catch(e) {
                    //old fashioned approach for non jscript iterables (dom collections, WScript.arguments etc)     
                    var l = obj.length - 1;
                    var i = -1;
                    while (i++ < l) {
                        arry.push(obj[i]);
                    }
                    return(arry);
                }
            }
        };
        
        q.enumerate = function enumerate(comlist) {
            //Converts a COM list into a JavaScript Array
            return q.toArray(comlist);
        };
        
        q.clean = function clean(obj) {
            for (var x in obj) {
                if (x == '__type') continue;
                obj[x] = undefined;
            }
            return obj;
        };
        
        //global me() func - makes function refs easier to access
        q.me = function me() {
            return arguments.callee.caller;
        };
        
        q.not = function not(boolFunc) {
            return !boolFunc.returns();
        };
        
        //cache some references to native system functions because we'll be overwriting them later on
        //q.sort = Array.prototype.sort;
        //q.reverse = Array.prototype.reverse;
        q.tostring = Object.prototype.toString();
        
        q.def = function def(obj, val) {
            //Defaults a variable (obj) to value (val) if it is not yet defined
            //e.g. var y = def(y, 100);
            //(val) can be a javascript code and will be interpreted as such
            //e.g. var y = def(y, "(total_users - 1)");
            if (typeof obj == "undefined" || obj == null) {
                q.me().args().from(1).each(function(arg) {
                    obj = obj || arg.toVar(true) || arg;
                });
            }
            return(obj);
        };
            
        q.dump = function dump(data, maxdepth, offset, depth) {
            //creates a json serialization of an object
            offset = q.def(offset, "");
            depth = q.def(depth, 0);
            maxdepth = q.def(maxdepth, -1);
        
            if (depth > maxdepth && maxdepth != -1) {
              return("");
            }
        
            var nextoff = offset + "  ";
            var nextdepth = depth + 1;
            var array = [];
        
            switch ((data != null && data.__type != null) ? data.__type() : "object") {
                case "string":
                    return('"'+ data.escapeJSON() +'"');
                    break;
                case "number":
                    return(data);
                    break;
                case "boolean":
                    return data.valueOf() ? "true" : "false";
                    break;
                case "notundefined":
                    return("null");
                    break;
                case "function":
                    return(data.toString());
                    break;
                case "hash":
                    return(data.dump());
                    break;
                case "object":
                    if (data == null ) {
                        return("null");
                    }
        
                    q.u.each(data, function(v, k) {
                        try {
                            var val = q.dump(v, maxdepth, nextoff, nextdepth);
                        }catch (e) {
                            val = "null";
                        }
        
                        if (val != null) {
                            k = '"' + k.toString().escapeJSON() + '"';
                            array.push(k + ": " + val);
                        }                    
                    });
        
                    if ( array.length == 1 && !array[0].match(/[\n\{\[]/) ) {
                        return "{ " + array[0] + " }";
                    }
        
                    return "{\r\n" + nextoff + array.join( ",\r\n" + nextoff ) + "\r\n" + offset + "}";
        
                    break;
        
                case "array":
                    data.each(function(v) {
                        array.push(q.dump(v, maxdepth, nextoff, nextdepth));
                    });
        
                    return "[\r\n" + nextoff + array.join(",\r\n" + nextoff) + "\r\n" + offset + "]";
                    break;
        
                default:
                  // unsupported data type
                  break;
            }
        
            return "";
        };
        
        q.unload = function unload(o) {
            //event to call on unload of application
            //optional o will push any object onto unloads queue
            if (o) {
                q.unloads.push(o); 
            }
            else {
                q.unloads.each(function(o) {
                    if (typeof(o.unload) == "function") {
                        o.unload.tryval();
                    }
                    else if (o.isFunction()) {
                        o.tryval();
                    }
                    o = null; 
                });
                
                q.unloads = [];
            }    
        };
        
        if (q.gc.window) {
            window.onbeforeunload = function() {
                q.unload();
            };
        }
        
        q.onload = function onload(o) {
            if (o) {
                q.onloads.push(o);
            }
            else {
                q.onloads.each(function(o) {
                    if (o.isFunction()) {
                        o.tryval();
                    }
                    else if (typeof(o.onload) == "function") {
                        o.onload.tryval(); 
                    }
                });
                
                q.onloads = [];
            }
        };
        
        q.report = function() {
            //report abstraction allows adjusting the method of informing the user
            alert(q.me().args().join(", "));
        };
        
        //Make public many of the q functions for convenience
        q.onload(function() {
            "clean dump not me report".words().each(function(v) {
                q[v].publicize();     
            });
        });
        
        q.scope = function(scope, func) {
            //returns an object with cached references to the function and scope & params
            //return object contains a single method exec to call the cached function
            return new function() {
                this.exec = function() {
                    func.apply(scope, arguments);   
                };
            };
        };
        
        q.namespace = function(ns) {
            //Creates a nested object chain equivalent to the provided namespace (ns)
            //so "q.html.element" = q.html.element
            var scope = gc;
            
            ns.split("\.").ea(function(s) {
                if (typeof scope[s] == "undefined") scope[s] = {};
                scope = scope[s];
            });
            
            return scope;
        };
        
        q.require = function(js, handler) {
            //On demand loading of javascript includes the provided js file in the global namespace
            //calls optional handler after library is pulled and loaded
            handler = q.def(handler, function() {});
            if (js.isUrl()) {
                js = q.http.get(js);
            } else {
                js = q.f.open(js);
            }

            js.tryval(true);
            
            handler.call(this);
        };
        
        return q;
    
})(this, 'q');
/*!
 * jQuery JavaScript Library v1.3.2
 * http://jquery.com/
 *
 * Copyright (c) 2009 John Resig
 * Dual licensed under the MIT and GPL licenses.
 * http://docs.jquery.com/License
 *
 * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
 * Revision: 6246
 */
(function(){

var
	// Will speed up references to window, and allows munging its name.
	window = this,
	// Will speed up references to undefined, and allows munging its name.
	undefined,
	// Map over jQuery in case of overwrite
	_jQuery = window.jQuery,
	// Map over the $ in case of overwrite
	_$ = window.$,

	jQuery = window.jQuery = window.$ = function( selector, context ) {
		// The jQuery object is actually just the init constructor 'enhanced'
		return new jQuery.fn.init( selector, context );
	},

	// A simple way to check for HTML strings or ID strings
	// (both of which we optimize for)
	quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,
	// Is it a simple selector
	isSimple = /^.[^:#\[\.,]*$/;

jQuery.fn = jQuery.prototype = {
	init: function( selector, context ) {
		// Make sure that a selection was provided
		selector = selector || document;

		// Handle $(DOMElement)
		if ( selector.nodeType ) {
			this[0] = selector;
			this.length = 1;
			this.context = selector;
			return this;
		}
		// Handle HTML strings
		if ( typeof selector === "string" ) {
			// Are we dealing with HTML string or an ID?
			var match = quickExpr.exec( selector );

			// Verify a match, and that no context was specified for #id
			if ( match && (match[1] || !context) ) {

				// HANDLE: $(html) -> $(array)
				if ( match[1] )
					selector = jQuery.clean( [ match[1] ], context );

				// HANDLE: $("#id")
				else {
					var elem = document.getElementById( match[3] );

					// Handle the case where IE and Opera return items
					// by name instead of ID
					if ( elem && elem.id != match[3] )
						return jQuery().find( selector );

					// Otherwise, we inject the element directly into the jQuery object
					var ret = jQuery( elem || [] );
					ret.context = document;
					ret.selector = selector;
					return ret;
				}

			// HANDLE: $(expr, [context])
			// (which is just equivalent to: $(content).find(expr)
			} else
				return jQuery( context ).find( selector );

		// HANDLE: $(function)
		// Shortcut for document ready
		} else if ( jQuery.isFunction( selector ) )
			return jQuery( document ).ready( selector );

		// Make sure that old selector state is passed along
		if ( selector.selector && selector.context ) {
			this.selector = selector.selector;
			this.context = selector.context;
		}

		return this.setArray(jQuery.isArray( selector ) ?
			selector :
			jQuery.makeArray(selector));
	},

	// Start with an empty selector
	selector: "",

	// The current version of jQuery being used
	jquery: "1.3.2",

	// The number of elements contained in the matched element set
	size: function() {
		return this.length;
	},

	// Get the Nth element in the matched element set OR
	// Get the whole matched element set as a clean array
	get: function( num ) {
		return num === undefined ?

			// Return a 'clean' array
			Array.prototype.slice.call( this ) :

			// Return just the object
			this[ num ];
	},

	// Take an array of elements and push it onto the stack
	// (returning the new matched element set)
	pushStack: function( elems, name, selector ) {
		// Build a new jQuery matched element set
		var ret = jQuery( elems );

		// Add the old object onto the stack (as a reference)
		ret.prevObject = this;

		ret.context = this.context;

		if ( name === "find" )
			ret.selector = this.selector + (this.selector ? " " : "") + selector;
		else if ( name )
			ret.selector = this.selector + "." + name + "(" + selector + ")";

		// Return the newly-formed element set
		return ret;
	},

	// Force the current matched set of elements to become
	// the specified array of elements (destroying the stack in the process)
	// You should use pushStack() in order to do this, but maintain the stack
	setArray: function( elems ) {
		// Resetting the length to 0, then using the native Array push
		// is a super-fast way to populate an object with array-like properties
		this.length = 0;
		Array.prototype.push.apply( this, elems );

		return this;
	},

	// Execute a callback for every element in the matched set.
	// (You can seed the arguments with an array of args, but this is
	// only used internally.)
	each: function( callback, args ) {
		return jQuery.each( this, callback, args );
	},

	// Determine the position of an element within
	// the matched set of elements
	index: function( elem ) {
		// Locate the position of the desired element
		return jQuery.inArray(
			// If it receives a jQuery object, the first element is used
			elem && elem.jquery ? elem[0] : elem
		, this );
	},

	attr: function( name, value, type ) {
		var options = name;

		// Look for the case where we're accessing a style value
		if ( typeof name === "string" )
			if ( value === undefined )
				return this[0] && jQuery[ type || "attr" ]( this[0], name );

			else {
				options = {};
				options[ name ] = value;
			}

		// Check to see if we're setting style values
		return this.each(function(i){
			// Set all the styles
			for ( name in options )
                if (options.hasOwnProperty(name)) {
				jQuery.attr(
					type ?
						this.style :
						this,
					name, jQuery.prop( this, options[ name ], type, i, name )
				);
                }
		});
	},

	css: function( key, value ) {
		// ignore negative width and height values
		if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 )
			value = undefined;
		return this.attr( key, value, "curCSS" );
	},

	text: function( text ) {
		if ( typeof text !== "object" && text != null )
			return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );

		var ret = "";

		jQuery.each( text || this, function(){
			jQuery.each( this.childNodes, function(){
				if ( this.nodeType != 8 )
					ret += this.nodeType != 1 ?
						this.nodeValue :
						jQuery.fn.text( [ this ] );
			});
		});

		return ret;
	},

	wrapAll: function( html ) {
		if ( this[0] ) {
			// The elements to wrap the target around
			var wrap = jQuery( html, this[0].ownerDocument ).clone();

			if ( this[0].parentNode )
				wrap.insertBefore( this[0] );

			wrap.map(function(){
				var elem = this;

				while ( elem.firstChild )
					elem = elem.firstChild;

				return elem;
			}).append(this);
		}

		return this;
	},

	wrapInner: function( html ) {
		return this.each(function(){
			jQuery( this ).contents().wrapAll( html );
		});
	},

	wrap: function( html ) {
		return this.each(function(){
			jQuery( this ).wrapAll( html );
		});
	},

	append: function() {
		return this.domManip(arguments, true, function(elem){
			if (this.nodeType == 1)
				this.appendChild( elem );
		});
	},

	prepend: function() {
		return this.domManip(arguments, true, function(elem){
			if (this.nodeType == 1)
				this.insertBefore( elem, this.firstChild );
		});
	},

	before: function() {
		return this.domManip(arguments, false, function(elem){
			this.parentNode.insertBefore( elem, this );
		});
	},

	after: function() {
		return this.domManip(arguments, false, function(elem){
			this.parentNode.insertBefore( elem, this.nextSibling );
		});
	},

	end: function() {
		return this.prevObject || jQuery( [] );
	},

	// For internal use only.
	// Behaves like an Array's method, not like a jQuery method.
	push: [].push,
	sort: [].sort,
	splice: [].splice,

	find: function( selector ) {
		if ( this.length === 1 ) {
			var ret = this.pushStack( [], "find", selector );
			ret.length = 0;
			jQuery.find( selector, this[0], ret );
			return ret;
		} else {
			return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){
				return jQuery.find( selector, elem );
			})), "find", selector );
		}
	},

	clone: function( events ) {
		// Do the clone
		var ret = this.map(function(){
			if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
				// IE copies events bound via attachEvent when
				// using cloneNode. Calling detachEvent on the
				// clone will also remove the events from the orignal
				// In order to get around this, we use innerHTML.
				// Unfortunately, this means some modifications to
				// attributes in IE that are actually only stored
				// as properties will not be copied (such as the
				// the name attribute on an input).
				var html = this.outerHTML;
				if ( !html ) {
					var div = this.ownerDocument.createElement("div");
					div.appendChild( this.cloneNode(true) );
					html = div.innerHTML;
				}

				return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0];
			} else
				return this.cloneNode(true);
		});

		// Copy the events from the original to the clone
		if ( events === true ) {
			var orig = this.find("*").andSelf(), i = 0;

			ret.find("*").andSelf().each(function(){
				if ( this.nodeName !== orig[i].nodeName )
					return;

				var events = jQuery.data( orig[i], "events" );

				for ( var type in events ) {
                    if (!events.hasOwnProperty(type)) continue;
					for ( var handler in events[ type ] ) {
                        if (!events[type].hasOwnProperty(handler)) continue;
						jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
					}
				}

				i++;
			});
		}

		// Return the cloned set
		return ret;
	},

	filter: function( selector ) {
		return this.pushStack(
			jQuery.isFunction( selector ) &&
			jQuery.grep(this, function(elem, i){
				return selector.call( elem, i );
			}) ||

			jQuery.multiFilter( selector, jQuery.grep(this, function(elem){
				return elem.nodeType === 1;
			}) ), "filter", selector );
	},

	closest: function( selector ) {
		var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null,
			closer = 0;

		return this.map(function(){
			var cur = this;
			while ( cur && cur.ownerDocument ) {
				if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) {
					jQuery.data(cur, "closest", closer);
					return cur;
				}
				cur = cur.parentNode;
				closer++;
			}
		});
	},

	not: function( selector ) {
		if ( typeof selector === "string" )
			// test special case where just one selector is passed in
			if ( isSimple.test( selector ) )
				return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector );
			else
				selector = jQuery.multiFilter( selector, this );

		var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
		return this.filter(function() {
			return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
		});
	},

	add: function( selector ) {
		return this.pushStack( jQuery.unique( jQuery.merge(
			this.get(),
			typeof selector === "string" ?
				jQuery( selector ) :
				jQuery.makeArray( selector )
		)));
	},

	is: function( selector ) {
		return !!selector && jQuery.multiFilter( selector, this ).length > 0;
	},

	hasClass: function( selector ) {
		return !!selector && this.is( "." + selector );
	},

	val: function( value ) {
		if ( value === undefined ) {
			var elem = this[0];

			if ( elem ) {
				if( jQuery.nodeName( elem, 'option' ) )
					return (elem.attributes.value || {}).specified ? elem.value : elem.text;

				// We need to handle select boxes special
				if ( jQuery.nodeName( elem, "select" ) ) {
					var index = elem.selectedIndex,
						values = [],
						options = elem.options,
						one = elem.type == "select-one";

					// Nothing was selected
					if ( index < 0 )
						return null;

					// Loop through all the selected options
					for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
						var option = options[ i ];

						if ( option.selected ) {
							// Get the specifc value for the option
							value = jQuery(option).val();

							// We don't need an array for one selects
							if ( one )
								return value;

							// Multi-Selects return an array
							values.push( value );
						}
					}

					return values;
				}

				// Everything else, we just grab the value
				return (elem.value || "").replace(/\r/g, "");

			}

			return undefined;
		}

		if ( typeof value === "number" )
			value += '';

		return this.each(function(){
			if ( this.nodeType != 1 )
				return;

			if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) )
				this.checked = (jQuery.inArray(this.value, value) >= 0 ||
					jQuery.inArray(this.name, value) >= 0);

			else if ( jQuery.nodeName( this, "select" ) ) {
				var values = jQuery.makeArray(value);

				jQuery( "option", this ).each(function(){
					this.selected = (jQuery.inArray( this.value, values ) >= 0 ||
						jQuery.inArray( this.text, values ) >= 0);
				});

				if ( !values.length )
					this.selectedIndex = -1;

			} else
				this.value = value;
		});
	},

	html: function( value ) {
		return value === undefined ?
			(this[0] ?
				this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") :
				null) :
			this.empty().append( value );
	},

	replaceWith: function( value ) {
		return this.after( value ).remove();
	},

	eq: function( i ) {
		return this.slice( i, +i + 1 );
	},

	slice: function() {
		return this.pushStack( Array.prototype.slice.apply( this, arguments ),
			"slice", Array.prototype.slice.call(arguments).join(",") );
	},

	map: function( callback ) {
		return this.pushStack( jQuery.map(this, function(elem, i){
			return callback.call( elem, i, elem );
		}));
	},

	andSelf: function() {
		return this.add( this.prevObject );
	},

	domManip: function( args, table, callback ) {
		if ( this[0] ) {
			var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(),
				scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ),
				first = fragment.firstChild;

			if ( first )
				for ( var i = 0, l = this.length; i < l; i++ )
					callback.call( root(this[i], first), this.length > 1 || i > 0 ?
							fragment.cloneNode(true) : fragment );

			if ( scripts )
				jQuery.each( scripts, evalScript );
		}

		return this;

		function root( elem, cur ) {
			return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ?
				(elem.getElementsByTagName("tbody")[0] ||
				elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
				elem;
		}
	}
};

// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;

function evalScript( i, elem ) {
	if ( elem.src )
		jQuery.ajax({
			url: elem.src,
			async: false,
			dataType: "script"
		});

	else
		jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );

	if ( elem.parentNode )
		elem.parentNode.removeChild( elem );
}

function now(){
	return +new Date;
}

jQuery.extend = jQuery.fn.extend = function() {
	// copy reference to target object
	var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;

	// Handle a deep copy situation
	if ( typeof target === "boolean" ) {
		deep = target;
		target = arguments[1] || {};
		// skip the boolean and the target
		i = 2;
	}

	// Handle case when target is a string or something (possible in deep copy)
	if ( typeof target !== "object" && !jQuery.isFunction(target) )
		target = {};

	// extend jQuery itself if only one argument is passed
	if ( length == i ) {
		target = this;
		--i;
	}

	for ( ; i < length; i++ )
		// Only deal with non-null/undefined values
		if ( (options = arguments[ i ]) != null )
			// Extend the base object
			for ( var name in options ) {
                if (!options.hasOwnProperty(name)) continue;
				var src = target[ name ], copy = options[ name ];

				// Prevent never-ending loop
				if ( target === copy )
					continue;

				// Recurse if we're merging object values
				if ( deep && copy && typeof copy === "object" && !copy.nodeType )
					target[ name ] = jQuery.extend( deep,
						// Never move original objects, clone them
						src || ( copy.length != null ? [ ] : { } )
					, copy );

				// Don't bring in undefined values
				else if ( copy !== undefined )
					target[ name ] = copy;

			}

	// Return the modified object
	return target;
};

// exclude the following css properties to add px
var	exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
	// cache defaultView
	defaultView = document.defaultView || {},
	toString = Object.prototype.toString;

jQuery.extend({
	noConflict: function( deep ) {
		window.$ = _$;

		if ( deep )
			window.jQuery = _jQuery;

		return jQuery;
	},

	// See test/unit/core.js for details concerning isFunction.
	// Since version 1.3, DOM methods and functions like alert
	// aren't supported. They return false on IE (#2968).
	isFunction: function( obj ) {
		return toString.call(obj) === "[object Function]";
	},

	isArray: function( obj ) {
		return toString.call(obj) === "[object Array]";
	},

	// check if an element is in a (or is an) XML document
	isXMLDoc: function( elem ) {
		return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
			!!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument );
	},

	// Evalulates a script in a global context
	globalEval: function( data ) {
		if ( data && /\S/.test(data) ) {
			// Inspired by code by Andrea Giammarchi
			// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
			var head = document.getElementsByTagName("head")[0] || document.documentElement,
				script = document.createElement("script");

			script.type = "text/javascript";
			if ( jQuery.support.scriptEval )
				script.appendChild( document.createTextNode( data ) );
			else
				script.text = data;

			// Use insertBefore instead of appendChild  to circumvent an IE6 bug.
			// This arises when a base node is used (#2709).
			head.insertBefore( script, head.firstChild );
			head.removeChild( script );
		}
	},

	nodeName: function( elem, name ) {
		return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
	},

	// args is for internal usage only
	each: function( object, callback, args ) {

		var name, i = 0, length = object.length;

		if ( args ) {
			if ( length === undefined ) {
				for ( name in object ) {
                    if (!object.hasOwnProperty(name)) continue;
					if ( callback.apply( object[ name ], args ) === false )
						break;
                }
			} else
				for ( ; i < length; )
					if ( callback.apply( object[ i++ ], args ) === false )
						break;

		// A special, fast, case for the most common use of each
		} else {
			if ( length === undefined ) {
				for ( name in object ) {
					if (!object.hasOwnProperty(name)) continue;
					if ( callback.call( object[ name ], name, object[ name ] ) === false )
						break;
				}
			} else
				for (var value = object[0]; i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
		}

		return object;
	},

	prop: function( elem, value, type, i, name ) {
		// Handle executable functions
		if ( jQuery.isFunction( value ) )
			value = value.call( elem, i );

		// Handle passing in a number to a CSS property
		return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ?
			value + "px" :
			value;
	},

	className: {
		// internal only, use addClass("class")
		add: function( elem, classNames ) {
			jQuery.each((classNames || "").split(/\s+/), function(i, className){
				if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
					elem.className += (elem.className ? " " : "") + className;
			});
		},

		// internal only, use removeClass("class")
		remove: function( elem, classNames ) {
			if (elem.nodeType == 1)
				elem.className = classNames !== undefined ?
					jQuery.grep(elem.className.split(/\s+/), function(className){
						return !jQuery.className.has( classNames, className );
					}).join(" ") :
					"";
		},

		// internal only, use hasClass("class")
		has: function( elem, className ) {
			return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
		}
	},

	// A method for quickly swapping in/out CSS properties to get correct calculations
	swap: function( elem, options, callback ) {
		var old = {};
		// Remember the old values, and insert the new ones
		for ( var name in options ) {
            if (!options.hasOwnProperty(name)) continue;
			old[ name ] = elem.style[ name ];
			elem.style[ name ] = options[ name ];
		}

		callback.call( elem );

		// Revert the old values
		for ( var name in options )
            if (!options.hasOwnProperty(name)) continue;
			elem.style[ name ] = old[ name ];
	},

	css: function( elem, name, force, extra ) {
		if ( name == "width" || name == "height" ) {
			var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ];

			function getWH() {
				val = name == "width" ? elem.offsetWidth : elem.offsetHeight;

				if ( extra === "border" )
					return;

				jQuery.each( which, function() {
					if ( !extra )
						val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
					if ( extra === "margin" )
						val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
					else
						val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
				});
			}

			if ( elem.offsetWidth !== 0 )
				getWH();
			else
				jQuery.swap( elem, props, getWH );

			return Math.max(0, Math.round(val));
		}

		return jQuery.curCSS( elem, name, force );
	},

	curCSS: function( elem, name, force ) {
		var ret, style = elem.style;

		// We need to handle opacity special in IE
		if ( name == "opacity" && !jQuery.support.opacity ) {
			ret = jQuery.attr( style, "opacity" );

			return ret == "" ?
				"1" :
				ret;
		}

		// Make sure we're using the right name for getting the float value
		if ( name.match( /float/i ) )
			name = styleFloat;

		if ( !force && style && style[ name ] )
			ret = style[ name ];

		else if ( defaultView.getComputedStyle ) {

			// Only "float" is needed here
			if ( name.match( /float/i ) )
				name = "float";

			name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();

			var computedStyle = defaultView.getComputedStyle( elem, null );

			if ( computedStyle )
				ret = computedStyle.getPropertyValue( name );

			// We should always get a number back from opacity
			if ( name == "opacity" && ret == "" )
				ret = "1";

		} else if ( elem.currentStyle ) {
			var camelCase = name.replace(/\-(\w)/g, function(all, letter){
				return letter.toUpperCase();
			});

			ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];

			// From the awesome hack by Dean Edwards
			// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291

			// If we're not dealing with a regular pixel number
			// but a number that has a weird ending, we need to convert it to pixels
			if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
				// Remember the original values
				var left = style.left, rsLeft = elem.runtimeStyle.left;

				// Put in the new values to get a computed value out
				elem.runtimeStyle.left = elem.currentStyle.left;
				style.left = ret || 0;
				ret = style.pixelLeft + "px";

				// Revert the changed values
				style.left = left;
				elem.runtimeStyle.left = rsLeft;
			}
		}

		return ret;
	},

	clean: function( elems, context, fragment ) {
		context = context || document;

		// !context.createElement fails in IE with an error but returns typeof 'object'
		if ( typeof context.createElement === "undefined" )
			context = context.ownerDocument || context[0] && context[0].ownerDocument || document;

		// If a single string is passed in and it's a single tag
		// just do a createElement and skip the rest
		if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) {
			var match = /^<(\w+)\s*\/?>$/.exec(elems[0]);
			if ( match )
				return [ context.createElement( match[1] ) ];
		}

		var ret = [], scripts = [], div = context.createElement("div");

		jQuery.each(elems, function(i, elem){
			if ( typeof elem === "number" )
				elem += '';

			if ( !elem )
				return;

			// Convert html string into DOM nodes
			if ( typeof elem === "string" ) {
				// Fix "XHTML"-style tags in all browsers
				elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){
					return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ?
						all :
						front + "></" + tag + ">";
				});

				// Trim whitespace, otherwise indexOf won't work as expected
				var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase();

				var wrap =
					// option or optgroup
					!tags.indexOf("<opt") &&
					[ 1, "<select multiple='multiple'>", "</select>" ] ||

					!tags.indexOf("<leg") &&
					[ 1, "<fieldset>", "</fieldset>" ] ||

					tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
					[ 1, "<table>", "</table>" ] ||

					!tags.indexOf("<tr") &&
					[ 2, "<table><tbody>", "</tbody></table>" ] ||

				 	// <thead> matched above
					(!tags.indexOf("<td") || !tags.indexOf("<th")) &&
					[ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||

					!tags.indexOf("<col") &&
					[ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||

					// IE can't serialize <link> and <script> tags normally
					!jQuery.support.htmlSerialize &&
					[ 1, "div<div>", "</div>" ] ||

					[ 0, "", "" ];

				// Go to html and back, then peel off extra wrappers
				div.innerHTML = wrap[1] + elem + wrap[2];

				// Move to the right depth
				while ( wrap[0]-- )
					div = div.lastChild;

				// Remove IE's autoinserted <tbody> from table fragments
				if ( !jQuery.support.tbody ) {

					// String was a <table>, *may* have spurious <tbody>
					var hasBody = /<tbody/i.test(elem),
						tbody = !tags.indexOf("<table") && !hasBody ?
							div.firstChild && div.firstChild.childNodes :

						// String was a bare <thead> or <tfoot>
						wrap[1] == "<table>" && !hasBody ?
							div.childNodes :
							[];

					for ( var j = tbody.length - 1; j >= 0 ; --j )
						if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length )
							tbody[ j ].parentNode.removeChild( tbody[ j ] );

					}

				// IE completely kills leading whitespace when innerHTML is used
				if ( !jQuery.support.leadingWhitespace && /^\s/.test( elem ) )
					div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild );

				elem = jQuery.makeArray( div.childNodes );
			}

			if ( elem.nodeType )
				ret.push( elem );
			else
				ret = jQuery.merge( ret, elem );

		});

		if ( fragment ) {
			for ( var i = 0; ret[i]; i++ ) {
				if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
					scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
				} else {
					if ( ret[i].nodeType === 1 )
						ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
					fragment.appendChild( ret[i] );
				}
			}

			return scripts;
		}

		return ret;
	},

	attr: function( elem, name, value ) {
		// don't set attributes on text and comment nodes
		if (!elem || elem.nodeType == 3 || elem.nodeType == 8)
			return undefined;

		var notxml = !jQuery.isXMLDoc( elem ),
			// Whether we are setting (or getting)
			set = value !== undefined;

		// Try to normalize/fix the name
		name = notxml && jQuery.props[ name ] || name;

		// Only do all the following if this is a node (faster for style)
		// IE elem.getAttribute passes even for style
		if ( elem.tagName ) {

			// These attributes require special treatment
			var special = /href|src|style/.test( name );

			// Safari mis-reports the default selected property of a hidden option
			// Accessing the parent's selectedIndex property fixes it
			if ( name == "selected" && elem.parentNode )
				elem.parentNode.selectedIndex;

			// If applicable, access the attribute via the DOM 0 way
			if ( name in elem && notxml && !special ) {
				if ( set ){
					// We can't allow the type property to be changed (since it causes problems in IE)
					if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode )
						throw "type property can't be changed";

					elem[ name ] = value;
				}

				// browsers index elements by id/name on forms, give priority to attributes.
				if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) )
					return elem.getAttributeNode( name ).nodeValue;

				// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
				// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
				if ( name == "tabIndex" ) {
					var attributeNode = elem.getAttributeNode( "tabIndex" );
					return attributeNode && attributeNode.specified
						? attributeNode.value
						: elem.nodeName.match(/(button|input|object|select|textarea)/i)
							? 0
							: elem.nodeName.match(/^(a|area)$/i) && elem.href
								? 0
								: undefined;
				}

				return elem[ name ];
			}

			if ( !jQuery.support.style && notxml &&  name == "style" )
				return jQuery.attr( elem.style, "cssText", value );

			if ( set )
				// convert the value to a string (all browsers do this but IE) see #1070
				elem.setAttribute( name, "" + value );

			var attr = !jQuery.support.hrefNormalized && notxml && special
					// Some attributes require a special call on IE
					? elem.getAttribute( name )
					: elem.getAttribute( name );

			// Non-existent attributes return null, we normalize to undefined
			return attr === null ? undefined : attr;
		}

		// elem is actually elem.style ... set the style

		// IE uses filters for opacity
		if ( !jQuery.support.opacity && name == "opacity" ) {
			if ( set ) {
				// IE has trouble with opacity if it does not have layout
				// Force it by setting the zoom level
				elem.zoom = 1;

				// Set the alpha filter to set the opacity
				elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
					(parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
			}

			return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
				(parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
				"";
		}

		name = name.replace(/-([a-z])/ig, function(all, letter){
			return letter.toUpperCase();
		});

		if ( set )
			elem[ name ] = value;

		return elem[ name ];
	},

	trim: function( text ) {
		return (text || "").replace( /^\s+|\s+$/g, "" );
	},

	makeArray: function( array ) {
		var ret = [];

		if( array != null ){
			var i = array.length;
			// The window, strings (and functions) also have 'length'
			if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )
				ret[0] = array;
			else
				while( i )
					ret[--i] = array[i];
		}

		return ret;
	},

	inArray: function( elem, array ) {
		for ( var i = 0, length = array.length; i < length; i++ )
		// Use === because on IE, window == document
			if ( array[ i ] === elem )
				return i;

		return -1;
	},

	merge: function( first, second ) {
		// We have to loop this way because IE & Opera overwrite the length
		// expando of getElementsByTagName
		var i = 0, elem, pos = first.length;
		// Also, we need to make sure that the correct elements are being returned
		// (IE returns comment nodes in a '*' query)
		if ( !jQuery.support.getAll ) {
			while ( (elem = second[ i++ ]) != null )
				if ( elem.nodeType != 8 )
					first[ pos++ ] = elem;

		} else
			while ( (elem = second[ i++ ]) != null )
				first[ pos++ ] = elem;

		return first;
	},

	unique: function( array ) {
		var ret = [], done = {};

		try {

			for ( var i = 0, length = array.length; i < length; i++ ) {
				var id = jQuery.data( array[ i ] );

				if ( !done[ id ] ) {
					done[ id ] = true;
					ret.push( array[ i ] );
				}
			}

		} catch( e ) {
			ret = array;
		}

		return ret;
	},

	grep: function( elems, callback, inv ) {
		var ret = [];

		// Go through the array, only saving the items
		// that pass the validator function
		for ( var i = 0, length = elems.length; i < length; i++ )
			if ( !inv != !callback( elems[ i ], i ) )
				ret.push( elems[ i ] );

		return ret;
	},

	map: function( elems, callback ) {
		var ret = [];

		// Go through the array, translating each of the items to their
		// new value (or values).
		for ( var i = 0, length = elems.length; i < length; i++ ) {
			var value = callback( elems[ i ], i );

			if ( value != null )
				ret[ ret.length ] = value;
		}

		return ret.concat.apply( [], ret );
	}
});

// Use of jQuery.browser is deprecated.
// It's included for backwards compatibility and plugins,
// although they should work to migrate away.

var userAgent = navigator.userAgent.toLowerCase();

// Figure out what browser is being used
jQuery.browser = {
	version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
	safari: /webkit/.test( userAgent ),
	opera: /opera/.test( userAgent ),
	msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
	mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
};

jQuery.each({
	parent: function(elem){return elem.parentNode;},
	parents: function(elem){return jQuery.dir(elem,"parentNode");},
	next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
	prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
	nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
	prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
	siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
	children: function(elem){return jQuery.sibling(elem.firstChild);},
	contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
}, function(name, fn){
	jQuery.fn[ name ] = function( selector ) {
		var ret = jQuery.map( this, fn );

		if ( selector && typeof selector == "string" )
			ret = jQuery.multiFilter( selector, ret );

		return this.pushStack( jQuery.unique( ret ), name, selector );
	};
});

jQuery.each({
	appendTo: "append",
	prependTo: "prepend",
	insertBefore: "before",
	insertAfter: "after",
	replaceAll: "replaceWith"
}, function(name, original){
	jQuery.fn[ name ] = function( selector ) {
		var ret = [], insert = jQuery( selector );

		for ( var i = 0, l = insert.length; i < l; i++ ) {
			var elems = (i > 0 ? this.clone(true) : this).get();
			jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
			ret = ret.concat( elems );
		}

		return this.pushStack( ret, name, selector );
	};
});

jQuery.each({
	removeAttr: function( name ) {
		jQuery.attr( this, name, "" );
		if (this.nodeType == 1)
			this.removeAttribute( name );
	},

	addClass: function( classNames ) {
		jQuery.className.add( this, classNames );
	},

	removeClass: function( classNames ) {
		jQuery.className.remove( this, classNames );
	},

	toggleClass: function( classNames, state ) {
		if( typeof state !== "boolean" )
			state = !jQuery.className.has( this, classNames );
		jQuery.className[ state ? "add" : "remove" ]( this, classNames );
	},

	remove: function( selector ) {
		if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
			// Prevent memory leaks
			jQuery( "*", this ).add([this]).each(function(){
				jQuery.event.remove(this);
				jQuery.removeData(this);
			});
			if (this.parentNode)
				this.parentNode.removeChild( this );
		}
	},

	empty: function() {
		// Remove element nodes and prevent memory leaks
		jQuery(this).children().remove();

		// Remove any remaining nodes
		while ( this.firstChild )
			this.removeChild( this.firstChild );
	}
}, function(name, fn){
	jQuery.fn[ name ] = function(){
		return this.each( fn, arguments );
	};
});

// Helper function used by the dimensions and offset modules
function num(elem, prop) {
	return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
}
var expando = "jQuery" + now(), uuid = 0, windowData = {};

jQuery.extend({
	cache: {},

	data: function( elem, name, data ) {
		elem = elem == window ?
			windowData :
			elem;

		var id = elem[ expando ];

		// Compute a unique ID for the element
		if ( !id )
			id = elem[ expando ] = ++uuid;

		// Only generate the data cache if we're
		// trying to access or manipulate it
		if ( name && !jQuery.cache[ id ] )
			jQuery.cache[ id ] = {};

		// Prevent overriding the named cache with undefined values
		if ( data !== undefined )
			jQuery.cache[ id ][ name ] = data;

		// Return the named cache data, or the ID for the element
		return name ?
			jQuery.cache[ id ][ name ] :
			id;
	},

	removeData: function( elem, name ) {
		elem = elem == window ?
			windowData :
			elem;

		var id = elem[ expando ];

		// If we want to remove a specific section of the element's data
		if ( name ) {
			if ( jQuery.cache[ id ] ) {
				// Remove the section of cache data
				delete jQuery.cache[ id ][ name ];

				// If we've removed all the data, remove the element's cache
				name = "";

				for ( name in jQuery.cache[ id ] ) {
                    if (!jQuery.cache[ id ].hasOwnProperty(name)) continue;
					break;
				}

				if ( !name )
					jQuery.removeData( elem );
			}

		// Otherwise, we want to remove all of the element's data
		} else {
			// Clean up the element expando
			try {
				delete elem[ expando ];
			} catch(e){
				// IE has trouble directly removing the expando
				// but it's ok with using removeAttribute
				if ( elem.removeAttribute )
					elem.removeAttribute( expando );
			}

			// Completely remove the data cache
			delete jQuery.cache[ id ];
		}
	},
	queue: function( elem, type, data ) {
		if ( elem ){

			type = (type || "fx") + "queue";

			var q = jQuery.data( elem, type );

			if ( !q || jQuery.isArray(data) )
				q = jQuery.data( elem, type, jQuery.makeArray(data) );
			else if( data )
				q.push( data );

		}
		return q;
	},

	dequeue: function( elem, type ){
		var queue = jQuery.queue( elem, type ),
			fn = queue.shift();

		if( !type || type === "fx" )
			fn = queue[0];

		if( fn !== undefined )
			fn.call(elem);
	}
});

jQuery.fn.extend({
	data: function( key, value ){
		var parts = key.split(".");
		parts[1] = parts[1] ? "." + parts[1] : "";

		if ( value === undefined ) {
			var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);

			if ( data === undefined && this.length )
				data = jQuery.data( this[0], key );

			return data === undefined && parts[1] ?
				this.data( parts[0] ) :
				data;
		} else
			return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
				jQuery.data( this, key, value );
			});
	},

	removeData: function( key ){
		return this.each(function(){
			jQuery.removeData( this, key );
		});
	},
	queue: function(type, data){
		if ( typeof type !== "string" ) {
			data = type;
			type = "fx";
		}

		if ( data === undefined )
			return jQuery.queue( this[0], type );

		return this.each(function(){
			var queue = jQuery.queue( this, type, data );

			 if( type == "fx" && queue.length == 1 )
				queue[0].call(this);
		});
	},
	dequeue: function(type){
		return this.each(function(){
			jQuery.dequeue( this, type );
		});
	}
});/*!
 * Sizzle CSS Selector Engine - v0.9.3
 *  Copyright 2009, The Dojo Foundation
 *  Released under the MIT, BSD, and GPL Licenses.
 *  More information: http://sizzlejs.com/
 */
(function(){

var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
	done = 0,
	toString = Object.prototype.toString;

var Sizzle = function(selector, context, results, seed) {
	results = results || [];
	context = context || document;

	if ( context.nodeType !== 1 && context.nodeType !== 9 )
		return [];

	if ( !selector || typeof selector !== "string" ) {
		return results;
	}

	var parts = [], m, set, checkSet, check, mode, extra, prune = true;

	// Reset the position of the chunker regexp (start from head)
	chunker.lastIndex = 0;

	while ( (m = chunker.exec(selector)) !== null ) {
		parts.push( m[1] );

		if ( m[2] ) {
			extra = RegExp.rightContext;
			break;
		}
	}

	if ( parts.length > 1 && origPOS.exec( selector ) ) {
		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
			set = posProcess( parts[0] + parts[1], context );
		} else {
			set = Expr.relative[ parts[0] ] ?
				[ context ] :
				Sizzle( parts.shift(), context );

			while ( parts.length ) {
				selector = parts.shift();

				if ( Expr.relative[ selector ] )
					selector += parts.shift();

				set = posProcess( selector, set );
			}
		}
	} else {
		var ret = seed ?
			{ expr: parts.pop(), set: makeArray(seed) } :
			Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
		set = Sizzle.filter( ret.expr, ret.set );

		if ( parts.length > 0 ) {
			checkSet = makeArray(set);
		} else {
			prune = false;
		}

		while ( parts.length ) {
			var cur = parts.pop(), pop = cur;

			if ( !Expr.relative[ cur ] ) {
				cur = "";
			} else {
				pop = parts.pop();
			}

			if ( pop == null ) {
				pop = context;
			}

			Expr.relative[ cur ]( checkSet, pop, isXML(context) );
		}
	}

	if ( !checkSet ) {
		checkSet = set;
	}

	if ( !checkSet ) {
		throw "Syntax error, unrecognized expression: " + (cur || selector);
	}

	if ( toString.call(checkSet) === "[object Array]" ) {
		if ( !prune ) {
			results.push.apply( results, checkSet );
		} else if ( context.nodeType === 1 ) {
			for ( var i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
					results.push( set[i] );
				}
			}
		} else {
			for ( var i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
					results.push( set[i] );
				}
			}
		}
	} else {
		makeArray( checkSet, results );
	}

	if ( extra ) {
		Sizzle( extra, context, results, seed );

		if ( sortOrder ) {
			hasDuplicate = false;
			results.sort(sortOrder);

			if ( hasDuplicate ) {
				for ( var i = 1; i < results.length; i++ ) {
					if ( results[i] === results[i-1] ) {
						results.splice(i--, 1);
					}
				}
			}
		}
	}

	return results;
};

Sizzle.matches = function(expr, set){
	return Sizzle(expr, null, null, set);
};

Sizzle.find = function(expr, context, isXML){
	var set, match;

	if ( !expr ) {
		return [];
	}

	for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
		var type = Expr.order[i], match;

		if ( (match = Expr.match[ type ].exec( expr )) ) {
			var left = RegExp.leftContext;

			if ( left.substr( left.length - 1 ) !== "\\" ) {
				match[1] = (match[1] || "").replace(/\\/g, "");
				set = Expr.find[ type ]( match, context, isXML );
				if ( set != null ) {
					expr = expr.replace( Expr.match[ type ], "" );
					break;
				}
			}
		}
	}

	if ( !set ) {
		set = context.getElementsByTagName("*");
	}

	return {set: set, expr: expr};
};

Sizzle.filter = function(expr, set, inplace, not){
	var old = expr, result = [], curLoop = set, match, anyFound,
		isXMLFilter = set && set[0] && isXML(set[0]);

	while ( expr && set.length ) {
		for ( var type in Expr.filter ) {
            if (!Expr.filter.hasOwnProperty(type)) continue;
			if ( (match = Expr.match[ type ].exec( expr )) != null ) {
				var filter = Expr.filter[ type ], found, item;
				anyFound = false;

				if ( curLoop == result ) {
					result = [];
				}

				if ( Expr.preFilter[ type ] ) {
					match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );

					if ( !match ) {
						anyFound = found = true;
					} else if ( match === true ) {
						continue;
					}
				}

				if ( match ) {
					for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
						if ( item ) {
							found = filter( item, match, i, curLoop );
							var pass = not ^ !!found;

							if ( inplace && found != null ) {
								if ( pass ) {
									anyFound = true;
								} else {
									curLoop[i] = false;
								}
							} else if ( pass ) {
								result.push( item );
								anyFound = true;
							}
						}
					}
				}

				if ( found !== undefined ) {
					if ( !inplace ) {
						curLoop = result;
					}

					expr = expr.replace( Expr.match[ type ], "" );

					if ( !anyFound ) {
						return [];
					}

					break;
				}
			}
		}

		// Improper expression
		if ( expr == old ) {
			if ( anyFound == null ) {
				throw "Syntax error, unrecognized expression: " + expr;
			} else {
				break;
			}
		}

		old = expr;
	}

	return curLoop;
};

var Expr = Sizzle.selectors = {
	order: [ "ID", "NAME", "TAG" ],
	match: {
		ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
		CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
		NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
		ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
		TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
		CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
		POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
		PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
	},
	attrMap: {
		"class": "className",
		"for": "htmlFor"
	},
	attrHandle: {
		href: function(elem){
			return elem.getAttribute("href");
		}
	},
	relative: {
		"+": function(checkSet, part, isXML){
			var isPartStr = typeof part === "string",
				isTag = isPartStr && !/\W/.test(part),
				isPartStrNotTag = isPartStr && !isTag;

			if ( isTag && !isXML ) {
				part = part.toUpperCase();
			}

			for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
				if ( (elem = checkSet[i]) ) {
					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}

					checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
						elem || false :
						elem === part;
				}
			}

			if ( isPartStrNotTag ) {
				Sizzle.filter( part, checkSet, true );
			}
		},
		">": function(checkSet, part, isXML){
			var isPartStr = typeof part === "string";

			if ( isPartStr && !/\W/.test(part) ) {
				part = isXML ? part : part.toUpperCase();

				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
					var elem = checkSet[i];
					if ( elem ) {
						var parent = elem.parentNode;
						checkSet[i] = parent.nodeName === part ? parent : false;
					}
				}
			} else {
				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
					var elem = checkSet[i];
					if ( elem ) {
						checkSet[i] = isPartStr ?
							elem.parentNode :
							elem.parentNode === part;
					}
				}

				if ( isPartStr ) {
					Sizzle.filter( part, checkSet, true );
				}
			}
		},
		"": function(checkSet, part, isXML){
			var doneName = done++, checkFn = dirCheck;

			if ( !part.match(/\W/) ) {
				var nodeCheck = part = isXML ? part : part.toUpperCase();
				checkFn = dirNodeCheck;
			}

			checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
		},
		"~": function(checkSet, part, isXML){
			var doneName = done++, checkFn = dirCheck;

			if ( typeof part === "string" && !part.match(/\W/) ) {
				var nodeCheck = part = isXML ? part : part.toUpperCase();
				checkFn = dirNodeCheck;
			}

			checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
		}
	},
	find: {
		ID: function(match, context, isXML){
			if ( typeof context.getElementById !== "undefined" && !isXML ) {
				var m = context.getElementById(match[1]);
				return m ? [m] : [];
			}
		},
		NAME: function(match, context, isXML){
			if ( typeof context.getElementsByName !== "undefined" ) {
				var ret = [], results = context.getElementsByName(match[1]);

				for ( var i = 0, l = results.length; i < l; i++ ) {
					if ( results[i].getAttribute("name") === match[1] ) {
						ret.push( results[i] );
					}
				}

				return ret.length === 0 ? null : ret;
			}
		},
		TAG: function(match, context){
			return context.getElementsByTagName(match[1]);
		}
	},
	preFilter: {
		CLASS: function(match, curLoop, inplace, result, not, isXML){
			match = " " + match[1].replace(/\\/g, "") + " ";

			if ( isXML ) {
				return match;
			}

			for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
				if ( elem ) {
					if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
						if ( !inplace )
							result.push( elem );
					} else if ( inplace ) {
						curLoop[i] = false;
					}
				}
			}

			return false;
		},
		ID: function(match){
			return match[1].replace(/\\/g, "");
		},
		TAG: function(match, curLoop){
			for ( var i = 0; curLoop[i] === false; i++ ){}
			return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
		},
		CHILD: function(match){
			if ( match[1] == "nth" ) {
				// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
				var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
					match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
					!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);

				// calculate the numbers (first)n+(last) including if they are negative
				match[2] = (test[1] + (test[2] || 1)) - 0;
				match[3] = test[3] - 0;
			}

			// TODO: Move to normal caching system
			match[0] = done++;

			return match;
		},
		ATTR: function(match, curLoop, inplace, result, not, isXML){
			var name = match[1].replace(/\\/g, "");

			if ( !isXML && Expr.attrMap[name] ) {
				match[1] = Expr.attrMap[name];
			}

			if ( match[2] === "~=" ) {
				match[4] = " " + match[4] + " ";
			}

			return match;
		},
		PSEUDO: function(match, curLoop, inplace, result, not){
			if ( match[1] === "not" ) {
				// If we're dealing with a complex expression, or a simple one
				if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
					match[3] = Sizzle(match[3], null, null, curLoop);
				} else {
					var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
					if ( !inplace ) {
						result.push.apply( result, ret );
					}
					return false;
				}
			} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
				return true;
			}

			return match;
		},
		POS: function(match){
			match.unshift( true );
			return match;
		}
	},
	filters: {
		enabled: function(elem){
			return elem.disabled === false && elem.type !== "hidden";
		},
		disabled: function(elem){
			return elem.disabled === true;
		},
		checked: function(elem){
			return elem.checked === true;
		},
		selected: function(elem){
			// Accessing this property makes selected-by-default
			// options in Safari work properly
			elem.parentNode.selectedIndex;
			return elem.selected === true;
		},
		parent: function(elem){
			return !!elem.firstChild;
		},
		empty: function(elem){
			return !elem.firstChild;
		},
		has: function(elem, i, match){
			return !!Sizzle( match[3], elem ).length;
		},
		header: function(elem){
			return /h\d/i.test( elem.nodeName );
		},
		text: function(elem){
			return "text" === elem.type;
		},
		radio: function(elem){
			return "radio" === elem.type;
		},
		checkbox: function(elem){
			return "checkbox" === elem.type;
		},
		file: function(elem){
			return "file" === elem.type;
		},
		password: function(elem){
			return "password" === elem.type;
		},
		submit: function(elem){
			return "submit" === elem.type;
		},
		image: function(elem){
			return "image" === elem.type;
		},
		reset: function(elem){
			return "reset" === elem.type;
		},
		button: function(elem){
			return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
		},
		input: function(elem){
			return /input|select|textarea|button/i.test(elem.nodeName);
		}
	},
	setFilters: {
		first: function(elem, i){
			return i === 0;
		},
		last: function(elem, i, match, array){
			return i === array.length - 1;
		},
		even: function(elem, i){
			return i % 2 === 0;
		},
		odd: function(elem, i){
			return i % 2 === 1;
		},
		lt: function(elem, i, match){
			return i < match[3] - 0;
		},
		gt: function(elem, i, match){
			return i > match[3] - 0;
		},
		nth: function(elem, i, match){
			return match[3] - 0 == i;
		},
		eq: function(elem, i, match){
			return match[3] - 0 == i;
		}
	},
	filter: {
		PSEUDO: function(elem, match, i, array){
			var name = match[1], filter = Expr.filters[ name ];

			if ( filter ) {
				return filter( elem, i, match, array );
			} else if ( name === "contains" ) {
				return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
			} else if ( name === "not" ) {
				var not = match[3];

				for ( var i = 0, l = not.length; i < l; i++ ) {
					if ( not[i] === elem ) {
						return false;
					}
				}

				return true;
			}
		},
		CHILD: function(elem, match){
			var type = match[1], node = elem;
			switch (type) {
				case 'only':
				case 'first':
					while (node = node.previousSibling)  {
						if ( node.nodeType === 1 ) return false;
					}
					if ( type == 'first') return true;
					node = elem;
				case 'last':
					while (node = node.nextSibling)  {
						if ( node.nodeType === 1 ) return false;
					}
					return true;
				case 'nth':
					var first = match[2], last = match[3];

					if ( first == 1 && last == 0 ) {
						return true;
					}

					var doneName = match[0],
						parent = elem.parentNode;

					if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
						var count = 0;
						for ( node = parent.firstChild; node; node = node.nextSibling ) {
							if ( node.nodeType === 1 ) {
								node.nodeIndex = ++count;
							}
						}
						parent.sizcache = doneName;
					}

					var diff = elem.nodeIndex - last;
					if ( first == 0 ) {
						return diff == 0;
					} else {
						return ( diff % first == 0 && diff / first >= 0 );
					}
			}
		},
		ID: function(elem, match){
			return elem.nodeType === 1 && elem.getAttribute("id") === match;
		},
		TAG: function(elem, match){
			return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
		},
		CLASS: function(elem, match){
			return (" " + (elem.className || elem.getAttribute("class")) + " ")
				.indexOf( match ) > -1;
		},
		ATTR: function(elem, match){
			var name = match[1],
				result = Expr.attrHandle[ name ] ?
					Expr.attrHandle[ name ]( elem ) :
					elem[ name ] != null ?
						elem[ name ] :
						elem.getAttribute( name ),
				value = result + "",
				type = match[2],
				check = match[4];

			return result == null ?
				type === "!=" :
				type === "=" ?
				value === check :
				type === "*=" ?
				value.indexOf(check) >= 0 :
				type === "~=" ?
				(" " + value + " ").indexOf(check) >= 0 :
				!check ?
				value && result !== false :
				type === "!=" ?
				value != check :
				type === "^=" ?
				value.indexOf(check) === 0 :
				type === "$=" ?
				value.substr(value.length - check.length) === check :
				type === "|=" ?
				value === check || value.substr(0, check.length + 1) === check + "-" :
				false;
		},
		POS: function(elem, match, i, array){
			var name = match[2], filter = Expr.setFilters[ name ];

			if ( filter ) {
				return filter( elem, i, match, array );
			}
		}
	}
};

var origPOS = Expr.match.POS;

for ( var type in Expr.match ) {
    if (!Expr.match.hasOwnProperty(type)) continue;
	Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
}

var makeArray = function(array, results) {
	array = Array.prototype.slice.call( array );

	if ( results ) {
		results.push.apply( results, array );
		return results;
	}

	return array;
};

// Perform a simple check to determine if the browser is capable of
// converting a NodeList to an array using builtin methods.
try {
	Array.prototype.slice.call( document.documentElement.childNodes );

// Provide a fallback method if it does not work
} catch(e){
	makeArray = function(array, results) {
		var ret = results || [];

		if ( toString.call(array) === "[object Array]" ) {
			Array.prototype.push.apply( ret, array );
		} else {
			if ( typeof array.length === "number" ) {
				for ( var i = 0, l = array.length; i < l; i++ ) {
					ret.push( array[i] );
				}
			} else {
				for ( var i = 0; array[i]; i++ ) {
					ret.push( array[i] );
				}
			}
		}

		return ret;
	};
}

var sortOrder;

if ( document.documentElement.compareDocumentPosition ) {
	sortOrder = function( a, b ) {
		var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
} else if ( "sourceIndex" in document.documentElement ) {
	sortOrder = function( a, b ) {
		var ret = a.sourceIndex - b.sourceIndex;
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
} else if ( document.createRange ) {
	sortOrder = function( a, b ) {
		var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
		aRange.selectNode(a);
		aRange.collapse(true);
		bRange.selectNode(b);
		bRange.collapse(true);
		var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
		if ( ret === 0 ) {
			hasDuplicate = true;
		}
		return ret;
	};
}

// Check to see if the browser returns elements by name when
// querying by getElementById (and provide a workaround)
(function(){
	// We're going to inject a fake input element with a specified name
	var form = document.createElement("form"),
		id = "script" + (new Date).getTime();
	form.innerHTML = "<input name='" + id + "'/>";

	// Inject it into the root element, check its status, and remove it quickly
	var root = document.documentElement;
	root.insertBefore( form, root.firstChild );

	// The workaround has to do additional checks after a getElementById
	// Which slows things down for other browsers (hence the branching)
	if ( !!document.getElementById( id ) ) {
		Expr.find.ID = function(match, context, isXML){
			if ( typeof context.getElementById !== "undefined" && !isXML ) {
				var m = context.getElementById(match[1]);
				return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
			}
		};

		Expr.filter.ID = function(elem, match){
			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
			return elem.nodeType === 1 && node && node.nodeValue === match;
		};
	}

	root.removeChild( form );
})();

(function(){
	// Check to see if the browser returns only elements
	// when doing getElementsByTagName("*")

	// Create a fake element
	var div = document.createElement("div");
	div.appendChild( document.createComment("") );

	// Make sure no comments are found
	if ( div.getElementsByTagName("*").length > 0 ) {
		Expr.find.TAG = function(match, context){
			var results = context.getElementsByTagName(match[1]);

			// Filter out possible comments
			if ( match[1] === "*" ) {
				var tmp = [];

				for ( var i = 0; results[i]; i++ ) {
					if ( results[i].nodeType === 1 ) {
						tmp.push( results[i] );
					}
				}

				results = tmp;
			}

			return results;
		};
	}

	// Check to see if an attribute returns normalized href attributes
	div.innerHTML = "<a href='#'></a>";
	if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
			div.firstChild.getAttribute("href") !== "#" ) {
		Expr.attrHandle.href = function(elem){
			return elem.getAttribute("href", 2);
		};
	}
})();

if ( document.querySelectorAll ) (function(){
	var oldSizzle = Sizzle, div = document.createElement("div");
	div.innerHTML = "<p class='TEST'></p>";

	// Safari can't handle uppercase or unicode characters when
	// in quirks mode.
	if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
		return;
	}

	Sizzle = function(query, context, extra, seed){
		context = context || document;

		// Only use querySelectorAll on non-XML documents
		// (ID selectors don't work in non-HTML documents)
		if ( !seed && context.nodeType === 9 && !isXML(context) ) {
			try {
				return makeArray( context.querySelectorAll(query), extra );
			} catch(e){}
		}

		return oldSizzle(query, context, extra, seed);
	};

	Sizzle.find = oldSizzle.find;
	Sizzle.filter = oldSizzle.filter;
	Sizzle.selectors = oldSizzle.selectors;
	Sizzle.matches = oldSizzle.matches;
})();

if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
	var div = document.createElement("div");
	div.innerHTML = "<div class='test e'></div><div class='test'></div>";

	// Opera can't find a second classname (in 9.6)
	if ( div.getElementsByClassName("e").length === 0 )
		return;

	// Safari caches class attributes, doesn't catch changes (in 3.2)
	div.lastChild.className = "e";

	if ( div.getElementsByClassName("e").length === 1 )
		return;

	Expr.order.splice(1, 0, "CLASS");
	Expr.find.CLASS = function(match, context, isXML) {
		if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
			return context.getElementsByClassName(match[1]);
		}
	};
})();

function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
	var sibDir = dir == "previousSibling" && !isXML;
	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
		var elem = checkSet[i];
		if ( elem ) {
			if ( sibDir && elem.nodeType === 1 ){
				elem.sizcache = doneName;
				elem.sizset = i;
			}
			elem = elem[dir];
			var match = false;

			while ( elem ) {
				if ( elem.sizcache === doneName ) {
					match = checkSet[elem.sizset];
					break;
				}

				if ( elem.nodeType === 1 && !isXML ){
					elem.sizcache = doneName;
					elem.sizset = i;
				}

				if ( elem.nodeName === cur ) {
					match = elem;
					break;
				}

				elem = elem[dir];
			}

			checkSet[i] = match;
		}
	}
}

function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
	var sibDir = dir == "previousSibling" && !isXML;
	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
		var elem = checkSet[i];
		if ( elem ) {
			if ( sibDir && elem.nodeType === 1 ) {
				elem.sizcache = doneName;
				elem.sizset = i;
			}
			elem = elem[dir];
			var match = false;

			while ( elem ) {
				if ( elem.sizcache === doneName ) {
					match = checkSet[elem.sizset];
					break;
				}

				if ( elem.nodeType === 1 ) {
					if ( !isXML ) {
						elem.sizcache = doneName;
						elem.sizset = i;
					}
					if ( typeof cur !== "string" ) {
						if ( elem === cur ) {
							match = true;
							break;
						}

					} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
						match = elem;
						break;
					}
				}

				elem = elem[dir];
			}

			checkSet[i] = match;
		}
	}
}

var contains = document.compareDocumentPosition ?  function(a, b){
	return a.compareDocumentPosition(b) & 16;
} : function(a, b){
	return a !== b && (a.contains ? a.contains(b) : true);
};

var isXML = function(elem){
	return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
		!!elem.ownerDocument && isXML( elem.ownerDocument );
};

var posProcess = function(selector, context){
	var tmpSet = [], later = "", match,
		root = context.nodeType ? [context] : context;

	// Position selectors must be done after the filter
	// And so must :not(positional) so we move all PSEUDOs to the end
	while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
		later += match[0];
		selector = selector.replace( Expr.match.PSEUDO, "" );
	}

	selector = Expr.relative[selector] ? selector + "*" : selector;

	for ( var i = 0, l = root.length; i < l; i++ ) {
		Sizzle( selector, root[i], tmpSet );
	}

	return Sizzle.filter( later, tmpSet );
};

// EXPOSE
jQuery.find = Sizzle;
jQuery.filter = Sizzle.filter;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.filters;

Sizzle.selectors.filters.hidden = function(elem){
	return elem.offsetWidth === 0 || elem.offsetHeight === 0;
};

Sizzle.selectors.filters.visible = function(elem){
	return elem.offsetWidth > 0 || elem.offsetHeight > 0;
};

Sizzle.selectors.filters.animated = function(elem){
	return jQuery.grep(jQuery.timers, function(fn){
		return elem === fn.elem;
	}).length;
};

jQuery.multiFilter = function( expr, elems, not ) {
	if ( not ) {
		expr = ":not(" + expr + ")";
	}

	return Sizzle.matches(expr, elems);
};

jQuery.dir = function( elem, dir ){
	var matched = [], cur = elem[dir];
	while ( cur && cur != document ) {
		if ( cur.nodeType == 1 )
			matched.push( cur );
		cur = cur[dir];
	}
	return matched;
};

jQuery.nth = function(cur, result, dir, elem){
	result = result || 1;
	var num = 0;

	for ( ; cur; cur = cur[dir] )
		if ( cur.nodeType == 1 && ++num == result )
			break;

	return cur;
};

jQuery.sibling = function(n, elem){
	var r = [];

	for ( ; n; n = n.nextSibling ) {
		if ( n.nodeType == 1 && n != elem )
			r.push( n );
	}

	return r;
};

return;

window.Sizzle = Sizzle;

})();
/*
 * A number of helper functions used for managing events.
 * Many of the ideas behind this code originated from
 * Dean Edwards' addEvent library.
 */
jQuery.event = {

	// Bind an event to an element
	// Original by Dean Edwards
	add: function(elem, types, handler, data) {
		if ( elem.nodeType == 3 || elem.nodeType == 8 )
			return;

		// For whatever reason, IE has trouble passing the window object
		// around, causing it to be cloned in the process
		if ( elem.setInterval && elem != window )
			elem = window;

		// Make sure that the function being executed has a unique ID
		if ( !handler.guid )
			handler.guid = this.guid++;

		// if data is passed, bind to handler
		if ( data !== undefined ) {
			// Create temporary function pointer to original handler
			var fn = handler;

			// Create unique handler function, wrapped around original handler
			handler = this.proxy( fn );

			// Store data in unique handler
			handler.data = data;
		}

		// Init the element's event structure
		var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
			handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){
				// Handle the second event of a trigger and when
				// an event is called after a page has unloaded
				return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
					jQuery.event.handle.apply(arguments.callee.elem, arguments) :
					undefined;
			});
		// Add elem as a property of the handle function
		// This is to prevent a memory leak with non-native
		// event in IE.
		handle.elem = elem;

		// Handle multiple events separated by a space
		// jQuery(...).bind("mouseover mouseout", fn);
		jQuery.each(types.split(/\s+/), function(index, type) {
			// Namespaced event handlers
			var namespaces = type.split(".");
			type = namespaces.shift();
			handler.type = namespaces.slice().sort().join(".");

			// Get the current list of functions bound to this event
			var handlers = events[type];

			if ( jQuery.event.specialAll[type] )
				jQuery.event.specialAll[type].setup.call(elem, data, namespaces);

			// Init the event handler queue
			if (!handlers) {
				handlers = events[type] = {};

				// Check for a special event handler
				// Only use addEventListener/attachEvent if the special
				// events handler returns false
				if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem, data, namespaces) === false ) {
					// Bind the global event handler to the element
					if (elem.addEventListener)
						elem.addEventListener(type, handle, false);
					else if (elem.attachEvent)
						elem.attachEvent("on" + type, handle);
				}
			}

			// Add the function to the element's handler list
			handlers[handler.guid] = handler;

			// Keep track of which events have been used, for global triggering
			jQuery.event.global[type] = true;
		});

		// Nullify elem to prevent memory leaks in IE
		elem = null;
	},

	guid: 1,
	global: {},

	// Detach an event or set of events from an element
	remove: function(elem, types, handler) {
		// don't do events on text and comment nodes
		if ( elem.nodeType == 3 || elem.nodeType == 8 )
			return;

		var events = jQuery.data(elem, "events"), ret, index;

		if ( events ) {
			// Unbind all events for the element
			if ( types === undefined || (typeof types === "string" && types.charAt(0) == ".") )
				for ( var type in events ) {
                    if (!events.hasOwnProperty(type)) continue;
					this.remove( elem, type + (types || "") );
				}
			else {
				// types is actually an event object here
				if ( types.type ) {
					handler = types.handler;
					types = types.type;
				}

				// Handle multiple events seperated by a space
				// jQuery(...).unbind("mouseover mouseout", fn);
				jQuery.each(types.split(/\s+/), function(index, type){
					// Namespaced event handlers
					var namespaces = type.split(".");
					type = namespaces.shift();
					var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");

					if ( events[type] ) {
						// remove the given handler for the given type
						if ( handler )
							delete events[type][handler.guid];

						// remove all handlers for the given type
						else
							for ( var handle in events[type] ) {
                                if (!events[type].hasOwnProperty(handle)) continue;
								// Handle the removal of namespaced events
								if ( namespace.test(events[type][handle].type) )
									delete events[type][handle];
                            }

						if ( jQuery.event.specialAll[type] && jQuery.event.specialAll[type].teardown )
							jQuery.event.specialAll[type].teardown.call(elem, namespaces);

						// remove generic event handler if no more handlers exist
						for ( ret in events[type] ) {
                            if (!events[type].hasOwnProperty(ret)) continue;
                            break;
                        }
						if ( !ret ) {
							if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem, namespaces) === false ) {
								if (elem.removeEventListener)
									elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
								else if (elem.detachEvent)
									elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
							}
							ret = null;
							delete events[type];
						}
					}
				});
			}

			// Remove the expando if it's no longer used
			for ( ret in events ) {
                if (!events.hasOwnProperty(ret)) continue;
                break;
            }

			if ( !ret ) {
				var handle = jQuery.data( elem, "handle" );
				if ( handle ) handle.elem = null;
				jQuery.removeData( elem, "events" );
				jQuery.removeData( elem, "handle" );
			}
		}
	},

	// bubbling is internal
	trigger: function( event, data, elem, bubbling ) {
		// Event object or event type
		var type = event.type || event;
		if( !bubbling ){
			event = typeof event === "object" ?
				// jQuery.Event object
				event[expando] ? event :
				// Object literal
				jQuery.extend( jQuery.Event(type), event ) :
				// Just the event type (string)
				jQuery.Event(type);

			if ( type.indexOf("!") >= 0 ) {
				event.type = type = type.slice(0, -1);
				event.exclusive = true;
			}

			// Handle a global trigger
			if ( !elem ) {
				// Don't bubble custom events when global (to avoid too much overhead)
				event.stopPropagation();
				// Only trigger if we've ever bound an event for it
				if ( this.global[type] )
					jQuery.each( jQuery.cache, function(){
						if ( this.events && this.events[type] )
							jQuery.event.trigger( event, data, this.handle.elem );
					});
			}

			// Handle triggering a single element

			// don't do events on text and comment nodes
			if ( !elem || elem.nodeType == 3 || elem.nodeType == 8 )
				return undefined;

			// Clean up in case it is reused
			event.result = undefined;
			event.target = elem;

			// Clone the incoming data, if any
			data = jQuery.makeArray(data);
			data.unshift( event );
		}

		event.currentTarget = elem;

		// Trigger the event, it is assumed that "handle" is a function
		var handle = jQuery.data(elem, "handle");
		if ( handle )
			handle.apply( elem, data );

		// Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
		if ( (!elem[type] || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
			event.result = false;

		// Trigger the native events (except for clicks on links)
		if ( !bubbling && elem[type] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
			this.triggered = true;
			try {
				elem[ type ]();
			// prevent IE from throwing an error for some hidden elements
			} catch (e) {}
		}

		this.triggered = false;

		if ( !event.isPropagationStopped() ) {
			var parent = elem.parentNode || elem.ownerDocument;
			if ( parent )
				jQuery.event.trigger(event, data, parent, true);
		}
	},

	handle: function(event) {
		// returned undefined or false
		var all, handlers;

		event = arguments[0] = jQuery.event.fix( event || window.event );
		event.currentTarget = this;

		// Namespaced event handlers
		var namespaces = event.type.split(".");
		event.type = namespaces.shift();

		// Cache this now, all = true means, any handler
		all = !namespaces.length && !event.exclusive;

		var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");

		handlers = ( jQuery.data(this, "events") || {} )[event.type];

		for ( var j in handlers ) {
            if (!handlers.hasOwnProperty(j)) continue;
			var handler = handlers[j];

			// Filter the functions by class
			if ( all || namespace.test(handler.type) ) {
				// Pass in a reference to the handler function itself
				// So that we can later remove it
				event.handler = handler;
				event.data = handler.data;

				var ret = handler.apply(this, arguments);

				if( ret !== undefined ){
					event.result = ret;
					if ( ret === false ) {
						event.preventDefault();
						event.stopPropagation();
					}
				}

				if( event.isImmediatePropagationStopped() )
					break;

			}
		}
	},

	props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),

	fix: function(event) {
		if ( event[expando] )
			return event;

		// store a copy of the original event object
		// and "clone" to set read-only properties
		var originalEvent = event;
		event = jQuery.Event( originalEvent );

		for ( var i = this.props.length, prop; i; ){
			prop = this.props[ --i ];
			event[ prop ] = originalEvent[ prop ];
		}

		// Fix target property, if necessary
		if ( !event.target )
			event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either

		// check if target is a textnode (safari)
		if ( event.target.nodeType == 3 )
			event.target = event.target.parentNode;

		// Add relatedTarget, if necessary
		if ( !event.relatedTarget && event.fromElement )
			event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;

		// Calculate pageX/Y if missing and clientX/Y available
		if ( event.pageX == null && event.clientX != null ) {
			var doc = document.documentElement, body = document.body;
			event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
			event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
		}

		// Add which for key events
		if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) )
			event.which = event.charCode || event.keyCode;

		// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
		if ( !event.metaKey && event.ctrlKey )
			event.metaKey = event.ctrlKey;

		// Add which for click: 1 == left; 2 == middle; 3 == right
		// Note: button is not normalized, so don't use it
		if ( !event.which && event.button )
			event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));

		return event;
	},

	proxy: function( fn, proxy ){
		proxy = proxy || function(){ return fn.apply(this, arguments); };
		// Set the guid of unique handler to the same of original handler, so it can be removed
		proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++;
		// So proxy can be declared as an argument
		return proxy;
	},

	special: {
		ready: {
			// Make sure the ready event is setup
			setup: bindReady,
			teardown: function() {}
		}
	},

	specialAll: {
		live: {
			setup: function( selector, namespaces ){
				jQuery.event.add( this, namespaces[0], liveHandler );
			},
			teardown:  function( namespaces ){
				if ( namespaces.length ) {
					var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");

					jQuery.each( (jQuery.data(this, "events").live || {}), function(){
						if ( name.test(this.type) )
							remove++;
					});

					if ( remove < 1 )
						jQuery.event.remove( this, namespaces[0], liveHandler );
				}
			}
		}
	}
};

jQuery.Event = function( src ){
	// Allow instantiation without the 'new' keyword
	if( !this.preventDefault )
		return new jQuery.Event(src);

	// Event object
	if( src && src.type ){
		this.originalEvent = src;
		this.type = src.type;
	// Event type
	}else
		this.type = src;

	// timeStamp is buggy for some events on Firefox(#3843)
	// So we won't rely on the native value
	this.timeStamp = now();

	// Mark it as fixed
	this[expando] = true;
};

function returnFalse(){
	return false;
}
function returnTrue(){
	return true;
}

// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
jQuery.Event.prototype = {
	preventDefault: function() {
		this.isDefaultPrevented = returnTrue;

		var e = this.originalEvent;
		if( !e )
			return;
		// if preventDefault exists run it on the original event
		if (e.preventDefault)
			e.preventDefault();
		// otherwise set the returnValue property of the original event to false (IE)
		e.returnValue = false;
	},
	stopPropagation: function() {
		this.isPropagationStopped = returnTrue;

		var e = this.originalEvent;
		if( !e )
			return;
		// if stopPropagation exists run it on the original event
		if (e.stopPropagation)
			e.stopPropagation();
		// otherwise set the cancelBubble property of the original event to true (IE)
		e.cancelBubble = true;
	},
	stopImmediatePropagation:function(){
		this.isImmediatePropagationStopped = returnTrue;
		this.stopPropagation();
	},
	isDefaultPrevented: returnFalse,
	isPropagationStopped: returnFalse,
	isImmediatePropagationStopped: returnFalse
};
// Checks if an event happened on an element within another element
// Used in jQuery.event.special.mouseenter and mouseleave handlers
var withinElement = function(event) {
	// Check if mouse(over|out) are still within the same parent element
	var parent = event.relatedTarget;
	// Traverse up the tree
	while ( parent && parent != this )
		try { parent = parent.parentNode; }
		catch(e) { parent = this; }

	if( parent != this ){
		// set the correct event type
		event.type = event.data;
		// handle event if we actually just moused on to a non sub-element
		jQuery.event.handle.apply( this, arguments );
	}
};

jQuery.each({
	mouseover: 'mouseenter',
	mouseout: 'mouseleave'
}, function( orig, fix ){
	jQuery.event.special[ fix ] = {
		setup: function(){
			jQuery.event.add( this, orig, withinElement, fix );
		},
		teardown: function(){
			jQuery.event.remove( this, orig, withinElement );
		}
	};
});

jQuery.fn.extend({
	bind: function( type, data, fn ) {
		return type == "unload" ? this.one(type, data, fn) : this.each(function(){
			jQuery.event.add( this, type, fn || data, fn && data );
		});
	},

	one: function( type, data, fn ) {
		var one = jQuery.event.proxy( fn || data, function(event) {
			jQuery(this).unbind(event, one);
			return (fn || data).apply( this, arguments );
		});
		return this.each(function(){
			jQuery.event.add( this, type, one, fn && data);
		});
	},

	unbind: function( type, fn ) {
		return this.each(function(){
			jQuery.event.remove( this, type, fn );
		});
	},

	trigger: function( type, data ) {
		return this.each(function(){
			jQuery.event.trigger( type, data, this );
		});
	},

	triggerHandler: function( type, data ) {
		if( this[0] ){
			var event = jQuery.Event(type);
			event.preventDefault();
			event.stopPropagation();
			jQuery.event.trigger( event, data, this[0] );
			return event.result;
		}
	},

	toggle: function( fn ) {
		// Save reference to arguments for access in closure
		var args = arguments, i = 1;

		// link all the functions, so any of them can unbind this click handler
		while( i < args.length )
			jQuery.event.proxy( fn, args[i++] );

		return this.click( jQuery.event.proxy( fn, function(event) {
			// Figure out which function to execute
			this.lastToggle = ( this.lastToggle || 0 ) % i;

			// Make sure that clicks stop
			event.preventDefault();

			// and execute the function
			return args[ this.lastToggle++ ].apply( this, arguments ) || false;
		}));
	},

	hover: function(fnOver, fnOut) {
		return this.mouseenter(fnOver).mouseleave(fnOut);
	},

	ready: function(fn) {
		// Attach the listeners
		bindReady();

		// If the DOM is already ready
		if ( jQuery.isReady )
			// Execute the function immediately
			fn.call( document, jQuery );

		// Otherwise, remember the function for later
		else
			// Add the function to the wait list
			jQuery.readyList.push( fn );

		return this;
	},

	live: function( type, fn ){
		var proxy = jQuery.event.proxy( fn );
		proxy.guid += this.selector + type;

		jQuery(document).bind( liveConvert(type, this.selector), this.selector, proxy );

		return this;
	},

	die: function( type, fn ){
		jQuery(document).unbind( liveConvert(type, this.selector), fn ? { guid: fn.guid + this.selector + type } : null );
		return this;
	}
});

function liveHandler( event ){
	var check = RegExp("(^|\\.)" + event.type + "(\\.|$)"),
		stop = true,
		elems = [];

	jQuery.each(jQuery.data(this, "events").live || [], function(i, fn){
		if ( check.test(fn.type) ) {
			var elem = jQuery(event.target).closest(fn.data)[0];
			if ( elem )
				elems.push({ elem: elem, fn: fn });
		}
	});

	elems.sort(function(a,b) {
		return jQuery.data(a.elem, "closest") - jQuery.data(b.elem, "closest");
	});

	jQuery.each(elems, function(){
		if ( this.fn.call(this.elem, event, this.fn.data) === false )
			return (stop = false);
	});

	return stop;
}

function liveConvert(type, selector){
	return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "|")].join(".");
}

jQuery.extend({
	isReady: false,
	readyList: [],
	// Handle when the DOM is ready
	ready: function() {
		// Make sure that the DOM is not already loaded
		if ( !jQuery.isReady ) {
			// Remember that the DOM is ready
			jQuery.isReady = true;

			// If there are functions bound, to execute
			if ( jQuery.readyList ) {
				// Execute all of them
				jQuery.each( jQuery.readyList, function(){
					this.call( document, jQuery );
				});

				// Reset the list of functions
				jQuery.readyList = null;
			}

			// Trigger any bound ready events
			jQuery(document).triggerHandler("ready");
		}
	}
});

var readyBound = false;

function bindReady(){
	if ( readyBound ) return;
	readyBound = true;

	// Mozilla, Opera and webkit nightlies currently support this event
	if ( document.addEventListener ) {
		// Use the handy event callback
		document.addEventListener( "DOMContentLoaded", function(){
			document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
			jQuery.ready();
		}, false );

	// If IE event model is used
	} else if ( document.attachEvent ) {
		// ensure firing before onload,
		// maybe late but safe also for iframes
		document.attachEvent("onreadystatechange", function(){
			if ( document.readyState === "complete" ) {
				document.detachEvent( "onreadystatechange", arguments.callee );
				jQuery.ready();
			}
		});

		// If IE and not an iframe
		// continually check to see if the document is ready
		if ( document.documentElement.doScroll && window == window.top ) (function(){
			if ( jQuery.isReady ) return;

			try {
				// If IE is used, use the trick by Diego Perini
				// http://javascript.nwbox.com/IEContentLoaded/
				document.documentElement.doScroll("left");
			} catch( error ) {
				setTimeout( arguments.callee, 0 );
				return;
			}

			// and execute any waiting functions
			jQuery.ready();
		})();
	}

	// A fallback to window.onload, that will always work
	jQuery.event.add( window, "load", jQuery.ready );
}

jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
	"mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave," +
	"change,select,submit,keydown,keypress,keyup,error").split(","), function(i, name){

	// Handle event binding
	jQuery.fn[name] = function(fn){
		return fn ? this.bind(name, fn) : this.trigger(name);
	};
});

// Prevent memory leaks in IE
// And prevent errors on refresh with events like mouseover in other browsers
// Window isn't included so as not to unbind existing unload events
jQuery( window ).bind( 'unload', function(){
	for ( var id in jQuery.cache ) {
        if (!jQuery.cache.hasOwnProperty(id)) continue;
		// Skip the window
		if ( id != 1 && jQuery.cache[ id ].handle )
			jQuery.event.remove( jQuery.cache[ id ].handle.elem );
    }
});
(function(){

	jQuery.support = {};

	var root = document.documentElement,
		script = document.createElement("script"),
		div = document.createElement("div"),
		id = "script" + (new Date).getTime();

	div.style.display = "none";
	div.innerHTML = '   <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';

	var all = div.getElementsByTagName("*"),
		a = div.getElementsByTagName("a")[0];

	// Can't get basic test support
	if ( !all || !all.length || !a ) {
		return;
	}

	jQuery.support = {
		// IE strips leading whitespace when .innerHTML is used
		leadingWhitespace: div.firstChild.nodeType == 3,

		// Make sure that tbody elements aren't automatically inserted
		// IE will insert them into empty tables
		tbody: !div.getElementsByTagName("tbody").length,

		// Make sure that you can get all elements in an <object> element
		// IE 7 always returns no results
		objectAll: !!div.getElementsByTagName("object")[0]
			.getElementsByTagName("*").length,

		// Make sure that link elements get serialized correctly by innerHTML
		// This requires a wrapper element in IE
		htmlSerialize: !!div.getElementsByTagName("link").length,

		// Get the style information from getAttribute
		// (IE uses .cssText insted)
		style: /red/.test( a.getAttribute("style") ),

		// Make sure that URLs aren't manipulated
		// (IE normalizes it by default)
		hrefNormalized: a.getAttribute("href") === "/a",

		// Make sure that element opacity exists
		// (IE uses filter instead)
		opacity: a.style.opacity === "0.5",

		// Verify style float existence
		// (IE uses styleFloat instead of cssFloat)
		cssFloat: !!a.style.cssFloat,

		// Will be defined later
		scriptEval: false,
		noCloneEvent: true,
		boxModel: null
	};

	script.type = "text/javascript";
	try {
		script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
	} catch(e){}

	root.insertBefore( script, root.firstChild );

	// Make sure that the execution of code works by injecting a script
	// tag with appendChild/createTextNode
	// (IE doesn't support this, fails, and uses .text instead)
	if ( window[ id ] ) {
		jQuery.support.scriptEval = true;
		delete window[ id ];
	}

	root.removeChild( script );

	if ( div.attachEvent && div.fireEvent ) {
		div.attachEvent("onclick", function(){
			// Cloning a node shouldn't copy over any
			// bound event handlers (IE does this)
			jQuery.support.noCloneEvent = false;
			div.detachEvent("onclick", arguments.callee);
		});
		div.cloneNode(true).fireEvent("onclick");
	}

	// Figure out if the W3C box model works as expected
	// document.body must exist before we can do this
	jQuery(function(){
		var div = document.createElement("div");
		div.style.width = div.style.paddingLeft = "1px";

		document.body.appendChild( div );
		jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
		document.body.removeChild( div ).style.display = 'none';
	});
})();

var styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat";

jQuery.props = {
	"for": "htmlFor",
	"class": "className",
	"float": styleFloat,
	cssFloat: styleFloat,
	styleFloat: styleFloat,
	readonly: "readOnly",
	maxlength: "maxLength",
	cellspacing: "cellSpacing",
	rowspan: "rowSpan",
	tabindex: "tabIndex"
};
jQuery.fn.extend({
	// Keep a copy of the old load
	_load: jQuery.fn.load,

	load: function( url, params, callback ) {
		if ( typeof url !== "string" )
			return this._load( url );

		var off = url.indexOf(" ");
		if ( off >= 0 ) {
			var selector = url.slice(off, url.length);
			url = url.slice(0, off);
		}

		// Default to a GET request
		var type = "GET";

		// If the second parameter was provided
		if ( params )
			// If it's a function
			if ( jQuery.isFunction( params ) ) {
				// We assume that it's the callback
				callback = params;
				params = null;

			// Otherwise, build a param string
			} else if( typeof params === "object" ) {
				params = jQuery.param( params );
				type = "POST";
			}

		var self = this;

		// Request the remote document
		jQuery.ajax({
			url: url,
			type: type,
			dataType: "html",
			data: params,
			complete: function(res, status){
				// If successful, inject the HTML into all the matched elements
				if ( status == "success" || status == "notmodified" )
					// See if a selector was specified
					self.html( selector ?
						// Create a dummy div to hold the results
						jQuery("<div/>")
							// inject the contents of the document in, removing the scripts
							// to avoid any 'Permission Denied' errors in IE
							.append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))

							// Locate the specified elements
							.find(selector) :

						// If not, just inject the full result
						res.responseText );

				if( callback )
					self.each( callback, [res.responseText, status, res] );
			}
		});
		return this;
	},

	serialize: function() {
		return jQuery.param(this.serializeArray());
	},
	serializeArray: function() {
		return this.map(function(){
			return this.elements ? jQuery.makeArray(this.elements) : this;
		})
		.filter(function(){
			return this.name && !this.disabled &&
				(this.checked || /select|textarea/i.test(this.nodeName) ||
					/text|hidden|password|search/i.test(this.type));
		})
		.map(function(i, elem){
			var val = jQuery(this).val();
			return val == null ? null :
				jQuery.isArray(val) ?
					jQuery.map( val, function(val, i){
						return {name: elem.name, value: val};
					}) :
					{name: elem.name, value: val};
		}).get();
	}
});

// Attach a bunch of functions for handling common AJAX events
jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
	jQuery.fn[o] = function(f){
		return this.bind(o, f);
	};
});

var jsc = now();

jQuery.extend({

	get: function( url, data, callback, type ) {
		// shift arguments if data argument was ommited
		if ( jQuery.isFunction( data ) ) {
			callback = data;
			data = null;
		}

		return jQuery.ajax({
			type: "GET",
			url: url,
			data: data,
			success: callback,
			dataType: type
		});
	},

	getScript: function( url, callback ) {
		return jQuery.get(url, null, callback, "script");
	},

	getJSON: function( url, data, callback ) {
		return jQuery.get(url, data, callback, "json");
	},

	post: function( url, data, callback, type ) {
		if ( jQuery.isFunction( data ) ) {
			callback = data;
			data = {};
		}

		return jQuery.ajax({
			type: "POST",
			url: url,
			data: data,
			success: callback,
			dataType: type
		});
	},

	ajaxSetup: function( settings ) {
		jQuery.extend( jQuery.ajaxSettings, settings );
	},

	ajaxSettings: {
		url: location.href,
		global: true,
		type: "GET",
		contentType: "application/x-www-form-urlencoded",
		processData: true,
		async: true,
		/*
		timeout: 0,
		data: null,
		username: null,
		password: null,
		*/
		// Create the request object; Microsoft failed to properly
		// implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
		// This function can be overriden by calling jQuery.ajaxSetup
		xhr:function(){
			return window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
		},
		accepts: {
			xml: "application/xml, text/xml",
			html: "text/html",
			script: "text/javascript, application/javascript",
			json: "application/json, text/javascript",
			text: "text/plain",
			_default: "*/*"
		}
	},

	// Last-Modified header cache for next request
	lastModified: {},

	ajax: function( s ) {
		// Extend the settings, but re-extend 's' so that it can be
		// checked again later (in the test suite, specifically)
		s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));

		var jsonp, jsre = /=\?(&|$)/g, status, data,
			type = s.type.toUpperCase();

		// convert data if not already a string
		if ( s.data && s.processData && typeof s.data !== "string" )
			s.data = jQuery.param(s.data);

		// Handle JSONP Parameter Callbacks
		if ( s.dataType == "jsonp" ) {
			if ( type == "GET" ) {
				if ( !s.url.match(jsre) )
					s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
			} else if ( !s.data || !s.data.match(jsre) )
				s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
			s.dataType = "json";
		}

		// Build temporary JSONP function
		if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
			jsonp = "jsonp" + jsc++;

			// Replace the =? sequence both in the query string and the data
			if ( s.data )
				s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
			s.url = s.url.replace(jsre, "=" + jsonp + "$1");

			// We need to make sure
			// that a JSONP style response is executed properly
			s.dataType = "script";

			// Handle JSONP-style loading
			window[ jsonp ] = function(tmp){
				data = tmp;
				success();
				complete();
				// Garbage collect
				window[ jsonp ] = undefined;
				try{ delete window[ jsonp ]; } catch(e){}
				if ( head )
					head.removeChild( script );
			};
		}

		if ( s.dataType == "script" && s.cache == null )
			s.cache = false;

		if ( s.cache === false && type == "GET" ) {
			var ts = now();
			// try replacing _= if it is there
			var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
			// if nothing was replaced, add timestamp to the end
			s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
		}

		// If data is available, append data to url for get requests
		if ( s.data && type == "GET" ) {
			s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;

			// IE likes to send both get and post data, prevent this
			s.data = null;
		}

		// Watch for a new set of requests
		if ( s.global && ! jQuery.active++ )
			jQuery.event.trigger( "ajaxStart" );

		// Matches an absolute URL, and saves the domain
		var parts = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url );

		// If we're requesting a remote document
		// and trying to load JSON or Script with a GET
		if ( s.dataType == "script" && type == "GET" && parts
			&& ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){

			var head = document.getElementsByTagName("head")[0];
			var script = document.createElement("script");
			script.src = s.url;
			if (s.scriptCharset)
				script.charset = s.scriptCharset;

			// Handle Script loading
			if ( !jsonp ) {
				var done = false;

				// Attach handlers for all browsers
				script.onload = script.onreadystatechange = function(){
					if ( !done && (!this.readyState ||
							this.readyState == "loaded" || this.readyState == "complete") ) {
						done = true;
						success();
						complete();

						// Handle memory leak in IE
						script.onload = script.onreadystatechange = null;
						head.removeChild( script );
					}
				};
			}

			head.appendChild(script);

			// We handle everything using the script element injection
			return undefined;
		}

		var requestDone = false;

		// Create the request object
		var xhr = s.xhr();

		// Open the socket
		// Passing null username, generates a login popup on Opera (#2865)
		if( s.username )
			xhr.open(type, s.url, s.async, s.username, s.password);
		else
			xhr.open(type, s.url, s.async);

		// Need an extra try/catch for cross domain requests in Firefox 3
		try {
			// Set the correct header, if data is being sent
			if ( s.data )
				xhr.setRequestHeader("Content-Type", s.contentType);

			// Set the If-Modified-Since header, if ifModified mode.
			if ( s.ifModified )
				xhr.setRequestHeader("If-Modified-Since",
					jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );

			// Set header so the called script knows that it's an XMLHttpRequest
			xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");

			// Set the Accepts header for the server, depending on the dataType
			xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
				s.accepts[ s.dataType ] + ", */*" :
				s.accepts._default );
		} catch(e){}

		// Allow custom headers/mimetypes and early abort
		if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
			// Handle the global AJAX counter
			if ( s.global && ! --jQuery.active )
				jQuery.event.trigger( "ajaxStop" );
			// close opended socket
			xhr.abort();
			return false;
		}

		if ( s.global )
			jQuery.event.trigger("ajaxSend", [xhr, s]);

		// Wait for a response to come back
		var onreadystatechange = function(isTimeout){
			// The request was aborted, clear the interval and decrement jQuery.active
			if (xhr.readyState == 0) {
				if (ival) {
					// clear poll interval
					clearInterval(ival);
					ival = null;
					// Handle the global AJAX counter
					if ( s.global && ! --jQuery.active )
						jQuery.event.trigger( "ajaxStop" );
				}
			// The transfer is complete and the data is available, or the request timed out
			} else if ( !requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout") ) {
				requestDone = true;

				// clear poll interval
				if (ival) {
					clearInterval(ival);
					ival = null;
				}

				status = isTimeout == "timeout" ? "timeout" :
					!jQuery.httpSuccess( xhr ) ? "error" :
					s.ifModified && jQuery.httpNotModified( xhr, s.url ) ? "notmodified" :
					"success";

				if ( status == "success" ) {
					// Watch for, and catch, XML document parse errors
					try {
						// process the data (runs the xml through httpData regardless of callback)
						data = jQuery.httpData( xhr, s.dataType, s );
					} catch(e) {
						status = "parsererror";
					}
				}

				// Make sure that the request was successful or notmodified
				if ( status == "success" ) {
					// Cache Last-Modified header, if ifModified mode.
					var modRes;
					try {
						modRes = xhr.getResponseHeader("Last-Modified");
					} catch(e) {} // swallow exception thrown by FF if header is not available

					if ( s.ifModified && modRes )
						jQuery.lastModified[s.url] = modRes;

					// JSONP handles its own success callback
					if ( !jsonp )
						success();
				} else
					jQuery.handleError(s, xhr, status);

				// Fire the complete handlers
				complete();

				if ( isTimeout )
					xhr.abort();

				// Stop memory leaks
				if ( s.async )
					xhr = null;
			}
		};

		if ( s.async ) {
			// don't attach the handler to the request, just poll it instead
			var ival = setInterval(onreadystatechange, 13);

			// Timeout checker
			if ( s.timeout > 0 )
				setTimeout(function(){
					// Check to see if the request is still happening
					if ( xhr && !requestDone )
						onreadystatechange( "timeout" );
				}, s.timeout);
		}

		// Send the data
		try {
			xhr.send(s.data);
		} catch(e) {
			jQuery.handleError(s, xhr, null, e);
		}

		// firefox 1.5 doesn't fire statechange for sync requests
		if ( !s.async )
			onreadystatechange();

		function success(){
			// If a local callback was specified, fire it and pass it the data
			if ( s.success )
				s.success( data, status );

			// Fire the global callback
			if ( s.global )
				jQuery.event.trigger( "ajaxSuccess", [xhr, s] );
		}

		function complete(){
			// Process result
			if ( s.complete )
				s.complete(xhr, status);

			// The request was completed
			if ( s.global )
				jQuery.event.trigger( "ajaxComplete", [xhr, s] );

			// Handle the global AJAX counter
			if ( s.global && ! --jQuery.active )
				jQuery.event.trigger( "ajaxStop" );
		}

		// return XMLHttpRequest to allow aborting the request etc.
		return xhr;
	},

	handleError: function( s, xhr, status, e ) {
		// If a local callback was specified, fire it
		if ( s.error ) s.error( xhr, status, e );

		// Fire the global callback
		if ( s.global )
			jQuery.event.trigger( "ajaxError", [xhr, s, e] );
	},

	// Counter for holding the number of active queries
	active: 0,

	// Determines if an XMLHttpRequest was successful or not
	httpSuccess: function( xhr ) {
		try {
			// IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
			return !xhr.status && location.protocol == "file:" ||
				( xhr.status >= 200 && xhr.status < 300 ) || xhr.status == 304 || xhr.status == 1223;
		} catch(e){}
		return false;
	},

	// Determines if an XMLHttpRequest returns NotModified
	httpNotModified: function( xhr, url ) {
		try {
			var xhrRes = xhr.getResponseHeader("Last-Modified");

			// Firefox always returns 200. check Last-Modified date
			return xhr.status == 304 || xhrRes == jQuery.lastModified[url];
		} catch(e){}
		return false;
	},

	httpData: function( xhr, type, s ) {
		var ct = xhr.getResponseHeader("content-type"),
			xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0,
			data = xml ? xhr.responseXML : xhr.responseText;

		if ( xml && data.documentElement.tagName == "parsererror" )
			throw "parsererror";

		// Allow a pre-filtering function to sanitize the response
		// s != null is checked to keep backwards compatibility
		if( s && s.dataFilter )
			data = s.dataFilter( data, type );

		// The filter can actually parse the response
		if( typeof data === "string" ){

			// If the type is "script", eval it in global context
			if ( type == "script" )
				jQuery.globalEval( data );

			// Get the JavaScript object, if JSON is used.
			if ( type == "json" )
				data = window["eval"]("(" + data + ")");
		}

		return data;
	},

	// Serialize an array of form elements or a set of
	// key/values into a query string
	param: function( a ) {
		var s = [ ];

		function add( key, value ){
			s[ s.length ] = encodeURIComponent(key) + '=' + encodeURIComponent(value);
		};

		// If an array was passed in, assume that it is an array
		// of form elements
		if ( jQuery.isArray(a) || a.jquery )
			// Serialize the form elements
			jQuery.each( a, function(){
				add( this.name, this.value );
			});

		// Otherwise, assume that it's an object of key/value pairs
		else
			// Serialize the key/values
			for ( var j in a )
                if (!a.hasOwnProperty(j)) continue;
				// If the value is an array then the key names need to be repeated
				if ( jQuery.isArray(a[j]) )
					jQuery.each( a[j], function(){
						add( j, this );
					});
				else
					add( j, jQuery.isFunction(a[j]) ? a[j]() : a[j] );

		// Return the resulting serialization
		return s.join("&").replace(/%20/g, "+");
	}

});
var elemdisplay = {},
	timerId,
	fxAttrs = [
		// height animations
		[ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
		// width animations
		[ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
		// opacity animations
		[ "opacity" ]
	];

function genFx( type, num ){
	var obj = {};
	jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function(){
		obj[ this ] = type;
	});
	return obj;
}

jQuery.fn.extend({
	show: function(speed,callback){
		if ( speed ) {
			return this.animate( genFx("show", 3), speed, callback);
		} else {
			for ( var i = 0, l = this.length; i < l; i++ ){
				var old = jQuery.data(this[i], "olddisplay");

				this[i].style.display = old || "";

				if ( jQuery.css(this[i], "display") === "none" ) {
					var tagName = this[i].tagName, display;

					if ( elemdisplay[ tagName ] ) {
						display = elemdisplay[ tagName ];
					} else {
						var elem = jQuery("<" + tagName + " />").appendTo("body");

						display = elem.css("display");
						if ( display === "none" )
							display = "block";

						elem.remove();

						elemdisplay[ tagName ] = display;
					}

					jQuery.data(this[i], "olddisplay", display);
				}
			}

			// Set the display of the elements in a second loop
			// to avoid the constant reflow
			for ( var i = 0, l = this.length; i < l; i++ ){
				this[i].style.display = jQuery.data(this[i], "olddisplay") || "";
			}

			return this;
		}
	},

	hide: function(speed,callback){
		if ( speed ) {
			return this.animate( genFx("hide", 3), speed, callback);
		} else {
			for ( var i = 0, l = this.length; i < l; i++ ){
				var old = jQuery.data(this[i], "olddisplay");
				if ( !old && old !== "none" )
					jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
			}

			// Set the display of the elements in a second loop
			// to avoid the constant reflow
			for ( var i = 0, l = this.length; i < l; i++ ){
				this[i].style.display = "none";
			}

			return this;
		}
	},

	// Save the old toggle function
	_toggle: jQuery.fn.toggle,

	toggle: function( fn, fn2 ){
		var bool = typeof fn === "boolean";

		return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
			this._toggle.apply( this, arguments ) :
			fn == null || bool ?
				this.each(function(){
					var state = bool ? fn : jQuery(this).is(":hidden");
					jQuery(this)[ state ? "show" : "hide" ]();
				}) :
				this.animate(genFx("toggle", 3), fn, fn2);
	},

	fadeTo: function(speed,to,callback){
		return this.animate({opacity: to}, speed, callback);
	},

	animate: function( prop, speed, easing, callback ) {
		var optall = jQuery.speed(speed, easing, callback);

		return this[ optall.queue === false ? "each" : "queue" ](function(){

			var opt = jQuery.extend({}, optall), p,
				hidden = this.nodeType == 1 && jQuery(this).is(":hidden"),
				self = this;

			for ( p in prop ) {
                if (!prop.hasOwnProperty(p)) continue;
				if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden )
					return opt.complete.call(this);

				if ( ( p == "height" || p == "width" ) && this.style ) {
					// Store display property
					opt.display = jQuery.css(this, "display");

					// Make sure that nothing sneaks out
					opt.overflow = this.style.overflow;
				}
			}

			if ( opt.overflow != null )
				this.style.overflow = "hidden";

			opt.curAnim = jQuery.extend({}, prop);

			jQuery.each( prop, function(name, val){
				var e = new jQuery.fx( self, opt, name );

				if ( /toggle|show|hide/.test(val) )
					e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop );
				else {
					var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
						start = e.cur(true) || 0;

					if ( parts ) {
						var end = parseFloat(parts[2]),
							unit = parts[3] || "px";

						// We need to compute starting value
						if ( unit != "px" ) {
							self.style[ name ] = (end || 1) + unit;
							start = ((end || 1) / e.cur(true)) * start;
							self.style[ name ] = start + unit;
						}

						// If a +=/-= token was provided, we're doing a relative animation
						if ( parts[1] )
							end = ((parts[1] == "-=" ? -1 : 1) * end) + start;

						e.custom( start, end, unit );
					} else
						e.custom( start, val, "" );
				}
			});

			// For JS strict compliance
			return true;
		});
	},

	stop: function(clearQueue, gotoEnd){
		var timers = jQuery.timers;

		if (clearQueue)
			this.queue([]);

		this.each(function(){
			// go in reverse order so anything added to the queue during the loop is ignored
			for ( var i = timers.length - 1; i >= 0; i-- )
				if ( timers[i].elem == this ) {
					if (gotoEnd)
						// force the next step to be the last
						timers[i](true);
					timers.splice(i, 1);
				}
		});

		// start the next in the queue if the last step wasn't forced
		if (!gotoEnd)
			this.dequeue();

		return this;
	}

});

// Generate shortcuts for custom animations
jQuery.each({
	slideDown: genFx("show", 1),
	slideUp: genFx("hide", 1),
	slideToggle: genFx("toggle", 1),
	fadeIn: { opacity: "show" },
	fadeOut: { opacity: "hide" }
}, function( name, props ){
	jQuery.fn[ name ] = function( speed, callback ){
		return this.animate( props, speed, callback );
	};
});

jQuery.extend({

	speed: function(speed, easing, fn) {
		var opt = typeof speed === "object" ? speed : {
			complete: fn || !fn && easing ||
				jQuery.isFunction( speed ) && speed,
			duration: speed,
			easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
		};

		opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
			jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;

		// Queueing
		opt.old = opt.complete;
		opt.complete = function(){
			if ( opt.queue !== false )
				jQuery(this).dequeue();
			if ( jQuery.isFunction( opt.old ) )
				opt.old.call( this );
		};

		return opt;
	},

	easing: {
		linear: function( p, n, firstNum, diff ) {
			return firstNum + diff * p;
		},
		swing: function( p, n, firstNum, diff ) {
			return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
		}
	},

	timers: [],

	fx: function( elem, options, prop ){
		this.options = options;
		this.elem = elem;
		this.prop = prop;

		if ( !options.orig )
			options.orig = {};
	}

});

jQuery.fx.prototype = {

	// Simple function for setting a style value
	update: function(){
		if ( this.options.step )
			this.options.step.call( this.elem, this.now, this );

		(jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );

		// Set display property to block for height/width animations
		if ( ( this.prop == "height" || this.prop == "width" ) && this.elem.style )
			this.elem.style.display = "block";
	},

	// Get the current size
	cur: function(force){
		if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) )
			return this.elem[ this.prop ];

		var r = parseFloat(jQuery.css(this.elem, this.prop, force));
		return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
	},

	// Start an animation from one number to another
	custom: function(from, to, unit){
		this.startTime = now();
		this.start = from;
		this.end = to;
		this.unit = unit || this.unit || "px";
		this.now = this.start;
		this.pos = this.state = 0;

		var self = this;
		function t(gotoEnd){
			return self.step(gotoEnd);
		}

		t.elem = this.elem;

		if ( t() && jQuery.timers.push(t) && !timerId ) {
			timerId = setInterval(function(){
				var timers = jQuery.timers;

				for ( var i = 0; i < timers.length; i++ )
					if ( !timers[i]() )
						timers.splice(i--, 1);

				if ( !timers.length ) {
					clearInterval( timerId );
					timerId = undefined;
				}
			}, 13);
		}
	},

	// Simple 'show' function
	show: function(){
		// Remember where we started, so that we can go back to it later
		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
		this.options.show = true;

		// Begin the animation
		// Make sure that we start at a small width/height to avoid any
		// flash of content
		this.custom(this.prop == "width" || this.prop == "height" ? 1 : 0, this.cur());

		// Start by showing the element
		jQuery(this.elem).show();
	},

	// Simple 'hide' function
	hide: function(){
		// Remember where we started, so that we can go back to it later
		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop );
		this.options.hide = true;

		// Begin the animation
		this.custom(this.cur(), 0);
	},

	// Each step of an animation
	step: function(gotoEnd){
		var t = now();

		if ( gotoEnd || t >= this.options.duration + this.startTime ) {
			this.now = this.end;
			this.pos = this.state = 1;
			this.update();

			this.options.curAnim[ this.prop ] = true;

			var done = true;
			for ( var i in this.options.curAnim )
                if (!this.options.curAnim.hasOwnProperty(i)) continue;
				if ( this.options.curAnim[i] !== true )
					done = false;

			if ( done ) {
				if ( this.options.display != null ) {
					// Reset the overflow
					this.elem.style.overflow = this.options.overflow;

					// Reset the display
					this.elem.style.display = this.options.display;
					if ( jQuery.css(this.elem, "display") == "none" )
						this.elem.style.display = "block";
				}

				// Hide the element if the "hide" operation was done
				if ( this.options.hide )
					jQuery(this.elem).hide();

				// Reset the properties, if the item has been hidden or shown
				if ( this.options.hide || this.options.show )
					for ( var p in this.options.curAnim ) {
                        if (!this.options.curAnim.hasOwnProperty(p)) continue;
						jQuery.attr(this.elem.style, p, this.options.orig[p]);
					}

				// Execute the complete function
				this.options.complete.call( this.elem );
			}

			return false;
		} else {
			var n = t - this.startTime;
			this.state = n / this.options.duration;

			// Perform the easing function, defaults to swing
			this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration);
			this.now = this.start + ((this.end - this.start) * this.pos);

			// Perform the next step of the animation
			this.update();
		}

		return true;
	}

};

jQuery.extend( jQuery.fx, {
	speeds:{
		slow: 600,
 		fast: 200,
 		// Default speed
 		_default: 400
	},
	step: {

		opacity: function(fx){
			jQuery.attr(fx.elem.style, "opacity", fx.now);
		},

		_default: function(fx){
			if ( fx.elem.style && fx.elem.style[ fx.prop ] != null )
				fx.elem.style[ fx.prop ] = fx.now + fx.unit;
			else
				fx.elem[ fx.prop ] = fx.now;
		}
	}
});
if ( document.documentElement["getBoundingClientRect"] )
	jQuery.fn.offset = function() {
		if ( !this[0] ) return { top: 0, left: 0 };
		if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
		var box  = this[0].getBoundingClientRect(), doc = this[0].ownerDocument, body = doc.body, docElem = doc.documentElement,
			clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
			top  = box.top  + (self.pageYOffset || jQuery.boxModel && docElem.scrollTop  || body.scrollTop ) - clientTop,
			left = box.left + (self.pageXOffset || jQuery.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
		return { top: top, left: left };
	};
else
	jQuery.fn.offset = function() {
		if ( !this[0] ) return { top: 0, left: 0 };
		if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] );
		jQuery.offset.initialized || jQuery.offset.initialize();

		var elem = this[0], offsetParent = elem.offsetParent, prevOffsetParent = elem,
			doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
			body = doc.body, defaultView = doc.defaultView,
			prevComputedStyle = defaultView.getComputedStyle(elem, null),
			top = elem.offsetTop, left = elem.offsetLeft;

		while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
			computedStyle = defaultView.getComputedStyle(elem, null);
			top -= elem.scrollTop, left -= elem.scrollLeft;
			if ( elem === offsetParent ) {
				top += elem.offsetTop, left += elem.offsetLeft;
				if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.tagName)) )
					top  += parseInt( computedStyle.borderTopWidth,  10) || 0,
					left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
				prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
			}
			if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" )
				top  += parseInt( computedStyle.borderTopWidth,  10) || 0,
				left += parseInt( computedStyle.borderLeftWidth, 10) || 0;
			prevComputedStyle = computedStyle;
		}

		if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" )
			top  += body.offsetTop,
			left += body.offsetLeft;

		if ( prevComputedStyle.position === "fixed" )
			top  += Math.max(docElem.scrollTop, body.scrollTop),
			left += Math.max(docElem.scrollLeft, body.scrollLeft);

		return { top: top, left: left };
	};

jQuery.offset = {
	initialize: function() {
		if ( this.initialized ) return;
		var body = document.body, container = document.createElement('div'), innerDiv, checkDiv, table, td, rules, prop, bodyMarginTop = body.style.marginTop,
			html = '<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';

		rules = { position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' };
		for ( prop in rules ) {
            if (!rules.hasOwnProperty(prop)) continue;
            container.style[prop] = rules[prop];
        }

		container.innerHTML = html;
		body.insertBefore(container, body.firstChild);
		innerDiv = container.firstChild, checkDiv = innerDiv.firstChild, td = innerDiv.nextSibling.firstChild.firstChild;

		this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
		this.doesAddBorderForTableAndCells = (td.offsetTop === 5);

		innerDiv.style.overflow = 'hidden', innerDiv.style.position = 'relative';
		this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);

		body.style.marginTop = '1px';
		this.doesNotIncludeMarginInBodyOffset = (body.offsetTop === 0);
		body.style.marginTop = bodyMarginTop;

		body.removeChild(container);
		this.initialized = true;
	},

	bodyOffset: function(body) {
		jQuery.offset.initialized || jQuery.offset.initialize();
		var top = body.offsetTop, left = body.offsetLeft;
		if ( jQuery.offset.doesNotIncludeMarginInBodyOffset )
			top  += parseInt( jQuery.curCSS(body, 'marginTop',  true), 10 ) || 0,
			left += parseInt( jQuery.curCSS(body, 'marginLeft', true), 10 ) || 0;
		return { top: top, left: left };
	}
};


jQuery.fn.extend({
	position: function() {
		var left = 0, top = 0, results;

		if ( this[0] ) {
			// Get *real* offsetParent
			var offsetParent = this.offsetParent(),

			// Get correct offsets
			offset       = this.offset(),
			parentOffset = /^body|html$/i.test(offsetParent[0].tagName) ? { top: 0, left: 0 } : offsetParent.offset();

			// Subtract element margins
			// note: when an element has margin: auto the offsetLeft and marginLeft
			// are the same in Safari causing offset.left to incorrectly be 0
			offset.top  -= num( this, 'marginTop'  );
			offset.left -= num( this, 'marginLeft' );

			// Add offsetParent borders
			parentOffset.top  += num( offsetParent, 'borderTopWidth'  );
			parentOffset.left += num( offsetParent, 'borderLeftWidth' );

			// Subtract the two offsets
			results = {
				top:  offset.top  - parentOffset.top,
				left: offset.left - parentOffset.left
			};
		}

		return results;
	},

	offsetParent: function() {
		var offsetParent = this[0].offsetParent || document.body;
		while ( offsetParent && (!/^body|html$/i.test(offsetParent.tagName) && jQuery.css(offsetParent, 'position') == 'static') )
			offsetParent = offsetParent.offsetParent;
		return jQuery(offsetParent);
	}
});


// Create scrollLeft and scrollTop methods
jQuery.each( ['Left', 'Top'], function(i, name) {
	var method = 'scroll' + name;

	jQuery.fn[ method ] = function(val) {
		if (!this[0]) return null;

		return val !== undefined ?

			// Set the scroll offset
			this.each(function() {
				this == window || this == document ?
					window.scrollTo(
						!i ? val : jQuery(window).scrollLeft(),
						 i ? val : jQuery(window).scrollTop()
					) :
					this[ method ] = val;
			}) :

			// Return the scroll offset
			this[0] == window || this[0] == document ?
				self[ i ? 'pageYOffset' : 'pageXOffset' ] ||
					jQuery.boxModel && document.documentElement[ method ] ||
					document.body[ method ] :
				this[0][ method ];
	};
});
// Create innerHeight, innerWidth, outerHeight and outerWidth methods
jQuery.each([ "Height", "Width" ], function(i, name){

	var tl = i ? "Left"  : "Top",  // top or left
		br = i ? "Right" : "Bottom", // bottom or right
		lower = name.toLowerCase();

	// innerHeight and innerWidth
	jQuery.fn["inner" + name] = function(){
		return this[0] ?
			jQuery.css( this[0], lower, false, "padding" ) :
			null;
	};

	// outerHeight and outerWidth
	jQuery.fn["outer" + name] = function(margin) {
		return this[0] ?
			jQuery.css( this[0], lower, false, margin ? "margin" : "border" ) :
			null;
	};

	var type = name.toLowerCase();

	jQuery.fn[ type ] = function( size ) {
		// Get window width or height
		return this[0] == window ?
			// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
			document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] ||
			document.body[ "client" + name ] :

			// Get document width or height
			this[0] == document ?
				// Either scroll[Width/Height] or offset[Width/Height], whichever is greater
				Math.max(
					document.documentElement["client" + name],
					document.body["scroll" + name], document.documentElement["scroll" + name],
					document.body["offset" + name], document.documentElement["offset" + name]
				) :

				// Get or set width or height on the element
				size === undefined ?
					// Get width or height on the element
					(this.length ? jQuery.css( this[0], type ) : null) :

					// Set the width or height on the element (default to pixels if value is unitless)
					this.css( type, typeof size === "string" ? size : size + "px" );
	};

});
})();

/*
////////////////////////////////////////////////////////////////////////////////
//http://www.network-science.de/ascii/
   - Using "Slant" font
   ____  __      _           __     ____             __        __                       
  / __ \/ /_    (_)__  _____/ /_   / __ \_________  / /_____  / /___  ______  ___  _____
 / / / / __ \  / / _ \/ ___/ __/  / /_/ / ___/ __ \/ __/ __ \/ __/ / / / __ \/ _ \/ ___/
/ /_/ / /_/ / / /  __/ /__/ /_   / ____/ /  / /_/ / /_/ /_/ / /_/ /_/ / /_/ /  __(__  ) 
\____/_.___/_/ /\___/\___/\__/  /_/   /_/   \____/\__/\____/\__/\__, / .___/\___/____/  
          /___/                                                /____/_/                 

////////////////////////////////////////////////////////////////////////////////
*/

(function() {
    
    Object.prototype.returns = function() {
        return this;
    };
    
    Object.prototype.extend = function (destination) {
        //returns a new object containing all properties of {this}
        var dest = {};
        this.each(function(v, k) {
            dest[k] = v;
        });
        return dest;
    };
    
    /*
     //Ok this is the second time I've implemented this function and it breaks stuff.
     //let's consider this a final warning - I'm not sure what broke as I forgot the scenario late last night
     //I'll have to debug that some other day
     Object.prototype.toString = function() {
         return q.dump(this);   
     };
    */
    
    //Function overloading - iterate through me().args() and match types and order of parameters to determine which function part to call
    //}
    
    Object.prototype.toVar = function() {
        return this;
    };
    
    Object.prototype.cases = function(arry, matcher) {
        /*
        //cases executes a function ref value in a hash for every respective matching key
            return { orange : function() { return "#aaaa33" },
                     blue   : function() { return "#0000CC" },
                     green  : function() { return "#00CC00" }.cases(args); */
       
        if (!arry.isArray || !arry.isArray()) {
            arry = [arry];    
        }
        
        matcher = matcher || Object.prototype.matches
        
        return this.each(function(v, k) {
            if (arry.contains(k)) return v.returns();  
        });
    };
    
    Object.prototype.hasKey = function(k) {
        return (this[k] != null) ? true : false;
    };
    
    
    Object.prototype.inspect = 
    Object.prototype.dump = function() {
        return(q.dump(this));    
    };
    
    Object.prototype.merge = function(obj, right) {
        //Merges an object (obj) with self, giving preference (right) to the supplied
        //object when a conflict occurs
        var self = this;
    
        obj.each(function(item, k) {
            if (self[k]) {
                if (self[k].__type() == "object" && item.__type() == "object") {
                    self[k].merge(item, right);
                }
                else if (right) {
                    //conflict - right preference means
                    //take what's in b, otherwise left
                    //preference takes precendent
                    self[k] = item;
                }
            }
            else {
                self[k] = item;
            }
        });
       
        return(this);
    };
    
    Object.prototype.diff = function(obj, addRight) {
        //Diffs an object (obj) with self, giving preference (right) to the supplied
        //object when a conflict occurs
        var self = this;
            
        obj.each(function(item, k) {
            if (self[k] != null) {
                if (self[k].__type() == "object" && item.__type() == "object") {
                    try {
                        self[k].diff(item, addRight);
                    } catch (e) {
                        delete self[k];
                    }
                }
                else if (self[k] == item) {
                    delete self[k];
                }
            }
            else {
                if (addRight) {
                    self[k] = item; 
                }
                else {
                    delete self[k];
                }
            }
        });
        
        return(this); 
    };
    
    Object.prototype.remember = function remember(key) {
        return q.mem.remember(key, this);
    };
    
    Object.prototype.recall = function(key) {
        return q.mem.recall(key);
    };
    
    Object.prototype.__type = function() {
        return("object");
    };
    
    Object.prototype.ea =
    Object.prototype.each = function(f, params) {
        return q.u.each(this, f, params);
    };
    
    Object.prototype.properties = function(summarize) {
        //returns all base & inherited properties in <i>obj</i>
        var props = [];
        var obj = this;
        
        //Summarize will speed up the time to compile the properties
        //of this object - great for huge arrays or hashes.
        //will not affect the actual object, but it will return properties
        //of the base type of this, and not any extended properties applied
        //to the specific subclass of this. 
        if (summarize && obj.__type() == "array") {
            var len = obj.length;
            obj = function() {};
            obj.prototype = this;
            if (len) {
                props.push("[0 - " + len + "]");
            } else {
                props.push("[empty array]");
            }
            obj.length = 0;
        }
        
        for (var i in obj) { props.push(i); }
        return(props);
    };
    
    Object.prototype.is =
    Object.prototype.equals = function(o) {
        return (this.dump() == o.dump());
    };
    
    Object.prototype.toArray = function() {
        return q.toArray(this);
    };
    
    Object.prototype.morph = function(type) {
        //fundamentally morph an object into another type by
        //passing in a variable of the desired result type
        if (type.isArray()) {
            return this.each(function(v) {
                return v; 
            });
        }
        else if (type.isBoolean()) {
            return this.keys().legnth ? true : false
        }
        else {
            this.prototype = type.prototype;
            return this;
        }
    };
    
    Object.prototype.keys = 
    Object.prototype.toKeyArray = function() {
        //Returns an array of this object's properties
        return this.each(function(v, k) { return k.toString(); });
    };
    
    Object.prototype.isArray = function() { return(false); };
    Object.prototype.isFunction = function() { return false; };
    Object.prototype.isNumeric = function() { return(false); };
    Object.prototype.isBoolean = function() { return(false); };
    Object.prototype.isRegex = 
    Object.prototype.isRegexp = function() { return(false); };
    Object.prototype.isObject = function() { return(true); };
    Object.prototype.isDate = function() { return(false); };
    Object.prototype.isString = function() { return(false); };
    
    String.prototype.isArray = function() { return(false); };
    String.prototype.isFunction = function() { return false; };
    String.prototype.isNumeric = function() { return(false); };
    String.prototype.isBoolean = function() { return(false); };
    String.prototype.isObject = function() { return(false); };
    String.prototype.isDate = function() { return(false); };
    String.prototype.isString = function() { return(true); };
    
    Object.prototype.toValueArray = function() {
        //Returns an Array of values from key/value pairs on dictionary objects
        var retArray = [];
    
        for (var x in this) {
          if (this.hasOwnProperty(x)) {
            retArray.push(this[x]);
          }
        }
    
        return(retArray);
    };
    
    Object.prototype.toQueryString =
    Object.prototype.toParamString = function() {
        //Converts a hash into a url parameter string
        return (this.each(function(v, k) {
            return encodeURIComponent(k) + "=" + encodeURIComponent(v); 
        }).join("&"));
    };
    
    Object.prototype.clone =
    Object.prototype.copyOf = function() {
        //creates a value copy of an object
        return(this.dump().toVar());    
    };
    
    //converts the arguments object into a param
    Object.prototype.from = function from(n) {
        return this.toArray().from(n);
    };
    
    Object.prototype.assert = function(o, msg) {
     
        if (o === true && this === true) {
            return(true);
        }
        if (o === false && this === false) {
            return(true);
        }
        if (o === this) {
            return(true);
        }
        if (o === null) {
            return(false);
        }
        
        throw new qError(600, "Assertion failed: " + msg);
    };
    
    Object.prototype.alert = function() {
        alert(this.stringify());
    };
    
    Object.prototype.stringify = function() {
        return q.u.stringify(this);
    };
    
    Object.prototype.use =
    Object.prototype.reuse = function(f) {
        return f.apply(this, [this].combine(q.toArray(arguments)));
    };
    
    Object.prototype.kvp = function() {
        //returns an object as an array of key value pairs
        return this.ea(function(v, k) {
            return [k, v];  
        });
    };
    
    Object.prototype.hashsort = function(k, rev) {
        //sorts an object
        return q.sort.apply(this.kvp(), function(a, b) {
            if (rev) {
                return (b[k] - a[k]);
            }
            else {
                return (a[k] - b[k]);
            }
        });
    };
    
    Object.prototype.isa =
    Object.prototype.isA = function(o) {
        return this.__type() == o.__type();    
    };
    
    Object.prototype.toXml = function(root_key) {
        
        var indent = 0;
        
        var str_repeat = function str_repeat(string,repeat)
        {
            var response = '';
            for(var i = 0; i < repeat; ++i)
            {
                response += string;
            }
            return response;
        };
        
        var serialize_value = function serialize_value(key_name,value,indent)
        {
            var response = '';
            if(typeof(value) === 'string' || typeof(value) === 'number' || typeof(value) === 'boolean')
            {
                response = '<![CDATA[' + (new String(value)).toString() + ']]>';
            }
            else if(typeof(value) === 'object')
            {
                response += String.fromCharCode(10);
                if('length' in value && 'splice' in value)
                {
                    for(var i = 0; i < value.length; ++i)
                    {
                        response += wrap_value(ActiveSupport.Inflector.singularize(key_name) || key_name,value[i],indent + 1);
                    }
                }
                else
                {
                    var object = value.toObject && typeof(value.toObject) === 'function' ? value.toObject() : value;
                    for(key_name in object)
                    {
                        response += wrap_value(key_name,object[key_name],indent + 1);
                    }
                }
                response += str_repeat(' ',4 * indent);
            }
            return response;
        };
        
        var sanitize_key_name = function sanitize_key_name(key_name)
        {
            return key_name.replace(/[\s\_]+/g,'-').toLowerCase();
        };
        
        var wrap_value = function wrap_value(key_name,value,indent)
        {
            key_name = sanitize_key_name(key_name);
            return str_repeat(' ',4 * indent) + '<' + key_name + '>' + serialize_value(key_name,value,indent) + '</' + key_name + '>' + String.fromCharCode(10);
        };
        
        root_key = sanitize_key_name(root_key);
        return '<' + root_key + '>' + serialize_value(root_key,this,0) + '</' + root_key + '>';
    };
    
    Object.prototype.save = function(f) {
        q.f.save(f, q.dump(this));
        return f;
    };
   
    Object.open = function(f) {
        return q.f.open(f).toVar(true) || {};  
    }; 
})();
/*
Object.prototype.assertContains = function(t) {
    if (!this.contains(t)) throw new qError(605, "assertContains failed: " + def(msg, "[MISSING MESSAGE]")); 
}; */
$H = Hash;

function Hash(o) {
    //A proper object wrapper class to implement prototype like extensions for base object types in javascript
    //without risking data overrides in the object namespace and to play nicely with other js frameworks that improperly
    //iterate over objects without a hasOwnProperty check

    if (o && typeof o.isHash == "function") {
        return(o); 
    }
    
    var self = this;
        self.data = o || {};
        
    self.each = function(f, params) {
        return q.u.each(self.data, f, params);
    };
    
    self.add = function(k, v) {
        self.data[k] = v;
        return self;
    };
    
    self._ = function(k, v) {
        //setter and getter for data values
        if (!v) {
            return(self.data[k]);
        }
        return self.add(k, v);
    };
    self.get = self._;
    self.set = self._;
    
    self.toObject = function() {
        return self.data;     
    };
    self.toObj = self.toObject;
    
    self.keys = function() {
        //Returns an Array of Keys from key/value pairs in Hash objects
        var ret = [];
        self.each(function(v, k) {
            ret.push(k);
        });
        return ret;
    };
    self.toKeyArray = self.keys;
    
    self.values = function() {
        //returns an array of values from key/value pairs in Hash objects
        var ret = [];
        return self.data.each(function(v, k) {
            return v;
        });
    };
    self.toArray = self.values;
    self.toValueArray = self.toArray;
    
    self.hasKey = function(k) {
        //safely determines if a key exists in the hash data
        return (k in self.data);
    };
    
    self.isNumeric = function() {
        //extensible data typing
        return(false);
    };

    self.dump = function() {
        return(q.u.dump(self.data));    
    };

    self.merge = function(obj, right) {
        //Merges a hash (obj) with self, giving preference (right) to the supplied
        //object when a conflict occurs
        if (obj.__type() == "hash") {
            obj = obj.data; //use reference to hash.data
        }
        
        obj.each(function(item, k) {
            if (self.data[k]) {
                    if (self.data[k].__type() == "object" && item.__type() == "object") {
                        self.data[k].merge(item, right);
                    }
                    else {
                        if (right) {
                            //conflict - right preference means
                            //take what's in b, otherwise left
                            //preference takes precendent
                            self.data[k] = item;
                        }
                    }
                
            }
            else {
                self.data[k] = item;
            }
        });
       
        return(this);
    };

    self.diff = function(obj, addRight) {
        //Merges an object (obj) with self, giving preference (right) to the supplied
        //object when a conflict occurs
            
        self.data.each(function(item, k) {
            if (self.data[k] != null) {
                if (self.data[k].__type() == "object" && item.__type() == "object") {
                    try {
                        self.data[k].diff(item, addRight);
                    } catch (e) {
                        delete self.data[k];
                    }
                }
                else if (self.data[k] == item) {
                    delete self.data[k];
                }
            }
            else {
                if (addRight) {
                    self.data[k] = item; 
                }
                else {
                    delete self.data[k];
                }
            }
        });
        
        return(this); 
    };

    self.__type = function() {
        return("hash");
    };

    self.properties = function() {
        //returns all base & inherited properties in <i>obj</i>
        var props = [];
        for (var i in this.data) { props.push(i); }
        return(props);
    };

    self.equals = function(t) {
        //compares two hashes for equality
        if (t && t.__type && t.__type() == "hash") {
            return (self.dump() == t.dump());
        }
    };

    self.isArray = function() {
        //Array.prototype.isArray overrides with true
        return(false);
    };

    self.toParamString = function() {
        //Converts a hash into a url parameter string
        return self.each(function(v, k) {
            params.push(encodeURIComponent(k) + "=" + encodeURIComponent(v)); 
        }).join("&");
    };
    self.uriEncode = self.toParamString;
    
    self.copyOf = function() {
        //creates a value copy of an object
        return(new Hash(self.dump().toVar()));    
    };
    
    return self;    
};

(function() {
    /*
    ////////////////////////////////////////////////////////////////////////////////
        ___                             ____             __        __                       
       /   |  ______________ ___  __   / __ \_________  / /_____  / /___  ______  ___  _____
      / /| | / ___/ ___/ __ `/ / / /  / /_/ / ___/ __ \/ __/ __ \/ __/ / / / __ \/ _ \/ ___/
     / ___ |/ /  / /  / /_/ / /_/ /  / ____/ /  / /_/ / /_/ /_/ / /_/ /_/ / /_/ /  __(__  ) 
    /_/  |_/_/  /_/   \__,_/\__, /  /_/   /_/   \____/\__/\____/\__/\__, / .___/\___/____/  
                           /____/                                  /____/_/                 
    
    ////////////////////////////////////////////////////////////////////////////////
    */
    
    Array.prototype.erase =
    Array.prototype.clear = function() {
        //erase this array
        this.length = 0;
    };
    
    Array.prototype.tuple = function() {
        //returns a hash of key value pairs where even indexes are the key and odd indexes are the value
        var r = {};
        var key = null;
        
        this.flatten().each(function(v, k, i) {
            if (i.isEven()) {
                key = v;
            }
            else if(key != null) {
                if (!r.hasKey(key)) {
                    r[key] = v;
                }
                else {
                    r[key] = [r[key], v].flatten().distinct();
                }
            }
        });
        
        return r;
    };
    
    Array.prototype.reduce =
    Array.prototype.compact =
    Array.prototype.clean = function() {
        //removes empty/whitespace only values in this array
        return this.each(function(v) {
            if (v && v.length && v.trim().length) return v; 
        });
    };
    
    Array.prototype.bubbleSort = function() {
        //sort this array using the bubble sort method
        var tmp;
        for(var i=0;i<this.length;i++) {
            for(var j=0;j<this.length;j++) {
                if(this[i]<this[j]) {
                    tmp = this[i];
                    this[i] = this[j];
                    this[j] = tmp;
                }
            }
        }
    };
    
    Array.prototype.sortByKey = function(k) {
        //sorts an array of hashes by a specific key's values
        //TODO: consider creating a matrix object and name this function colSort
        return this.sort(function(a, b) {
            return a[k] - b[k];  
        });
    };
    
    Array.prototype.filterHashKeys = function(keys) {
        keys = me().args();
        
        return this.each(function(o) {
            ret = {};
            keys.each(function(k) {
                ret[k] = o[k];
            })
            return ret;
        });
    };
    
    if (![].indexOf) {
        //Deafult function for arrays in modern browsers
        Array.prototype.indexOf = function(elem) {
            //Returns the numerical index of a given element
            this.each(function(v, k, i) {
                if (v === elem) {
                    return i;
                }
            });
        };
    }
    
    Array.prototype.toJSON = function() {
        //convert this array into a jsonified string
        return JSON.stringify(this);   
    };
    
    Array.prototype.inGroupsOf = function(size) {
        //returns an array of arrays - each with (size) values
        if (size == 1) return this;
        if (!size) return [];
        if (size > this.length) size = this.length;
        
        var block = (this.length / size).ceil();
        var self = this;
        return block.each(function(i) {
            return self.mid(i * block, block);
        });
    };
    
    Array.prototype.map = function(f) {
        //slightly different from each in that map produces an array with a length equal to this array
        return this.each(function(v, k, i, r) {
            r[k] = f.tryval(v, k, i, r) || "";
        });
    };
    
    Array.prototype.trim = function() {
        //trims each item in an array
        return this.each(function(v) {
            return v.toString().trim();  
        });
    };
    
    Array.prototype.flatten = function() {
        //recursively flattens this array of arrays into a single array
        var r = [];
        this.each(function(v, k, i) {
            var type = Object.prototype.toString.call(v).split(' ').pop().split(']').shift().toLowerCase();
            if (type) {
                r = r.concat(/^(array|collection|arguments|object)$/.test(type) ? Array.prototype.flatten.call(v) : v);
            }
        });
        return r;
    };
    
    Array.prototype.size = 
    Array.prototype.count = function() {
        //aliases for this array's length
        return this.length;   
    };
    
    Array.prototype.column = function(c) {
        //Returns a key from each hash in a hash array as a new array
        return this.each(function(o) {
            if (typeof(o[c]) == "function") {
                return o[c].tryval();
            }
            else {
                return o[c];
            }
        });
    };
    
    Array.prototype.every = function(n) {
        //Returns evern nth record from an array    
        return this.each(function(e, i, c) {
            if ((c + 1) % n == 0) return e;
        });
    };
    
    Array.prototype.remove = function(idx) {
        //Returns a new array excluding the indexed (idx) item
        return this.each(function(e, i) {
            if (idx != i) ret.push(e);    
        });
    };
    
    Array.prototype.__type = function() {
        //returns the type of this object (array)
        return("array");
    };
    
    Array.prototype.pushOnce = function(obj) {
        //Pushes an item onto an array if one does not already exist
        if (obj.__type() == "string" && this.contains(obj) == false) {
            this[this.length] = obj;
        }
    };
    
    Array.prototype.all = 
    Array.prototype.ea =    
    Array.prototype.each = 
    Array.prototype.iterate = function(f, params) {
        //used to iterate over overloaded array objects by ignoring
        //all overloads and only iterating over the true array indices
        var k=0;
        var clean = [];
        for (var i=0; i < this.length; i++) {
            clean.push(this[i]);
        }
        return q.u.each(clean, f, params);
    };
        
    Array.prototype.eachThreaded = function(f, status_handler, params) {
        //failed attempt at a threaded each in nonbrowser engines
        return q.u.eachThreaded(this, f, status_handler, params);    
    };
    
    Array.prototype.first = function(idx) {
        //returns the first index of an array or up to the nth (idx) index
        if (idx) {
            return this.slice(0, idx);
        } else {
            return(this[0]);
        } 
    };
    
    Array.prototype.quote = function(dbl) {
        dbl = q.def(dbl, "'");
        return this.ea(function(i) {
            return i.toString().wrap(dbl);  
        });
    };
    
    Array.prototype.dblQuote = function() {
        return this.quote('"');   
    };
     
    Array.prototype.last = function(idx) {
        //returns the last index of an array or the last n (idx) indexes
        if (idx != null) {
            return this.slice(this.length - idx);
        } else {
            return this[this.length - 1]; 
        }
    };
    
    Array.prototype.maxValue = 
    Array.prototype.max = function() {
        //Returns the largest value from an array of numbers
        var lastMax;
      
        this.each(function(v) {
            var tnum = v.toString().toFloat();
            lastMax = Math.max(tnum, lastMax);
        });
      
        return(lastMax);
    };
    
    Array.prototype.min = function() {
        //returns the smallest value from an array of numbers
        var lastMin;
        
        this.each(function(v) {
            var tnum = v.toString().toFloat();
            lastMin = Math.min(tnum, lastMin);
        });
      
        return(lastMin);
    };
    
    Array.prototype.toLower = 
    Array.prototype.toLowerCase = function() {
        return this.each(function(v) {
            return v.toString().toLowerCase();  
        });
    };
    
    Array.prototype.contains = function(v) {
        //returns true if this array contains the value v
        if (typeof(v) == "number") {
          v = v.toString();
        }
        
        var cached = null;
        
        return this.each(function(c) {
          if (typeof(v) == "string") {
            if (c.toString().toLowerCase() == v.toString().toLowerCase()) {
              q.u.exit(true);
            }
          }
          else if (v.__type() == "array" && v.contains(c)) {
            q.u.exit(true);
          }
          else if (typeof(v) == "object") {
            if ((cached && cached == c.dump()) ||
                ((cached = v.dump()) && cached == c.dump())) {
                q.u.exit(true);
            }
          }
          else if (c == v) {
              q.u.exit(true);
          }
        }) == true;
    };
    
    Array.prototype.toArray = function() {
        //normalized access to objects
        return(this);
    };
    
    Array.prototype.distinct = 
    Array.prototype.unique = function() {
        //returns only unique items in an array
        var r = [];
        this.each(function(v, k, i) {
            if (!r.contains(v)) r.push(v);    
        });
        return r;
    };
    
    Array.prototype.minus =
    Array.prototype.diff = function(a) {
        //returns all items in (this) array that are not in (a)
        return this.each(function(v) {
            if (!a.contains(v)) return(v); 
        });
    };
    
    Array.prototype.combine = function(arr) {
        //combines this array with provided arr
        return this.concat(arr);
    };
    
    Array.prototype.intersection = function(arr) {
        //returns the common values between two arrays
        var res = [];
    
        for (var i=0; i<this.length; i++) {
            var t1 = this[i].toString().toLowerCase();
          
            if (arr.__type() == "array") {
                for (var j=0; j<arr.length; j++) {
                    var t2 = arr[j].toString().toLowerCase();
          
                    if (t1 == t2) {
                        res.push(this[i].toString());
                        break;
                    }
                }
            }
            else {
                if (arr.toString().toLower() == t1) {
                    res.push(this[i].toString()); 
                }
            }
        }
    
        return(res);
    };
    
    Array.prototype.consecutiveIntersection = function(arr) {
        //returns continuous matching segments between two arrays
        var res = [];
    
        for (var i=0; i<this.length; i++) {
          var t1 = this[i].toString().toLowerCase().trim();
          var len = res.length;
    
          for (var j=0; j<arr.length; j++) {
            var t2 = arr[j].toString().toLowerCase().trim();
    
            if (t1 == t2) {
              res.push(this[i].toString());
              break;
            }
          }
    
          if (len == res.length) {
            //a break in the match sequence
            //exit now
            break;
          }
        }
    
        return(res);
    };
    
    Array.prototype.uniqueIntersection = function(x) {
    
        var res = [];
    
        for (var i=0; i<this.length; i++) {
          for (var j=0; j<x.length; j++) {
            var t1 = this[i].toString().toLowerCase();
            var t2 = x[j].toString().toLowerCase();
    
            if (t1 == t2) {
              res.push(this[i].toString());
              this.splice(i, 1);
              i = i - 1;
              break;
            }
          }
        }
    
        return(res);
    };
    
    Array.prototype.random = function(cnt) {
        //returns N random values from an array
        if (cnt >= this.length) return this;
        
        return (cnt || 1).each(function(i) {
            return(this[rand(0, this.length)]);
        }).trueType();
    };
    
    Array.prototype.surrounding = 
    Array.prototype.near = function(i, perc, abs) {
        //returns a new array with +-2.5% of the surrounding items
        var margin = null; 
        
        perc = perc || 5;
        
        if (abs != null) {
            margin = (abs / 2).floor();
        }
        else if (perc != null) {
            margin = (this.length * (perc / 2).percent()).floor();
        }
        
        return this.slice((i - margin).max(0), (i + margin).min(this.length)); 
    };
    
    Array.prototype.trueType = function() {
        //returns the "true type" of an array
        //if there's only one index then let's return the index
        if (this.length == 1) {
            return this[0];
        } else {
            return this;
        }
    };
    
    Array.prototype.toCSV = function() {
        //Converts an array of Hashes to a CSV file using the hash keys in array index 0 as the CSV header
        var head = this[0].toKeyArray().join(",");
        
        var content = this.each(function(r) {
            return r.toValueArray().join(",");     
        }).join("\n");
        
        return [head, content].join("\n");
    };
    
    Array.prototype.toTable = function() {
        //Converts an array of hashes or values into html table markup
        var table = "<table><tr><th>" + this[0].toKeyArray().join("</th><th>") + "</th></tr>";
        
        return table + this.each(function(r) {
            return "<tr><td>" + r.toValueArray().join("</td><td>") + "</td></tr>";
        }).join("\r\n") + "</table>";
    };
    
    Array.prototype.prepend = function(itm) {
        this.unshift(itm);
        return this;
    };
    
    Array.prototype.strip = function(a) {
        //Strips matching records found in (a) from this array
        var res = [];
        
        this.each(function(r) {
            
            var b = false;
            
            a.each(function(v) {
                if (r == v) {
                    b = true;
                }
            });
            
            if (!b) {
                res.push(r);
            }
        });
        
        return res;
    };
    
    Array.prototype.append = function(itm) {
        this.concat(itm);
        return this;
    };
    
    Array.prototype.alert = function() {
        //Alerts each index of an array     
        this.each(function(i) {
            var proceed = i.toString().confirm();
            if (!proceed) q.u.exit();
        });
    };
    
    Array.prototype.isArray = function() {
        //Of course it is!
        return(true);
    };
    
    Array.prototype.find = 
    Array.prototype.filter = function(f) {
        //Filters an object by a function reference(f);
        if (f == null) return this;
        
        if (typeof(f) == "function") {
            return this.each(f);
        }
        else if ("string object number".contains(typeof(f))) {
            return this.each(function(o, k, i) {
                if (o.toString().matches(f)) return o;
            });
        }
    };
    
    Array.prototype.sum = function sum(optkey) {
        //returns a sum of all values in this array
        //if optkey is specified it will sum up the value of optkey in each hash
        var ret = 0;
        this.each(function(v) {
            if (optkey) {
                ret += q.def(v[optkey], 0).toFloat();
            }
            else {
                ret += v.toFloat();
            }
        });
        return ret;
    };
    
    Array.prototype.median = function() {
        //returns the median of all values in this array
        if (this.length % 2 == 0) { 
          //even number count - average the two middle points
          return (this[(this.length / 2)] + this[(this.length / 2) + 1]) / 2; 
        }
        else {
          return this[((this.length - 1) / 2) + 1];
        }
    };
    
    Array.prototype.average = function() {
        //returns the average of all values in this array
        return (this.sum() / this.length);
    };
    
    Array.prototype.remember = function(key) {
        //remembers a serialization of this array by a given key
        return q.mem.remember(key, this.dump());    
    };
    
    Array.prototype.clone = 
    Array.prototype.copyOf = function() {
        //creates a value copy of this array
        return(this.dump().toVar());    
    };
    
    //An alias for slice which describes the behavior a little better
    //as where slice could mean cut an array from index n and to index m
    //or it could mean cut an array from index n with a length of m
    Array.prototype.between = Array.prototype.slice;
    
    Array.prototype.to = function(end) {
        //returns values up to the specified ending index
        return this.slice(0, end);
    };
    
    Array.prototype.from = function(start) {
        //alias for slicing this array with a start position only
        //from is inclusive
        //uses max to prevent wrapping behavior of slice
        return(this.slice(Math.max(start - 1, 1)));   
    };
    
    Array.prototype.after = function(start) {
        //alias for slicing this array after a start position
        //after is exclusive
        //uses min to prevent wrapping behavior of slice
        return(this.slice(Math.min(start, this.length)));   
    };
    
    Array.prototype.mid = function(start, length) {
        //allows for vb like slicing of arrays
        return this.from(start).to(length);    
    };
    
    Array.prototype.label = function(params) {
        //Add's key names to each index of this array and returns a hash
        params = me().args().flatten();
    
        var ret = {};
        this.each(function(v, i) {
            ret[params[i]] = v;
        });
        return ret;
    };
    
    Array.prototype.padLeft = function(n, chr) {
        //Assuming this is an array of strings
        //pad each item with n characters
        return this.each(function(v) {
            return v.toString().padLeft(n, chr);  
        });
    };
    
    Array.prototype.occurrence = function() {
        //returns a distinct hash of values and their occurrence counts
        var h = q.clean({});
        
        this.each(function(v) {
            h[v] = h[v] != null ? h[v] + 1 : 0;
        });
       
        return h; 
    };
    
    Array.prototype.returns = function() {
        //returns a logical value from various array forms
        if (!this.length) return null;
        if (this.length == 1) return this[0];
        return this;
    };
    
    Array.prototype.rotate = function Array_prototype_rotate() {
        //Rotates the structure of an array of hashes
        //used primarily for q.web.element[table_elem].toHash() 
        //from a vertial to horizontal arrangement of data
        hashrows = this;
        newkeys = [];
        headerhash = {};
        primarykey = this[0].keys()[0];
       
        //take all values in "left most column" of data for use as new header
        hashrows.each(function(h) {
            var v = h.toValueArray()[0];
            headerhash[v.toAlphaNum().camelCase()] = v;
        });
        
        //remove actual header record
        primaryvals = hashrows.shift(); 
        newhash = {};
        
        hashrows.each(function(h) {
            //use the first value as the key for the remaining values
            var col = h.toValueArray()[0].toAlphaNum().camelCase();
            h.each(function(v, k, i) {
                //don't record the first column as it's used as the key
                if (i >= 1) {
                    newhash[k] = q.u.def(newhash[k], {});
                    newhash[k][primarykey] = primaryvals[k];
                    newhash[k][col] = v;
                }
            });
        });
        
        //convert newhash into an array
        return [headerhash].concat(newhash.morph([]));
    };
    
    Array.prototype.sortByDateAttr = function(attr, desc) {
        return this.sort(function(a, b) {
            var d1 = new Date(a[attr]);
            var d2 = new Date(b[attr]);
            return desc ? (d2 - d1) : (d1 - d2);
        });
    };
    
    Array.prototype.sortByStringAttr = function(attr, desc) {
        return this.sort(function(a, b) {
            var s1 = a[attr].toLowerCase();
            var s2 = b[attr].toLowerCase();
            if (s1 < s2) {return desc ? 1 : -1;}
            if (s1 > s2) {return desc ? -1 : 1;}
            return 0;
        });
    };
    
    Array.prototype.sortByNumAttr = function(attr, desc) {
        return this.sort(function(a, b) {
            return desc ? (b[attr] - a[attr]) : (a[attr] - b[attr]);
        });
    };
    
    Array.open = function(path) {
        //returns an array that operates as usual except you can save it at anypoint and it will write itself out to the (path)        
        var arry = [];
       
        //load array object from file 
        arry.refresh = function() {
            //refresh an array object from file overwritting any recent modifications
            arry.length = 0;
            arry.splice.apply(arry, [0, 0].concat(q.f.open(path).toVar() || []));
        };
        arry.refresh();
        
        //bind a save function if not already there
        arry.save = function() {
            //save this array to {path}
            q.f.save(path, arry.dump());
        };
        
        var realpush = arry.push;
        arry.push = function() {
            realpush.apply(arry, arguments);
            arry.save();
        };
        
        arry.del = function() {
            //delete this bound json array file
            arry = null;
            path.del();
        };
        
        return arry;
    };
    
    Array.prototype.counts = function() {
        //return a hash of unique items and their respective frequencies this array
        return this.each(function(w) {
            c[w] = q.def(c[w], 0);
            c[w]++;
        });
    };    
})();
/*
////////////////////////////////////////////////////////////////////////////////
   _____ __       _                ____             __        __                       
  / ___// /______(_)___  ____ _   / __ \_________  / /_____  / /___  ______  ___  _____
  \__ \/ __/ ___/ / __ \/ __ `/  / /_/ / ___/ __ \/ __/ __ \/ __/ / / / __ \/ _ \/ ___/
 ___/ / /_/ /  / / / / / /_/ /  / ____/ /  / /_/ / /_/ /_/ / /_/ /_/ / /_/ /  __(__  ) 
/____/\__/_/  /_/_/ /_/\__, /  /_/   /_/   \____/\__/\____/\__/\__, / .___/\___/____/  
                      /____/                                  /____/_/  
 
////////////////////////////////////////////////////////////////////////////////
*/

(function() {

    String.prototype.spliceMultiwords = function(divider, convert) {
        return str.replace(/::/g, '/').replace(/([A-Z]+)([A-Z][a-z])/g, function(match){
            match = match.split("");
            return match[0] + divider + match[1];
        }).replace(/([a-z\d])([A-Z])/g, function(match){
            match = match.split("");
            return match[0] + divider + match[1];
        }).replaceAll("_", divider).toLowerCase();
    };
    
    String.prototype.dasherize = function dasherize() {
        return this.spliceMultiwords("-", "_");
    }
    
    String.prototype.underscore = function underscore(str) {
        return this.spliceMultiwords("_", "-");
    }
        
    String.prototype.truncate = function(len, suffix, strict) {
        //truncates each string to a specified length including the optional suffix
        //strict cuts off the length at the specified character
        //   this is a really long titl...
        //otheriwse the closes word length match
        suffix = suffix || "";
        
        if (strict) {
            return this.substr(0, len - suffix.length) + suffix;
        }
        else {
            var res = "";
            this.words().each(function(w) {
                var remlen = len - res.length - suffix.length;
                var excess = res.length + w.length - len + suffix.length;
                
                if ((remlen > excess) || excess < 0) {
                    res += w + " ";
                } else {
                    q.u.exit();
                }
            });
            
            return res.trim() + suffix;
        }
    };
    
    String.prototype.condense = function() {
        return this.replace(/\s+/gi, " "); 
    };
    
    String.prototype.isNumber = function() {
        //returns true if this value is actually a number
        return this == String(parseFloat(this));
    };
    
    String.prototype.eod = function() {
        //Splits off endofdata tag from this string
        return this.split("[ENDOFDATA]")[0];    
    };
    
    String.prototype.render = function(data, opt_brackets) {
        //opt_variable_char_par is an array of characters used to wrap variables in template files
        //allows for customized wrappers like ["|", "|"] or ["{", "}"]
        var brackets = opt_brackets || ['{', '}'];
       
        if (brackets == "guess") {
            var c = this.charCounts().hashsort("char");
            
            if (c["%"] > c["{"]) {
                brackets = "%";
            }
        }
        
        if (typeof brackets == "string") {
            brackets = [brackets, brackets];
        }
        
        //Match valid javascript variable names inside specified brackets
        var js_var = new RegExp("\\" + brackets[0] + "([a-z$_][\\[\\]\\.a-z0-9$_]*?)\\" + brackets[1], "i");
        var output = this.eod();
        var changing = "";
        
        while (true) {
            var vars = output.match(js_var);
            if (vars && vars[1]) {
                if (data && data[vars[1]] != null) {
                    do {
                        changing = output;
                        output = output.replace(vars[0], (data[vars[1]] || ""));
                    } while (changing != output);
                }
                else {
                    //remove variable from the content
                    output = output.replace(vars[0], (vars[1].tryval() || ""));
                }
            }
            else {
                break;
            }
        }
            
        return(output);
    };
    
    String.prototype.when = function(cond) {
        //Evaluates the string when condition [cond] == true
        if (cond) {
            this.tryval(true); 
        }
    };
    
    String.prototype.each = function() {
        //normalized interface for "single index" arrays :D
        return [].each.apply([this], arguments);
    };
    
    String.prototype.join = function(str) {
        //Normalizes Array.join behavior when param is either an array or string
        return this;   
    };
    
    String.prototype.remember = function(key) {
        //Remember the value of a this by a given key
        return q.mem.remember(key, this);    
    };
    
    String.prototype.toLower = function() {
        return(this.toLowerCase());    
    };
    
    String.prototype.lastChar = function() {
        return(this.substring(-1, 0));
    };
    
    String.prototype.copy = function() {
        return q.os.copy(this);
    };
    
    String.prototype.isString = function() {
        return(true);
    };
    
    String.prototype.toInt = function() {
        var val = this.replace(/[^\d.]/g, "");
        return(parseInt(val));
    };

    String.prototype.shift = function(v) {
        return [this, v].clean();    
    };
    
    String.prototype.alert = function() {
        alert(this);
    };
    
    String.prototype.doesMatch = 
    String.prototype.matches = function(reg, opt) {
        return (new RegExp(reg, opt)).test(this);
    };
    
    String.prototype.Capitalize = function() {
        if (this.trim().length > 0) {
            return this.chars()[0].toUpperCase() + this.chars().from(1).join("").toLowerCase();    
        }
        return "";
    };
    
    String.prototype.capitalize =
    String.prototype.CAPITALIZE = function() {
        return this.toUpperCase();
    };
    
    String.prototype.Camelize = 
    String.prototype.upperCamel = 
    String.prototype.CamelCase = function() {
        return (this.words().each(function(w) {
            return w.trim().Capitalize();   
        }).join("").replace("_", ""));
    };
    
    String.prototype.camelize =
    String.prototype.lowerCamelCase = 
    String.prototype.camelCase = function() {
        //converts a string to camel case
        var chars = this.CamelCase().chars();
        if (chars.length == 0) {
            return "";
        }
        else {
            return chars[0].toLowerCase() + chars.from(1).join("");
        }
    };
    
    String.prototype.escapeJSON = function() {
        //Escape Text for JSON Evaluation
        //  escape '\' and '"'
        var str = this.replace(/\\/g, "\\\\").replace(/\"/g, "\\\"");
        // escape \n and \r
        str = str.replace(/\n/g, "\\n").replace(/\r/g, "\\r");
        
        return(str);
    };
    
    String.prototype.varType = 
    String.prototype.__type = function() {
        return('string');
    };
        
    String.prototype.divide = function(n) {
        //Divides the string at character position (n)
        var ret = [];
        ret.push(this.slice(0, n));
        ret.push(this.slice(n + 1));
        return(ret);
    };
    
    String.prototype.escape = function() {
        //Escapes a string for standard url key chars
        var escaped = escape(this);
            escaped = escaped.replace(/\//g,"%2F");
            escaped = escaped.replace(/\?/g,"%3F");
            escaped = escaped.replace(/=/g,"%3D");
            escaped = escaped.replace(/&/g,"%26");
            escaped = escaped.replace(/@/g,"%40");
        return(escaped);
    };
    
    Number.prototype.encode = 
    String.prototype.encode = function() {
        //encodes a string for url component use
        return encodeURIComponent(this.toString());    
    };
    
    Number.prototype.decode = 
    String.prototype.decode = function() {
        //decodes a string encoded with encode
        return decodeURIComponent(this.toString());    
    };
    
    String.prototype.htmlEntityEncode = function() {
        //TODO: continue enhancing this function
        var encoded = this;
            encoded = encoded.replace(/\&/g, "&amp;")
            encoded = encoded.replace(/</g, "&lt;");
            encoded = encoded.replace(/>/g, "&gt;");
        return(encoded);
    };
    
    String.prototype.isEmail = function() {
        //returns true if string (this) is an email
        if (this.match(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b/i)) {
            return(true);
        }
    };
    
    String.prototype.isUrl = function() {
        return this.match(/^https?:\/\//i) ? true : false;     
    };
    
    String.prototype.words = function() {
        ret = [];
        wrd = "";
        lst = "";
        nxt = "";
        chrs = this.chars();
        
        chrs.each(function(c, i, x) {
            nxt = chrs[x + 1] || "";
            
            if (c.match(/[^$%#@a-z0-9]/i)) {
                 if (wrd) {
                    if (c.match(/[\'\-\:\.]/) && nxt.match(/[a-z0-9]/i)) {
                        //allow contractions, compounds and continuations
                        wrd += c;
                    }
                    else {
                        //add word and separately add character
                        if (wrd.trim()) {
                            ret.push(wrd);
                        }
                        if (c.trim()) {
                            ret.push(c);
                        }
                        wrd = "";
                    }
                 }
                 else {
                    if (c != " ") {
                        //add isolated character
                        ret.push(c);
                    }
                 }
            }
            else {
                //compile the next word
                wrd += c;
            }
            //'remember' the last character next loop
            lst = c;
        });
        
        if (wrd.trim()) {
            //add the last word to the list
            ret.push(wrd);
        }
        
        return(ret);
    };
    
    String.prototype.toChars = 
    String.prototype.toCharacters = 
    String.prototype.characters =
    String.prototype.chars = 
    String.prototype.letters = function() {
        return(this.split(""));
    };
    
    String.prototype.alphas = function() {
        return(this.replace(/[^a-z ]*/gi, ""));
    };
    
    String.prototype.alphaNums = function() {
        return(this.replace(/[^a-z 0-9]*/gi, ""));
    };
    String.prototype.toAlphaNum = String.prototype.alphaNums;
    
    String.prototype.toHashKey = function() {
        //Converts any string to a suitable hash key
        return this.toAlphaNum().replace(/\s/g, "_").removeWhitespace();    
        
    }
    String.prototype.lines = function() {
        return(this.split(/[\r\n]/));
    };
    
    String.prototype.sentences = function() {
        var txt = this.replace(/(\r\n|\.\s|;\s|\:\s|\."\s|\.\)\s|\s{3,})/g, "$1\r\n");
        return(txt.split(/(\r\n)+/g));
    };
    
    String.prototype.removeWhitespace = function() {
        return(this.replace(/\r|\n|\t|\s/g, ""));
    };
    
    String.prototype.regexEscape = function() {
        //escapes characters in this string for use in regex
        return this.toChars().ea(function(c) {
            if ("^[].${}*()\+|?<>".match(c)) {
                return "\\" + c;
            } else {
                return c;
            }
        });
    };
    
    String.prototype.strip = 
    String.prototype.removeChars = 
    String.prototype.removeChar = function(chars) {
        chars = chars.join("").toChars();
        
        chars = chars.ea(function(c) {
            return c.regexEscape();  
        });
        
        var x = new RegExp("[" + chars.join("|") + "]", "gi");
        
        return this.replace(x, "");
    };
    
    String.prototype.removeNewLines = 
    String.prototype.removeNewlines = function() {
        return(this.replace(/\r|\n/g, ""));
    };
    
    String.prototype.fileExtension = function() {
        var str = this.split(/\?/, 1)[0];
        
        if (str.contains(".")) {
            if (str.length - str.lastIndexOf('.') <= 4) {
                return(str.slice(str.lastIndexOf('.')));
            } else {
                return('');
            }
        }
        return('');
    };
    String.prototype.fileExt = String.prototype.fileExtension;
    
    String.prototype.charClusters = function() {
      //return groups of characters separated by white space
      return(this.replace(/\r|\n|\t/g, " ").split(/\s+/g));
    };
    
    String.prototype.wordCombinations = function() {
        
        fs = [];
        words = x.words();
    
        words.length.each(function(n) {
            words.length.each(function(m) {
                var ws = words.slice(n, words.length - m); 
                if (ws.join("").trim()) {
                    fs.pushOnce(ws.join(" "));
                }
            });
        });
        
        return(fs);
    }
    
    String.prototype.keywords = function() {
        //returns words of nominal length for key words
        return this.words().each(function(w) {
            if (w.length >= 3 && w.length <= 15) {
                return w;
            }
        });
    };
    
    String.prototype.wordCounts = function() {
        //return a hash of words and their respective frequencies
        
        //Add to counts for each instance of a given word
        var retCounts = this.words().counts();
    
        return retCounts.ea(function(v, k) {
            return { word : k, count : v }; 
        });
    };
    
    String.prototype.charCounts = function() {
        return this.chars().counts().ea(function(v, k) {
            return { char : k, count : v };
        });
    };

    String.prototype.toFloat = function() {
      return(parseFloat(this) || 0);
    };
    
    String.prototype.removeUnicode = function() {
      return(this.replace(/[\u0080-\uFFFF]+/g, ''));
    };
    
    String.prototype.matchCount = function(s1) {
        //returns a count of matched strings (s1)
        return (this.length - this.replace(new RegExp(s1,"g"), '').length) / s1.length;
    };
      
    String.prototype.translate = function(flang, tlang) {
      //translates the current string into the provided language
      return(language().translate(this, flang, tlang));
    };
    
    String.prototype.newLines = function() {
        //Returns a count of new line characters in a string
        var newlines = this.match(/\r\n/g);
            
        if (newlines) {
            return(newlines.length);
        }
        else {
            return(0);
        }
    };
    
    String.prototype.toCharCode = function() {
    
        var ret = '';
        
        this.characters().each(function(i) {
            ret += i.charCodeAt(0);
        });
        
        return ret;
    };
    
    String.prototype.abbreviate = function(len, show_ending) {
        len = len || 20;
        show_ending = show_ending || false;
    
        if (this.length > len) {
            if (show_ending) {
                //returns ThisIsA...FileName
                return(this.substr(0, len / 2) + "..." + this.substring(this.length - (len / 2 + 1)));
            }
            else {
                //returns ThisIsAVeryLong...
                return(this.substr(0, len) + "...");
            }
        }
        else {
            return(this.toString());
        }
      };
    
    String.prototype.trim = function(chars) {
        //Trims leading and trailing whitespace from this string
        //[chars] allows for trimming alternate characters from this string
        chars = q.def(chars, "\\s");
        return this.replace(new RegExp("^[" + chars + "]+|[" + chars + "]+$", "g"), '');
    };
    
    String.prototype.ltrim = 
    String.prototype.trimLeft = function(chars) {
        chars = q.def(chars, "\\s");
        return this.replace(new RegExp("^[" + chars + "]+", "g"), "");
    };
    
    String.prototype.rtrim = 
    String.prototype.trimRight = function(chars) {
        chars = q.def(chars, "\\s");
	return this.replace(new RegExp("[" + chars + "]+$", "g"), "");
    };
    
    String.prototype.foundIn = function(val) {
        return(val.intersection(this).length);     
    };
    
    String.prototype.contains = function(val) {
        //returns true if this string contains all supplied params
        var tests = q.me().args();
        return tests.ea(function() {
            if (this.intersection(val).length) {
                return 1;
            } else {
                q.u.exit(0);
            }
        }).sum() == tests.length; 
    };
    
    String.prototype.toXML = function() {
        //Converts XML text into an XML DOM Object
        var dom = null;
        var parseError = new Error(-500, "Unable to parse XML: \n\n" + dom.parseError.reason + "\n\n" + dom.parseError.srcText);
        
        if (window.DOMParser) {
           try { 
              dom = (new DOMParser()).parseFromString(this, "text/xml"); 
           } 
           catch (e) { throw parseError }
        }
        else if (window.ActiveXObject) {
           try {
              dom = new ActiveXObject('Microsoft.XMLDOM');
              dom.async = false;
              if (!dom.loadXML(this)) throw parseError;
           } 
           catch (e) { throw parseError }
        }
        else {
           Function.notImplemented();
        }
        
        return dom;
    };
    
    String.prototype.intersection = function(val) {
        var str = this;
        
        if (val.__type() == "string") {
            if (str.toLowerCase().indexOf(val.toLowerCase()) > -1) {
                return(val);
            } 
        }
        else if (val.__type() == "array") {
            return val.each(function(x) {
                if (str.contains(x.toString())) {
                    return x;
                }
            });
        }
    
        return("");
    };
    
    String.prototype.tryval = function(report) {
        //eval a script inside a try catch to swallow any errors
        //or report them to the alert handler
        try {
            return this.eval();
        } catch(e) {
            if (!e.exit && report) {
                e.debug();
            }
            return(false);
        }
    };
    
    String.prototype.eval = function() {
        eval("\n " + this + "\n");
        return(true);
    };
    
    String.prototype.debug = function() {
        if (!WScript.simulated) {
            ("debugger;" + this).eval();
        } else {
            var ctr = q.u.guid().forVar();
            q.gc[ctr] = 0;
            var lines = this.split(/\}/g);
            var debug = ctr + "++;" + lines.join("}" + ctr + "++;");
            try {
                debug.eval();
            } catch(e) {
                var i = q.gc[ctr];
                if (i == 0) {
                    this.copy();
                    alert("Invalid Syntax");
                }
                else {
                    alert("Error: on line " + i);
                    alert("Error: statement: \n\n" + lines[i]);
                }
            }
        }
    };
    
    String.prototype.toArray = function(opt_separator) {
        //Convert a string to an array by placing the string contents in the first index
        var ret = new Array();
        if (opt_separator) {
            this.split(opt_separator).each(function (i) {
                ret.push(i.trim());
            });
        }
        else {
            //To convert a string variable into an array
            //with the contents of the string, good for
            //consistent interations over mixed data types
            ret.push(this.toString());
        }
        return(ret);
    };
    
    String.prototype.from = function(v) {
        //returns a part of the string from either the numerical index of v or
        //if v is a string from the first matching position of the string
        if (!v) return this;
        
        if (v.isNumeric()) {
            return this.substr(v);
        }
    
        var pos = this.indexOf(v);
        if (pos >= 0) {
            return this.substr(pos);
        }
        
        return "";
    };
    
    String.prototype.to = function(v) {
        
        if (!v) return this;
        
        if (v.isNumeric()) {
            return this.substr(0, v);
        }
    
        var pos = this.indexOf(v);
        if (pos >= 0) {
            return this.substr(0, pos);
        }
        
        return "";    
    };
        
    String.prototype.endsWith = function(str) {
        //Returns true if the word ends with [str]
        //TODO: compare performance of each approach
        //return (this.slice(-1 * str.length) == str);
        
        //Returns true if the word ends in [str]
        return this.substr(this.length - str.length) == str;
    };
    
    String.prototype.removeTrailing = function(str) {
        //removes {str} from end of this if it exists
        if (this.endsWith(str)) {
            return this.substr(0, this.length - str.length);
        }
        
        return this;
    };
    
    String.prototype.forwardSlashes = function() {
        //Switches all backslashes to forwardslashes
        return(this.replace(/\\/g, "\/"));
    };
    
    String.prototype.backSlashes = function() {
        //Switches all forwardslashes to backslashes 
        return(this.replace(/\//g, "\\"));
    };
    
    String.prototype.startsWith = function(str) {
        //Returns true if the word starts with [str]
        return (this.substr(0, str.length) == str);
    };
    
    String.prototype.replaceAll = function(reg, rep) {
        if (reg.__type() == 'regexp') {
            reg = reg.source;
        } 
        return this.replace(new RegExp(reg, "g"),rep);
    };
    
    String.prototype.descape = 
    String.prototype.unescape = function() {
        return(unescape(this));    
    };
    
    String.prototype.forVar = function() {
        //returns a string capable of being a variable name in javascript
        return "Q" + this.replace(/[^a-z0-9_]/ig, "");    
    };
    
    String.prototype.toVar = function(silent) {
        //Evaluates [this] and returns result as a varaible
        var evalstr = "";
        if (this.toString()) {
          try {
            eval("evalstr = " + this.trim('"') + ";");
          } catch (e) {
            if (!e.exit && !silent) {
              e.debug();
            }
          }
        }
    
        return(evalstr);
    };
    
    String.prototype.isEmpty = 
    String.prototype.isBlank = function() {
    //returns true or false if a string is empty
        return (this == null || this == "");
    };
    
    String.prototype.summary = function(perc) {
        //Creates a 10% summary of the text
        perc = q.u.def(perc, 10);
        var wd = q.apps.word.openNew();
            wd.content(this);
            wd.autoFormat();
        var res = wd.summary(10);
            wd.close();
    
        return(res);
    };
    
    String.prototype.isFunction = function() {return(false);};
    
    String.prototype.commafy = function () {
        return this.replace(/(^|[^\w.])(\d{4,})/g, function($0, $1, $2) {
                return $1 + $2.replace(/\d(?=(?:\d\d\d)+(?!\d))/g, "$&,");
        });
    };
    
    String.prototype.repeat = function(cnt) {
        var a = new Array(cnt.toInt() + 1);
        return(a.join(" ").replace(/\s/g, this).toString());
    };
                
    String.prototype.hasDriveLetter = function() {
        //Check for drive
        return this.match(/^[a-z]:/i);
    };
    
    String.prototype.forFilename = 
    String.prototype.toFile = function(ext) {
        //Turns any string into a valid file path
        var f = this.replace(/^file\:\/\/\//, "");
            f = f.replace(/\\/g, "/");
        
        var drive = "", path = "", fname = "";
        
        if (f.hasDriveLetter()) {
            drive = f.slice(0, 3);
            path = f.slice(3);
        }
        else {
            drive = "";
            path = f;
        }
        
        var parts = "";
        
        if (path.contains("/")) {
            parts = path.split(/\//g);
        }
        else {
            parts = [path];
        }
        
        parts.each(function(p, i) {
            parts[i] = p.replace(/:|\s|\*|\"|\>|\<|\|/g, "_");  
        });
        
        path = parts.join("/");
    
        //add provided extension if it exists
        if (ext) {
            path += "." + ext.replace(/^\./, '');
        }
        
        return drive + path;
    };
    
    String.prototype.tempFile = function() {
        return this.fileName().prepend(q.f.temp().parentFolder());
    };
    
    String.prototype.prepend = function(str) {
        return str + this; 
    };
    
    String.prototype.count = function(str) {
        var l1 = this.length;
        var s = this;
        
        if (str != null && str.length > 0) {
            return((l1 - s.replaceAll(str, "").length) / str.length);
        }
    
        //Allow for result count testing where functions either return
        //a single string or an array of strings depending on count
        return (1);
    };
    
    
    String.prototype.alert = function() {
    //Alerts a string to the user via the implemented alert interface
        alert(this);
    };
    
    String.prototype.confirm = function() {
        //Allows inspection of strings via a dialog box
        return(confirm(this));
    };
    
    String.prototype.paginate = function(len) {
    //Returns an array of strings sized less than or equal to [len]
        var pages = [];
        var totalLen = this.length;
       
        for (var i = 0; i < totalLen; i = i + len) {
            pages.push(this.substr(i, len));
        }
    
        return(pages);
    };
    
    String.prototype.isImage = function () {
      return(this.match(/\.(jpg|gif|bmp|png)$/) != null);
    }
    
    String.prototype.isNumeric = function() {
        //Returns true if value is or could be a number
        return !isNaN(parseFloat(this));
    };
    
    String.prototype.toNumber = function() {
        return(parseFloat(this));
    };
    
    String.prototype.getSet = function(v) {
        //gets or updates a string value
        if (v != null) {
            return v;
        } else {
            return this;
        }
    };
    
    String.prototype.name = 
    String.prototype.functionName = function() {
    //Returns the name of a dump()'d function reference
      var n = this.match(/function (.*?)\(/);
    
      if (!n) n = this.match(/(?:var )*(.*?)\s*=\s*function/);
      if (!n) n = ["", "anonymous"];
    
      return(n[1]);
    };
    
    String.prototype.escapeQuotes = function(escChar) {
        if (!escChar) {
            escChar = '\\"';
        }
        return(this.replace(/\"/g, escChar));
    };
    
    String.prototype.files = function(filter) {
        //Returns an array of fso file objects contained in the folder
        //if the string is a folder
        if (this.isDirectory()) {
            return q.u.enumerate(q.f.getFolder(this).dir('.')).filter(filter); 
        }
    };
    
    String.prototype.padLeft = function(n, chr) {
        chr = q.def(chr, " ");
        
        var ret = this;
        
        while (n) {
            ret = chr + ret;
            n--;
        }
        
        return(ret);
    };
    
    String.prototype.padRight = function(n, chr) {
        var cnt = Math.max(n - this.length, 0);
        var ret = this;
        
        while(cnt) {
            ret += chr;
            cnt--;
        }
        
        return(ret);
    };
    
    String.prototype.precision = function(n) {
        var parts = this.split(/\./);
        
        if (!parts[1]) {
            parts.push("00");
        }
        parts[1] = parts[1].padRight(n, "0");
        
        return(parts.join("."));
    };
    
    String.prototype.speakAsync = function(voice_id) {
        q.shell.evalAsync({ str: this,
                            voice_id: voice_id },
                            "str.speak(voice_id);");
    };
    
    String.prototype.toVoiceSentences = function() {
       //remove special characters that can invoke the demo registration advert in our voice engine
       //TODO: let's add a feature to convert works to their phonetic countertypes to improve audibility of the voices
       return this.replace(/[\r\n\s\"\:\-\,\(\)\[\]]/g, " ").replace(/[\;\?\!]/g, '. ').replace(/\s+/g, " ").sentences();
    };
    
    String.prototype.record = function(outfile, voice_id, speed) {
        //records this text to audio file (outfile)
        if (!outfile) return false;
       
        var path = outfile.parentFolder();
        var fname = outfile.fileName().withoutExt() || "final";
        
        //record each sentence to it's own file
        this.toVoiceSentences().each(function(sentence, key, idx) {
            q.shell.runDelay(700, '.\\bin\\opd2d.exe ' + path + "recording" + idx.padLeft(5, '0') + '.mp3 20min', q.shell.style.showCurNoActive);
            sentence.speak(voice_id);
            q.shell.kill("opd2d.exe");
        });
        
        q.u.sleep(500);
        
        //now play back all sentence files and record to one audio file
        q.shell.runAsync('.\\bin\\opd2d.exe ' + path + fname + '.mp3 60min', q.shell.style.showCurNoActive);
        
        q.f.dir(path, "recording\\d+\.mp3").each(function(f) {
            return f.path;
        }).join(" ").play();
        
        q.shell.kill("opd2d.exe");
    };
    
    String.prototype.save = function(f) {
        q.f.save(f, this);
        return f;
    };
    
    String.prototype.play = function() {
        //play this media file path
        //switched to q.api because ocx7 has a bug that shows a blocking dialog when the media lacks drm components
        q.api.call.apply(this, this.split(" ").prepend("play"));
    }
    
    String.prototype.speak = function(voice_id, speed) {
        
        if (speed == null) speed = -3;
        
        //Speaks this string using the cepstral voice synthesizer
        var v = new ActiveXObject("Sapi.spVoice");
            v.voice = v.getvoices().item(voice_id || 0);
           
        var sc = 0; 
    
        this.toVoiceSentences().each(function(sentence) { 
            if (sc % 2 == 0) { 
                sc = 0; 
                v.volume = 0; 
                v.rate = 10; 
                v.speak("a"); 
            } 
            
            sc++; 
            
            v.volume = 100; 
            v.rate = speed;
            v.speak(sentence); 
        });
    };
    
    String.prototype.exec = function() {
        q.shell.run(this);   
    };
    
    String.prototype.wrap = function(str, str2) {
        //Wraps a string (this) with copies of str or sandwiches between str & str2
        if (!str2) {
            str2 = str;
        }
        
        return str + this + str2;   
    };
    
    String.prototype.quote = function() {
        return this.wrap("'");   
    };
    
    String.prototype.dblquote = 
    String.prototype.dblQuote = 
    String.prototype.doubleQuote = function() {
        return this.wrap('"');
    };
    
    String.prototype.toWord =
    String.prototype.toWords = function() {
        //Convert stringified numbers to their english words
        return (this * 1).toWords();
    };
    
    String.prototype.toDeg = function() {
        //Convert a numeric string to degrees
        return this.toNumber().toDeg();    
    };
    
    String.prototype.toRad = function() {
        //Convert a numeric string to radians
        return this.toNumber().toRad();   
    };
    
    String.prototype.prepHTML =
    String.prototype.prepHtml = function() {
        return this.replace(/\</g, "&nbsp;<").replace(/\<\/p\>/i, "\n\n").replace(/\<\/li\>/i, "\n\n");
    };
    
    String.prototype.stripXml = 
    String.prototype.stripHtml = 
    String.prototype.stripTags = function(ie_opt, tag_opt) {
        ie_opt = q.def(ie_opt, q.ie);
        tag_opt = q.def(tag_opt, "div");
        
        var obj = ie_opt.document.createElement(tag_opt);
            obj.innerHTML = this.prepHtml();
        var txt = obj.innerText
            obj = null;
            
        return txt;
    };
    
    String.prototype.test = function(str, opt) {
        //Normalizer for RegExp.test prototype
        if (opt.contains("i")) {
            return str.toLower() == this.toLower();
        }
        else {
            return str == this;
        }
    };
    
    String.prototype.toHtml = function() {
        var str = this;
            str = str.htmlEntityEncode();
            str = str.replaceAll("\r\n", "<br/>");
            str = str.replaceAll("\n", "<br/>");
            str = str.replaceAll("\t", "&nbsp;".repeat(4));
        return str;
    };
    
    String.prototype.scriptTags = function() {
        //returns an array of script references in a given chunk of html     
        var block = false, incs = [], code = [];
        
        this.lines().ea(function(txt) {
            if (!block) {
                if (txt.contains("<" + "script")) {
                    if (txt.contains("src=")) {
                        incs.push(txt.between('src="', '"'));
                    } else {
                        block = true;
                    }
                }
            } else {
                if (txt.contains("</" + "script>")) {
                    block = false;   
                } else {
                    code.push(txt);
                }
            }
        });
                        
        return { code : code.join("\r\n"),
                 includes : incs };
    };
   
    String.prototype.removeScripts = function() {
        //removes any script tags from this string
        var block = false;
        return this.lines().ea(function(txt) {
            if (!block) {
                if ( txt.contains("<" + "script") &&
                    !txt.contains("src=")) {
                    block = true;
                }
            } else {
                if (txt.contains("</" + "script>")) {
                    block = false;   
                } else {
                    return txt;
                }
            }
        }).join("\r\n");
    };
    
    String.prototype.cssTags = function() {
        var block = false, incs = [], styles = [];
        
        this.lines().ea(function(txt) {
            if (!block) {
                if (txt.contains("<" + "style")) {
                    if (txt.contains("href=")) {
                        incs.push(txt.between('href="', '"'));
                    } else {
                        block = true;
                    }
                }
            } else {
                if (txt.contains("</" + "style>")) {
                    block = false; 
                }
                else {
                    styles.push(txt);
                }
            }
        });
    
        return ({ includes : incs,
                  styles : styles.join("\r\n") });
    };
    
    String.prototype.removeCSS = function() {
        //removes any style tags from this string
        var block = false;
        return this.lines().ea(function(txt) {
            if (!block) {
                if ( txt.contains("<" + "style") &&
                    !txt.contains("href=")) {
                    block = true;
                }
            } else {
                if (txt.contains("</" + "style>")) {
                    block = false;   
                } else {
                    return txt;
                }
            }
        }).join("\r\n");
    };
    
    String.prototype.between = function(left, right) {
        //returns matching text between left & right matching strings
        //left & right can be numeric and the string will be parsed by character index
        return this.from(left).to(right);
    };
    
    String.prototype.reduce = function(str, mods) {
        //reduces duplicate consecutive instances of provided str
        //mods defaults to "gi" but can be augmented
        mods = q.def(mods, "gi");
        
        var r = new RegExp(str + "{2,}", mods);
        
        return this.replace(r, str);
    };
    
    /*
    ////////////////////////////////////////////////////////////////////////////////
        _   __                __                 ____             __        __                       
       / | / /_  ______ ___  / /_  ___  _____   / __ \_________  / /_____  / /___  ______  ___  _____
      /  |/ / / / / __ `__ \/ __ \/ _ \/ ___/  / /_/ / ___/ __ \/ __/ __ \/ __/ / / / __ \/ _ \/ ___/
     / /|  / /_/ / / / / / / /_/ /  __/ /     / ____/ /  / /_/ / /_/ /_/ / /_/ /_/ / /_/ /  __(__  ) 
    /_/ |_/\__,_/_/ /_/ /_/_.___/\___/_/     /_/   /_/   \____/\__/\____/\__/\__, / .___/\___/____/  
                                                                            /____/_/                 
    
    ////////////////////////////////////////////////////////////////////////////////
    */
    
    Number.prototype.isEven = function() {
        return this.mod(2) == 0;    
    };
    
    Number.prototype.isOdd = function() {
        return this.mod(2) != 0; 
    };
    
    Number.prototype.toWord =
    Number.prototype.toWords = function() {
        //Convert 3 into "three"   
        var th = ['','thousand','million', 'billion','trillion'];
        var dg = ['zero','one','two','three','four', 'five','six','seven','eight','nine'];
        var tn = ['ten','eleven','twelve','thirteen', 'fourteen','fifteen','sixteen', 'seventeen','eighteen','nineteen'];
        var tw = ['twenty','thirty','forty','fifty', 'sixty','seventy','eighty','ninety'];
        
        var s = this.toString();
            s = s.removeChar(",");
        if (s.isNumber()) {
            var x = s.indexOf('.');
            
            if (x == -1) x = s.length;
            if (x > 15) return '[a really big number]';
           
            var n = s.split('');
            var str = '';
            var sk = 0;
            for (var i=0; i < x; i++) {
                if ((x-i)%3==2) {
                    if (n[i] == '1') {
                        str += tn[Number(n[i+1])] + ' ';
                        i++;
                        sk=1;
                    }
                    else if (n[i]!=0) {
                        str += tw[n[i]-2] + ' ';
                        sk=1;
                    }
                }
                else if (n[i]!=0) {
                    str += dg[n[i]] +' ';
                    if ((x-i)%3==0) str += 'hundred ';
                    sk=1;
                } if ((x-i)%3==1) {
                    if (sk) str += th[(x-i-1)/3] + ' ';
                    sk=0;
                }
            }
            
            if (x != s.length) {
                var y = s.length;
                str += 'point ';
                for (var i=x+1; i<y; i++) {
                    str += dg[n[i]] +' ';
                }
            }
            
            return str.condense();
        }
        else {
            return "[nan]";
        }
    };
    
    
    Number.prototype.dividedBy = function(val) {
        return this / val;    
    };
    
    Number.prototype.div =
    Number.prototype.division = function(val) {
        return Math.floor(this / val);    
    };
    
    Number.prototype.mod =
    Number.prototype.modulus = function(val) {
        return this % val;    
    };
    
    Number.prototype.remember = function(key) {
        return q.mem.remember(key, this);    
    };
    
    Number.prototype.join = function(str) {
        //Repeats a string n times
        return this.toArray().join(str) + str;
    };
    
    Number.prototype.toArray = function() {
        //creates an array with n indexes
        return new Array(this);
    };
    
    Number.prototype.isNumeric = function() {
        //normalized object interface
        return(true);
    };
    
    Number.prototype.toRomanNumeral = function() {
        //3 returns III
    };
    
    Number.prototype.ordinalize = 
    Number.prototype.toOrdinal = function() {
        //3 returns "3rd"
        if (11 <= this % 100 && this % 100 <= 13)
        {
            return this + "th";
        }
        else
        {
            return this + 
            { 1 : "st",
              2 : "nd",
              3 : "rd"}.cases(this % 10);
        }
    };
    
    Number.prototype.toThePowerOf =
    Number.prototype.power = function(n) {
        return(Math.pow(this, n));
    };
    
    Number.prototype.times = function(n, params) {
        if (n.isString()) n = n.toNumber();
        
        if (n.isNumber()) {
            return(this * n);
        }
        else if (n.isFunction()) {
            return(this.each(n, params))
        }
        else if (n.isArray()) {
            var ret = [];
            n.each(function(a) {
                n.push(a.times(n));  
            });
            return(ret);
        }
        else {
            return Function.notImplemented();
        }
    };
    
    Number.prototype.successor = function() {
        return(this + 1);    
    };
    
    Number.prototype.round = function() {
        return(Math.round(this));    
    };
    
    Number.prototype.floor = function() {
        return(Math.floor(this));
    };
    
    Number.prototype.abs = function() {
        return(Math.abs(this));
    };
    
    Number.prototype.ceil = function() {
        return(Math.ceil(this));
    };
    
    Number.prototype.toInt = function() {
        return(parseInt(this));
    };
    
    Number.prototype.__type = function() {
        return("number");
    };
    
    Number.prototype.ea =
    Number.prototype.each = function(f, params) {
        var a = [];
        var l = this;
        var i = 0;
        while (l--) {
          a.push(i++);
        }
        //reuse logic in q.u.each
        return q.u.each(a, f, params);
    };
    
    Number.prototype.round = function(dec) {
        //Rounds a number to the nearest decimal (dec) point
        return Math.round(this*Math.pow(10,dec))/Math.pow(10,dec);
    };
    
    Number.prototype.limit = function(m) {
        //Returns this or the maximum limit of m
        return Math.min(m, this);
    };
    
    Number.prototype.commafy = function () {
        return this.toString().commafy();
    };
    
    Number.prototype.alert = function() {
        alert(this.toString());
    };
    
    Number.prototype.toChar = function() {
        //Returns the number's respective ASCII Character
        return(String.fromCharCode(this));
    };
    
    Number.prototype.dividedBy = function(n) {
        //Divides this by n
        return(this / n);    
    };
    
    Number.prototype.remainder = function(n) {
        //returns the modulus (remainder) of n
        return(this % n);
    };
    
    Number.prototype.inMinutes = function() {
        //returns the numeric representation in minutes
        return(this.dividedBy(1000 * 60));    
    };
    
    Number.prototype.inSeconds = function() {
        //returns the numeric representation in seconds
        return(this.dividedBy(1000));
    };
    
    Number.prototype.seconds = function() {
        //returns the number of milliseconds for this number of seconds
        return(this * 1000);
    };
    
    Number.prototype.padLeft =
    Number.prototype.pad = function(n, chr) {
        chr = q.def(chr, "0")
        var ret = this.toString();
        var cnt = Math.max(n - ret.length, 0);
        
        while (cnt) {
            ret = chr + ret;
            cnt--;
        }
        
        return(ret);
    };
    
    Number.prototype.max = function(a) {
        //returns the largest number in supplied array params
        var m = null;
        
        return q.me().args().flatten().each(function(i) {
            return Math.max(this, i, m);
        }).last();
    };
    
    Number.prototype.min = function(a) {
        //returns the smallest number in supplied array params
        var m = null;
        
        return q.me().args().flatten().each(function(i) {
            return Math.min(this, i, m);
        }).last();    
    };
    
    Number.prototype.percent = function() {
        //returns a number in it's mathmatical percentage form
        return this / 100;
    };
    
    Number.prototype.toFloat = function() {
      return(parseFloat(this) || 0);
    };
    
    Number.prototype.toPrecisionFixed = function(precision) {
        //Format the significant digits of a number, using only fixed-point notation (no exponential)
        //@param   {Number} precision: Number of significant digits to appear in the returned string
        //@returns {String} A string representation of number which contains precision significant digits
        var numb = this < 0 ? -this : this;  // can't take log of -ve number...
        var sign = this < 0 ? '-' : '';
        
        if (numb == 0) { n = '0.'; while (precision--) n += '0'; return n };  // can't take log of zero
      
        var scale = Math.ceil(Math.log(numb)*Math.LOG10E);  // no of digits before decimal
        var n = String(Math.round(numb * Math.pow(10, precision-scale)));
        if (scale > 0) {  // add trailing zeros &amp; insert decimal as required
          l = scale - n.length;
          while (l-- > 0) n = n + '0';
          if (scale < n.length) n = n.slice(0,scale) + '.' + n.slice(scale);
        } else {          // prefix decimal and leading zeros if required
          while (scale++ < 0) n = '0' + n;
          n = '0.' + n;
        }
        return sign + n;
    };
    
    Number.prototype.toDeg = function() {
        /** Convert radians to numeric (signed) degrees */
        return this * 180 / Math.PI;
    };
    
    Number.prototype.toRad = function() {
        return this * Math.PI / 180;
    };
    
    Number.prototype.toNumber = function() {
        return this;
    };
    
    Number.prototype.compassDirection = function() {
        //returns a compass direction based on a degree 0 - 360  
        var wd = this;
        var dir = "N";
        
        if (!wd || wd < 0 || wd > 360) return("N");
        if (wd >= 0 && wd <= 11.25 ||
            wd > 348.75 && wd <= 360) dir = "N";
        if (wd > 11.25 && wd <= 33.75) dir = "NNE";
        if (wd > 33.75 && wd <= 56.25) dir = "NE";
        if (wd > 56.25 && wd <= 78.75) dir = "ENE";
        if (wd > 78.75 && wd <= 101.25) dir = "E";
        if (wd > 101.25 && wd <= 123.75) dir = "ESE";
        if (wd > 123.75 && wd <= 146.25) dir = "SE";
        if (wd > 146.25 && wd <= 168.75) dir = "SSE";
        if (wd > 168.75 && wd <= 191.25) dir = "S";
        if (wd > 191.25 && wd <= 213.75) dir = "SSW";
        if (wd > 213.75 && wd <= 236.25) dir = "SW";
        if (wd > 236.25 && wd <= 258.75) dir = "WSW";
        if (wd > 258.75 && wd <= 281.25) dir = "W";
        if (wd > 281.25 && wd <= 303.75) dir = "WNW";
        if (wd > 303.75 && wd <= 326.25) dir = "NW";
        if (wd > 326.25 && wd <= 348.75) dir = "NNW";
        
        return dir;
    }
    
    Number.prototype.toVar = 
    Number.prototype.tryval = function() {
        return this;   
    };
    
    
    
    /*
    ////////////////////////////////////////////////////////////////////////////////
        ____                          ____             __        __                       
       / __ \___  ____ ____  _  __   / __ \_________  / /_____  / /___  ______  ___  _____
      / /_/ / _ \/ __ `/ _ \| |/_/  / /_/ / ___/ __ \/ __/ __ \/ __/ / / / __ \/ _ \/ ___/
     / _, _/  __/ /_/ /  __/>  <   / ____/ /  / /_/ / /_/ /_/ / /_/ /_/ / /_/ /  __(__  ) 
    /_/ |_|\___/\__, /\___/_/|_|  /_/   /_/   \____/\__/\____/\__/\__, / .___/\___/____/  
               /____/                                            /____/_/
               
    ////////////////////////////////////////////////////////////////////////////////
    */
    RegExp.prototype.match = function(s) {
        //Match a regext in string [s]
    };
    
    RegExp.prototype.__type = function() {
        return('regexp'); 
    };
    
    RegExp.prototype.isRegexp = 
    RegExp.prototype.isRegex = function() {
        return true;
    };
    /*
    ////////////////////////////////////////////////////////////////////////////////
        ____              __                    ____             __        __                       
       / __ )____  ____  / /__  ____ _____     / __ \_________  / /_____  / /___  ______  ___  _____
      / __  / __ \/ __ \/ / _ \/ __ `/ __ \   / /_/ / ___/ __ \/ __/ __ \/ __/ / / / __ \/ _ \/ ___/
     / /_/ / /_/ / /_/ / /  __/ /_/ / / / /  / ____/ /  / /_/ / /_/ /_/ / /_/ /_/ / /_/ /  __(__  ) 
    /_____/\____/\____/_/\___/\__,_/_/ /_/  /_/   /_/   \____/\__/\____/\__/\__, / .___/\___/____/  
                                                                           /____/_/
                                                                           
    ////////////////////////////////////////////////////////////////////////////////
    */
    
    Boolean.prototype.__type = function() {
        return("boolean");
    };
    
    Boolean.prototype.isBoolean = function() {
        return(true);
    };
    
    Boolean.prototype.alert = function() {
        alert(this.toString());
    };
    
    Boolean.prototype.succeeds = function() {
        return this;
    };
    
    Boolean.prototype.dump = function() {
        return q.dump(this);        
    };
    /*
    ////////////////////////////////////////////////////////////////////////////////
        ____        __          ____             __        __                       
       / __ \____ _/ /____     / __ \_________  / /_____  / /___  ______  ___  _____
      / / / / __ `/ __/ _ \   / /_/ / ___/ __ \/ __/ __ \/ __/ / / / __ \/ _ \/ ___/
     / /_/ / /_/ / /_/  __/  / ____/ /  / /_/ / /_/ /_/ / /_/ /_/ / /_/ /  __(__  ) 
    /_____/\__,_/\__/\___/  /_/   /_/   \____/\__/\____/\__/\__, / .___/\___/____/  
                                                           /____/_/
                                                           
    ////////////////////////////////////////////////////////////////////////////////
    */
    Number.prototype.length = function() {
        return this.toString().length;
    };
    
    Number.prototype.toDate = function() {
        return new Date(this);
    };
    
    Number.prototype.format = function(m) {
        
        var comma = false;
        
        if (m == "#") {
            //limit formatting to only numbers
            return parseInt(this);
        }
        
        //If at least one comma, use comma's
        if (m.contains(",")) {
            comma = true;
        }
    
        //Split the number and mask by a decimal if it exists    
        var halves = this.toString().split("\.");
        
        var lh = halves[0].characters().reverse();
        
        try {
            var rh = halves[1].characters();
        } catch(e) { rh = []; }
        
        halves = m.split("\.");
        
        var lm = halves[0].characters().reverse();
        
        try {
            var rm = halves[1].characters();
        } catch(e) { rm = []; }
        
        var ret = "";
    
    
        //For the greater of the two    
        Math.max(lh.length, lm.length).each(function(i) {
            //no more left masks
            if (lm.length == 0) {
                //no more left values
                if (lh.length == 0) {
                    //we're done
                    return;
                }
                else {
                    //stack on the remainder
                    ret = lh.pop() + ret;
                    return;
                }
            }
            else {
                var nd = lh.pop();
                var nm = lm.pop();
            
                //Match A Pound Sign To A Number
                if (nm == "#") {
                    if (nd >= 0 && nd <= 9) {
                        ret = nd + ret;
                    } else {
                        ret = "0" + ret;
                    }
                }
    
                if (nm == "*") {
                    ret = nd + ret;
                    return;
                }
                
                if (comma && lh.length > 0 && ret.match(/^\d{3}/)) {
                    ret = "," + ret;
                }
                
            }
        });
        
        return(ret);
        
    }
    
    String.prototype.format = function(m) {
        //Ensures proper formatting of strings via a format mask (m)
     
        if (this.isNumeric()) {
            //Numbers get special formatting
        }
        else {
            //Strings are formatted left to right
        }
    };
    
    String.prototype.append = function(m) {
        //append string m to this
        return this + m;   
    };
    
    Date.prototype.toString = function(f) {
        //Formats a date via custom format (f)
        //d = day
        //m = month
        //y = year
        //q = quarter
        //h = hour
        //n = minute
        //s = second
        //l = milliseconds
        //a = am/pm
        //u = usual date
        //w = week
        
        if (!f) {
            return(this.getTime());        
        }
        else {
            f = f.replace(/d{3}/gi, this.longDays[this.getDate()]);
            f = f.replace(/d{2}/gi, this.getDate().format("##"));
            f = f.replace(/d{1}/gi, this.getDate().format(""))
        }
    };
    
    Date.prototype.__type = function() {
        return("date");
    };
    
    Date.prototype.isDate = function() {
        return(true);
    };
    
    Date.prototype.DOY = function() {
        //Returns the day of the year
        var onejan = new Date(this.getFullYear(),0,1);
        return Math.ceil((this - onejan) / 86400000);
    };
    
    Date.prototype.WOY = function() {
        //returns the week of year
        var onejan = new Date(this.getFullYear(),0,1);
        return Math.ceil((((this - onejan) / 86400000) + onejan.getDay())/7);
    };
    
    Date.prototype.isLeapyear = function() {
        //returns true if current year is a leap year
        var leap = false;
        if (this.getFullYear() % 4   == 0) leap = true;
        if (this.getFullYear() % 100 == 0 &&
            this.getFullYear() % 400 != 0) leap = false;
    
        return(leap);
    };
    
    Date.prototype.century = function() {
        //returns century of date
        return(Math.floor(this.getFullYear() / 100) + 1);
    }
    
    Date.prototype.decade = function() {
        //returns decade of date
        return(Math.floor(this.getFullYear() / 10) + 1);
    }
    
    Date.prototype.millenia = function() {
        //returns millenia of date
        return(Math.floor(this.getFullYear() / 1000) + 1);
    }
    
/*
////////////////////////////////////////////////////////////////////////////////
    ______                 __  _                ____             __        __                       
   / ____/_  ______  _____/ /_(_)___  ____     / __ \_________  / /_____  / /___  ______  ___  _____
  / /_  / / / / __ \/ ___/ __/ / __ \/ __ \   / /_/ / ___/ __ \/ __/ __ \/ __/ / / / __ \/ _ \/ ___/
 / __/ / /_/ / / / / /__/ /_/ / /_/ / / / /  / ____/ /  / /_/ / /_/ /_/ / /_/ /_/ / /_/ /  __(__  ) 
/_/    \__,_/_/ /_/\___/\__/_/\____/_/ /_/  /_/   /_/   \____/\__/\____/\__/\__, / .___/\___/____/  
                                                                           /____/_/
                                                                           
////////////////////////////////////////////////////////////////////////////////

curry ( arg... )
    wrap
*/

    
    Function.prototype.__type = function __type() {
        return("function");
    }
    
    Function.prototype.isFunction = function() {
        return(true);
    };
    
    Function.prototype.createRef = 
    Function.prototype.getScope = function(scope) {
        return(q.u.createRef(scope, this, q.toArray(arguments).from(1)));
    };
    
    Function.prototype.repeatEvery = function(ms) {
        try {
            this.interval = ms;
            this.intervalId = window.setInterval(this, ms);
        } catch (e) { }
    };
    
    Function.prototype.delay = function(scope, ms) {
        try {
            //backwards compat for non scoped or pre-scoped delay calls
            if (scope != null && scope.isNumeric() && ms == null) {
                ms = scope
                scope = q.gc;
            }
            
            scope = q.def(scope, q.gc);
            ms = q.def(ms, 100);
            target = this;
            target.timeout = ms;
            target.timeoutId = window.setTimeout(function() { target.apply(scope); }, ms);
        } catch (e) {
            //alert("Function.prototype.delay(): " + e.description);
        }
        return this;
    };
    
    var pauses = {};
    
    Function.prototype.onPause = function(dur) {
        //Executes this function after a delay of [dur] only if this
        //function isn't called again.  Useful for keypress events where
        //you want an action to occur after the user pauses typing
        var k = this.toString();
        
        if (pauses.hasKey(k)) {
            window.clearTimeout(pauses[k].timer);
        }
        else {
            pauses[k] = {};
        }
        
        pauses[k].timer = window.setTimeout(this, dur);
    };
    
    Function.prototype.prepare = function() {
        //returns an atomic function reference with predefined parameter values
        var ref = this;
        var a = this.args();
        
        return (function() {
            var b = q.toArray(arguments);
            ref.apply(ref, a.combine(b));
        });
    };
    
    Function.prototype.debug = function(scope, params) {
        
        var ps = q.me().args().from(1);
        
        var tfunc = "";
        top.m__bp = "[first line]";
        var locs = this.toString().split(";");
        
        tfunc = locs.each(function(l, i) {
            if (i.toInt() + 1 < locs.length) {
                return l + ";\ntop.m__bp = " + locs[i.toInt() + 1].trim().escapeJSON().quote().append(';');
            }
            else {
                return "\ntop.m__bp = '[last line]';\n" + l.append(";");
            }
        }).join("");
        
        try {
            alert(tfunc);
            tfunc.toVar().apply(scope, ps);
        } catch (e) {
            alert("Function [" + this.name() + "] died on the following line: \n\t" + top.m__bp.trim() + "\nWith the following error: \n\t" + e.description);
            return;
        }
    
        alert("Function [" + this.name() + "] succeeded");
    };
    
    Function.prototype.name = function() {
        try {
            var name = this.toString().lines()[0];
                name = name.trim().match(/^function (.*?)\(/)[1];
                //TODO: compare speeds of regexes /function (\\w+)/
        } catch(e) {
            return("anonymous");
        }
        return(name);
    }
    
    Function.prototype.params = function() {
        var params = this.toString().lines()[0];
        params = params.trim().replace(/^function.*?\(/, "");
        params = params.match(/(.*?)\)/)[1].trim();
        
        if (!params) {
            params = '[no params]';
        }
        
        return(params);
    };
    
    Function.prototype.notImplemented = function() {
        throw new Error(-100, "NotImplemented");
    };
    
    Function.prototype.template = function() {
        //Retrieves any template comments from JS function declaration
        var t = {};
        
        //get comments of this function ref
        this.comments().reuse(function() {
            //iterate over each comment
            this.each(function(v) {
                //look for named comment block [[comment_name]]
                var tpl = v.match(/.*?\[\[(.*?)\]\]((.|\r|\n)*?)$/g);
                if (tpl && tpl[0]) {
                    var name = tpl[0].trim().match(/\[\[(.*?)\]\]/)[0];
                    var body = tpl[0].replace(name, "").trim();
                    t[name.replace(/\[|\]/g, "")] = body;
                }
            });
        });
        
        return(t);
    };
    
    Function.prototype.comments = function(idx) {
        //Returns an array of comment blocks in [this] function
        
        var desc = [];
        var add = false;
        var ignore_slash = false;
        
        function addDesc(line) {
            if (ignore_slash) {
                desc.push(line.trim().replace(/\/\*|\*\//, ""));
            } else {
                desc.push(line.trim().replace(/\/\/|\/\*|\*\//, ""));
            }
        }
        
        var r = this.toString().lines().each(function(line) {
            
            if (add == false) {
                desc = [];
            }
            
            if (line.trim().match(/\/\//)) {
                addDesc(line);
            }
            
            if (line.trim().match(/\/\*/)) {
                ignore_slash = true;
                add = true; 
            }
            
            if (line.trim().match(/\*\//)) {
                ignore_slash = false;
                add = false;
                addDesc(line);
            }
            
            if (add) {
                addDesc(line);
            }
            else {
                add = false;
                return desc.join("\r\n");
            }
            
        }).clean() || [];
        
        return idx != null ? r[idx] : r;
    };
    
    Function.prototype.explanation = function() {
        //Provides the explanation of a function - the first
        //series of comments supplied inside the function construct
        if (!this || !this.toString) {
            return("Unknown Object Or COM Object Without Symbols");
        }
    
        return this.comments()[0] || "Unknown Purpose";
    };
    
    Function.prototype.stackTrace = function() {
        //Attempts to compile the stack trace of a call stack
        var str = "";
        var re  = new RegExp("^function\\s+(\\w+)|^function\(\)",  "");
        var level = 0;
        var gov = 0;
        for(var c = this.caller; c!=null; c=c.caller) {
            re.exec( c );
            str += level + ": " + RegExp.$1 + "\n" + " args: ".padLeft(level.length() + 1, " ") + q.toArray(c.arguments).join(",") +"\n\n"; 
            level++;
            if (level > 99) {
                break;
            }
        }
        
        return str;
    };
    
    Function.prototype.milliseconds = function() {
        //Returns number of milliseconds it takes to execute the function
        
        //get start time
        var t1 = (new Date()).getTime();
        //run function with any arguments
        this(arguments);
        //get stop time
        var t2 = (new Date()).getTime();
        
        return(t2 - t1);
    };
    
    Function.prototype.extend = function(dstObj) {
        var srcObj = this;
        dstObj.each(function (k) {
            if ( srcObj[k] == undefined ) {
                srcObj[k] = dstObj[k];
            }
        });
        return srcObj;
    };
    
    Function.prototype.override = function() {
        var srcObj = this;
        dstObj.each(function (k) {
            srcObj[k] = dstObj[k];
        });
        return srcObj;
    };
    
    Function.prototype.assertDoesNotFail = function(msg) {
        try {
            this();
        }catch (e) {
            throw new qError(601, "Function: [" + this.name() + "] failed: " + msg);  
        };
    };
    
    Function.prototype.assertFailEquals = function(error_obj, msg) {
        var err = null;
        try {
            this();
        }catch(e) {
            err = e;
        } finally {
            if (!err || !err.equals(error_obj))
                throw new qError(602, "Function: [" + this.name() + "] failed incorrectly: " + msg);
        }
    };
    
    Function.prototype.wait = function(limit) {
        //Waits the specified seconds(limit) until executing the function
        var startTime = q.date.now();
        
        q.u.sleep(limit);
        
        return this();
    };
    
    Function.prototype.tryval = function(report) {
        //Executes a function wrapped in a try catch, conditionally reporting (report) any errors
        try {
            return this();
        } catch(e) {
            if (!e.exit && report) {
                e.debug();
            }
            return false;
        }
    };
    
    Function.prototype.trycatch = function() {
        //Wrap function call with params in a try catch - returns null on error
        try {
            return this.apply(this, arguments); 
        } catch(e) {
            return null; 
        }
    };
    
    Function.prototype.exec = function(args) {
        //Execute self - alternative to fname("test") as fname.exec("test");
        //at this point pure syntactical sugar
        this.apply(this, arguments);           
    };
    
    
    Function.prototype.partial = function() {
        var fn = this, args = me().args();
        return function(){
            var arg = 0;
            for ( var i = 0; i < args.length && arg < arguments.length; i++ ) {
                if ( args[i] === undefined ) {
                    args[i] = arguments[arg++];
                }
            }
            return fn.apply(this, args);
        };
    };
    
    Function.prototype.when = function(cond) {
        if (cond) {
            return this(params, me().args().from(1));    
        }
    };
    
    Function.prototype.unless = function(cond) {
        if (!cond) {
            return this.apply(this, me().caller.args().from(1));
        }
    };
    
    Function.prototype.proto = function(name, parray) {
        //[function].proto("isNotObject", "string", "boolean", "function", "date"); 
        me().args().flatten().from(1).each(function(t) {
            t.CamelCase().toVar().prototype[name] = this;
        });
    };
    
    Function.prototype.args = function() {
        return q.toArray(q.me().caller.arguments);
    };
    
    Function.prototype.publicize = function() {
        //creates a public instance of a scoped function
        (this.name() + " = " + this.toString().replace("function " + this.name() + "(", "function(")).tryval();  
    };
    
    Function.prototype.returns = function() {
        return this();
    };
    
    //references to bound functions 
    var bound_refs = {};
    
    Function.prototype.bind = function(trigger) {
        //binds a function and params to a specific trigger tag
        bound_refs[trigger] = q.def(bound_refs[trigger], []);
        bound_refs[trigger].push([this, this.args()]);
    };
    
    Function.prototype.trigger = function() {
        //normalized interface to executing trigger tags
        this.apply(this, this.args());     
    };
    
    String.prototype.trigger = function(onerror, oncomplete, onfinal) {
        //given a named trigger - execute all functions attached
        //optionally supplied error, item complete and all complete handlers
        bound_refs[this].each(function(ref) {
            try {
                ref[0].apply(ref[0], ref[1]);
                if (oncomplete) oncomplete.trigger();
            } catch(e) {
                onerror.trigger(e);
            }
        });
        
        if (onfinal) onfinal.trigger(); 
    };
   
    Function.prototype.intercept = function(func, scope) {
        //Intercept a call to a provided func reference and call
        //this function beforehand.  If successful call the intercepted function.
        return new function() {
            try {
                func.apply(scope, arguments);
            } catch(e) {
                if (!e.ignore()) {
                    return this.apply(scope, arguments);
                }
            }
            return this.apply(scope, arguments);
        };
    };
    /*
    
    Event
    element ( event )
    extend ( event )
    findElement ( event, tagName )
    fire ( element, eventName[, memo] )
    isLeftClick ( event )
    observe ( element, eventName, handler )
    pointerX ( event )
    pointerY ( event )
    stop ( event )
    stopObserving ( element[, eventName[, handler]] )
    
    Form
    disable ( formElement )
    enable ( formElement )
    findFirstElement ( formElement )
    focusFirstElement ( formElement )
    getElements ( formElement )
    getInputs ( formElement[, type[, name]] )
    request ( [options] )
    reset ( formElement )
    serialize ( formElement[, getHash = false] )
    serializeElements ( elements[, getHash = false] )
    
    Form.Element
    activate ( element )
    clear ( element )
    disable ( element )
    enable ( element )
    focus ( element )
    getValue ( element )
    present ( element )
    select ( element )
    serialize ( element )
    setValue ( element, value )
    Element (constructor)
    absolutize ( element )
    addClassName ( element, className )
    addMethods ( [methods] )
    adjacent ( element[, selectors... ] )
    ancestors ( element )
    childElements ( element )
    classNames
    cleanWhitespace ( element )
    clonePosition ( element, source[, options] )
    cumulativeOffset ( element )
    cumulativeScrollOffset ( element )
    descendantOf ( element, ancestor )
    descendants ( element )
    down ( element[, cssRule ][, index = 0] )
    empty ( element )
    extend ( element )
    fire ( eventName[, memo ] )
    firstDescendant ( element )
    getDimensions ( element )
    getElementsByClassName
    getElementsBySelector
    getHeight ( element )
    getOffsetParent ( element )
    getStyle ( element, property )
    getWidth ( element )
    hasClassName ( element, className )
    hide ( element )
    identify ( element )
    immediateDescendants
    insert ( element, {position: content} )
    makeClipping ( element )
    makePositioned ( element )
    match ( element, selector )
    next ( element[, cssRule][, index = 0] )
    nextSiblings ( element )
    observe ( element, eventName, handler )
    positionedOffset ( element )
    previous ( element[, cssRule][, index = 0] )
    previousSiblings ( element )
    readAttribute ( element, attribute )
    recursivelyCollect ( element, property )
    relativize ( element )
    remove ( element )
    removeClassName ( element, className )
    replace ( element[, html] )
    scrollTo ( element )
    select ( element, selector... )
    setOpacity ( element, opacity )
    setStyle ( element, styles )
    show ( element )
    siblings ( element )
    stopObserving ( element[, eventName[, handler]] )
    toggle ( element )
    toggleClassName ( element, className )
    undoClipping ( element )
    undoPositioned ( element )
    up ( element[, cssRule][, index = 0] )
    update ( element[, newContent] )
    viewportOffset ( element )
    visible ( element )
    wrap ( element, wrapper[, attributes] )
    writeAttribute ( element, attribute[, value = true] )
    
    Ajax
    new Ajax.PeriodicalUpdater ( container, url[, options] )
    new Ajax.Request ( url[, options] )
    new Ajax.Updater ( container, url[, options] )
    
    document
    fire ( eventName[, memo] )
    observe ( eventName, handler )
    stopObserving ( eventName[, handler] )
    
    
    TimedObserver
    new Form.Element.Observer ( element, freq, callback )
    new Form.Observer ( element, freq, callback )
    
    
    Position
    absolutize
    clone
    cumulativeOffset
    offsetParent
    overlap
    page
    positionedOffset
    prepare
    realOffset
    relativize
    within
    withinIncludingScrolloffsets
    
    Insertion
    After
    Before
    Bottom
    Top
    
    
    Utility Methods
    $ ( id | element )
    $$ ( cssRule )
    $A ( iterable )
    $F ( element )
    $H ( [obj] )
    $R ( start, end[, exclusive = false] )
    $w ( String )
    Try.these ( Function... )
    document.getElementsByClassName
    
    
    Class
    addMethods ( methods )
    create ( [superclass][, methods... ])
    
    ObjectRange (constructor)
    
    Template (constructor)
    evaluate ( object )
    
    PeriodicalExecuter (constructor)
    stop ( event )
    
    Ajax.Responders
    register ( responder )
    unregister ( responder )
    
    XPath
    
    Offsets/Dimensions
    fd
    Element
    viewportOffset.left
    cumulativeOffset.top
    */
    
    
    //Error.prototype.functions
    function qError(number, description){
        this.trace = this.stackTrace();
        this.number = number || null;
        this.description = description || null;
        this.source = "Que";
    }
    
    Error.prototype.isError = function() {
        return(true);
    };
    
    Error.prototype.equals = function(err) {
        if (err.isNumber()) return(this.number == err);   
        if (err.isError()) return(this.number == err.number);
    };
    
    Error.prototype.info =
    Error.prototype.toString = function Error_prototype_toString(info) {
        //Returns a good string representation of an error object
        info = q.def(info, "NO DETAIL PROVIDED");
        return("Error: #" + this.number + " - " + this.description + "\nDetail: [" + info + "]" + "\nStack Trace:\n------------------------------\n" + this.stackTrace());
    };
    
    Error.prototype.ignore = function Error_prototype_ignore() {
        //returns true for known "non-errors" used for workflow purposes
        if (this.description == "break" || this.exit == true) {
            return true;
        }
    };
    
    Error.prototype.debug = function Error_prototype_debug() {
        
        if (q.lib.confirm({ msg : ("An error has occurred:\n\n" + this + "\nWould you like to debug?").toHtml(),
                        title : "Caught Exception..."
        })) {
           //if the user clicks yes - hit debug statement
           debugger;
           return true;
        }
        else {
            return false;
        }
    };
    
    Error.prototype.handle = function() {
        //attempts to handle all errors
        //will return undefined if nothing further to do
        //will return a value if the error was intended to return a value (each iterators)
        //will rethrow the error if it was intended to be reported via the .report attribute
        if (this.ignore()) {
            return this.retval;
        }
        
        if (this.debug()) return;
        
        if (this.report) {
            throw this;
        }
    };

})();
(function() {
    
    q.f = new function FileSystem() {
        
        var self = this;
        
        //private properties
        
        //use mock fso object instead
        var fso = null;
        
        try {
            fso = new ActiveXObject("Scripting.FileSystemObject");
        } catch(e) {
            //limited usage of this system      
        }
        
        self.access = function(str) {
            //Provides a list of file access modes
            var accs = {
                read     : 1,
                readonly : 1,
                write    : 2,
                append   : 8
            };
            
            //allow access by string value    
            if (str) {
                return accs[str]; 
            }
            
            return accs;
        };
       
        self.formats = function(str) {
            //Provides a list of file format types
            var fmts = {
                system  : -2,
                unicode : -1,
                ascii   : 0
            };
            
            if (str) {
                return fmts[str];  
            }
            
            return(fmts);
        };
    
        //file object - returned by FileSystem functions    
        function file(path) {
            
            this.__type = function() {
                return "file";     
            };
            
            this.rename = 
            this.move = function(dest) {
                //Moves this file to a new destination [dest]
                //dest can be a new file name, a new file path or a new directory path in which case the existing file name is reused
                if (path.isUrl() && dest.isUrl()) {
                    path = path.replace(/^https?:\/\//i, "").replace(/[\\\/]/g, '.');
                    dest = dest.replace(/^https?:\/\//i, "").replace(/[\\\/]/g, '.');
                    q.http.get("http://" + q.server_addr + "/rest.php?method=move&store=" + path.encode() +
                               "&destination=" + dest.encode());
                    
                    return this;
                }
                else {
                    var folder = dest;
                    var fname = path.fileName();
                    
                    //if dest is a file then we're renaming the file in the move
                    if (dest.isFile()) {
                        //if dest is only a filename then use the current file's directory path
                        folder = dest.filePath() || path.filePath();
                        fname = dest.fileName();
                    }
                
                    //clean path parts  
                    folder = folder.reduce("\\").removeTrailing("\\");
                    fname = fname.reduce("\\");
                    path = path.reduce("\\");
                    dest = folder + "\\" + fname;
                    
                    //verify path
                    self.verify(folder);
                    
                    //delete target to avoid fso error
                    //TODO: consider being more cautious 
                    dest.erase();
                    
                    //move file
                    fso.moveFile(path, folder + "\\" + fname);
                    
                    //update path
                    path = folder + "\\" + fname;
                }
                
                return this;
            };
            
            this.copy = function(dest) {
                //Copies this file to a specified destination [dest] 
                if (dest.isDirectory()) {
                    dest = dest + "\\" + path.fileName();
                }
               
                //prevent fso error
                //TODO: consider being more cautious here
                dest.erase();
                
                //copy file to destination
                fso.copyFile(path, dest);
                
                //update new path 
                path = dest;
                
                return this;
            };
            
            this.erase =
            this.del = function() {
                fso.deleteFile(path);
                path = null;
            };
           
            this.filePath =
            this.directory = function() {
                //Returns the path of this file up to the last folder or drive
                // c:\test\file.txt => c:\test\
                return path.parentFolder();
            };
           
            this.parentFolders = function() {
                //returns an array of all folders in this file's path
                return path.parentFolders();
            };
            
            this.parentFolder = function() {
                //returns a folder object referencing this files parent folder
                return new folder(this.filePath());
            };
            
            this.name = function () {
                //returns the name of this file without any path information
                return path.fileName();
            };
            
            this.path = 
            this.toString = function() {
                return path;
            };
            
            this.absolutePath = function() {
                try {
                    return fso.GetAbsolutePathName(path);
                } catch(e) {};
                return path;
            };
            
            this.format = function() {
                //Determines the encoded format of the specified file (f)
                var fmt = self.formats("ascii");
                
                try {
                    //Open file as ascii first
                    var h = fso.GetFile(path);
                    var ts = h.OpenAsTextStream(self.access("read"), self.formats("ascii"));
    
                    //If file's first character code is 255 - likely to be unicode
                    //best we can determine in js land with fso
                    if (!ts.atEndOfStream) {
                        var ln = ts.ReadLine();
                        if (ln && ln.charCodeAt(0) == 255) {
                            fmt = self.formats("unicode");
                        } 
                    }
                    
                    ts.Close();
                } catch(e) { }
                
                return(fmt);
            };
            
            this.read = 
            this.open = function(access) {
                //Read the content of this file and return as a string
                var data = "";
                
                if (path.isUrl()) {
                    path = path.replace(/^https?:\/\//i, "");
                    path = path.replace(/[\\\/]/g, '.');
                    var url = "http://" + q.server_addr + "/rest.php?method=open&store=" + path.encode() + "&access=" + (access || "read");
                    return q.http.get(url);
                }
                
                try {
                    var stream = this.openStream(path);
                    if (!stream.atEndOfStream) {
                        data = stream.ReadAll();
                    }
                    stream.Close();
                } catch (e) { }
                
                return(data);
            };
        
            this.openStream = function() {
                //returns a file stream based on the existing file format
                return fso.GetFile(path).OpenAsTextStream(self.access("read"), this.format());
            };
            
            this.fop = function(path, txt, fmt, mode) {
                //file operation - place txt in this file in a given format (fmt) via an access mode (mode)
                
                if (path.isUrl()) {
                    //URL resources are all saved to qserver - potentially any server in the future, but for now qserver
                    path = path.replace(/^https?:\/\//i, "");
                    path = path.replace(/[\\\/]/g, '.');
                    txt = "content=" + txt.encode();
                    var url = "http://" + q.server_addr + "/rest.php?method=save&store=" + path.encode();
                    return q.http.post(url, txt);
                }
                
                fmt = q.def(fmt, this.format(), self.formats("unicode"));
                mode = q.def(mode, "write");
                txt = q.def(txt, "");
                
                //ensure file path exists
                self.verify(path.filePath());
                
                if (typeof txt == "unknown") {
                    var adTypeBinary = 1;
                    var adSaveCreateOverwrite = 2;
                    var stream = new ActiveXObject("ADODB.Stream");
                        stream.Type = adTypeBinary;
                        stream.Open();
                        stream.Write(txt);
                        stream.SaveToFile(path, adSaveCreateOverwrite);
                }
                else {
                    try {
                        var h = fso.OpenTextFile(path, self.access(mode), true, fmt);
                            h.Write(txt);
                            h.Close();
                    } catch(e) {
                        q.report("Error Saving File in FS.js: ", e);     
                    }
                }
                
                return this;
            };
            
            this.save = function(txt, fmt) {
                //Overwrites this file with the given text (txt) in an optional specified format (fmt)
                //fmt defaults to unicode
                return this.fop(path, txt, fmt, "write");
            };
            
            this.append = function(txt, fmt) {
                //Appends this file with the given text (txt) in an optional specified format (fmt)
                //fmt defaults to unicode
                return this.fop(path, txt, fmt, "append"); 
            };
            
            if (!path) {
                path = self.temp();
            }
        }
        
        function folder(path) {
            
            this.__type = function() {
                return "folder";     
            };
            
            this.path = function() {
                return path.rtrim("\\") + "\\";     
            };
            
            this.move = function(dest) {
                fso.moveFolder(path, dest);
                return this;
            };
            
            this.copy = function(dest) {
                fso.copyFolder(path, dest); 
                return this;
            };
            
            this.erase =
            this.del = function() {
                fso.deleteFolder(path.rtrim("\\"));
                return this;
            };
            
            this.toString = function() {
                return path;   
            };
            
            this.dir = function(filter, recurse, limit) {
                return q.f.dir(path, filter, recurse, limit);   
            };
            
            this.sub = function(subpath, recurse) {
                //returns a sub path or file
                var p = path + "\\" + subpath;
                if (p.isDirectory()) {
                    return new folder(p);
                }
                return null;
            };
        }

        self.temp = function(path, ext) {
            ext = q.def(ext, "tmp");
            path = q.def(path, "c:\\temp\\");
            return path + "\\" + q.u.guid().toFile() + "." + ext;
        };
        
        self.save = function(f, txt, fmt) {
            return (new file(f)).save(txt, fmt);     
        };
        
        self.append = function(f, txt, fmt) {
            return (new file(f)).append(txt, fmt); 
        };
        
        String.prototype.save = function(f, fmt) {
            return q.f.save(f, this, fmt);
        };
        
        self.open = function(f, access) {
            if (f.isFile() || f.isUrl()) {
                return (new file(f)).open(access);
            }
            else {
                q.report("Invalid file path: " + f);
            }
        };
        
        String.prototype.open = function(access) {
            return q.f.open(this, access); 
        };
        
        String.prototype.exists = 
        String.prototype.fileExists = function() {
            //returns true if the filepath exists
            try {
                fso.GetFile(this);
                return true;
            } catch(e) {
                try {
                    fso.GetFolder(this);
                    return true;
                } catch (e) { }
                return false;
            }            
        };
        
        String.prototype.isFile = function() {
            //Returns true if this string is formatted like a file path
            //purposefully naive - mostly just a sanity check
            if (this.match(/\?|\*|\"|\>|\<|\|/g)) return(false);
            
            if (this.match(/\||\r|\n/g)) return(false);
        
            if (this.length > 200) return(false);
            
            return(true);
        };
        
        String.prototype.parentFolder = 
        String.prototype.filePath = function() {
            //Returns the Path portion of a string containing a file uri
            return(this.substring(0, Math.max(this.lastIndexOf("\\"), this.lastIndexOf("/")) + 1));
        };
        
        String.prototype.parentFolders = function(f) {
            //returns an array of strings for each folder in a file path
            var parts = this.parentFolder().split(/[\\|\/]/g).clean();
            
            if (this.startsWith("\\\\")) {
                return ["\\\\" + parts[0] + "\\" + parts[1]].combine(parts.from(3));
            }
            else {
                return parts;
            }
        };
        
        String.prototype.fileName = function() {
            //Returns the Name portion of a string containing a file uri
            return(this.substring(Math.max(this.lastIndexOf("\\"), this.lastIndexOf("/")) + 1));
        };
        
        String.prototype.withoutExt = function() {
            //retuns the a file name without the extension
            if (this.contains(".")) {
                if (this.length - this.lastIndexOf(".") <= 5) {
                    //typically a file extension
                    return(this.slice(0, this.lastIndexOf(".")));
                } else {
                    //remove random periods from file name
                    return(this.replace(/\./i, "_"));
                }
            }
            
            return(this);
        };
        
        String.prototype.isDirectory = function() {
            //returns whether a filepath is a directory
            return(fso.folderExists(this));
        };
        
        self.verify = function(p, doNotCreate) {
            //Finds a file folder at a pecified path (p)
            //or creates the folder unless doNotCreate is true
            var fld = null;
            
            try {
                fld = fso.GetFolder(p);
            } catch(e) {
                if (!doNotCreate) {
                    debugger;
                    try {
                        //Create the directory path folder by folder
                        var cpath = "";
                        (p + "\\dummy.file").parentFolders().each(function(d) {
                            cpath += d + "\\";
                            if (!cpath.exists()) {
                                fld = fso.createFolder(cpath);
                            }
                        });
                    } catch(e) {
                        debugger;
                        alert(e);
                    }
                }
            }
            return(fld);
        };
        
        self.erase =
        self.del = function(f) {
            //deletes a file from the file system
            //can also remove a file from qserver if url path is provided
            if (f.isUrl()) {
                f = f.replace(/^https?:\/\//i, "");
                f = f.replace(/[\\\/]/g, '.');
                return q.http.get("http://" + q.server_addr + "/rest.php?method=delete&store=" + f.encode());
            }
            
            //delete file [f] from system
            if (f.isFile() && f.exists()) {
                try {
                    fso.DeleteFile(f);
                } catch(e) {
                    return q.report(e); 
                }
            }
            
            if (f.isDirectory()) {
                try {
                    fso.deleteFolder(f.removeTrailing("\\"), true);
                } catch(e) {
                    return q.report(e);
                }
            }            
        };
        
        //a shame delete is a reserved word
        String.prototype.erase =
        String.prototype.del = function() {
            //delete [this] string if it's a filepath
            //will accept wildcards in last component of path
            self.del(this);
        };
        
        self.move = function(f, dest) {
            //Move file f to destination
            return new file(f).move(dest);
        };
        
        String.prototype.move = function(dest) {
            //Move (this) file to destination path
            self.move(this, dest);    
        };
        
        self.copy = function(f, dest) {
            return new file(f).copy(dest);     
        };
        
        //convert to find
        self.getFile = function(fpath) {
            return new file(fpath);
        };
       
        //convert to find
        self.getFolder = function(fpath) {
            return new folder(fpath);   
        };
    
        //convert to find
        self.specialFolders = function(i) {
            var obj = new ActiveXObject("WScript.shell");
            if (i) return(obj.SpecialFolders(i));   
            return(q.toArray(obj.SpecialFolders));   
        };
        
        //convert to find
        self.desktop = function() {
            //return desktop folder
            return self.getFolder(self.specialFolders().filter(function(i) { if (i.contains("\\Desktop")) return i; })[1]);
        };

        self.drives = function() {
            //returns the list of drives available on the system
            return(q.toArray(fso.drives));   
        };
    
        self.recurse = function(path) {
            //returns a list of all sub directories
            var stack = [];
            var folders = [];
            var limit = 1000;
            
            if (typeof(path) == "string") {
                path = fso.getFolder(path);
            }
            
            while(limit--) {
                if (path != null) {
                    q.enumerate(path.subFolders).each(function(f) {
                        stack.push(f);
                    });
                }
                else {
                    if (stack.length == 0) break;
                }
                
                folders.push(path);
                path = stack.pop();
            }
            
            return(folders);
        };
        
        self.find =
        self.dir = function(path, filter, recurse, limit) {
            //resturns a list of files from a sub directory
            
            if (path.isUrl()) {
                return self.netdir(path, filter, limit);     
            }
            
            filter = q.def(filter, "*")
            
            if (filter.__type() == "string") {
                //support for "*.txt" dir searches
                filter = filter.replace("*", ".*?");
            }
            
            if (typeof path == "string") {
                path = fso.getFolder(path);
            }
            
            //Array of directories to search 
            var dirs = recurse ? self.recurse(path) : [path];
          
            var res = [];
            
            dirs.each(function(d) {
                q.toArray(d.files).each(function(f) {
                    //return matching files
                    if (!filter || (f.path).match(filter)) res.push(self.toHash(f));
                    
                    //the governator
                    if (limit && res.length >= limit) q.u.exit();
                });
                if (limit && res.length >= limit) q.u.exit();
            });
            
            return res;
        };
        
        self.netdir = function(path, filter, limit) {
            //Returns files found on qserver
            path = path.replace(/^https?:\/\//i, "");
            path = path.replace(/[\\\/]/g, '.');
            filter = filter || "";
            limit = limit || 100;
            
            return q.http.get("http://" + q.server_addr + "/rest.php?method=find&store=" + path.encode() +
                              "&limit=" + limit.encode() +
                              "&filter[]=" + filter.join("&filter[]=").encode());
        };
        
        self.toHash = function(o) {
            //converts a file object to a hash object
            try {
                return {
                    "name" : o.Name,
                    "path" : o.Path,
                    "size" : o.Size,
                    "type" : o.Type
                };
            } catch (e) {
                //q.report(e);
            }
        };
    
        
        return self;     
    };
    
})();
(function() {
    
    q.fs = new file();
    
    function file() {
        //Depracated - use q.f instead
        //File System Class - used to access and manipulate files & drives
        var self = this;
        var fso = null;
        try {
            fso = new ActiveXObject("Scripting.FileSystemObject");
        } catch (e) {
            
        }
        
        self.recurse = function(path) {
            //returns a list of all sub directories
            var stack = [];
            var folders = [];
            var limit = 1000;
            
            if (typeof(path) == "string") {
                path = fso.getFolder(path);
            }
            
            while(limit--) {
                if (path != null) {
                    q.enumerate(path.subFolders).each(function(f) {
                        stack.push(f);
                    });
                }
                else {
                    if (stack.length == 0) break;
                }
                
                folders.push(path.path);
                path = stack.pop();
            }
            
            return(folders);
        };
        
        self.dir = function(path, filter, recurse, limit) {
            //resturns a list of files from a sub directory
            var res = [];
        
            filter = q.def(filter, "*")
            
            if (filter.__type() == "string") {
                //support for "*.txt" dir searches
                filter = filter.replace("*", ".*?");
            }
            
            if (typeof path == "string") {
                path = fso.getFolder(path);
            }
            
            //Array of directories to search 
            var dirs = [];
            
            if (recurse) {
                dirs = dirs.combine(self.recurse(path));
            } else {
                dirs = [path];
            }
            
            dirs.each(function(d) {
                
                if (typeof d == "string") {
                    d = fso.getFolder(d);
                }
                
                q.u.enumerate(d.files).combine(q.u.enumerate(d.SubFolders)).each(function(f) {
                    if (!filter || (f.path).match(filter)) {
                        res.push(self.toHash(f));
                    }
                    if (limit != null && res.length >= limit) {
                        q.u.exit(); 
                    }
                });
            });
            
            return(res);
        };
        
        self.toHash = function(o) {
            //converts a file object to a hash object
            try {
                return {
                    "dateCreated" : o.DateCreated,
                    "dateLastAccessed" : o.DateLastAccessed,
                    "dateLastModified" : o.DateLastModified,
                    "drive" : o.Drive,
                    "name" : o.Name,
                    "parentFolder" : o.ParentFolder,
                    "path" : o.Path,
                    "shortName" : o.ShortName,
                    "shortPath" : o.ShortPath,
                    "size" : o.Size,
                    "type" : o.Type
                };
            }
            catch (e) {
                return { error : e };
            }
        };
        
        self.unzip = function(fpath, outdir) {
            try {
                //extract zip to temporary directory
                var tmpdir = "C:\\temp\\" + q.u.guid();
                q.fs.folder(tmpdir);
                q.shell.tail('unzip -o "' + fpath + '" -d ' + tmpdir);
                
                //ensure outdir exists
                q.fs.folder(outdir);
                
                //move contents to outdir
                q.fs.dir(tmpdir + "\\", ".", true).ea(function(f) {
                    //remove file if it exists
                    try { q.f.getFile(outdir + "\\" + f.name).del(); } catch(e) {};
                    //copy to output
                    q.f.getFile(f.path).move(outdir + "\\");
                });
                
                //cleanup garbage
                q.f.getFolder(tmpdir).del();
            } catch(e) {
                alert("Error unzipping file: " + e.description);
            }
        };
        
        self.zip = function(indir, outzip, options) {
            //Zips up all files in [indir] into a zip file [outzip]
            //options include -m (move files)
            //                -j (flatten directories)
            options = q.u.def(options, '');
            
            try {
                var str = 'zip -j -r ' + options + ' '  + outzip.dblquote() + ' ' + indir.dblquote();
                q.shell.tail(str);
            } catch(e) {
                alert("Error zipping directory: " + indir + " : " + e.description);
            }
        };
    
        self.open = q.f.open;
        self.folder = q.f.verify;
        self.deleteFile = q.f.del;
        self.save = q.f.save;
        
        return(this);
    }
})();
function CreateObject(str) {
    return new ActiveXObject(str);    
};

q.u = new Util();

q.cls = function() {
    q.quelog.val('');
};

function Util() {

    var self = this;

    try {
        self.wmi = GetObject("winmgmts:\\\\.\\root\\cimv2");
    } catch(e) {
        self.wmi = e.description;
    }

    self.rand = function(min, max) {
        //Produces a random number between the range (min) and (max)
        return(Math.floor(Math.random()*(max - 1)) + min);
    };
    self.random = self.rand;

    self.init = function(obj, val) {
        //Like def, but will only work on undefined (yet to be set) variables.
        //Will not work on Nulls, Empty Strings or 0's
        
        if (typeof val == "undefined") {
            alert("BUGHUNT: val is undefined error as seen in outlook clean inbox?");
            debugger;
        }
        
        if (typeof(obj) == "undefined") {
            try {
                eval("\n obj = " + val + ";\n");
            } catch(e) {
                obj = val;
            }
        }
    };

    self.parseParams = function(pstr) {
        //Parses app parameters allowing for grouped params via the ' or " characters
        //TODO: Create a string_match function
        //BUG: as it stands, the system will match either " or ' for param boundaries meaning
        //     run.exe param1 param2 "params' bug"
        //     will produce:
        //     ['param1', 'param2', 'params', 'bug'];
        var params = [];
        
        var nextparam = "";
        var group_boundary = "'\"";
        var group = false;
        
        pstr.characters().each(function(c) {
            if (!group) {
                if (c != '.' && group_boundary.match(c)) {
                    group = true;
                } else {
                    if (c != " ") {
                        nextparam += c;
                    } else {
                        if (nextparam != '') {
                            params.push(nextparam);
                            nextparam = '';
                        }
                    }
                }
            } else if (group) {
                if (c == '.' || !group_boundary.match(c)) {
                    nextparam += c;
                } else {
                    group = false;
                    params.push(nextparam);
                    nextparam = '';
                }
            }
        });
        
        if (nextparam != '') {
            params.push(nextparam);
        }
        
        return params;
    };
    
    self.CLIParams = function() {
        //TODO: rework this function to q.args
        //      get application name and eval it to obtain htaRef instead of relying on parameter
        var params = [];

        try {
            params = q.toArray(WScript.Arguments);
            params.unshift(WScript.ScriptFullName); //normalize params to include script name as first param
        } catch(e) {
            try {
                var cli = self.htaRef().commandLine;
                var path = location.href.toFile().backSlashes();
                var params = self.parseParams(cli.replace(path.dblquote(), "").trim());
                    params.unshift(path);
            } catch(e) {}
        }

        return(params);
    };

    self.htaRef = function() {
        //returns the HTA Application object as defined in the html source
        //Microsoft does not provide a generic reference or function to obtain the application object
        //nor can you access the hta:application element via the DOM to access the ID attribute
        try {
            var src = q.ie.document.getElementsByTagName("HTML")[0].outerHTML;
            if (src.contains("hta:application")) {
                var id = src.split(/hta:application/i)[1].split(/\>/)[0].match(/id="?(.*?)["|\s]/i)[1];
                return id.toVar();
            }
        } catch (e) { }

        return null;
    };

    self.def = function(obj, val) {
        //Defaults a variable (obj) to value (val) if it is not yet defined
        //e.g. var y = def(y, 100);
        //(val) can be a javascript code and will be interpreted as such
        //e.g. var y = def(y, "(total_users - 1)");
        if (typeof val == "undefined") {
            alert("BUGHUNT: val is undefined error as seen in outlook clean inbox?");
            debugger;
        }
        
        if (typeof obj == "undefined") {
            try {
                eval("\n obj = " + val + ";\n");
            } catch(e) {
                obj = val;
            }
        }
        return(obj);
    };

    self.guid = function() {
        //Returns a globally unique identifier
        try {
            var guidGen = new ActiveXObject("Scriptlet.Typelib");
            return(guidGen.guid.replace(/\{|\}|\n|\r/gi, "").substring(0, 36));
        } catch(e) {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 
                var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); 
                return v.toString(16); 
            }).toUpperCase(); 
        }
    };

    self.wait = function(evalstr, safety) {
        //Waits for a change in the javascript namespace (evalstr) to evaluate to true
        //or until a max wait period (safety) is reached
        safety = self.def(safety, 10);

        while (safety) {
          try {
            eval("var x = " + evalstr);
            if (x) {
              return(true);
            }
          } catch(e) {
          }
          safety--;
          self.sleep(1000);
        }

        return(false);
    };

    self.sleep = function(ms) {
        //suspends execution of scripts for supplied duration (ms)
        //does not allow other events to occur while suspended
        try { 
            WScript.Sleep(ms);
        } catch(e) {
            q.api.call("sleep", ms);
        }
    };

    self.syskey = {
      KEY_BACKSPACE: 8,
      KEY_TAB:       9,
      KEY_RETURN:   13,
      KEY_ESC:      27,
      KEY_LEFT:     37,
      KEY_UP:       38,
      KEY_RIGHT:    39,
      KEY_DOWN:     40,
      KEY_DELETE:   46,
      KEY_HOME:     36,
      KEY_END:      35,
      KEY_PAGEUP:   33,
      KEY_PAGEDOWN: 34,
      KEY_INSERT:   45
    };

    self.keyCodes = {
      "backspaceKey": "8",
      "tabKey": "9",
      "enterKey": "13",
      "returnKey": "13",
      "shiftKey": "16",
      "ctrlKey": "17",
      "altKey": "18",
      "pauseBreakKey": "19",
      "capsLockKey": "20",
      "escapeKey": "27",
      "spacebarKey": "32",
      "spaceKey": "32",
      "pageUpKey": "33",
      "pageDownKey": "34",
      "endKey": "35",
      "homeKey": "36",
      "leftArrowKey": "37",
      "upArrowKey": "38",
      "rightArrowKey": "39",
      "downArrowKey": "40",
      "insertKey": "45",
      "deleteKey": "46",
      "zeroKey": "48",
      "oneKey": "49",
      "twoKey": "50",
      "threeKey": "51",
      "fourKey": "52",
      "fiveKey": "53",
      "sixKey": "54",
      "sevenKey": "55",
      "eightKey": "56",
      "nineKey": "57",
      "aKey": "65",
      "bKey": "66",
      "cKey": "67",
      "dKey": "68",
      "eKey": "69",
      "fKey": "70",
      "gKey": "71",
      "hKey": "72",
      "iKey": "73",
      "jKey": "74",
      "kKey": "75",
      "lKey": "76",
      "mKey": "77",
      "nKey": "78",
      "oKey": "79",
      "pKey": "80",
      "qKey": "81",
      "rKey": "82",
      "sKey": "83",
      "tKey": "84",
      "uKey": "85",
      "vKey": "86",
      "wKey": "87",
      "xKey": "88",
      "yKey": "89",
      "zKey": "90",
      "leftWindowKeyKey": "91",
      "rightWindowKeyKey": "92",
      "selectKeyKey": "93",
      "numpad0Key": "96",
      "numpad1Key": "97",
      "numpad2Key": "98",
      "numpad3Key": "99",
      "numpad4Key": "100",
      "numpad5Key": "101",
      "numpad6Key": "102",
      "numpad7Key": "103",
      "numpad8Key": "104",
      "numpad9Key": "105",
      "multiplyKey": "106",
      "addKey": "107",
      "subtractKey": "109",
      "decimalPointKey": "110",
      "divideKey": "111",
      "f1Key": "112",
      "f2Key": "113",
      "f3Key": "114",
      "f4Key": "115",
      "f5Key": "116",
      "f6Key": "117",
      "f7Key": "118",
      "f8Key": "119",
      "f9Key": "120",
      "f10Key": "121",
      "f11Key": "122",
      "f12Key": "123",
      "numLockKey": "144",
      "scrollLockKey": "145",
      "semicolonKey": "186",
      "equalSignKey": "187",
      "commaKey": "188",
      "dashKey": "189",
      "periodKey": "190",
      "forwardSlashKey": "191",
      "graveAccentKey": "192",
      "openBracketKey": "219",
      "backSlashKey": "220",
      "closeBraketKey": "221",
      "singleQuoteKey": "222"
    };

    self.inspect = function(o) {
        var x = open("about:blank", "inspect");
            x.document.open();
            x.document.write(q.u.dump(o));
            x.document.close();
            x = null;    
    };
    
    self.copyOf = function(obj) {
        return self.dump(obj).toVar();     
    };
    
    self.dump = function(data, maxdepth, offset, depth) {

        offset = self.def(offset, "");
        depth = self.def(depth, 0);
        maxdepth = self.def(maxdepth, -1);

        if (depth > maxdepth && maxdepth != -1) {
          return("");
        }

        var nextoff = offset + "  ";
        var nextdepth = depth + 1;
        var array = [];

        switch ((data != null && data.__type != null) ? data.__type() : "object") {
            case "string":
                return('"'+ data.escapeJSON() +'"');
                break;
            case "number":
                return(data);
                break;
            case "boolean":
                return data ? "true" : "false";
                break;
            case "notundefined":
                return("null");
                break;
            case "function":
                return(data.toString());
                break;
            case "hash":
                return(data.dump());
                break;
            case "object":
                if (data == null ) {
                    return("null");
                }

                data.each(function(v, k) {
                    var val = null;
                    try {
                        val = self.dump(v, maxdepth, nextoff, nextdepth);
                    }catch (e) {
                        val = "null";
                    }

                    if (val != null) {
                        k = '"' + k.toString().escapeJSON() + '"';
                        array.push(k + ": " + val);
                    }                    
                });

                if ( array.length == 1 && !array[0].match(/[\n\{\[]/) ) {
                    return "{ " + array[0] + " }";
                }

                return "{\r\n" + nextoff + array.join( ",\r\n" + nextoff ) + "\r\n" + offset + "}";

                break;

            case "array":
                data.each(function(v, k) {
                    array.push(self.dump(v, maxdepth, nextoff, nextdepth));
                });

                return "[\r\n" + nextoff + array.join(",\r\n" + nextoff) + "\r\n" + offset + "]";
                break;

            default:
              // unsupported data type
              break;
        }

        return "";
    };

    self.createRef = function(scope, method) {
        //create reference to a function remembering the arguments passed into the created reference
        return function() {  method.apply(scope, q.toArray(arguments).from(2)); };
    };

    self.enumerate = function(comlist) {
        //Converts a COM list into a JavaScript Array
        var e = new Enumerator(comlist);
        var arry = [];

        for (; !e.atEnd(); e.moveNext()) {
            arry.push(e.item());
        }
        return(arry);
    };

    self.exit = function(retval) {
        //used to break an each loop or just quit the execution of a script
        var e = new Error(-1000, "break");
            e.exit = true;
            e.retval = retval;
        throw e;
    };

    self.testConditions = function(cond) {
        //returns a constructed function containing
        //evaluations for all conditions supplied
        try {
            if (typeof cond == "string" || cond.__type() == "array") {
                var efunc = "function() { return (%conditions%) ? true : false; };";
                var conds = [];
                
                q.me().args().each(function(a) {
                    conds.push(a);
                });
                
                efunc = efunc.render( { conditions : conds.join(" && ") }, "%");
                
                return efunc.toVar();
            }
        } catch(e) {
            e.handle();
        }
        
        return;
    };
    
    self.each = function(array_or_object, function_reference, zero_or_more_params_for_function_ref) {
        //each calls a function reference with the following params:
        //    (value, key, counter, result_array, [args...])
        //on all explicit items in an object array or string.
        //function_reference can also be an array of string comparison filters designed to test the "this" object
        var args = q.me().caller.args();
        
        var data = args[0];
        var func = args[1];
        var params = args.from(3); //extra params for loop function
    
        var i = 0; //counter for loop
        var retary = [];
        
        var filter = self.testConditions(func); //check for stringified test conditions
        
        try {
            for (var x in data) {
                if (!data.hasOwnProperty || data.hasOwnProperty(x)) {
                    
                    var res;
                    
                    if (filter) {
                        if (filter.call(data[x])) {
                            res = data[x];
                        }
                    } else {
                        res = func.call(data, data[x], x, i++, retary, params);
                    }
                    
                    if (res != undefined) {
                        retary.push(res);
                    }
                }
            }
        } catch(e) {
            //handle the variety of error types 
            e = e.handle();
            
            if (e != undefined) {
                //returned value means exit was called with a specific value
                return e;                
            }
        }
        
        return (retary);
    };

    self.notify = function(msg) {
        if (msg) {
            alert(msg.dump());
        }
    };

    self.inform = function(msg, obj) {
        //Publish a notice that can be read asynchronously to the execution process
        
        if (!obj) { obj = ''; }
        
        msg = "<h2>" + msg + "</h2><pre>" + self.dump(obj) + "</pre>";
       
        var w = q.ie.open("about:blank", q.date.now().toString(), "status=no,toolbar=no,location=no,menubar=no,height=300,width=400,resizable=yes");
            w.document.open();
            w.document.write(msg);
            w.document.close();
    };
    
    self.change_hist = {};
    self.hasChanged = function(var_str, bReset) {
        //Determines if a variable has changed since we last inspected it
        eval("var var_val = " + var_str + ".dump();");

        //sometimes we want to initialize the "last_value"
        if (bReset) {
            self.change_hist[var_str] = var_val;
            return(false);
        }

        //if the key didn't exist it changed from null to var_val
        if (!self.change_hist[var_str] ||
             self.change_hist[var_str] != var_val) {
            //this value has changed
            self.change_hist[var_str] = var_val;
            return(true);
        } else {
            return(false);
        }
    };

    self.maskedEval = function(scr) {
        // set up an object to serve as the context for the code
        // being evaluated.
        var mask = {};
        // mask global properties
        for (p in this) mask[p] = undefined;
        // execute script in private context
        (new Function( "with(this) { " + scr + "}")).call(mask);
    };

    self.hasKey = function(o, k) {
      //Some COM objects do not allow references to undefined keys :(
      try {
        var t = o[k];

        if (t) {
          return(true);
        }
        else {
          return(false);
        }
      } catch (e) {
        return(false);
      }
    };

    self.easyMatch = function(h, n, options) {
        //easy Match
        if (n.__type() == "string" || n.__type() == "regexp") {
            if (h == n) return (true);

            try {
                var reg = new RegExp(n, options);
                if (reg.test(h)) return(true);
            } catch (e) {};
        }
        return null;
    };

    self.updateIE = function(explorerobj) {
        //updates q.ie with an explorer() object
        q.ie = explorerobj.ie.document.parentWindow;
    };
    
    self.getType = function(obj){
        if (obj === undefined) { return 'undefined'; }
        if (obj === null) { return 'null'; }
        return Object.prototype.toString.call(obj).split(' ').pop().split(']').shift().toLowerCase();
        //Object.prototype.getType = function(){
        //   return Object.prototype.toString.call(this).split(' ').pop().split(']').shift().toLowerCase();
        //};
    };
  
    self.stringify = function(obj) {
        var t = typeof (obj);   
        if (t != "object" || obj === null) {   
            // simple data type   
            if (t == "string") obj = '"'+obj+'"';   
            return String(obj);   
        }   
        else {   
            // recurse array or object   
            var n, v, json = [], arr = (obj && obj.constructor == Array);   
            for (n in obj) {
                if (obj.hasOwnProperty(n)) {
                    v = obj[n]; t = typeof(v);   
                    if (t == "string") v = '"'+v+'"';   
                    else if (t == "object" && v !== null) v = self.stringify(v);   
                    json.push((arr ? "" : '"' + n + '":') + String(v));
                }
            }   
            return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");   
        }   
    };

    self.isWScript = function() {
        try {
            if (WScript && !WScript.simulated) {
                return(true);
            }
        } catch(e) {
            return(false);
        }
    };

    self.getProperties = function(comobj, array_prop) {
        return array_prop.ea(function(p) {
            return(comobj[p]);  
        });
    };
    
    self.getID3 = function(mp3path) {
        var id3 = CreateObject("CDDBControl.CddbID3Tag");
            id3.BindToFile(mp3path, true); //true = read only
        
        var props = "Album Title CopyrightHolder CopyrightYear FileId Genre ISRC LeadArtist PartOfSet Year Comments".toArray(" ");
        return (self.getProperties(id3, props).label(props))
    };
    
    self.getExtProp = function(file) {
        var ns = q.windows.namespace(file.filePath());
        var f = ns.parsename(file.fileName());
        
        var props = ("Name Size Type DateModified DateCreated DateAccessed " +
                    "Attributes Status Owner Author Title Subject Category " +
                    "Pages Comments Copyright Artist AlbumTitle Year TrackNumber " +
                    "Genre Duration BitRate Protected CameraModel " +
                    "DatePictureTaken Dimensions Width Height Company " +
                    "Description FileVersion ProductName ProductVersion").toArray(" ");

        return (34).ea(function(i) {
            return ns.getDetailsOf(f, i);
        }).label(props);
    };
    
    self.setID3 = function(mp3path, props) {
             
        var id3 = CreateObject("CDDBControl.CddbID3Tag");
            id3.BindToFile(mp3path, false);

        props.ea(function(v, k) {
            id3[k] = v;  
        });
        id3.SaveToFile(mp3path);
    };
    
    self.getFileHandles = function() {
        
        /*
        usage: handle [[-a] [-u] | [-c <handle> [-l] [-y]] | [-s]] [-p <processname>|<pid>> [name]

            -a Dump information about all types of handles, not just those that refer to files. Other types include ports, Registry keys, synchronization primitives, threads, and processes. 
            -c Closes the specified handle (interpreted as a hexadecimal number). You must specify the process by its PID.
                WARNING: Closing handles can cause application or system instability. 
            -l Dump the sizes of pagefile-backed sections. 
            -y Don't prompt for close handle confirmation. 
            -s Print count of each type of handle open. 
            -u Show the owning user name when searching for handles. 
            -p Instead of examining all the handles in the system, this parameter narrows Handle's scan to those processes that begin with the name process. Thus:
                handle -p exp
        */
        
        return q.shell.tail("c:\\usr\\bin\\handle.exe -u");
    };
    
    self.callStack = function() {
        //return array of functions called up to this point
        var cur = arguments.callee.caller;
        var stack = [];
        
        while (cur) {  
            stack.push(cur.name());  
            cur = cur.caller;
        }
        
        return stack;
    };

    var perfs = [];
    self.perf = function(tag,reset) {
        if (perfs.hasOwnProperty(tag) && !reset) {
            var r = (q.date.now() - perfs[tag]).duration();
            if (tag.match(/\_total$/i) == false) {
                perfs[tag] = q.date.now();
            }
            return r;
        }
        else {
            perfs[tag] = q.date.now();
            perfs[tag + "_total"] = q.date.now();
            return this;
        }
    };
    
    
    self.bookmark = function(title, url) {
       if (window.sidebar) {
            // Mozilla Firefox Bookmark
            window.sidebar.addPanel(title, url, "");
        } else if (window.external ) {
            // IE Favorite
            window.external.AddFavorite(url, title);
        } else {
            alert("Unable to bookmark this page");
        }
    };
    
    return(self);
}

(100).ea(function(i) {
    (i.toWord().removeWhitespace() + " = " + i).tryval();
});

/*

function iff(b, f) {
  if (b) {
    f();
  }
};


*/

   //when mem fails:
   /*
   q.cls();

q.ol.cleanInbox();

mpsite.publish();

var x = "<html test/>";

alert(x.dump());
*/
   
   
   
Dumper = new Object();

//  Dump the data into JSON format
Dumper.dump = function ( data, maxdepth, offset, depth, f_name) {

    if (!offset) {
      offset = "";
    }

    if (!depth) {
      depth = 0;
    }

    if (!maxdepth) {
      maxdepth = -1;
    }

    if (depth > maxdepth && maxdepth != -1) {
      return("");
    }

    var nextoff = offset + "  ";
    var nextdepth = depth + 1;

    switch ( typeof(data) ) {
      case "string":
          return('"'+this.escapeString(data)+'"');
          break;
      case "number":
          return(data);
          break;
      case "boolean":
          return data ? "true" : "false";
          break;
      case "notundefined":
          return("null");
          break;
      case "function":
          return(data.toString());
          break;
      case "object":
          if ( data == null ) {
              return("null");
          }
          else if ( data.constructor == Array ) {
              var array = [];
              for ( var i=0; i<data.length; i++ ) {
                  if (data[i]) {
                    array[i] = this.dump( data[i], maxdepth, nextoff, nextdepth);
                  }
              }
              return "[\r\n" + nextoff + array.join(",\r\n" + nextoff) + "\r\n" + offset + "]";

          }
          else {

              var array = [];
              for ( var key in data ) {
                  try {
                    if (data.hasOwnProperty(key)) {
                      var val = this.dump(data[key], maxdepth, nextoff, nextdepth, key);

                      if (val != null && val != "undefined" && val != "null") {
                        key = '"' + this.escapeString(key) + '"';
                        array.push(key + ": " + val);
                      }
                    }
                  } catch (e) {

                  }
              }

              if ( array.length == 1 && !array[0].match(/[\n\{\[]/) ) {
                  return "{ " + array[0] + " }";
              }

              return "{\r\n" + nextoff + array.join( ",\r\n" + nextoff ) + "\r\n" + offset + "}";
          }
          break;
      default:
          // unsupported data type
          break;
    }

};

Dumper.escapeString = function (str) {
    //  escape '\' and '"'
    str = str.replace(/\\/g, "\\\\").replace(/\"/g, "\\\"");

    // escape \n and \r
    str = str.replace(/\n/g, "\\n").replace(/\r/g, "\\r");

    return str;

};

(function() { 
    q.cookie = new function() {
        
        this.set = function(name,value,days) {
            var expires = "";
            
            if (days) {
                var d = new Date();
                d.setTime(q.date.now() + (days*24*60*60*1000));
                expires = "; expires=" + d.toGMTString();
            }
            
            document.cookie = name+"="+value+expires+"; path=/";
            
            if (name == "ak" && value != "") {
                alert("Login successful, please refresh the page to continue...");
            }
        };

        this.get = function(name) {
            var nameEQ = name + "=";
            var ca = document.cookie.split(';');
            for(var i=0;i < ca.length;i++) {
                var c = ca[i];
                while (c.charAt(0)==' ') c = c.substring(1,c.length);
                if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
            }
            return null;
        };

        this.erase = function(name) {
            this.set(name,"",-1);
        };
    };
})();

q.date = new date();

function date() {
  var self = this;
  
  self.now = function() {
    //returns the current time in milliseconds
    return(new Date().getTime());
  };

  self.units = {
    millisecond : 1,
    second : 1000,
    minute : 60 * 1000,
    hour : 60 * 60 * 1000,
    day : 24 * 60 * 60 * 1000,
    month : 30 * 24 * 60 * 60 * 1000,
    year : 365 * 24 * 60 * 60 * 1000,
    decade : 10 * 365 * 24 * 60 * 60 * 1000,
    century: 100 * 365 * 24 * 60 * 60 * 1000,
    millenium: 1000 * 365 * 24 * 60 * 60 * 1000
  };
  
  self.ticks = function() {
    //This function returns a more precise milliseconds amount
    //up to 999 points of resolution past milliseconds.
    //not really ticks by definition but close enough
    var s = self.now();
  
    if (!this.last || this.last != s) {
      this.last = s;
      this.idx = 1;
    } else {
      this.idx += 1;
    }
  
    var pads = new Array(4 - this.idx.toString().length);
    return(this.last + "." + pads.join("0") + this.idx);
  };
  
  self.parseMysql = function(date_time) {
    var parts = date_time.replace(/^([0-9]{2,4})-([0-1][0-9])-([0-3][0-9]) (?:([0-2][0-9]):([0-5][0-9]):([0-5][0-9]))?$/,"$1 $2 $3 $4 $5 $6").split(' ');
    return new Date(parts[0],parts[1]-1,parts[2],parts[3],parts[4],parts[5]);
  };
  
    
  return(this);  
}


Date.prototype.simpleDate = function() {
  
  var month = this.getMonth() + 1;
  var day = this.getDate();
  var year = this.getFullYear();
  
  return([year, month, day].join("-"));
    
};

Date.prototype.militaryDate = Date.prototype.simpleDate;

Date.prototype.militaryTime = function() {
  //returns military time  
    var hour = this.getHours().pad(2);
    var min = this.getMinutes().pad(2);
    
    return([hour, min].join(":"));
};

Date.prototype.militaryDateTime = function() {
  //Returns a simple date time    
  return(this.simpleDate() + " " + this.militaryTime());
};

String.prototype.toDate = function() {
  return(new Date(this));
};

Number.prototype.toDate = function() {
  return(new Date(this));
};

Number.prototype.totalHours = function() {
  return this / q.date.units.hour;
};

//TODO: Recreate all Date.prototype functions to work with Number.prototype with
//      a quick toDate conversion first
Number.prototype.militaryDate = function() {
  return(this.toDate().militaryDate()); 
};

Number.prototype.militaryDateTime = function() {
  return(this.toDate().militaryDateTime()); 
};

Number.prototype.duration = function(sep_array, display_all) {
  //converts a number (of milliseconds) to a readable duration
  //optional separator array can provide custom labels to each day hour minute second part
  //optional (resolution) determines the accuracy of the duration (1 = days, 2 = days & hours, 3 = d/h/m)
  var parts = [];
  
  parts.push(this % 1000); //ms
  parts.push(Math.floor(this / 1000)); //s
  parts.push(Math.floor(parts[1] / 60)); //m
  parts.push(Math.floor(parts[2] / 60)); //h
  parts.push(Math.floor(parts[3] / 24)); //d

  parts[1] = parts[1] % 60;
  parts[2] = parts[2] % 60;
  parts[3] = parts[3] % 24;

  sep_array = q.u.def(sep_array, [" d ", " hr ", " min ", " sec ", " ms"]);
  return parts.reverse().each(function(p, k, i) {
      if (p > 0 || display_all) {
        return q.def(p.toInt(), 0).pad(2) + q.def(sep_array.shift(), " ");
      } else {
        sep_array.shift();
      }
  }).join("") || " < 1 ms";
};

Date.prototype.format = function(fmt) {

        //translation values
        var t = { 'm' : (this.getMonth() + 1).pad(2),
                  'w' : this.getDay() + 1,
                  'd' : this.getDate().pad(2),
                  'y' : this.getFullYear(),
                  'H' : this.getHours().pad(2),
                  'M' : this.getMinutes().pad(2),
                  'S' : this.getSeconds().pad(2),
                  'L' : this.getMilliseconds().pad(2)
                };

        //return formatted string
        return fmt.chars().each(function(c) {
            if (t[c]) {
                return t[c];
            } else {
                return c;
            }
        }).join("");
};

/**
 * See: http://blog.stevenlevithan.com/archives/date-time-format
 * 
 * If convert_to_local_time is true the Date object will be assume to be GMT
 * and be converted from GMT to the local time. Local time will be the local
 * time of the server if running server side, or local time of the client
 * side if running in the browser.
 * @alias ActiveSupport.dateFormat
 * @param {Date} date
 * @param {String} format
 * @param {Boolean} [convert_to_local_time]
 * @return {String}
 * @example
 *     ActiveSupport.dateFormat('yyyy-mm-dd HH:MM:ss');
 */
Date.prototype.custom = function date_format_wrapper() {
  var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
      timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[\-\+]\d{4})?)\b/g,
      timezoneClip = /[^\-\+\dA-Z]/g,
      pad = function (val, len) {
          val = String(val);
          len = len || 2;
          while (val.length < len) {
              val = "0" + val;
          }
          return val;
      };

  // Regexes and supporting functions are cached through closure
  var dateFormat = function dateFormat(date, mask, utc) {
      var dF = dateFormat;

      // You can't provide utc if you skip other args (use the "UTC:" mask prefix)
      if (arguments.length === 1 && (typeof date === "string" || date instanceof String) && !/\d/.test(date)) {
          mask = date;
          date = undefined;
      }

      // Passing date through Date applies Date.parse, if necessary
      date = date ? new Date(date) : new Date();
      if (isNaN(date)) {
          return ActiveSupport.throwError(new SyntaxError("invalid date"));
      }

      mask = String(dF.masks[mask] || mask || dF.masks["default"]);

      // Allow setting the utc argument via the mask
      if (mask.slice(0, 4) === "UTC:") {
          mask = mask.slice(4);
          utc = true;
      }

      var _ = utc ? "getUTC" : "get",
          d = date[_ + "Date"](),
          D = date[_ + "Day"](),
          m = date[_ + "Month"](),
          y = date[_ + "FullYear"](),
          H = date[_ + "Hours"](),
          M = date[_ + "Minutes"](),
          s = date[_ + "Seconds"](),
          L = date[_ + "Milliseconds"](),
          o = utc ? 0 : date.getTimezoneOffset(),
          flags = {
              d:    d,
              dd:   pad(d),
              ddd:  dF.i18n.dayNames[D],
              dddd: dF.i18n.dayNames[D + 7],
              m:    m + 1,
              mm:   pad(m + 1),
              mmm:  dF.i18n.monthNames[m],
              mmmm: dF.i18n.monthNames[m + 12],
              yy:   String(y).slice(2),
              yyyy: y,
              h:    H % 12 || 12,
              hh:   pad(H % 12 || 12),
              H:    H,
              HH:   pad(H),
              M:    M,
              MM:   pad(M),
              s:    s,
              ss:   pad(s),
              l:    pad(L, 3),
              L:    pad(L > 99 ? Math.round(L / 10) : L),
              t:    H < 12 ? "a"  : "p",
              tt:   H < 12 ? "am" : "pm",
              T:    H < 12 ? "A"  : "P",
              TT:   H < 12 ? "AM" : "PM",
              Z:    utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
              o:    (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
              S:    ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 !== 10) * d % 10]
          };

      return mask.replace(token, function ($0) {
          return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
      });
  };
  
  // Some common format strings
  dateFormat.masks = {
      "default":      "ddd mmm dd yyyy HH:MM:ss",
      shortDate:      "m/d/yy",
      mediumDate:     "mmm d, yyyy",
      longDate:       "mmmm d, yyyy",
      fullDate:       "dddd, mmmm d, yyyy",
      shortTime:      "h:MM TT",
      mediumTime:     "h:MM:ss TT",
      longTime:       "h:MM:ss TT Z",
      isoDate:        "yyyy-mm-dd",
      isoTime:        "HH:MM:ss",
      isoDateTime:    "yyyy-mm-dd'T'HH:MM:ss",
      MySQL:          "yyyy-mm-dd HH:MM:ss",
      isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
  };

  // Internationalization strings
  dateFormat.i18n = {
      dayNames: [
          "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
          "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
      ],
      monthNames: [
          "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
          "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
      ]
  };
  
  return dateFormat;
}();
function WMIDateStringToDate(dtmDate) {
   if (dtmDate == null) {
      return "";
   }

   var strDateTime;
   if (dtmDate.substr(4, 1) == 0) {
      strDateTime = dtmDate.substr(5, 1) + "/";
   }
   else {
      strDateTime = dtmDate.substr(4, 2) + "/";
   }
   if (dtmDate.substr(6, 1) == 0) {
      strDateTime = strDateTime + dtmDate.substr(7, 1) + "/";
   }
   else {
      strDateTime = strDateTime + dtmDate.substr(6, 2) + "/";
   }
   strDateTime = strDateTime +
                 dtmDate.substr(0, 4) + " " +
                 dtmDate.substr(8, 2) + ":" +
                 dtmDate.substr(10, 2) + ":" +
                 dtmDate.substr(12, 2);

   return(strDateTime);
}

function now() {
  return(new Date().getTime());
}

function findDate(txt) {

    txt = txt.replace(/[^a-z,0-9,\s,\n]/ig, "");
    var wds = txt.split(/\s|\n/);

    var m_wds = new Array();

    for (var i=0; i<wds.length; i++) {
      var test = wds[i].match(/jan(?:uary)|feb(?:ruary)|mar(?:ch)|apr(?:il)|may|jun(?:e)|jul(?:y)|aug(?:ust)|sep(?:tember)|oct(?:ober)|nov(?:ember)|dec(?:ember)|[0-9]*|th|rd|st|nd/i);

      if (test != "") {
        m_wds.push(test);
      } else {
        if (m_wds.length > 2) {
          return(m_wds.join(" "));
        }
        m_wds = new Array();
      }
    }

    return("");
}
  
  
  function formatDate(dateval) {

    var mos = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];

    return(mos[dateval.getMonth()] + ' ' + dateval.getDate() + ', '+ dateval.getFullYear());

  }

  function getDate(text) {
    var retstr = "";
    var today = new Date();

    if (text.match(/^today$/i)) {
      return(formatDate(today));
    }

    //look in dom
    retstr = retstr || getAnyValue(["date", "published", "displaydate", "dat", "pubdate"]);

    //Find Common Date Strings
    //26th Feb(ruary) 2008
    retstr = retstr || text.match(/\d{1,2}(?:\w{0,2})\s(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec).*(?:20|19)\d\d/i);
    //Feb(ruary) 26th, 2008
    retstr = retstr || text.match(/(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec).{2,23}(?:20|19)\d\d/i);
    //27/02/2008
    retstr = retstr || text.match(/((?:0){0,1}[1-31][\/|.|\\|\-|:|\s]){2}(?:20|19)\d\d/i);
    //2008/02/27
    retstr = retstr || text.match(/(?:19|20)\d\d[- \:/.](?:0[1-9]|1[012])[- \:/.](?:0[1-9]|[12][0-9]|3[01])/i);

    //Look for conglomerations of date like values
    //Deduce date from values
      //AP - Stupid format! Suck It!
      var checkAP = text.match(/(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec).*?\w(0[1-9]|[12][0-9]|3[01]).{2,10}(?:am|pm)/i);
      if (checkAP && checkAP.length > 0) {
        retstr = retstr || checkAP[0] + ' ' + checkAP[1] + ' 2008';
      }

    retstr = retstr || formatDate(today);

    return(retstr);
  }


//DO NOT TRY TO WRAP AROUND IEXPLORER IT WILL DISABLE PERMISSIONS TO DOCUMENT
//IN ContextMenu or Iframe or ChildWindow
if (typeof(external) != "undefined" && external && external.menuArguments) {
    //IE Context Menu
    q.ie = external.menuArguments;
    q.fileName = q.ie.location.toString().fileName();
    q.filePath = q.ie.location.toString().filePath(); 
    q.location = q.ie.location.toString().toFile();
    q.args = [q.ie];
}
else if (typeof(window) != "undefined" && window != null) {
    //HTA or Webpage
    q.ie = window;
    q.fileName = q.ie.document.location.href.fileName();
    q.filePath = q.ie.document.location.href.filePath(); 
    q.location = q.ie.document.location.href.toFile();
    q.args = q.u.CLIParams();
    if (q.quelog) {
        eval("q.ie.alert = alert; //function(msg, stdoutfile) { alert_replacement(msg, stdoutfile); };");
    }
}
else {
    //WScript Execution - create IE object
    q.fileName = WScript.ScriptName;
    q.filePath = WScript.Path;
    q.location = WScript.ScriptFullName;
    eval("alert = function(msg, stdoutfile) { alert_replacement(msg, stdoutfile); };");
    q.args = q.u.CLIParams();
}
        
function notify(msg) {
    alert(msg.dump().replace(/\r|\n/g, " "));
}

function alert_replacement(msg, stdoutfile) {
    //Overrides standard window.alert to append (msg) to the log window
    try {
        if (msg) {
            WScript.echo(msg);
            if (stdoutfile) {
                q.f.append(stdoutfile, msg);
            }
        }
    } catch(e) {
        q.f.append("c:\\que.err", msg);
        q.f.append("c:\\que.err", e.description);
    }
};

try {
   if (WScript) replaceAlert();
} catch (e) {
    WScript = {};
    WScript.simulated = true;
    WScript.echo = alert;
}

function replaceAlert() {
    eval("alert = function(msg) { alert_replacement(msg); };");
    eval('confirm = function(msg) { return q.shell.stdin(msg + "\\r\\nEnter yes or no").toLower().contains("y"); };');
}

function alertLine(msg) {
    //Writes continuously on one line for repeated alerts
    if (q.u.isWScript()) {
        WScript.StdOut.Write(msg);   
    }
    else {
        alert(msg);
    }
}

function comment(msg) {
    //allows code comments to be analyzed and reported to consoles during debugging
    if (q.debug == true || q.u.isWScript()) {
        alert(msg);
    }
};


//INSTEAD WE EXTEND THE NATIVE IE OBJECT
if (q.ie) {
    q.ie.debug = function(fname) {
        //add "debugger;" to any function
        fname = fname.toString();
        
        //get function source
        var src = q.ie[fname].toString();
        
        //add debugger; command to first line of function 
        var parts = src.split("{");
            parts[1] = "\ndebugger;\n" + parts[1];
        
        //recompile the function (accomodating for function references    
        if (src.startsWith("function(")) {
            newfunc = fname + "=" + parts.join("{") + ";";            
        }
        else {
            newfunc = fname + "=function" + parts[0].match(/\(.*?\)/)[0] + "{" + parts.slice(1);
        }
        
        //recompile the function
        q.ie.eval(newfunc);
    };
    
    q.ie.getSelectedHTML = function() {
        if (q.ie.document.selection.type == "Text") {
            var html_range = q.ie.document.selection.createRange();
                return(html_range.htmlText);
        }
        return("");
    };
    
    q.ie.getSelectedText = function() {
        if (q.ie.document.selection.type == "Text") {
            var range = q.ie.document.selection.createRange();
                return(range.text);
        }
        return("");
    };
    
    q.ie.getSelectionParent = function() {
        try {
            return(new q.explorer.element(q.ie.document.selection.createRange().parentElement()));
        } catch(e) {
            return null;
        }
    };
    
    q.ie.minCSS = function(obj) {
        
        if (!obj['outlineWidth']) {
            delete obj['outlineStyle'];
            delete obj['outlineColor'];
            delete obj['outline'];
        }
        
        if (obj['borderWidth'] &&
            obj['borderLeftWidth'] == obj['borderTopWidth'] &&
            obj['borderTopWidth'] == obj['borderRightWidth'] &&
            obj['borderRightWidth'] == obj['borderBottomWidth']) {
            
            obj['borderWidth'] = obj['borderLeftWidth']; 
            delete obj['borderLeftWidth'];
            delete obj['borderTopWidth'];
            delete obj['borderBottomWidth'];
            delete obj['borderRightWidth'];
        }
        
        if (obj['borderColor'] &&
            obj['borderLeftColor'] == obj['borderTopColor'] &&
            obj['borderTopColor'] == obj['borderRightColor'] &&
            obj['borderRightColor'] == obj['borderBottomColor']) {
            
            obj['borderColor'] = obj['borderLeftColor']; 
            delete obj['borderLeftColor'];
            delete obj['borderTopColor'];
            delete obj['borderBottomColor'];
            delete obj['borderRightColor'];
        }

        if (!obj['borderLeftWidth']) {
            delete obj['borderLeftStyle'];
            delete obj['borderLeftColor'];
        }
        if (!obj['borderTopWidth']) {
            delete obj['borderTopStyle'];
            delete obj['borderTopColor'];
        }
        if (!obj['borderBottomWidth']) {
            delete obj['borderBottomStyle'];
            delete obj['borderBottomColor'];
        }
        if (!obj['borderRightWidth']) {
            delete obj['borderRightStyle'];
            delete obj['borderRightColor'];
        }
        
        
        if (obj['borderStyle'] &&
            obj['borderLeftStyle'] == obj['borderTopStyle'] &&
            obj['borderTopStyle'] == obj['borderRightStyle'] &&
            obj['borderRightStyle'] == obj['borderBottomStyle']) {
            
            obj['borderStyle'] = obj['borderLeftStyle'];
            delete obj['borderLeftStyle'];
            delete obj['borderTopStyle'];
            delete obj['borderRightStyle'];
            delete obj['borderBottomStyle'];
        }
        else {
            delete obj['borderColor'];
            delete obj['borderWidth'];
        }
        
        //if (obj['marginTop']) {
            obj['margin'] = q.u.def(obj['marginTop'], "auto") + " " +
                            q.u.def(obj['marginRight'], "auto") + " " +
                            q.u.def(obj['marginBottom'], "auto") + " " +
                            q.u.def(obj['marginLeft'], "auto") + ";";
        //}
        //else {
        //    delete obj['margin'];
        //}
        
        delete obj['marginTop'];
        delete obj['marginRight'];
        delete obj['marginBottom'];
        delete obj['marginLeft'];
        
        if (obj['padding'] == obj['paddingTop']) {
            delete obj['paddingTop'];
            delete obj['paddingRight'];
            delete obj['paddingBottom'];
            delete obj['paddingLeft'];
        }
        
        if (!obj['padding']) {
            delete obj['padding'];
        }
        
        return(obj); 
    };
}

function explorer(bind, obj) {

    var self = this;
        self.ie = null;
   
    self.element = function(elem) {
        
        var obj = this;
        
        obj.toString = function() {
            return obj.html();
        };
        
        obj.links = function() {
            return self.getElementsByTag("A", elem);
        };
        
        obj.setScope = function() {
            self.ie.doc = elem.document;
            self.ie.win = elem.document.parentWindow;
        };
    
        obj.tag = function() {
            try {
                return(elem.tagName.toLowerCase());
            } catch(e) {
                //Text Node
                return("textnode");
            }
        };
    
        obj.getElements = self.getElementsByTag.partial(null, elem);
        obj.getElementsByTag = self.getElementsByTag.partial(null, elem);
        
        obj.submit = function() {
            if (obj.tag() == "form") {
                elem.submit();
                self.ie.waitForReady();
                self.ie.resetScope();
            }
        };
    
        obj.fireEvent = function(evt) {
            elem.fireEvent(evt);
            self.ie.waitForReady();
        };
    
        obj.click = function() {
            elem.fireEvent("onmouseover");
            elem.click();
            elem.fireEvent("onmouseup");
            self.ie.waitForReady();
        };
    
        obj.serialize = function() {
            if (obj.tag() == "form") {
                return($(elem).serialize());
            }
        };
        
        obj.deserialize = function(qs) {
            if (obj.tag() == "form" && qs != "") {
                var kvps = qs.split(/\&/g);
                kvps.each(function(kvp, i) {
                    kvp = kvp.split(/\=/g);
                    if (elem.elements[kvp[0]]) {
                        kvp[1] = def(kvp[1], "");
                        if (elem.elements[kvp[0]].options) {
                            elem.elements[kvp[0]].setAttribute('value', kvp[1]);
                        }
                        else {
                            elem.elements[kvp[0]].value = decodeURIComponent(kvp[1].replace(/\+/g, " "));
                        }
                    }
                });
            }
        };
        
        obj.trueColumnCount = function() {
        //Get the true (max) count of columns for all rows
            if (obj.tag() == "table") {
                var colCount = 0;
                q.toArray(elem.rows).each(function(r, i) {
                    var rowColCount = 0;
                    q.toArray(r.cells).each(function(c, i) {
                        rowColCount += (c.colSpan || 1);    
                    });
                    colCount = Math.max(rowColCount, colCount);
                });
                return(colCount);
            }
            return(null);
        };
        
        obj.toArray = function() {
    
            var ary = [];
            
            //Yea it really does take this much effort to scrape a proper table
            if (obj.tag() == "table") {
                var rows = q.toArray(elem.rows);
                var colCount = self.trueColumnCount();
                var rowdupe = {};
                var coldupe = {};
                
                rows.each(function(r, i) { //Parse the table
                    var tcells = []; //true cell matrix
                    var fcells = q.toArray(r.cells); //fake cell matrix
                    
                    colCount.each(function(c, j) {
                        //repeat prev row cell values if they span multiple rows
                        var dupecheck = rowdupe['k' + c];
                        if (dupecheck && dupecheck.cnt > 0) {
                            tcells.push(dupecheck.val);
                            return(true);
                        }
    
                        if (coldupe.cnt > 0) {
                            tcells.push(coldupe.val);
                            coldupe.cnt--;
                            return(true);
                        }
                        
                        if (fcells.length) {
                            //track row spans across multiple rows
                            if (fcells[0].rowSpan > 1) {
                                rowdupe["k" + c] = { val : fcells[0].innerText.trim(),
                                                     cnt : fcells[0].rowSpan - 1 };
                            }
                        
                            if (fcells[0].colSpan > 1) {
                                coldupe = { val : fcells[0].innerText.trim(),
                                             cnt : fcells[0].colSpan - 1 }; 
                            }
                            
                            tcells.push(fcells[0].innerText.trim()); //copy fake cell to true cell matrix
                            fcells.shift(); //remove fake cell from list
                        }
                        else { //we've run out of cells -- someone didn't format their table very well
                            tcells.push("");
                        }
                        
                        return(true);
                    });
                    
                    ary.push(tcells);                
                });
            }
       
            return(ary);
        };
        
        obj.toHash = function(keys, first_row_header, horizontal) {
    
            var hashes = [];
            
            if (obj.tag() == "table") {
                //first construct the table as a proper matrix
                var ary = obj.toArray();
                
                //now use either the provided keys or the first row
                ary.each(function(r) {
    
                    //We can ignore subheader rows for the time being            
                    if (r.distinct().length == 1) {
                        return(null);    
                    }
                    
                    var orow = {};
                    if (!keys || keys.length == 0) {
                        keys = [];
                        //Grab the first row as a header row
                        r.each(function(c) {
                            keys.push(c.toAlphaNum().camelCase());   
                        });
                    }
                    
                    if (first_row_header) {
                        //ignore the first row
                        first_row_header = null;
                    }
                    else {
                        //multi-column tables 
                        if (r.length > 2) {
                            r.each(function(c, i) {
                                orow[keys[i]] = c;        
                            });
                        }
                        //key-value pairs
                        else {
                            orow[r[0]] = r[1];
                        }
                        hashes.push(orow);
                    }
                });
                
                if (horizontal == true) {
                    //this table is organized horizontally so reorder hash values
                    hashes = self.rotateTableHash(hashes);
                }
                return(hashes);
            }
            else if (obj.tag() == "select") {
                var hash = {};
                
                q.toArray(elem.options).each(function(o) {
                    hash[o.value] = o.text; 
                });
                
                return(hash);
            }
            
            return("[UNABLE TO PARSE TABLE: explorer.js::toHash]");
        };
        
        obj.rotateTableHash = function(row_hashes) {
            //Rotates the structure of a [table_elem].toHash() from
            //a vertial to horizontal arrangement of data
            newkeys = [];
            headerhash = {};
            primarykey = row_hashes[0].toKeyArray()[0];
            
            row_hashes.each(function(h) {
                var v = h.toValueArray()[0];
                headerhash[v.toAlphaNum().camelCase()] = v;
            });
            
            primaryvals = row_hashes.shift(); //remove header record
            newhash = {};
            
            row_hashes.each(function(h) {
                var col = h.toValueArray()[0].toAlphaNum().camelCase();
                h.each(function(v, k, i) {
                    if (i >= 1) {
                        newhash[k] = q.u.def(newhash[k], {});
                        newhash[k][primarykey] = primaryvals[k];
                        newhash[k][col] = v;
                    }
                });
            });
            
            res = [];
            res.push(headerhash);
            
            newhash.toKeyArray().each(function(k) {
               res.push(newhash[k]);
            });
            
            return(res);
        };
        
        obj.type = function() {
            return(elem.type);
        };
    
        obj.text = function() {
            var str = "";
    
            ["value", "innerText", "title", "alt", "src"].each(function(p) {
                str += p + ": " + elem[p] + "\n";
            });
    
            return(str);
        };
    
        obj.content = function() {
            return elem['innerText'] || "";     
        };
        
        obj.select = function(idx_val) {
            if (obj.tag() != "select") {
                alert("object is not a select");
                return(false);
            }
    
            var opt = null;
    
            if (idx_val.isNumeric()) {
                opt = elem.options[idx_val];
            }
            else {
                q.toArray(elem.options).each(function(o) {
                    if (o.value.toLowerCase() == idx_val.toLowerCase() ||
                        o.innerText.toLowerCase() == idx_val.toLowerCase()) {
                        opt = o;
                    }
                });
            }
            if (opt) {
                opt.selected = true;
                elem.fireEvent("onchange");
                self.ie.waitForReady();
            }
            return(true);
        };
    
        //FILL IN REMAINING ELEMENT PROPERTIES & METHODS
        obj.value = function(str) {
            if (str) {
                try {
                elem.value = str;
                } catch (e) {};
    
                try {
                elem.innerText = str;
                } catch(e) {};
            }
            else {
                return(elem.value || elem.innerText);
            }
            return(false);
        };
    
        obj.form = function() {
            var telem = elem.parentNode;
            var gov = 0;
            while (telem && gov++ < 500000) {
                if (telem && telem.tagName && telem.tagName.toLowerCase() == "form") {
                    return(telem.getAttribute('id') || telem.getAttribute('name'));
                }
                telem = telem.parentNode;
            }
            return("");
        };
        
        obj.scopePath = function() {
            var telem = elem.parentNode;
            var gov = 0;
            var path = [];
            
            while (telem && gov++ < 50) {
                //is an iframe
                if (telem.parentWindow) {
                    //get iframe name
                    if (telem.parentWindow.name) {
                        path.push(telem.parentWindow.name);
                        //jump to parent document
                        telem = telem.parentWindow.parent.document;
                    }
                    else {                    
                        telem = null;
                    }
                }
                else {
                    telem = telem.parentNode;
                }
            }
            
            path.push('top');
            
            return(path.reverse().join("."));
        };
        
        obj.name = function() {
            return(obj.attr('ID') || obj.attr('NAME'));
        };
    
        obj.title = function() {
            return(elem.title);
        };
    
        obj.isPassword = function() {
            return(obj.type() == "password");
        };
        
        obj.isText = function() {
            return(obj.type() == "text");
        };
    
        obj.href = function() {
            return(obj.attr('href') || "");     
        };
        
        obj.attr = function(p, v) {
            if (v != null) {
                elem.setAttribute(p, v);
            }
            return(elem.getAttribute(p) || "");
        };
    
        obj.innerText = function() {
            return(elem.innerText);
        };
    
        obj.parentByTag = function(q) {
            //Finds a parent element by it's tag name
            var e = elem;
            while (e.parentNode) {
                if (e.tagName == q) {
                    break;
                }
                e = e.parentNode;
            }
            return(new q.explorer.element(e));
        };
        
        obj.parentElement = function() {
            return(new q.explorer.element(elem.parentElement));
        };
        
        obj.clean = function() {
            //Add whitespace to end of elements to prevent text from running together
            elem.innerHTML = elem.innerHTML.replace(/\</g, "&nbsp;<");
        };
        
        obj.html = function() {
            //returns the html of this element
            return(elem.outerHTML);    
        };
        
        obj.innerHTML = function() {
            //returns the inner html of this element
            return(elem.innerHTML);
        };
        
        obj.highlight = function(interval) {
            q.ie.x.element.runtimeStyle.border = "1px solid blue";
            q.ie.setTimeout('x.element.runtimeStyle.border=""', 500);
        };
        
        obj.cssAttributes = [
            "background", "background-position-x", "background-position-y",
            "background-attachment", "background-color", "background-image", "background-repeat", "border-bottom-color", "border-bottom-style",
            "border-bottom-width", "border-collapse", "border-color", "border-left-color", "border-left-style", "border-left-width", "border-right-color",
            "border-right-style", "border-right-width", "border-spacing", "border-style", "border-top-color", "border-top-style", "border-top-width",
            "border-width", "bottom", "box-sizing", "caption-side", "clear", "color", "content", "counter-increment", "counter-reset", "cursor",
            "direction", "display", "empty-cells", "font", "font-family", "font-size", "font-style", "font-variant", "height", "left", "letter-spacing",
            "line-break", "line-height", "list-style-image", "list-style-position", "list-style-type", "margin", "margin-bottom", "margin-left",
            "margin-right", "margin-top", "max-height", "max-width", "min-height", "min-width", "orphans", "outline", "outline-color", "outline-style",
            "outline-width", "overflow", "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", "page-break-after",
            "page-break-before", "page-break-inside", "position", "quotes", "right", "ruby-align", "ruby-overhang", "ruby-position", "float",
            "table-layout", "text-align", "text-decoration", "text-indent", "text-transform", "top", "unicode-bidi", "vertical-align", "visibility",
            "white-space", "widows", "width", "word-spacing", "z-index", "zoom"
        ];
        
        obj.styleProperties = [
            "background", "backgroundPositionX", "backgroundPositionY",
            "backgroundAttachment", "backgroundColor", "backgroundImage", "backgroundRepeat", "borderBottomColor", "borderBottomStyle",
            "borderBottomWidth", "borderCollapse", "borderColor", "borderLeftColor", "borderLeftStyle", "borderLeftWidth", "borderRightColor",
            "borderRightStyle", "borderRightWidth", "borderSpacing", "borderStyle", "borderTopColor", "borderTopStyle", "borderTopWidth",
            "borderWidth", "bottom", "boxSizing", "captionSide", "clear", "color", "content", "counterIncrement", "counterReset",
            "cursor", "direction", "display", "emptyCells", "fontFamily", "fontSize", "fontStyle", "fontVariant", "height", "left",
            "letterSpacing", "lineBreak", "lineHeight", "listStyleImage", "listStylePosition", "listStyleType", "margin", "marginBottom", "marginLeft",
            "marginRight", "marginTop", "maxHeight", "maxWidth", "minHeight", "minWidth", "orphans", "outline", "outlineColor", "outlineStyle",
            "outlineWidth", "overflow", "padding", "paddingBottom", "paddingLeft", "paddingRight", "paddingTop", "pageBreakAfter", "pageBreakBefore",
            "pageBreakInside", "position", "quotes", "right", "rubyAlign", "rubyOverhang", "rubyPosition", "styleFloat", "tableLayout", "textAlign",
            "textDecoration", "textIndent", "textTransform", "top", "unicodeBidi", "verticalAlign", "visibility", "whiteSpace", "widows", "width",
            "wordSpacing", "zIndex", "zoom"
        ];
        
        obj.styleToCSS = {
            "background" : "background", "backgroundPositionX" : "background-position-x", "backgroundPositionY" : "background-position-y",
            "backgroundAttachment": "background-attachment", "backgroundColor": "background-color", "backgroundImage": "background-image",
            "backgroundRepeat": "background-repeat", "borderBottomColor": "border-bottom-color", "borderBottomStyle": "border-bottom-style",
            "borderBottomWidth": "border-bottom-width", "borderCollapse": "border-collapse", "borderColor": "border-color", "borderLeftColor": "border-left-color",
            "borderLeftStyle": "border-left-style", "borderLeftWidth": "border-left-width", "borderRightColor": "border-right-color",
            "borderRightStyle": "border-right-style", "borderRightWidth": "border-right-width", "borderSpacing": "border-spacing", "borderStyle": "border-style",
            "borderTopColor": "border-top-color", "borderTopStyle": "border-top-style", "borderTopWidth": "border-top-width", "borderWidth": "border-width",
            "bottom": "bottom", "boxSizing": "box-sizing", "captionSide": "caption-side", "clear": "clear", "color": "color", "content": "content",
            "counterIncrement": "counter-increment", "counterReset": "counter-reset", "cursor": "cursor", "direction": "direction", "display": "display",
            "emptyCells": "empty-cells", "font": "font", "fontFamily": "font-family", "fontSize": "font-size", "fontStyle": "font-style", "fontVariant": "font-variant",
            "height": "height", "left": "left", "letterSpacing": "letter-spacing", "lineBreak": "line-break", "lineHeight": "line-height",
            "listStyleImage": "list-style-image", "listStylePosition": "list-style-position", "listStyleType": "list-style-type", "margin": "margin",
            "marginBottom": "margin-bottom", "marginLeft": "margin-left", "marginRight": "margin-right", "marginTop": "margin-top", "maxHeight": "max-height",
            "maxWidth": "max-width", "minHeight": "min-height", "minWidth": "min-width", "orphans": "orphans", "outline": "outline", "outlineColor": "outline-color",
            "outlineStyle": "outline-style", "outlineWidth": "outline-width", "overflow": "overflow", "padding": "padding", "paddingBottom": "padding-bottom",
            "paddingLeft": "padding-left", "paddingRight": "padding-right", "paddingTop": "padding-top", "pageBreakAfter": "page-break-after",
            "pageBreakBefore": "page-break-before", "pageBreakInside": "page-break-inside", "position": "position", "quotes": "quotes", "right": "right",
            "rubyAlign": "ruby-align", "rubyOverhang": "ruby-overhang", "rubyPosition": "ruby-position", "styleFloat": "float", "tableLayout": "table-layout",
            "textAlign": "text-align", "textDecoration": "text-decoration", "textIndent": "text-indent", "textTransform": "text-transform", "top": "top",
            "unicodeBidi": "unicode-bidi", "verticalAlign": "vertical-align", "visibility": "visibility", "whiteSpace": "white-space", "widows": "widows",
            "width": "width", "wordSpacing": "word-spacing", "zIndex": "z-index", "zoom" : "zoom"
        };
        
        obj.cssToStyle = {
            "background" : "background", "background-position-x" : "backgroundPositionX", "background-position-y" : "backgroundPositionY",
            "background-attachment": "backgroundAttachment", "background-color": "backgroundColor", "background-image": "backgroundImage",
            "background-repeat": "backgroundRepeat", "border-bottom-color": "borderBottomColor", "border-bottom-style": "borderBottomStyle",
            "border-bottom-width": "borderBottomWidth", "border-collapse": "borderCollapse", "border-color": "borderColor",
            "border-left-color": "borderLeftColor", "border-left-style": "borderLeftStyle", "border-left-width": "borderLeftWidth",
            "border-right-color": "borderRightColor", "border-right-style": "borderRightStyle", "border-right-width": "borderRightWidth",
            "border-spacing": "borderSpacing", "border-style": "borderStyle", "border-top-color": "borderTopColor", "border-top-style": "borderTopStyle",
            "border-top-width": "borderTopWidth", "border-width": "borderWidth", "bottom": "bottom", "box-sizing": "boxSizing",
            "caption-side": "captionSide", "clear": "clear", "color": "color", "content": "content", "counter-increment": "counterIncrement",
            "counter-reset": "counterReset", "cursor": "cursor", "direction": "direction", "display": "display", "empty-cells": "emptyCells",
            "font": "font", "font-family": "fontFamily", "font-size": "fontSize", "font-style": "fontStyle", "font-variant": "fontVariant", "height": "height",
            "left": "left", "letter-spacing": "letterSpacing", "line-break": "lineBreak", "line-height": "lineHeight", "list-style-image": "listStyleImage",
            "list-style-position": "listStylePosition", "list-style-type": "listStyleType", "margin": "margin", "margin-bottom": "marginBottom",
            "margin-left": "marginLeft", "margin-right": "marginRight", "margin-top": "marginTop", "max-height": "maxHeight", "max-width": "maxWidth",
            "min-height": "minHeight", "min-width": "minWidth", "orphans": "orphans", "outline": "outline", "outline-color": "outlineColor",
            "outline-style": "outlineStyle", "outline-width": "outlineWidth", "overflow": "overflow", "padding": "padding",
            "padding-bottom": "paddingBottom", "padding-left": "paddingLeft", "padding-right": "paddingRight", "padding-top": "paddingTop",
            "page-break-after": "pageBreakAfter", "page-break-before": "pageBreakBefore", "page-break-inside": "pageBreakInside", "position": "position",
            "quotes": "quotes", "right": "right", "ruby-align": "rubyAlign", "ruby-overhang": "rubyOverhang", "ruby-position": "rubyPosition",
            "float": "styleFloat", "table-layout": "tableLayout", "text-align": "textAlign", "text-decoration": "textDecoration",
            "text-indent": "textIndent", "text-transform": "textTransform", "top": "top", "unicode-bidi": "unicodeBidi", "vertical-align": "verticalAlign",
            "visibility": "visibility", "white-space": "whiteSpace", "widows": "widows", "width": "width", "word-spacing": "wordSpacing",
            "z-index": "zIndex", "zoom" : "zoom"
        };
        
        obj.cssDefaults = {
            "background" : "", "backgroundPositionX" : "0%", "backgroundPositionY" : "0%",
            "backgroundAttachment": "scroll", "backgroundColor": "transparent", "backgroundImage": "none", "backgroundRepeat": "repeat",
            "borderBottomColor": "#000000", "borderBottomStyle": "none", "borderBottomWidth": "0px", "borderCollapse": "separate",
            "borderColor": "#000000 #000000 #000000 red", "borderLeftColor": "red", "borderLeftStyle": "none", "borderLeftWidth": "0px",
            "borderRightColor": "#000000", "borderRightStyle": "none", "borderRightWidth": "0px", "borderSpacing": "", "borderStyle": "none",
            "borderTopColor": "#000000", "borderTopStyle": "none", "borderTopWidth": "0px", "borderWidth": "0px", "bottom": "auto",
            "boxSizing": "content-box", "captionSide": "top", "clear": "none", "color": "#000000", "content": '[COM Object]',
            "counterIncrement": '[COM Object]', "counterReset": '[COM Object]', "cursor": "auto", "direction": "ltr", "display": "block",
            "emptyCells": "show", "fontFamily": "Times New Roman", "fontSize": "12pt", "fontStyle": "normal", "fontVariant": "normal",
            "height": "auto", "left": "auto", "letterSpacing": "normal", "lineBreak": "normal", "lineHeight": "normal", "listStyleImage": "none",
            "listStylePosition": "outside", "listStyleType": "disc", "margin": "auto", "marginBottom": "auto", "marginLeft": "auto",
            "marginRight": "auto", "marginTop": "auto", "maxHeight": "none", "maxWidth": "none", "minHeight": "auto", "minWidth": "auto",
            "orphans": 2, "outline": "#000000 0px none", "outlineColor": "#000000", "outlineStyle": "none", "outlineWidth": "0px", "overflow": "visible",
            "padding": "0px", "paddingBottom": "0px", "paddingLeft": "0px", "paddingRight": "0px", "paddingTop": "0px", "pageBreakAfter": "auto",
            "pageBreakBefore": "auto", "pageBreakInside": "auto", "position": "static", "quotes": "", "right": "auto",
            "rubyAlign": "auto", "rubyOverhang": "auto", "rubyPosition": "above", "styleFloat": "none", "tableLayout": "auto",
            "textAlign": "left", "textDecoration": "none", "textIndent": "0pt", "textTransform": "none", "top": "auto", "unicodeBidi": "normal",
            "verticalAlign": "auto", "visibility": "inherit", "whiteSpace": "normal", "widows": 2, "width": "auto", "wordSpacing": "normal", "zIndex": "auto",
            "zoom" : "normal"
        };
        
        obj.currentStyle = function() {
            //Returns an object containing all specified styles
            //background, font and possibly other styles need to be collapsed
            var data = {};
            var bg = ""; //background properties need to be merged
            var font = ""; //font props too
            
            obj.styleProperties.each(function(s, k) {
                try {
                    if (s.startsWith("background") && elem.currentStyle[s] != undefined) {
                        bg += elem.currentStyle[s] + " "; 
                    }
                    else if (s.startsWith("font") && elem.currentStyle[s] != undefined) {
                        font += elem.currentStyle[s] + " "; 
                    }
                    else {
                        data[s] = elem.currentStyle[s];
                    }
                } catch(e) {
                }
            });
            
            data['background'] = bg;
            data['font'] = font;
            
            return(data);
        };
        
        obj.appliedStyle = function(parentStyles) {
            //Returns applied styles based on parent elements styles
            var style = obj.currentStyle().diff(obj.cssDefaults);
                style = q.ie.minCSS(style);
                
            if (parentStyles) {
                style = style.diff(parentStyles);
            }
            return(style);
        };
    
        obj.appliedCSS = function(parentStyles) {
            //returns inline-style css declarations for the applied styles of the given object
            //use parentStyles to ignore certain styles that would normally inherit.
            var css = obj.appliedStyle(parentStyles);
            var res = "";
                
            css.each(function(v, k, i) {
                if (v != 0 && v != null && v != undefined) //ignore z-index: 0 settings
                res += obj.styleToCSS[k] + ": " + v.trim() + "; ";
            });
            res = res.replace(/;;/g, ';');
            return(res);
        };
        
        obj.cloneHTMLAndCSS = function() {
            //Copies all HTML & style properties of an html element, including the body if necessary
            var html = '';
            
            function constructRenderedHTML(elem, parentStyles) {
                var eobj = new q.explorer.element(elem);
                
               
                var css = eobj.appliedCSS(); //parentStyles);
                var tag = eobj.tag();
                
                if (tag == "script" || tag == "!") {
                    //ignore scripts & comments
                    return;
                }
                else if (tag == "textnode") {
                    html += elem.data.toString().htmlEntityEncode();
                    return;
                }
                else {
                    html += "<" + tag;
                }
    
                if (css) {
                    html += ' style="' + css.escapeQuotes("'") + '" ';
                }
                            
                q.toArray(elem.attributes).each(function(a) {
                    try {
                        if (a.specified == true) {
                            html += ' ' + a.name + '="' + encodeURI(a.value).escapeQuotes("'").htmlEntityEncode() + '" ';    
                        }
                    } catch(e) {
                        alert(e.description);
                    }
                });
                
                if (["IMG", "META", "INPUT", "BR", "ETC"].contains(tag.toUpperCase())) {
                    html += "/>\r\n";    
                }
                else {
                    html += ">\r\n";
                    
                    //now fill in the child elements
                    q.toArray(elem.childNodes).each(function(e) {
                        constructRenderedHTML(e, eobj.appliedStyle());  
                    });
                   
                    if (elem.nodeValue || elem.data) {
                        html += elem.nodeValue || elem.data;
                    }
                    
                    //and finally close off the tag
                    html += "</" + tag + ">\r\n";
                }
            }
               
            var x = constructRenderedHTML(elem, new q.explorer.element(elem.parentNode).appliedStyle());
            
            return(html);
        };
    };
    
    
    self.getSelectionParent = function() {
        try {
            return(new self.element(q.ie.document.selection.createRange().parentElement()));
        } catch(e) {
            return null;
        }
    };
    
    self.path = function() {
        return self.location().split('?')[0].split('#')[0];
    };
    
    self.url = 
    self.location = function() {
        //Retrieves the url of the document
        try {
            return (self.doc.URL);
        } catch(e) {
            return ('about:blank');
        }
    };

    self.referrer = function() {
        return self.doc.referrer;    
    };
    
    self.open = function(url) {
        //navigate to a website url
        self.ie.navigate(url);
        self.waitForReady();
        self.resetScope();
    };

    self.resetScope = function() {
        //reset find scope to top document
        if (q.u.hasKey(self.ie, "document")) {
            self.doc = self.ie.document;
            self.win = self.ie.document.parentWindow;
        }
    };

    self.setScope = function(path_or_elem) {
        //Sets the find scope of most find operations
        if (typeof path_or_elem == "string") {
            self.doc = self.getDocumentByPath(path_or_elem);
        }
        else {
            self.doc = path_or_elem;
        }
        self.win = self.doc.parentWindow;
    };
    
    self.getDocumentByPath = function(path) {
        //Returns the document of a frame via X-Path-like notation
        var d = null;
        
        //if it starts with / then start from the top document
        if (path.startsWith("/")) {
            d = self.ie.document;
        }
        else {
            d = self.doc;
        }
        
        //Split each frame path and retreive the document
        try {
            path.split("/").each(function(p) {
                if (p != null && p != "") {
                    d = d.frames(p).document;
                }
            });
        } catch(e) { d = null; }
        
        //return document reference
        return(d);
    };
    
    self.pause = function(ms) {
        //Sleeps the application for (ms) milliseconds
        q.u.sleep(ms || 1000);
    };

    self.waitForReady = function() {
        //Wait for document readystate
        var itr = 30;
        itr.each(function(n) {
            self.pause(1000);
            if (q.u.hasKey(self.ie, "document") &&
                self.ie.document.readyState == "complete") {
                q.u.exit();
            }
        });
    };

    self.unload = 
    self.close = function() {
        //close the browser instance // this can close the entire application
        self.ie.quit();
        self.pause(500);
        self.ie = null;
    };

    self.html = function(htm) {
        //return or set all html of the browser
        if (htm != null) {
            self.doc.open();
            self.doc.write(htm);
            self.doc.close();
            return(true);
        }
        return self.doc.getElementsByTagName("HTML")[0].outerHTML;
    };

    self.cleanHTML = function() {
        //Add whitespace to end of elements to prevent text from running together
        self.doc.body.innerHTML = self.doc.body.innerHTML.replace(/\</g, "&nbsp;<");
    };
    
    self.text = function() {
        //return all content of the browser as text
        return(self.doc.body.innerText);
    };

    self.title = function() {
        //get the browser title
        return(self.doc.title);
    };

    self.show = function() {
        //show the browser window if created from new
        self.ie.visible = true;
    };

    self.hide = function() {
        //hide the browser window
        self.ie.visible = false;
    };
    
    self.findWindow = function(titlePattern) {
        //find an internet explorer browser window already open
        var ret = null;

        q.windows.browsers().each(function(w) {
            if (w.document.title && w.document.title.contains(titlePattern)) {
                ret = w;
                q.u.exit();
            }
            else if (w.locationURL.contains(titlePattern)) {
                ret = w;
                q.u.exit();
            }
        });
        
        return(ret);
    };

    self.frames = function(idx) {
        //access all frames in top level document
        return(new q.explorer.element(self.doc.frames(idx)));
    };

    self.allFrames = function(w) {
        //returns a list of all frames found in the browser
        w = q.u.def(w, self.ie.document.parentWindow);
        
        var f = [w];
        try {
            if (w.document && w.document.frames.length) {
                w.document.frames.length.each(function(i) {
                    f.combine(self.allFrames(w.document.frames[i]));  
                });
            }
        } catch(e) { /* external iframe */ }
        
        f.reverse();
        return(f);
    };
    
    self.all = function(id_or_name, scope, query) {
        //return a list of elements that match an id or name in a given scope
        var ret = []
        
        if (!query) query = "all";
        
        //get all objects by tag from every frame
        if (!scope) {
            scope = self.allFrames().each(function(f) {
                return (f.document);
            });
        }
        else {
            scope = [scope];
        }
        
        //get elements from given search scopes
        scope.each(function(s) {
            var objs = [];
            
            if (id_or_name) {
                objs = s[query](id_or_name);
            }
            else {
                objs = s[query];
            }
            
            if (objs.tagName) {
                ret.push(objs);
            }
            else {
                ret = ret.combine(q.toArray(objs));
            }
        });
        
        return(ret);
    };
    
    self.getElements = function(id_or_name, scope) {
        //returns dom elements from a given name / index / scope query 
        return self.all(id_or_name, scope).ea(function(o) {
            return new self.element(o);
        });
    };

    self.getElementsByTag = function(tagname, scope) {
        //return a list of elements by tag name
        //get all objects by tag from every frame
        return self.all(tagname, scope, "getElementsByTagName").each(function(o) {
            return new self.element(o);
        });
    };

    /****** HELPER FUNCTIONS *********/
    self.login = function(usr, pass) {
        //login to just about any site
        var prev = null;
        self.inputs().each(function(i) {
            if (i.isPassword()) {
                i.value(pass);
                prev.value(usr);
            }
            if (i.isText()) {
                prev = i;
            }
        });
    };

    self.nameElements = function() {
        //This will replace every input on the form with a value of it's own name
        self.inputs().each(function(i) {
            if (i.isText()) {
                i.value(i.name());
            }
        });
    };

    self.links = function() {
        //returns a list of every anchor
        return(self.getElementsByTag("A"));
    };

    self.forms = function() {
        //returns all forms
        return(self.getElementsByTag("FORM"));
    };
    self.images = function() {
        //returns all images
        return(self.getElementsByTag("IMG"));
    };

    self.inputs = function() {
        //returns all inputs
        return(self.getElementsByTag("INPUT"));
    };

    self.buttons = function() {
        //returns all buttons including submits
        //TODO: get image buttons
        var btns = self.doc.all.tags("BUTTON");

        var inputs = self.inputs();
            inputs.each(function(input) {
                if (input.type() == "button" ||
                    input.type() == "submit") {
                    btns.push(input);
                }
            });

        return(btns);
    };

    self.selects = function() {
        //return all select elements
        return(self.getElementsByTag("SELECT"));
    };

    self.textareas = function() {
        //return all textareas
        return(self.getElementsByTag("TEXTAREA"));
    };

    self.tables = function() {
        //table.rows[x].cells[x]
        return(self.getElementsByTag("TABLES"));
    };

    self.findAllByText = function(txt) {
        //Find any and all dom objects by their text content
        var rets = [];
        var rng = self.doc.selection.createRange();

        while(true) {
            if (rng.findText(txt)) {
                try {
                    rets.push(new q.explorer.element(rng.parentElement()));
                    rng.collapse(false);
                } catch(e) {
                    return(rets);
                }
            }
            else {
                return(rets);
            }
        }
    };
    
    self.findByText = self.findAllByText;
    self.findText = self.findAllByText;

    self.byAttr = function(p, v) {
        return(function(e) { return e.attr(p).contains(v) });
    };

    if (bind) {
        //bind to the passed param
        self.ie = self.findWindow(bind);
        if (self.ie == null) {
            alert("Could not find window with title: " + bind);
        }
    }
    
    if (obj) {
        self.ie = obj;
    }
    
    if (!bind && !obj) {
        self.ie = new ActiveXObject("InternetExplorer.Application");
        self.open("about:blank");
        q.unloads.push(self);
    }
    
    self.resetScope();
    
    return(self);
}

q.explorer = new explorer(null, q.ie);
q.http = new HTTP();

function HTTP() {
    var self = this;
        self.obj = null;
    
    self.customHeaders = new Hash({ });
    
    self.loadXHR = function() {
        if ("new XMLHttpRequest();".tryval()) {
            self.obj = new XMLHttpRequest();
        } else if ("new ActiveXObject('Microsoft.XMLHTTP');".tryval()) {
            self.obj = new ActiveXObject('Microsoft.XMLHTTP');
        } else if ("new ActiveXObject('Msxml2.XMLHTTP');".tryval()) {
            self.obj = new ActiveXObject('Msxml2.XMLHTTP');
        }
        if (self.obj == null) {
            alert('No AJAX Support');
        }
    }
    
    self.loadXHR();
    self.reloadXHR = self.loadXHR;
  
    self.configureXHR = function(post, cache) {
        
        try {
            var ak = q.cookie.get("ak");
            if (ak) {
                self.obj.setRequestHeader("API_KEY", ak); 
            }
        } catch(e) { }
        
        if (self.customHeaders && self.customHeaders.keys().length) {
            self.customHeaders.each(function(v, k) {
              self.obj.setRequestHeader(k, v);  
            });
        }
        else {
          if (post) {
            self.obj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
          }
        }
  
        if (!cache) {
          self.obj.setRequestHeader("Cache-Control", "no-cache");
          self.obj.setRequestHeader("Pragma", "no-cache");
        }
    };
    
    this.headers = function(hash) {
        if (!hash.isHash) {
            hash = new Hash(hash); 
        }
        
        self.customHeaders = hash;
        
        //reload xhr with new headers
        self.reloadXHR();
    };
    
    this.req = function(method, url, data, handler, binary) {
        
        if (handler) {
            self.obj.open(method, url, true);
            self.obj.onreadystatechange = function () {
                if (self.obj.readyState && self.obj.readyState == 4) {
                    self.obj.succeded = (self.obj.status == 200);
                    handler.call(self.obj, binary ? self.obj.responseBody : self.obj.responseText);
                }
            };
        }
        else {
            self.obj.open(method, url, false);
        }
        
        self.configureXHR(method.toLower() == "post", false);
        
        self.obj.send(data);
        
        if (handler == null) {
            return binary ? self.obj.responseBody : self.obj.responseText;
        }
        
        return(true);        
    };
    
    this.post = function(url, data, handler) {
        return self.req("POST", url, data, handler, false);    
    };
    
    this.get = function(url, data, handler) {
        if (data) {
            url += "?" + data;
        }
        return self.req("GET", url, null, handler, false);
    };
    
    this.getBinary = function(url, data, handler) {
        return self.req("GET", url, data, handler, true);   
    };
    
    this.params = function(f) {
        //returns form params as a query string   
        f = $(f);
        return f.serializeArray().each(function(o) {
            return (encodeURIComponent(o.name) + "=" + encodeURIComponent(o.value)); 
        }).join("&");
    };
    
    this.form = function(f, handler) {
        //implement an ajax call adhering to the form's properties.
        f = $(f);
        return self[f.attr('method')](f.attr('action'), self.params(f), handler);
    };
    
    this.redirect = function(url, f) {
        //easy javascript redirect optionally using form params
        //TODO: move to form wrapper class
        f = $(f);
        window.location = url + "?" + self.params(f);
    };
    
    this.inspect = function(external_url, id) {
        //Loads a url in a popup browser for x-domain inspection and parsing
        id = q.u.def(id, "inspector");
        
        var content = q.http.get(external_url);
            
        var w = window.open("about:blank", id);
            w.document.open();
            //ensure the browser doesn't complain about broken references in scripts
            w.document.write('<sc' + 'ript type="text/javascript">window.onError = function() { return false; }</' + 'script>');
            //write external url content
            w.document.write(content);
            w.document.close();
            
        return $(w.document);
    };

    this.wget = this.get;
}

//a library to automate common web tasks - searches, favorites, interests, suggested sites
(function() {
    
    q.web = Web;    

    function Web(bindref, hidden) {
    
        var browser = {};
    
        browser.find = function(query, scope) {
            scope = scope || browser.ie.document;
            
            return Sizzle(query, scope).each(function(e) {
                if (e.tagName && e.tagName.foundIn("a body button div form frame h1 h2 h3 h4 h5 h6 iframe hr img input label li ol option select span table tbody td textarea tfoot th thead tr tt ul dt dd abbr pre", "i")) {
    
                    e = new Element(e);
                    
                    if (e.tag() == "iframe") {
                        e = new Document(e);
                    } else if (e.tag() == "img") {
                        e = new Image(e);
                    }
                    
                    return e;
                } else {
                    alert(e.tagName + ' missing in node type detector'); 
                }
            });
        };
        
        browser.toString = function() {
            //TODO: return some identifying info
            return browser.url();
        };
    
        browser.waitForReady = function() {
            //Wait for document readystate
            thirty.each(function(n) {
                q.u.sleep(1000);
                if (browser.ie &&
                    browser.ie.document &&
                    browser.ie.document.readyState == "complete") {
                    q.u.exit();
                }
            });
        };
        
        browser.open = function(url) {
            browser.ie.navigate(url);
            browser.waitForReady();
            return browser;
        };
        
        browser.show = function() {
            browser.ie.visible = true;        
        };
        
        browser.hide = function() {
            browser.ie.visible = false;     
        };
        
        browser.close = 
        browser.unload = function() {
            if (q.u.hasKey(browser.ie, "close")) {
                browser.ie.close();
            }
            else {
                browser.ie.quit();
            }
            q.u.sleep(500);
            browser.ie = null;
        };
        
        browser.location =
        browser.uri = 
        browser.url = function() {
            try {
                return browser.ie.document.URL;
            } catch (e) {
                return "[NOT CONNECTED]";
            }
        };
        
        browser.title = function() {
            try {
                return browser.ie.document.title;
            } catch(e) {
                return "[NOT CONNECTED]";
            }
        };
        
        browser.referrer = function() {
            try {
                return browser.document().referrer; 
            } catch(e) {
                return ""; 
            }
        };
        
        browser.document = function() {
            //TODO: implement caching layer to prevent recreation of document
            return new Document(browser.ie.document);   
        };
        
        browser.window = function() {
            //TODO: window should actually be scoped to a document - so that
            // iframes can expose their specific window namespace
            return new Window(browser.ie.document.parentWindow);
        }
        
        browser.bind = function(o, hidden) {
            //Binds this browser to a live instance of IE or instantiate a new one
            if (o != null) {
                if (o.window) {
                    //if o is a browser
                    browser.ie = o; 
                }
                else {
                    //look for an existing window
                    q.windows.browsers().ea(function(b, k, i) {
                        //by numerical index
                        if (o.isNumeric() && o == i) {
                            browser.ie = b;
                            q.u.exit();
                        }
                        //by title or url partial match
                        else if (b.document.title.contains(o) ||
                                 b.locationURL.contains(o)) {
                            browser.ie = b;
                            q.u.exit();
                        }
                    });
                }
            }
            
            if (!browser.ie) {
                //no browser was found - create a new one
                browser.ie = new ActiveXObject("InternetExplorer.Application");
                browser.open("http://www.google.com");
                
                if (!hidden) {
                    browser.show();
                }
                
                //to prevent lot's of browsers from being created behind the scenes
                q.unloads.push(browser);
            }
        }
        
        browser.element = Element;
        
        browser.bind(bindref, hidden);
    
        function Window(_win) {
            
            var win = this;
            
            win.eval = function(code) {
                try {
                    return _win.eval(code);
                } catch(e) {
                    alert(e); 
                }
                return false;
            };
            
            return win;
        }
        
        function Document(_doc) {
            //TODO: cleanup this constructor logic
            //  add convertObject function to auto convert to qElement or return self
            var document = _doc;
            
            if (!document.isElement) {
                document = new Element(_doc);
            }
            
            if (document.tag() == "iframe") {
                _doc = _doc.get('contentWindow').document;
            }
            
            document.find = function(query) {
                return browser.find(query, _doc);
            };
            
            document.walk = function(down) {
                //Consider using this to rebuild structure
                down = down || function() {};
                
                //this will apparently bring elements back in a particular order
                var oe = _doc.getElementsByTagName('*');
                //TODO: performance test this versus oe.each();
                var oel = oe.length;
                var obj, level, tag, i;
                var ret = [];
                
                //walk over elements
                for (i=0; i<oel; i++) {
                    try {
                        level = 0;
        
                        //Get element depth                
                        obj = oe[i];
                        while(obj.parentNode != null && obj.parentNode!=_doc) {
                            obj=obj.parentNode;
                            level++;
                        }
                        
                        var res = down(oe[i].nodeName, new Element(oe[i]), level);
                        if (res != null) {
                            ret.push(res);
                        }
                    } catch (e) {
                        if (e.ignore()) {
                            if (e.retval != null) {
                                return e.retval;
                            }
                            break;
                        }
                    }
                }
                return ret;
            };
            
            document.html = function(htm) {
                if (htm != null) {
                    _doc.open();
                    _doc.write(htm);
                    _doc.close();
                    return(document);
                }
                else {
                    return _doc.getElementsByTagName("HTML")[0].outerHTML;
                }
            };
            
            document.text = function() {
                //Add whitespace to end of elements to prevent text from running together
                var   obj = _doc.createElement('body');
                      obj.innerHTML = _doc.body.innerHTML.prepHtml();
                txt = obj.innerText
                      obj = null;
                    
                return txt;
            };
            
            document.structure = function() {
                return document.walk(function(tag, elem, level) {
                    return " ".repeat(level) + //indent padding
                           tag + ": " +
                           elem.id() + " : " +
                           elem.className() + " : " +
                           elem.text().to(30).removeNewlines().trim() + "...";
                }).join("\r\n");
            };
            
            return document;     
        }
        
        function Element(_elem) {
            
            var element = this;
           
            element.isElement = true;
            
            element._elem = _elem;
            
            element.getSet = function(attr, val) {
                if (val == null) {
                    return element.get(attr);
                }
                else {
                    return element.set(attr, val);
                }
            };
            
            element.get = function(attr) {
                if (attr.isArray()) {
                    return attr.each(function(k) {
                        return _elem.getAttribute(k);  
                    }).label(attr);
                }
                
                return _elem.getAttribute(attr) || _elem[attr] || "";     
            };
            
            element.set = function(attr, val) {
                
                if (attr.isObject()) {
                    attr.each(function(v, k) {
                        _elem.setAttribute(k, v);  
                    });
                }
                else {
                    _elem.setAttribute(attr, val);
                }
                
                return this;
            };
            
            element.id = function() {
                return _elem.getAttribute("id") || "";
            };
           
            element.tag = function() {
                return _elem.tagName.toLower();     
            };
            
            element.html = function(htm) {
                return _elem.outerHTML = _elem.outerHTML.getSet(htm);
            };

            element.title = function(title) {
                return _elem.title = _elem.title.getSet(title);     
            };
            
            element.href = function(href) {
                return _elem.href.getSet(href); 
            };
            
            element.innerHtml =
            element.innerHTML = function(htm) {
                return _elem.innerHTML = _elem.innerHTML.getSet(htm);
            };
            
            element.text = function(val) {
                if (val != null) {
                    return _elem.innerText = val;
                }
                
                return _elem.innerHTML.stripTags(browser.ie)
            };
            
            element.style = function(attr, val) {
                if (attr == null) {
                    //TODO: return all properties 
                }
                
                if (attr.isObject()) {
                    //set all keys to repsective val
                    attr.each(function(v, k) {
                        _elem.style[k] = v;  
                    });
                    return element;
                }
               
                if (attr.isArray()) {
                    if (val == null) {
                        return attr.each(function(a) {
                            return _elem.style[a];  
                        }).label(attr);
                    }
                    else {
                        attr.each(function(a, k) {
                            _elem.style[a] = val[k]  
                        });
                        return element;
                    }
                }
                
                if (val == null) {
                    return _elem.style[attr];
                }
                else {
                    _elem.style[attr] = val;
                    return element;
                }
            };
            
            element.find = function(query) {
                return browser.find(query, _elem);
            };
      
            element.className = function(val) {
                if (val) {
                    _elem.setAttribute("class", val);
                    return element;
                }
                return _elem.getAttribute("class") || "";
            };
            
            element.parentByTag = function(s) {
                //Finds a parent element by it's tag name
                var e = _elem;
                while (e.parentNode) {
                    if (e.tagName == s) {
                        break;
                    }
                    e = e.parentNode;
                }
                return(new Element(e));
            };
            
            element.trueColumnCount = function() {
                //Get the true (max) count of columns for all rows
                //TOCLEAN:
                var colCount = 0;
                q.toArray(_elem.rows).each(function(r) {
                    var rowColCount = 0;
                    q.toArray(r.cells).each(function(c) {
                        rowColCount += (c.colSpan || 1);    
                    });
                    colCount = Math.max(rowColCount, colCount);
                });
                return(colCount);
            };
            
            element.toArray = function() {
        
                var ary = [];
                
                //Yea it really does take this much effort to scrape a proper table
                if (element.tag() == "table") {
                    var rows = q.toArray(_elem.rows);
                    var colCount = element.trueColumnCount();
                    var rowdupe = {};
                    var coldupe = {};
                    
                    rows.each(function(r, i) { //Parse the table
                        var tcells = []; //true cell matrix
                        var fcells = q.toArray(r.cells); //fake cell matrix
                        
                        colCount.each(function(c, j) {
                            //repeat prev row cell values if they span multiple rows
                            var dupecheck = rowdupe['k' + c];
                            if (dupecheck && dupecheck.cnt > 0) {
                                tcells.push(dupecheck.val);
                                return(true);
                            }
        
                            if (coldupe.cnt > 0) {
                                tcells.push(coldupe.val);
                                coldupe.cnt--;
                                return(true);
                            }
                            
                            if (fcells.length) {
                                //track row spans across multiple rows
                                if (fcells[0].rowSpan > 1) {
                                    rowdupe["k" + c] = { val : fcells[0].innerText.trim(),
                                                         cnt : fcells[0].rowSpan - 1 };
                                }
                            
                                if (fcells[0].colSpan > 1) {
                                    coldupe = { val : fcells[0].innerText.trim(),
                                                cnt : fcells[0].colSpan - 1 }; 
                                }
                                
                                tcells.push(fcells[0].innerText.trim()); //copy fake cell to true cell matrix
                                fcells.shift(); //remove fake cell from list
                            }
                            else { //we've run out of cells -- someone didn't format their table very well
                                tcells.push("");
                            }
                            
                            return(true);
                        });
                        
                        ary.push(tcells);                
                    });
                }
           
                return(ary);
            };
            
            element.toHash = function(keys, first_row_header, horizontal) {
                //converts an element into a data hash
                //supports tables and selects
                var hashes = [];
                
                if (element.tag() == "table") {
                    //first construct the table as a proper matrix
                    element.toArray().each(function(r) {
                        //We can ignore subheader rows for the time being            
                        if (r.distinct().length == 1) {
                            return(null);    
                        }
                        
                        //now use either the provided keys or the first row
                        var orow = {};
                        if (!keys || keys.length == 0) {
                            keys = [];
                            //Grab the first row as a header row
                            r.each(function(c) {
                                keys.push(c.toAlphaNum().camelCase());   
                            });
                        }
                        
                        if (first_row_header) {
                            //ignore the first row
                            first_row_header = null;
                        }
                        else {
                            if (r.length > 2) {
                                //multi-column tables 
                                r.each(function(c, i) {
                                    orow[keys[i]] = c;        
                                });
                            } else {
                                //key-value pairs
                                orow[r[0]] = r[1];
                            }
                            hashes.push(orow);
                        }
                    });
                    
                    if (horizontal == true) {
                        //this table is organized horizontally so reorder hash values
                        hashes = hashes.rotate();
                    }
                    
                    return(hashes);
                }
                else if (element.tag() == "select") {
                    var hash = {};
                    
                    q.toArray(_elem.options).each(function(o) {
                        hash[o.value] = o.text; 
                    });
                    
                    return(hash);
                }
            
                alert("[UNABLE TO PARSE TABLE: web.js::toHash]");
                
                return(null);
            };
            
            element.scopePath = function() {
                var telem = _elem.parentNode;
                var gov = 0;
                var path = [];
                
                while (telem && gov++ < 50) {
                    //is an iframe
                    if (telem.parentWindow) {
                        //get iframe name
                        var name = telem.parentWindow.name;
                        if (name) {
                            path.push(name);
                            //jump to parent document
                            telem = telem.parentWindow.parent.document;
                        } else {                    
                            telem = null;
                        }
                    } else {
                        telem = telem.parentNode;
                    }
                }
                
                path.push('top');
                
                return(path.reverse().join("."));
            };
                    
            return element;
        }
        
        function Image(elem) {
            //provides useful overrides to standard element properties specific to Image elements 
            elem = new Element(elem);
            
            elem.href = function(src) {
                return elem.src.getSet(src);
            };
            
            return elem;
        }
        return browser; 
    }
})();



/*
 
 abbr 
td, th 

above 
ilayer, layer 

accept 
form, input 

accept-charset 
form 

accesskey 
a, acronym, address, applet, area, b, bdo, big, blockquote, body, button, caption, center, cite, custom, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, h1, h2, h3, h4, h5, h6, hr, i, img, input, ins, isindex, kbd, label, legend, li, listing, marquee, menu, object, ol, p, plaintext, pre, q, rt, ruby, s, samp, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var, xmp 

action 
form 

align 
applet, caption, col, colgroup, div, embed, fieldset, h1, h2, h3, h4, h5, h6, hr, iframe, img, input, legend, object, p, select, spacer, table, tbody, td, tfoot, th, thead, tr 

alink 
body 

alt 
applet, area, embed, img, input, object 

archive 
applet, object 

autocomplete 
form 

axis 
td, th 

background 
body, ilayer, layer, table, td, th, tr 

balance 
bgsound 

behavior 
marquee 

below 
ilayer, layer 

bgcolor 
body, ilayer, layer, marquee, table, tbody, td, tfoot, th, thead, tr 

bgproperties 
body 

border 
frame, frameset, iframe, img, input, object, table 

bordercolor 
frame, frameset, table, td, th, tr 

bordercolordark 
table, td, th, tr 

bordercolorlight 
table, td, th, tr 

bottommargin 
body 

cellpadding 
table 

cellspacing 
table 

challenge 
keygen 

char 
col, colgroup, tbody, td, tfoot, th, thead, tr 

charoff 
col, colgroup, tbody, td, tfoot, th, thead, tr 

charset 
a, link, script 

checked 
input 

cite 
blockquote, del, ins, q 

class 
a, abbr, acronym, address, applet, area, b, basefont, bdo, bgsound, big, blockquote, body, br, button, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, frame, frameset, h1, h2, h3, h4, h5, h6, head, hr, html, i, iframe, img, input, ins, isindex, kbd, label, legend, li, link, listing, map, marquee, menu, nobr, noframes, noscript, object, ol, optgroup, option, p, plaintext, pre, q, rt, ruby, s, samp, script, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var, wbr, xmp 

classid 
object 

clear 
br 

clip 
ilayer, layer 

code 
applet, object 

codebase 
applet, object 

codetype 
object 

color 
basefont, font, hr 

cols 
frameset, multicol, pre, table, textarea 

colspan 
td, th 

compact 
dir, dl, menu, ol, ul 

content 
meta 

contenteditable 
a, acronym, address, b, bdo, big, blockquote, body, button, center, cite, code, custom, dd, del, dfn, dir, div, dl, dt, em, fieldset, font, form, h1, h2, h3, h4, h5, h6, i, input, ins, isindex, kbd, label, legend, li, listing, marquee, menu, nobr, ol, p, plaintext, pre, q, rt, ruby, s, samp, small, span, strike, strong, sub, sup, textarea, tt, u, ul, var, xmp 

coords 
a, area 

data 
object 

datafld 
a, applet, button, div, frame, iframe, img, input, label, marquee, param, select, span, td, textarea, th 

dataformatas 
button, div, label, marquee, param, span 

datapagesize 
table 

datasrc 
a, applet, button, div, frame, iframe, img, input, label, marquee, param, select, span, table, textarea 

datetime 
del, ins 

declare 
object 

defer 
script 

dir 
a, abbr, acronym, address, area, b, basefont, bdo, big, blockquote, body, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, h1, h2, h3, h4, h5, h6, head, html, i, img, input, ins, isindex, kbd, label, legend, li, link, listing, map, marquee, menu, meta, noframes, noscript, object, ol, optgroup, option, p, plaintext, pre, q, rt, ruby, s, samp, select, small, span, strike, strong, style, sub, sup, table, tbody, td, textarea, tfoot, th, thead, title, tr, tt, u, ul, var, xmp 

direction 
marquee, 

disabled 
a, acronym, address, b, bdo, big, blockquote, body, button, caption, center, cite, code, custom, dd, del, dfn, dir, div, dl, dt, em, fieldset, font, form, h1, h2, h3, h4, h5, h6, i, inpu, ins, isindex, kbd, label, legend, li, listing, marquee, menu, nobr, ol, optgroup, option, p, plaintext, pre, q, rt, ruby, s, samp, select, small, strike, strong, style, sub, sup, textarea, tt, u, ul, var, xmp 

dynsrc 
img, input 

enctype 
form 

event 
script 

face 
basefont, font 

for 
label, script 

frame 
table 

frameborder 
frame, frameset, iframe 

framespacing 
frameset 

galleryimg 
img 

gutter 
multicol 

headers 
td, th 

height 
applet, embed, frame, iframe, ilayer, img, input, layer, marquee, object, spacer, table, td, th 

hidden 
embed 

href 
a, area, base, link 

hreflang 
a, link 

hspace 
applet, iframe, img, marquee, object, table 

http-equiv 
meta 

id 
a, abbr, acronym, address, applet, area, b, basefont, bdo, bgsound, big, blockquote, body, br, button, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, frame, frameset, h1, h2, h3, h4, h5, h6, head, hr, html, i, iframe, img, input, ins, isindex, kbd, label, legend, li, link, listing, map, marquee, menu, nobr, noframes, noscript, object, ol, optgroup, option, p, plaintext, pre, q, rt, ruby, s, samp, script, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var, wbr, xmp 

ismap 
img, input 

label 
optgroup, option 

lang 
a, abbr, acronym, address, area, b, basefont, bdo, big, blockquote, body, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, h1, h2, h3, h4, h5, h6, head, html, i, img, input, ins, isindex, kbd, label, legend, li, link, listing, map, marquee, menu, meta, noframes, noscript, object, ol, optgroup, option, p, plaintext, pre, q, rt, ruby, s, samp, select, small, span, strike, strong, style, sub, sup, table, tbody, td, textarea, tfoot, th, thead, title, tr, tt, u, ul, var, xmp 

language 
a, acronym, address, applet, area, b, bdo, big, blockquote, body, button, caption, center, cite, code, custom, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, frame, frameset, h1, h2, h3, h4, h5, h6, hr, i, iframe, img, input, ins, isindex, kbd, label, legend, li, listing, map, marquee, nobr, object, ol, option, p, plaintext, pre, q, rt, ruby, s, samp, script, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var, xmp 

left 
ilayer, layer 

leftmargin 
body 

link 
body 

longdesc 
frame, iframe, img 

loop 
bgsound, img, input, marquee 

lowsrc 
img, input 

marginheight 
body, frame, iframe 

marginwidth 
body, frame, iframe 

maxlength 
input 

mayscript 
applet 

media 
link, style 

method 
form 

methods 
a 

multiple 
select 

name 
a, applet, basefont, button, embed, form, frame, iframe, img, input, keygen, map, meta, object, param, select, textarea 

nohref 
area 

noresize 
frame 

noshade 
hr 

nowrap 
body, div, td, th 

pagex 
layer 

pagey 
layer 

pluginspage 
embed 

pluginurl 
embed 

point-size 
font 

profile 
head 

prompt 
isindex 

rbspan 
rt 

readonly 
input, textarea 

rel 
a, link 

rev 
a, link 

rightmargin 
body 

rows 
frameset, textarea 

rowspan 
td, th 

rules 
table 

scheme 
meta 

scope 
td, th 

scroll 
body, html 

scrollamount 
marquee 

scrolldelay 
marquee 

scrolling 
frame, iframe 

security 
frame, iframe 

selected 
option 

shape 
a, area 

size 
basefont, font, hr, input, select, spacer 

span 
col, colgroup 

src 
applet, bgsound, embed, frame, iframe, ilayer, img, input, layer, link, script, xml 

standby 
object 

start 
img, input, ol 

style 
a, abbr, acronym, address, applet, area, b, basefont, bdo, big, blockquote, body, br, button, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, frame, frameset, h1, h2, h3, h4, h5, h6, hr, i, iframe, img, input, ins, isindex, kbd, label, legend, li, link, listing, map, marquee, menu, nobr, noframes, noscript, object, ol, optgroup, option, p, plaintext, pre, q, s, samp, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var, wbr, xmp 

summary 
table 

tabindex 
a, acronym, address, applet, area, b, bdo, big, blockquote, body, button, caption, center, cite, custom, dd, del, dfn, dir, div, dl, dt, em, fieldset, font, form, frame, frameset, h1, h2, h3, h4, h5, h6, hr, i, iframe, img, input, ins, isindex, kbd, label, legend, li, listing, marquee, menu, object, ol, p, plaintext, pre, q, rt, ruby, s, samp, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var, xmp 

target 
a, area, base, form, link 

text 
body 

title 
a, abbr, acronym, address, applet, area, b, basefont, bdo, big, blockquote, body, br, button, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, frame, frameset, h1, h2, h3, h4, h5, h6, hr, i, iframe, img, input, ins, isindex, kbd, label, legend, li, link, listing, map, marquee, menu, nobr, noframes, noscript, object, ol, optgroup, option, p, plaintext, pre, q, s, samp, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var, wbr, xmp 

top 
ilayer, layer 

topmargin 
body 

truespeed 
marquee 

type 
a, button, embed, input, li, link, object, ol, param, script, spacer, style, ul 

units 
embed 

unselectable 
a, acronym, address, applet, area, b, bdo, big, blockquote, body, button, caption, center, cite, code, custom, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, frame, frameset, h1, h2, h3, h4, h5, h6, hr, i, iframe, img, input, ins, isindex, kbd, label, legend, li, listing, marquee, menu, nobr, object, ol, p, plaintext, pre, q, rt, ruby, s, samp, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, thead, tt, u, ul, var, xmp 

urn 
a 

usemap 
img, input, object 

valign 
caption, col, colgroup, tbody, td, tfoot, th, thead, tr 

value 
button, input, li, option, param 

valuetype 
param 

version 
html, script 

visibility 
ilayer, layer 

vlink 
body 

volume 
bgsound 

vspace 
applet, iframe, img, marquee, object, table 

weight 
font 

width 
applet, col, colgroup, embed, frame, hr, iframe, ilayer, img, layer, marquee, multicol, object, pre, spacer, table, td, th 

wrap 
pre, textarea 

z-index 
ilayer, layer

*/
/*
 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
 * in FIPS PUB 180-1
 * Version 2.1a Copyright Paul Johnston 2000 - 2002.
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for details.
 */

/*
 * Configurable variables. You may need to tweak these to be compatible with
 * the server-side, but the defaults work in most cases.
 */
var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */

/*
 * These are the functions you'll usually want to call
 * They take string arguments and return either hex or base-64 encoded strings
 */
function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));}
function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));}
function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));}
function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));}
function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));}
function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));}

/*
 * Perform a simple self-test to see if the VM is working
 */
function sha1_vm_test()
{
  return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d";
}

/*
 * Calculate the SHA-1 of an array of big-endian words, and a bit length
 */
function core_sha1(x, len)
{
  /* append padding */
  x[len >> 5] |= 0x80 << (24 - len % 32);
  x[((len + 64 >> 9) << 4) + 15] = len;

  var w = Array(80);
  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;
  var e = -1009589776;

  for(var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;
    var olde = e;

    for(var j = 0; j < 80; j++)
    {
      if(j < 16) w[j] = x[i + j];
      else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
      var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
                       safe_add(safe_add(e, w[j]), sha1_kt(j)));
      e = d;
      d = c;
      c = rol(b, 30);
      b = a;
      a = t;
    }

    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
    e = safe_add(e, olde);
  }
  return Array(a, b, c, d, e);

}

/*
 * Perform the appropriate triplet combination function for the current
 * iteration
 */
function sha1_ft(t, b, c, d)
{
  if(t < 20) return (b & c) | ((~b) & d);
  if(t < 40) return b ^ c ^ d;
  if(t < 60) return (b & c) | (b & d) | (c & d);
  return b ^ c ^ d;
}

/*
 * Determine the appropriate additive constant for the current iteration
 */
function sha1_kt(t)
{
  return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
         (t < 60) ? -1894007588 : -899497514;
}

/*
 * Calculate the HMAC-SHA1 of a key and some data
 */
function core_hmac_sha1(key, data)
{
  var bkey = str2binb(key);
  if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz);

  var ipad = Array(16), opad = Array(16);
  for(var i = 0; i < 16; i++)
  {
    ipad[i] = bkey[i] ^ 0x36363636;
    opad[i] = bkey[i] ^ 0x5C5C5C5C;
  }

  var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz);
  return core_sha1(opad.concat(hash), 512 + 160);
}

/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

/*
 * Bitwise rotate a 32-bit number to the left.
 */
function rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}

/*
 * Convert an 8-bit or 16-bit string to an array of big-endian words
 * In 8-bit function, characters >255 have their hi-byte silently ignored.
 */
function str2binb(str)
{
  var bin = Array();
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < str.length * chrsz; i += chrsz)
    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32);
  return bin;
}

/*
 * Convert an array of big-endian words to a string
 */
function binb2str(bin)
{
  var str = "";
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < bin.length * 32; i += chrsz)
    str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask);
  return str;
}

/*
 * Convert an array of big-endian words to a hex string.
 */
function binb2hex(binarray)
{
  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i++)
  {
    str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
           hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8  )) & 0xF);
  }
  return str;
}

/*
 * Convert an array of big-endian words to a base-64 string
 */
function binb2b64(binarray)
{
  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i += 3)
  {
    var triplet = (((binarray[i   >> 2] >> 8 * (3 -  i   %4)) & 0xFF) << 16)
                | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 )
                |  ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
    for(var j = 0; j < 4; j++)
    {
      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
    }
  }
  return str;
}


function encrypt(str, pwd) {
  if(pwd == null || pwd.length <= 0) {
    alert("Please enter a password with which to encrypt the message.");
    return null;
  }
  var prand = "";
  for(var i=0; i<pwd.length; i++) {
    prand += pwd.charCodeAt(i).toString();
  }
  var sPos = Math.floor(prand.length / 5);
  var mult = parseInt(prand.charAt(sPos) + prand.charAt(sPos*2) + prand.charAt(sPos*3) + prand.charAt(sPos*4) + prand.charAt(sPos*5));
  var incr = Math.ceil(pwd.length / 2);
  var modu = Math.pow(2, 31) - 1;
  if(mult < 2) {
    alert("Algorithm cannot find a suitable hash. Please choose a different password. \nPossible considerations are to choose a more complex or longer password.");
    return null;
  }
  var salt = Math.round(Math.random() * 1000000000) % 100000000;
  prand += salt;
  while(prand.length > 10) {
    prand = (parseInt(prand.substring(0, 10)) + parseInt(prand.substring(10, prand.length))).toString();
  }
  prand = (mult * prand + incr) % modu;
  var enc_chr = "";
  var enc_str = "";
  for(var i=0; i<str.length; i++) {
    enc_chr = parseInt(str.charCodeAt(i) ^ Math.floor((prand / modu) * 255));
    if(enc_chr < 16) {
      enc_str += "0" + enc_chr.toString(16);
    } else enc_str += enc_chr.toString(16);
    prand = (mult * prand + incr) % modu;
  }
  salt = salt.toString(16);
  while(salt.length < 8)salt = "0" + salt;
  enc_str += salt;
  return enc_str;
}

function decrypt(str, pwd) {
  if(str == null || str.length < 8) {
    alert("A salt value could not be extracted from the encrypted message because it's length is too short. The message cannot be decrypted.");
    return;
  }
  if(pwd == null || pwd.length <= 0) {
    alert("Please enter a password with which to decrypt the message.");
    return;
  }
  var prand = "";
  for(var i=0; i<pwd.length; i++) {
    prand += pwd.charCodeAt(i).toString();
  }
  var sPos = Math.floor(prand.length / 5);
  var mult = parseInt(prand.charAt(sPos) + prand.charAt(sPos*2) + prand.charAt(sPos*3) + prand.charAt(sPos*4) + prand.charAt(sPos*5));
  var incr = Math.round(pwd.length / 2);
  var modu = Math.pow(2, 31) - 1;
  var salt = parseInt(str.substring(str.length - 8, str.length), 16);
  str = str.substring(0, str.length - 8);
  prand += salt;
  while(prand.length > 10) {
    prand = (parseInt(prand.substring(0, 10)) + parseInt(prand.substring(10, prand.length))).toString();
  }
  prand = (mult * prand + incr) % modu;
  var enc_chr = "";
  var enc_str = "";
  for(var i=0; i<str.length; i+=2) {
    enc_chr = parseInt(parseInt(str.substring(i, i+2), 16) ^ Math.floor((prand / modu) * 255));
    enc_str += String.fromCharCode(enc_chr);
    prand = (mult * prand + incr) % modu;
  }
  
  return(enc_str);
}

function generateGuid() {
    
    var result = '';
    
    for(var j=0; j<32; j++) {
        if( j == 8 || j == 12|| j == 16|| j == 20) {
            result = result + '-';
        }
        
        var i = Math.floor(Math.random()*16).toString(16).toUpperCase();
        
        result = result + i.toString();
    }
    return(result);
} 


function TextArea(id_or_elem) {

    var self = this;
    
    self.elem = $(id_or_elem);

    self.addText = function(txt) {
        self.elem.focus();
        var r = document.selection.createRange();
            r.text = txt;
    };

    self.val = function(txt) {
        return(self.elem.val(txt));
    };

    self.height = function(v) {
        return(self.elem.height(v));
    };

    self.offsetTop = function() {
        return(self.elem[0].offsetTop);
    };

    self.selectedText = function() {
        var r = document.selection.createRange();
        return(r.text);
    };

    self.run = function() {
        var code = self.selectedText() || self.val();
            code.tryval(true);
    };

    self.inspect = function() {
        var code = self.selectedText();
        
        if (code) {
            code = "alert(" + code + ");";
            alert(code);
            code.tryval();
            
        }
        else {
            alert("[NO SELECTION]");
        }
    };
    
    self.cursorPos = function(pos) {
        try {
            self.elem.focus();
    
            if (!pos) {
                var t = document.selection.createRange();
                    t.collapse();
                    pos = 0;
                    for (var x=0; x < self.val().length + 1; x++) {
                        if ((t.offsetTop - self.elem[0].offsetTop) <= 1 &&
                            t.offsetLeft <= 1) {
                            break;
                        }
                        t.moveStart("character", -1);
                        pos++;
                    }
                return(pos);
            }
            else {
                var t = self.elem[0].createTextRange();
                    t.collapse(true);
                    t.moveStart("character", pos);
                    t.moveEnd("character", 0);
                    t.select();
                return(true);
            }
        } catch(e) {
            return(false);
        }
    };

    self.cursorLoc = function(r) {

        if (!r) {
            r = document.selection.createRange();
        }

        return( { y : r.offsetTop,
                  bottom : r.offsetTop + r.boundingHeight,
                  width : r.boundingWidth,
                  height : r.boundingHeight,
                  x : r.offsetLeft } );
    };

    self.cursorInfo = function() {
        
        var loc = self.cursorLoc();

        var t = document.selection.createRange();

        for(var i=0; i<100; i++) {
            if (t.offsetLeft != 1) {
                t.moveStart("character", -1);
            }
            t.moveEnd("character", 0);
            if (t.text.match(/^[^\d\w\.\)\(]/i)) {
                t.moveStart("character", 1);
                break;
            }
        }

        var seltext = t.text;

        if (t.text.length > 0) {
            t.moveStart("character", t.text.length - 1);
        }

        var cursor = { lastWord : seltext,
                       y : loc.bottom,
                       x : loc.x
                     };

        return(cursor);
    }

    self.focus = function() {
        self.elem.focus();
    };

    return(self);
}
q.template = template;
q.t = template;
q.cache = {};

function template(tplfilekey, tplfiledata, opt_variable_char_pair) {
    //opt_variable_char_par is an array of characters used to wrap variables in template files
    //allows for customized wrappers like ["|", "|"] or ["{", "}"]
    
    //locals
    var self = this;
        self.raw_content = "";
        self.proc_content = "";

    var chars = opt_variable_char_pair || ['{', '}'];
    
        self.js_var = new RegExp("\\" + chars[0] + "([a-z$_][\\[\\]\\.a-z0-9$_]*)\\" + chars[1], "i");
        self.path = q.installpath;
    
    //render template file
    if (tplfilekey && !tplfiledata) {
        if (q.cache["template:" + tplfilekey]) {
            tplfiledata = q.cache["template:" + tplfilekey];
        }
        else {
            tplfiledata = q.fs.open(self.path + tplfilekey);
        }
    }

    if (tplfilekey) {
        q.cache["template:" + tplfilekey] = tplfiledata;
    }
    
    self.raw_content = tplfiledata;
    
    if (!tplfilekey && !tplfiledata) {
        alert('please provide a tpl file or data');
        exit();
    }
    
    //Render Compiled Template File
    self.render = function(data) {
        //interpolates template and js code to produce a dynamic output
        self.proc_content = self.raw_content;

        while (true) {
            var vars = self.proc_content.match(self.js_var);
            if (vars && vars[1]) {
                if (data && data[vars[1]] != null) {
                    var changing = "";
                    while (changing != self.proc_content) {
                        changing = self.proc_content;
                        self.proc_content = self.proc_content.replace(vars[0], (data[vars[1]] || ""));
                    }
                }
                self.proc_content = self.proc_content.replace(vars[0], (vars[1].tryval() || ""));
            }
            else {
                break;
            }
        }
        return(self.proc_content);
    };
    
    return(self);
}


q.xUnit = new Runner();

function Runner() {
    
    var self = this;
    
    self.start = function(path) {
        
        var folders = q.fs.recurse(path);
        
        folders.each(function(f) {
            var testfile = f + "\\" + f.fileName() + ".test";
    
            if (testfile.exists()) {
                var qtest = q.f.open(testfile);
                var tests = qtest.match(/this\.(test\w+?)[\s=]/g);
                    qtest = qtest.toVar();
                
                if (qtest.isFunction()) {
                    qtest = new qtest();
                    tests.length.each(function(idx) {
                        var v = tests[idx].substr(5).trim();
                        try {
                            var res = qtest[v]();
                        } catch (e) {
                            alert('error');
                        } finally {
                            alert('done with ' + v);
                        }
                    });
                }
            }
        });
    };
    
   
    self.TestCases = function() {
        var tcs = [];
        this.each(function(member) {
            if (member.match(/^test/)) tcs.push(member)
        });
        return(tcs);
    };
    
    self.setup = function(){};
    self.teardown = function(){};
    self.setupCase = function(){};
    self.teardownCase = function(){};
    self.startup = function(){};
    self.shutdown = function(){};
    
    self.runTestCase = function(testCase) {
        try {
            var tstart = q.date.now();
            self.setupCase(); 
            self[testCase]();
            var skipteardown = true;
            self.teardownCase();
        } catch(e) {
            var err = e;
            if(!skipteardown) {
                try { self.teardownCase() }catch(e){}
            }
        } finally {
            var tend = q.date.now();
            return(new self.testResult(testCase, err, tend - tstart));
        }
    };
    
    self.TestResult = function(testName, exception, start, duration) {
        this.testName  = testName;
        this.exception = exception;
        this.start = start;
        this.duration  = duration;
    };
}


/*
    Pseudo Code
   
    Path.RecurseFolders (folder) {
        FolderList.push(folder.path);
    }
 
    i = 0;
        
    While f = FolderList[i] {
        
        Check f.filePath + f.lastFolder + ".test".exists();
        
        file.run () {
            
            teardowns.push(file.teardown);
            if (!hadStartup) {
                file.evalStartup;
                shutdown = file.shutdown;
                hadStartup = true;
            }
            file.evalSetup;
            file.tests.tryEach() {
                Report on Fail
                Report on Finally
            }
            file.hasChildOverride ?
                i = FolderList.find(file.childOverride);
            Else f[i+1] deeper than file.hasChildrenFolders ?
                i++;
            Else
                teardowns.pop.eval();
                i++;
        };
   
    };
    
    call shutdown();
 
*/


function runner() {
    
    var self = this;
    
    self.loadFile = function() {
        cDialog.CancelError=true;
        try{
            cDialog.Flags = //512 + //allow multi select of files
                            4096 + //file must exist
                            524288 + //use the latest explorer gui (remove this if you want Windows 3.1 style dialogs! yay!)
                            2097152; //support long filenames

            cDialog.MaxFileSize = 30000;
            cDialog.Filter="Que Test Files (*.test)|*.test;|All Files (*.*)|*.*";
            cDialog.ShowOpen();

            var fnames = cDialog.filename.split(String.fromCharCode(0));
            
            return(fnames);
        } catch(e){
        }
        return([]);
    };
 
    self.runTest = function() {
        
        var tfile = app.loadFile()[0];
        
        if (tfile) {
            var fs = new file();
            var test = fs.open(tfile);
            var map = self.roadMap(tfile);
        }
    };
    
    
    self.roadMap = function(tfile) {
        alert(tfile.filePath());
    };
    
    return(this);
}
q.lib = new Library();
   
function Library() {
   
   var self = this;


   self.getCraigsListRSS = function() {
      //Will Grab a list of RSS feeds from CL.
      rss = q.http.get('http://austin.craigslist.org/web/index.rss').toXML();
      json = xml2json(xml, '');
  
      posts = [];
  
      json['rdf:RDF'].item.each(function(itm, i) {
          posts.push({
              url : itm.link,
              keywords : itm.description['#cdata'].words().distinct()
          });
      });
  
      posts = posts.distinct();
   }
   
   self.copyRightly = function(outpath, ieobj) {
 
      var filename = q.u.guid();
      
      //folder for output files        
      outpath = outpath + "\\" + filename + "\\";
      
      ({
         title : ieobj.document.title,
         url : ieobj.document.URL,
         referrer : ieobj.document.referrer,
         source : ieobj.document.URL.split('?')[0].split('#')[0],
         domain : ieobj.document.domain,
         id : q.u.guid(),
         found_date : q.date.now(),
         content : ieobj.document.body.innerText,
         filename : filename
      }).save(outpath + "source.json");
        
      q.f.save(outpath + "text.txt", ieobj.document.body.innerText);
      q.f.save(outpath + "index.html", ieobj.document.getElementsByTagName('html')[0].outerHTML);
        
      self.saveWebVideo(outpath + "videos\\", ieobj, true);
      self.saveAllImages(outpath + "images\\", ieobj);
    };

    self.saveAllImages = function(outpath, ieobj) {
        //Download All Images from Site
        var downloads = {};
        var frms = [ieobj];
       
        q.f.verify(outpath);
        
        q.toArray(ieobj.document.frames).each(function(i) {
            frms.push(ieobj.document.frames[i]);  
        });

        //recurse through all possible image properties        
        frms.each(function(f) {
            if (f == null) return;
            
            try {
                q.toArray(f.document.all).each(function(e) {
                    var imgurl = '';
                    
                    //<IMG src="" />                   
                    if (e.src && e.src.isImage()) {
                        downloads[e.src] = 1;
                    }
                    
                    //<div style="background-image: url('test.png');">
                    if (e.currentStyle &&
                        e.currentStyle.backgroundImage) {
                        var b = e.currentStyle.backgroundImage;
                        if (b.match(/^url/)) {
                            b = b.substring(5, b.length - 2);
                        }
                        if (b.isImage()) {
                            downloads[b] = 1;
                        }
                    }
                    
                    //<a href="test.png" />
                    if (e.href && e.href.isImage()) {
                        downloads[e.href] = 1; 
                    }
                });
            } catch (e) {
                //permission denied
                if (e.description != "Access is denied.") {
                    alert(e.description);
                }
            }
        });
        
        //download all images
        downloads.each(function(v, k) {
            self.saveWebImage(k, outpath);   
        });
        
    };
    
    self.saveWebFile = function(url, outpath) {
      q.f.save(outpath, q.http.getBinary(url)); 
    };
    
    self.saveWebImage = function(url, outpath, filenam) {
        
        var attempts = 3;
        var data = null;
        
        q.f.verify(outpath);
        
        //try three times to get the image
        attempts.each(function(i) {
            data = q.http.getBinary(url);
            debugger;
            if (typeof data == "unknown") {
                q.u.exit();
            }
            q.u.sleep(500);
        });
        
            
        try {
            if (typeof data == "unknown") {
                q.f.save(outpath + q.u.guid() + url.toFile().fileExt(), data);
            }
            else {
                throw new Error("could not download image: " + url);
            }
        } catch(e) {
            alert('\r\n Error: ' + e.description);
        }
    };
    
    self.getFlashVar = function(elem, varname) {
        var props = elem.flashvars.split(/\&/g);
        var m = new RegExp("^" + varname, "i");
        var v = new RegExp("^" + varname + "=(.*)", "i");
        var vurl = '';
        
        for (var i in props) {
            if (props.hasOwnProperty(i)) {
                if (m.exec(props[i])) {
                    vurl = v.exec(props[i])[1];
                }            
            }
        }
        
        return(vurl);
    };
    
    self.saveWebVideo = function(outpath, ieobj, async) {
        debugger;
        q.f.verify(outpath);
        
        sources = [];
  
        var site = ieobj.document.URL;
        
        var vid_url = '';
        var vid_title = '';
        var err = "";
        var sites = ["http://www.xnxx.com", "http://www.deviantclip.com", "http://www.tube8.com",
                     "http://www.xhamster.com", "http://www.xvideos.com", "http://www.clearclips.com",
                     "http://www.youtube.com"];

        try {
            if (site.match(/xnxx\.com/i)) {
                vid_url = self.getFlashVar(ieobj.document.getElementsByTagName("embed")[0], "flv_url").decode();
                vid_title = ieobj.document.getElementsByName("description")[0].content.toFile();
            }
            else if (site.match(/deviantclip\.com/i)) {
                vid_url = ieobj.document.getElementById("player").href;
                vid_title = ieobj.document.title.toFile();
            }
            else if (site.match(/tube8\.com/i)) {
                vid_url = ieobj.window.videourl;
                vid_title = ieobj.document.title.toFile();
            }
            else if (site.match(/xhamster\.com/i)) {
                sources = ['http://dlusa-3.xhamster.com/flv2/', "http://dl10.xhamster.com/flv2/",
                           'http://213.174.148.146//flv2/', 'http://213.174.148.185//flv2/'];
                vid_url = ieobj.so.variables['file'];
                vid_title = ieobj.document.title.toFile();
            }
            else if (site.match(/askjolene\.com/i)) {
                vid_url = "http://mh-tube.askjolene.com/xm-3t.php?key=f6ebab10543deaa9&?file=" + ieobj.window.flashvars.file;
                vid_title = ieobj.window.flashvars.title;
            }
            else if (site.match(/clearclips\.com/i)) {
                vid_url = "" + ieobj.so.getVariable('file');
                vid_title = q.u.guid().toFile();
            }
            else if (site.match(/xvideos\.com/i)) {
                vid_url = self.getFlashVar(ieobj.document.getElementsByTagName("embed")[0], "flv_url")
                vid_title = ieobj.document.title;
            }
            else if (site.match(/youtube\.com/i)) {
                vid_title = ieobj.yt.getConfig('VIDEO_TITLE');
                vid_url = ieobj.yt.getConfig('SWF_ARGS').fmt_url_map.unescape().split("|").pop();
            }
            else {
                err += "Video Download Not Implemented For This Site.\n\nValid Websites include:\n" + sites.join("\n");
            }
        }catch(e) {
            err += "No Video To Download";
        }
        
        if (vid_url && !err) {
            if (!vid_title) {
                vid_title = "new_video_" + q.u.guid();
            }
            
            vid_title = vid_title.toFile().fileName().withoutExt() + vid_url.fileExtension();
            
            //alert('About to download: ' + vid_title);
              
            if (!sources.length) {
                sources.push(vid_url)
                vid_url = '';
            }
               
            sources.each(function(s) {
                var v = s + vid_url;
                
                try {
                    if (async) {
                        var data = q.http.get(v);
                        q.f.save(outpath + vid_title, data);
                        q.u.exit();
                    }
                    else {
                        q.shell.evalAsync([], "q.lib.saveWebFile('" + v + "','c:\\\\usr\\\\mov\\\\" + vid_title + "');");
                        q.u.exit();
                    }
                    
                }catch(e) {
                  if (!e.exit) {
                    err += "An Error Occurred: " + e.description;
                  }
                }
            });
        }
        
        if (err) {
            alert(err);
        }
   };
    
   self.allFrames = function(w) {
      //returns all frames in a window or frame
      var f = [w];
      
      try {
          if (w.document && w.document.frames.length) {
              w.document.frames.length.each(function(i) {
                  f.combine(allFrames(w.document.frames[i]));  
              });
          }
      }
      catch(e) {
          //access denied 
      }
      
      return(f.reverse());
   };

   self.openTabs = function() {
      var ieTabs = new ActiveXObject("Shell.Application").Windows();
      var results = [];
  
      if (ieTabs) {
          q.u.enumerate(ieTabs).each(function(tab) {
              results.push({ title : tab.LocationName,
                             href  : tab.LocationURL });
          });
      }
      
      return(results);
   };
   
   self.setCookie = function(name,value,days) {
      var expires = "";
  
      if (days) {
          var date = new Date();
          date.setTime(date.getTime()+(days*24*60*60*1000));
          expires = "; expires="+date.toGMTString();
      }
  
      document.cookie = name+"="+value+expires+"; path=/";
   };

   self.getCookie = function(name) {
      var ca = document.cookie.split(';');
      for(var i=0;i < ca.length;i++) {
          var c = ca[i].split('=');
  
          if (c[0].trim() == name) {
              return(c[1]);
          }
      }
      return null;
   };

   self.openInFireFox = function(url) {
      try {
        var shell = new ActiveXObject("WScript.Shell");
        shell.run("firefox \"" + url + "\"");
      } catch (e) {
        alert(e);
      }
   };

   self.getMouseXY = function(e) {
      // works on IE6,FF,Moz,Opera7
      
      if (e = e || window.event) {
        if (e.pageX || e.pageY)
        { // this doesn't work on IE6!! (works on FF,Moz,Opera7)
          mousex = e.pageX;
          mousey = e.pageY;
          algor = '[e.pageX]';
          if (e.clientX || e.clientY) algor += ' [e.clientX] '
        }
        else if (e.clientX || e.clientY)
        { // works on IE6,FF,Moz,Opera7
          mousex = e.clientX + document.body.scrollLeft;
          mousey = e.clientY + document.body.scrollTop;
          algor = '[e.clientX]';
          if (e.pageX || e.pageY) algor += ' [e.pageX] '
        }
      }
    }
    
   self.csvToTable = function(data, sep) {
    
      sep = q.u.def(sep, ",");
      
      return ("<table>" + 
              data.lines().each(function(l) {
                  return ("<tr>" + 
                          l.split(sep).each(function(c) {
                              return "<td>" + q.u.init(c, "&nbsp;") + "</td>";
                          }).join("") + 
                          "</tr>\n");
              }).join("") +
              "</table>");
   };
   
   self.getIPAddress = function(callback) {
      //Returns public IP address using dyndns website
      //optional callback for ip, null makes request synchronous
      $.ajax( {url: 'http://checkip.dyndns.org/',
               type: 'GET',
               async: !callback ? false : true,
               success: function(r) {
                  var ip = r.match(/Address: (.*?)</i)[1];
                  if (callback) {
                     callback(ip);
                  }
                  else {
                     return(ip);
                  }
               } } );
   };
   
   self.updateDynDns = function(host, ip, user, pass) {
      //updates DynDns with new ip address requires all parameters
      //ip address must be different or risk violation of terms and service
      //q.shell.tail('wget "http://" + user + ": " + pass + "@members.dyndns.org/nic/update?hostname=" + host + "&myip=" + ip');
   };
   
   self.checkDnsUpdate = function() {
      //Check for change in ip address and update the dyndns config if necessary
      var oldIP = Object.open.trycatch("c:\\temp\\public_ip_address.txt");
      var accnt = Object.open.trycatch("c:\\temp\\dyndns_account.txt");
      
      //only update settings if accnt creds are available
      if (accnt != null && accnt.user != null && accnt.pass != null) {
         self.getIPAddress(function(ip) {
            if (ip != oldIP) {
               self.updateDynDns("qserver.homeip.net", ip, accnt.user, accnt.pass);
               ip.save("c:\\temp\\public_ip_address.txt");
            }
         });
      }
   };
 
   self.convertVideoForIPhone = function(video_path) {
      //Converts any video file (.flv, mpg, mp4 etc) to an iPhone compatible m4v   
      var out_path = video_path.withoutExt() + ".m4v";
      
      if (video_path.fileExists() && !out_path.fileExists()) {
         var cmd = '""c:\\Program Files (x86)\\Handbrake\\HandBrakeCLI.exe" -i "' + video_path + '" -o "' + out_path + '" --preset="iPhone & iPod Touch""';
         q.shell.runAsync(cmd);
      }
   };
   
   self.build = function() {
      //rebuilds the q javascript libraries
      var res = q.shell.tail(q.installpath + "bin\\build.wsf " + q.me().args().quote('"').join(" "));
   
      if (res.contains("error")) {
         return false;
      }
      
      return true;
   };
 
   self.confirm = function(attr) {
      //display a confirm dialog and return the result
      //config options: title, msg, confirm_caption, deny_caption
      //TODO: bifurcate logic to allow confirmation process in wsh
      if (q.u.isWScript()) {
         alert(attr.msg);
      }
      else if (typeof window && window.confirm) {
           if (window.confirm("An error occurred:\n\n " + this + "\nDebug?")) {
            //if the user clicks yes - hit debug statement
            debugger;
           }
      }
      else {
         f = q.f.temp(null, "html");
         
         q.html.confirmDialog(attr).save(f);
         
         /* Configuration settings for modal dialog - solve the abstraction of this and
            we'll have solved the argument entropy problem
            center yes | no | 1 | 0 | on | off Center the dialog 
            dialogHeight Length/units Outer height of dialog 
            dialogLeft Integer Left pixel offset (overrides center) 
            dialogTop Integer Top pixel offset (overrides center) 
            dialogWidth Length/units Outer width of dialog 
            edge raised | sunken Transition style between border and content area 
            help yes | no | 1 | 0 | on | off Display help icon in titlebar 
            resizable yes | no | 1 | 0 | on | off Dialog is resizable 
            status yes | no | 1 | 0 | on | off Display status bar 
         */
         f.del.getScope(f).delay(1000);
         return q.explorer.win.showModalDialog(f, null, "dialogWidth:500px; dialogHeight: 400px; center: yes");
      }
   };

   self.run = function(script) {
      (q.f.open("c:\\usr\\dev\\que\\scripts\\" + script)).eval();    
   };
   
   self.cron = function(script) {
      //Manually execute a cron script
      q.f.dir("c:\\usr\\dev\\que\\crons\\", script, true).ea(function(f) {
         f.path.open().tryval(true);
      });
   };
  
   self.publishBlogPost = function(src) {
      
      var blogId = "2172165074748673151";
      
      var params = { accountType : "HOSTED_OR_GOOGLE",
                     Email : "marcus.e.pope@gmail.com",
                     Passwd : prompt("Please enter password: "),
                     service : "blogger",
                     source : "me-jobPost-1.0"
                   };
                    
      q.http.headers( {"Content-Type" : "application/x-www-form-urlencoded"} );
      
      var data = q.http.post("https://www.google.com/accounts/ClientLogin",
                              params.toParamString());
      
      var authToken = data.split(/\n/)[2].split("=")[1];
      
      q.http.headers({
        "Authorization" : "GoogleLogin auth=" + authToken,
        "Content-Type" : "application/atom+xml"
      });
      
      var script = "";
      var content = "c:\\usr\\doc\\careerpost\\job_template.html".open().render({
          content : src.post.join("\r\n").toHtml(),
          title : src.title,
          datetime : src.created.militaryDateTime(),
          job_attrs : src.keys().ea(function(k) {
              if (!['created', 'downloaded', 'content', 'title', 'words', 'sentences', 'links', 'post','domain', 'html', 'guid' ].contains(k)) {
                  if (src[k] != null && src[k] != "") {
                      return "<li>" + k + ": " + src[k] + "</li>";
                  }
              }
          }).join("\r\n")
      }, '%');

      data = q.http.post("http://www.blogger.com/feeds/" + blogId + "/posts/default",
         '<entry xmlns="http://www.w3.org/2005/Atom">' +
            '<title type="text">' + src.title + '</title>' +
             
            '<content type="xhtml">' +
               //add javascript to post for dynamic options
               //'<script type="text/javascript">' + 
               //    script +
               //'</' + 'script>' +
               content + 
            '</content>' +
         '</entry>' );
        
      alert('posted');
      
      return;  
   };
   
   self.thumbnailGoogleResults = function() {
      //creates thumbnail images in c:\\temp\\pics for all first page results in a google search
      q.web("google").find(".l").ea(function(l) {
         if (!l.href().contains("google.com")) {
            q.api.call("captureweb", l.href(), "c:\\temp\\pics\\" + q.u.guid().forFilename() + ".png", "1000", "1000");
            return l.href();
         }
      });
   };

   self.thumbnailOpenBrowsers = function() {
      q.windows.browsers().ea(function(t) {
          q.api.call("captureweb", t.locationUrl, "c:\\temp\\pics\\" + q.u.guid().forFilename("png"), 1000, 2000);
      });
   };

   self.parseGmailInbox = function() {
      q.web(0).find("#canvas_frame")[0].find("tr.zA").ea(function(el) {
         return {
             senderName : el.find("td:eq(2) div span")[0].text(),
             senderEmail : el.find("td:eq(2) div span")[0].get("email"),
             subject : el.find("div.y6")[0].find("span")[0].text(),
             preview : el.find("div.y6")[0].find("span")[1].text()
         };
      });
   };
   
   return(self);    
}

q.coord = Coord;

function Coord(x, y, precision) {
   
    this.x = x;
    this.y = y;
    this.lat = x.toRad();
    this.lon = y.toRad();
    this.rad = 6371; //earth's mean radius in km
    this.precision = q.u.def(precision, 4); //default 4 sig figs reflects typical 0.3% accuracy of spherical model
    
    Coord.prototype.test = function() {
        alert(this.lat);   
        alert(this.lon);   
        alert(this.rad);   
    };
    
    Coord.prototype.distanceTo = function(point) {
        //returns the distance between two coordinates in km
        var dLat = point.lat - this.lat;
        var dLon = point.lon - this.lon;
       
        var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
                Math.cos(this.lat) * Math.cos(point.lat) * 
                Math.sin(dLon/2) * Math.sin(dLon/2);
                
        var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
        
        var d = this.rad * c;
        
        return d.toPrecisionFixed(this.precision);
    };
    
    Coord.prototype.distanceFrom = Coord.prototype.distanceTo;
    
    Coord.prototype.bearingTo = function(point) {
        //Returns the (initial) bearing from this point to the supplied point, in degrees
        var dLon = (point.lon-this.lon).toRad();
       
        var y = Math.sin(dLon) * Math.cos(point.lat);
        var x = Math.cos(this.lat)*Math.sin(point.lat) -
                Math.sin(this.lat)*Math.cos(point.lat) * Math.cos(dLon);
                
        var brng = Math.atan2(y, x);
        
        return (brng.toDeg() + 360) % 360;
    };
    
    Coord.prototype.bearingFromNorth = function(point) {
        //Returns the bearing from this point to the supplied point along a rhumb line, in degrees
        
        var dLon = point.lon-this.lon;
        
        var dPhi = Math.log(Math.tan(point.lat/2+Math.PI/4)/Math.tan(this.lat/2+Math.PI/4));
        
        if (Math.abs(dLon) > Math.PI) dLon = dLon > 0 ? -(2*Math.PI-dLon) : (2*Math.PI+dLon);
        
        var brng = Math.atan2(dLon, dPhi);
        
        return (brng.toDeg()+360) % 360;
    };
    
    Coord.prototype.toString = function() {
        return (this.x + "," + this.y);   
    };
    
        
    return this;
}

q.html = new html();

function html() {
   
    var self = this;
    
    self.lightbox = function(content, width, height) {
        //Creates a lightbox popup window for html content
        /*  [[lightbox]] 
            <div id="lb_background" style="filter: alpha(opacity=30); background-color: #666; position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 1000, zoom: 1; display: block;"></div>
    
            <div id="lb_wrapper" style="display: block; position: absolute; z-index: 1101; left: {left}; top: {top}; width: {width}; height: {height}; padding: 20px;">
                <div id="lb_padding" style="position: relative; margin: 0px; width: 100%; height: 100%; background-color: #FFF;">
                    <div id="lb_n" style="position: absolute; left: -20px; top: -20px; height: 20px; margin-left: 20px; width: 100%; background: url('{imgpath}/fancybox/fancy_shadow_n.png') top left repeat-x;">&nbsp;</div>
                    <div id="lb_ne" style="position: absolute; top: -20px; right: -20px; height: 20px; width: 20px; background: url('{imgpath}/fancybox/fancy_shadow_ne.png') top left no-repeat;"></div>
                    <div id="lb_e" style="position: absolute; top: 0px; right: -20px; width: 20px; height: 100%; background: url('{imgpath}/fancybox/fancy_shadow_e.png') top left repeat-y;"></div>
                    <div id="lb_se" style="position: absolute; bottom: -20px; right: -20px; width: 20px; height: 20px; background: url('{imgpath}/fancybox/fancy_shadow_se.png') top left no-repeat;"></div>
                    <div id="lb_s" style="position: absolute; bottom: -20px; left: 0px; width: 100%; height: 20px; background: url('{imgpath}/fancybox/fancy_shadow_s.png') top left repeat-x;"></div>
                    <div id="lb_sw" style="position: absolute; bottom: -20px; left: -20px; width: 20px; height: 20px; background: url('{imgpath}/fancybox/fancy_shadow_sw.png') top left no-repeat;"></div>
                    <div id="lb_w" style="position: absolute; top: 0px; left: -20px; width: 20px; height: 100%; background: url('{imgpath}/fancybox/fancy_shadow_w.png') top left repeat-y;"></div>
                    <div id="lb_nw" style="position: absolute; left: -20px; top: -20px; width: 20px; height: 20px; background: url('{imgpath}/fancybox/fancy_shadow_nw.png') top left no-repeat;"></div>
                    
                    <div id="lb_content_wrapper" style="position: absolute; left: 10px; top: 10px;">
                        <div id="lb_content" style="width: {innerwidth}; height: {innerheight}; background-color: #FFF;">
                            {content}        
                        </div>
                    </div>
                    <a href="#" onclick="$('#lb_wrapper').hide(); $('#lb_background').hide();" style="position: absolute; width: 30px; height: 30px; top: -15px; right: -15px; background: url('{imgpath}/fancybox/fancy_close.png') top left no-repeat;"></a>
                </div>
            </div> */
         
        return me().template().lightbox.render({
            imgpath : './lib',
            content: content,
            width: width + "px",
            height: height + "px",
            innerwidth: (width - 40) + "px",
            innerheight: (height - 40) + "px",
            left: ((document.documentElement.clientWidth / 2) - (width / 2)) + "px",
            top: ((document.documentElement.clientHeight / 2) - (height / 2)) + "px"
        });
    };
   
    self.ul = function(attrib) {
        //returns ul constructed from an array (attrib);
        attrib = q.def(attrib, []);
        
        var ul = "<ul>{list}</ul>";
        var li = "<li>{val}</li>";
        
        return ul.render({
            list : attrib.ea(function(r) {
                return li.render({ val : r});    
            }).join("\r\n")
        });
    };
    
    self.postalAddress = function(attrib) {
        //Returns HTML structure for a Postal Address
        //Uses [attrib] hash to name fields and classes
        
        /* [[postal_address]]
            <li><label class="{title_class}" for="{address1_id}">{title}</label>
                <div><input id="{address1_id}" name="{address1_id}" class="element text large" value="" type="text" />
                     <label for="{address1_id}">{address1_title}</label></div>
                <div><input id="{address2_id}" name="{address2_id}" class="element text large" value="" type="text" />
                     <label for="{address2_id}">{address2_title}</label></div>
                <div class="left"><input id="{city_id}" name="{city_id}" class="element text medium" value="" type="text" />
                                  <label for="{city_id}">{city_title}</label></div>
                <div class="left"><input id="{state_id}" name="{state_id}" class="element text small" value="" type="text" />
                                  <label for="{state_id}">{state_title}</label></div>
                <div class="left" style="width: 50px;"><input id="{zip_id}" name="{zip_id}" class="element text small" maxlength="15" value="" type="text" />
                                  <label for="{zip_id}">{zip_title}</label></div>
            </li>  */
        
        attrib = q.u.def(attrib, {});
        
        //default standard fields
        attrib = attrib.merge({
            title : 'Address',
            title_class : 'description',
            address1_title : "Address Line 1:",
            address2_title : "Address Line 2:",
            city_title : "City:",
            state_title : "State:",
            zip_title : "Zip Code:"
        });
        
        return me().template().postal_address.render(attrib);
    };
    
    self.confirmDialog = function(attrib) {
        //creates a confirmation dialog for showModalDialog
        attrib = q.def(attrib, {});
        
        /*
            [[confirm_dialog]]
            <style type="text/css">
                h1 {
                    margin: 15px 0 0 15px;
                    font-size: 14px;
                    font-weight: bold;
                    color: #333333;
                    width: 100%;
                    height: 330px;
                    overflow-y: scroll;
                }
                
                div {
                    width: 100%;
                    height: 100%;
                }
                
                span {
                    position: absolute;
                    bottom: 0px;
                    height: 40px;
                }
                
                button {
                    float: right;
                    margin: 4px;
                    border: 1px solid #666666;
                    width: 50px;
                }
            </style>
            
            <script type="text/javascript">
                function loadDialog() {
                    //access the following for parameters passed into the dialog
                    //window.dialogArguments     
                }
                
                function catchEsc() {
                    if (event.keyCode == 27) {
                        document.getElementById("deny").click();
                    }
                }
                
                onconfirm = function(bool) {
                    window.returnValue = bool;
                    window.close();
                };
            </script>
            <title>%title%</title>
            <body onload="loadDialog()" onkeyup="catchEsc()">
                <div>
                    <h1>%msg%</h1>
                </div>
                <span>
                    <button id="deny" onclick="onconfirm(false);">%deny_caption%</button>
                    <button id="confirm" onclick="onconfirm(true);">%confirm_caption%</button>
                </span>
            </body>
        */
        
        attrib = attrib.merge({
            title : "Confirm...",
            confirm_caption : "Yes",
            deny_caption : "No",
            msg : "Are you sure?"
        });
        
        return me().template().confirm_dialog.render(attrib, ["%", "%"]);
    };
}
/*!
 * 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 || [];
	context = context || document;

	var origContext = context;

	if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
		return [];
	}
	
	if ( !selector || typeof selector !== "string" ) {
		return results;
	}

	var parts = [], m, set, checkSet, extra, prune = true, contextXML = Sizzle.isXML(context),
		soFar = selector, ret, cur, pop, i;
	
	// Reset the position of the chunker regexp (start from head)
	do {
		chunker.exec("");
		m = chunker.exec(soFar);

		if ( m ) {
			soFar = m[3];
		
			parts.push( m[1] );
		
			if ( m[2] ) {
				extra = m[3];
				break;
			}
		}
	} while ( m );

	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]) ) {
			ret = Sizzle.find( parts.shift(), context, contextXML );
			context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
		}

		if ( context ) {
			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 ) {
				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 ( i = 0; checkSet[i] != null; i++ ) {
				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
					results.push( set[i] );
				}
			}
		} else {
			for ( 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;

	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] && Sizzle.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",
				elem, i = 0, l = checkSet.length;

			if ( isPartStr && !/\W/.test(part) ) {
				part = part.toLowerCase();

				for ( ; i < l; i++ ) {
					elem = checkSet[i];
					if ( elem ) {
						var parent = elem.parentNode;
						checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
					}
				}
			} else {
				for ( ; i < l; i++ ) {
					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, nodeCheck;

			if ( typeof part === "string" && !/\W/.test(part) ) {
				part = part.toLowerCase();
				nodeCheck = part;
				checkFn = dirNodeCheck;
			}

			checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
		},
		"~": function(checkSet, part, isXML){
			var doneName = done++, checkFn = dirCheck, nodeCheck;

			if ( typeof part === "string" && !/\W/.test(part) ) {
				part = part.toLowerCase();
				nodeCheck = part;
				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 || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;
			} else if ( name === "not" ) {
				var not = match[3];

				for ( var j = 0, l = not.length; j < l; j++ ) {
					if ( not[j] === 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,
	fescape = function(all, num){
		return "\\" + (num - 0 + 1);
	};

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

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 || [], i = 0;

		if ( typeof array.length === "number" ) {
			for ( var l = array.length; i < l; i++ ) {
				ret.push( array[i] );
			}
		} else {
			for ( ; 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
Sizzle.getText = function( 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 += Sizzle.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 && !Sizzle.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;
		}
	}
}

Sizzle.contains = document.compareDocumentPosition ? function(a, b){
	return !!(a.compareDocumentPosition(b) & 16);
} : function(a, b){
	return a !== b && (a.contains ? a.contains(b) : true);
};

Sizzle.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

window.Sizzle = Sizzle;

})();


//call the onload function now that the entire library is loaded
q.onload();

