/*
 * jwydget.js
 * An extension of the jwyre JavaScript library, providing various visual tools.
 * Version 0.9.4.2 Copyright (C) Ryan Hardy 2010
 * See http://www.hardwyred.net for more info.
 * (File generated: Nov 3, 2010 7:53:54 AM)
 */
/**
 * @projectDescription
 * Makes an HTMLElement moveable on a page by click and dragging.
 * 
 * @author Ryan Hardy
 * @version 1.2.5
 */
 //TODO: update documentation
(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;
			ns.log("Deprecation warning: use of deprecated jwyre.moveable() constructor (use initializer object).");
		}
		// specifies if we want snap-to-grid behavior
		var snapTo = ns.parseBoolean(initializer.snapTo, false);
		var snapPix = 0;
		// specifies the pixel tolerance to use for snapping
		if (snapTo) {
			snapPix = ns.parseNumber(initializer.snapPix, 0);
		}
		// specifies if the snapping occurs on mousemove or on mouseup
		var snapAtEnd = ns.parseBoolean(initializer.snapAtEnd, false);
		
		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(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; });            
        		    
        // the element to use in detecting bounds (trigger or element)
        var boundsCtr = ns.parseBoolean(initializer.useElement, false) ? el : tr;
        // get self-reference for use in inner/non-member functions
		var self = this;
        // x- and y- adjustment values for moved element
        var adjX = 0, adjY = 0;
        // the position of orig. mouse click within the container element
        var x, y;
        var tDims;
        var w, h;
		// the position and dimension of the container
        var cPos, cDims;
        // the boundary coords
        var minX, minY, maxX, maxY;
        // indicates if moving is currently active
        var isActive = false;
        // the x/y (relative to container) of the last move call
        var currPoint = null;
		
		/*
		======================================================================== 
	                      Public instance member functions
        ======================================================================== 
		*/
        /**
         * Allow for dynamic assignment of the element being moved.
         * 
         * @param {string|HTMLElement} element
         */
        this.setElement = function(element) {
            el = jwyre.element(element);
        };
        
        /**
         * Allows a fine-grained adjustment of the moved element's position.
         * @param {Object} x
         * @param {Object} y
         */
        this.setAdjustment = function(x, y) {
            adjX = (isNaN(x)) ? 0 : x;
            adjY = (isNaN(y)) ? 0 : y;
        };
        
        /**
         * 
         */
        this.getPoint = function() {
            return currPoint;
        };
        
        /*
        ======================================================================== 
                              Private functions
        ======================================================================== 
        */
		
        // mousedown callback   
        function mDown(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;
			// call at inception to create Point
            mkPoint(event, snapTo && !snapAtEnd);
            downCb.call(self, ns.event(event));
            return ns.kill(event);
        }
        
		function doPositioning(event, doSnap) {
			mkPoint(event, doSnap);
			var x = currPoint.x;
            var y = currPoint.y;
            ns.style(el, "left", x + "px");
            ns.style(el, "top", y + "px");
		}
		
		function mkPoint(event, doSnap) {
            var scr = ns.scroll();
            var nX = event.clientX - cPos.left - x + scr.left + adjX;
            var nY = event.clientY - cPos.top - y + scr.top + adjY;
            
            if (doSnap) {
                nX = roundTo(nX, snapPix);
                nY = roundTo(nY, snapPix);              
            }
    
            nX = (nX < minX) ? minX : nX;
            nX = (nX > maxX) ? maxX : nX;
            nY = (nY < minY) ? minY : nY;
            nY = (nY > maxY) ? maxY : nY;
                
            currPoint = new _Point(nX, nY);			
		}
		
        // mouseup callback
        function mUp(event) {
            if (!upCb.call(self, ns.event(event))) {
                return ns.kill(event);
            }
            if (snapTo && snapAtEnd) {
				doPositioning(event, true);
			}    
            isActive = false;
            return ns.kill(event);
        }
        
        // mousemove callback
        function mMove(event) {
            if (!isActive) {
                return ns.kill(event);
            }
            event = ns.event(event);
            if (!moveCb.call(self, event)) {
                return ns.kill(event);
            }
			doPositioning(event, snapTo && !snapAtEnd);
            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);             
        }
		
		/**
		 * Removes all listeners.
		 */
		this.remove = function() {
	        ns.removeListener(tr, "mousedown", mDown);
	        ns.removeListener(el, "mouseup", mUp);
	        ns.removeListener(el, "mousemove", mMove);
	        if (ns.isIE()) {
	            ns.removeListener(document.body, "mouseup", mUp);
	            ns.removeListener(document.body, "mousemove", mMove, ns.Event.CAPTURE);              
	        } else {
	            ns.removeListener(window, "mouseup", mUp);
	            ns.removeListener(window, "mousemove", mMove, ns.Event.CAPTURE);             
	        }			
		};
    }
	function _Point(x, y) {
		this.x = x;
		this.y = y;
		this.toString = function() {
			return "[x: " + x + ", y: " + y + "]";
		};
	}
	//rounds to the nearest multiple of the num given
	function roundTo(number, snap) {
		var mod = number % snap;
		if (mod == 0) {
			return number;
		} else if (mod < (snap / 2)) {
			return number - mod;
		} else {
			return number + (snap - mod);
		}
	}
	
	// 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
 *   
 * Creates an HTML menu that may accept menu items or other menus.
 * 
 * @author Ryan Hardy
 * @version 1.3.5
 */     
(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.BOTTOM = 0;
    /**
     * Used to indicate that the submenu will be coming from the left, 
     * relative to parent.
     */
    ns.Menu.RIGHT = 1;

    /**
     * Internal helper object; represent a clickable/hoverable item in the menu
     * or sub-menu.
     * 
     * @param {Object} initializer
     */
    function _MenuItem(initializer) {
        // the id of this instance; will be rendered as the HTML id attribute
        this._id = initializer.id;
		// the unique name/display text for this item
        var name = initializer.name;
		// the owning Menu instance
        var parent = initializer.parent;
		// the click action
        var action;
		// if a link has been provided in initializer, is a simple go-to-page item
        if (initializer.link) {
            action = function() {
                window.location.href = initializer.link;
            };
        } else {
			initializer.action = initializer.action || function() {};
			// otherwise, a custom click action may be provided
            if (typeof initializer.action != "function") {
                throw new Error("If an action is provided, it must be a function.");
            }
			action = function(event) {
                parent.getTopLevelParent().closeCurrentSubMenu();
				initializer.action(event);
			};
        }
		// if a hoverOn action has been provided, a hoverOff action must also be provided
		// a simpler solution is to provide a hoverStyles object for simple style effects on hover
        if (initializer.hoverOn && !initializer.hoverOff) {
            throw new Error("If a on-hover function is provided, an off-hover function must also be provided.");
        }
		// if no custom menu item HTML creator function has been prvided, use default
		var menuItemCreator = 
		  initializer.menuItemCreator ||
		  function(_init) {
				var html = jwyre.create("<span>" + _init.name + "</span>");
				jwyre.attribute(html, "id", _init.id);
				if (_init.tagClass) {
	    			jwyre.attribute(html, "class", _init.tagClass); 
				}
				if (_init.styles) {
		       		jwyre.style(html, _init.styles);
				}
				return html;
		  };
        // the cached HTML element; created on first call to getHTML()
        var html;
        
        /**
         * Obtains the name, and display text, for this instance.
         */
        this.getName = function() {
            return name;
        };

        /**
         * Obtains the order of this instance within its parent Menu.
         */
        this.getOrder = function() {
            return initializer.order;
        };
        
        /**
         * Renders the MenuItem as HTML.
         */
        this.getHtml = function() {
            if (html) {
                return html;
            }
            html = menuItemCreator({
            	"id" : initializer.id, 
            	"tagClass" : initializer.tagClass, 
            	"styles" : initializer.styles,
            	"name" : name,
            	"link" : initializer.link,
            	"action" : action
            });
            jwyre.addClick(html, action);
            if (initializer.hoverStyles) {
                jwyre.addHover(html, initializer.hoverStyles);
            } else if (initializer.hoverOn) {
                jwyre.addHover(html, initializer.hoverOn, initializer.hoverOff);
            } else if (initializer.hoverClassOn) {
				var fns = (function() {
					var classes = jwyre.attribute(html, "class");
					classes = (classes && classes != "") ? classes : "";
					var hClasses = classes + " " + initializer.hoverClassOn;
					return {
						on : function(event) {
                            jwyre.attribute(html, "class", hClasses);							
						},
						off : function(event) {
							jwyre.attribute(html, "class", classes);
						}
					}
				})();
				jwyre.addHover(html, fns.on, fns.off);
			}
            
            return html;
        };
    }
    
	/**
	 * 
	 * @param {Object} initializer
	 */
    function _Menu(initializer) {
        // when created as a prototype, will be w/out initializer; just return
        if (!initializer) {
            return;
        }
        // the id of this instance; will be rendered as the HTML id attribute
        this._id = initializer.id;
        // mapping of MenuItem names to MenuItem (each name, then, must be unique)
        var menuItems = {};
        // used to add ordering to MenuItems
        var itemCtr = 0;
        // if this is a subMenu, this will be reference to parent
        var parent = initializer.parent;
        // z-index for this instance (used to correctly layer subs)
        var z = jwyre.parseNumber(initializer.zIndex, 1);
        // how far from the right edge a subMenu will be positioned
        var subOffset = jwyre.parseNumber(initializer.subOffset, 10);
        // if this is a subMenu, this is one of the constants indicating on which 
        // side to display the subMenu
        var dispDir = jwyre.parseNumber(initializer.dispDirection, ns.Menu.RIGHT);
        // reference to the HTML rendering of this instance
        this._html = null;
        // reference to a Menu object if this instance has a subMenu showing
        this._currSub = null;
		// the target element of the originating mouse-click when displaying a context menu
		var contextTarget = null;
        // the originating mouse-click x-/y- coords when displaying a context menu
		// is an object with properties obj.x and obj.y
        var contextPoint = null;
        // get reference to this Menu instance for use in event handlers
        var self = this;
        // if a custom display function has not been provided, assign default
		var subMenuDisplayer = 
		  initializer.subMenuDisplayer || 
		  function(_init) {
            jwyre.append(document.body, _init.html);
            jwyre.styles(_init.html, {
                "position" : "absolute",
                "top" : _init.y + "px",
                "left" : _init.x + "px"
            });			
		};
		var menuCreator =
		  initializer.menuCreator ||
		  function(_init) {
            var html = jwyre.create("<div></div>");
            jwyre.attribute(html, "id", _init.id);
            if (_init.tagClass) {
                jwyre.attribute(html, "class", _init.tagClass); 
            }
            if (_init.styles) {
                jwyre.style(html, _init.styles);
            }
            return html;
        };
        
        // if making this a context menu, disable native context menu
        // and add listener
        if (initializer.makeContextMenu) {
            jwyre.killContextMenu();
            jwyre.addListener(document, "mousedown", 
                function(event) {
                    event = jwyre.event(event);
                    if (!self._isMenuClick(event)) {
                        self.close();                               
                    }
                    if (jwyre.isRightClick(event)) {
						contextTarget = event.target;
						//TODO: not cross-browser compatible...add layerX/Y to object in call to jwyre.event() (?)
						contextPoint = { x : event.layerX, y : event.layerY };
                        var x = event.clientX;
                        x = (x > 10) ? x - 10 : 0;
                        var y = event.clientY;
                        y = (y > 10) ? y - 10 : 0;
                        self.display(x, y, null);
                    }
                },
                jwyre.Event.BUBBLE
            );                  
        } else if (!parent) {
	        jwyre.addListener(document, "mousedown", 
	            function(event) {
	                event = jwyre.event(event);
	                if (!self._isMenuClick(event)) {
	                    self.closeCurrentSubMenu();                               
	                }
	            },
	            jwyre.Event.BUBBLE
	        );			
		}
        
		/*
		 * Internal helper function used to determine if a mouse click occurred
		 * on a menu or submenu.
		 */ 
        this._isMenuClick = function(event) {
            var isIn = self._html && jwyre.isIn(self._html, event.clientX, event.clientY);
            if (this._currSub) {
                return isIn || this._currSub._isMenuClick(event); 
            } else {
                return isIn;
            }
        };
        
        /**
         * 
         * @param {integer} x
         * @param {integer} y
         * @param {MenuItem} menuItem (may be null)
         */
        this.display = function(x, y, menuItem) {
            // if there is a parent, set this as parent's current subMenu
            if (parent) {
                //parent.closeCurrentSubMenu();
                parent._currSub = this;                     
            }
            var scr = jwyre.scroll();
			x += scr.left;
			y += scr.top;
            //TODO: ensure that always renders on-screen
            this._html = this.getHtml();
			subMenuDisplayer({
				"html" : this._html, 
				"x" : x, 
				"y" : y, 
				"menuItem" : menuItem
			});
			return this._html;
        };
        
		/**
		 * Obtains the target HTMLElement of the originating mouse-click if this
		 * is a context-menu.
		 */
		this.getContextTarget = function() {
			return contextTarget;
		};

        /**
         * Obtains the target Point object of the originating mouse-click if this
         * is a context-menu.
         */
        this.getContextPoint = function() {
			//TODO: make immutable
            return contextPoint;
        };
		
        /**
         * Returns true if this instance is currently rendered on screen.
         */
        this.isShowing = function() {
            return this._html != null;
        };
        
        /**
         * 
         */
        this.close = function() {
            if (this._html) {
                jwyre.remove(this._html);
                this.closeCurrentSubMenu();
            }
            this._html = null;
        };
        
		/**
		 * Returns parent, if this is a submenu, or self (allows for method 
		 * chaining).
		 */
		this.getParent = function() {
			return parent || this;
		};

        /**
         * Returns parent, if this is a submenu, or self (allows for method 
         * chaining).
         */
        this.getTopLevelParent = function() {
			if (parent) {
				return parent.getTopLevelParent();
			} else {
				return this;
			}
        };
		
        /**
         * 
         */
        this.closeCurrentSubMenu = function() {
            if (this._currSub) {
                this._currSub.close();
                this._currSub = null;
            }   
        };
        
		// adds mouseover/out behavior to newly created MenuItems
        function injectMItemHover(mItem, menu, subMenu) {
            // get reference to the MenuItem's getHtml() function, so
            // that it may be overridden but still called
            var htmlF = mItem.getHtml;
            mItem.getHtml = function() {
                // do call to super.getHtml()
                var html = htmlF();
    			jwyre.addListener(html, "mouseover", function() {
                    if (!subMenu) {
                        menu.closeCurrentSubMenu();
                        return true;
                    }					
					if (menu._currSub != subMenu) {
                        menu.closeCurrentSubMenu();
					}
                    // if already showing, return
				    if (subMenu.isShowing()) {
				        return true;
				    }
                    var x, y;
                    var pos = jwyre.position(html);
                    var scr = jwyre.scroll();
                    switch (dispDir) {
                    case ns.Menu.BOTTOM:
                        x = html.offsetLeft - scr.left;
                        y = html.offsetTop + self._html.offsetHeight - subOffset - scr.top;
                        ns.log("Bottom: " + x + " : " + y);
                        break;
                    default:
                        x = pos.left + self._html.offsetWidth + subOffset - scr.left;
                        y = pos.top - scr.top;
                        ns.log("Right: " + x + " : " + y);
                        break;
                    }
                    subHtml = subMenu.display(x, y, mItem);
			    });			
                return html;
			};            
        }
        
        /**
         * 
         * @param {Object} mItemInitalizer
         * 
         * @return reference to self, for methdo chaining
         */
        this.addMenuItem = function(mItemInitalizer) {
            mItemInitalizer.order = itemCtr++;
			mItemInitalizer.parent = this;
            var mItem = new _MenuItem(mItemInitalizer);
            injectMItemHover(mItem, this, null);
            menuItems[mItem.getName()] = mItem;
			
			return this;
        };
		
        /**
         * 
         * @param {Object} mItemInitalizer
         * @return {Menu}
         */
        this.addSubMenu = function(mItemInitalizer, menuInitializer) {
            // set this instance as new Menu's parent
            menuInitializer.parent = this;
            menuInitializer.zIndex = z + 1;
            // create the new subMenu object; will be returned by function
            var subMenu = new _Menu(menuInitializer);
            
            mItemInitalizer.order = itemCtr++;
            mItemInitalizer.parent = this;
            // override any click action with a no-op (MenuItem's action
            // is only to open new subMenu)
            mItemInitalizer.action = function(event) {
                //self._html.blur();
                return jwyre.kill(event);
            };
            var mItem = new _MenuItem(mItemInitalizer);
            injectMItemHover(mItem, this, subMenu);
            menuItems[mItem.getName()] = mItem;
            
            return subMenu;
        };
        
        /**
         * 
         */
        this.getHtml = function() {
            var ary = jwyre.array();
            for (var nm in menuItems) {
                ary.push(menuItems[nm]);
            }
            ary.sort(function(a, b) { return a.getOrder() - b.getOrder(); });

            var html = menuCreator({
            	"id" : initializer.id, 
            	"tagClass" : initializer.tagClass, 
            	"styles" : initializer.styles
            });
            jwyre.style(html, "z-index", z);
            ary.each(function() { jwyre.append(html, this.getHtml()); });
            
			// ensure that _html is always populated
			if (!this._html) {
				this._html = html;
			}
            return html;
        };
        
        /**
         * Returns display-friendly string for this instance.
         */
        this.toString = function() {
            return "Menu: " + this._id;
        };
    }

    jwyre.menu = function(initializer) {
		var _mnu = new _Menu(initializer); 
		// create a facade object
		return {
			getParent : function() {
				return _mnu.getParent();
			},
			addMenuItem : function(initializer) {
				var mItem = _mnu.addMenuItem(initializer);
				
				return mItem;
			},
            addSubMenu : function(initializer, menuInitializer) {
                var mItem = _mnu.addSubMenu(initializer, menuInitializer);
				
				return mItem;
            },
			getHtml : function() {
				return _mnu.getHtml();
			},
			isMenuClick : function(event) {
				return _mnu._isMenuClick(event);
			},
			close : function() {
				_mnu.close();
			},
            getContextTarget : function() {
	            return _mnu.getContextTarget();
	        },
            getContextPoint : function() {
                return _mnu.getContextPoint();
            }
		};
	};
})(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)",
 *      contentPadding : "10px",
 *      boxBorderSize : "2px",
 *      boxBorderColor : "rgb(100, 100, 100)",
 *      boxBackgroundColor : "rgb(240, 240, 250)",
 *      boxOpacity : "1.0",
 *      okText : "Ok",
 *      cancelText : "Cancel",
 *      okButtonHtml : null,
 *      cancelButtonHtml : null,
 *      buttonFont : "Arial",
 *      buttonFontSize : "12px",
 *      buttonFontColor : "rgb(0, 0, 0)",
 *      buttonBorderSize : "2px",
 *      buttonBorderColor : "rgb(100, 100, 100)",
 *      buttonBackgroundColor : "rgb(240, 240, 250)",
 *      display : function(container, message, top, left),
 *      topOverride : null,
 *      leftOverride : null
 * }
 * 
 * @author Ryan Hardy
 * @version 1.1.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)",
		contentPadding : "10px",
        boxBorderSize : "2px",
        boxBorderColor : "rgb(100, 100, 100)",
        boxBackgroundColor : "rgb(240, 240, 250)",
        boxOpacity : "1.0",
        okText : "Ok",
        cancelText : "Cancel",
        okButtonHtml : null,
        cancelButtonHtml : null,
        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" : initializer.contentPadding,
            "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 _typeDisplay = "display";
        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 a simple message with no close button. 
         * 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.display = function(initializer) {
            var message = createContent(initializer.message);
            if (typeof initializer == "string") {
                message = createContent(initializer);
            }
            var okCallback = function() {};
            var openerCallback = h._getFn(initializer.openerCallback);
            var closerCallback = h._getFn(initializer.closerCallback);
            
            return _helper(_typeDisplay, message, openerCallback, okCallback, function() {}, closerCallback);
        };
		
        /**
         * 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 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.body).height + "px");
            ns.style(container, "width", ns.dimensions(document.body).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) {
                        close();
                        return ns.kill(event);                       
                    }
                    return true;
                };
                ns.addListener(document, "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;
                }
            }
            
			if (type != _typeDisplay) {
				var ok;
				if (initializer.okButtonHtml) {
		            ok = ns.create(initializer.okButtonHtml);					
				} else {
		            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;
            	if (initializer.cancelButtonHtml) {
                    cancel = ns.create(initializer.cancelButtonHtml);
            	} else {
                    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.body).height, width = ns.dimensions(document.body).width;
            var top = ns.parseNumber(initializer.topOverride, (height - dims.height) / 2); 
            var left = ns.parseNumber(initializer.leftOverride, (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();
            
            // this is a workaround; scroll was 0, 0 if alert shown on page-load
            window.setTimeout(function() {
        	    var scroll = jwyre.scroll();
        	    ns.log(scroll);
        	    ns.log("Top: " + (scroll.top + top + "px"));
        	    ns.log("Left: " + (scroll.left + left + "px"));
        	    ns.style(message, {
        	    	"top" : scroll.top + top + "px",
        	    	"left" : scroll.left + left + "px"        	    	
        	    });
                ns.style(container, {
                    "height" : scroll.top + ns.dimensions(document.body).height + "px",
                	"width" : scroll.left + ns.dimensions(document.body).width + "px"                	
                });
            }, 20);
            
            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 for the creation of several different types of image and text gallery
 * facilities.
 * 
 * @author Ryan Hardy
 * @version 1.1.1
 */
(function(ns) {
	//TODO: need to do extensive documentation
	
    // 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 = {
		Type : {
			TYPE_1 : "_type1",
            TYPE_2 : "_type2",
			FILMSTRIP : "_strip"
		},
        Behavior : {
            FADER : "_fader",
            ROTATOR : "_rotator"
        }
    };
    
    // sets the image source, if addPic is true, or inner text on item 
    function setItem(addPic, el, item) {
		if (!item) {
			return;
		}
        if (addPic) {
			setImage(el, item, "");
        } else {
			ns.text(el, item);
        }
    }
	
    /*
     * Obtains the image source from the element, by checking to 
     * see if it is an image element, or an HTML element with a 
     * background image.  Returns empty string if none found.
     * @param {string || HTMLElement} element
     */
    function getImageSrc(element) {
        if (ns.tag(element) == "img") {
            return ns.attribute(element, "src");
        } else {
            var img = ns.style(element, "background-image");                     
            img = img.replace(/url\(['"]/gi, "");
            img = img.replace(/['"]\)/gi, "");
            img = ns.trim(img);
            
            return img;
        }
    }
    
    /*
     * Sets the image attributes on the provided element.  If it is
     * an image element, the text will be set on the alt attribute, and
     * will be ignored otherwise.
     * 
     * @param {string || HTMLElement} element
     * @param {string} src
     * @param {string} text
     */
    function setImage(element, src, text) {
        if (ns.tag(element) == "img") {
            ns.attribute(element, "src", src);
            ns.attribute(element, "alt", text);
        } else {
            ns.style(element, "background-image", "url('" + src + "')");
        }
    }
            
    /**
     * 
     * @param {Object} initializer, with properties:
     * 
     *  element {string || HTMLElement} : (optional; behavior dependent)
     *  interval {integer}  : (optional)
     *  isPicGallery {boolean} : (optional)
     *  behavior {string || Object} : (optional)
     *  duration {integer} : (optional; behavior dependent)
     */
    function _Gallery(initializer) {
        initializer.gallery = this;
        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);
			if (isPic) {
	            ns.loadImgs([item]);				
			}
        };

        /**
         * 
         */
        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 isActive = false;
        
        //MUST ensure that duration is smaller that interval by a certain degree
        if (dur >= intvl) {
            intvl = dur * 1.1;
        }
        
        //    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
        var cId = "_jwyre_item_clone_" + new Date().getTime();
        ns.attribute(clone, "id", cId);
        initializer.gallery.cloneId = cId;
        setItem(isPic, el, items.getNextItem());
        setItem(isPic, clone, items.getNextItem());
        
        fader1.element = el;
        fader1.fadeIn = false;
        fader1.f = ns.animator(fader1.element);
        fader1.isDone = false;
        
        fader2.element = clone;
        fader2.fadeIn = true;
        fader2.f = ns.animator(fader2.element);       
        fader2.isDone = false;
        
        /**
         * 
         * @param {string | Array} item
         */
        this.add = function(item) {
            items.add(item);
            if (isPic) {
                ns.loadImgs([item]);                
            }
        };
        
        /**
         * 
         */
        this.start = function(callback) {
            isActive = true;
            callback = h._getFn(callback);
                        
            function fade(_fader) {
                _fader.isDone = false;
                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();
                            _fader.isDone = true;
                            if (fader1.isDone && fader2.isDone) {
                                window.setTimeout(_, intvl);                
                            }
                        }
                     );
                } else {
                    ns.style(_fader.element, "zIndex", "1");
                    _fader.f.fadeOut(dur, 
                        function() {
                            callback("out", item);
                            _fader.f.reset();
                            _fader.isDone = true;
                            if (fader1.isDone && fader2.isDone) {
                                window.setTimeout(_, intvl);                
                            }
                        }
                    );
                }
                _fader.fadeIn = !_fader.fadeIn;
            }
            function _() {
                if (!isActive) {
                    return;
                }
                fade(fader1);
                fade(fader2);
            }
            _();
        };
        
        /**
         * Stops the fader.
         */
        this.stop = function () {
            isActive = false;
        };
    }
    
	/* ********************************************************************** **
	 * 
 	 *                             New Gallery Code
	 * 
    ** ********************************************************************** */
	
    /**
     * 
     * @param {Object} initializer with properties
     * 
     *    displayArea : 
     *    textArea : 
     *    textImages : 
     *    images : 
     *    displayer : 
     *    imageStrip : 
     */
    function _GalleryManager(initializer) {
        // get reference to this for inner functions
        var self = this;
        // the main image display element
        this._displayArea = ns.element(initializer.displayArea);
        // the main image text display element, if there is one
        this._textArea = (initializer.textArea) ? ns.element(initializer.textArea) : document.createDocumentFragment();
                    
        // array of image sources
        this._images = ns.array();
        this._imageTexts = {};
        if (initializer.textImages) {
            // if textImages provided, pull out 'src' prop from each item
            ns.iterator(initializer.textImages, true).getArray().each(
                function() {
                    self._images.push(this.name);
                    self._imageTexts[this.name] = this.value;
                }
            );
        } else {
            this._images = ns.array(initializer.images);
            // if textImages not provided, set all texts to empty string
            this._images.each(
                function() {
                    self._imageTexts[this] = "";
                }
            );
        }
		// preload the gallery images
		jwyre.loadImgs(this._images);
        
        // the currently displayed image's source
        this._currentImage = this._images[0];
        // initialize current image area
        this.showCurrentImage();
        
        // the Displayer implementation used for showing an enlarged image
        var displayer = getDisplayer(initializer, this);
        // the ImageStrip implementation used for displaying thumbnails
        var imageStrip = getImageStrip(initializer, this);
                    
        // convenience function for getting or creating a default Displayer implementation
        function getDisplayer(init, gm) {
            if (init.displayer) {
                return init.displayer;
            }
            init = ns.clone(init);
            init.galleryManager = gm;
            return new _DefaultDisplayer(init);
        }
        // convenience function for getting or creating a default ImageStrip implementation
        function getImageStrip(init, gm) {
            if (init.imageStrip) {
                return init.imageStrip;
            }
            init = ns.clone(init);
            init.galleryManager = gm;
            return new _DefaultImageStrip(init);
        }
    }
    _GalleryManager.prototype = {
        /**
         * Obtains the array of image sources.
         */
        getImages : function() {
            return this._images;
        },
        /**
         * Sets the current image.  If a text area has been configured, any
         * text associated with the image will also be displayed.
         * 
         * @param {string} imageSrc
         */
        setCurrentImage : function(imageSrc) {
            this.showImage(imageSrc);
            this._currentImage = imageSrc;
        },
        /**
         * Temporarily displays the provided image in the main image area,
         * but does not update it as the current image.
         * 
         * @param {string} imageSrc
         */
        showImage : function(imageSrc) {
            var text = this._imageTexts[imageSrc];
            ns.text(this._textArea, text);
            setImage(this._displayArea, imageSrc, text);
        },
        /**
         * Displays the current image (if it is not currently showing, such as
         * when an image has been temporarily displayed by showImage()).
         */
        showCurrentImage : function() {
            this.showImage(this._currentImage);                
        },
        /**
         * Obtains the currently displayed image's source.
         */
        getCurrentImage : function() {
            return this._currentImage;
        },
        /**
         * Obtains the HTMLElement that is used as the main image display area.
         */
        getDisplayArea : function() {
            return this._displayArea;
        },
        /**
         * Obtains an Object that contains a mapping of image sources mapped
         * to image texts (contains empty strings if none provided).
         */ 
        getImageTexts : function() {
            return this._imageTexts;
        },
        /**
         * Obtains an Object that contains a mapping of image sources mapped
         * to image texts (contains empty strings if none provided).
         * 
         * @param imageSrc
         */ 
        getImageText : function(imageSrc) {
            var src = this._imageTexts[imageSrc];
            return src || "";
        }
    };      

    /**
     * Creates a Displayer instance with subclass specific initializer object.
     * 
     * @param {Object} initializer with properties
     * 
     *     displayTrigger : 
     */
    function Displayer(initializer) {
        var trigger = null; 
        // do not attempt to initialize if initializer is absent (being used as prototype)
        if (initializer) {
            // get reference to this for use in inner functions
            var self = this;
            // assign for external use
            this._galleryManager = initializer.galleryManager;
            // the HTMLElement to add the display click function
            trigger = initializer.displayTrigger;
            
            ns.addClick(trigger, 
                function(event) {
                    var gm = self.getGalleryManager();
                    var img = gm.getCurrentImage();
                    var text = gm.getImageText(img);                
                    self.display(img, text);
                    
                    return ns.kill(event);
                }
            );
        }
        /**
         * Obtains the element that the display event handler is attached to.
         */
        this.getTrigger = function() {
            return trigger;
        };  
    }
    Displayer.prototype = {
        /**
         * Obtains the GalleryManager tied to this instance.
         */
        getGalleryManager : function() {
            return this._galleryManager;
        },
        /**
         * Displays the chosen image.
         * 
         * @param {string} imageSrc
         * @param {string} text (optional)
         */
        display : function(imageSrc, text) { }
    };
    
    /**
     * Creates a Displayer instance with default display behavior.
     * 
     * @param {Object} initializer with properties
     *      (from superclass) 
     *      displayTrigger : 
     * 
     *      (for subclass)
     *      maxWidth : 
     *      showDuration :
     *      hideDuration :
     */
    function _DefaultDisplayer(initializer) {
        Displayer.call(this, initializer);
        // the maximum size to grow the image to
        this._maxWidth = ns.parseNumber(initializer.maxWidth, 450);
        this._showDuration = ns.parseNumber(initializer.showDuration, 300);
        this._hideDuration = ns.parseNumber(initializer.hideDuration, 300);
    }
    _DefaultDisplayer.prototype = new Displayer();
    /**
     * 
     * @param {string} imageSrc
     * @param {string} text (optional)
     */
    _DefaultDisplayer.prototype.display = function(imageSrc, text) { 
       // get reference to self for use in inner functions
        var self = this; 
        
        text = (!text) ? "" : text;
        var element = this.getGalleryManager().getDisplayArea();
        var dims = ns.dimensions(element);
        var pos = ns.position(element, false);
        
        var bkgrnd = ns.create("<div></div>");
        ns.style(bkgrnd, {
            "position" : "absolute",
            "top" : "0px",
            "left" : "0px",
            "height" : ns.dimensions(document.body, true).height + ns.scroll().top + "px",
            "width" : ns.dimensions(document.body, true).width + ns.scroll().left + "px",
            "background-color" : "black",
            "opacity" : "0",
            "z-index" : "1000"
        });
        if (ns.getBrowserInfo().name == ns.Browser.CHROME) {
        ns.style(bkgrnd, {
            "height" : ns.dimensions(document.body, true).height + 30 + "px",
            "width" : ns.dimensions(document.body, true).width + "px"
        });
        }
        ns.append(document.body, bkgrnd);
        
        var box = ns.create("<div></div>");
        ns.style(box, {
            "position" : "absolute",
            "padding" : "30px 10px 30px 10px",
            "background-color" : "black",
            "border" : "1px white solid"
        });
        var closer = ns.create("<div>[close]</div>");
        ns.style(closer, {
            "position" : "absolute",
            "top" : "6px",
            "left" : "10px",
            "color" : "white",
            "cursor" : "pointer",
            "font-family" : "Segoe UI",
            "font-size" : "10px",
            "opacity" : "0"
        });
        ns.append(box, closer);
        var clone = ns.create("<img src='" + imageSrc + "' alt='" + text + "' />");
        ns.style(clone, {
            "width" : dims.width + "px",
            "z-index" : "1001"
        });
        ns.append(box, clone);
        var comments =  ns.create("<div></div>");
        ns.style(comments, {
            "margin-top" : "5px",
            "text-align" : "center",
            "color" : "white",
            "font-family" : "Segoe UI",
            "z-index" : "1001"
        });
        ns.append(box, comments);
        ns.append(document.body, box);
        ns.position(box, pos.left, pos.top, 1000);
        
        var bDims = ns.dimensions(document.body); 
        var x = (bDims.width / 2) - (this._maxWidth / 2);
        var y = 100;
        var dW = this._maxWidth - dims.width;
        var ratio = dims.width / dims.height;
        var anim = ns.animator(box);
        ns.addClick(closer, function() {
            anim.reset();
            anim.moveTo({
                "x" : pos.left,
                "y" : pos.top,
                isRelative : false,
                duration : self._hideDuration,
                updater : function(progress) { 
                    var w = self._maxWidth - (dW * progress);
                    ns.style(box, "width", w + "px");
                    ns.style(clone, "width", w + "px");
                    ns.style(comments, "width", w + "px");
                    ns.style(bkgrnd, "opacity", "" + (.7 - progress));
                },
                callback : function() {  
                    ns.remove(box);
                    ns.remove(bkgrnd);                   
                }
            });             
        });

        anim.moveTo({
            "x" : x,
            "y" : y,
            isRelative : false,
            duration : self._showDuration,
            updater : function(progress) { 
                var w = dims.width + (dW * progress);
                ns.style(clone, "width", w + "px");
                ns.style(comments, "width", w + "px");
                ns.style(closer, "opacity", "" + (progress));
                ns.style(bkgrnd, "opacity", "" + (progress * .7));
            },
            callback : function() { 
                ns.text(comments, text);
            }
        });       
    };

    /**
     * 
     * @param {Object} initializer Object with the following properties:
     * 
     *     fwdTrigger :
     *     bkwdTrigger :
     *     numThumbs :
     */
    function ImageStrip(initializer) {
        // do not attempt to initialize if initializer is absent (being used as prototype)
        if (initializer) {
            // get reference to this for use in inner functions
            var self = this;
            // assign for external use
            this._galleryManager = initializer.galleryManager;
            
            // the element to attach event handler for sliding thumbs forward
            var fwdTrigger = initializer.fwdTrigger;
            // the element to attach event handler for sliding thumbs back
            var bkwdTrigger = initializer.bkwdTrigger;
            // get the images array; will convert to DualRotator
            var rotator = this.getGalleryManager().getImages();
            // number of thumbs to display
            this._numThumbs = Math.min(rotator.size(), initializer.numThumbs);
            // convert to a dualRotator; allows for forward and backward traversal 
            // through the images
            this._rotator = rotator.dualRotator(this._numThumbs);
            
            // glad indicating that a click is active
            var isActive = false;
            
            ns.addClick(fwdTrigger, function(event) {
                if (!isActive) {
                    isActive = true;
                    self.moveForward(function() { isActive = false; });
                }
                return ns.kill(event);
            });
            ns.addClick(bkwdTrigger, function(event) {
                if (!isActive) {
                    isActive = true;
                    self.moveBack(function() { isActive = false; });
                }
                return ns.kill(event);
            });
        }       
    }
    ImageStrip.prototype = {
        /**
         * Obtains the GalleryManager tied to this instance.
         */
        getGalleryManager : function() {
            return this._galleryManager;
        },
        /**
         * Obtains the DualRoator of image sources.
         */
        getImageRotator : function() {
            return this._rotator;
        },
        /**
         * 
         */
        getNumberOfThumbs : function() {
            return this._numThumbs;
        },
        /**
         * Advances the currently displaying thumbnails forward by one through the
         * internal image collection.
         * 
         * @param {function} callback
         */
        moveBack : function(callback) { },
        /**
         * Advances the currently displaying thumbnails backward by one through the
         * internal image collection.
         * 
         * @param {function} callback
         */
        moveForward : function(callback) { }
    };
    
    /**
     * Creates a ImageStrip instance with default display behavior.
     * 
     * @param {Object} initializer with properties:
     *      (from superclass)
     *      fwdTrigger :
     *      bkwdTrigger :
     *      numThumbs :
     *      
     *      (for subclass)
     *      thumbHeight :
     *      thumbWidth :
     *      thumbMargin :
     *      thumbInitializer :
     *      container :
     *      containerStyles :
     *      duration :
     */
    function _DefaultImageStrip(initializer) {
        ImageStrip.call(this, initializer);
        // get reference to this for use in inner functions
        var self = this;
        // the dimensions of each thumbnail
        var thumbH = initializer.thumbHeight;
        var thumbW = initializer.thumbWidth;
        // the spacing between thumbnails       
        var thumbMgn = initializer.thumbMargin;
        // ensure it is an even number (problem with fractional pixels in IE)
        thumbMgn = (thumbMgn % 2 == 1) ? thumbMgn++ : thumbMgn;  
        
        // an optional function for adding custom behavior to the thumbails
        var thumbInitializer = (initializer.thumbInitializer) ? initializer.thumbInitializer : function() {}; 
        
        // the number of thumbails to display at a time
        var num = this.getNumberOfThumbs();

        // the HTML container for the thumbnails
        this._container = ns.create("<div></div>");
        // the styles to apply to the container
        var containerStyles = initializer.containerStyles;
        // override the following styles
        containerStyles.height = thumbH + thumbMgn + "px";
        containerStyles.width = (num * (thumbW + thumbMgn)) + "px";
        containerStyles.overflow = "hidden";
        
        var containerParent = ns.create("<div></div>");          
        ns.style(containerParent, containerStyles);
        ns.style(this._container, {
            "position" : "absolute",
            "top" : "0px",
            "left" : "0px",
            "height" : containerStyles.height,
            "width" : containerStyles.width
        });

        ns.append(initializer.container, containerParent);
        ns.append(containerParent, this._container);
        
        // the width needed to move a hidden thumb into view; used by moveForward()/moveBack() methods
        this._width = thumbW + thumbMgn;
        
        var initImgs = this.getImageRotator().current();
        // the amount of time in which to perform swutch animation
        this._duration = ns.parseNumber(initializer.duration, 200);
        // the thumbnails used to facilitate slide behavior
        this._leftHidden = null, this._rightHidden = null;
        // the visible thumbnails
        this._displays = ns.array();
        
        for (var i = -1; i <= num; i++) {
            var top = (thumbMgn / 2);
            var left = (i * (thumbW + thumbMgn)) + (thumbMgn / 2);
            ns.log("Top: " + top + ", left: " + left);
            var html = ns.create("<img />");
            if (i == -1) {
                this._leftHidden = html;
            } else if (i == num) {
                this._rightHidden = html;
            } else {
                this._displays.push(html);
                var src = initImgs[i];
                var txt = this.getGalleryManager().getImageText(src);
                setImage(html, src, txt);
                
                var fns = (function(element) {
                    return {
                        on : function() {
                            var src = getImageSrc(element);
                            self.getGalleryManager().showImage(src);
                        },
                        off : function() {
                            self.getGalleryManager().showCurrentImage();
                        },
                        click : function(event) {
                            var src = getImageSrc(element);
                            self.getGalleryManager().setCurrentImage(src);
                            return ns.kill(event);
                        }
                    };
                })(html);
                ns.addHover(html, fns.on, fns.off);
                ns.addClick(html, fns.click);
                thumbInitializer(html);
            }
            ns.append(this._container, html);
            ns.style(html, {
                "position" : "absolute",
                "top" : top + "px",
                "left" : left + "px",
                "height" : thumbH + "px",
                "width" : thumbW + "px"
            });
        }
    }
    _DefaultImageStrip.prototype = new ImageStrip();
    /**
     * Internal helper function used to reset the strip elements after an
     * animation cycle.
     * 
     * @param {integer} width
     * @param {Array} imgs
     */
    _DefaultImageStrip.prototype._reset = function(width, imgs) {
        var self = this;
        var x = ns.position(this._container, true).left;
        ns.style(this._container, "left", (x + width) + "px");
        ns.log("Before resetting.");
        ns.log("Left - src: " + this._leftHidden.src + ", text: " + this._leftHidden.alt);
        ns.log("Right - src: " + this._rightHidden.src + ", text: " + this._rightHidden.alt);
        
        setImage(this._leftHidden, "", "");
        setImage(this._rightHidden, "", "");
        
        this._displays.each(function(item, counter) {
            //ns.log("Before resetting (" + counter + ").");
            //ns.log("Src: " + this.src + ", text: " + this.alt);
            var src = imgs[counter];
            var txt = self.getGalleryManager().getImageText(src);
            setImage(this, src, txt);                        
            ns.log("After resetting (" + counter + ").");
            ns.log("Src: " + "" + ", text: " + this.alt);
        });
    };
    /**
     * Advances the currently displaying thumbnails forward by one through the
     * internal image collection.
     * 
     * @param {function} callback
     */
    _DefaultImageStrip.prototype.moveBack = function(callback) {
        var self = this;
        this.getImageRotator().next();
        var imgs = this.getImageRotator().current();
        var imgSrc = imgs.last();
        ns.log("Calling next, src=" + imgSrc);
        var text = this.getGalleryManager().getImageText(imgSrc);
        setImage(this._rightHidden, imgSrc, text);
        
        var width = this._width;            
        
        var anim = ns.animator(this._container);
        anim.move({
            "x" : -width,
            "y" : 0,
            "duration" : self._duration,
            "callback" : function() {
                self._reset(width, imgs);
                callback();
            }
        });
    };
    /**
     * Advances the currently displaying thumbnails backward by one through the
     * internal image collection.
     * 
     * @param {function} callback
     */
    _DefaultImageStrip.prototype.moveForward = function(callback) { 
        var self = this;
        var imgSrc = this.getImageRotator().back();
        var imgs = this.getImageRotator().current();
        ns.log("Calling back, src=" + imgSrc);
        var text = this.getGalleryManager().getImageText(imgSrc);   
        setImage(this._leftHidden, imgSrc, text);
        var width = this._width;
        
        var anim = ns.animator(this._container);
        anim.move({
            "x" : width,
            "y" : 0,
            "duration" : self._duration,
            "callback" : function() {
                self._reset(-width, imgs);
                callback();
            }
        });     
    };
    
	/*
	============================================================================
	                       'Filmstrip' type gallery
    ============================================================================
	 */
	
	/**
	 * Creates a basic 'filmstrip' like gallery, with forward/backward behavior.
	 * 
	 * @param {Object} initializer, with properties:
	 * 
	 * {
	 *     displayArea
	 *     textArea
	 *     textImages || images
	 *     fwdTrigger
	 *     bkwdTrigger
	 * }
	 */
	function _Filmstrip(initializer) {
        // get reference to this for inner functions
        var self = this;
        // the main image display element
        this._displayArea = ns.element(initializer.displayArea);
        // the main image text display element, if there is one
        this._textArea = (initializer.textArea) ? ns.element(initializer.textArea) : document.createDocumentFragment();
                    
        // array of image sources
        this._images = ns.array();
        this._imageTexts = {};
        if (initializer.textImages) {
            // if textImages provided, pull out 'src' prop from each item
            ns.iterator(initializer.textImages, true).getArray().each(
                function() {
                    self._images.push(this.name);
                    self._imageTexts[this.name] = this.value;
                }
            );
        } else {
            this._images = ns.array(initializer.images);
            // if textImages not provided, set all texts to empty string
            this._images.each(
                function() {
                    self._imageTexts[this] = "";
                }
            );
        }
        // preload the gallery images
        jwyre.loadImgs(this._images);
        
        // the currently displayed image's source
        this._currentImage = this._images[0];
        // initialize current image area
        this.showCurrentImage();		
		
		var rotator = this._images.dualRotator();
		/*
		========================================================================
                  THIS IS WHERE CUSTOMIZABLE DISPLAY BEHAVIOR WILL GO -
                            IT IS NOT CURRENTLY IMPLEMENTED                      
        ========================================================================
		 */
		var fwdTrigger = jwyre.element(initializer.fwdTrigger);
		if (!fwdTrigger) {
			throw new Error("No forward trigger has been configured.");
		}
        var bkwdTrigger = jwyre.element(initializer.bkwdTrigger);
        if (!bkwdTrigger) {
            throw new Error("No forward trigger has been configured.");
        }
		jwyre.addClick(fwdTrigger, 
			function(event) {
				var nxt = rotator.next();
				self.setCurrentImage(nxt);
                return jwyre.kill(event);
			}
        );
        jwyre.addClick(bkwdTrigger, 
            function(event) {
                var lst = rotator.back();
                self.setCurrentImage(lst);
                return jwyre.kill(event);
            }
        );
	}
    _Filmstrip.prototype = {
        /**
         * Obtains the array of image sources.
         */
        getImages : function() {
            return this._images;
        },
        /**
         * Sets the current image.  If a text area has been configured, any
         * text associated with the image will also be displayed.
         * 
         * @param {string} imageSrc
         */
        setCurrentImage : function(imageSrc) {
            this.showImage(imageSrc);
            this._currentImage = imageSrc;
        },
        /**
         * Temporarily displays the provided image in the main image area,
         * but does not update it as the current image.
         * 
         * @param {string} imageSrc
         */
        showImage : function(imageSrc) {
            var text = this._imageTexts[imageSrc];
            ns.text(this._textArea, text);
            setImage(this._displayArea, imageSrc, text);
        },
        /**
         * Displays the current image (if it is not currently showing, such as
         * when an image has been temporarily displayed by showImage()).
         */
        showCurrentImage : function() {
            this.showImage(this._currentImage);                
        },
        /**
         * Obtains the currently displayed image's source.
         */
        getCurrentImage : function() {
            return this._currentImage;
        },
        /**
         * Obtains the HTMLElement that is used as the main image display area.
         */
        getDisplayArea : function() {
            return this._displayArea;
        },
        /**
         * Obtains an Object that contains a mapping of image sources mapped
         * to image texts (contains empty strings if none provided).
         */ 
        getImageTexts : function() {
            return this._imageTexts;
        },
        /**
         * Obtains an Object that contains a mapping of image sources mapped
         * to image texts (contains empty strings if none provided).
         * 
         * @param imageSrc
         */ 
        getImageText : function(imageSrc) {
            var src = this._imageTexts[imageSrc];
            return src || "";
        }
    };      
	
    ns.gallery = function(initializer) { 
		if (initializer && initializer.galleryType == ns.Gallery.Type.TYPE_1) {
            return new _Gallery(initializer);
		} else if (initializer && initializer.galleryType == ns.Gallery.Type.FILMSTRIP) {
            new _Filmstrip(initializer);
		} else {
            new _GalleryManager(initializer);
        }
	};
})(jwyre);
/**
 * @projectDescription
 * Used to create a custom scroll bar around an HTMLElement.
 * 
 * @author Ryan Hardy
 * @version 0.9.4
 */
(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);

/**
 * @projectDescription
 * Creates a custom 'Please Wait' type animation box.
 * 
 * Requires jwydget.alert.js and jwydget.waiter.js.
 * 
 * May be provided an initializer object to customize look and behavior.
 * Example (showing defaults):
 *  {
 *     "showAnimation" : true,
 *     "numberOfBars" : 5,
 *     "barHeight" : 15,
 *     "barWidth" : 10,
 *     "barSpacing" : 10,
 *     "jumpDistance" : 20,
 *     "duration" : 500,
 *     "barStyles" : {
 *         "background-color" : "rgb(200, 200, 200)",
 *         "border" : "1px rgb(150, 150, 150) solid"
 *     },
 *     "activeStyles" : {
 *         "background-color" : "rgb(240, 240, 240)",
 *         "border" : "1px rgb(150, 150, 220) solid"
 *     },
 *     "labelText" : "Please wait...",
 *     "labelStyles" : {},
 *     "alert" : jwyre.alert({
 *         "closeOnEnter"  : true,
 *         "disableScroll"  : false,
 *         "backgroundColor"  : "rgb(0, 0, 0)",
 *         "backgroundZ" : "100",
 *         "backgroundOpacity"  : ".20",
 *         "font"  : "Arial",
 *         "fontSize"  : "11px",
 *         "fontColor"  : "rgb(100, 100, 120)",
 *         "boxBorderSize"  : "2px",
 *         "boxBorderColor"  : "rgb(100, 100, 100)",
 *         "boxBackgroundColor"  : "rgb(255, 255, 255)",
 *         "boxOpacity"  : "1.0",
 *         "okText"  : "Ok",
 *         "cancelText"  : "Cancel",
 *         "buttonFont"  : "Arial",
 *         "buttonPadding"  : "5px",
 *         "buttonMargin"  : "10px 0px 0px 0px",
 *         "buttonFontSize"  : "11px",
 *         "buttonFontColor"  : "rgb(0, 0, 0)",
 *         "buttonBorderSize"  : "2px",
 *         "buttonBorderColor"  : "rgb(100, 100, 100)",
 *         "buttonBackgroundColor"  : "rgb(240, 240, 250)"        
 *     })
 *  };
 * 
 * @author Ryan Hardy
 * @version 1.0.4
 */
(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;
	
	// default initializer object
    var defaults = {
		"animationType" : "bars",// either 'bars', 'bumpBars', or 'boxes'
		
		// bars/bumpBars config
        "numberOfBars" : 6,
        "barHeight" : 15,
        "barWidth" : 10,
        "barSpacing" : 10,
        "jumpDistance" : 20,
        "barStyles" : {
            "background-color" : "rgb(200, 200, 200)",
            "border" : "1px rgb(150, 150, 150) solid"
        },
        "activeStyles" : {
            "background-color" : "rgb(240, 240, 240)",
            "border" : "1px rgb(150, 150, 220) solid"
        },
        
        // boxes config
        "boxHeight" : 10,
        "boxSpacing" : 3,
        "boxColor" : "blue",
        "boxAnimColor" : "blue",
        
        // common config	
        "duration" : 500,
        "labelText" : "Please wait...",
        "labelStyles" : {},
        "alertProps" : {
            "closeOnEnter"  : true,
            "disableScroll"  : false,
            "backgroundColor"  : "rgb(0, 0, 0)",
            "backgroundZ" : "100",
            "backgroundOpacity"  : ".10",
            "font"  : "Arial",
            "fontSize"  : "11px",
            "fontColor"  : "rgb(100, 100, 120)",
            "boxBorderSize"  : "1px",
            "boxBorderColor"  : "rgb(120, 120, 140)",
            "boxBackgroundColor"  : "rgb(255, 255, 255)",
            "boxOpacity"  : "1.0",
            "okText"  : "Ok",
            "cancelText"  : "Cancel",
            "buttonFont"  : "Arial",
            "buttonPadding"  : "5px",
            "buttonMargin"  : "10px 0px 0px 0px",
            "buttonFontSize"  : "11px",
            "buttonFontColor"  : "rgb(0, 0, 0)",
            "buttonBorderSize"  : "2px",
            "buttonBorderColor"  : "rgb(100, 100, 100)",
            "buttonBackgroundColor"  : "rgb(240, 240, 250)"        
        }
    };
    
    function _Waiter(initializer) {
    	if (!initializer) {
    		initializer = {};
    	}
    	var alertProps = ns.combine(defaults.alertProps, initializer.alertProps);
        initializer = ns.combine(defaults, initializer);
        var type = initializer.animationType;
        // container box for animated elements
        var box = ns.create("<div></div>");
        // the function used to start animation cycle
        var beginAnimation;
        // the total animation time for each 'bump' (up and down)
        var duration = initializer.duration;
        
        if (type == "bars" || type == "bumpBars") {
            // the number of progress bars to display
            var numBars = initializer.numberOfBars;
            // the height and width of the bars
            var bH = initializer.barHeight, bW = initializer.barWidth;
            /// the spacing between each
            var spc = initializer.barSpacing;
            // how far to 'bump' a bar on each iteration
            var dist = initializer.jumpDistance;
            var showAnim = type == "bumpBars";
    		                
            ns.style(box, {
                "position" : "relative",
                "top" : "0px",
                "left" : "0px",
                "height" : (bH * 2) + 10 + "px",
                "width" : ((bW + spc) * numBars) + "px",
                "padding" : "10px"
            });
            // progress bar elements
            var ps = ns.array();
            // mapping of each element's id to its direction (true=up, false=down)
            var pDirs = {};             
            for (var i = 0; i < numBars; i++) {
                var p = ns.create("<div id='_wtr_p" + i + "'></div>");
                // set user styles first; will overwrite any invalid style settings
                ns.style(p, initializer.barStyles);
                ns.style(p, {
                    "position": "absolute",
                    "top": (dist + 10) + "px",
                    "left": spc + (i * (bW + spc)) + "px",
                    "height": bH + "px",
                    "width": bW + "px"
                });
                ns.append(box, p);
                ps.push(p);
                pDirs[p.id] = true;
            }
    		
            var lbl = ns.create("<div>" + initializer.labelText + "</div>");
            // set user styles first; will overwrite any invalid style settings
            ns.style(lbl, initializer.labelStyles);
            ns.style(lbl, {
                "position" : "absolute",
                "top" : (bH + dist + 15) + "px",
                "left" : "10px",
                "font-family" : "Arial",
                "font-size" : "10px"
            });
            ns.append(box, lbl);
            
            // create a rotator for cycling through bars
            var rotator = ps.rotator();
            // flag used to kill animation and remove element
            var isCancelled = false;
            
            beginAnimation = function() {
                var el = rotator.next();
                var id = el.id;
                ns.style(el, initializer.activeStyles);
    			
    			if (!showAnim) {
    				window.setTimeout(
    				    function() {
    	                    ns.style(el, initializer.barStyles);                    
    	                    if (!isCancelled) {
    	                        beginAnimation(); 
    	                    }						
    					},
    					duration
    				);
    			} else {
    	            ns.animator(el).move({
    	                "x" : 0, 
    	                "y" : -20,
    	                "duration" : duration / 2, 
    	                "updater" : function() {},
    	                "callback" : function() {
    	                    ns.animator(el).move({
    	                        "x" : 0, 
    	                        "y" : 20,
    	                        "duration" : duration / 2, 
    	                        "updater" : function() {},
    	                        "callback" : function() {
    	                            ns.style(el, initializer.barStyles);                    
    	                            if (!isCancelled) {
    	                                beginAnimation(); 
    	                            }
    	                        } 
    	                    });             
    	                }
    	            });				
    			}
            };   	
        } else if (type == "boxes") {
        	var bH = initializer.boxHeight;
        	var bSpc = initializer.boxSpacing;
        	var bColor = initializer.boxColor;
        	var animColor = initializer.boxAnimColor;
            ns.style(box, {
                "position" : "relative",
                "top" : "0px",
                "left" : "0px",
                "height" : (bH * 3) + (bSpc * 4) + "px",
                "width"  : (bH * 3) + (bSpc * 4) + "px",
                "padding" : "20px"
            });
            var animBoxes = [];
        	for (var i = 0; i < 9; i++) {
        		var aBox = jwyre.create("<div></div>");
        		var top = ((i < 3) ? 0 : (i < 6) ? 1 : 2) * bH;
        		top += ((i < 3) ? 0 : (i < 6) ? 1 : 2) * bSpc;
        		var left = ((i % 3) * bH) +  ((i % 3) * bSpc);
        		jwyre.style(aBox, {
        			"position" : "absolute",
        			"top" : top + "px",
        			"left" : left + "px",
        			"height" : bH + "px",
        			"width" : bH + "px",
        			"background-color" : bColor
        		});
        		jwyre.append(box, aBox);
    			animBoxes.push(aBox);
        	}
            var lbl = ns.create("<div>" + initializer.labelText + "</div>");
            // set user styles first; will overwrite any invalid style settings
            ns.style(lbl, initializer.labelStyles);
            ns.style(lbl, {
                "position" : "absolute",
                "top" : (bH * 3) + (bSpc * 4) + 15 + "px",
                "left" : "10px",
                "font-family" : "Arial",
                "font-size" : "10px"
            });
            ns.append(box, lbl);
        	
        	// re-order to do proper animation rotation
        	var temp = jwyre.array();
    		// 4 is the middle box; is not an animation box, so will not push
        	temp.push(animBoxes[0], animBoxes[1], animBoxes[2], animBoxes[5], animBoxes[8], animBoxes[7], animBoxes[6], animBoxes[3]);
            // create a rotator for cycling through bars
            var rotator = temp.rotator();
            // flag used to kill animation and remove element
            var isCancelled = false;
            var lastBox = rotator.next();
            beginAnimation = function() {
            	jwyre.style(lastBox, "background-color", bColor);
            	lastBox = rotator.next();
            	jwyre.style(lastBox, "background-color", animColor);
            	if (!isCancelled) {
            		window.setTimeout(function() {
            			beginAnimation();
            		}, duration);
            	}
            };

        }
        
        /* ********************************************************** **
         *                  Public Members and Methods
        ** ********************************************************** */
        // function returned by jwydget.alert...used to close display box
        var closer;
        /**
         * 
         * @param {function} openerCallback (optional)
         */
        this.start = function(openerCallback) {
			if (closer) {				
				return;
			}
			openerCallback = h._getFn(openerCallback);
			var cAlert = ns.alert(alertProps);
            closer = cAlert.display({
                "message" : box,
                "openerCallback" : function() {
                    beginAnimation();
					openerCallback();					
				}
            });
        };
        /**
         * 
         */
        this.cancel = function() {
            isCancelled = true;
            if (closer) {
                closer();
				closer = null; 
            }
        };
    }	
	// add to namespace (jwyre.waiter())
	ns.waiter = function(initializer) { return new _Waiter(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
 * 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;
                ns.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(), true);
            ns.select(years, currDate.getYear(), true);
            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.6
 */
(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) {
	            ns.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) : a whole number between 0 and 100
     *       end (optional; required if begin specified) : a whole number between 0 and 100 
     *       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 (h._x(begin)) {
            if (!h._x(end)) {
                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) {
            element.each(function(item) {
                add("start", Fader, {
	                "fElement" : item, 
	                "duration" : duration, 
	                "begin" : begin, 
	                "end" : end, 
	                "callback" : callback,
	                "_this" : self
	            });				
			});
        };
        
        /**
         * 
         * @param {integer} duration
         * @param {Function} callback (optional)
         */
        this.fadeIn = function(duration, callback) {
			element.each(function(item) {
	            add("start", Fader, {
	                "fElement" : item, 
	                "duration" : duration, 
	                "begin" : 0, 
	                "end" : 100, 
	                "callback" : callback,
	                "_this" : self
	            });				
			});
        };

        /**
         * 
         * @param {integer} duration
         * @param {Function} callback (optional)
         */
        this.fadeOut = function(duration, callback) {
			element.each(function(item) {
	            add("start", Fader, {
	                "fElement" : item, 
	                "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
 * 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.2
 */
(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);
	        }
	    }
	    
		this.display = function(initializer) {
            if (!panel) {
                createPanel();
            }
            cb = h._getFn(initializer.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(initializer.target);
            var dims = ns.dimensions(initializer.target);
            var cDims = ns.dimensions(panel);
            
			var xOffset = ns.parseNumber(initializer.xOffset, 0);
            var yOffset = ns.parseNumber(initializer.yOffset, 0);
            var top = pos.top + yOffset;
            var left = pos.left + dims.width + xOffset; 
    
            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);
			var z = ns.parseNumber(initializer.zIndex, 10000);
			ns.style(panel, "z-index", z);
		};
		
	    /**
	     * Displays the color panel.
	     */
	    this.show = function(item, top, left, callback) {
			ns.log("Deprecation warning: ColorPanel.show() is deprecated.");
            this.display({
				"target" : item,
				"callback" : callback
			});
	    };
	
	    /**
	     * Hides/removes the color panel.
	     */
	    this.hide = function() {
	        if (!panel) {
	            return;
	        }
	        ns.remove(panel);
	    };
	}
	ns.colorPanel = function(initializer) { return new _ColorPanel(initializer); };
})(jwyre);


