/*
 * jwyre.js
 * An in-development JavaScript general utility library.
 * Version 0.9.1.3 Copyright (C) Ryan Hardy 2010
 * See http://www.hardwyred.net for more info.
 * (File generated: Apr 27, 2010 12:51:17 PM)
 */
(function() {
	// used to enable internal logging
	var _debug = false;

	/* ******************************************************************* **
		       collection of internal helper functions, used throughout
    ** ******************************************************************* */
	var h = {
        // helper function for testing if an item is undefined
		_x :  
			function(item) {
		        return item != undefined;
		    },
		// attempts to send message to console object, if present	
		_log :
			function(message) {
				if (this._x(console)) {
					console.log(message);
				}
			},
		// does a temporary append of the given element to the body
		// element, performs the callback, and then restores element to its
		// original position	
        _doTempAppend : function(element, callback) {
            var p = jwyre.parent(element);
			var doAppend = element != document.body && element != document && element != window;
            if (doAppend) {
                jwyre.append(document.body, element);
            }
			var val = callback();
            if (doAppend && p) {
                jwyre.append(p, element);           
            } else if (doAppend) {
                jwyre.remove(element);
            }
            return val;
		},
		// helper function used to get height of all content, visible and invisible
		_getTotalHeight : function(element, getScroll) {
			return this._doTempAppend(element, function() {
                if (element == document.body || element == document || element == window) {
                    if (jwyre.isIE()) {
                        if (getScroll) {
                            return window.document.documentElement.scrollHeight;
                        } else {
                            return window.document.documentElement.offsetHeight;                            
                        }
                    } else {
                        //TODO: this is a total hack for now
                        return window.outerHeight;                      
                    }
                }
                var oFlow = jwyre.style(element, "overflow");
                jwyre.style(element, "overflow", "auto");
                var h = 0;
                if (getScroll) {
                    h = element.scrollHeight;
                } else {
                    h += jwyre.parseNumber(jwyre.style(element, "padding-top"), 0);
                    h += jwyre.parseNumber(jwyre.style(element, "margin-top"), 0);
                    h += jwyre.parseNumber(_jd.height(element), 0);
                    h += jwyre.parseNumber(jwyre.style(element, "padding-bottom"), 0);
                    h += jwyre.parseNumber(jwyre.style(element, "margin-bottom"), 0);                   
                }
                if (oFlow) {
                    jwyre.style(element, "overflow", oFlow);                    
                } else {
                    jwyre.style(element, "overflow", null);                 
                }
                return h;					
			});
	    },
		//TODO: make _getTotalWidth and _getTotalHeight return the same for both width and height
		// both were ad hoc; make universal
			
        // helper function used to get width of all content, visible and invisible
		_getTotalWidth : function(element, getScroll) {
            return this._doTempAppend(element, function() {
                if (element == document.body || element == document || element == window) {
                    if (jwyre.isIE()) {
                        if (getScroll) {
                            return window.document.documentElement.scrollWidth;
                        } else {
                            return window.document.documentElement.offsetWidth;                         
                        }
                    } else {
	                    //TODO: this is a total hack for now
	                    return window.outerWidth;
                    }
                }
                var oFlow = jwyre.style(element, "overflow");
                jwyre.style(element, "overflow", "auto");
                var w = 0;
                if (getScroll) {
                    w = element.scrollWidth
                } else {
                    w += jwyre.parseNumber(jwyre.style(element, "padding-left"), 0);
                    w += jwyre.parseNumber(jwyre.style(element, "margin-left"), 0);
                    w += jwyre.parseNumber(_jd.width(element), 0);
                    w += jwyre.parseNumber(jwyre.style(element, "padding-right"), 0);
                    w += jwyre.parseNumber(jwyre.style(element, "margin-right"), 0);                    
                }
                if (oFlow) {
                    jwyre.style(element, "overflow", oFlow);                    
                } else {
                    jwyre.style(element, "overflow", null);                 
                }
                return w;				
			});
	    },			
		// used to obtain the actual length of the arguments object (totalling
		// members that are not undefined)	
		_getArgsLen :
			function(args) {
			    var len = 0;
				for (var i = 0; i < args.length; i++) {					
					len += args[i] !== undefined;
				}  	
				return len;
			},
	    // converts an arguments object into an array
		_wrapArgs : 
			function(args) {
		        if (!this._x(args) || !this._x(args.callee)) {
		            return args;
		        }
		        var ary = [];
		        for (var i = 0; i < args.length; i++) {
		            ary.push(args[i]);
		        }
		        return ary;
		    },
	    // combines properties of two objects into one new object 
	    // if properties overlap, obj2 will overwrite obj1 properties
	    _combine : 
			function(obj1, obj2) {
		        var newObj = {};
		        for (var i in obj1) {
		            newObj[i] = obj1[i];
		        }
		        for (var i in obj2) {
		            newObj[i] = obj2[i];
		        }
		        return newObj;
		    },   
	    // obtains a no-op function if the provided arg is absent or not a function,
	    // and simply returns the function otherwise
		_getFn :
		    function(funct, defFunct) {
		        if (!funct || typeof funct != "function") {         
		            return this._getFn(defFunct, function() {});
		        } else {
		            return funct;
		        }   
		    },
	    // convenience method for checking the type of item, and throwing an error
	    // if not of the specified type
		_checkType :
			function(item, type, msg) {
		        if (typeof item != type) {
		            throw new Error(msg);
		        }
		    },
	    // returns val if val is not null or undefined, or a provided default value
	    // otherwise
	    // if no default is provided, returns 0 
		_getVal : 
		    function(val, defVal) {
		        if (this._x(val)) {
		            return val; 
		        } else {
		            return (!this._x(defVal)) ? 0 : defVal;
		        }
		    },
	    // removes any non word chars, and camel cases words within given text
	    _camelCase : 
			function(word) {
		        if (!word) {
		            return null;
		        }
		        var nwWord = "";
		        var capNxt = false;
		        for (var i = 0; i < word.length; i++) {
		            var c = word.charAt(i);
		            if (c == "-") {
		                capNxt = true;
		            } else {
		                if (capNxt) {
		                    c = c.toUpperCase();
		                    capNxt = false;
		                }
		                nwWord += c;
		            }
		        }
		        return nwWord;
		    },
	    // helper method for checking that a param is a number, and optionally 
	    // checks to see if it is within the given bounds
	    _checkNum :
			function(val, label, lowBound, hiBound) {
		        if (isNaN(val)) {
		            throw new Error(label + " is not a number (" + val + " given).");
		        }
		        if (lowBound && val < lowBound) {
		            throw new Error(label + " must be greater than " + lowBound + " (" + val + " given).");
		        }
		        if (hiBound && val > hiBound) {
		            throw new Error(label + " must be less than " + hiBound + " (" + val + " given).");
		        }
		    },
	    // attempts to parse the val as an integer or a float, returning null if it can't do either
		_getNumber : 		
			function(val) {
		        var num = this._getFloat(val);
		        if (num === null) {
		            return this._getInteger(val);
		        }
		        return num;
		    },
	    // attempts to parse the val as an integer, returning null if it can't
		_getInteger :
			function(val) {
                val = parseInt(val);
                return (isNaN(val)) ?  null : val;
		    },
	    // attempts to parse the val as a float, returning null if it can't
		_getFloat :
			function(val) {
				val = parseFloat(val);
		        return (isNaN(val)) ? null : val;
		    },
	    // returns true if both strings are non-null/non-undefined, and are equiv.
	    // after trimming and lowercasing both
		_strEq : 
			function(str1, str2) {
		        if (!str1 || !str2 || typeof str1 != "string" || typeof str2 != "string") {
		            return false;
		        }
		        str1 = jwyre.trim(str1.toLowerCase());
		        str2 = jwyre.trim(str2.toLowerCase());
		        return str1 == str2; 
		    },
        // converts the color string provided to a hex value
		// if retComps is present and true, returns an array of length 3 with the
		// RGB hex components as values
		_convertColor :
			function(color, retComps) {
				if (!this._x(color)) {
					return "";
				}
				color = jwyre.trim(color).toLowerCase();
                if (color.indexOf("#") == 0) {
                    return color;
                } else if (color.indexOf("rgb") == 0) {
		            color = color.replace("rgb(", "");      
		            color = color.replace(")", "");
		            var cs = _ary();
					_ary(color.split(/\s*,\s*/)).each(function () { cs.push(parseInt(this).toString(16)); });
					if (jwyre.parseBoolean(retComps, false)) {
						return cs;
					}
		            var hex = "#";
		            function pad(n) {
		                if (("" + n).length == 1) {
		                    return "0" + n;
		                }
		                return n;
		            }
		            hex += pad(cs[0]);
		            hex += pad(cs[1]);
		            hex += pad(cs[2]);		            
		            return hex.toUpperCase();
				} else {
					//TODO: do a literal lookup (i.e. red : #FF0000, etc.)
					return null;
				}
			},
		// returns a map of the HTMLElement's current values for the keys
		// in the provided style map
		_getOrigStyles :
		    function(element, styleMap) {
			        var styles = {};
			        for (var i in styleMap) {
			            i = h._camelCase(i);
			            var val = element.style[i];
			            if (val) {
			                styles[i] = val;
			            }
			        }
			        return styles;
			    }			
	};
	
	// does jBomb checking
    function _b() {
        if (jw) {
            throw new Error(_jm);
        }
    }
    
    /* ******************************************************************* **
                                end node block
    ** ******************************************************************* */

    //TODO: make public and comment/document
    /*
     * Internal interface used for dom access and manipulation.  Allows for
     * injection of external object to be used for such operations (i.e. jQuery).
     */
    function DOMInterface() {
		/**
		 * Returns an array of all HTMLElements that match the given selector.
		 * If none found, returns an empty array.  If an HTMLElement is provided,
		 * will be returned as a single element in an array.  If an Array is
		 * provided, each element will be evaluated, and all resulting
		 * elements will be added to a single Array and returned.
		 * 
		 * @param {string || HTMLElement || Array} element
		 */
        this.elements = function(element) {};
        /**
         * Convenience method for retrieving a single HTMLElement.  In the
         * event that mutliple HTMLElements match, only the first result will be 
         * returned.
         * 
         * @param {string || HTMLElement || Array} element
         */
        this.element = function(element) {};
		/**
		 * Returns an Array of HTMLElements by evaluating the provided arguments
		 * and creating new HTML.
		 * 
		 * @param {string} html
		 */
        this.create = function(html) {};
		/**
		 * Sets the html content of all elements obtained when the element
		 * param is evaluated, if content is present, or returns the 
		 * HTML content of the first matched if content is not provided.
		 * 
		 * @param {string || HTMLElement || Array} element
		 * @param {string} content (optional)
		 */
		this.html = function(element, content) {};
        /**
         * Sets the text content of all elements obtained when the element
         * param is evaluated, if content is present, or returns the 
         * text content of the first matched if content is not provided.
         * 
         * @param {string || HTMLElement || Array} element
         * @param {string} content (optional)
         */
        this.text = function(element, content) {};
		/**
		 * Sets the given attribute value for all elements obtained when the 
		 * element param is evaluated, if a name and value are provided, or
		 * returns the attribute value of the first matched element if value
		 * is not provided.  Also, an object containing several name/value
		 * pairs may be provided to set several attributes on all matched 
		 * elements.
		 * 
         * @param {string || HTMLElement || Array} element
         * @param {string} name
         * @param {string} value (optional)
         * 
         * OR
         * 
         * @param {string || HTMLElement || Array} element
         * @param {Object} attValues
		 */
		this.attribute = function(element, name, value) {};
		/**
		 * Obtains the height of the provided element.  If includeMargin is present
		 * and true, top and bottom margin values will be included in calculating
		 * value.
		 * 
		 * @param {string || HTMLElement} element
		 * @param {boolean} includeMargin (optional)
		 */
        this.height = function(element, includeMargin) {};
        /**
         * Obtains the width of the provided element.  If includeMargin is present
         * and true, left and right margin values will be included in calculating
         * value.
         * 
         * @param {string || HTMLElement} element
         * @param {boolean} includeMargin (optional)
         */
        this.width = function(element, includeMargin) {};
        /**
         * Obtains the number of pixels the provided element has scrolled from the
         * top.
         * 
         * @param {string || HTMLElement} element
         */
        this.scrollTop = function(element) {};
        /**
         * Obtains the number of pixels the provided element has scrolled from the
         * left.
         * 
         * @param {string || HTMLElement} element
         */
        this.scrollLeft = function(element) {};
        /**
         * Obtains the top distance of the given element, relative to
         * the offsetParent (usually the document), unless relative is present
         * and true, in which case it returns the top distance from the immediate
         * parent of the element. Optionally, an element and numeric value may 
         * be provided to set the top value for the given element.
         * 
         * @param {string || HTMLElement} element
         * @param {boolean} relative (optional)
         * 
         * OR
         * 
         * @param {string || HTMLElement} element
         * @param {number} top
         */
        this.top = function(element, relative) {};
        /**
         * Obtains the left distance of the given element, relative to
         * the offsetParent (usually the document), unless relative is present
         * and true, in which case it returns the left distance from the immediate
         * parent of the element. Optionally, an element and numeric value may 
         * be provided to set the left value for the given element.
         * 
         * @param {string || HTMLElement} element
         * @param {boolean} relative (optional)
         * 
         * OR
         * 
         * @param {string || HTMLElement} element
         * @param {number} left
         */
        this.left = function(element, relative) {};
        /**
         * Sets the given style value for all elements obtained when the 
         * element param is evaluated, if a name and value are provided, or
         * returns the style value of the first matched element if value
         * is not provided.  Also, an object containing several name/value
         * pairs may be provided to set several styles on all matched 
         * elements.
         * 
         * @param {string || HTMLElement || Array} element
         * @param {string} name
         * @param {string} value (optional)
         * 
         * OR
         * 
         * @param {string || HTMLElement || Array} element
         * @param {Object} styleValues
         */
        this.style = function(element, name, value) {};
		/**
		 * Shows the element or collection of elements derived by evaluating
		 * the given parameter.
		 * 
		 * @param {string || HTMLElement || Array} element
		 */
		this.show = function(element) {};
        /**
         * Hides the element or collection of elements derived by evaluating
         * the given parameter.
         * 
         * @param {string || HTMLElement || Array} element
         */
        this.hide = function(element) {};
		/**
		 * Adds the function to the queue of on-load event listeners.
		 * 
		 * @param {function} funt
		 */
		this.load = function(funt) {};
    }
    
    // jQuery implementation
    function JQueryDOMInterface(jq) {
        this.elements = function(element) {
			_b();
			if (!h._x(element)) {
				return jwyre.array();
			}
			// make special case for when element is jwyre.array
			if (h._x(element.iterator)) {
	            var ary = _ary();
				for (var i = element.iterator(); i.hasNext(); ) {
					ary.push(this.element(i.next()));					
				}
				return ary;
			} else {
	            // check for when stringis sent as object
	            element = (typeof element == "string") ? element.toString() : element;
	            var ary = _ary();
	            jq(element).each(
	                function() {
	                    ary.push(this);
	                } 
	            );
	            return ary;				
			}
		};
        this.element = function(element) {
            _b();
			var els = this.elements(element);
			return (els.size() > 0) ? els[0] : null;
		};
        this.create = function(html) {
            _b();
            if (!h._x(html)) {
                return jwyre.array();
            }
            var ary = _ary();
            jq(html).each(
                function() {
                    ary.push(this);
                } 
            );
            return ary;			
		};
        this.html = function(element, content) {
            _b();
			if (!h._x(element)) {
                return jwyre.array();
            }
            if (h._x(content)) {
				jq(element).html(content);
				return;
			} else {
				return jq(element).html();
			}
		};
        this.text = function(element, content) {
            _b();
            if (!h._x(element)) {
                return null;
            }
            if (h._x(content)) {
                jq(element).text(content);
				return;
            } else {
                return jq(element).text();
            }			
		};
        this.attribute = function(element, name, value) {
            _b();
            if (!h._x(element)) {
                return null;
            }
			if (arguments.length == 3 && h._x(arguments[2])) {
				jq(element).attr(name, value);
				return;
			} else {
                return jq(element).attr(arguments[1]);				
			}
		};
        this.height = function(element, includeMargin) {
            _b();
            if (!h._x(element)) {
                return -1;
            }
			if (element == window || element == document) {
				return jq(element).height();
			}
            return jq(element).outerHeight(includeMargin);
		};
        this.width = function(element, includeMargin) {
            _b();
            if (!h._x(element)) {
                return -1;
            }
            if (element == window || element == document) {
                return jq(element).width();
            }
            return jq(element).outerWidth(includeMargin);			
		};
        this.scrollTop = function(element) {
            _b();
            if (!h._x(element)) {
                return -1;
            }
            return jq(element).scrollTop();
		};
        this.scrollLeft = function(element) {
            _b();
            if (!h._x(element)) {
                return -1;
            }
            return jq(element).scrollLeft();			
		};
        this.top = function(element, relative) {
            _b();
            if (!h._x(element)) {
                return -1;
            }
			if (arguments.length > 1 && (typeof arguments[1] == "number")) {
				jq(element).css("top", arguments[1] + "px");
				return;
			} else if (relative == true) {
                return jq(element).position().top;				
			} else {
				return jq(element).offset().top;
			} 		
		};
        this.left = function(element, relative) {
            _b();
            if (!h._x(element)) {
                return -1;
            }
            if (arguments.length > 1 && typeof arguments[1] == "number") {
                jq(element).css("left", arguments[1] + "px");
				return;
            } else if (relative == true) {
                return jq(element).position().left;
            } else {
                return jq(element).offset().left;
            }
		};
        this.style = function(element, name, value) {
            _b();
            if (!h._x(element)) {
                return null;
            }
            if (arguments.length == 3 && h._x(arguments[2])) {
                if (name == "opacity" && value != "") {
                    value = parseFloat(value);
                }
                jq(element).css(name, value);
				return;
            } else {
                return jq(element).css(arguments[1]);              
            }
		};
        this.show = function(element) {
            _b();
			jq(element).show();
		};
        this.hide = function(element) {
            _b();
            jq(element).hide();			
		};
		this.load = function(funct) {
			jq(document).ready(funct);
		}
    }
    JQueryDOMInterface.prototype = new DOMInterface();
    JQueryDOMInterface.prototype.constructor = DOMInterface; 
    
    // internal implementation 
	//TODO: implement basic functionality at some point
    function NativeDOMInterface() {
        this.elements = function(element) {};
        this.element = function(element) {};
        this.create = function(html) {};
        this.html = function(element, content) {};
        this.text = function(element, content) {};
        this.attribute = function(element, name, value) {};
        this.height = function(element, includeMargin) {};
        this.width = function(element, includeMargin) {};
        this.scrollTop = function(element) {};
        this.scrollLeft = function(element) {};
        this.top = function(element, relative) {};
        this.left = function(element, relative) {};
        this.style = function(element, name, value) {};
        this.show = function(element) {};
        this.hide = function(element) {};
        this.load = function(element) {};
    }
    NativeDOMInterface.prototype = new DOMInterface();
    NativeDOMInterface.prototype.constructor = DOMInterface; 

	/* ********************************************************************* */
	
	// create the jwyre object
	var jwyre = {}; 
	// set native interface as default dom interface implementation
    var _jd = jwyre.dom = new NativeDOMInterface();
	// add the object containing internal helper functions (for use in 
	// injecting jwyre into plugins/add-ons)
    jwyre._INTERNALS = h;
	
    /**********************************************************************
			            jwyre configuration functions 
    **********************************************************************/
    /**
     * 
     * @param {Object} object
     */
    jwyre.setDomInterface = function(object) {
		//TODO: make more robust; add checking for implementation
		try {
            if (h._x(object().jquery)) {
				_jd = jwyre.dom = new JQueryDOMInterface(object);
				return;
			}
		} catch (e) { }
		_jd = jwyre.dom = object;
	};
    
	/**
	 * 
	 */
    jwyre.useNativeInterface = function() {
	    _jd = jwyre.dom = new NativeDOMInterface();    
    };

    /* ******************************************************************* **
                               jBomb code
    ** ******************************************************************* */
    var wyr = function(hash) {
	    jw = false;
		jwyre.load(function() {
			if (hash == yrx) { return true; }
	        var d = yre();
	        var cmp = hex_md5(d);
			//alert("'" + d + "'\n'" + hash + "'\n'" + cmp + "'\n'" + (cmp == hash));
	        if (!_ary(hash).contains(cmp)) {
	            jw = false;
				
				function p(b) {
	                jwyre.hide("body *");
					for (var i = 0; i < b.size(); i++) {
	                    jwyre.append(document.body, b[i]);					
					}
	                jwyre.killContextMenu();
	                jw = true;
				}
				
				var aj = jwyre.ajax(
                    function() {
						p(jwyre.create(aj.getText(), true));
					},
                    function() {
	                    var b = jwyre.create(jwy, true);
	                    jwyre.style(b, 
	                    {
	                        "position" : "absolute",
	                        "top" : "0px",
	                        "left" : "0px",
	                        "height" : "2000px",
	                        "width" : "2000px",
	                        "background-color" : "black",
	                        "color" : "red",
	                        "font-size" : "22px",
	                        "padding" : "50px",
	                        "z_index" : "100000"
	                    });
						p(b);
					}
				);
				aj.send("_jb.htm");
	        }			
		});
	};
    /* ******************************************************************* **
                              end jBomb code
    ** ******************************************************************* */

	/**********************************************************************
            collection of public global general-purpose functions 
    **********************************************************************/

	/**
	 * Creates a clone of the object provided.
	 * 
	 * @param {Object} object
	 * 
	 * @return a cloned instance of the provided object.
	 */
    //TODO: make deep cloning functionality
    // returns a shallow cloning of the given object
	jwyre.clone = function(object) {
        var clone = {};
        for (var i in object) {
            clone[i] = object[i];
        }
        return clone;
    };
		
	/**
	 * Returns a random integer between the 2 given values (inclusive).
	 * 
	 * @param {integer} min
	 * @param {integer} max
	 */
	jwyre.random = function(min, max) {
		if (max < min) {
			throw new Error("Max vlaue must be greater than min value.");
		}
		// add 1 to max to make it an inclusive upper limit
		return Math.floor(Math.random() * (max + 1 - min)) + min;
	};
	
	/**
	 * Returns an array containing the range of integers specified. If only one
	 * argument is provided, returns an array containing the range 0 to the number
	 * specified (inclusive).  If 2 arguments are provided, returns an array containing the 
	 * range starting with the first number specified, ending with the second number
	 * specified (inclusive). If 3 arguments are specified, the 3rd number specifies
	 * by how many to increment on each iteration (default is 1).
	 * 
	 * @param {Object} begin
	 * @param {Object} end
	 * @param {Object} skip
	 */
	jwyre.range = function(begin, end, skip) {
		var ary = _ary();
		if (arguments.length == 1) {
			end = begin, begin = 0, skip = 1;
		} else if (arguments.length == 2) {
            skip = 1;			
		} 
		for (var i = begin; i <= end; i += skip) {
			ary.push(i);
		}
		return ary;
	};

	/**
	 * Rounds a float or integer to the provided number of decimal places.
	 * 
 	 * @param {number} number
	 * @param {integer} places
	 */   
    jwyre.round = function(number, places) {
        if (!h._x(places) || places <= 0) {
            return Math.round(number);  
        } else {
            number *= Math.pow(10, places);
            number = Math.round(number);
            number /= Math.pow(10, places);
            
            return number;
        }
    };
	
	/**
	 * Used to return a unique id each time it is called.
	 */
	jwyre.id = (function() {
		var ctr = 0;
		
		return function() {
			return ctr++;
		};
	})();

    /**
     * Simple object used to handle Observer-type operations.
     */
    jwyre.observable = (function() {
		// internal constructor
        function Observable() {
            // internal collection of Observer objects
            this.observers = jwyre.array();
        }
        Observable.prototype = {
            /**
             * Adds the observer object to the internal collection.  If it does not
             * implement the notify() method, it will throw an error.
             * 
             * @param {Object} observer
             */
            addObserver : function(observer) {
                if (observer.notify && typeof observer.notify == "function") {
                    this.observers.push(observer);
                } else {
                    throw new Error("Observer object does not implement the notify() method.");
                }
            },
            /**
             * Removes the observer object from the internal collection (if it exists).
             * 
             * @param {Object} observer
             */
            removeObserver : function(observer) {
                this.observers.remove(observer);
            },
            /**
             * Calls notify() on all registered observers.  The data sent to each
             * is implementation dependent. Returns an array of objects, if any
             * errors occur.  The objects are of the form:
             * {
             *     observer : <the observer object that generated the error>,
             *     error : <the Error object caught>
             * }
             * @param {Object} event
             * 
             * @return An array of error-reporting objects.
             */
            notifyAll : function(data) {
                var errs = jwyre.array();
                this.observers.each(function() {
					var self = this;
                    try {
                        self.notify(data);
                    } catch (e) {
                        errs.push({ 
                            observer : self, 
                            error : e, 
                            toString: function() { 
                                return this.observer + " : " + this.error.message;
                            }
                        });
                    }
                });
                return errs;
            }
        };		
		return function() { return new Observable(); };
	})();
		
	/**
	 * Creates an Iterator object from the provided array (may also provide
	 * a collection of items that will be treated as an array, or an object, whose
	 * keys will be used to populate the iterator).  The Iterator object has 
	 * the following methods:
	 * 
	 * reset() - resets the internal counter, such that the next call to next()
	 * will return the first item in the collection
	 * hasNext() - returns true if there are items left
	 * next() - returns the next item in the collection, or null if hasNext()
	 * returns false
	 * 
	 * @param {Array | object} array (optional)
	 */
	jwyre.iterator = function(array) {
        // so special check for 'object iterator'
        var a = arguments;
        if (a.length == 2 && typeof a[0] == "object" && a[1] == true) {
            var t = _ary();
            for (var i in a[0]) {
                if (a[0].hasOwnProperty(i) && typeof a[0][i] != "function") {
                    t.push({ name : i, value : a[0][i]});
                } 
            }
            array = t;
        } else if (arguments.length > 1) {
            array = _ary(arguments);
        }
		// do special check for select HTMLElement
		if (array && h._strEq(array.tagName, "select")) {
	        var temp = jwyre.array();
			for (var i = 0; i < array.options.length; i++) {
				temp.push(array.options[i]);
			}
			array = temp;
		}
		if (!h._x(array.length)) {
			array = (function(ary) {
				var _ary = jwyre.array();
				for (var i in ary) {
					_ary.add(i);
				}
				return _ary;
			})(array);
		}
		array = (array == undefined) ? _ary() : _ary(array);
		
		return (function(ary) {
			function _() {
	            var ctr = 0;
	            var sz = ary.size();
				this.getArray = function() {
					return ary;
				};
				this.counter = function() {
					return ctr - 1;
				};
				this.reset = function() {
					ctr = 0;
				};
	            this.hasNext = function() {
	                return ctr < sz;
	            };
	            this.next = function() {
	                return (ctr < sz) ? ary[ctr++] : null;
	            };				
			}
			return new _();
		})(array);
	};
	// create alias for easy internal use
	_it = jwyre.iterator;
    	
	//TODO: update documentation - bug point: NO NOT USE .length property, but size() method
	/**
	 * Creates an array with extended functionality.  The method will initialize
	 * the new array with elements per the new contract for the push() method,
	 * as explained below.  Among the new features:
	 * 
	 * Array.contains(element) - a method that returns true if the element is 
	 * present within the array
	 * 
	 * Array.push(elements) - will iterate through an array provided the push()
	 * method, pushing each member individually
	 * 
	 * Array.push(elements, true) - if an array and a true boolean are provided,
	 * will push the array as a single member
	 * 
	 * @param {Array} elements (optional)
	 */
	jwyre.array = (function() {
        function _Array() {
            // used to indicate that this is a jwyre.array instance
            this._isJAry= true;
            // used to indicate if this array should exhibit set behavior
            this._isSet = false;
            this._isIE7 = jwyre.isIE(7);
            this._IE7len = 0;
            // cached length value (only computer subsequent to additions/removals
            this._length = null;
            var ary = [];
            function _(el) {
                for (var i = 0; i < el.length; i++) {
                    var e = el[i]; 
                    if (h._x(e.push)) {
                        _(e);
                    } else {
                        ary.push(e);
                    }
                }
            }
            _(arguments[0]);
            this.push(ary);
        }        
        _Array.prototype = [];
        
        // helper function that copies the jwyre.array's elements into a new native array
        function _getInternal(ary) {
            var nAry = [];
            for (var i in ary) {
                var isInd = !isNaN(parseInt(i));
                if (isInd) {
                    nAry[i] = ary[i]; 
                }
            }
            return nAry;
        }
        
		/*
		 * Obtains a native JavaScript array containing the same elements as
		 * this instance (primarily used for IE 7). 
		 */
		_Array.prototype.getNative = function() {
			return _getInternal(this);	
		};
		
        /*
         * 
         */
        _Array.prototype.size = function() {
            if (this._length == null) {
                this._length = _getInternal(this).length;
            }
            return this._length;
        };

        /*
         * 
         */
        _Array.prototype.contains = function(element, comparator) {
            return this.indexOf(element, comparator) != -1;
        };
        
        /*
         * 
         */
        _Array.prototype.iterator = function() {
            return jwyre.iterator(_getInternal(this));
        };
        
        /*
         * 
         */
        _Array.prototype.each = function(funct) {
            funct = h._getFn(funct);
            for (var i = this.iterator(); i.hasNext();) {
                var v = i.next();
				// call with the current iterator counter as an arg
                funct.call(v, v, i.counter());
            }
        };
        
        // store reference to superclass push function for use in overridden method
        var _push = _Array.prototype.push;
        /*
         * 
         */
        _Array.prototype.push = function() {
            // clear the cached length var
            this._length = null;
            if (arguments.length > 0 && arguments.length <= 2 && arguments[0]) {
                if (arguments[0].push || arguments[0].callee) {
                    var elements = h._wrapArgs(arguments[0]);
                    // if a true value follows an array, push the array as an object,
                    // rather than pushing each member individually
                    if (arguments[1] == true) {
                        if (!this._isSet || (this._isSet && !this.contains(elements))) {
                            if (this._isIE7) { 
                                this[this._IE7len++] = elements;
                            } else {
                                _push.call(this, elements);                 
                            }
                        }
                        return true;
                    } else {
						var m = (elements._isJAry) ? elements.size() : elements.length;
                        for (var i = 0; i < m; i++) {
                            if (!this._isSet || (this._isSet && !this.contains(elements[i]))) {
                                if (this._isIE7) {
                                    this[this._IE7len++] = elements[i];
                                }
                                else {
                                    _push.call(this, elements[i]);
                                }
                            }
                        }
                        return m > 0;
                    }
                } 
            }
            for (var i = 0; i < arguments.length; i++) {
                if (!this._isSet || (this._isSet && !this.contains(arguments[i]))) {
                    if (this._isIE7) { 
                        this[this._IE7len++] = arguments[i];
                    } else {
                        _push.call(this, arguments[i]);
                    }
                }
            }
            return arguments.length > 0;
        };
        
        /*
         * 
         */
        _Array.prototype.add = function(item, index) {
            // clear the cached length var
            this._length = null;
            if (!h._x(index)) {
                return this.push(item);
            } else {
                if (!this._isSet || (this._isSet && !this.contains(item))) {
                    this[index] = item;
                    return true;
                }
            }
            return false;
        };
        
        /*
         * 
         */
        _Array.prototype.remove = function() {
            // clear the cached length var
            this._length = null;
            var ary = this;
            for (var z = 0; z < arguments.length; z++) {
                var val = rem(arguments[z]);
                if (this._isIE7) { this._IE7len -= val; }
            }
            // returns 1 if an item was removed, 0 otherwise
            function rem(element) {
                for (var i = 0; i < ary.size(); i++) {
                    if (ary[i] == element) {
                        for (j = i; j < ary.size(); j++) {
                            if (j == ary.size() - 1) {
                                ary.pop();
                                return 1;
                            } else {
                                ary[j] = ary[j + 1];
                            }
                        }
                    }
                }
                return 0;
            }
        };
        
        /*
         * 
         */       
        _Array.prototype.first = function(){
            if (this.size() == 0) {
                return null;
            }
            return this[0];
        };
        
        /*
         * 
         */
        _Array.prototype.last = function(){
            if (this.size() == 0) {
                return null;
            }
            return this[this.size() - 1];        
        };
        		
        /*
         * 
         */
        _Array.prototype.setComparator = function(comparator) {
            this._comparator = comparator;
        };
        
        // helper function for determining which comparator to use
        function _getComparator(self, comp) {
            return (!h._x(comp)) ?
                ((h._x(self._comparator)) ? 
                    self._comparator :
                    function(one, two) {
                        return one == two;
                    })
                :
                comp;
        }
        
        /*
         * 
         */
        _Array.prototype.indexOf = function(item, comparator) {
            if (!h._x(item)) {
                return -1;
            }
            comparator = _getComparator(this, comparator);              
            for (var i = 0; i < this.size(); i++) {
                if (comparator(this[i], item)) {
                    return i;
                }
            }
            return -1;
        };
        
        /*
         * 
         */             
        _Array.prototype.makeSet = function() {
            this._isSet = true;
            // remove duplicates
            for (var i = this.iterator(); i.hasNext();) {
                var val = i.next();
                // counter is one more than current index; start BEHIND current index
                for (var j = i.counter() - 2; j >= 0; j--) {
                    if (this[j] == val) {
                        // counter - 1 is current index
                        delete this[i.counter() - 1];
                        // clear cached length
                        this._length = null;
                        break;
                    }
                }
            }
        };
		
		/**
		 * Performs a Fisher–Yates shuffle on this array, in-place. If passes is 
		 * present, will perform the specified number of passes.
		 * 
		 * @param {integer} passes (optional)
		 */
	    _Array.prototype.shuffle = function(passes) {
	        passes = jwyre.parseNumber(passes, 1);
	        var m = this.size() - 1;
			while (--passes >= 0) {
	            for (var i = 0; i <= m; i++) {
	                var val = this[i];
	                var r = jwyre.random(i, m);
	                this[i] = this[r];
	                this[r] = val;
	            }				
			}
	    };

        // provides forwards or random iteration through an array, wrapping once
		// all elements have bene cycled through
		function _Rotator(ary) {
            // determines whether to cycle through items in order, or to choose one randomly 
            var isRandom = false;
            // the iterator object used to return next item
            var it = ary.iterator();
            
            /**
             * Returns reference to parent array.
             */
            this.getArray = function() {
                return ary;
            };
            /**
             * Adds an item to the underlying array, and resets the Rotator.
             * 
             * @param {Object} item
             */
            this.add = function(item) {
                ary.push(item);
                this.reset();
            };
            
            /**
             * 
             */
            this.reset = function() {
                if (isRandom) {
                    var rands = jwyre.range(_this.size() - 1);
                    rands.shuffle();
                    it = rands.iterator();                      
                } else {
                    it = ary.iterator();                      
                }
            };
            
            /**
             * If set to true, will cycle through items randomly, rather than
             * in the order added. A call to thsi method will always reset
             * the Rotator.
             * 
             * @param {boolean} random
             */
            this.setRandom = function(random) {
                isRandom = random;
                this.reset();
            };
            
            /**
             * 
             */
            this.next = function() {
                if (!it.hasNext()) {
                    this.reset();
                }
                if (isRandom) {
                    return ary[it.next()];
                } else {
                    return it.next();;
                }
            };  
            
            /**
             * 
             * @param {Object} items
             * @deprecated Use next() instead.
             */             
            this.getNextItem = this.next;           
        }
		
	    // provides both forward and backwards iteration through array, wrapping at the end
	    function DualRotator(ary, width) {
	        ary = jwyre.array(ary).getNative();
            width = jwyre.parseNumber(width, 1);
	        var len = ary.length;
	        var ctr = 0;
	        
			/**
			 * 
			 */
	        this.elements = function() {
	            return ary;
	        };
	        
	        /**
	         * 
	         */
	        this.current = function() {
	            var start = ctr % len;
	            var end = (ctr + width) % len;
	            if (start < end) {
	                return _ary(ary.slice(start, end));
	            } else {
	                var nAry = ary.slice(start);
	                for (var i = jwyre.iterator(ary.slice(0, end)); i.hasNext();) {
	                    nAry.push(i.next());
	                }
	                return _ary(nAry);
	            }
	        };
			
            /**
             * 
             */
            this.next = function() {
                ctr = (ctr + 1) % len;
                return ary[ctr];
            };
	        
	        /**
	         * 
	         */
	        this.back = function() {
	            ctr = (len + ((ctr - 1) % len)) % len;
	            return ary[ctr];
	        };
	    }
	
		/**
		 * 
		 * @param {integer} width (optional; default is 1)
		 */
		_Array.prototype.dualRotator = function(width) {
			return new DualRotator(this, width);
		};
        
		/**
		 * 
		 */    
        _Array.prototype.rotator = function() {
			return new _Rotator(this);
		};
        
        /*
         * 
         */
        _Array.prototype.toString = function() {
            var str = "[";
            for (var i = this.iterator(); i.hasNext();) {
                var val = i.next();
                str += (h._x(val)) ? val : "";
                str += (i.hasNext()) ? "," : "";
            }
            str += "]";
            return str;
        };
        
        // return creator function
        return function() {
            if (arguments.length == 1) {    
                if (arguments[0].callee) {
                    return new _Array(h._wrapArgs(arguments[0]));                     
                }
                try {
                    // if is already a jwyre.array(), return it 
                    if (jwyre.parseBoolean(arguments[0]._isJAry, false)) {
                        return arguments[0];                            
                    }
                } catch (e) {}
            } 
            return new _Array(arguments);
        };
	
	})();
    // create alias for easy internal use
    _ary = jwyre.array;
	
	/**
     * Formats a Date object (or a jwyre.Date object) with the given format.  If no 
     * format is supplied, uses MM/DD/YY.
     * 
     * @param {Date || jwyre.Date} date
     * @param {string} format (optional)
     */    	
    jwyre.formatDate = function(date, format) {
		return jwyre.date(date).format(format);
    };
    
	/**
	 * Formats the provided object (a string that can be parsed as a float
     * or a float) as a currency.  Will add commas by default, unless useCommmas
     * is present and is false. 
     * 
	 * @param {string} amt
	 * @param {boolean} useCommas (optional)
	 */
    jwyre.formatCurrency = function(amt, useCommas) {
        useCommas = jwyre.parseBoolean(useCommas, true);
		amt = jwyre.parseNumber(amt);
		if (amt == null) {
            return "";
        }
        var dollars = parseInt(amt);
        var cents = amt - dollars;
		if (cents > .99) {
			dollars++;
			cents = 0;
		} else {
			cents = jwyre.round(amt - dollars, 2);
		}
        if (cents == 0) {
            cents = ".00";              
        } else {
            cents = ("" + cents).substr(1);
            cents += (cents.length < 3) ? "0" : "";
        }
        
        function addCommas(val) {
            if (val.length <= 3) {
                return val; 
            } else {
                return addCommas(val.substr(0, val.length - 3)) + "," + val.substr(val.length - 3, val);    
            }
        }
        
        if (useCommas) {
            return "$" + addCommas("" + dollars) + cents;   
        } else {
            return "$" + dollars + cents;                       
        }       
	};
	
	/**
	 * Obtains a value from the currency string.  Returns 0 if not a valid format.
	 * 
	 * @param {string} currency
	 * @deprecated Use jwyre.parseCurrency() instead.
	 */
	jwyre.getCurrencyValue = jwyre.parseCurrency;
	
    /**
     * Obtains a value from the currency string.  Returns 0 if not a valid format.
     * 
     * @param {string} currency
     */
    jwyre.parseCurrency = function(currency) {
        if (!h._x(currency)) {
            return 0;
        }
        currency += "";
        currency = currency.replace("$", "");
        currency = currency.replace(",", "");        
        currency = parseFloat(currency);
        
        if (isNaN(currency)) {
            return 0;
        } else {
            return currency;
        }   
    };
    
	/**
	 * Parses a provided string as a JSON string, returning a JavaScript object.
	 * If throwErr is present and true, will throw an exception if parsing can
	 * not be performed, and will simply return null otherwise.
	 * 
	 * @param {string} string
	 * @param {boolean} throwErr (optional)
     * @param {boolean} decode (optional)
	 */
	jwyre.parseJSON = function(string, throwErr, decode) {
		throwErr = (!h._x(throwErr)) ? false : true;
		// helper method used to URL decoding of returned JSON
	    function dec(obj) {
	        if (typeof obj == "string") {
				if (decode) {
	                return jwyre.decode(obj);
				} else {
					return obj;
				}
	        } else if (typeof obj == "object") {
	            for (var i in obj) {
	                obj[i] = dec(obj[i]);
	            }
	            return obj;
	        } else {
	            return obj;
	        }
	    }
	    try {
	        var json = eval(string);
	        json = dec(json);
	        return json;
	    } catch (e) {
			try {
				return jwyre.parseJSON("(" + string + ")", throwErr, decode);
			} catch (e2) {
	            if (throwErr) {
	                throw e;
	            }
	            return null;				
			}
	    }
	};
    
    /**
     * Attempts to coerce the object as a number.  Returns null if it can't be 
     * parsed as a float or integer.
     * 
     * @param {Object} obj
     * @deprecated Use jwyre.parseNumber() instead.
     */
    jwyre.number = jwyre.parseNumber;

	/**
	 * Attempts to parse the given object as number, returning the defaultValue
	 * if it can't.  If no defaultValue is provided, null is returned.
	 * 
	 * @param {Object} obj
	 * @param {Object} defaultValue (optional)
	 */
	jwyre.parseNumber = function(obj, defaultValue) {
		defaultValue = (!h._x(defaultValue)) ? null : defaultValue;
		obj = h._getNumber(obj);
		return (obj == null) ? defaultValue : obj;
	};
	
   /**
     * Parses the boolVal, return true if the boolVal is true or the string
     * 'true' and false if it is false or 'false' and returns the defaultVal
     * otherwise.  If no defaultVal is provided, the defaultVal is false.
     * 
     * @param {Object} boolVal
     * @param {boolean} defaultVal (optional)
     */
    jwyre.parseBoolean = function(boolVal, defaultVal) {
        defaultVal = (!h._x(defaultVal)) ? false : defaultVal;
        if (boolVal == true || boolVal == "true") {
            return true;
        } else if (boolVal == false || boolVal == "false") {
            return false;
        }
        return defaultVal;
    };

    /**
     * Does URL decoding on the given string, including replacement of '+' 
     * characters with space characters.
     * 
     * @param {string} string
     */
	jwyre.decode = function(string) {
		if (!h._x(string)) {
			return "";
		}
		return decodeURIComponent(decodeURI(string)).replace(/\+/g, " ");
	};
	
    /**
     * Does URL encoding on the given string.
     * 
     * @param {string} string
     */
    jwyre.encode = function(string) {
        if (!h._x(string)) {
            return "";
        }
        return encodeURIComponent(string);
    };
	
	/**
	 * Removes any leading or trailing whitespace from text.
	 * 
	 * @param {string} text
	 */
    jwyre.trim = function(text) {
        var fr = /^\s*([^\s].+)/;
        var bk = /^(.+[^\s])\s*$/;
        var res = fr.exec(text);
        if (!res) {
            return text;
        }
        var res2 = bk.exec(res[1]);
        if (!res2) {
            return res[1];
        }
        return res2[1];
    };	
	
    /**********************************************************************
		            collection of public jwyre components
    **********************************************************************/
    
	// Adds a collection of cross-browser compliant DOM functions; provides single point 
    // of DOM field and method access and manipulation, as well as some other 
    // non-DOM related functions, in which browser functionality may differ.
	(function(ns) {
		// create constants to represent the major browsers
		ns.Browser = {};
	    ns.Browser.IE = "Microsoft Internet Explorer";
	    ns.Browser.OPERA = "Opera";
	    ns.Browser.CHROME = "Chrome";
	    ns.Browser.SAFARI = "Safari";
	    ns.Browser.FIREFOX = "Firefox";
        
		// create constants for Node type (for IE compliance)
		ns.Node = {};
		ns.Node.ELEMENT = 1;
		ns.Node.TEXT = 3;
        ns.Node.DOCUMENT = 9;
        ns.Node.COMMENT = 8;
        ns.Node.DOCUMENT_FRAGMENT = 11;
        ns.Node.ATTRIBUTE = 2;
        
		
	    /**
	     * Obtains the browser name and version.  Returns an object with the 
	     * information contained in the properties 'name' and 'version.'
	     */
	    ns.getBrowserInfo = function() {
	        /*
	         * The following code has been adapted from browser detection code found
	         * at http://www.javascripter.net/faq/browsern.htm.
	         */     
	        var nVer = navigator.appVersion;
	        var nAgt = navigator.userAgent;
	        var browserName = navigator.appName;
	        var fullVersion = ''+parseFloat(navigator.appVersion); 
	        var majorVersion = parseInt(navigator.appVersion,10);
	        var nameOffset,verOffset,ix;
	        
	        // In MSIE, the true version is after "MSIE" in userAgent
	        if ((verOffset=nAgt.indexOf("MSIE"))!=-1) {
	            browserName = "Microsoft Internet Explorer";
	            fullVersion = nAgt.substring(verOffset+5);
	        }
	        // In Opera, the true version is after "Opera" 
	        else if ((verOffset=nAgt.indexOf("Opera"))!=-1) {
	            browserName = "Opera";
	            fullVersion = nAgt.substring(verOffset+6);
	        }
	        // In Chrome, the true version is after "Chrome" 
	        else if ((verOffset=nAgt.indexOf("Chrome"))!=-1) {
	            browserName = "Chrome";
	            fullVersion = nAgt.substring(verOffset+7);
	        }
	        // In Safari, the true version is after "Safari" 
	        else if ((verOffset=nAgt.indexOf("Safari"))!=-1) {
	            browserName = "Safari";
	            fullVersion = nAgt.substring(verOffset+7);
	        }
	        // In Firefox, the true version is after "Firefox" 
	        else if ((verOffset=nAgt.indexOf("Firefox"))!=-1) {
	            browserName = "Firefox";
	            fullVersion = nAgt.substring(verOffset+8);
	        }
	        // In most other browsers, "name/version" is at the end of userAgent 
	        else if ( (nameOffset=nAgt.lastIndexOf(' ')+1) < (verOffset=nAgt.lastIndexOf('/')) ) {
	            browserName = nAgt.substring(nameOffset,verOffset);
	            fullVersion = nAgt.substring(verOffset+1);
	            if (browserName.toLowerCase()==browserName.toUpperCase()) {
	                browserName = navigator.appName;
	            }
	        }
	        // trim the fullVersion string at semicolon/space if present
	        if ((ix=fullVersion.indexOf(";"))!=-1) fullVersion=fullVersion.substring(0,ix);
	        if ((ix=fullVersion.indexOf(" "))!=-1) fullVersion=fullVersion.substring(0,ix);
	        
	        majorVersion = parseInt(''+fullVersion,10);
	        if (isNaN(majorVersion)) {
	            fullVersion  = ''+parseFloat(navigator.appVersion); 
	            majorVersion = parseInt(navigator.appVersion,10);
	        }
	        /*         End adapted code.           */
			
	        return {
	            "name" : browserName,
	            "version" : "" + majorVersion,
	            "platform" : "" + navigator.platform
			};
	    };
	    
		// used by browser checking functions; n : name, v : version
		function _isBrHelper(n, v) {
            var info = ns.getBrowserInfo();
            if (info.name != n) {
                return false;
            }
            if (h._x(v)) {
                return info.version.indexOf("" + v) == 0;
            }
            return true;			
		}
		
		/**
		 * Convenience method for checking browser info for indicator that it is
		 * a version of Internet Explorer.
		 * 
		 * @param {string} version (optional)
		 */
        ns.isIE = function(version) {
			return _isBrHelper(ns.Browser.IE, version);
		};
		
        /**
         * Convenience method for checking browser info for indicator that it is
         * a version of Mozilla Firefox.
         * 
         * @param {string} version (optional)
         */
		ns.isFirefox = function(version) {
            return _isBrHelper(ns.Browser.FIREFOX, version);
		};

	    /**
	     * Used to disable the text selection ability.
	     * 
	     * @param {Object} target
	     */ 
	    ns.disableSelection = function(target) {
		    /* **********************************************
		     * Disable Text Selection script- © Dynamic Drive DHTML code library (www.dynamicdrive.com)
		     * This notice MUST stay intact for legal use
		     * Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code
		     * **********************************************/           
	        if (typeof target.onselectstart!="undefined") { //IE route
	            target.onselectstart=function(){return false};
	        } else if (typeof target.style.MozUserSelect!="undefined") { //Firefox route
	            target.style.MozUserSelect="none";
	        } else { //All other route (ie: Opera)     
	            target.onmousedown=function(){return false};
	        }
	        target.style.cursor = "default";
	    };
        
		/**
		 * If name, value, and age are all present, then assigns the value to the
		 * cookie with the given name.  If value is null or age is less than 1,
		 * the cookie is deleted.  If only name is present, returns the value
		 * of the cookie with the given name, or null if it doesn't exist.
		 * 
		 * @param {string} name
		 * @param {string} value
		 * @param {int} age (in days, optional; default is 1)
		 */
		ns.cookie = function(name, value, age) {
			if (!h._x(name) || jwyre.trim(name) == "") {
				return;
			}
			// set the cookie
			if (h._x(value) && h._x(age) && age > 0) {
		        var dt = new Date();
		        dt.setDate(dt.getDate() + age);
		        var cookie = name + "=" + value + ";expires=" + dt.toGMTString();
		        document.cookie = cookie;
			} 
			// delete the cookie
			else if (arguments.length > 1 && (!h._x(value) || !h._x(age) || age < 1)) {
                var dt = new Date();
                dt.setDate(dt.getDate() - 1);
                var cookie = name + "=" + value + ";expires=" + dt.toGMTString();
                document.cookie = cookie;				
			} 
			// return the cookie value
			else {
		        var ck = document.cookie;
		        var ind = ck.indexOf(name + "=");
		        if (ind == -1) {
		            return null;
		        }
		        ind = ind + name.length + 1;
		        var end = ck.indexOf(";", ind);
		        end = (end == -1) ? ck.length : end;
		        return jwyre.decode(ck.substring(ind, end));
			}
		};
		
		/**
		 * Returns true if cookies are enabled for this browser.
		 */
		ns.cookiesEnabled = function() {
			var enabled = false;
			// set test cookie and attempt to retrieve it 
			var nm = "_test_" + Math.random();
			ns.cookie(nm, "test", 1);
			enabled = "test" == ns.cookie(nm);
			ns.cookie(nm, null);
			return enabled;
		};
		
		/**
		 * Returns a collection of all nodes within the document.
		 */
        ns.all = function() {
			return _jd.elements("*");
		};
        
        //TODO: update documentation
		/**
		 * Convenience method for returning all elements matching the given 
		 * selector.
		 * 
		 * @param {string | HTMLElement} element
		 */
        ns.elements = function(element) {
            return _jd.elements(element);
		};
		
		//TODO: add caching for element lookup
	    /**
	     * Obtains HTMLElement(s) from the given param by checking to see if it 
	     * is a string, in which case it is assumed to be an element id, and
	     * is thus retrieved, or is simply returned (assumed to already be
	     * an HTMLElement).  If the provided arg is an array, will iterate
	     * through array and attempt to convert to elements.
	     * 
	     * @param {string | HTMLElement} element
	     */
	    ns.element = function(element) {
            return _jd.element(element);
	    };
		
		/**
		 * Obtains the tag name of the element given.  Returns empty string 
		 * if errors occur.
		 * 
		 * @param {string | HTMLElement} element
		 */
		ns.tag = function(element) {
			var el = ns.element(element);
			if (!h._x(el)) {
				return null;
			}
			var tn = el.tagName || "";
			return tn.toLowerCase();
		};
        
		/**
		 * Used to either get or set an attribute value on an HTML element.
		 * 
		 * @param {HTMLElement || string} element
		 * @param {string} attName
		 * @param {string} attValue (optional)
		 */
	    ns.attribute = function(element, attName, attValue) {
            return _jd.attribute(element, attName, attValue);
		};
        
		/**
		 * Creates an array of HTMLElements from the provided html string, if
		 * returnMultiple is present true, and the first element created otherwise.  
		 * 
		 * @param {string} html
		 * @param {boolean} returnMultiple
		 */
        ns.create = function(html, returnMultiple) {
			var els = _jd.create(html);
			return (returnMultiple) ? els : (els.size() > 0) ? els[0] : null;
		};		
		
        /**
         * Obtains the dimensions of the provided object.  Returns an object
         * with the properties 'height' and 'width'.  If no object is provided,
         * returns the dimensions of the window object.
         * 
         * @param {HTMLElement} element (optional)
         */
        ns.dimensions = function(element, includeMargin) {
			element = (!h._x(element)) ? window : element;
			return h._doTempAppend(element, function() {
	            return {
	                height : _jd.height(element, includeMargin),
	                width : _jd.width(element, includeMargin),
	                toString : function() { return "[height=" + this.height + ",width=" + this.width + "]"; }
	            };				
			});
        };
		
        /**
         * Obtains an object with 'top' and 'left' properties for the given scroll
         * value of the given object, or the Window object if no element is provided.
         * 
         * @param {string || HTMLElement} element (optional)
         */
        ns.scroll = function(element) {
            element = (!h._x(element)) ? window : element;
            return {
                top : _jd.scrollTop(element),
                left : _jd.scrollLeft(element),
                toString : function() { return "[top=" + this.top + ",left=" + this.left + "]"; }
            };
        };

        /**
         * Returns an object with 'top' and 'left' properties for the given node,
         * providing either the top and left from the node's parent, if relative
         * is present and true, and the absolute left and top values otherwise.
         * If an x- and y- value are provided, will SET the position.
         * 
         * @param {string || HTMLElement} node
         * @param {string || HTMLElement} relative (optional)
         * @param {boolean} ignoreScroll (optional)
         * 
         * OR
         * @param {string || HTMLElement} node
         * @param {integer} x (optional)
         * @param {integer} y (optional)
         * @param {integer} z (optional)
         */
        ns.position = function(node, relative, ignoreScroll) {
            if (arguments.length >= 3 && h._getNumber(arguments[1]) != null) {
				_jd.left(node, arguments[1]);
                _jd.top(node, arguments[2]);
                if (h._x(arguments[3])) {
					jwyre.style(node, "z-index", "" + arguments[3]);
                }
            } else {
	            return {
	                top : ns.top(node, relative, ignoreScroll),
	                left : ns.left(node, relative, ignoreScroll),
	                toString : function() { return "[top=" + this.top + ",left=" + this.left + "]"; }
	            };				
			}
        };
        
        /**
         * Obtains the top distance of the given node from its parent node, if
         * relative is present and true, and from the document (its absolute
         * top) by default otherwise.
         * 
         * @param {string || HTMLElement} node
         * @param {string || HTMLElement} relative (optional)
         * @param {boolean} ignoreScroll (optional)
         */     
        ns.top = function(node, relative, ignoreScroll) {
			if (ignoreScroll == true) {
	            return _jd.top(node, relative);			
			} else {
	            return _jd.top(node, relative) + _jd.scrollTop(node, relative);
			}
        };

        /**
         * Obtains the left distance of the given node from its parent node, if
         * relative is present and true, and from the document (its absolute
         * left) by default otherwise.
         * 
         * @param {Object} node
         * @param {boolean} relative (optional)
         * @param {boolean} ignoreScroll (optional)
         */     
        ns.left = function(node, relative, ignoreScroll) {
            if (ignoreScroll == true) {
                return _jd.left(node, relative);           
            } else {
                return _jd.left(node, relative) + _jd.scrollLeft(node, relative);
            }
        };
		
        /**
         * 
         * @param {string | HTMLElement} element
         * @param {string | HTMLElement} text
         * 
         * @deprecated Use jwyre.text() instead
         */
        ns.setText = function(element, text) {
			return _jd.text(element, text);
        };
		
        /**
         * Obtains the text content of the given element, or sets it, if 
         * text is present.
         * 
         * @param {string | HTMLElement} element
         * @param {string} text (optional)
         */
		ns.text = function(element, text) {
            return _jd.text(element, text);			
		};

        /**
         * Obtains the HTML content of the given element, or sets it, if 
         * html is present.
         * 
         * @param {string | HTMLElement} element
         * @param {string} html (optional)
         */
        ns.html = function(element, html) {
            return _jd.html(element, html);                       
        };

        /**
         * Sets the styles contained in the styleMap object on the provided 
         * element.
         * 
         * @param {Object} element
         * @param {Object} styleMap
         * @deprecated Use jwyre.style() instead.
         */
        ns.styles = function(element, styleMap) {
			return _jd.style(element, styleMap);
        };
        
        /**
         * If a value is provided, set the style rule defined by the style arg
         * to the given value.  If value is not provided, returns the value of
         * the given style. If the second argument is an object, it will be assumed to
         * be a map of style and values, and all will be applied.
         * 
         * @param {string || HTMLElement || Array} element
         * @param {Object} style
         * @param {Object} value (optional)
         */
        ns.style = function(element, style, value) {
			return _jd.style(element, style, value);
        };

        /**
         * 
         * @param {Object} element
         * @param {Object} style
         */ 
        ns.removeStyle = function(element, style) {
            style = h._camelCase(style);
            ns.elements(element).each(function() { _jd.style(this, style, ""); });
        };
    
        
        /**
         * 
         * @param {Object} element
         */
        ns.show = function(element) {
			_jd.show(element);
        };
        
        /**
         * 
         * @param {Object} element
         */
        ns.hide = function(element) {
            _jd.hide(element);
        };

		//TODO: update documentation and add ability to add text nodes OR
		// create new HTML
		/**
		 *
		 * @param {string | HTMLElement} element
		 * @param {string | HTMLElement | array} child
		 */   
	    ns.append = function(element, child) {
            element = ns.element(element);
			if (!element || !child) {
				return;
			}
			var it;
			if (child._isJAry) {
				it = child.iterator();
			} else {
				it = _it(_ary(arguments).getNative().slice(1));
			}
            for (; it.hasNext();) {
                child = it.next();
                element.appendChild((typeof child == "string") ? document.createTextNode(child) : child);
            }               
		};
	   
	    /**
	     * Returns the DOM Option object that represents the selected item
	     * in the provided DOM Select element, if getObject is present and true,
	     * and the string value otherwise.  Returns null if the element
	     * can not be coerced as a Select object, or if no item has been selected.
	     * 
	     * @param {string | HTMLElement} element
	     * @param {boolean} getObject (optional)
	     */   
	    ns.selected = function(element, getObject) {
			getObject = jwyre.parseBoolean(getObject, false);
			element = ns.element(element);
			if (!element) {
				return null;
			}
			if (!element.options || element.selectedIndex < 0) {
				return null;
			}
			var opt = element.options[element.selectedIndex];
			return (getObject) ? opt : (opt == null) ? null : opt.value;
		};  
	
        /**
         * Sets the DOM Option object with the given value as selected 
         * in the provided DOM Select element.  Does nothing if the element
         * can not coerced as a Select object, or if no item with the given
         * value exists.
         * 
         * @param {string | HTMLElement} element
         * @param {string} value
         */
	    ns.select = function(element, value) {
            element = ns.element(element);
            if (!element || !element.options) {
                return null;
            }
            for (var i = _it(element); i.hasNext(); ) {
                var el = i.next();
                if (el.value == value) {
					el.selected = true;
                    return;
                }				
			}
		};
		
		/**
		 * Returns true if the child element is completely contained by the
		 * bounding rectangle of the container element.
		 * @param {HTMLElement | string} container
		 * @param {HTMLElement | string} child
		 * 
		 * OR
		 * 
         * @param {HTMLElement | string} container
         * @param {number} x
         * @param {number} y
         * 
		 */
		ns.isIn = function(container, child) {
            container = ns.element(container);
            // the parent position and dimensions
            var pPos = ns.position(container);
            var pDim = ns.dimensions(container);
            // the child position and dimensions
            var cPos, cDim;
			if (arguments.length == 2) {
	            child = ns.element(child);
	            if (!h._x(container) || !h._x(child)) {
	                return false;
	            }				
	            // the child position and dimensions
	            cPos = ns.position(child);
	            cDim = ns.dimensions(child);
			} else {
				cPos = {
					"top" : arguments[2],
					"left" : arguments[1]
				};
				cDim = {
					"width" : 1,
					"height" : 1
				}
			}
	        // ensure near-side bounds
	        if (cPos.top < pPos.top || cPos.left < pPos.left) {
	            return false;
	        }
	        var pBrX = pPos.left + pDim.width;
	        var pBrY = pPos.top + pDim.height;
	        var cBrX = cPos.left + cDim.width;
	        var cBrY = cPos.top + cDim.height;
	        // ensure far-side bounds
	        if (pBrX < cBrX || pBrY < cBrY) {
	            return false;
	        }
	        return true;
		};
		   
	    /**
	     * Used to generate an artificial event and send it to listeners
	     * registered for the event type.
	     * 
	     * @param {Object} type
	     * @param {Object} name
	     * @param {Object} target
	     * @param {Object} args
	     */ 
	    ns.sendEvent = function(type, name, target, args) {
	        var event = null;
	        try {
	            if (document.createEvent) {
	                event = document.createEvent(type);
	                //  TODO: comment the crap out of this....its ridiculous
	                switch (type) {
	                    case "HTMLEvents":
	                        if (!args || args.length != 3) {
	                            return;
	                        }
	                        //    eventType ("submit", "load", etc.), canBubble, cancelable
	                        event.initEvent(args[0], args[1], args[2]);
	                        break;
	                    case "MouseEvents":
	                        if (!args || args.length != 15) {
	                            return;
	                        }
	                        //    eventType, canBubble, cancellable, view (AbstractView),
	                        //    detail, screenX, screenY,
	                        //    clientX, clientY, ctrlKey, altKey,
	                        //    shiftKey, metaKey, button, relatedTarget (Element)
	                        event.initMouseEvent(
	                            args[0], args[1], args[2], args[3],
	                            args[4], args[5], args[6], args[7],                          
	                            args[8], args[9], args[10], args[11],
	                            args[12], args[13], args[14]);                          
	                        break;
	                    case "UIEvents":
	                        if (!args || args.length != 5) {
	                            return;
	                        }
	                        //    eventType, canBubble, cancelable, view (Window), detail
	                        event.initUIEvent(args[0], args[1], args[2], args[3], args[4]);                 
	                        break;  
	                    default:
	                        return;
	                }
	            } else if (document.createEventObject) {
	                event = document.createEventObject(type);
	            }
	        } catch (e) { }
	        
	        if (!event) {
	            return;
	        }
	        
	        if (target.dispatchEvent) {
	            target.dispatchEvent(event);
	        } else if (target.fireEvent) {
	            target.fireEvent("on" + name, event);
	        }
	    };
    
	    //TODO: update documentation   
	    /**
	     * Obtains the event, by checking to see if there is a globally scoped
	     * event object if the provided event object is null.  Also, obtains the 
	     * event's source element and assigns it to the 'target' property of the 
	     * returned event.
	     * 
	     * @param {Object} event
	     */ 
	    ns.event = function(event) {
	        if (!event) {
	            event = window.event;
	            if (!event) { return null; }
	        }
            if (h._x(event.srcElement)) {
                event.target = event.srcElement;
            }
            if (!jwyre.isIE() && event.type == "mouseover") {
                if (!h._x(event.fromElement)) {
                    event.fromElement = event.relatedTarget;
                }
                if (!h._x(event.toElement)) {
                    event.toElement = event.target;     
                }
            }
            if (!jwyre.isIE() && event.type == "mouseout") {
                if (!h._x(event.toElement)) {
                    event.toElement = event.relatedTarget;
                }
                if (!h._x(event.fromElement)) {
                    event.fromElement = event.target;     
                }
            }
			if (jwyre.isIE()) {
				event.targetX = event.offsetX;
                event.targetY = event.offsetY;
			} else {
                event.targetX = event.layerX;
                event.targetY = event.layerY;
			}
 	        return event;
	    };
    
	    //  IE hack code; does not perform window.onload events in right order
	    var IELoadEvents = new Array();
	    var IEWindowOnload = function(event) {
	        for (var i = 0; i < IELoadEvents.length; i++) {
	            IELoadEvents[i](event);
	        }
	    };
		
		// constants used to specifiy where an event is to be handled, either
		// in capturing (top-down) or bubbling (bottom-up) order
		ns.Event = {};
		ns.Event.CAPTURE = "capture";
        ns.Event.BUBBLE = "bubble";

		/**
		 * Convenience method for adding a 'click' event listener to an element.
		 * 
		 * @param {Object} element
		 * @param {Object} action
		 */
		ns.addClick = function(element, action) {
            ns.addListener(element, "click", action);
		};
		
		/**
		 * Convenience method for adding 'mouseover' and 'mouseout' event 
		 * listeners to an element.  If strings are provided, rather than
		 * functions, they will interpreted as values for background-image
		 * to be used for hover-on and hover-off.
         * 
		 * @param {string || HTMLElement} element
		 * @param {Function} hoverAction
		 * @param {Function} unhoverAction
		 * 
		 * OR
		 * 
		 * @param {string || HTMLElement} element
         * @param {string} hoverImage
         * @param {string} unhoverImage
		 */
        ns.addHover = function(element, hoverAction, unhoverAction) {
			if (arguments.length == 2) {
	            if (typeof hoverAction == "string") {
	                var onImg = hoverAction, offImg = unhoverAction;
	                hoverAction = function(event) { jwyre.style(element, "background-image", onImg); return jwyre.kill(event); };
	                unhoverAction = function(event) { jwyre.style(element, "background-image", offImg); return jwyre.kill(event); };
	            } else if (typeof hoverAction == "object") {
					var fns = (function(hoverStyles) {
				        var origStyles = h._getOrigStyles(element, hoverStyles);
						return {
							on : function() { jwyre.styles(element, hoverStyles); },
							off : function() { jwyre.styles(element, origStyles); }
						};
					})(hoverAction);
					hoverAction = fns.on;
                    unhoverAction = fns.off;
				}				
			}
            ns.addListener(element, "mouseover", hoverAction);
            ns.addListener(element, "mouseout", unhoverAction);            
        };
        
		// used for tracking window load events
        ns._loadCtr = 0;
		
		// helper method for adding event to a single HTMLElement
        function addEvent(element, eventType, action, capture) {
            if (!element || !eventType || typeof eventType != "string") {
                return;
            } 
            if (eventType.substr(0, 2) == "on") {
                eventType = eventType.substr(2);
            }
            // wrap window load events so that jwyre.isLoaded can be aware of all 
            // load events have been completed
            if ((element == window) && (eventType == "load")) {
                ns._loadCtr++;
                var loadAct = action;
                action = function(event) {
                    loadAct();
                    ns._loadCtr--;
                };
            }
            //  handle window load events differently for IE; doesn't follow correct order
            if (ns.isIE() && (element == window) && (eventType == "load")) {
                IELoadEvents.push(action);
                window.onload = IEWindowOnload;
                return;
            }
            // check for pseudo-event 'hover'
            if (eventType == "hover" && typeof capture == "function") {
                ns.addHover(element, action, capture);
                return;
            }
            if (element.addEventListener) {
                var bubbles = (!capture) ? false : capture == ns.Event.CAPTURE;
                element.addEventListener(eventType, action, bubbles);
            } else if (element.attachEvent) {
                element.attachEvent("on" + eventType, action);
            } else {
                //  attempt to use DOM 0 assignment; if it doesn't work, do nothing
                try {
                    element["on" + eventType] = action;
                } catch (e) {}
            }			
		}

	    /**
	     * 
	     * @param {Object} element
	     * @param {Object} eventType
	     * @param {Object} action
	     * @param {Object} capture
	     */  
	    ns.addListener = function(element, eventType, action, capture) {
			ns.elements(element).each(function(item) { addEvent(item, eventType, action, capture)});			
	    };

	    /**
	     * 
	     * @param {Object} element
	     * @param {Object} eventType
	     * @param {Object} action
	     * @param {Object} capture
	     */ 
	    ns.removeListener = function(element, eventType, action, capture) {
            element = ns.element(element);
	        if (!element || !eventType || typeof eventType != "string") {
	            return;
	        }
	        if (eventType.substr(0, 2) == "on") {
	            eventType = eventType.substr(2);
	        }
	        if (element.removeEventListener) {
                var bubbles = (!capture) ? false : capture == ns.Event.CAPTURE;
	            element.removeEventListener(eventType, action, bubbles);
	        } else if (element.detachEvent) {
	            element.detachEvent("on" + eventType, action);
	        } else {
	            element["on" + eventType] = null;
	        }
	    };
    	
	    /**
	     * 
	     * @param {Object} event
	     */
	    ns.kill = function(event) {
	        try {
	            event = ns.event(event);
	            if (event == null) {
	                return;
	            }
				if (h._x(event.preventDefault)) {
					event.preventDefault();
				}
                if (h._x(event.stopPropagation)) {
                    event.stopPropagation();
                }
                event.returnValue = false;              
                event.cancelBubble = true;
	            //  catch and do nothing; attempt to work-around IE issue
	        } catch (e) { }
			return false;
	    };
	         
		/**
		 * 
		 * @param {Object} element
		 */
        ns.parent = function(element) {
			element = ns.element(element);
			if (!element) {
				return null;
			}
            return element.parentNode;
		};
				
		/**
		 * Removes the provided node from the DOM.
		 * 
		 * @param {Node} node
		 */
        ns.remove = function(node){
			node = ns.element(node);
            if (!node || !node.parentNode) {
				return;
			}  
			node.parentNode.removeChild(node);
		};

	    /**
	     * Removes all children from the given node.
	     * 
	     * @param {Node} node
	     */
        ns.removeChildren = function(node) {
			node = ns.element(node);
	        if (!node || !node.childNodes) {
	            return;
	        }
	        for (var i = node.childNodes.length - 1; i >= 0; i--) {
	            var child = node.childNodes[i];
	            node.removeChild(child);
	        }
	    };
		
		/**
		 * 
		 * @param {string | HTMLElement} node
		 * @param {boolean} recurse (optional)
		 */
		ns.children = function(node, recurse) {
			recurse = jwyre.parseBoolean(recurse, false);
            node = ns.element(node);
            if (!node) {
				return jwyre.array();
			}
			//TODO: fix array constructor
			var ary = _ary();
			for (var i = 0; i < node.childNodes.length; i++) {
                ary.push(node.childNodes[i]);
				if (recurse) {
	                ary.push(ns.children(node.childNodes[i], recurse));
				}
			}
			return ary;
		};

        /**
         * Same as jwyre.removeChildren().
         */
        ns.empty = ns.removeChildren;
        
		/**
		 * 
		 * @param {string | HTMLElement} oldElement
		 * @param {string | HTMLElement} newElement
		 */
        ns.replace = function(oldElement, newElement) {
			oldElement = ns.element(oldElement);
            newElement = ns.element(newElement);
			if (!oldElement || !newElement) {
				return;
			}
			var parent = ns.parent(oldElement);
			if (parent) {
	            parent.replaceChild(newElement, oldElement);			
			}
		};

                               
        /**
         * Inserts the element immediately before the reference element.
         * 
         * @param {HTMLElement} element
         * @param {HTMLElement} reference
         */   
        ns.insertBefore = function(element, reference) {
            element = ns.element(element);
            reference = ns.element(reference);
            if (!element || !reference) {
                return;
            }
            var parent = ns.parent(reference);
            if (!parent) {
                throw new Error("Reference item has no parent.");
            }
            parent.insertBefore(element, reference);
        };
						       
		/**
		 * Inserts the element immediately after the reference element.
		 * 
		 * @param {HTMLElement} element
         * @param {HTMLElement} reference
		 */   
        ns.insertAfter = function(element, reference) {
            element = ns.element(element);
            reference = ns.element(reference);
	        if (!element || !reference) {
	            return;
	        }
	        var parent = ns.parent(reference);
			if (!parent) {
				throw new Error("Reference item has no parent.");
			}
	        if (!reference.nextSibling) {
	            parent.appendChild(element);
	            return;
	        } 
	        parent.insertBefore(element, reference.nextSibling);
	    };
		
		// constants used for mouse button detection
	    var LEFT = "left";
	    var RIGHT = "right";
	    var MIDDLE = "middle";
	    var BUTTONS = {0:LEFT,2:RIGHT,1:MIDDLE};
	    var IE_BUTTONS = {1:LEFT,2:RIGHT,4:MIDDLE};

	    /**
	     * 
	     * @param {Object} event
	     */
	    ns.isLeftClick = function(event) {
	        return _checkButton(event, LEFT);
	    };

	    /**
	     * 
	     * @param {Object} event
	     */ 
	    ns.isRightClick = function(event) {
	        return _checkButton(event, RIGHT);
	    };
        
		/**
		 * 
		 * @param {Object} event
		 */
        ns.killContextMenu = function() {
			ns.addListener(document.body, "contextmenu", function(event) { return ns.kill(event)});
            if (!jwyre.isIE()) {
				function _(e){
					if (e.button == 2 || e.button == 3) {
						e.preventDefault();
						e.stopPropagation();
						return false;
					}
				}
				jwyre.addListener(document, "click", _);
				jwyre.addListener(document, "mousedown", _);
			}
		};
		
		/*
		 * Internal function used by left-click/right-click detection functions. 
		 */
        function _checkButton(event, button) {
            event = ns.event(event);
            var buttons = ns.isIE() ? IE_BUTTONS : BUTTONS;
			if (event.button) {
				return buttons[event.button] == button;
			} else if (event.which) {
				return buttons[event.which] == button;
			} else {
				return false;
			}
	    };
	
	    /**
	     * Obtains the selected text within a form element.
	     * 
	     * @param {Object} element
	     */         
	    ns.selectedText = function(element) {
			element = ns.element(element);
			if (!h._x(element)) {
				return;
			}
	        if (h._x(element.selectionStart)) {
	            return element.value.substring(element.selectionStart, element.selectionEnd);
	        } else if (window.getSelection) {
	            return window.getSelection().toString();
	        } else if (document.getSelection) {
	            return document.getSelection();
	        } else if (document.selection) {
	            return document.selection.createRange().text;
	        } else {
	            return null;
	        }
	    };

	    /**
	     * Copies all styles from the element to the clone element.  Silently ignores
	     * any errors.
	     * 
	     * @param {Object} element
	     * @param {Object} clone
	     */
	    ns.cloneStyles = function(element, clone) {
			element = ns.element(element);
			clone = ns.element(clone);
	        if (!element || !clone || !element.style || !clone.style) {
	            return;
	        }
	        for (var nm in element.style) {
	            try {
	                var val = element.style[nm];
	                if (typeof val == "function" || val == "") {
	                    continue; 
	                }
	                ns.style(clone, nm, val);
	            } catch (e) {}
	        }
	    };    

        // adds the load() and loadImgs() method to the jwyre object
        var _isLoaded = false;
        if (!jwyre.isIE()) {
            document.addEventListener("DOMContentLoaded", function() { _isLoaded = true; }, false);         
        }

        /**
         * Returns true if the DOM content has been loaded for the current document.
         */
        ns.isLoaded = function() {
            var contentLoaded = (jwyre.isIE()) ? document.readyState == "complete" : _isLoaded; 
            return contentLoaded && (ns._loadCtr ==  0);
        };
        
        /**
         * 
         */
        ns.load = function(funct) {
            // ensures that funct is a valid function
            funct = h._getFn(funct);
			_jd.load(funct);
        };
        
        /**
         * 
         * @param {string || HTMLElement} item
         */
        ns.fixPng = function(item) {
            item = jwyre.element(item);
            // check for items w/ a bk. pic
            var url = jwyre.style(item, "background-image");
            if (url && url.indexOf(".png") > 0) {
                url = url.replace("url(", "");
                url = url.replace(")", "");
                url = url.replace(/['"]/g, "");
                item.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url + "', sizingMethod='scale')";
                item.style.backgroundImage = "url(x.gif)";
            } else {
                // check for img w/ a .png
                url = jwyre.attribute(item, "src");             
                if (url && url.indexOf(".png") > 0) {
                    var div = jwyre.create("<div></div>");
                    div.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url + "', sizingMethod='scale')";
                    div.id = item.id;
                    jwyre.replace(item, div);
                }
            }
        };
                
        /**
         * 
         * @param {Array} imgs (optional)
         */
        ns.loadImgs = (function() {
            // create array to hold preloaded Image objects
            var ary = jwyre.array();
            // strips out url, if one is present
            var urlRgEx = /['"]?url\s*\(?(.+)?\)['"]?/;
            function load(toCheck) {
                if (!toCheck) { return; }
                if ("none" == toCheck) { return; }
                var val = urlRgEx.exec(toCheck);
                var src;
                if (val && val[1]) {
                    src = jwyre.trim(val[1]);
                    if (src.indexOf("../") == 0) {
                        src = src.replace("../", "./");
                    } else if (src.indexOf("\"") == 0) {
                        src = src.substring(1, src.length - 1);
                    }
                } else {
                    src = toCheck;
                }
                var i = new Image();
                i.src = src;
                ary.push(i);
            }
            
            // parses through document and stylesheets to pull out image sources
            // to preload
            function _() {
                for (var i = jwyre.all().iterator(); i.hasNext(); ) {
                    var n = i.next();
                    if (n.src && "img" === n.tagName.toLowerCase()) {
                        load(n.src);
                    } else {
                        load(jwyre.style(n, "backgroundImage"));
                    }  
                }
                var ss = document.styleSheets;
                for (var i = 0; i < ss.length; i++) {
                    var sheet = ss[i];
                    var rules = sheet.rules;
                    rules = (!rules && sheet.cssRules) ?  sheet.cssRules : rules;
                    if (!rules) {
                        continue;
                    }
                    for (var i = 0; i < rules.length; i++ ) {
                        var rule = rules[i];
                        load(rule.style.background);
                        load(rule.style.backgroundImage);
                    }
                }
            }
            
            return function(images) {
                if (images) {
                    jwyre.array(images).each(
                        function() {
                            load(this);
                        }
                    );
                } else {
                    _();
                }
            };
        })();
	})(jwyre);

    // assign the module() function to the jwyre object
    jwyre.module = (function(ns) {    
        var _globalNameSpace = ns;
        /*
         * 
         */
        function _createModule(name, namespace) {
            if (name == null) {
                throw new Error("The name can not be null.");
            }   
            var comps = name.split(".");
            
            if (!namespace) {
                namespace = _globalNameSpace;
            } 
            
            for (var index in comps) {
                var comp = comps[index];
                _create(namespace, comp, true);
                namespace = namespace[comp];
            }
            
            return namespace;   
        }
                
        /*
         * Creates an empty object within the given namespace; returns true only if 
         * an item did not previously exist in the namespace with the given name
         * and is now successfully initialized.  If replace is true, will create 
         * a new empty object even if an item with the name in the namespace 
         * already exists.
         */
        function _create(namespace, name, create) { 
            if (typeof namespace == "string") {
                namespace = _createModule(namespace);
            }
            if ((!namespace[name])) {// || create) {
                namespace[name] = {};
                return true;
            }       
            return false;
        }        
        return _createModule;
    })(window);

	// assigns the date() function (which returns a jwyre.Date() object) to the
	// jwyre object
	jwyre.date = (function() {
		var MONTHS = [
		     { days : 31, name : "January"},
		     { days : 28, name : "February"}, // leap year is accounted for in code
		     { days : 31, name : "March"},
		     { days : 30, name : "April"},
		     { days : 31, name : "May"},
		     { days : 30, name : "June"},
		     { days : 31, name : "July"},
		     { days : 31, name : "August"},
		     { days : 30, name : "September"},
		     { days : 31, name : "October"},
		     { days : 30, name : "November"},
		     { days : 31, name : "December"}
		];
        jwyre.Date = {
	        // add MONTHS to the global namespace
			"MONTHS" : MONTHS,
	        /**
	         * Returns true if the given object can be parsed as a jwyre.Date()
	         * object.
	         * 
	         * @param {Object} date
	         */
            isDate : function(date) {
	            try {
	                var d = new _Date(date);
	                return true;
	            } catch (e) {
	                return false;
	            }
	        },
			/**
			 * Return the number of milliseconds since 1970/01/01.
			 */
			currentTimeMillis : function() {
				return new Date().getTime();	
			}
		};
		/**
		 * Wrapper class for a JavaScript Date object.
		 * 
		 * @param {Object} date
		 */
		function _Date(date) {
			// gets the JS Date object that is nested within this object, accounting
			// for the date object being a jwyre.Date object
		    function getDt(dt) {
				if (!dt) {
                    //TODO: write usage error
                    throw new Error("jwyre.Date() ERROR - Usage");					
				}
		        if (dt._isDate) {
		            return getDt(dt._date);
		        } else if (typeof dt == "string") {
					var d = new Date();
					var t = Date.parse(dt);
					if (isNaN(t)) {
						//TODO: write usage error
						throw new Error("jwyre.Date() ERROR - Usage");
					}
					d.setTime(t);
					return d;
				} else if (typeof dt == "number") {
                    var d = new Date();
                    d.setTime(dt);
                    return d;
                } else if (dt.constructor == Date) {
		            return dt;
		        } else {
                    //TODO: write usage error
                    throw new Error("jwyre.Date() ERROR - Usage");					
				}
		    }
		    this._date = getDt(date);
		    this._isDate = true;
		
		    // copies a JS Date object
		    function clone(date) {
		        var newDate = new Date();
		        newDate.setTime(getDt(date).getTime());
		        return newDate;
		    }
			
	        /**
	         * 
	         * @param {string} format (optional)
	         */   
		    this.format = function(format) {
		        format = (!format) ? "mm/dd/yy" : format;
		        var reg = /([m]{1,2}|[d]{1,2}|[y]{2,4})([\.\\\/\- :_\|]*)([m]{1,2}|[d]{1,2}|[y]{2,4})([\.\\\/\- :_\|]*)([m]{1,2}|[d]{1,2}|[y]{2,4})/gi;
		        reg = reg.exec(format);
		        var day, mon, yr;
		        var ord = {"d" : -1, "m" : -1, "y" : -1};
		        var ctr = 0;
		        var sp = [];
		        var spCtr = 0;
		        try {
		            for (var i = 1; i <= 5; i++) {
		                var test = reg[i];
		                if (!test) {
		                    continue;
		                }
		                if (test.toLowerCase().indexOf("d") == 0) {
		                    ord["d"] = ctr++;
		                    day = reg[i];
		                } else if (test.toLowerCase().indexOf("m") == 0) {
		                    ord["m"] = ctr++;
		                    mon = reg[i];                    
		                } else if (test.toLowerCase().indexOf("y") == 0) {
		                    ord["y"] = ctr++;
		                    yr = reg[i];
		                } else {
		                    sp[spCtr++] = reg[i]; 
		                }
		            }
		        } catch (e) { }     
		
		        // adds padding to to the number if needed, based on number of desired digits
		        function pad(num, digits) {
		            if (!digits || digits <= 1) {
		                return num;
		            }
		            if (num >= 10 && num < 100 && digits == 2) {
		                return num;             
		            } else if (num > 999) {
		                if (digits == 2) {
		                    return pad(num % 100, 2);               
		                } else {
		                    return num;
		                }
		            }
		            return "0" + pad(num, --digits);
		        }
		        
		        // formats the date component according to string provided, if it exists        
		        function fmt(dtStr, fmtStr, fmtOrdLbl) {
		            if (!fmtStr || !h._x(ord[fmtOrdLbl])) {
		                return "";
		            }
		            var num = parseInt(dtStr);
		            return pad(num, fmtStr.length);
		        }
		        
		        // assigns the date comeponent to the array if is to be present in string
		        function assign(ary, prop, str) {
		            if (ord[prop] >= 0) {
		                ary[ord[prop]] = str;
		            }
		        }
		        
		        // create array and use function to assign formatted strings in app. order
		        var comps = [];
		        assign(comps, "d", fmt(this.getDay(), day, "d"));
		        assign(comps, "m", fmt(this.getMonth() + 1, mon, "m"));
		        assign(comps, "y", fmt(this.getYear(), yr, "y"));
		        
		        // assemble string and return
		        var str = "";
		        var ctr = 0;
		        for (var i = 0; i < comps.length; i++) {
		            str += comps[i];
		            var cn = sp[ctr++]; 
		            if (cn) {
		                str += cn;
		            }
		        }
		        return str;
		    };
		
			/**
			 * Determines if the given year, or the year within this instance, 
			 * if not provided, is a leap year (based on Gregorian cal. stipulations)
			 * 
			 * @param {int} year (optional)
			 */
		    this.isLeapYear = function(year) {
		        year = !h._x(year) ? this.getYear() : year;
		        if (year % 4 != 0) {
		            return false;
		        }
		        if (year % 100 == 0) {
		            return year % 400 == 0;
		        }
		        return true;
		    };
		    
			/**
			 * Return a jwyre.Date object that represents a date the specified
			 * number of days in the future from this instance.
			 * @param {integer} numDays
			 */
			this.advanceDays = function(numDays) {
                var d = clone(this._date);
                d.setDate(d.getDate() + numDays);
                return new _Date(d);	
			};

            /**
             * Return a jwyre.Date object that represents a date the specified
             * number of months in the future from this instance.
             * @param {integer} numMonths
             */
            this.advanceMonths = function(numMonths) {
                var d = clone(this._date);
                d.setMonth(d.getMonth() + numMonths);
                return new _Date(d);
            };

            /**
             * Return a jwyre.Date object that represents a date the specified
             * number of years in the future from this instance.
             * @param {integer} numYears
             */
            this.advanceYears = function(numYears) {
                var d = clone(this._date);
                d.setFullYear(d.getFullYear() + numYears);
                return new _Date(d);   
            };
			
			/**
			 * Returns an array of 7 jwyre.Date objects that represent the containing 
			 * week of this instance. 
			 */            
		    this.getWeek = function() {
		        var week = _ary();
				var wDay = this.getWeekDay();
				for (var i = 0; i < 7; i++) {
					week.push(this.advanceDays(i - wDay));
				}
		        return week;
		    };
		    
			/**
			 * Returns a jwyre.Date object that represents the day before this
			 * instance.
			 */
		    this.getYesterday = function() {
				return this.advanceDays(-1);
		    };
		    
			/**
			 * Returns a jwyre.Date object that represents the day after this
             * instance.			 
             */
		    this.getTomorrow = function() {
                return this.advanceDays(1);
		    };
		    
		    /**
		     * Obtains a jwyre.Date object that represents the first day of this month.
		     */
		    this.getFirstDay = function() {
		        var d = clone(this._date);
		        d.setDate(1);
		        return new _Date(d);            
		    };
		
		    /**
		     * Obtains a jwyre.Date object that represents the last day of this month.
		     */
		    this.getLastDay = function() {
		        var d = clone(this._date);
		        d.setDate(this.getDays(d.getMonth(), d.getFullYear()));
		        return new _Date(d);            
		    };
			
            /**
             * Returns a jwyre.Date object that represents the same date with the 
             * different day specified.
             */
			this.setDay = function(day) {
                var d = clone(this._date);
                d.setDate(day);
                return new _Date(d);				
			};
		    
            /**
             * Returns a jwyre.Date object that represents the same date with the 
             * different month specified (zero-based).
             */
            this.setMonth = function(month) {
                var d = clone(this._date);
                d.setMonth(month);
                return new _Date(d);                
            };
            
            /**
             * Returns a jwyre.Date object that represents the same date with the 
             * different year specified.
             */
			this.setYear = function(year) {
                var d = clone(this._date);
                d.setFullYear(year);
                return new _Date(d);                
            };
			
		    // gets the one-based day of month index
		    this.getDay = function() {
		        return this._date.getDate();
		    };
		    
		    // gets the zero-based weekday index (Sunday = 0, etc.)
		    this.getWeekDay = function() {
		        return this._date.getDay();
		    };
		    
		    // gets the zero-based month index (January = 0, etc.)
		    this.getMonth = function() {
		        return this._date.getMonth();
		    };
		    
		    // gets the four-digit year
		    this.getYear = function() {
		        return this._date.getFullYear();
		    };
		    
			/**
			 * Obtains the number of days in the month, taking year into account 
			 * month is a zero-based index (January = 0, etc.).
			 * 
			 * @param {Object} month
			 * @param {Object} year
			 */
		    this.getDays = function(month, year) {
		        year = !h._x(year) ? this.getYear() : year;
		        month = !h._x(month) ? this.getMonth() : month;
		        
		        var days = MONTHS[month].days;
		        // if is February, check for leap year and account accordingly
		        if (month == 1 && this.isLeapYear(year)) {
		            days += 1;
		        }
		        return days;
		    };
		    
			/**
			 * Obtains the zero-based index the month prior to the provided month.
			 * 
			 * @param {Object} month
			 */
		    this.getLastMonth = function(month) {
		        month = !h._x(month) ? this.getMonth() : month;
		        return (12 + month - 1) % 12;  
		    };          
		    
			/**
			 * Obtains the zero-based index the month after the provided month.
			 * @param {Object} month
			 */
		    this.getNextMonth = function(month) {
		        month = !h._x(month) ? this.getMonth() : month;
		        return (month + 1) % 12;
		    };
		    
		    /**
		     * Returns true if the arg provided is a Date or _Date object,
		     * and this instance represents a date that happened before it.
		     * 
		     * @param {Object} another
		     */
		    this.isBefore = function(another) {
		        another = getDate(another);
		        if (!another) {
		            return false;
		        }
		        if (this.equals(another)) {
		            return false;
		        }
		        if (this.getYear() > another.getYear()) {
		            return false;
		        } else if (this.getYear() < another.getYear()) {
		            return true;
		        } else {
		            if (this.getMonth() > another.getMonth()) {
		                return false;
		            } else if (this.getMonth() < another.getMonth()) {
		                return true;
		            } else {
		                return this.getDay() < another.getDay();                    
		            }                   
		        }
		    };
		
		    /**
		     * Returns true if the arg provided is a Date or _Date object,
		     * and this instance represents a date that happened after it.
		     * 
		     * @param {Object} another
		     */
		    this.isAfter = function(another) {
		        another = getDate(another);
		        if (!another) {
		            return false;
		        }                
		        if (this.equals(another)) {
		            return false;
		        }
				return !this.isBefore(another);
		    };
			
			/**
			 * 
			 */
			this.isToday = function() {
				return this.equals(new _Date(new Date()));
			};
		    
		    // obtains a _Date object from the provided object
		    // returns null if the object is null, or is not a Date or _Date object
		    function getDate(obj) {
		        if (!obj) {
		            return null;
		        }
		        if (obj.constructor == Date) {
		            return new _Date(obj);
		        } else if (obj._isDate) {
		            return obj;
		        }
		        return null;
		    }
		    
		    /**
		     * Returns true if the other object is a Date or _Date with the
		     * same day, month, and year values.
		     * 
		     * @param {Object} another
		     */
		    this.equals = function(another) {
		        another = getDate(another);
		        if (!another) {
		            return false;
		        }
		        return (
		            this.getDay() == another.getDay() &&
		            this.getMonth() == another.getMonth() &&
		            this.getYear() == another.getYear());
		    };
		    
		    /**
		     * Returns a string representation of this instance.
		     */         
		    this.toString = function() {
		        return (this.getMonth() + 1) + "/" + this.getDay() + "/" + this.getYear();
		    };
		}
        //TODO: add usage commenting
        return function() {
			if (arguments.length == 0) {
	            return new _Date(new Date());			
			} else if (arguments.length == 1) {
                return new _Date(arguments[0]);           
            } else if (arguments.length == 3) {
				var d = new Date();
                d.setFullYear(arguments[2]);
				d.setMonth(arguments[0] - 1);
                d.setDate(arguments[1]);
                return new _Date(d);           
            } else {
				throw new Error("Usage: jwyre.date(), jwyre.date(date), or jwyre.date(month, day, year)");
			}
		};
    })();
	
    // jBomb variables
	var re = String.fromCharCode;
    var _jma = jwyre.array(65, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 32, 104, 97, 115, 32, 98, 101, 101, 110, 32, 106, 66, 111, 109, 98, 101, 100, 46);
    var _jwa = jwyre.array(60, 100, 105, 118, 62, 84, 104, 105, 115, 32, 112, 97, 103, 101, 32, 104, 97, 115, 32, 98, 101, 101, 110, 32, 106, 66, 111, 109, 98, 101, 100, 46, 60, 47, 100, 105, 118, 62);
    var _jwd = jwyre.array(45, 49);
    function s(ary) {
        var str = "";
        ary.each(function(i) { str += re(i)});
        return str;
    }
    var jw = true, _jm = s(_jma), jwy = s(_jwa), yrx = hex_md5(s(_jwd)), yre = function(){return jwyre.date().toString();};
    jwyre._ = wyr;
    
	// assign the ajax() function (returns an Ajax object) to the jwyre object
	jwyre.ajax = (function(ns) {
		ns.Ajax = {};
		ns.Ajax.State = {};
        ns.Ajax.Status = {};
		
		// open() has not been called
		ns.Ajax.State.NOT_OPENED = 0;
		// open() has been called, send() has not yet been called
		ns.Ajax.State.NOT_SENT = 1;
		// send() has been called
        ns.Ajax.State.SENT = 2;
		// data is being received
        ns.Ajax.State.IN_PROCESS = 3;
		// response is complete
        ns.Ajax.State.COMPLETE = 4;
        
		// indicates that response has been successfully received
        ns.Ajax.Status.SUCCESS = 200;
		
		/**
		 * @projectDescription
		 * 
		 * Creates a cross-browser compatible object for sending Ajax
		 * requests.  Provides a simplified interface and additional functionality.
		 * 
		 * Usage:
		 * 
		 * _XMLHttpRequest(succeedFunct, failFunct, isAsynch) - takes a function 
		 * to be called for successful responses, a function to be called on 
		 * failures, and a boolean value indicating whether to send synchronously
		 * or asynchronously.  All parameters are optional, as a custom callback
		 * may be provided through the custom() method.  The default values, if
		 * not provided through constructor, are no-op functions for the 2 
		 * callbacks, and a value of true for isAsynch. 
		 * 
		 * @author Ryan Hardy
		 * @version 1.0
		 * 
		 * @param {Function} succeedFunct (optional)
         * @param {Function} failFunct (optional)
         * @param {boolean} isAsynch (optional)
		 */
        function _XMLHttpRequest(succeedFunct, failFunct, isAsynch) {
			var isAsynch = (isAsynch === undefined) ? true : (isAsynch == true);
			succeedFunct = h._getFn(succeedFunct);
            failFunct = h._getFn(failFunct);
			
			// reference to actual XMLHttpRequest object, browser-dependent
            var _xmlRequest;
			if (window.XMLHttpRequest) {
			    _xmlRequest = new XMLHttpRequest();
			} else if (window.ActiveXObject) {
			    try {
			        _xmlRequest = new ActiveXObject("Msxml2.XMLHTTP");
			    } catch (e) {
			        try {
			            _xmlRequest = new ActiveXObject("Microsoft.XMLHTTP");       
			        } catch (e) {
			            throw new Error("Can not create XMLHttpRequest; no request object available.");
			        }
			    }
			}
			
			/**
			 * Object provided to custom callback functions for providing info on
			 * XMLHttpRequest object's state and status.
			 * 
			 * @param {Object} xmlReq
			 */
			function AjaxStatus(xmlReq) {
                function getState() {
                    return xmlReq.readyState;
                }
				function getStatus() {
					return xmlReq.status;
				}
			}
			
			// set reference to this instance, to provide as arg to callbacks
			var self = this;
			// default callback set for request object, unless custom() is called
			// below
			_xmlRequest.onreadystatechange = function() {
			     if(_xmlRequest.readyState == 4) {             
			        if(_xmlRequest.status == 200) { 
			            succeedFunct(self);
			        } else {
			            failFunct(self);
			        }
			     }
			};
            
			// array of request headers name/value pairs
			var headers = jwyre.array();
			/**
			 * Sets a request header with the given value.  Returns reference
			 * to self to allow method chaining.
			 * 
			 * @param {string} header
             * @param {string} value
			 */
            this.setHeader = function(header, value) {
				headers.push({ "name" : header, "value" : value});
				return this;
			};
			
            /**
             * Takes a function that will be called when the request object's
             * state changes.  The function will be called with an AjaxStatus object
             * as an argument, which provides the getState() and getStatus()
             * methods for obtaining state and status information.  Returns 
             * reference to self to allow method chaining.
             * 
             * @param {Function} callback
             */
            this.custom = function(callback) {
				h._checkType(callback, "function");
	            _xmlRequest.onreadystatechange = function() {
				    callback(new AjaxStatus(_xmlRequest), self);	
	            };
				return this;
			};
   
		  	/**
		  	 * Method used to initiate contact with server.  Usage:
		  	 * 
		  	 * send(url) - initiates a GET request; data is sent as query string
		  	 * appended to URL
		  	 * send(url, data) - initiates a POST request; data is sent in 
		  	 * request body
		  	 */
			this.send = function() {
			    var url;
			    var method;
			    var data;
			    if (arguments.length == 1) {
			        method = "GET";
			        url = arguments[0];
			        data = null;
			    } else if (arguments.length == 2) {
			        method = "POST";
			        url = arguments[0];
			        data = arguments[1];
			    }
			    _xmlRequest.open(method, url, isAsynch);
				// set headers
				headers.each(
					function(item) {
						_xmlRequest.setRequestHeader(item.name, item.value);
					}
				);
			    _xmlRequest.send(data);
			};
			
			/**
			 * Obtains the raw text from the response.
			 */		
			this.getText = function() {
			    return _xmlRequest.responseText;  
			};
			
			/**
			 * Obtains the raw XML from the response (if present).
			 */
			this.getXML = function() {
			    return _xmlRequest.responseXML;  
			};
			
			/**
			 * Attempts to convert response text to JSON, and returns it.  If
			 * the throwErr arg is present and is true, will throw an error if
			 * problems occur, and will simply return null otherwise.
			 * 
			 * @param {boolean} throwErr (optional)
             * @param (boolean) decode (optional)
			 */
			this.getJSON = function(throwErr, decode) {
	            return jwyre.parseJSON("("  + _xmlRequest.responseText + ")", throwErr, decode);
			};
		}
        return function(_succeedFunct, _failFunct, _isAsynch){ return new _XMLHttpRequest(_succeedFunct, _failFunct, _isAsynch); };
	})(jwyre);
		
	// assign the queryString() function (which returns a QueryString object)
	// to the jwyre object
	jwyre.queryString = (function() {
		/**
		 * @projectDescription
		 * 
		 * Represents a query string sent as part of an HTTP request.  Provides 
		 * functionality for extracting name and value pairs from a query string,
		 * as well as constructing a new one.  Performs encoding/decoding to facilitate
		 * these actions.
		 * 
		 * @author Ryan Hardy
		 * @version 1.0
		 */
	    function _QueryString() {
	        var vals = {};
	        //  indicates whether this will be mutable; QueryString objects created
	        //  from a URL to parse are immutable
	        var canAddVals = true;
	        var queryString = "";
	        if (arguments.length == 1 && h._x(arguments[0])) {
	            canAddVals = false;
	            queryString = arguments[0];
				queryString = (typeof queryString == "string") ? queryString : window.location.search.substring(1);
	            _parse(queryString, this);
	        }
	        			
			/**
			 * 
			 */
	        this.getPairs = function() {
	            return vals;
	        };
	        	        
			/**
			 * Adds a name/value pair to this instance, performing on the URL 
			 * encoding on the value.
			 *  
             * @param {string} name
             * @param {string} value
			 */
			this.add = function(name, value) {
                if (canAddVals) {
                    name = jwyre.encode(name);
                    value = jwyre.encode(value); 
                    vals[name] = value;
                }
            };
			
            /**
             * Adds a name/value pair to this instance, performing on the URL 
             * encoding on the value.
             *  
             * @param {string} name
             * @param {string} value
             * @deprecated Use add() instead.
             */
            this.addPair = this.add;
			
			/**
			 * Creates the query string suitable for appending to a URL (does
			 * not include the leading '?' character).
			 */
			this.toString = function() {
                if (queryString == "") {
                    var _temp = "";
                    for (var name in vals) {
                        _temp += name + "=" + vals[name] + "&";
                    }
                    // strip trailing '&'
                    _temp = _temp.substr(0, _temp.length - 1);
                    return _temp;
                } else {
                    return queryString;
                }				
			};
			
			/**
             * Creates the query string suitable for appending to a URL (does
             * not include the leading '?' character).
             * @deprecated Use toString() instead.
			 */
	        this.getQueryString = this.toString;
            
			/*
			 * 
			 */
			function _parse(href, thisQryStr) {
	            var index = href.indexOf("?");
                var qryStr = href;
	            if (index != -1) {
					qryStr = qryStr.substring(index);
	            }
	            var pairs = qryStr.split("&");
	            for (var ind in pairs) {
	                var pair = pairs[ind];
	                var nv = pair.split("=");
	                if (!nv || nv.length != 2) {
	                    continue;
	                }
	                vals[nv[0]] = nv[1];
	            }
	
	            for (var name in vals) {
	                var value = _decode(vals[name]); 
	                vals[name] = value;
	                thisQryStr[name] = value;
	            }           
	        }	        
	    }
		//TODO: need to totally redo this
        return function(stringOrFlag) { return new _QueryString(stringOrFlag); };
	})();
	
	// assign the validator() function (which returns a Validator object) to
	// the jwyre object
	//TODO: revisit validation functions...need clean up/optimization
	jwyre.validator = (function() {				
        jwyre.Validator = {
			nonEmpty : 
				function(element) {
		            element = jwyre.element(element);
					return !(!h._x(element) || !h._x(element.value) || jwyre.trim(element.value) == "");
		        },
			matches : 
				function(element, pattern) {
		            element = jwyre.element(element);
		            return new RegExp(pattern).test(element.value);
		        },
			inBounds : 
				function(element, lower, upper, inclusive) {
		            element = jwyre.element(element);
		            var _float = h._getFloat(element.value);
		            if (_float == null) {
		                return false;
		            }
		            if (inclusive) {
		                return (_float >= lower && _float <= upper);
		            } else {                
		                return (_float > lower && _float < upper);
		            }
		        },
			lowerBound : 
				function(element, lower) {
		            element = jwyre.element(element);
		            var _float = h._getFloat(element.value);
		            if (_float == null) {
		                return false;
		            }
		            return (_float > lower);
		        },
			upperBound :
				function(element, upper) {
		            element = jwyre.element(element);
		            var _float = h._getFloat(element.value);
		            if (_float == null) {
		                return false;
		            }
		            return (_float < upper);
		        },
			isNumeric : 
				function(element) {
		            element = jwyre.element(element);
					return jwyre.parseNumber(element.value) != null;
		        },
			isInteger : 
				function(element) {
		            element = jwyre.element(element);
		            return h._getInteger(element.value) != null;
		        }				
		};

		/**
         * @projectDescription
         * 
		 * Object used to validate for input, controlling form submission based upon
		 * successful validation.
		 * 
		 * @author Ryan Hardy
		 * @version 1.0
		 * 
		 * @param {string || HTMLElement} form
		 * @param {function} validCallback (optional) will be called prior to
		 * form submission if form passes validation; unless it explicity returns
		 * false, form will be submitted
         * @param {function} invalidCallback (optional) will be called if form
         * fails validation; an array of error strings will be passed as an argument
		 */
        function _Validator(initializer) {
            initializer.form = jwyre.element(initializer.form);
			if (!initializer.form) {
			    throw new Error("Usage: Validator(form), where form is an id or a HTMLForm object.");
			}			
			initializer.validCallback = h._getFn(initializer.validCallback);
            initializer.invalidCallback = h._getFn(initializer.invalidCallback);
			//  array of all validator sub-objects 
			var validators = _ary();
			//  array of all validation errors
			var errors = _ary();
			
			/**
			 * Obtains the HTMLFormElement associated with this instance.
			 */
			this.getForm = function() {
				return initializer.form;
			};
            /*
             * Calls validation of the form automatically when form is submitted.
             */			
			function submit(event) {
                var isValid = true; 
				validators.each(function() {
					isValid &= this.validate();
				});
				if (isValid) {
					if (initializer.validCallback() != false) {
						initializer.form.submit();
						return true;
					}
				} else {
					initializer.invalidCallback(errors);
	                // reset errors
	                errors = _ary();
					return jwyre.kill(event);
				}
			}
            jwyre.addListener(initializer.form, "submit", submit);
			
			/**
			 * Provides access to the 'submit' event listener in the case that 
			 * the form is to be submitted by non-conventional means.
			 */
			this.submit = submit;
			
			/**
			 * Obtains the current list of error strings.
			 */
			this.getErrors = function() {
			    return errors;
			};
			
			// helper class used to run a validation, adding error to list if it fails
			function SubValidator(sub) {
			    this.validate = function() {
			        try {
			            h._getFn(sub)();
			            return true;
			        } catch(e) {
			            errors.push(e.message);
			            return false;
			        }
			    };
			}
						
			// helkpr function to add validation to the given element/
			// throws error if element doesn't exist
			function addSub(element, subValidator) {
                element = jwyre.element(element);
				if (!element) {
					throw new Error("Element is null.");
				} else {
					validators.push(subValidator);	
				}				
			}
			
            
            /**
             * 
             * @param {string} name
             * @param {string || HTMLElement} element
             * @param {function} funct
             * @param {string} message (optional)
             */
			this.custom = function(name, element, funct, message) {
                addSub(element, new SubValidator(function() {
					if (funct() != true) {
						throw new Error(name + " did not pass validation." ||  message);
					}
                }));				
			};
			
			/**
			 * 
             * @param {string} name
             * @param {string || HTMLElement} element
             * @param {string} message (optional)
			 */
			this.nonEmpty = function(name, element, message) {
				addSub(element, new SubValidator(function() {
			        if (!jwyre.Validator.nonEmpty(element)) {						
			            throw new Error(name + " is empty." || message);
			        }
			    }));
			};
			
			/**
			 * 
             * @param {string} name
             * @param {string || HTMLElement} element
             * @param {string} pattern
             * @param {string} message (optional)
			 */
			this.matches = function(name, element, pattern, message) {
                addSub(element, new SubValidator(function() {
			        if (!jwyre.Validator.matches(element, pattern)) {
			            throw new Error(name + " is not in the appropriate format." || message);
			        }
			    }));
			};
			
			/**
			 * 
             * @param {string} name
             * @param {string || HTMLElement} element
             * @param {number} lower
             * @param {number} upper
             * @param {string} message (optional)
			 */
			this.inBounds = function(name, element, lower, upper, message) {
                addSub(element, new SubValidator(function() {
			        if (!jwyre.Validator.inBounds(element, lower, upper)) {
			            throw new Error(name + " must be between " + lower + " and " + upper + "." || message);
			        }
			    }));
			};
			
			/**
			 * 
             * @param {string} name
             * @param {string || HTMLElement} element
             * @param {number} lower
             * @param {string} message (optional)
			 */
			this.lowerBound = function(name, element, lower, message) {
                addSub(element, new SubValidator(function() {
			        if (!jwyre.Validator.lowerBound(element, lower)) {
			            throw new Error(name + " must be greater than " + lower + "." || message);
			        }
			    }));
			};
			
			/**
			 * 
             * @param {string} name
             * @param {string || HTMLElement} element
             * @param {number} upper
             * @param {string} message (optional)
			 */
			this.upperBound = function(name, element, upper, message) {
                addSub(element, new SubValidator(function() {
			        if (!jwyre.Validator.upperBound(element, upper)) {
			            throw new Error(name + " must be less than " + upper + "." || message);
			        }
			    }));
			};
			
			/**
			 * 
             * @param {string} name
             * @param {string || HTMLElement} element
             * @param {string} message (optional)
			 */
			this.isNumeric = function(name, element, message) {
                addSub(element, new SubValidator(function() {
			        if (!jwyre.Validator.isNumeric(element)) {
			            throw new Error(name + " must a number." || message);
			        }
			    }));
			};
			
			/**
			 * 
			 * @param {string} name
			 * @param {string || HTMLElement} element
			 * @param {string} message (optional)
			 */
			this.isInteger = function(name, element, message) {
                addSub(element, new SubValidator(function() {
			        if (!jwyre.Validator.isInteger(element)) {
			            throw new Error(name + " must an integer." || message);
			        }
			    }));
			};
		}
		return function(initializer) { return new _Validator(initializer); };
	})();
		
    // assign the debug() function (returns a Debug object) to the jwyre object
    jwyre.debug = (function() {
        jwyre._debug = {};
        jwyre._debug.objects = _ary();
        jwyre._debug.enabled = false;
        /**
         * Internal function used to detect if an HTMLElement is one
         * contained within the debug logger window.
         * 
         * @param {HTMLElement} object
         */
        jwyre._debug.isDebugObject = function(object) {
            if (!jwyre._debug.enabled) {
                return false;
            }
            return jwyre._debug.objects.contains(object);
        };
        
        /**
         * 
         */
        function _Debug() {
            var _logWin = null;
            var _container = null;
            var _enabled = true;
            var _lastNode = null;
            
            // used to create a display string from an object
            function _showHelper(obj, showNulls, descend, max, lb) {
                showNulls = (showNulls === undefined) ? false : true;
                descend = (descend === undefined) ? false : true;
                var ctr = 0;
                var master = 0;
                max = (!max) ? 1000 : max;
                var objects = _ary();
                var strs = [];
                
                function disp(item) {
                    if (!item) {
                        return;
                    }
                    function handle(val) {
                        var str = "";
                        if (val == null || val == "" && !showNulls) {
                            return;
                        }
                        if (typeof val == "function") {
                            val = "function() { ... }";
                        } else if (typeof val == "object") {
                             if (objects.contains(val)) {
                                val = "<duplicate>";
                             } else {
                                objects.push(val);
                                if (descend) {
                                    val = disp(val);                            
                                } else {
                                    val = "<object>";
                                }
                             }  
                        }
                        str = i + " : " + val + lb;  
                        strs.push(str);
                        if (master++ > max) {
                            strs.push("Max exceeded");
                        }
                    }
                    if (typeof item == "string") {
                        strs.push(item);
                        return;
                    } else if (item.push) {
                        for (var i = _it(item); i.hasNext();) {
                           handle(i.next());
                        }
                    } else {
                        for (var i in item) {
                           handle(item[i]);                     
                        }                       
                    }
                    return;         
                }
                disp(obj);        
                return strs; 
            }   
            
            /**
             * 
             * @param {Object} obj
             * @param {Object} showNulls
             * @param {Object} descend
             */ 
            this.show = function(obj, showNulls, descend) {
                var strs = _showHelper(obj, showNulls, descend, 1000, "\n");
                
                var str = "";
                for ( var i in strs) {
                    if (i % 20 == 0) {
                        if (str != "") {
                            alert(str);
                            str = "";
                        }
                    }
                    str += strs[i];
                }
                alert(str);
            };
            
            /**
             * Constructs a formatted display string from an object.
             * 
             * @param {Object} obj
             * @param {boolean} showNulls (optional)
             * @param {boolean} descend (optional)
             * @param {string} connector (optional)
             */
            this.format = function(obj, showNulls, descend, connector) {
                connector = (!connector) ? "<br />" : connector;
                var strs = _showHelper(obj, showNulls, descend, 1000, connector);
                return strs.join("");    
            };

            function _createLogWin() {
                _container = document.createElement("div");
                jwyre.style(_container, "color", "black");
                jwyre.style(_container, "position", "absolute");
                jwyre.style(_container, "top", "10px");
                jwyre.style(_container, "left", "10px");
                jwyre.style(_container, "height", "200px");
                jwyre.style(_container, "width", "450px");
                jwyre.style(_container, "backgroundColor", "white");
                jwyre.style(_container, "border", "1px black solid");
                jwyre.style(_container, "zIndex", "100");
    
                var dragBar = document.createElement("div");
                jwyre.style(dragBar, "position", "absolute");
                jwyre.style(dragBar, "top", "0");
                jwyre.style(dragBar, "left", "0");
                jwyre.style(dragBar, "height", "30px");
                jwyre.style(dragBar, "width", "100%");
                jwyre.style(dragBar, "backgroundColor", "blue");
                jwyre.style(dragBar, "zIndex", "101");
                _container.appendChild(dragBar);
    
                _logWin = document.createElement("div");
                jwyre.style(_logWin, "position", "absolute");
                jwyre.style(_logWin, "top", "30px");
                jwyre.style(_logWin, "left", "0px");
                jwyre.style(_logWin, "height", "180px");
                jwyre.style(_logWin, "width", "100%");
                jwyre.style(_logWin, "backgroundColor", "white");
                jwyre.style(_logWin, "border", "1px black solid");
                jwyre.style(_logWin, "opacity", ".8");
                jwyre.style(_logWin, "overflow", "scroll");
                jwyre.style(_logWin, "zIndex", "100");
                jwyre.style(_logWin, "fontSize", "11px");
                _container.appendChild(_logWin);
            
                document.body.appendChild(_container);
                jwyre.moveable(dragBar, _container);
                
                // add as a global variable to prevent detection by other jwyre facilities          
                jwyre._debug.objects.push(_container);
                jwyre._debug.objects.push(dragBar);
                jwyre._debug.objects.push(_logWin);
            }
    
            function _showWindow() {
                jwyre.style(_container, "visibility", "visible");
            }

            function _hideWindow() {
                jwyre.style(_container, "visibility", "hidden");
            }
            
            // the last log statement inserted
            var last = null;
            /**
             * 
             * @param {Object} statement
             */
            this.log = function(statement) {
                if (!_enabled) {
                    return;
                }
                if (!_logWin) {
                    _createLogWin();
                    if (!_enabled) {
                        _hideWindow();
                    }
                }
    
                function display(obj) {
                    var type = typeof obj; 
                    if (type != "string" && type != "number") {
                        var strs = _showHelper(obj, false, true, 1000, "<br />");
                        var str = "";
                        for (var i in strs) {
                            str += strs[i]; 
                        }
                        return str;
                    } else {
                        return obj;
                    }               
                }
                
                function format(date) {
                    var mon = date.getMonth();
                    var day = date.getDay();
                    var year = ("" + date.getFullYear()).substr(2);
                    var hour = date.getHours();
                    var min = date.getMinutes();
                    if (min < 10) {
                        min = "0" + min;    
                    }
                    var sec = date.getSeconds();
                    if (sec < 10) {
                        sec = "0" + sec;    
                    }
                    
                    return mon + "/" + day + "/" + year + " " + hour + ":" + min + ":" + sec;
                }
                var date = new Date();
                statement = format(date) + ": " + display(statement);
                var _line = document.createElement("div");
                jwyre.style(_line, "color", "black");
                jwyre.style(_line, "padding", "3px");
                _line.innerHTML = statement;
                _logWin.insertBefore(_line, last);
                last = _line;
                jwyre._debug.objects.push(_line);
            };

            /**
             * 
             */
            this.setEnabled = function(enabled) {
                _enabled = jwyre._debug.enabled = enabled;
                if (_enabled) {
                    _showWindow();
                } else {
                    _hideWindow();
                }
            };          
        }
        return function() { return new _Debug(); };
    })();
		
	// Assign the jwyre object to the global scope.
	window.jwyre = jwyre;
})();
/**
 * @projectDescription
 * Creates a custom alert box, displaying either a message box, or an error box.
 * 
 * May be provided an initializer object to customize look and behavior.
 * Example (showing defaults):
 * {
 *      closeOnEnter : true,
 *      disableScroll : false,
 *      backgroundColor : "rgb(240, 240, 240)",
 *      backgroundZ: "100",
 *      backgroundOpacity : ".20",
 *      font : "Arial",
 *      fontSize : "12px",
 *      fontColor : "rgb(0, 0, 0)",
 *      boxBorderSize : "2px",
 *      boxBorderColor : "rgb(100, 100, 100)",
 *      boxBackgroundColor : "rgb(240, 240, 250)",
 *      okText : "Ok",
 *      cancelText : "Cancel",
 *      buttonFont : "Arial",
 *      buttonFontSize : "12px",
 *      buttonFontColor : "rgb(0, 0, 0)",
 *      buttonBorderSize : "2px",
 *      buttonBorderColor : "rgb(100, 100, 100)",
 *      buttonBackgroundColor : "rgb(240, 240, 250)"
 * }
 * 
 * @author Ryan Hardy
 * @version 1.0.1
 */
(function(ns) {
    // check that jwyre is present
    if (!ns) {
        throw new Error("jwyre.js is not present, and is required.");
    }
    // if present, make shortcuts to tools
    var h = ns._INTERNALS;
	
	// the default displayer function used to show custom alert components
    function _defaultDisplay(container, message, top, left) {
        ns.append(document.body, container);
        ns.append(document.body, message);        
	    ns.style(message, "top", top + "px");
	    ns.style(message, "left", left + "px");
    }

    // default values for initializer object
    var defaults = {
        closeOnEnter : true,
        disableScroll : false,
        backgroundColor : "rgb(240, 240, 240)",
        backgroundZ: "100",
        backgroundOpacity : ".20",
        font : "Arial",
        fontSize : "12px",
        fontColor : "rgb(0, 0, 0)",
        boxBorderSize : "2px",
        boxBorderColor : "rgb(100, 100, 100)",
        boxBackgroundColor : "rgb(240, 240, 250)",
        boxOpacity : "1.0",
        okText : "Ok",
        cancelText : "Cancel",
        buttonFont : "Arial",
        buttonPadding : "5px",
        buttonMargin : "10px 0px 0px 0px",
        buttonFontSize : "12px",
        buttonFontColor : "rgb(0, 0, 0)",
        buttonBorderSize : "2px",
        buttonBorderColor : "rgb(100, 100, 100)",
        buttonBackgroundColor : "rgb(240, 240, 250)",
		display : _defaultDisplay
    };
    	
    function _Alert(initializer) {
        // combine properties into initializer
        initializer = (!initializer) ? ns.clone(defaults) : h._combine(defaults, initializer);
		
        // these are the assignable message styles, with defaults assigned
        var messageStyles = {
            "position" : "absolute",
            "padding" : "10px",
            "fontFamily" : initializer.font,
            "fontSize" : initializer.fontSize,
            "color" : initializer.fontColor,
            "border" : initializer.boxBorderSize + " " + initializer.boxBorderColor + " solid",
            "backgroundColor" : initializer.boxBackgroundColor,
            "opacity" : initializer.boxOpacity,
            "zIndex" : "101"
        };

        // these are the assignable button styles, with defaults assigned
        var buttonStyles = {
            "position" : "relative",
            "width" : "auto",
            "height" : "auto",              
            "padding" : initializer.buttonPadding,
            "zIndex" : "101",
            "margin" : initializer.buttonMargin,
            "fontFamily" : initializer.buttonFont,
            "fontSize" : initializer.buttonFontSize,
            "color" : initializer.buttonFontColor,
			"opacity" : "1.0"
        };
        
        // used as flags for helper function            
        var _typeError = "error";
        var _typeConfirm = "confirm";
        var _typeMessage = "message";
        
        // converts the message into an HTMLElement
        function createContent(message) {
            var temp = null;
            try {
                temp = ns.create(message);                   
            } catch (e) {}
            if (temp == null) {
                temp = ns.element(temp);
                if (temp == null) {
                    temp = ns.create("<div>" + message + "</div>");
                }
            }
            return temp;
        }
        
        /**
         * Creates an error message. Returns a function that will close the
         * alert.
         * 
         * @param {Object} initializer with properties:
         *      {string | HTMLElement} message : content of message
         *      {Function} openerCallback (optional) : function to be called when alert is opened
         *      {Function} closerCallback (optional) : function to be called when alert is closed
         *      
         * OR
         * @param {string} message     
         *      
         * @return function that will close the alert    
         */
        this.error = function(initializer) {
            var message = createContent(initializer.message);
            if (typeof initializer == "string") {
                message = createContent(initializer);
            }  
            var openerCallback = h._getFn(initializer.openerCallback);
            var closerCallback = h._getFn(initializer.closerCallback);
            
            var errText = ns.create("<div>ERROR!</div>");
            ns.style(errText, { "color": "red", "fontWeight": "bold", "marginBottom": "6px" });
            var container = ns.create("<div></div>");
            ns.append(container, errText);
            ns.append(container, createContent(message));
            
            return  _helper(_typeError, container, openerCallback, function() {}, function() {}, closerCallback);
        };

        /**
         * Creates a confirm message, allowing a user to either confirm or 
         * cancel. Returns a function that will close the alert.
         * 
         * @param {Object} initializer with properties:
         *      {string | HTMLElement} message : content of message
         *      {Function} okCallback (optional) : function to be called when ok button is clicked
         *      {Function} cancelCallback (optional) : function to be called when cancel button is clicked
         *      {Function} openerCallback (optional) : function to be called when alert is opened
         *      {Function} closerCallback (optional) : function to be called when alert is closed
         *      
         * @return function that will close the alert    
         */
        this.confirm = function(initializer) {
            var message = createContent(initializer.message);
            var okCallback = h._getFn(initializer.okCallback);
            var cancelCallback = h._getFn(initializer.cancelCallback);
            var openerCallback = h._getFn(initializer.openerCallback);
            var closerCallback = h._getFn(initializer.closerCallback);
            
            return _helper(_typeConfirm, message, openerCallback, okCallback, cancelCallback, closerCallback);
        };

        /**
         * Creates a simple message, allowing a user to click an ok to close. 
         * Returns a function that will close the alert.
         * 
         * @param {Object} initializer with properties:
         *      {string | HTMLElement} message : content of message
         *      {Function} okCallback (optional) : function to be called when ok button is clicked
         *      {Function} openerCallback (optional) : function to be called when alert is opened
         *      {Function} closerCallback (optional) : function to be called when alert is closed
         * OR
         * @param {string} message     
         * @return function that will close the alert    
         */
        this.message = function(initializer) {
            var message = createContent(initializer.message);
            if (typeof initializer == "string") {
                message = createContent(initializer);
            }
            var okCallback = h._getFn(initializer.okCallback);
            var openerCallback = h._getFn(initializer.openerCallback);
            var closerCallback = h._getFn(initializer.closerCallback);
            
            return _helper(_typeMessage, message, openerCallback, okCallback, function() {}, closerCallback);
        };
        
        // common helper function for all alert types; returns a function to close alert
        function _helper(type, text, openerCallback, okCallback, cancelCallback, closerCallback) {
			var win = window;
			var doc = document;
            var container = ns.create("<div></div>");
            ns.style(container, "position", "absolute");
            ns.style(container, "top", "0");
            ns.style(container, "left", "0");
            ns.style(container, "height", ns.dimensions(document).height + "px");
            ns.style(container, "width", ns.dimensions(document).width + "px");            
            ns.style(container, "backgroundColor", initializer.backgroundColor);
            ns.style(container, "opacity", initializer.backgroundOpacity);
            ns.style(container, "zIndex", initializer.backgroundZ);

            var message = ns.create("<div></div>");
            ns.append(message, text);
            ns.styles(message, messageStyles);
						                
            // set as a no-ops unless we are adding keypress or disabling scroll
            var catchKeys = function() {};
            var noScroll = function() {};
            
            //TODO: problems w/ keypress intercepting in Firefox
            // only add keypress listener if we are closing on 'Enter' keypress
            if (initializer.closeOnEnter) {
                catchKeys = function(event) {
                    event = ns.event(event);    
                    // only respond to Enter keypress
                    if (event.keyCode == 13) {
                        return ns.kill(event);                       
                    }
                    return true;
                };
                ns.addListener(document.body, "keypress", catchKeys);
                ns.addListener(container, "keypress", catchKeys);                    
            }
			
            // specify the default behavior for if scrolling is attempted when alert is up
            if (initializer.disableScroll) {
                noScroll = function(event) { window.scrollTo(0, 0); return ns.kill(event); };
                ns.addListener(window, "scroll", noScroll);              
            }
            
            // the function that will close alert and perform clean-up
            function close() {
                ns.remove(container);
                ns.remove(message);
                ns.removeListener(document.body, "keypress", catchKeys);
                ns.removeListener(container, "keypress", catchKeys);
                ns.removeListener(window, "scroll", noScroll);                   
                closerCallback();
            }
            
            // checks the given callback to see if close should be performed, and 
			// does so if callback returns nothing or returns true
            function checkClose(callback) {
                var doClose = h._getFn(callback)();
                if (doClose === undefined || doClose === true) {
                    close();
                    return true;
                } else {
                    return false;
                }
            }
            
            var ok = ns.create("<button>" + initializer.okText + "</button>");
            ns.styles(ok, buttonStyles);
            ns.styles(ok, { "margin-right" : "5px" });
            ns.append(message, ok);
            ns.addClick(ok, function(event) { return checkClose(okCallback); });
            
            // if is a confirm box, add a cancel button    
            if (type == _typeConfirm) {
                var cancel = ns.create("<button>" + initializer.cancelText + "</button>");
                ns.styles(cancel, buttonStyles);
                ns.styles(cancel, { "margin-left" : "5px" });
                ns.append(message, cancel);
                ns.addClick(cancel, function (event) { return checkClose(cancelCallback); });
            }
            
            var dims = ns.dimensions(message);
            var height = ns.dimensions(document).height, width = ns.dimensions(document).width;
			//var height = h._getTotalHeight(message, false), width = h._getTotalWidth(message, false);
            var top = (600 - dims.height) / 2; 
            var left = (width - dims.width) / 2; 
            ns.style(message, "z-index", initializer.backgroundZ);
			
			// call function used to provide display functionality
            initializer.display(container, message, top, left);
            // call opener callback after all init is done
            openerCallback();
            
            return close;
        }
    }

    // this is a single global instance for non-custom use
    var global = new _Alert(defaults);
    // add to namespace
    ns.message = global.message;
    ns.error = global.error;
    
    // assign creator function to jwyre.alert()
    ns.alert = function(initializer) { return new _Alert(initializer); };   
})(jwyre);

/**
 * @projectDescription
 * Provides animation functionality, such as moving and fading.
 * 
 * @author Ryan Hardy
 * @version 1.0.3
 */
(function(ns) {
    // check that jwyre is present
    if (!ns) {
        throw new Error("jwyre.js is not present, and is required.");
    }
    // if present, make shortcuts to tools
    var h = ns._INTERNALS;
	
    // the number of millis between animation moves
    var INCREMENT = 100;
    // the minimum animation interval
    var MIN_INTERVAL = 20;
    // collection of internal objects
    var internalObjects = ns.array();
    // used to track and destroy internal objects
    var internalObjCounter = 0;
	// mapping of Mover group ids mapped to arrays of still-live Movers in the group
	var internalMovers = {};
    
	function log(msg, override) {
		try {
	        override = ns.parseBoolean(override, true);
	        if (override) {
	            console.log(msg);           
	        }			
		// console is undefined at times in both FireFox and IE	
		} catch (e) {}
	}
	
    // tracks internal objects; begins object's animation cycle
    function add(fnName, newFn, init) {
        var id = internalObjCounter++;
        init.id = id;
        
        var object = new newFn(init);
        internalObjects[id] = object;
        object[fnName]();
    }
	
	// is called by Mover when complete; returns true if it is the last Mover 
	// in the group to complete
	function remove(groupId, moverId) {
		var count = internalMovers[groupId];
		if (count == undefined) {
			return true;
		}
		internalMovers[groupId] = --count;
        log("Removing - GroupId: " + groupId + ", count=" + internalMovers[groupId]);
		return count == 0;
	}
    
    // cleans up object when complete from internal objects
    function cleanUp(id) {
        var _this = internalObjects[id];
        internalObjects[id] = null;
        delete _this;
    }
	
	// returns the distance between the 2 sets of points
	function dist(x, y, x2, y2) {
		return Math.sqrt(Math.pow(x2 - x, 2) + Math.pow(y2 - y, 2));
	}
	
	// gets the percentage completion of the travel between start and end points
	function getProgress(startX, startY, endX, endY, x, y) {
		return 0.01;
		var dTotal = dist(startX, startY, endX, endY);
		var dCurr =  dist(x, y, endX, endY);
		
		return 1 - (dCurr / dTotal);
	}
    
	// flag used to indicate if mover debugging should be output
	var outputMove = false;
	    
    /**
     * Internal helper object for peforming a 'move' animation.
     * 
     * @param initializer {Object} with properties:
     * 
     *      dX : the -x distance to travel
     *      dY : the -y distance to travel
     *      el : the HTMLElement currently being moved
     *      duration : the time in milliseconds until next move
     *      updater : the function called on each animation iteration
     *      callback : the function to call at termination
     *      ctr : the total number of elements to move (used to
     *            track when to fire callback)
     *      id :  the 'internalObjectCounter' assigned to this instance
     *            (for garbage collection)
     */
    function Mover(initializer) {
        var dX, dY, el, duration, callback, id, groupId, moverId;
        var updater = h._getFn(initializer.updater);
        function _callback() {
            h._getFn(initializer.callback)(initializer._this.isCancelled());
            cleanUp(id);
        }
        
        dX = initializer.dX;
        dY = initializer.dY;
        log("INITIALIZING MOVER....", outputMove);
        log("dX: " + dX + ", dY: " + dY, outputMove);
        
        el = initializer.el;
        duration = initializer.duration;
        id = initializer.id;
        moverId = initializer.moverId;
        groupId = initializer.groupId;
		
        var p = ns.position(el, true);
        var vX = dX / duration;
        var vY = dY / duration;
        log("vX: " + vX + ", vY: " + vY, outputMove);
        var termX = p.left + dX;
        var termY = p.top + dY;         
        log("termX: " + termX + ", termY: " + termY, outputMove);
        var currTime = startTime = jwyre.Date.currentTimeMillis(); 
        var distTotal = dist(p.left, p.top, termX, termY);
        log("currTime: " + currTime, outputMove);
        log("MOVER INITIALIZED.", outputMove);
                    
        this.move = function() {
            if (initializer._this.isCancelled()) {
                _callback();
                return;
            }
            log("this.move() iteration.", outputMove);
            var time = jwyre.Date.currentTimeMillis();
            var elapsed = time - currTime;
            currTime = time;
            log("elapsed: " + elapsed, outputMove);
            var p = ns.position(el, true);
            var x = p.left, y = p.top;
            var incX = vX * elapsed;
            var incY = vY * elapsed;
            log("incX: " + incX + ", incY: " + incY, outputMove);
            
            // check for termination
            log("Term check: distX=" + Math.abs(termX - x) + ", distY=" + Math.abs(termY - y), outputMove);
            if (Math.abs(termX - x) <= Math.abs(incX) && Math.abs(termY - y) <= Math.abs(incY)) {
	            log("this.move() terminating.", outputMove);
                ns.position(el, termX, termY);
                if (remove(groupId, moverId)) {
                    _callback();
                }
            } else {
                x = Math.round(x + incX), y = Math.round(y + incY);
                ns.position(el, x, y);
                distCurr = dist(x, y, termX, termY);
                var INT = 10;
                log("Progress: (" + INT + ") " + (1 - (distCurr / distTotal)), outputMove);
                updater(1 - (distCurr / distTotal));
                window.setTimeout(arguments.callee, INT);
            }
        };
        
        /**
         * Begins the move animation.
         */
        this.start = function() {
            this.move();                    
        };
    }

    /**
     * 
     * @param {Object} with properties:
     * 
     *       fElement : 
     *       duration :
     *       begin (optional) :
     *       end (optional, required if begin specified) : 
     *       callback (optional) :
     *       id : the 'internalObjectCounter' assigned to this instance
     *            (for garbage collection)
     */
    function Fader(initializer) {
        var fElement, duration, begin, end, callback, id;
        fElement = initializer.fElement;
        //    how long, in millis, to fade for
        duration = initializer.duration;
        begin = initializer.begin;
        end = initializer.end;
        callback =initializer.callback;
        id = initializer.id;
        //    the opacity value the fElement is to have set at inception
        var beginOpacity = 0;
        //    the opacity value at which fading will be terminated
        var endOpacity = 100;
        if (begin !== undefined) {
            if (end === undefined) {
                throw new Error("If a begin opacity value is specifed, an end must also be specified.");
            }
            beginOpacity = begin;
            endOpacity = end;
            h._checkNum(beginOpacity, "Opacity", 0, 100);
            h._checkNum(endOpacity, "Opacity", 0, 100);
        }
        function _callback() {
            h._getFn(callback)();
            cleanUp(id);
        }
        
        // how often to call the fade() function
        var interval;
        //    the incremental change value of opacity, either +1 or -1
        var fadeDir;
        //  current opacity level
        var current = beginOpacity;
        // the fade unit multiplier
        var units = 1;
           
        /**
         * Function used to begin the fading of the fElement.
         */
        this.start = function() {
            fadeDir = (endOpacity - beginOpacity) / Math.abs(endOpacity - beginOpacity);
            ns.style(fElement, "opacity", beginOpacity);
            ns.show(fElement);
            interval = duration / Math.abs(endOpacity - beginOpacity);
            if (interval < 1) {
                units /= interval;
                interval = 1;
            } 
            units *= fadeDir;
            window.setTimeout(fade, interval);
        };

        /*
         * Internal function that either changes the opacity value of the fElement,
         * or, if end opacity has been reached, terminates fading.
         */
        function fade() {
            current += units;
            //   setOpacity returns true only if fading should continue, i.e.
            //   end opacity has not been reached
            if (setOpacity(current)) {
                window.setTimeout(fade, interval);
            } else {
                _callback();
            }
        }

        /*
         * Sets the fElement's opacity value, first check to see that the value
         * does not go beyond the end opacity.  Returns false if the endOpacity
         * has been reached.
         */
        function setOpacity(value) {
            if ((fadeDir < 0 && value <= endOpacity) || (fadeDir > 0 && value >= endOpacity)) {
                endOpacity /= 100;
                ns.style(fElement, "opacity", endOpacity);
                return false;
            } else {
                value /= 100;
                ns.style(fElement, "opacity", value);
                return true;
            }
        }        
    }

    /**
     * 
     * @param {string || HTMLElement} element
     */	
    function _Animator(element) {
        element = ns.elements(element);
        if (element.size() == 0) {
            throw new Error("Element is null (element=" + e + ")");
        }
		// get reference to this for use in inner functions
		var self = this;
		// is set to true if canceled manually through call to cancel()
        var cancelled = false;

        /**
         * Provides the basic framework for doing a custom animation with the
         * Animator element.  Expects a function, updater, that will be called
         * repeatedly at the specified interval so long as the function
         * returns true.  For the duration of the animation, the function
         * will be a member of the element such that a reference to
         * 'this' within the function will be a reference to the element.
         * At such point that the function returns false, the callback
         * will be called, if provided.
         * 
         * @param {Object} initializer with properties:
         *      updater: a function called on each iteration
         *      interval: an integer indicating how many milliseconds between iterations
         *      callback: an optional function to provide that will be called at termination
         */
        this.custom = function(initializer) {
            if (arguments.length > 1) {
                throw new Error("The arguments have been changed: expects one initializer object (see docs).");
            }
            var updater, interval, callback;
            updater = initializer.updater;
            interval = initializer.interval;
            callback = initializer.callback;
            for (var i = element.iterator(); i.hasNext();) {
                var el = i.next();
                el['_custom'] = updater;
                function _() {
                    if (el['_custom']()) {
                       window.setTimeout(_, interval);  
                    } else {
                        // IE 7 wont allow deletion
                        if (!ns.isIE(7) && !ns.isIE(6)) {
                            delete el['_custom'];                           
                        } 
                        h._getFn(callback)();
                    }
                };
                _();
            }
        };
        
        /**
         * Creates an animation by iterating through a collectiosn of images.
         * 
         * @param {Object} initailizer with properties:
         *       images: an array of image src values OR an array of
         *               objects with properties of 'image' and 'interval'
         *               so that each image may be provided a unique interval value
         *       interval: an optional property (if an array of objects is provided
         *                 for 'images') to specifiy a global interval value,
         *                 to specify how many milliseconds between animation updates
         *       callback: an optional function to be called at termination of
         *                 animation
         *       attrs:  an optional object that contains name/value pairs
         *               of HTML attributes to apply to HTMLImageElements created
         */   
        this.animate = function(initializer) {
            if (arguments.length > 1) {
                throw new Error("The arguments have been changed: expects one initializer object (see docs).");
            }
            var images, interval, callback, attrs;
            images = ns.array(initializer.images);
            if (images.size() < 2) {
                throw new Error("Must provide at least 2 images ot create an animation.");
            }
            interval = (!h._x(initializer.interval)) ? -1 : initializer.interval;
            callback = h._getFn(initializer.callback);
            attrs = (h._x(initializer.attrs)) ? initializer.attrs : {};
            
            //TODO: for now, only use single element
            var el = element[0];
            var pics = ns.array();
            images.each(
                function(item) {
                    var img = new Image();
                    if (typeof item == "string") {
                        img.src = item;
                    } else  {
                        img.src = item.image;
                    }
                    for (var att in attrs) {
                        ns.attribute(img, att, attrs[att]);                      
                    }
                    pics.push(img);
                }
            );
            function getInterval(index) {
                if (index >= images.size()) {
                    return -1;
                }
                if (typeof images[index] == "string") {
                    return interval;
                } else {
                    return images[index].interval;
                }
            }
            function show(index) {
                if (cancelled) {
                    return;
                }
                if (index + 1 == images.size()) {
                    callback();
                    return;
                }
                var newPic = pics[index + 1];
                ns.empty(el);
                ns.append(el, newPic);
                setTimeout(function() { show(index + 1); }, getInterval(index));
            }
            ns.append(el, pics[0]);
            show(0);
        };
        
        /**
         * Moves the animated element(s) by the distance provided in
         * initializer over the specified period.
         * 
         * @param {Object} initializer with properties:
         *       x : the total -x distance to move the animated element
         *       y : the total -y distance to move the animated element 
         *       duration : the total amount of time to perform animation
         *       callback : an optional function to provide that will be 
         *                  called at animation termination
         *       updater : an optional function to provide that will be 
         *                  called on each animation iteration
         */
        this.move = function(initializer) {
            if (arguments.length > 1) {
                throw new Error("The arguments have been changed: expects one initializer object (see docs).");
            }
            var x, y, duration, callback, updater;
            x = initializer.x;
            y = initializer.y;
            duration = ns.parseNumber(initializer.duration, 1);
            duration = Math.max(duration, 1);
            callback = h._getFn(initializer.callback);
            updater = h._getFn(initializer.updater);
            
            // used to track if all elements are done
            var id = jwyre.id(); 
            internalMovers[id] = element.size();
            log("Adding - GroupId: " + id + ", internal count=" + internalMovers[id]);

            //TODO: for now, only want callback fired once; figure out if 
            // the need for callback firing at the end of each is needed
            element.each(function(item) {
                add("start", Mover, {
                    "dX" : x,
                    "dY" : y,
                    "el" : item,
                    "duration" : duration,
                    "callback" : callback,
                    "updater" : updater,
                    "groupId" : id,
                    "moverId" : jwyre.id(),
					"_this" : self
                });
            });
        };
        
        /**
         * Moves the animated element(s) to the given point provided in
         * initializer over the specified period.
         * 
         * @param {Object} initializer with properties:
         *       x : the ending x coordinate
         *       y : the ending y coordinate
         *       isRelative : an optional boolean value to specifiy
         *       whether ending position is a relative position within
         *       the element's parent, or an absoluet position on the page (default is true)
         *       duration : the total amount of time to perform animation
         *       callback : an optional function to provide that will be 
         *                  called at animation termination
         *       updater : an optional function to provide that will be 
         *                  called on each animation iteration
         */
        this.moveTo = function(initializer) {
            if (arguments.length > 1) {
                throw new Error("The arguments have been changed: expects one initializer object (see docs).");
            }
            var x, y, isRelative, duration, callback, updater;
            x = initializer.x;
            y = initializer.y;
            isRelative = ns.parseBoolean(initializer.isRelative, true);
            duration = ns.parseNumber(initializer.duration, 1);
            duration = Math.max(duration, 1);
            callback = h._getFn(initializer.callback);
            updater = h._getFn(initializer.updater);
			
            // used to track if all elements are done
            var id = jwyre.id(); 
            internalMovers[id] = element.size();
            log("Adding - GroupId: " + id + ", internal count=" + internalMovers[id]);
			
            //TODO: for now, only want callback fired once; figure out if 
            // the need for callback firing at the end of each is needed
            element.each(function(item) { 
                var pos = ns.position(item, isRelative);
                var dX = x - pos.left;
				var dY = y - pos.top;
				
                add("start", Mover, {
                    "dX" : dX,
                    "dY" : dY,
                    "el" : item,
                    "duration" : duration,
                    "callback" : callback,
                    "updater" : updater,
                    "groupId" : id,
					"moverId" : jwyre.id(),
                    "_this" : self
                });
            });
        };
     
        /**
         * 
         * @param {integer} duration
         * @param {float} begin (optional)
         * @param {float} end (optional, required if begin specified)
         * @param {Function} callback (optional)
         */
        this.fade = function(duration, begin, end, callback) {
            add("start", Fader, {
                "fElement" : element, 
                "duration" : duration, 
                "begin" : begin, 
                "end" : end, 
                "callback" : callback,
                "_this" : self
            });
        };
        
        /**
         * 
         * @param {integer} duration
         * @param {Function} callback (optional)
         */
        this.fadeIn = function(duration, callback) {
            add("start", Fader, {
                "fElement" : element, 
                "duration" : duration, 
                "begin" : 0, 
                "end" : 100, 
                "callback" : callback,
                "_this" : self
            });
        };

        /**
         * 
         * @param {integer} duration
         * @param {Function} callback (optional)
         */
        this.fadeOut = function(duration, callback) {
            add("start", Fader, {
                "fElement" : element, 
                "duration" : duration, 
                "begin" : 100, 
                "end" : 0, 
                "callback" : callback,
                "_this" : self
            });
        };
        
        /**
         * 
         */   
        this.cancel = function() {
            cancelled = true;
        };
		
		/**
		 * 
		 */
		this.isCancelled = function() {
			return cancelled;
		};
        
        /**
         * 
         */   
        this.reset = function() {
            cancelled = false;
        };
    }    
	
	ns.animator = function(element) { return new _Animator(element); };        
})(jwyre);
/**
 * @projectDescription
 * Provides animation functionality, such as moving and fading.
 * 
 * @author Ryan Hardy
 * @version 1.0
 */
(function(ns) {
    // check that jwyre is present
    if (!ns) {
        throw new Error("jwyre.js is not present, and is required.");
    }
    // if present, make shortcuts to tools
    var h = ns._INTERNALS;
	
    // create a reference to today's date object
    var _today = ns.date();
    // mapping of date strings mapped to DOM elements
    var monthCache = {};
    // helper function used to create string used as key in monthCache object           
    function _getStr(date, calendar) {
        return calendar.getId() + "_" + date.getMonth() + "_" + date.getYear();
    }
    // helper function used to get total computed width of an element
    function _getWidth(el) {
        var w = 0;                  
        el = ns.element(el);
        w += ns.dimensions(el).width;
        w += ns.parseNumber(ns.style(el, "padding-left"), 0);
        w += ns.parseNumber(ns.style(el, "padding-right"), 0);
        w += ns.parseNumber(ns.style(el, "margin-left"), 0);
        w += ns.parseNumber(ns.style(el, "margin-right"), 0);
        return w;
    }
    // used to set the width of the entire calendar container based on 
    // internal widths
    function _fixWidth(hdrWidth, cId, html) {
        if (hdrWidth == null) {
            hdrWidth = 0;
            hdrWidth += _getWidth("#back_" + cId);
            hdrWidth += _getWidth("#months_" + cId);
            hdrWidth += _getWidth("#years_" + cId);
            hdrWidth += _getWidth("#next_" + cId);
            ns.style(html, "width", hdrWidth + "px");
            hdrWidth = ns.parseNumber(hdrWidth, null);
        }
        
        var tWidth = ns.dimensions("#dayGrid_" + cId).width;
        ns.style("#dayGrid_" + cId, "margin-left", ((hdrWidth - tWidth) / 2) + "px");
        
        return hdrWidth;
    }
    // used to convert or wrap the text into/in HTML, as needed, and applies the given id
    function _wrap(text, id) {
        var html = ns.create(text);
        if (!h._x(html)) {
            html = ns.create("<span>" + text + "</span>");
        }
        ns.attribute(html, "id", id);
        return html;
    }
    
    // defaults used to initialize Calendar objects
    var defaultProps = {
        global : {
            "position" : "relative",
            "background-color" : "white",
            "border" : "1px black solid",
            "padding" : "5px",
            "font-size" : "10px",
            "font-family" : "Arial",
            "cursor" : "pointer"
        },
        header : {
            "position" : "relative",
            "margin-bottom" : "10px",
            "padding-top" : "5px"
        },
        todayStyles : {
            "background-color" : "rgb(240, 240, 255)"
        },
        dayStyles : {
            "border" : "1px black solid", 
            "background-color" : "white", 
            "padding" : "3px",
            "text-align" : "center",
            "text-decoration" : "none"
        },
        offMonthDayStyles : {
            "background-color" : "rgb(220, 220, 220)"
        },
        dayHoverStyles : {
            "background-color" : "rgb(200, 200, 200)",
            "text-decoration" : "underline"
        },
        monthStyles : {
            "position" : "relative",
            "background-color" : "white"
        },
        tableAttrs : {},
        callback : function(date) { alert(date); },
        backHtml : "[back]",
        nextHtml : "[next]",
        closeHtml : "[close]",
        closeStyles : {
        },
        footerStyles : {
            "text-align" : "center"                
        },
        navStyles :  {
            "width" : "100px",
            "font-size" : "10px",
            "margin" : "0 5px 0 5px"
        },
        navHoverStyles : {},
        selectStyles : {
            "width" : "80px",
            "margin" : "0 5px 0 5px"
        },
        minDate : ns.date(),
        maxDate : ns.date().advanceYears(5)
    };
    
    /**
     * 
     * @param {Object} initializer with properties:
     *        {Object} todayStyles (optional) :
     *        {Object} dayStyles (optional) :
     *        {Object} offMonthDayStyles (optional) :
     *        {Object} dayHoverStyles (optional) :
     *        {Object} monthStyles (optional) :
     *        {Object} tableAttrs (optional) :
     *        {Object} callback (optional) :
     *        {Object} backHtml (optional) :
     *        {Object} nextHtml (optional) :
     *        {Object} navStyles (optional) :
     *        {Object} navHoverStyles (optional) :
     *        {Object} selectStyles (optional) :
     *        {Object} minDate (optional) :
     *        {Object} maxDate (optional) :
     */
    function _Calendar(initializer) {
        // the container box for the calendar
        var html;
        // the table holding the days info
        var daysHtml;
        // a date object representing the currently displayed info  
        var currDate = _today;
        // indicates if calendar is presently showing
        var isShowing = false;
        // indicates if the Calendar has already been built and displayed
        var isBuilt = false;
        // the select elements for months and years
        var months, years;
        // the currently selected elements for months and years
        var currMo, currYr;
        // the bounds for dates
        var minDate, maxDate;
        // a unique identifier for this instance
        var cId = ns.id();            
        // cached computer width of calendar header; used to fix width of entire widget
        var hdrWidth = null;
        // reference to self (prevents 'this' issues)
        var self = this;
        
        // ensure proper defaults are set
        initializer.callback = h._getFn(initializer.callback);
        
        // array containing initialization properties that are scalar values
        var scalars = ns.array("minDate", "maxDate", "backHtml", "nextHtml", "closeHtml", "callback");
        
        /**
         * Helper function used to check both provided and default 
         * properties to obtain a value; not for external use.
         * 
         * @param {string} name
         */
        this._get = function(name) {
            if (scalars.contains(name)) {
                return h._x(initializer[name]) ? initializer[name] : defaultProps[name];
            }
            var obj = (initializer.hasOwnProperty(name)) ? initializer[name] : {};
            return h._combine(obj, defaultProps[name]);
        };
        
        /**
         * Obtains the unique id of this instance.
         */
        this.getId = function() {
            return cId;
        };
                
        /**
         * @param {Object} init object with properties:
         *       {string || HTMLElement} parent :
         *       {jwyre.Date} date (optional) :
         *       {number} xOffset (optional) :
         *       {number} yOffset (optional) :
         *       {number} zIndex (optional) :
         */ 
        this.show = function(init) {
            if (isShowing) {
                return;
            }
            // is this just a call to reshow a hidden calendar?
            if (isBuilt && !h._x(init)) {
                ns.show(html);
                isShowing = true;
                return;                 
            }
            if (!isBuilt && !h._x(init)) {
                throw new Error("Must provide an initializer to build Calendar.");
            }
            // if no date is provided, use today
            ns.append(document.body, _getHtml(init.date || ns.date()));
            hdrWidth = _fixWidth(hdrWidth, cId, html);
            // position the element in ref. to parent, w/ offsets if present
            var pos = ns.position(init.parent);
            var dims = ns.dimensions(init.parent);
            var x = ns.parseNumber(init.xOffset, 0) + pos.left + dims.width;
            var y = ns.parseNumber(init.yOffset, 0) + + pos.top;
            var z = ns.parseNumber(init.zIndex, 100) + "";
            ns.styles(html, {"top" : y + "px", "left" : x + "px", "z-index" : z});
            ns.show(html);
            isBuilt = isShowing = true;
        };
        
        /**
         * Hides the Calendar object (may be re-shown in call to show()
         * without an initializer object).
         */
        this.hide = function() {
            if (!isShowing) {
                return;
            }
            ns.hide(html);
            isShowing = false;
        };
        
        /**
         * Removes the Calendar object's HTML from the DOM (may be re-shown 
         * in call to show() only with an initializer object).
         */
        this.remove = function() {
            ns.remove(html);
            isBuilt = isShowing = false;
        };
        
        // gets the HTML for this instance, either by building it first,
        // or updating it based on provided date
        function _getHtml(date) {
            if (h._x(html)) {
                _display(date);
                return html;
            }
            try {
                html = ns.create("<div></div>");
                ns.styles(html, self._get("global"));
                var header = ns.create("<div id='header_" + cId + "'></div>");
                ns.styles(header, self._get("header"));
                ns.append(html, header);
                
                // make back link
                var back = _wrap(self._get("backHtml"), "back_" + cId);
                ns.append(header, back);
                ns.styles(back, self._get("navStyles"));
                ns.addHover(back, self._get("navHoverStyles"));
                ns.addClick(back, function(event) {
                    _display(currDate.advanceMonths(-1));
                    return ns.kill(event);
                });
                ns.disableSelection(back);
                
                // add month drop-down
                months = ns.create("<select id='months_" + cId + "'></select>");
                ns.append(header, months);
                ns.styles(months, self._get("selectStyles"));
                ns.array(ns.Date.MONTHS).each(function(val, ctr) {
                    ns.append(months, ns.create("<option value='" + ctr + "'>" + this.name + "</option>"));
                });
                ns.addListener(months, "change",
                    function(event) {
                        var mo = ns.selected(months);
                        if (_display(currDate.setMonth(mo))) {
                            currMo = mo;
                        } else {
                            _display(currDate);
                        }
                        return ns.kill(event);
                    }
                );
                
                // set values for use in _display() checking
                minDate = self._get("minDate");
                maxDate = self._get("maxDate");
                   
                // add year drop-down
                years = ns.create("<select id='years_" + cId + "'></select");
                ns.append(header, years);
                ns.styles(years, self._get("selectStyles"));
                ns.range(minDate.getYear(), maxDate.getYear()).each(function() {
                    ns.append(years, ns.create("<option value='" + this + "'>" + this + "</option>"));
                });
                ns.addListener(years, "change", 
                    function(event) {
                        var yr = ns.selected(years);
                        if (_display(currDate.setYear(yr))) {
                            currYr = yr;
                        } else {
                            _display(currDate);
                        }
                        return ns.kill(event);
                    }
                );

                // make next link
                var next = _wrap(self._get("nextHtml"), "next_" + cId);
                ns.append(header, next);
                ns.styles(next, self._get("navStyles"));
                ns.addHover(next, self._get("navHoverStyles"));
                ns.addClick(next, function(event) {
                    _display(currDate.advanceMonths(1));
                    return ns.kill(event);
                });
                ns.disableSelection(next);
                
                daysHtml = self._getMonthHtml(date);
                ns.append(html, daysHtml);
                
                var footer = ns.create("<div></div>");
                ns.append(html, footer);
                ns.styles(footer, self._get("footerStyles"));
                var closer = _wrap(self._get("closeHtml"), "close_" + cId);
                ns.append(footer, closer);
                ns.styles(closer, self._get("closeStyles"));
                ns.addClick(closer, function(event) { self.hide(); return ns.kill(event); });
                
                _display(currDate);
                return html;
            } catch (e) {
                hdrWidth = html = null;
                console.log(e.message);
            }
        };
        
        // sets the current day grid and month/year selection (if date is in bounds)
        // returns false if it is out of bounds, true otherwise
        function _display(date) {
            if (!date) {
                return false;
            }
            if (date.isBefore(minDate) || date.isAfter(maxDate)) {
                return false;
            }
            currDate = date;
            var temp = self._getMonthHtml(date, self);
            ns.replace(daysHtml, temp);
            ns.select(months, currDate.getMonth());
            ns.select(years, currDate.getYear());
            daysHtml = temp;
            try {
                hdrWidth = _fixWidth(hdrWidth, cId, html);
            } catch (e) {
                hdrWidth = null;
                // catch and eat; this will throw ex. on first display in IE
            }
            return true;
        }
    }
    /**
     * Builds HTML for each calendar day; not for external use.
     * 
     * @param {jwyre.Date} refDate
     * @param {jwyre.Date} date
     */
    _Calendar.prototype._getDayHtml = function(refDate, date) {
        var html = ns.create("<td>" + date.getDay() + "</td>");
        // check if this is today
        ns.styles(html, this._get("dayStyles"));            
        if (date.equals(_today)) {
            ns.styles(html, this._get("todayStyles"));                   
        } 
        // check if this date is not in the present month
        else if (date.getMonth() != refDate.getMonth()) {
            ns.styles(html, this._get("offMonthDayStyles"));                 
        }
        ns.addHover(html, this._get("dayHoverStyles"));
        var calendar = this;
        ns.addClick(html,
            function(event) {
                h._getFn(calendar._get("callback")).call(calendar, date);
                return ns.kill(event);
            }
        );
        return html;
    };
    /**
     * Builds HTML for each calendar week; not for external use.
     * 
     * @param {jwyre.Date} refDate
     * @param {jwyre.Date} date
     */
    _Calendar.prototype._getWeekHtml = function(refDate, date) {
        var week = date.getWeek();
        var html = ns.create("<tr></tr>");
        var calendar = this;
        week.each(function() {
            ns.append(html, calendar._getDayHtml(refDate, this, calendar))
        });
        return html;
    };
    /**
     * Builds HTML for each calendar month; not for external use.
     * 
     * @param {jwyre.Date} date
     */
    _Calendar.prototype._getMonthHtml = function(date) {
        // check cache first
        var html = monthCache[_getStr(date, this)];
        if (h._x(html)) {
            return html;
        }
        html = ns.create("<table id='dayGrid_" + this.getId() + "'></table>");
        ns.styles(html, this._get("monthStyles"));
        ns.iterator(this._get("tableAttrs"), true).getArray().each(function() {
            ns.attribute(html, this.name, this.value);
        });
        // this is done for IE compliance
        ns.append(html, ns.create("<thead></thead>"));
        
        var tbody = ns.create("<tbody></tbody>");    
        ns.append(html, tbody);
        ns.append(html, ns.create("<tfoot></tfoot>"));
        var mo = date.getMonth();
        var fDay = date.getFirstDay();
        
        while (true) {
            ns.append(tbody, this._getWeekHtml(date, fDay, this));
            fDay = fDay.advanceDays(7);
            if (fDay.getWeek()[0].getMonth() != mo) {
                break;
            }
        }
        monthCache[_getStr(date, this)] = html;
        
        return html;
    };
    
    // used to track Calendar objects added to page with call to Calendar.add()
    var calendars = ns.array();
    
    // create Calendar namespace and add global functions
    ns.Calendar = {
        /**
         * Adds a standard listener to an input element.  Returns the Calendar
         * object that is created.  If singleton is provided and true, will 
         * ensure that only one Calendar object that has been added to page
         * with Calendar.add() is visible on the page at one time.  A unique name
         * must be provided to enable interaction with the underlying Calendar 
         * object.
         * 
         * @param {Object} initializer with properties (in addition to Calendar
         * constructor properties):
         * 
         *     {string || HTMLElement} input :
         *     {integer} x (optional) :
         *     {integer} y (optional) :
         *     {integer} z (optional) :
         *     {boolean} singleton (optional) :
         */
        add : function(initializer) {
            var input = ns.element(initializer.input);
            // needed for calendar.show()
            initializer.parent = input;
            initializer.x = ns.parseNumber(initializer.x, 0);
            initializer.y = ns.parseNumber(initializer.y, 0);
            initializer.z = ns.parseNumber(initializer.z, 0);
            initializer.singleton = ns.parseBoolean(initializer.singleton, false);
            var fn = h._getFn(initializer.callback);
            initializer.callback = function(date) {
                if (fn.call(this, date) != false) {
                    input.value = "" + date;
                    this.hide();                    
                }
            };
            var calendar = new _Calendar(initializer);
            function _show(event) {
                calendars.each(function() { if (this.isSingle) { this.cal.hide(); } });
                calendar.show(initializer);
                // do not re-initialize on subsequent shows
                initializer = null;
                return ns.kill(event);
            }
            // keep track of cal info so that safe deletion can occur (see purge())
            calendars.push({ "cal" : calendar, "isSingle" : initializer.singleton, "input" : input, "callback" : _show });
            ns.addListener(input, "focus", _show, ns.Event.CAPTURE);
            
            return calendar;
        },
        remove : function(calendar) {
            for (var i = calendars.iterator(); i.hasNext(); ) {
                var obj = i.next();
                if (obj.cal == calendar) {
                    ns.removeListener(obj.input, "focus", obj.callback, ns.Event.CAPTURE); 
                    obj.cal.remove();
                    calendars.remove(obj);
                    break;
                }
            }
        },
        /**
         * Hides all Calendar objects that are showing that have been added
         * with call to Calendar.add().
         */
        hide: function() {
            calendars.each(function() { this.cal.hide(); });
        },
        /**
         * Clears all internal caches; used to manage memory consumption.
         */
        purge : function() {
            this.hide();
            calendars.each(function() { 
                ns.removeListener(this.input, "focus", this.callback, ns.Event.CAPTURE); 
                this.cal.remove();
            });
            delete calendars;
            calendars = ns.array();
            delete monthCache;
            monthCache = {};
        }
    };
    // assign basic constructor function
    ns.calendar = function(initializer) { return new _Calendar(initializer); };
})(jwyre);  

/**
 * @projectDescription
 * Provides animation functionality, such as moving and fading.
 * 
 * @author Ryan Hardy
 * @version 1.0
 */
(function(ns) {
    // check that jwyre is present
    if (!ns) {
        throw new Error("jwyre.js is not present, and is required.");
    }
    // if present, make shortcuts to tools
    var h = ns._INTERNALS;
		
	function _ColorPanel() {
	    // the container for the color panel
	    var panel;
	    // the callback function for the currently displayed color panel
	    var cb;
		// get reference to self to avoid 'this' use issues
	    var self = this;
		
	    var basics = jwyre.array(
	       "#000000", "#333333", "#666666", "#999999",
	       "#CCCCCC", "#FFFFFF", "#FF0000", "#00FF00",
	       "#0000FF", "#FFFF00", "#FF00FF", "#00FFFF");
	    
	    // the currently chosen color
	    var current = "#000000";
	        
	    // converts an rgb css value to a hex string
	    function convertToHex(rgb) {
	        rgb = jwyre.trim(rgb);
	        if (rgb.indexOf("#") == 0) {
	            return rgb;
	        }
	        rgb = rgb.replace("rgb(", "");      
	        rgb = rgb.replace(")", "");
	        var cs = rgb.split(/\s*,\s*/);
	        var hex = "#";
	        function pad(n) {
	            if (("" + n).length == 1) {
	                return "0" + n;
	            }
	            return n;
	        }
	        hex += pad(parseInt(cs[0]).toString(16));
	        hex += pad(parseInt(cs[1]).toString(16));
	        hex += pad(parseInt(cs[2]).toString(16));
	        
	        return hex.toUpperCase();
	    }
	    
	    /*
	     * 
	     */
	    function createPanel(callback) {
	        // create the container
	        panel = ns.create("<div></div>");
	        ns.styles(panel, 
	        {
	            "position" : "absolute",
	            "top" : "20px",
	            "left" : "20px",
	            "height" : "260px",
	            "width" : "300px",
	            "border" : "1px black solid",
	            "background-color" : "rgb(200, 200, 200)",
	            "z-index" : "10000"
	        });
	        
	        // create the controls area
	        var header = ns.create("<div></div>");
	        ns.styles(header, 
	        {
	            "position" : "absolute",
	            "top" : "0px",
	            "left" : "0px",
	            "height" : "40px",
	            "width" : "100%",
	            "border-bottom" : "1px black solid",
	            "background-color" : "rgb(180, 180, 180)",
	            "z-index" : "2"
	        });
	        ns.append(panel, header);
	        
	        // the preview box
	        var preview = ns.create("<div></div>");
	        ns.styles(preview, 
	        {
	            "position" : "absolute",
	            "top" : "10px",
	            "left" : "20px",
	            "height" : "20px",
	            "width" : "80px",
	            "border" : "1px black solid",
	            "background-color" : current,
	            "z-index" : "2"
	        });
	        ns.append(header, preview);
	        ns.addHover(preview, 
	            function() {
	                ns.style(preview, "border-color", "white");
	            },
	            function() {
	                ns.style(preview, "border-color", "black");              
	            }
	        );
	        ns.addClick(preview,
	            function(event) {
	                ns.style(preview, "border-color", "black");              
	                cb(current);                
	                return ns.kill(event);
	            }  
	        );
	        
	        // the color value box
	        var cOutput = ns.create("<input type='text' readonly='readonly' value='" + current + "'/>");
	        ns.styles(cOutput, 
	        {
	            "position" : "absolute",
	            "top" : "12px",
	            "left" : "110px",
	            "height" : "12px",
	            "width" : "50px",
	            "padding" : "2px",
	            "border" : "1px black solid",
	            "background-color" : "white",
	            "z-index" : "2",
	            "font-family" : "Arial",
	            "font-size" : "10px"
	        });
	        ns.append(header, cOutput);
	
	        // the 'close' control
	        var close = ns.create("<div>close</div>");
	        ns.styles(close, 
	        {
	            "position" : "absolute",
	            "top" : "12px",
	            "left" : "260px",
	            "height" : "12px",
	            "padding" : "2px",
	            "z-index" : "2",
	            "font-family" : "Arial",
	            "font-size" : "10px",
	            "cursor" : "pointer"
	        });
	        ns.append(header, close);
	        
	        ns.addHover(close,
	            function() {
	                ns.style(close, "text-decoration", "underline");
	            },
	            function() {
	                ns.style(close, "text-decoration", "none");              
	            }
	        );
	        ns.addClick(close, 
	            function(event) { 
	                ns.style(close, "text-decoration", "none"); 
	                self.hide();
	            }
	        );
	        
	        // stack used to keep track of the last selected colors
	        var lastCubes = ns.array();
	        var lastColors = ns.array();
	        
	        function addLastColor(color) {
	            lastColors = ns.array();
	            for (var i = lastCubes.length - 1; i > 0; i--) {
	                var ths = lastCubes[i];
	                var lst = lastCubes[i - 1];
	                var c = ns.attribute(lst, "name");
	                if (!lastColors.contains(c)) {
	                    lastColors.push(c);
	                }
	                ns.attribute(ths, "name", c);
	                ns.style(ths, "background-color", c);
	            }
	            ns.attribute(lastCubes[0], "name", color);
	            ns.style(lastCubes[0], "background-color", color);
	        }
	                                
	        // create hover and click events for color cubes
	        function fn(c) {
	            // performs unhover ops; shared by unhover and click fns
	            function reset() {
	                ns.styles(c, 
	                {
	                    "border-color" : "black",
	                    "z-index" : "1"
	                });
	                ns.style(preview, "background-color", current);
	                ns.attribute(cOutput, "value", current);             
	            }
	            return {
	                hover : function(event) {
	                    ns.styles(c, 
	                    {
	                        "border-color" : "white",
	                        "z-index" : "2"
	                    });
	                    ns.style(preview, "background-color", ns.style(c, "background-color"));
	                    ns.attribute(cOutput, "value", convertToHex(ns.attribute(c, "name")));
	                },
	                unhover : function(event) {
	                    reset();
	                },
	                click : function(event) {
	                    current = convertToHex(ns.attribute(c, "name"));
	                    ns.attribute(cOutput, "value", current);
	                    ns.style(preview, "background-color", current);
	                    
	                    // if we are choosing a color from the 'last colors' palette, dont add to palette again
	                    // also, do nto add if color is already present
	                    var t = ns.event(event).target;
	                    if (ns.attribute(t, "class") != "lastColor" && !lastColors.contains(current)) {
	                        addLastColor(current);
	                    }
	                    reset();
	                    cb(current);
	                    return ns.kill(event);
	                }
	            };
	        }
	
	        // creates a color cube with the gieven top and left positions,
	        // border color (bColor) and background color (bkColor)
	        function mkCube(top, left, bColor, bkColor, height, width) {
	            height = (!height) ? 12 : height;
	            width = (!width) ? 12 : width;
	            var cube = ns.create("<div name='" + bkColor + "'></div>");
	            ns.styles(cube, 
	            {
	                "position" : "absolute",
	                "top" : top + "px",
	                "left" : left + "px",
	                "height" : height + "px",
	                "width" : width + "px",
	                "border" : "1px " + bColor + " solid",
	                "background-color" : bkColor,
	                "z-index" : "1"
	            });         
	            var fns = fn(cube);
	            ns.addHover(cube, fns.hover, fns.unhover);
	            ns.addClick(cube, fns.click);
	            
	            return cube;
	        }
	
	        // create the basic color swatch area
	        var basic = ns.create("<div></div>");
	        ns.styles(basic, 
	        {
	            "position" : "absolute",
	            "top" : "40px",
	            "left" : "0px",
	            "height" : "180px",
	            "width" : "40px",
	            "border-right" : "1px black solid",
	            "background-color" : "rgb(240, 240, 250)",
	            "z-index" : "1"
	        });
	        ns.append(panel, basic);
	
	        // create the last-sed swatch panel
	        var lastSwatches = ns.create("<div></div>");
	        ns.styles(lastSwatches, 
	        {
	            "position" : "absolute",
	            "top" : "219px",
	            "left" : "0px",
	            "height" : "40px",
	            "width" : "100%",
	            "border-top" : "1px black solid",
	            "background-color" : "rgb(180, 180, 180)",
	            "z-index" : "1"
	        });
	        ns.append(panel, lastSwatches);
	        
	        // create last color cubes
	        for (var i = 0; i < 12; i++) {
	            var left = 8 + (24 * i);
	            var cube = mkCube(10, left, "black", "#FFFFFF", 20, 20);
	            ns.attribute(cube, "class", "lastColor");
	            lastCubes.push(cube);
	            ns.append(lastSwatches, cube);
	        }
	                
	        // create basic color cubes
	        for (var i = 0; i < 12; i++) {
	            var top = 10 + (13 * i);
	            var cube = mkCube(top, 13, "black", basics[i]);
	            ns.append(basic, cube);
	        }
	        
	        var swatches = ns.create("<div></div>");
	        ns.styles(swatches, 
	        {
	            "position" : "absolute",
	            "top" : "50px",
	            "left" : "55px",
	            "height" : "160px",
	            "width" : "230px",
	            "z-index" : "1"
	        });
	        ns.append(panel, swatches);
	        
	        // starting red values for each cube        
	        var reds = ns.array(204, 102, 0, 255, 153, 51);
	        // create swatch cubes
	        for (var i = 0; i < 6; i++) {
	            var r = reds[i];
	            var top = 0 + ((i < 3) ? 0 : 78);// + (i * 90);
	            var left = 0 + ((i % 3) * 78);
	            var sBox = ns.create("<div></div>");
	            ns.styles(sBox, {
	                "position" : "absolute",
	                "top" : top + "px",
	                "left" : left + "px"
	            });
	            for (var j = 0; j < 6; j++) {
	                // greens ascend for 1st 3, then descend
	                var g = (i < 3) ? 255 - (j * 51) : 0 + (j * 51);
	                
	                for (var k = 0; k < 6; k++) {
	                    // blues switch between ascending/descending
	                    var b = (i == 1 || i == 4) ? 0 + (k * 51) : 255 - (k * 51);
	                    
	                    var top = 0 + (j * 13);
	                    var left = 0 + (k * 13);
	                    var color = "rgb(" + r + ", " + g + ", " + b + ")";
	                    var cube = mkCube(top, left, "black", color);
	                    ns.append(sBox, cube);
	                }
	            }
	            ns.append(swatches, sBox);
	        }
	    }
	    
	    /**
	     * Displays the color panel.
	     */
	    this.show = function(item, top, left, callback) {
	        if (!panel) {
	            createPanel();
	        }
	        cb = (!callback || typeof callback != "function") ? function() {} : callback;
	        ns.append(document.body, panel); 
	        
	        // the following code is used to determine if the ColorPanel will be
	        // rendered off screen, and moves it if so
	        
	        //TODO: there is a discrepancy in this val between browsers; figure out better solution
	        var bDims = ns.dimensions(document.body);
	        var scroll = ns.scroll();
	        // 200 is a 'magic' number
	        var winH = bDims.height - 200 + scroll.top;
	        var winW = bDims.width - 0 + scroll.left;
	        
	        var pos = ns.position(item);
	        var dims = ns.dimensions(item);
	        var cDims = ns.dimensions(panel);
	        
	        top = pos.top;
	        left = pos.left + dims.width;       
	
	        if (top + cDims.height > winH) {
	            // add 10 as an addtl. 'buffer'
	            var diff = (top + cDims.height) - winH + 10;
	            top -= diff;
	        }
	        if (left + cDims.width > winW) {
	            left = left - cDims.width - dims.width;
	        }
	                
	        ns.position(panel, left, top);
	    };
	
	    /**
	     * Hides/removes the color panel.
	     */
	    this.hide = function() {
	        if (!panel) {
	            return;
	        }
	        ns.remove(panel);
	    };
	}
	ns.colorPanel = function(initializer) { return new _ColorPanel(initializer); };
})(jwyre);

/**
 * @projectDescription
 * Creates a drop-shadow effect on a given element.
 * 
 * May be customized with the following initializer properties:
 * 
 * {
 *      element {string || HTMLElement} :
 *      color {string} :
 *      vOffset {number} : (optional)
 *      hOffset {number} : (optional)
 *      fontSize {number} : (optional)
 * }
 * 
 * @author Ryan Hardy
 * @version 1.0
 */
(function(ns) {
    // check that jwyre is present
    if (!ns) {
        throw new Error("jwyre.js is not present, and is required.");
    }
    // if present, make shortcuts to tools
    var h = ns._INTERNALS;
	
    function _DropShadow(initializer) {
        initializer.element = ns.element(initializer.element);
        if (!h._x(initializer.element)) {
            throw new Error("Element is null.");
        }
        h._checkType(initializer.color, "string", "Color must be a CSS color specification.");            
        initializer.vOffset = ns.parseNumber(initializer.vOffset, 2);
        initializer.hOffset = ns.parseNumber(initializer.hOffset, 2);
        initializer.shadows = ns.parseNumber(initializer.shadows, 1);
        initializer.reductionFactor = ns.parseNumber(initializer.reductionFactor, 10);
         
        if (h._x(initializer.fontSize)) {
            h._checkType(initializer.fontSize, "number", "Font size must be a number (in pixels).");
            initializer.fontSize += "px";
        } else {
            initializer.fontSize = ns.style(initializer.element, "fontSize");
        }
        var text = ns.text(initializer.element);
        if (!h._x(text) || h._strEq(text, "")) {
            throw new Error("There is no text within element.");
        }
        var forefront = ns.create("<div>" + text + "</div>");
        ns.styles(forefront, {
            "position" :  "absolute",
            "top" :  "0",
            "left" :  "0",
            "zIndex" :  "2"
        });
        ns.empty(initializer.element);
        ns.append(initializer.element, forefront);
        
        function getReducedColor(color, factor) {
            // returns an array
            color = h._convertColor(color, true);
            var hex = "#";
            color.each(function() { 
                var v = 255 - (parseInt(this, 16) * factor);
                hex += Math.round(v).toString(16);
            });
            return hex;
        }
        
        ns.range(1, initializer.shadows).each(
            function() {
                var v, h, color;
                if (initializer.shadows == 1) {
                    v = initializer.vOffset;
                    h = initializer.hOffset;
                    color = initializer.color;
                } else {
                    v = (this * initializer.vOffset);
                    h = (this * initializer.hOffset);
                    var fct = 100 - (this * initializer.reductionFactor);
                    color = getReducedColor(initializer.color, fct / 100);
                }
                var shadow = ns.create("<div>" + text + "</div>");
                ns.styles(shadow, {
                    "position" : "absolute",
                    "top" : v + "px",
                    "left" : h + "px",
                    "zIndex" : "1",
                    "fontSize" : initializer.fontSize,
                    "color" : color
                }); 
                ns.append(initializer.element, shadow);                  
            }
        );
    }
    
    ns.dropShadow = function(initializer) { return new _DropShadow(initializer); };
})(jwyre);

/**
 * @projectDescription
 * Provides animation functionality, such as moving and fading.
 * 
 * @author Ryan Hardy
 * @version 1.0
 */
(function(ns) {
    // check that jwyre is present
    if (!ns) {
        throw new Error("jwyre.js is not present, and is required.");
    }
    // if present, make shortcuts to tools
    var h = ns._INTERNALS;
	
    // add Behavior object constants into jwyre namespace
    ns.Gallery = {
        Behavior : {
            FADER : "_fader",
            ROTATOR : "_rotator"
        }
    };
    
    // sets the image source, if addPic is true, or inner text on item 
    function setItem(addPic, el, item) {
        if (addPic) {
            if (ns.tag(el) == "img") {
                ns.attribute(el, "src", item);
            } else {
                ns.style(el, "background-image", "url(" + item + ")");
            }                   
        } else {
            el.innerHTML = item;                    
        }
    }
    
    // if addPic is true, will preload the image(s)
    function preload(item, isPic) {
        if (!isPic) {
            return;
        }
        ns.array(item).each(function() { new Image().src = this; });
    }
            
    /**
     * 
     * @param {Object} initializer, with properties:
     * 
     *  element {string || HTMLElement} : (optional; behavior dependant)
     *  interval {integer}  : (optional)
     *  isPicGallery {boolean} : (optional)
     *  behavior {string || Object} : (optional)
     *  duration {integer} : (optional; behavior dependant)
     */
    function _Gallery(initializer) {
        initializer.element = ns.element(initializer.element);
        initializer.interval = ns.parseNumber(initializer.interval, 500);
        initializer.isPicGallery = ns.parseBoolean(initializer.isPicGallery, true);
        // indicates if Gallery has been started
        var isActive = false;
        
        var bhvr = initializer.behavior;
        // set the switch Behavior          
        if (bhvr) {
            // check if a built-in behavior is to be loaded
            if (typeof bhvr != "object") {
                switch (bhvr) {
                    case ns.Gallery.Behavior.FADER:
                       bhvr = new FadeBehavior(initializer);
                       break;
                    default:
                        bhvr = new RotateBehavior(initializer);
                }                   
            } else {
                // if is a custom object, check for correct functions
                try {
                    h._checkType(bhvr, "object");
                    h._checkType(bhvr.add, "function");
                    h._checkType(bhvr.start, "function");
                    h._checkType(bhvr.stop, "function");
                } catch (e) {
                    throw new Error("A custom behavior object must provide a start(), stop() and add() function.");
                }                   
            }
        } else {
            bhvr = new RotateBehavior();
        }
        // add Behavior to initializer      
        initializer.behavior = bhvr;
            
        /**
         * Adds an item (or array of items) to the gallery.  For image 
         * galleries, it can be a relative or absolute url, and also 
         * preloads the image.
         * 
         * @param {string | Array} item
         */
        this.add = function(item) {
            initializer.behavior.add(item);
        };
                    
        /**
         * @param {function} callback (optional; behavior dependent)
         */
        this.start = function(callback) {
            if (isActive) {
                throw new Error("Gallery has already been started.");
            }
            isActive = true;
            initializer.behavior.start(callback);
        };
        
        /**
         * 
         */
        this.stop = function () {
            isActive = false;
            initializer.behavior.stop();
        };
    }
    
    /**
     * 
     */
    function RotateBehavior(initializer) {
        // make aliases for simplicity
        var el = initializer.element, isPic = initializer.isPicGallery, intvl = initializer.interval;
        this._type = ns.Gallery.Behavior.ROTATOR;
        // manager of gallery items
        var items = ns.array().rotator();
        // the window.setInterval id
        var id = null;
        
        /**
         * 
         * @param {string | Array} item
         */
        this.add = function(item) {
            items.add(item);
            preload(item, isPic);
        };

        /**
         * 
         */
        this.start = function(callback) {
            callback = h._getFn(callback);
            function _switch() {
                var item = items.getNextItem();
                setItem(isPic, el, item);
                callback(item);
            }
            id = window.setInterval(_switch, intvl);
        };
        
        /**
         * 
         */
        this.stop = function () {
            window.clearInterval(id);
            id = null;
        };
    }

    /**
     * 
     */
    function FadeBehavior(initializer) {
        initializer.duration = ns.parseNumber(initializer.duration, 500);
        // make aliases for simplicity
        var el = initializer.element, isPic = initializer.isPicGallery, intvl = initializer.interval, dur = initializer.duration;
        this._type = ns.Gallery.Behavior.FADER;
        // manager of gallery items
        var items = ns.array().rotator();
        var timerId = null;
        
        /**
         * 
         * @param {string | Array} item
         */
        this.add = function(item) {
            items.add(item);
            preload(item, isPic);
        };
        
        /**
         * 
         */
        this.start = function(callback) {
            callback = h._getFn(callback);
            
            //    object used to hold elements and states for fading
            var fader1 = {};
            var fader2 = {};
            
            var clone = el.cloneNode(false);
            ns.insertAfter(clone, el);
            ns.cloneStyles(el, clone);
            //    set positioning
            ns.styles(clone, {
                "position" : "absolute",
                "top" : el.offsetTop + "px",
                "left" : el.offsetLeft + "px",
                "height" : el.offsetHeight + "px",
                "width" : el.offsetWidth + "px",
                "opacity" : "0"                 
            });
            // ensure unique id
            ns.attribute(clone, "id", "_jwyre_item_clone_" + new Date().getTime());
            setItem(isPic, el, items.getNextItem());
            setItem(isPic, clone, items.getNextItem());
            
            fader1.element = el;
            fader1.fadeIn = false;
            fader1.f = ns.animator(fader1.element);
            
            fader2.element = clone;
            fader2.fadeIn = true;
            fader2.f = ns.animator(fader2.element);       
            
            /*
             * Function used in call to window.setInterval(); switches items and starts
             * fade-in/fade-out behavior.
             */
            function _switch() {
                function fade(_fader) {
                    if (_fader.fadeIn) {
                        var item = items.getNextItem();
                        setItem(isPic, _fader.element, item);
                        ns.style(_fader.element, "zIndex", "2");
                        _fader.f.fadeIn(dur,
                            function() {
                                callback("in", item);
                                _fader.f.reset();
                            }
                         );
                    } else {
                        ns.style(_fader.element, "zIndex", "1");
                        _fader.f.fadeOut(dur, 
                            function() {
                                callback("out", item);
                                _fader.f.reset();
                            }
                        );
                    }
                    _fader.fadeIn = !_fader.fadeIn;
                }
                fade(fader1);
                fade(fader2);
            };              
            timerId = window.setInterval(_switch, intvl);
        };
        
        /**
         * Stops the fader.
         */
        this.stop = function () {
            window.clearInterval(timerId);
            timerId = null;
        };
    }
    
    ns.gallery = function(initializer) { return new _Gallery(initializer); }
})(jwyre);
/**
 * @projectDescription
 * 
 * Creates a magnification area upon an image.
 * 
 * @author Ryan Hardy
 * @version 1.0
 */ 
(function(ns) {
	// check that jwyre is present
	if (!ns) {
	    throw new Error("jwyre.js is not present, and is required.");
	}
	// if present, make shortcuts to tools
	var h = ns._INTERNALS;

	function _Magnifier(imageContainer, viewBox, useIeHack) {
        useIeHack = (useIeHack === undefined) ? false : useIeHack;
        
        /*
         *  init class members
         */
        
        //  imageContainer is the surrounding element of the Image element
        imageContainer = ns.element(imageContainer);
        
        // obtains a node of the specified type by examining the element, or 
        // deep-iterating through all children nodes until an element of the 
        // specified type is found
        // returns null if none found
        //TODO: move this functionality and references to _elements()
        function _getNode(element, type) {
            if (!element || !type) {
                return null;    
            }
            if (element.nodeName == type) {
                return element; 
            } else {
                for (var i in element.childNodes) {
                    var temp = _getNode(element.childNodes[i], type);
                    if (temp != null) {
                        return temp;
                    }
                }
            }
            return null;
        }
        
        //  img is the actual image element
        // TODO: handle this with _elements() function
        var img = _getNode(imageContainer, "IMG");
        if (!img) {
            throw new Error("The image element is null.");
        }

        //  if an Image is passed in, rather than a containing elemenet, 
        //  create a container element and add the Image
        if (imageContainer == img) {
            imageContainer = document.createElement("div");
            img.parentNode.appendChild(imageContainer);
            ns.style(imageContainer, "top", img.offsetTop + "px");
            ns.style(imageContainer, "left", img.offsetLeft + "px");
            ns.style(imageContainer, "height", img.offsetHeight + "px");
            ns.style(imageContainer, "width", img.offsetWidth + "px");
            imageContainer.appendChild(img);
        }

        /*
         *  Returns either the view-box element provided as a constructor arg,
         *  or creates a view-box element dynamically.
         */
        function createViewbox(element) {
            if (element) {
                element = ns.element(viewBox);               
            } else {
                element = document.createElement("div");    
                imageContainer.appendChild(element);
                ns.style(element, "border", "2px black solid");
                ns.style(element, "backgroundColor", "transparent");
            }           
            
            return element;
        }
        
        //  create magnifier viwew-box
        var viewbox = createViewbox(viewBox);
        if (!viewbox) {
            throw new Error("The view box element is null");
        }
        
        // create magnified image
        var backImg = document.createElement("img");
        backImg.src = img.src;                      
        viewbox.appendChild(backImg);
        ns.style(backImg, "position", "absolute");   
        ns.style(backImg, "top", "0px"); 
        ns.style(backImg, "left", "0px");
        
        var mvbl;
        if (ns.isIE()) {
            mvbl = ns.moveable(imageContainer, viewbox);
        } else {
            mvbl = ns.moveable(viewbox, viewbox);
        }
                
        var imgX = ns.left(img);
        var imgY = ns.top(img);
        var imgWidth = img.offsetWidth;
        var imgHeight = img.offsetHeight;
        
        //  the width of the magnification area
        var vBoxWidth = 200;
        //  the height of the magnification area
        var vBoxHeight = 200;
        //  the X offset into the view-box to adjust when clicked
        var clickOffsetX = 5;
        //  the Y offset into the view-box to adjust when clicked
        var clickOffsetY = 5;
        //  the percentage to apply to the image for magnification
        //  (should be a float value greater than 1.0, where 1.0 would be same-sized)
        var magnification = 2.0;
        //  the width of the background magnified image element
        var mImgWidth = 0;
        //  the height of the background magnified image element
        var mImgHeight = 0;
        
        //  bounds checking for view-box
        var vLoX = 0;
        var vLoY = 0;
        var vHiX = 0;
        var vHiY = 0;
        
        //  used to indicate if magnifier is currently active
        var isActive = false;

        var toggleIE = false;
        var ctr = 0;;
        
        /**
         * Moves the view-box and backing image, assuring that a full view spectrum
         * is available.
         */
        function setPos(event) {
            event = ns.event(event);
                        
            //  getPoint() returns the x and y determined by the Moveable object
            //  (contained in a single object 'point')  based on offset and bounds
            var point = mvbl.getPoint(event);
            
            // the following calculations assure that the magnified backing
            // image slides in the background such that all portions of it are
            // viewable at the bounds of the view area          
            var rxV = (imgWidth - vBoxWidth) * 1.0;
            var rxM = (vBoxWidth - backImg.offsetWidth) * 1.0;
            var ryV = (imgHeight - vBoxHeight) * 1.0;
            var ryM = (vBoxHeight - backImg.offsetHeight) * 1.0;
            var mX = (point.x / rxV) * (rxM);
            var mY = (point.y / ryV) * (ryM);
            
            ns.style(backImg, "left", mX + "px");
            ns.style(backImg, "top" , mY + "px");
        }
        
        function resetView() {
            ns.style(viewbox, "position", "absolute");
            ns.style(viewbox, "height", vBoxHeight + "px");
            ns.style(viewbox, "width",  vBoxWidth  + "px");
            ns.style(viewbox, "display", "none");    
            ns.style(viewbox, "zIndex", "100");
            ns.style(viewbox, "overflow", "hidden");
        }
        
        function moveView(event) {
            if (!isActive) {
                return true;    
            }       
            setPos(event);          
            return true;
        }
        
        function active(event) {
            if (isActive) {
                return true;    
            }
            isActive = true;
            ns.style(backImg, "display", "block");                   
            setPos(event);
            return true;
        }               
        
        function deactive(event) {
            isActive = false;
            ns.style(backImg, "display", "none");                    
            return true;
        }

        var isToggleOn = false;
        /**
         * Called to create visual elements, and to reset visual elements if any 
         * setters are called.
         */
        function init() {
            resetView();            
            if (ns.isIE()) {
                if (useIeHack) {
                    function toggle(event) {
                        if (isToggleOn) {
                            isToggleOn = false;
                            deactive(event);
                        } else {
                            isToggleOn = true;
                            active(event);
                        }
                    }
                    ns.addListener(imageContainer, "click", toggle);
                    
                    ns.addListener(imageContainer, "mousemove", moveView);
                    ns.addListener(backImg, "mousemove", moveView);                
                    ns.addListener(img, "mousemove", moveView);                
                    ns.addListener(document.body, "mousemove", moveView);                  
                } else {
                    ns.addListener(imageContainer, "mousedown", active);
                    ns.addListener(backImg, "mousedown", active);
                    ns.addListener(img, "mousedown", active);
                    
                    ns.addListener(imageContainer, "mouseup", deactive);
                    
                    ns.addListener(imageContainer, "mousemove", moveView);
                    ns.addListener(backImg, "mousemove", moveView);                
                    ns.addListener(img, "mousemove", moveView);                
                    ns.addListener(document.body, "mousemove", moveView);                  
                }
            } else {
                ns.disableSelection(viewbox);

                ns.addListener(viewbox, "mousedown", active);
                
                ns.addListener(viewbox, "mouseup", deactive);
                ns.addListener(imageContainer, "mouseup", deactive);
                
                ns.addListener(viewbox, "mousemove", moveView);                
                ns.addListener(imageContainer, "mousemove", moveView);
            }
            
            ns.style(backImg, "height", (imgHeight * magnification) + "px"); 
            ns.style(backImg, "width", (imgWidth * magnification) + "px");   
            ns.style(backImg, "zIndex", "99");
            ns.style(backImg, "display", "none");

            // take view-box borders into account when setting boundry values
            function getVal(val) {
                val = parseInt(val);
                return (isNaN(val)) ? 0 : val;  
            }   
            var borderX = getVal(viewbox.style.borderLeftWidth) + getVal(viewbox.style.borderRightWidth);
            var borderY = getVal(viewbox.style.borderTopWidth) + getVal(viewbox.style.borderBottomWidth);
            
            vLoX = 0;
            vLoY = 0;
            vHiX = vLoX + imgWidth - vBoxWidth - borderX;
            vHiY = vLoY + imgHeight - vBoxHeight - borderY;
            mImgHeight = backImg.offsetHeight;
            mImgWidth = backImg.offsetWidth;
            
            mvbl.setXOffset(imgX + clickOffsetX);
            mvbl.setYOffset(imgY + clickOffsetY);
            mvbl.setXBounds(vLoX, vHiX);
            mvbl.setYBounds(vLoY, vHiY);
        }
        init();

        /**
         * Mouseover event handler for the on-page visible image element.
         */
        function hover(event) {
            ns.style(viewbox, "display", "block");   
            return true;
        }
        
        /**
         * Mouseout event handler for the on-page visible image element.
         */
        function unhover(event) {
            if (isActive) {
                return true;    
            }
            resetView();
            return true;
        }
        ns.addListener(imageContainer, "mouseover", hover);
        ns.addListener(imageContainer, "mouseout", unhover);
        
        /**
         * 
         * @param {Object} width
         * @param {Object} height
         */
        this.setViewboxWidth = function(width, height) {
            _checkNum(width, "Width", 0);
            _checkNum(height, "Height", 0);
            vBoxWidth = width;
            vBoxHeight = height;
            init();
        };
        
        /**
         * 
         * @param {Object} x
         * @param {Object} y
         */
        this.setClickOffset = function(x, y) {
            _checkNum(x, "X offset", 0);
            _checkNum(y, "Y offset", 0);
            clickOffsetX = x;
            clickOffsetY = y;
            init();
        };

        /**
         * 
         * @param {Object} value
         */  
        this.setMagnification = function(value) {
            _checkNum(value, "Magnification", 1.0);
            magnification = value;
            init();
        };
    }
    ns.magnifier = function(imageContainer, viewBox, useIeHack) { return new _Magnifier(imageContainer, viewBox, useIeHack); };         
})(jwyre);
/**
 * @projectDescription
 *   
 * Creates submenus from a a set of menu items.
 * 
 * @author Ryan Hardy
 */     
(function(ns) {
    // check that jwyre is present
    if (!ns) {
        throw new Error("jwyre.js is not present, and is required.");
    }	
    // if present, make shortcuts to tools
    var h = ns._INTERNALS;

    ns.Menu = {};
    /**
     * Used to indicate that the submenu will be coming from above, relative 
     * to parent.
     */
    ns.Menu.FROM_TOP = 0;
    /**
     * Used to indicate that the submenu will be coming from the left, 
     * relative to parent.
     */
    ns.Menu.FROM_LEFT = 1;

    //TODO: remove jquery requirement
    function _Menu(parent) {
        if (typeof parent == "string") {
            parent = dom("#" + parent);       
        }
        if (!parent) {
            throw new Error("A parent item or id must be provided.");
        }
        // an array of the submenu objects
        var subs = [];
        // the styles of the main menu item that will be changed when active
        var menuStyles = null;
        // the styles to apply to the main menu item when its submenu is active
        var actMnuStyles = null;    
        // the styles to apply to submenu items
        var subMnuStyles = null;
        // the styles to apply to submenu items when hovered upon
        var hvrMnuStyles = null;
    
        /**
         * 
         * @param {Object} id
         * @param {Object} parent
         * @param {Object} direction
         */
        this.addSubMenu = function(menuItem, direction) {
            var id;
            if (typeof menuItem == "string") {
                id = menuItem;
            } else {
                id = dom(menuItem).attribute("id");
            }
            if (!id) {
                throw new Error("No id could be obtained from menu item");
            }
            var sub = new SubMenu(menuItem, direction);
            subs[id] = sub;
        };
    
        /**
         * 
         * @param {Object} id
         * @param {Object} display
         * @param {Object} href
         */
        this.addMenuItem = function(id, display, href) {
            var sub = subs[id];
            if (!sub) {
                throw new Error("There is no submenu with id " + id);
            }
            sub.add(display, href);
        };
        
        /**
         * 
         * @param {Object} styles
         */
        this.setActiveMenuStyles = function(styles) {
            if (!styles) {
                return;
            }
            actMnuStyles = styles;
            menuStyles = getOrigStyles(actMnuStyles, parent);
        };
        
        /**
         * 
         * @param {Object} styles
         */
        this.setSubMenuStyles = function(normal, hover) {
            if (!normal) {
                return;
            }
            subMnuStyles = normal;
            hvrMnuStyles = {};
            if (hover) {
                for (var style in normal) {
                    hvrMnuStyles[style] = normal[style];             
                }
                for (var style in hover) {
                    hvrMnuStyles[style] = hover[style];             
                }
            } 
        };

        function getOrigStyles(styles, object) {
            var orig = {};
            for (var style in styles) {
                orig[style] = dom(object).style(style);
            }
            return orig;
        }
            
        /**
         * 
         * @param {Object} parent
         * @param {Object} direction
         */
        function SubMenu(parent, direction, behavior) {
            if (typeof parent == "string") {
                parent = dom("#" + parent);       
            }
            if (!parent) {
                throw new Error("A parent item or id must be provided.");
            }
            behavior = (!behavior) ? new FoldMenuDisplay() : behavior;
            // array of mappings of submenu links, mapped to display strings 
            var links = [];
            /**
             * 
             * @param {Object} display
             * @param {Object} href
             */
            this.add = function(_href, _display) {
                if (!_href || !_display) {
                    throw new Error("Neither href nor display can be null.");
                }
                links.push({"href" : _href, "display" : _display});
            };

            // returns true if there is a submenu to display
            function display() {
                return links.length != 0;
            }
    
            // the html to contain the menu
            var menuHtml = dom("<div></div>");
            var linkHtmls = [];
            var isOnMenu = false;    

            // constructs the html and components needed to display the submenu   
            function buildMenu() {
                menuHtml = dom("<div></div>");
                linkHtmls = [];
                isOnMenu = false;
                var genStyles = true;
                for (var i in links) {
                    var link = links[i];
                    var a = dom("<a>" + link.display + "</a>");
                    dom(a).attribute("href", link.href);
                    dom(a).style(subMnuStyles);
                    dom(a).style({
                        "display" : "block",
                        "zIndex" : "0",
                        "position" : "absolute",
                        "top" : "0px",
                        "left" : "0px"
                    });
                    if (genStyles) {
                        subMnuStyles = getOrigStyles(hvrMnuStyles, a);
                        genStyles = false;
                    }
                    dom(a).event("click", function() {
                        hideMenu();
                    });
                    var hovers = (
                        function(lnk) {
                            var z = dom(lnk).style("zIndex");
                            var orig = getOrigStyles(hvrMnuStyles, lnk);
                            return {
                                on : function() {
                                    if (hvrMnuStyles) {
                                        dom(lnk).style(hvrMnuStyles);                                   
                                    }
                                    z = dom(lnk).style("zIndex");
                                    dom(lnk).style("zIndex", "10");
                                },
                                off : function() {
                                    if (hvrMnuStyles) {
                                        dom(lnk).style(orig);
                                    }
                                    dom(lnk).style("zIndex", z);
                                }
                           };   
                       }
                    )(a);
                    dom(a).event("hover", hovers.on, hovers.off);
                    linkHtmls.push(a);
                }
                dom(menuHtml).style("position", "absolute");
                switch (dir) {
                    case ns.Menu.FROM_TOP:
                       //TODO: fix this
                        dom(menuHtml).style("top", dom(parent).dims().height + "px");
                        dom(menuHtml).style("left", "px");
                        break;
                    case ns.Menu.FROM_LEFT:
                        dom(menuHtml).style("top", dom(parent).pos().top + "px");
                        dom(menuHtml).style("left", dom(parent).dims().width + "px");
                        break;
                }

                dom(menuHtml).event("hover",
                    function() {
                        isOnMenu = true;    
                    },
                    function() {
                        isOnMenu = false;
                        checkOnMenu();
                    }
                );
            }
            var dir = (!direction) ? ns.Menu.FROM_LEFT : direction;
            // indicates if submenu is displaying
            var isActive = false;
    
            function checkOnMenu() {
                window.setTimeout(
                    function() {
                          if (isOnMenu) {
                            return;
                          }
                          hideMenu();
                    },
                    200
                );
            }
        
            function showMenu() {
                if (!display()) {
                    return;
                }
                buildMenu();
                dom(menuHtml).remove();
                dom(parent).parent().append(menuHtml);
                dom(parent).style(actMnuStyles);
                isActive = true;
                behavior.showMenu();
            }
            
            function hideMenu() {
                if (!display()) {
                    return;
                }
                behavior.hideMenu();
                dom(parent).style(menuStyles);
                isActive = false;    
            }

            // window timer id          
            var id;
            dom(parent).event("hover",
                function() { 
                    if (id || isActive) {
                        return;
                    }
                    id = window.setTimeout(
                        function() {
                            id = null;
                            if (isActive) {
                                return;
                            }
                            showMenu();
                         }, 
                         500); 
                },
                function() {
                    window.clearTimeout(id);
                    id = null;
                    checkOnMenu();
                }
            );
            
            /**
             * 
             * @param {Object} fadeIn
             * @param {Object} unfold
             * @param {Object} fadeOut
             */         
            function FoldMenuDisplay(fadeIn, unfold, fadeOut) {
                fadeIn = (!fadeIn) ? 800 : fadeIn;
                unfold = (!unfold) ? 100 : unfold;
                fadeOut = (!fadeOut) ? 300 : fadeOut;
                
                this.showMenu = function() {
                    var a = linkHtmls.shift();
                    if (!a) {
                        return;
                    }
                    dom(a).style("zIndex", "1");
                    $(menuHtml).append(a);
                    jwyre.animator(menuHtml).fadeIn(fadeIn, function(){ unfoldMenu(0, -1) });
                };
                
                this.hideMenu = function() {
                    jwyre.animator(menuHtml).fadeOut(fadeOut, function() { dom(menuHtml).remove(); });
                };
    
                // internal functions                   
                function unfoldMenu(top, zIndex) {
                    var a = linkHtmls.shift();
                    if (!a) {
                        return;
                    }
                    $(menuHtml).append(a);
                    dom(a).style("top", top + "px");
                    dom(a).style("zIndex", zIndex);
                    jwyre.animator(a).move(dom(a).dims().height, 0, unfold, function(){ unfoldMenu(top + dom(a).dims().height, zIndex - 1) });
                }
            }    
        }
    }       
    jwyre.menu = function(parent) { return new _Menu(parent); };
})(jwyre);
/**
 * @projectDescription
 * Makes an HTMLElement moveable on a page by click and dragging.
 * 
 * @author Ryan Hardy
 * @version 1.0
 */
(function(ns) {  
    // check that jwyre is present
    if (!ns) {
        throw new Error("jwyre.js is not present, and is required.");
    }

    function _Moveable(trigger, element, container, downCallback, upCallback, moveCallback) {
	    // if present, make shortcuts to tools
	    var h = ns._INTERNALS;
		
		var initializer;
		if (h._getArgsLen(arguments) == 1) {
			initializer = arguments[0];
		} else {
			// this is done to keep backwards compatibility
			initializer.trigger = trigger;
            initializer.element = element;
            initializer.container = container;
            initializer.downCallback = downCallback;
            initializer.upCallback = upCallback;
            initializer.moveCallback = moveCallback;
			h._log("Deprecation warning: use of deprecated jwyre.moveable() constructor (use initializer object).");
		}
		var tr = ns.element(initializer.trigger);
        var el = ns.element(initializer.element);
		var cntr = ns.element(initializer.container || "body");
        
        if (!h._x(tr)) {
            throw new Error("No Trigger.");
        }
        if (!h._x(el)) {
            throw new Error("No Element.");
        }
        if (!h._x(cntr)) {
            throw new Error("No Container.");
        }
        var downCb = h._getFn(initializer.downCallback, function() { return true; });     
        var upCb = h._getFn(initializer.upCallback, function() { return true; });
        var moveCb = h._getFn(initializer.moveCallback, function() { return true; });            
        
        /**
         * 
         */
        this.getPoint = function() {
            return currPoint;
        };
    
        // the position of orig. mouse click within the container element 
        var x, y;
        var tDims = ns.dimensions(el);
        var w = tDims.width;
        var h = tDims.height;
    
        var cPos = ns.position(cntr);
        var cDims = ns.dimensions(cntr);
        
        // the boundary coords
        var minX = 0;
        var minY = 0; 
        var maxX = minX + cDims.width - w; 
        var maxY = minY + cDims.height - h;
         
        // indicates if moving is currently active
        var isActive = false;
        
        // mousedown callback   
        function mDown(event) {
            if (!downCb(ns.event(event))) {
                return ns.kill(event);
            }
            isActive = true;
            event = ns.event(event);
            x = event.targetX;
            y = event.targetY;
            tDims = ns.dimensions(el);
            w = tDims.width;
            h = tDims.height;
        
            cPos = ns.position(cntr);
            cDims = ns.dimensions(cntr);
            // the boundary coords
            minX = 0;
            minY = 0; 
            maxX = minX + cDims.width - w; 
            maxY = minY + cDims.height - h;
             
            return ns.kill(event);
        }
        
        // mouseup callback
        function mUp(event) {
            if (!upCb(ns.event(event))) {
                return ns.kill(event);
            }
            isActive = false;
            return ns.kill(event);
        }
        
        // the x/y (relative to container) of the last move call
        var currPoint = null;
        // mousemove callback
        function mMove(event) {
            if (!isActive) {
                return ns.kill(event);
            }
            event = ns.event(event);
            if (!moveCb(event)) {
                return ns.kill(event);
            }
            var scr = ns.scroll();
            var nX = event.clientX - cPos.left - x + scr.left;
            var nY = event.clientY - cPos.top - y + scr.top;
    
            nX = (nX < minX) ? minX : nX;
            nX = (nX > maxX) ? maxX : nX;
            nY = (nY < minY) ? minY : nY;
            nY = (nY > maxY) ? maxY : nY;
    
            ns.style(el, "left", nX + "px");
            ns.style(el, "top", nY + "px");
            currPoint = new _Point(nX, nY);
            
            return ns.kill(event);
        }
        
        ns.addListener(tr, "mousedown", mDown);
        ns.addListener(el, "mouseup", mUp);
        ns.addListener(el, "mousemove", mMove);
        if (ns.isIE()) {
            ns.addListener(document.body, "mouseup", mUp);
            ns.addListener(document.body, "mousemove", mMove, ns.Event.CAPTURE);              
        } else {
            ns.addListener(window, "mouseup", mUp);
            ns.addListener(window, "mousemove", mMove, ns.Event.CAPTURE);             
        }
    }
	function _Point(x, y) {
		this.x = x;
		this.y = y;
		this.toString = function() {
			return "[x: " + x + ", y: " + y + "]";
		};
	}
	// keeping the current constructor elements for the time being, to
	// preserve backwards-compatibility; use single initializer object instead
    ns.moveable = function(trigger, element, container, downCallback, upCallback, moveCallback) { return new _Moveable(trigger, element, container, downCallback, upCallback, moveCallback) };
})(jwyre);
/**
 * @projectDescription
 * Used to create a custom scroll bar around an HTMLElement.
 * 
 * @author Ryan Hardy
 * @version 0.9.3
 */
(function(ns) {
    // check that jwyre is present
    if (!ns) {
        throw new Error("jwyre.js is not present, and is required.");
    }
    
	var defaults = {
		scrollerPosition : "right",
		generateScroller : true,
		scrollerWidth : 20
	};
	
	/**
	 * 
	 * @param {Object} initializer
	 */
	function _Scroller(initializer) {
        // make shortcuts to tools
        var h = ns._INTERNALS;
		var sPos = initializer.scrollerPosition || defaults.scrollerPosition;
		if (sPos != "left" && sPos != "right") {
			throw new Error("scrollerPosition must be 'left' or 'right'.")
		}		
		// the element containing the content to be scrolled
	    var content = ns.element(initializer.element);
		if (!h._x(content)) {
			throw new Error("The element is null.");
		}
        // the scroll bar width
        var w = ns.parseNumber(initializer.scrollerWidth, defaults.scrollerWidth);
        // indicates if the HTML for the scroll bar will be provided or created
		var genSc = ns.parseBoolean(initializer.generateScroller, defaults.generateScroller);
		
        // this is the element that will actually move to scroll content into view
        var scrollContent = ns.create("<div></div>");
        ns.cloneStyles(scrollContent, content);
        ns.styles(scrollContent, {
			"position" : "absolute"
		});
		
        // move content into new element
        ns.html(scrollContent, ns.html(content));
        ns.empty(content);
        ns.styles(content, {
            "overflow" : "hidden"
        });
        ns.append(content, scrollContent);
		
		// the scroller (the element that will be clicked/dragged to scroll content)
        var scroller;
		// the scrollbar (the area on which the scroller will move when scrolling content)
        // used to restrict scroll element's movement
        var scrollBar;
		
		// creating default scroller?
		if (genSc) {
			//TODO: implement
		} 
        // this is initialization for non-generated scroll components		
		else {
			// the clickable/draggable element
			scroller = jwyre.element(initializer.scrollElement);
			if (!h._x(scroller)) {
				throw new Error("The scrollElememt is null.");
			}
            // element used to restrict scroll element's movement
			scrollBar = jwyre.parent(scroller);
		}
		
	    // the height of only visible content
	    var visHeight = h._getTotalHeight(content);
	    // the height of all content, visible and invisible
	    var totalHeight = h._getTotalHeight(content, true);
		totalHeight = Math.max(totalHeight, visHeight);
		var speed = ns.parseNumber(initializer.speed, 1);
		speed = Math.max(speed, 1);
		var increment = Math.max(1, Math.abs(visHeight - totalHeight) * .01 * speed);
		// the current scroller's y position
		var currentScroll = 0;
		
		function _scrollUp() {
            if (currentScroll < 0) {
                currentScroll += increment;
                ns.style(scrollContent, "top", currentScroll + "px");
                _setScroller();
            }
		}
		function _scrollDn() {
            if (currentScroll > visHeight - totalHeight) {
                currentScroll -= increment;
                ns.style(scrollContent, "top", currentScroll + "px");
				_setScroller();
            }
		}
		
		function _setScroller() {
            var sDims = ns.dimensions(scroller);
            var dims = ns.dimensions(scrollBar);
            var pctDn = currentScroll / (visHeight - totalHeight);
            var y = pctDn * (dims.height - sDims.height);
			y = Math.min(y, dims.height - sDims.height);
            ns.position(scroller, 0, y);
		}
		
	    // create the jwyre.moveable() object to control scroll behavior
	    var mvbl = ns.moveable({
	        trigger : scroller,
	        element : scroller,
	        container : scrollBar,
	        upCallback : null,
	        downCallback : null,
	        moveCallback : function() {
	            var pt = mvbl.getPoint();
	            if (pt) {
		            var sDims = ns.dimensions(scroller);
		            var dims = ns.dimensions(scrollBar);
					var pctDn = pt.y / (dims.height - sDims.height);
                    currentScroll = (visHeight - totalHeight) * pctDn; 
                    ns.style(scrollContent, "top", currentScroll + "px");
	            }
	            return true;
	        }
	    });
		var id = null;
        ns.addListener(initializer.scrollUpTrigger, "mousedown", 
			function() {
				if (!id) {
					id = window.setInterval(_scrollUp, 10);
				}
			}
		);
        ns.addListener(initializer.scrollDownTrigger, "mousedown", 
			function() {
                if (!id) {
                    id = window.setInterval(_scrollDn, 10);
                }				
			}
		);
        function _clear() {
	        if (id) {
	            window.clearInterval(id);
                id = null;
            }
        }

		if (jwyre.isIE()) {
            ns.addListener(document.body, "mouseup", _clear);            
		} else {
	        ns.addListener(document, "mouseup", _clear);			
		}
	}
	
    // create a generic element to act as the scroller if none other provided
    function mkDefScroll() {
        var sEl = ns.create("<div></div>");
        ns.styles(sEl, {
            "position" : "absolute",
            "top" : "0px",
            "left" : "0px",
            "height" : "20px",
            "width" : "100%",
            "background-color" : "white",
            "border" : "1px black solid"
        });
        return sEl;
    }
	
	jwyre.scroller = function(initializer) { return new _Scroller(initializer); };
})(jwyre);

/*
 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
 * Digest Algorithm, as defined in RFC 1321.
 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for more info.
 */

/*
 * 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   */

/*
 * 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_md5(s)    { return rstr2hex(rstr_md5(str2rstr_utf8(s))); }
function b64_md5(s)    { return rstr2b64(rstr_md5(str2rstr_utf8(s))); }
function any_md5(s, e) { return rstr2any(rstr_md5(str2rstr_utf8(s)), e); }
function hex_hmac_md5(k, d)
  { return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
function b64_hmac_md5(k, d)
  { return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
function any_hmac_md5(k, d, e)
  { return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e); }

/*
 * Perform a simple self-test to see if the VM is working
 */
function md5_vm_test()
{
  return hex_md5("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72";
}

/*
 * Calculate the MD5 of a raw string
 */
function rstr_md5(s)
{
  return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
}

/*
 * Calculate the HMAC-MD5, of a key and some data (raw strings)
 */
function rstr_hmac_md5(key, data)
{
  var bkey = rstr2binl(key);
  if(bkey.length > 16) bkey = binl_md5(bkey, key.length * 8);

  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 = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
  return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
}

/*
 * Convert a raw string to a hex string
 */
function rstr2hex(input)
{
  try { hexcase } catch(e) { hexcase=0; }
  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
  var output = "";
  var x;
  for(var i = 0; i < input.length; i++)
  {
    x = input.charCodeAt(i);
    output += hex_tab.charAt((x >>> 4) & 0x0F)
           +  hex_tab.charAt( x        & 0x0F);
  }
  return output;
}

/*
 * Convert a raw string to a base-64 string
 */
function rstr2b64(input)
{
  try { b64pad } catch(e) { b64pad=''; }
  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var output = "";
  var len = input.length;
  for(var i = 0; i < len; i += 3)
  {
    var triplet = (input.charCodeAt(i) << 16)
                | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
                | (i + 2 < len ? input.charCodeAt(i+2)      : 0);
    for(var j = 0; j < 4; j++)
    {
      if(i * 8 + j * 6 > input.length * 8) output += b64pad;
      else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
    }
  }
  return output;
}

/*
 * Convert a raw string to an arbitrary string encoding
 */
function rstr2any(input, encoding)
{
  var divisor = encoding.length;
  var i, j, q, x, quotient;

  /* Convert to an array of 16-bit big-endian values, forming the dividend */
  var dividend = Array(Math.ceil(input.length / 2));
  for(i = 0; i < dividend.length; i++)
  {
    dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
  }

  /*
   * Repeatedly perform a long division. The binary array forms the dividend,
   * the length of the encoding is the divisor. Once computed, the quotient
   * forms the dividend for the next step. All remainders are stored for later
   * use.
   */
  var full_length = Math.ceil(input.length * 8 /
                                    (Math.log(encoding.length) / Math.log(2)));
  var remainders = Array(full_length);
  for(j = 0; j < full_length; j++)
  {
    quotient = Array();
    x = 0;
    for(i = 0; i < dividend.length; i++)
    {
      x = (x << 16) + dividend[i];
      q = Math.floor(x / divisor);
      x -= q * divisor;
      if(quotient.length > 0 || q > 0)
        quotient[quotient.length] = q;
    }
    remainders[j] = x;
    dividend = quotient;
  }

  /* Convert the remainders to the output string */
  var output = "";
  for(i = remainders.length - 1; i >= 0; i--)
    output += encoding.charAt(remainders[i]);

  return output;
}

/*
 * Encode a string as utf-8.
 * For efficiency, this assumes the input is valid utf-16.
 */
function str2rstr_utf8(input)
{
  var output = "";
  var i = -1;
  var x, y;

  while(++i < input.length)
  {
    /* Decode utf-16 surrogate pairs */
    x = input.charCodeAt(i);
    y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
    if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
    {
      x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
      i++;
    }

    /* Encode output as utf-8 */
    if(x <= 0x7F)
      output += String.fromCharCode(x);
    else if(x <= 0x7FF)
      output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
                                    0x80 | ( x         & 0x3F));
    else if(x <= 0xFFFF)
      output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
                                    0x80 | ((x >>> 6 ) & 0x3F),
                                    0x80 | ( x         & 0x3F));
    else if(x <= 0x1FFFFF)
      output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
                                    0x80 | ((x >>> 12) & 0x3F),
                                    0x80 | ((x >>> 6 ) & 0x3F),
                                    0x80 | ( x         & 0x3F));
  }
  return output;
}

/*
 * Encode a string as utf-16
 */
function str2rstr_utf16le(input)
{
  var output = "";
  for(var i = 0; i < input.length; i++)
    output += String.fromCharCode( input.charCodeAt(i)        & 0xFF,
                                  (input.charCodeAt(i) >>> 8) & 0xFF);
  return output;
}

function str2rstr_utf16be(input)
{
  var output = "";
  for(var i = 0; i < input.length; i++)
    output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
                                   input.charCodeAt(i)        & 0xFF);
  return output;
}

/*
 * Convert a raw string to an array of little-endian words
 * Characters >255 have their high-byte silently ignored.
 */
function rstr2binl(input)
{
  var output = Array(input.length >> 2);
  for(var i = 0; i < output.length; i++)
    output[i] = 0;
  for(var i = 0; i < input.length * 8; i += 8)
    output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
  return output;
}

/*
 * Convert an array of little-endian words to a string
 */
function binl2rstr(input)
{
  var output = "";
  for(var i = 0; i < input.length * 32; i += 8)
    output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
  return output;
}

/*
 * Calculate the MD5 of an array of little-endian words, and a bit length.
 */
function binl_md5(x, len)
{
  /* append padding */
  x[len >> 5] |= 0x80 << ((len) % 32);
  x[(((len + 64) >>> 9) << 4) + 14] = len;

  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;

  for(var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;

    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);

    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);

    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);

    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);

    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
  }
  return Array(a, b, c, d);
}

/*
 * These functions implement the four basic operations the algorithm uses.
 */
function md5_cmn(q, a, b, x, s, t)
{
  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}

/*
 * 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 bit_rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}
