/**
 * @namespace
 * This namespace holds common functions, classes, constants, and utilities used on the GridMob site.
 */
(function() { 
	gm.util = {
		/**
		 * Returns true if a function false otherwise
		 * @param {Object} it
		 * @return {boolean}
		 */
		isFunction : function(it) {
			if (!isSafari || !(typeof it == "function" && it == "[object NodeList]")) {
				return typeof it == "function" || it instanceof Function;
			} else {	
				return false;
			}
		},
	
		/**
		 * Returns true if it is a JavaScript object (or an Array, a Function or null)
		 * @param {Object} it
		 * @return {boolean}
		 */
		isObject : function(it){
			return it !== undefined && (it === null || typeof it == "object" || this.isArray(it) || this.isFunction(it));
		},
	
		/**
		 * Checks if the passed in element is an array.
		 * @param {Object} el
		 * @return {boolean}
		 */
		isArray : function(el) {
			return el && el instanceof Array || typeof el == "array";
		},
	
		/**
		 * Checks if the passed in element has array properties array.  Looser description of an array than checking the typeof.
		 * @param {Object} el
		 * @return {boolean}
		 */
		isArrayLike : function(el) {
			return (el!=null && typeof(el)=="object" && typeof(el.length)=="number" && (el.length==0 || typeof((el[0])) != "undefined"));	
		},
	
		/**
		 * Returns true if string
		 * @param {Object} el 
		 * @return {boolean}
		 */
		isString : function(el) {
			return typeof el == "string" || el instanceof String; // Boolean
		},
	
		/**
		 * Checks if the passed in element is an html element
		 * 
		 * @method isHTMLElement
		 * @param  {Object}       ele
		 * @param  {String|Array} tagname  (optional) the valid tagname/s for the element to be
		 * @return {boolean}               true if an html element matching the tagname/s, false otherwise
		 */
		isHTMLElement : function(el, tagname) {
			// if the element is not an 
			if (el == null || typeof el != "object" || el.nodeName == null) {
				return false;
			}
	
			// if no tagname we are not comparing to anything, so return true
			if (!tagname) {return true;}
	
			// if the tagname is a string compare it to the elements nodename
			if (typeof tagname == "string" && tagname.toLowerCase() ==  el.nodeName.toLowerCase()) {
				return true;
			}
				
			// if we have an array of elements we want to compare to do a recursive call with the array contents
			if (this.isArray(tagname)) {
				for (var i = 0; i < tagname.length; i++) {
					// recursive call with the element to determine the contents of the array
					if (this.isHTMLElement(el,tagname[i])) {
						return true;
					}
				}
			}
	
			return false;
		},
	    
	    /**
	     * Unescapes a value containing HTML markup.
	     *   
	     * @param {String} value  escaped value
	     * @return the unescaped value
	     */
	    unescapeHTML : function(value) {
	        return value.replace(/&([^;]+);/g, function(match, match1) { 
	            switch (match1) {
	                case 'lt':
	                    return '<';
	                case 'gt':
	                    return '>';
	                case 'amp':
	                    return '&';
	                case 'quot':
	                    return '"';
	                default:
	                    // Assume char code.
	                    if (match1.charAt(0) == '#') { match1 = match1.substring(1, match1.length); }
	                    return String.fromCharCode(match1);
	            }
	        });
	    },
	    
		/**
		 * 
		 * @param {Object} value
		 */
	    isEmptyString : function(value) {
	        return (!(value) || (value.length == 0));
	    },
	
		/**
		 * Function will trim spaces off a string
		 * @param {String} s  the string we want to trim
		 * @return {String}
		 */
	    trim : function(s) {
			return s.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
		},
		
		/**
		 * 
		 * @param {Object} value
		 */
	    capitalize : function(value) {
	        return value.replace(/\w\S*/g, function(word) { return word.charAt(0).toUpperCase() + word.substr(1).toLowerCase(); });
	    },
		
		
		/**
		 * Function assists in setting of transparent pngs.  accounts for ie6 mainly
		 * @param {String|HTMLElement} img   the handle to the image
		 * @param {String} src               the src of the image
		 * @param {String} mode              (optional) the mode of the image "scale" or "noscale" defaults to "noscale"
		 */
		setImgToPng : function(img,src,mode) {
			img = $(img);
			mode = mode || "noscale";
	        if (isIE && isIE < 7) {
				img.src = "/cdn/beta/images/px.gif";
				img.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizingMethod='" + mode + "')";
	        } else {
				img.src = src;
	        }
		},

		/**
		 * Locates the first index of the provided value in the passed array. If the value is not found, -1 is returned.
		 * For details on this method, see: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf
		 * @param {Array}   array
		 * @param {Object}  value
		 * @param {Integer} fromIndex  (optional) index to start searching from
		 * @param {Boolean} findLast   (optional) will return the last occurance if true is passed in
		 * @retur {int} the index of the entry in the array, or -1 if not found in the array
		 */
		indexOf : function(array, value,fromIndex,findLast){
			var i = 0, step = 1, end = array.length;
			if(findLast){
				i = end - 1;
				step = end = -1;
			}
			for(i = fromIndex || i; i != end; i += step){
				if(array[i] == value){ return i; }
			}
			return -1;
		},

	    /**
	     * Opens a window according to the given parameters.
	     * 
	     * @param {Object} fPage    window url
	     * @param {Object} fName    window name
	     * @param {Object} fWidth   window width
	     * @param {Object} fHeight  window height
	     * @param {Object} fScroll  whether or not to show a scroll bar
	     */
	    newWindow : function(fPage, fName, fWidth, fHeight, fScroll) {   
	        fScroll = "yes";
			var winl = 10, wint = 10;
	        var winprops = 'height='+fHeight+',width='+fWidth+',top='+wint+',left='+winl+',scrollbars='+fScroll+',directories=no,resizable=yes';
	        win = window.open(fPage, fName, winprops); 
	        if (parseInt(navigator.appVersion) >= 4) {
	            win.focus();
	        }  
	        return win;
		},

		/**
		 * Dojo extends the functionality of nodes to have an addEventListener to IE.  Since we don't use some of 
		 * dojo's codebase, we have to have custom code to attach events ... that is called from their stuff 
	     * @param {HTMLElement} el      the element to bind the handler to
	     * @param {string}      eType   the type of event handler
	     * @param {function}    fn      the callback to invoke
		 */
		addEventListener : function(el,eType,fn) {
		    if (window.addEventListener) {
	            el.addEventListener(eType, fn,false);
		    } else if (window.attachEvent) {
	            el.attachEvent("on" + eType, fn);
		    }
		},

		/**
		 * Dojo extends the functionality of nodes to have an addEventListener to IE.  Since we only use some of 
		 * dojo's codebase, we have to have custom code to attach events ... that is called from their stuff 
		 * @param {Object} el
		 * @param {Object} eType
		 * @param {Object} fn
		 */
		removeEventListener : function(el,eType,fn) {
	        if (window.removeEventListener) {
	            el.removeEventListener(eType, fn,false);
	        } else if (window.detachEvent) {
	            el.detachEvent("on" + eType, fn);
	        }
		},

		/**
		 * Will set the style of the passed in element
		 * @param {String|HTMLNode} el       the element we are styling
		 * @param {String}          property the style property name
		 * @param {String|int}      val      the property value
		 */
		setStyle :  function(el,property,val) {
			el = $(el);
		    if (isIE) {
	            switch (property) {
	                case 'opacity':
	                    if (this.isString(el.style.filter)) { // in case not appended
	                        el.style.filter = 'alpha(opacity=' + val * 100 + ')';
	
	                        if (!el.currentStyle || !el.currentStyle.hasLayout) {
	                            el.style.zoom = 1; // when no layout or cant tell
	                        }
	                    }
	                    break;
	                case 'float':
	                    property = 'styleFloat';
	                default:
	                    el.style[property] = val;
	            }
		    } else {
	            if (property == 'float') {
	                property = 'cssFloat';
	            }
	            el.style[property] = val;
		    }
		},
	
		/**
		 * Returns whether or not the specified classes are a portion of the class list currently applied to the node. 
		 * @param  {DomNode|String} node
		 * @param  {String}         classStr
		 * @return {Boolean}
		 */
		hasClass : function(node,classStr){
			return ((" " + $(node).className + " ").indexOf(" " + this.trim(classStr) + " ") >= 0);  
		},
		
		/**
		 * Adds the specified classes to the end of the class list on the passed node.
		 * @param  {DomNode|String} node     the dom node or it's identifier that we want to add the class to
		 * @param  {String}         classStr the class that will be added
		 */
		addClass : function(node,classStr){
			node = $(node);
			var cls = node.className;
			if((" "+cls+" ").indexOf(" " + this.trim(classStr) + " ") < 0){
				node.className = cls + (cls ? ' ' : '') + classStr;
			}
		},
		
		/**
		 * Removes the specified classes from node.
		 * @param  {DomNode|String} node     the dom node or it's identifier that we want to add the class to
		 * @param  {String}         classStr the class that will be removed
		 */
		removeClass : function(node,classStr){
			node = $(node);
			var t = this.trim((" " + node.className + " ").replace(" " + classStr + " ", " "));
			if(node.className != t){ node.className = t; }
		},
	
		/**
		 * Adds a class to node if not present, or removes if present. Pass a boolean condition if you want 
		 * to explicitly add or remove.
		 * @param  {DomNode|String} node      the dom node or it's identifier
		 * @param  {String}         classStr  the class we will be toggling
		 * @param  {Boolean}        condition (optional) If passed, true means to add the class, false means to remove.
		 */
		toggleClass : function(/*DomNode|String*/node, /*String*/classStr, /*Boolean?*/condition){
			if(condition === undefined){
				condition = !this.hasClass(node, classStr);
			}
			this[condition ? "addClass" : "removeClass"](node, classStr);
		},

		/**
		 * Function will check if the provided xy point is outside of the bounds of the provided element.
		 * @method pointInBounds
		 * @param  {Array}   pXY     {x,y} position of point
		 * @param  {Object}  el      element that we are checking the region for
		 * @param  {int}     margin  the allowable margin that we will still return that the xy is inside the bounds
		 * @return {boolean}         true if the point is within the element bounds, false if outside of the element bounds
		 */
		pointInBounds : function (p,el,margin) {
			margin = margin || 0;
			var elc = this.coords(el,true);
			return (elc.x - margin < p.x && p.x < elc.x + elc.w + margin &&  elc.y - margin < p.y && p.y < elc.y + elc.h + margin);
		},
		
		
		/**
		 * Function gets the xy of the mouse from the passed in event
		 * @param {Object} ev
		 */
		getXYFromEvent : function (ev) {
			// get where the mousdown event occurred
			if (isIE) {
				// grab the x-y pos.s if browser is IE
				return {x: window.event.clientX + this.getDocumentScrollLeft(),y:window.event.clientY + this.getDocumentScrollTop()};
	
			} else {
				// grab the x-y pos.s if browser is other
				return {x: ev.pageX,y:ev.pageY};
			}
		},
	
		/**
		 * This function return the bounding box of an element relative to the page.
		 *
		 * @method coords
		 * @param  {object}   el   The element we want the bounding box for
		 * @return {object}         The array of points 
		 */
		coords : function (el) {
			var xy = this.getXY(el);
			return (!xy) ? false : {
				l : xy[0],
				t : xy[1],
				x : xy[0],
				y : xy[1],
				w : el.offsetWidth,
				h : el.offsetHeight
			};
		},
	
		/**
		 * Function will return the xy position of the passed in element
		 * @param {HTMLElement} el 
		 * @return {Array}         an array that holds the [x,y] position of the element
		 */
		getXY : function(el) {
			var pd,box,rootNode,pos,parentNode,accountForBody,regTest = null;
	        if (isIE) {
	            box = el.getBoundingClientRect();
	            rootNode = el.ownerDocument;
	            return [box.left + this.getDocumentScrollLeft(rootNode), box.top + this.getDocumentScrollTop(rootNode)];
	        } else {
	            pos = [el.offsetLeft, el.offsetTop];
	            parentNode = el.offsetParent;
	
	            // safari: subtract body offsets if el is abs (or any offsetParent), unless body is offsetParent
	            accountForBody = (isSafari && el.style && el.style.position == "absolute" && el.offsetParent == el.ownerDocument.body);
	
				// loop through absolute positioned parents and get their offsets to add to the root xy
	            if (parentNode != el) {
	                while (parentNode) {
	                    pos[0] += parentNode.offsetLeft;
	                    pos[1] += parentNode.offsetTop;
	                    if (!accountForBody && isSafari && el.style && el.style.position == "absolute") { 
	                        accountForBody = true;
	                    }
	                    parentNode = parentNode.offsetParent;
	                }
	            }
	
				//safari doubles in this case
	            if (accountForBody) { 
	                pos[0] -= el.ownerDocument.body.offsetLeft;
	                pos[1] -= el.ownerDocument.body.offsetTop;
	            } 
	            parentNode = el.parentNode;
				pd = "";
				regTest = /^(?:inline|table-row)$/i;
	            // account for any scrolled ancestors
	            while (parentNode.tagName && parentNode.tagName != "body" && parentNode.tagName != "html") {
	                if (parentNode.scrollTop || parentNode.scrollLeft) {
	                    // work around opera inline/table scrollLeft/Top bug (false reports offset as scroll)
						pd = (parentNode.style && parentNode.style.display) ? parentNode.style.display : "";
	                    if (regTest.test(pd) && (!isOpera || (parentNode.style && parentNode.style.overflow !== 'visible'))) { 
	                        pos[0] -= parentNode.scrollLeft;
	                        pos[1] -= parentNode.scrollTop;
	                    }
	                }
	                parentNode = parentNode.parentNode; 
	            }
	            return pos;
	        }
		},
	
	    /**
	     * Returns the left scroll value of the document 
	     * @param {HTMLDocument} document (optional) The document to get the scroll value of
	     * @return {Int}  The amount that the document is scrolled to the left
	     */
	    getDocumentScrollLeft: function(doc) {
	        doc = doc || document;
	        return Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft);
	    }, 
	
	    /**
	     * Returns the top scroll value of the document 
	     * @param {HTMLDocument} document (optional) The document to get the scroll value of
	     * @return {Int}  The amount that the document is scrolled to the top
	     */
	    getDocumentScrollTop: function(doc) {
	        doc = doc || document;
	        return Math.max(doc.documentElement.scrollTop, doc.body.scrollTop);
	    },
	
		/**
		 * This function will tell you if the element in question will overlap the edges of the screen when 
		 * displayed at the coordinates provided.
		 *
		 * @method getPageOverlap
		 * @param  {DomNode}  el        The element we want the bounding box
		 * @param  {Object}   p         (optional) The x/y coordinates we will show the element
		 * @return {object}             Static object containing the amount the object overlaps
		 *                              the edges of the document.
		 */
		getPageOverlap : function (el,p) {
		    var v = this.getDocumentDimensions();
			var elc = this.coords(el, true);
		
		    // if position is not provided use the position of the element provided
		    if (p.x === null || p.y === null) {
		        p.x = elc.x;
		        p.y = elc.y;
		    }
		
		    // check if the element overlaps the edges
		    overlap = {
		        overTop : (p.y < v.t) ? v.t - p.y : 0,
		        overBottom :(p.y + elc.h > v.vh + v.t) ? (p.y + elc.h) - (v.vh + v.t) : 0,
		        overLeft : (p.x < v.l) ? v.l - p.x : 0,
		        overRight :(p.x + elc.w > v.vw + v.l) ? (p.x + elc.w) - (v.vw + v.l) : 0
		    };
		
		    return overlap;
		},

		/**
		 * Get's all the different dimensions for the document
		 * @return {Object} object containing the values of the viewport width and height (obj.vw,obj.vh), the 
		 *                  pages scroll amount (obj.l,obj.t), and the pages width and height (obj.w,obj.h)
		 */
		getDocumentDimensions : function() {
		    var d = {};
			var dde = document.documentElement;
			var b = document.body;
		
		    // viewport
		    d.vw = (dde && dde.clientWidth)  ? dde.clientWidth  : window.innerWidth || self.innerWidth || b.clientWidth; 
		    d.vh = (dde && dde.clientHeight) ? dde.clientHeight : window.innerHeight || self.innerHeight || b.clientHeight; 
		
		    // scroll
		    d.l = (dde && dde.scrollLeft) ? dde.scrollLeft : window.pageXOffset || self.pageXOffset || b.scrollLeft; 
		    d.t = (dde && dde.scrollTop)  ? dde.scrollTop  : window.pageYOffset || self.pageYOffset || b.scrollTop; 
		
		    // page
		    d.w = (dde && dde.scrollWidth)  ? dde.scrollWidth  : (b.scrollWidth > b.offsetWidth) ? b.scrollWidth : b.offsetWidth; 
		    d.h = (dde && dde.scrollHeight) ? dde.scrollHeight : (b.scrollHeight > b.offsetHeight) ? b.scrollHeight : b.offsetHeight;
		    return d;
		},

		/**
		 * IE and FF Mac have issues with some window elements ignoring zIndexes.  This method appends a dom 
		 * node specific for those situations.  Specifically using an iframe to prevent IE5+ select boxes from 
		 * floating and using a transparent scrollable div to prevent Firefox2 Mac from having scrollbars float 
		 * to the top.
		 * 
		 * @param {Object} syncEl the dom node that we want to sync our dom hack to
		 */
		addBrowserHackDomNode : function(syncEl) {
			// init vars
			var domEl,listener = null;
		
			// get the dom node based on the browser
			domEl = this.getBrowserHackDomNode(syncEl);
			document.body.appendChild(domEl);
		
			// for ff mac listen to window events and reset overlays
			if (isFF && isMac) {
				listener = this.addFFMacListeners(domEl);
			}
			return {domEl:domEl,listener:listener};
		},

		/**
		 * Removes hack nodes that have been added
		 * @param {Object} syncEl the dom node that we want to sync our dom hack to
		 */
		removeBrowserHackDomNode : function(hackObject) {
			if (hackObject.domEl) {
				try {
					document.body.removeChild(hackObject.domEl);
				} catch (e) {}
			}
		
			// for ff mac remove listener on window events
			if (isFF && iMac && hackObject.listener) {
				this.removeFFMacListeners(hackObject.listener);
			}
		},
		
		/**
		 * Finds elements with a passed in className
		 * @param {String} className
		 * @param {String|HTMLElement} baseEl (optional) the element to look inside for the class.  if not present 
		 *                                    will default to the document body.
		 */
		getElementsByClassName : function(classname,baseEl) {
			if(!baseEl) {
				baseEl = document.getElementsByTagName("body")[0];
			} else {
				baseEl = $(baseEl);
			}
			var a = [];
			var re = new RegExp('\\b' + classname + '\\b');
			var els = baseEl.getElementsByTagName("*");
			for (var i = 0; i < els.length; i++) {
				if (re.test(els[i].className)) {
					a.push(els[i]);
				}
			}
			return a;
		},

		/**
		 * IE and FF Mac have issues with some window elements ignoring zIndexes.  This method returns a dom 
		 * node specific for those situations.  Specifically using an iframe to prevent IE5+ select boxes from 
		 * floating and using a transparent scrollable div to prevent Firefox2 Mac from having scrollbars float 
		 * to the top.
		 * 
		 * @param {Object}  syncEl  the dom node that we want to sync our dom hack to
		 */
		getBrowserHackDomNode : function(syncEl) {
		    var coords = this.coords(syncEl,true), hack = null;
		    // create an overflow div for firefox mac
		    if (isFF && isMac) {
		        hack = document.createElement("div");
		        hack.style.overflow = "auto";
		    }
		
		    // create an iframe for ie 6 and lower
		    if (isIE && isIE < 7) {
		        hack = document.createElement("iframe");
		        hack.src = "javascript:false;";
		        hack.style.filter = "alpha(opacity=0)";
		        hack.frameBorder = 0;
		    }
		
		    if (!hack) {
		        return;
		    }
		
		    // sync with passed in el
		    hack.style.width = coords.w + "px";
		    hack.style.height = coords.h + "px";
		    hack.style.position = "absolute";
		    hack.style.left = coords.x + "px";
		    hack.style.top = coords.y + "px";
		    hack.style.border = "none";
		    hack.style.padding = "0";
		    hack.style.margin = "0";
		    hack.style.zIndex = parseInt(syncEl.style.zIndex,10) - 2;
		    
		    return hack;            
		},

		/**
		 * Sets Focus listeners for the window to prevent scrollbars from showing through
		 * @param {DomNode} el
		 */
		addFFMacListeners : function(el) {
		    var listenerId = dojo.connect(window,'focus',function(){
		        try {
		            document.body.removeChild(el);
		            document.body.appendChild(el);
		        } catch (e) {}
		    });
		    return listenerId;
		},

		/**
		 * Removes Focus listeners for the window for the given el
		 * @param {String} listenerId  the id of the listener that we want to remove
		 */
		removeFFMacListeners : function(listenerId) {
		    dojo.disconnect(listenerId);
		},

		/**
		 * 
		 * @param {HTMLElement} el                the html element we are processing
		 * @param {String}      defaultText       the text that we are going to check
		 * @param {String}      defaultTextClass  (optional) the classname for the default text classs
		 */
		setInputDefault : function(el,defaultText,defaultTextClass) {
			defaultTextClass = defaultTextClass || "defaultText";
			if (el.value.replace(/^\s*/,'').replace(/\s*$/,'') == '') {
				el.value=defaultText;
				this.addClass(el,defaultTextClass);		
			}
		},
		
		/**
		 * 
		 * @param {HTMLElement} el                the html element we are processing
		 * @param {String}      defaultText       the text that we are going to check
		 * @param {String}      defaultTextClass  (optional) the classname for the default text classs
		 */
		clearInputDefault : function(el,defaultText,defaultTextClass) {
			defaultTextClass = defaultTextClass || "defaultText";
			if(el.value == defaultText){
				el.value='';
			}
			this.removeClass(el,defaultTextClass);
		},
		
		/**
		 * returns a list of url parameters and their values
		 */
		getURLParameters : function() {
			var loc = location.search.substring(1, location.search.length);
			var params = loc.split("&");
			var arr = [];
	
			for (i=0; i<params.length;i++) {
				arr[params[i].substring(0,params[i].indexOf('='))] = params[i].substring(params[i].indexOf('=')+1);
			}
	
			return arr;
		},
		
		/**
		 * Returns the value of the parameter if it exists
		 * @param  {String} param
		 * @return {String|Null}   the string value if it exists, null otherwise
		 * 
		 */
		getURLParameter : function(param) {
			return this.getURLParameters()[param] || null;
		}
	};
	
	/**
	 * @namespace
	 * Holds basic functions for classes 
	 */
	gm.util.Class = {
		/**
		 * Create a simple class.
		 * @method create
		 * @param {Object|Function} def   A object literal containing properties and methods to be applied
		 *                                to the class prototype. The 'initialize' method implements the
		 *                                constructor. A function is also acceptable as the definition,
		 *                                in which case the function becomes the initialize method with
		 *                                an empty prototype.
		 * @return {Function} The new class.
		 */
		create: function(def) {
			// Create base class.
			var props = typeof def == 'function' ? def.prototype : def || {};
			var cls = function() {
				// Apply instance variables (two levels, skipping functions).
				var iprops = arguments.callee.prototype;
				for (var prop in iprops) {
					if (typeof iprops[prop] == 'object' && !(iprops[prop] instanceof Array) && iprops[prop] != null) {
						var subprops = iprops[prop];
						this[prop] = {};
						for (var sprop in subprops) {
							this[prop][sprop] = subprops[sprop];
						}
					} else if (typeof iprops[prop] != 'function') {
						this[prop] = iprops[prop];
					}
				}
				// Initialize.
				this.initialize.apply(this, arguments);
			};
			// Apply prototype.
			for (var prop in props) {
				cls.prototype[prop] = props[prop];
			}
			// Apply initialization.
			if (!cls.prototype.initialize) {
				cls.prototype.initialize = typeof def == 'function' ? def : function() {};
			}
			return cls;
		},
		
		/**
		 * Extends a class/object with additional properties and methods.
		 * @method extend
		 * @param {Object|Function} dest        The destination class to extend. No existing properties or methods 
		 *                                      will be overwritten, all conflicts are skipped.
		 * @param {Array|Object|Function} src   A source object/function to copy properties and methods from. 
		 *                                      This can also be an array of sources.
		 * @param {boolean} overwriteConflicts  (optional) defaults to true.  false will prevent it from overwriting
		 *                                      base functionality.
		 * @return {Object|Function} The updated class or object.
		 */
		extend: function(dest, src, overwriteConflicts) {
			// Mix in source classes/objects.
			dest = typeof dest == 'function' ? dest.prototype : dest || {};
			if (!(src instanceof Array)) {
				src = [src];
			}
			for (var i = 0; i < src.length; i++) {
				var props = typeof src[i] == 'function' ? src[i].prototype : src[i] || {};
				for (var prop in props) {
					if (!dest[prop] || !!overwriteConflicts) {
						dest[prop] = props[prop];
					}
				}
			}
			return dest;
		},
		
		/**
		 * Inherits a new class from a parent class.
		 * @method inherit
		 * @param {Function} parent       The parent class to derive from. The constructor/initialize method 
		 *                                will automatically be called upon initializing the new class.
		 * @param {Object|Function} def   A object literal containing properties and methods to be applied
		 *                                to the class prototype. The 'initialize' method implements the
		 *                                constructor. A function is also acceptable as the definition,
		 *                                in which case the function becomes the 'initialize' method with
		 *                                an empty prototype.
		 * @return {Function} The new class.
		 */
		inherit: function(parent, def) {
			// Create destination class.
			var cls = gm.util.Class.create(def);
			gm.util.Class.extend(cls, parent);
			// Inherit parent class.
			var init = cls.prototype.initialize;
			var base = parent.prototype.initialize || parent;
			cls.prototype.initialize = function() {
				init.apply(this, arguments);
				base.apply(this, arguments);
			};
			// Set a reference to the base class.
			cls.prototype.base = parent;
			return cls;
		}
	};
	
	
	/**
	 * @namespace 
	 * This namespace contains common functions for exeucting ajax requests.
	 * 
	 * @static
	 */
	gm.util.AJAX = {
	
	    /**
	     * Synchronously requests the given url and returns the response.
	     * 
	     * @param {String} url     url
	     * @param {Object} config  parameters used to control the request
	     * @return the response
	     */
	    syncRequest : function(url, config) {
	        // TODO: error handling
	        if (!config) {
				config = { };
			}
	        config.async = false;
	        var req = this._createRequest(url, config);
	        
	        var xhr = req.xhr;
	        xhr.send(null);
	        
	        var response = (xhr.responseXML) ? xhr.responseXML : (xhr.status == 200) ? xhr.responseText : '';
	        if (req.timeout) { clearTimeout(req.timeout); }
	        return response;   
	    },
	    
	    /**
	     * Asynchronously requests the given url and executes the given callback passing the response. 
	     * 
	     * @param {String} url         url
	     * @param {Function} callback  callback that handles the request
	     * @param {Object} config      parameters used to control the request
	     * @return the response
	     */
	    asyncRequest : function(url, callback, config) {
	        // TODO: error handling
	        if (!config) {
				config = { };
			}
	        config.async = true;
	        var req = this._createRequest(url, config);
	
	        var xhr = req.xhr;
	        
	        xhr.onreadystatechange = function() {
	            if (xhr.readyState == 4) {
	                var response = (xhr.status == 200) ? xhr.responseText : '';
	                if (req.timeout) { clearTimeout(req.timeout); }
	                callback(response);
	            }
	        };
	        
	        xhr.send(null);
	    },
	    
	    /**
	     * Sets up the request.
	     * 
	     * @param {Object} url      url
	     * @param {Object} config  parameters used to control the request
	     */
	    _createRequest : function(url, config) {
	    	// Create the request in a browser-independent manner.
	        var xhr;
	        
	        if (window.XMLHttpRequest) {
	            xhr = new XMLHttpRequest();
	        } else if (window.ActiveXObject) {
		       	try {
		        	xhr = new ActiveXObject("Msxml2.XMLHTTP");
		      	} catch(e) {
		        	try {
		          		xhr = new ActiveXObject("Microsoft.XMLHTTP");
		        	} catch(e) {
		          		xhr = false;
		        	}
				}
	
	
	        }
	
			if (!xhr) {
				return false;
			}
	        
	        xhr.open((config.method ? config.method.toUpperCase() : 'POST'), url, config.async);
	        
	        // Add a timeout handler if requested.
	        var timeout;
	        
	        if (config.timeout) {
	            timeout = setTimeout(function() { gm.util.AJAX._handleTimeout(xhr);}, config.timeout);
	        }
	        
	        var req = {
	            xhr : xhr,
	            timeout : timeout
	        };
	        
	        return req;
	    },
	    
	    /**
	     * Handles a request timeout.  
	     */
	    _handleTimeout : function(xhr) {
	    	xhr.abort();
	    }
	};
	
	
	/**
	 * @namespace
	 * This namespace holds utilities related to managing and building functionality in widgets
	 */
	gm.util.widget = {
		/**
		 * Will replace the provided key in the 
		 * @param {Object} txt
		 * @param {Object} key
		 * @param {Object} value
		 * @return {String}
		 */
		replace : function(txt,key,value) {
			// in the text passed in replace the keys with the string value passed in
			value = (value != null ? value : '').toString();
			return txt.replace(new RegExp("\\$\\{" + key + "\\}",'g'),value);
		},
	
		/**
		 * This function provides a way to provide our HTML a way to define event attachment inline without having
		 * to worry about the memory leaks associated with providing event attachment through onclick=""
		 * and similar html coding.  Since inline events typically have poor garbage collection and handle closures 
		 * poorly, we will use dojo to handle the event attachment of custom attributes.
		 * 
		 * The pattern we use here is to have a custom attribute in the html called "attachevent".  The attributes
		 * in attach event will provide keys to map to a function or set of functions as defined by the definitions
		 * file passed in.
		 * 
		 * This will effectively allow multistep event attachment with a small markup footprint, and circumvent the
		 * need for eval and other memory intensive procedures.
		 * 
		 * @example
		 * The syntax for the attribute is a set of comma delimited keys
		 * 
		 * <div attachevent="key1,key2">...</div>
		 * 
		 * The definitions object that is passed into the object will look like this:
		 * 
		 * object = {
		 *     key1 : {event:'click',fn:function() {gm.foo();}},
		 * 	   key2 : {event:'mousedown',fn:foo,context:gm}
		 * }
		 * 
		 * In the above example both key1 and key2 are examples of ways to call the same function.
		 * 
		 * @param {String|HTMLElement} baseEl  the handle to the element to start looking for events from
		 * @param {Object} mapObj              the object that maps the attachevent keys to functions
		 */
		attachEvents : function(baseEl,mapObj) {
			if(!baseEl) {
				return;
			} else if (gm.util.isString(baseEl)) {
				baseEl = $(baseEl);
			}
	
			var i,j,els,arr,st,def;
	
			// rotate through all nodes in el and attach events as needed
			els = baseEl.getElementsByTagName("*");
	
			// loop through all the elements in
			for (i = 0; i < els.length; i++) {
				st = els[i].getAttribute("gmattachevent");
				st = st;
				if (st) {
					st = gm.util.trim(st);
					arr = (st.indexOf(",") >= 0) ? st.split[","] : [st];
					for (j = 0; j < arr.length;j++) {
						def = mapObj[arr[j]];
						if (!def) {
							alert("No mapping for gmattachevent : " + arr[j]);
						}
						def.context = def.context || null;
						dojo.connect(els[i],def.event,def.context,def.fn);
					}
					els[i].removeAttribute("attachevent");
				}
			}
		},
	
		/**
		 * Format a template string with a list of replacement strings
		 * 
		 * @param {String} s        The string to run replacements on
		 * @param {Object} replace  An object literal of hashes to be replaced along with their values
		 * @return {String}         The resulting string
		 */
		template : function(s,replace) {
			for (var i in replace) {
				s = this.replace(s,i,replace[i]);
			}
			return s;
		},
	
		/**
		 * Function will search for a dom node with id "scriptToEval" and eval it.  Generic pattern to prevent us
		 * from having to make another request for json data associated with html.
		 */
		evalScript : function() {
			// eval the script in the scriptToEval div
			if ($('scriptToEval')) {
				// eval the passed in script and remove the node from the tree
				var s = $('scriptToEval');
				eval(s.innerHTML);
				s.parentNode.removeChild(s);
			}
		}
	};
})();
