/*****************************************************************************
 * @title:   gidFunctions.js
 * @author:  Andrew Southwick, Byung Kim
 * @date:    10-24-2007
 * @rev:     4.20
 * @desc:    Core GID Javascript Library
 * @assumes: prototype.js 1.5 rel., scriptaculous.js, brandConstants.js
 *
 * (C) Copyright 2004-2007 by Gap Inc.
 *  All Rights Reserved.
 *
 * This software is the confidential and proprietary information
 * of Gap Inc. ("Confidential Information"). Redistribution of the source
 * code or binary form is not permitted without prior authorization
 * from the Gap Inc.
 *
 *****************************************************************************/

/**
 * ClientBrowser is a class that defines our existing browser support.
 * It defines variables that can be accessed at runtime to determine
 * a user's browser type and version.
 * Instantiated as clientBrowser.
 *
 * Based on PLONE Browser Support document (10/9/2007)
 * Safari applewebkit versions from http://developer.apple.com/internet/safari/uamatrix.html
 *
 * @constructor
 * @author Byung Kim
 * @date 10/24/2007
 */
var ClientBrowser = Class.create();
ClientBrowser.prototype = {
	userAgent:navigator.userAgent,
	product:navigator.product,
	mimeTypes:navigator.mimeTypes,
	supportLevel:null,

	/* All currently supported browsers.
	 *
	 * name - string we use to name the new properties
	 * key - userAgent search string. Can be a partial RegExp string
	 * versions - browser versions supported.
	 * 		alias - the string used to name the property
	 * 		key - the RegExp to match all version numbers represented
	 * 		baseVersion - the minimum version number represented for the browser version we're checking.  This is used to determine the "Up" boolean.
	 */
	browsers:[
		{name:"IE",key:"MSIE ",versions:[
			{alias:"55",key:"5.5",baseVersion:"5.5"}, // IE 5.5
			{alias:"6",key:"6",baseVersion:"6"}, // IE 6
			{alias:"7",key:"7",baseVersion:"7"}, // IE 7
			{alias:"8",key:"8",baseVersion:"8"} // IE 8
		]},
		{name:"Firefox",key:"Firefox/",versions:[
			{alias:"1",key:"^1\.0[0-9\.]*",baseVersion:"1"}, // Firefox 1
			{alias:"15",key:"^1\.5[0-9\.]*",baseVersion:"1.5"}, // Firefox 1.5
			{alias:"2",key:"^2[0-9\.]*",baseVersion:"2"}, // Firefox 2
			{alias:"3",key:"^3[0-9\.]*",baseVersion:"3"} // Firefox 3
		]},
		{name:"Safari",key:"AppleWebKit/",versions:[
			{alias:"132",key:"^(312\.8)|(312\.8\.1)",baseVersion:"312.8"}, // Safari 1.3.2 (appleWebKit versions 312.8 - 312.8.1)
			{alias:"203",key:"^(417\.9)|(418)",baseVersion:"417.9"}, // Safari 2.0.3 (appleWebKit versions 417.9 - 418)
			{alias:"204",key:"^(418\.8)|(418\.9)|(418\.9\.1)|(419)|(419\.2\.1)|(419\.3)",baseVersion:"418.8"}, // Safari 2.0.4 (appleWebKit versions 418.8 - 419.3)
			{alias:"304",key:"^(523\.10)",baseVersion:"523.10"} // Safari 3.0.4 (appleWebKit versions 523.10+)
		]}
	],

	/**
	 * initialize clientBrowser
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	initialize:function() {
		this.setBrowserInfo();
//		this.setFlashDetection();
		this.setSupportLevel();
	},

	/**
	 * setBrowserInfo sets browser info properties
	 * Code flow:
	 * 1) check if the browser is in the userAgent string. If the browser wasn't found, we end here and no property is created.
	 * 2) create a new boolean property to denote that the browser was found in the userAgent.
	 * 3) get the full browser version string
	 * 4) create a new property for the full browser version string
	 * 5) check each supported version defined for the browser and set booleans for each version
	 * 6) set the boolean for each browser version (e.g.  isFirefox2, isIE55, isSafari203 )
	 *    The property is only set when it is not true.  This is to allow multiple checks to the same alias with different keys.
	 *    Useful for Safari versions which are based on multiple applewebkit versions.
	 * 7) set the boolean for each browser version and above (e.g. isFirefox2up, isIE55Up, isSafari203Up )
	 *    The property is only set when it doesn't exist.  The "Up" boolean is only set with the initial version number for a given alias.
	 *    Useful for Safari versions which are based on multiple applewebkit versions.
	 * 8) If the browser is detected, break the loop to minimize iterations.
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setBrowserInfo:function() {
		var client = this;
		var iterator = function(browser) {
			var inUA = (client.userAgent.match(browser.key) != null); // 1
			if (inUA) {
				client["is"+browser.name] = inUA; // 2
				var uaVer = client.getVersion(browser.key); // 3
				if (uaVer) {
					client["ver"+browser.name] = uaVer; // 4
					if (browser.versions) {
						browser.versions.each( // 5
							function(version) {
								var prop = "is"+browser.name+version.alias;
								if (!client[prop]) client[prop] = (uaVer.match(new RegExp(version.key)) != null); // 6
								if (client[prop+"Up"] == undefined) client[prop+"Up"] = (uaVer >= version.baseVersion); // 7
							}
						);
					}
				}
				$break; // 8
			}
		}
		this.browsers.each(iterator);

		// Check for Gecko
		this.isGecko = (navigator.product == "Gecko");
		if (this.isGecko) {
			this.verGecko = this.getVersion("rv:");
		}

		// Set Platform
		this.isMac = this.userAgent.include("Mac");
		this.isWin = this.userAgent.include("Windows");

	},

	/**
	 * getVersion returns the full version string from the userAgent
	 * @param {string} key The key used to search the userAgent string
	 * @return a string representing the version of the browser
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	getVersion:function(key) {
		var keyMatch = this.userAgent.match(new RegExp(key+"[0-9\.]*"));
		var keyValue = null;
		if (keyMatch && keyMatch.length > 0) {
			keyValue = keyMatch[0].match(/[0-9\.]+/)[0];	
		}
		return keyValue;
	},

	/**
	 * setSupportLevel sets the business support level
	 * 0 = no support
	 * 1 = semi support
	 * 2 = full support
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setSupportLevel:function(){
		if (this.isFirefox15Up || this.isIE6Up || this.isSafari204Up) {
			this.supportLevel = 2;
		} else if (this.isSafari132Up || this.isFirefox1Up || this.isNetscape7Up) {
			this.supportLevel = 1;
		} else {
			this.supportLevel = 0;
		}

	},

	/**
	 * setFlashDetection detects the flash plugin
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setFlashDetection:function() {
		var mimeTypes = this.mimeTypes;
		if (mimeTypes && mimeTypes["application/x-shockwave-flash"] && mimeTypes["application/x-shockwave-flash"].enabledPlugin) {
			var flashPlugin = mimeTypes["application/x-shockwave-flash"].enabledPlugin;
			this.isFlash = true;
			this.verFlash = flashPlugin.description.match(/[\d.]+/)[0];
		} else if (this.isWin && this.isIE6Up) {
			document.write('<scr' + 'ipt language=VBScript>' + '\n' +
			'Dim hasPlayer, playerversion' + '\n' +
			'hasPlayer = false' + '\n' +
			'playerversion = 10' + '\n' +
			'Do While playerversion > 0' + '\n' +
			'On Error Resume Next' + '\n' +
			'hasPlayer = (IsObject(CreateObject("ShockwaveFlash.ShockwaveFlash." & playerversion)))' + '\n' +
			'If hasPlayer = true Then Exit Do' + '\n' +
			'playerversion = playerversion - 1' + '\n' +
			'Loop' + '\n' +
			'client.verFlash = playerversion' + '\n' +
			'client.isFlash = hasPlayer' + '\n' +
			'<\/sc' + 'ript>');
		}
	}

};

var clientBrowser = new ClientBrowser();

/**
 * GidLib is a class containing the core javascript library for GID.
 * Instantiated as gidLib.
 *
 * @constructor
 * @author Byung Kim
 * @date 10/24/2007
 */
var LastKnownHiddenDropDowns = [];

var GidLib = Class.create();
GidLib.prototype = {

	/**
	 * GID constants
	 * @base GidLib
	 *
	 * @author Byung Kim
	 * @date 12/19/2007
	 */
	constants:{

	},

	/**
	 * initialize for GidLib
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	initialize:function() {
		this.addPrototypeMethods();
	},

    /**
     * clone - makes a shallow copy of desired obj, only sets the prototype to point to the original obj
     * this is different than prototypes own clone function which copies basic type attributes of original obj
     * @author yoshi
     */
     clone: function(obj) {
         function F() {}
         F.prototype = obj;
         return new F;
     },

    loadDomObjMap : function(obj, map) {
		Object.keys(map).each(function(key) {
			obj[key] = $(map[key]);
		});
	},

	loadBtnImgMap : function(obj, map, path) {
		Object.keys(map).each(function(key) { obj[key] = {src:path + map[key]}; });
	},

    /**
	 * addPrototypeMethods is used to add prototype methods to native classes
	 *
	 * @author Byung Kim
	 * @date 10/31/2007
	 */
	addPrototypeMethods:function() {
        /**
         * extend Array method to return index of found element instead of element value
         */
        Object.extend(Array.prototype, {findIndex : function(iterator) {
            var result;
            this.each(function(value, index) {
              if (iterator(value, index)) {
                result = index;
                $break;
              }
            });
            return result;
        }});

        /**
		 * add hasOwnProperty method to Object class if it doesn't exist. (e.g. Safari 1.3.2)
		 */
		if(!Object.prototype.hasOwnProperty) {
			Object.prototype.hasOwnProperty = function(prop) {
				return this.constructor.prototype[prop] === undefined;
			}
		}

		/**
		 * add setSrc method to IMG and INPUT tags via Prototype Element.addMethods();
		 */
		 Element.addMethods(['IMG','INPUT'],{
			setSrc:function(element,src) {
				$(element)["src"] = src;
			}
		 });

		/**
		 * add trim methods to String class
		 */
		 String.prototype.lTrim = function() {
		 	return this.replace(/^\s+/,'');
		 }
		 String.prototype.rTrim = function() {
		 	return this.replace(/\s+$/,'');
		 }
		 String.prototype.trim = function() {
		 	return this.replace(/^\s+|\s+$/g,'');
		 }
	},

	/**
	 * onLoadHandler is a collection of methods to execute on load of a page.  This is called globally
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	onLoadHandler:function() {
		if (window["GIDPageViewAdapter"]) {
			objGIDPageViewAdapter = new GIDPageViewAdapter();
			objGIDPageViewAdapter.objGIDProducts = new GIDProducts();
		}
	    var disableLayer = $('disableLayer');
        if(disableLayer) disableLayer.style.display = 'none';
//		this.buttonEvents.processQueue();

		var path = location.pathname;
        if (!/\/browse\//.exec(path)) {
        	this.setButtonEvents();
        }

        // Top nav and universal bar for all pages
    	this.setButtonEvents($('universalBar'));
    	this.setButtonEvents($('topNav'));

		this.setLabelOnClick();

		/* pageOnLoadFunctions() is deprecated - use Event.observe() to attach events */
		pageOnLoadFunctions();
	},

    /**
	 * stop events from bubbling up
	 *
	 * @author yoshi
	 * @date 07/09/2008
	 */
    stopBubbling: function(e) {
        if (!e) var e = window.event
        e.cancelBubble = true;
        if (e.stopPropagation) e.stopPropagation();
    },

    /**
	 * onUnloadHandler is a collection of methods to execute on unload of a page.  This is called globally
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	onUnloadHandler:function() {
		/* pageOnLoadFunctions() is deprecated - use Event.observe() to attach events */
        var disableLayer = $('disableLayer');
        if(disableLayer) disableLayer.style.display = 'block';
        pageOnUnloadFunctions();
	},


	/**
	 * hideDropDownsUnderElement hides all select boxes under a specified element for IE6
	 * @param {object} element A pointer reference to the element we want to hide <select> elements under
	 * @param {string} optional css selector
	 * @return an array of <select> elements that were hidden
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */

	hideDropDownsUnderElement:function(element, exp) {
		var hiddenDropDowns = [];

		if 	(LastKnownHiddenDropDowns.length > 0) {
			hiddenDropDowns = LastKnownHiddenDropDowns;
		}

		if (element && clientBrowser.isIE6 == true) {
			var dropDowns = $$(exp||'select');
			var iterator = function(dropDown) {
				var isHidden = (dropDown.getStyle("visibility") == "hidden" || dropDown.visible == false);
				if (dropDown && isHidden == false) {
					var dropDownPosition = Position.cumulativeOffset(dropDown);
					var dropDownWidth = dropDown.offsetWidth;
					var dropDownHeight = dropDown.offsetHeight;
					var isTopLeftUnderElement = Position.within(element,dropDownPosition[0],dropDownPosition[1]);
					var isTopRightUnderElement = Position.within(element,dropDownPosition[0]+dropDownWidth,dropDownPosition[1]);
					var isBottomLeftUnderElement = Position.within(element,dropDownPosition[0],dropDownPosition[1]+dropDownHeight);
					var isBottomRightUnderElement = Position.within(element,dropDownPosition[0]+dropDownWidth,dropDownPosition[1]+dropDownHeight);
					if (isTopLeftUnderElement || isTopRightUnderElement || isBottomLeftUnderElement || isBottomRightUnderElement) {
						dropDown.style.visibility = "hidden";
						hiddenDropDowns.push(dropDown);
					}
				}
			}
			dropDowns.each(iterator);
		}
		LastKnownHiddenDropDowns = hiddenDropDowns;
		return hiddenDropDowns;
	},

	/**
	 * showDropDowns shows all select boxes in the array created by the result of hideDropDownsUnderElement
	 * @param {object} hiddenDropDowns An array of <select> elements to set style visibility = visible
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	showDropDowns:function(hiddenDropDowns) {
		if (hiddenDropDowns&& hiddenDropDowns.length > 0 && clientBrowser.isIE6 == true) {
			hiddenDropDowns.invoke("setStyle", {visibility:"visible"});
		}
	},

	/**
	 * setFocus tries to focus on an element. If an exception is caught, return false.
	 * This method is necessary for IE which throws a JS error when you try to focus on
	 * an element where the element or any parent element is hidden or disabled.
	 *
	 * TODO: Only use the try..catch for IE.
	 *
	 * @param {object} element The DOM element we want to try to set focus() to.
	 * @return true or false depending on if the method was successful in setting the focus() on the specified element.
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setFocus:function(element) {
		var isFocusable = true;
		try {
			element.focus();
		} catch(e) {
			// the element could not be focused on. return false.
			isFocusable = false;
		}
		return isFocusable;
	},


	buttonEvents : {
		nodeQueue : [],
       	spriteBaseRegExp : /(universalButtonSprite\S+)(On|Over|Off)/,
       	spriteClassRegExp : /universalButtonSprite\S+/,

       	addToQueue : function(elementNode) {
			this.nodeQueue.push(elementNode);
			elementNode.onload = null;
		},

       	setEvents : function(elementNode) {
			if (elementNode.buttonEventsCompleted != true) {
				var imgsrc = elementNode.src;
				var searchIndex = elementNode.src.indexOf("?");
				var imgIndex = elementNode.src.indexOf(".gif");
				if (searchIndex == -1 || searchIndex > imgIndex) { // ensure the match is not part of a param or the start of params is after the img name
					var suffix = (imgsrc.indexOf("_sm.gif") > 0 ? "_sm.gif" : ".gif");
					if (imgsrc.search(/(_off|_on)/) != -1) {
						elementNode.offState = imgsrc;
			            elementNode.onState = imgsrc.search(/_on(_sm)?\.gif/) != -1 ? imgsrc : imgsrc.replace(/(_off)?(_sm)?\.gif/, "_on" + suffix);
			            elementNode.overState = imgsrc.replace(/(_off|_on)?(_sm)?\.gif/,"_over" + suffix);
						elementNode.isSpriteBased = false;

						Event.observe(elementNode,"mouseover",this.setOverState.bind(this));
						Event.observe(elementNode,"focus",this.setOnState.bind(this));
						Event.observe(elementNode,"mouseout",this.setOffState.bind(this));
						Event.observe(elementNode,"blur",this.setOffState.bind(this));
					}
					// Handle sprites
					else if (this.spriteClassRegExp.test(elementNode.className)) {
						var matches = this.spriteBaseRegExp.exec(elementNode.className);
						elementNode.isSpriteBased = true;
						elementNode.spriteBase = matches[1];

						Event.observe(elementNode,"mouseover",this.setOverState.bind(this));
						Event.observe(elementNode,"focus",this.setOnState.bind(this));
						Event.observe(elementNode,"mouseout",this.setOffState.bind(this));
						Event.observe(elementNode,"blur",this.setOffState.bind(this));
					}

				}
				elementNode.buttonEventsCompleted = true;
			}
		},

		setOverState : function(event) {
			var targetElement = Event.element(event);
            var isDisabled = targetElement.getAttribute('isDisabled')
            if(isDisabled) return;

            if (targetElement.isSpriteBased) {
            	targetElement.className = targetElement.className.replace(this.spriteClassRegExp,targetElement.spriteBase + "Over");
            } else {
	            targetElement.src = targetElement.overState;
	        }
		},

		setOnState : function(event) {
			var targetElement = Event.element(event);
            var isDisabled = targetElement.getAttribute('isDisabled')
            if(isDisabled) return;

            if (targetElement.isSpriteBased) {
            	targetElement.className = targetElement.className.replace(this.spriteClassRegExp,targetElement.spriteBase + "On");
            } else {
	            targetElement.src = targetElement.onState;
	        }
		},

        setOffState : function(event) {
            var targetElement = Event.element(event);
            var isDisabled = targetElement.getAttribute('isDisabled')
            if(isDisabled) return;

            if (targetElement.isSpriteBased) {
            	targetElement.className = targetElement.className.replace(this.spriteClassRegExp,targetElement.spriteBase + "On");
            } else {
	            targetElement.src = isDisabled ? targetElement.offState : targetElement.onState;
	        }
        },

        processQueue : function() {
        	this.nodeQueue.each(this.setEvents.bind(this));
        }

	},


	/**
	 * setButtonEvents sets button mouseover, mouseout, focus, and blur events for all navigational buttons
	 * @param {object} root The root DOM element to start searching for elements to modify
	 *
	 * Modified 11/12/2007 Byung Kim - fixed to support "_sm" type buttons
	 * Modified 7/22/2008 Byung Kim - added support for setting button events on specific root nodes even with HAS_BUTTON_MOUSE_OVERS = false
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setButtonEvents:function(root) {
		var elements = [];

		var addElements = function(attr) {
			var rootElement = (root ? $(root) : $('mainContent'));
			//var rootElement = (root ? $(root) : Element.extend(document.body));
			elements = elements.concat(rootElement ? rootElement.getElementsBySelector('img'+attr,'input'+attr) : $$('img'+attr,'input'+attr));
		}

		// a specific root node is specified
		if (root != null && root != undefined && $(root)) {
			addElements("[src*=/common/buttons/en/button_]");

		// search all buttons in the DOM
		} else {
			// branded buttons
			if (brandConst.HAS_BUTTON_MOUSE_OVERS) addElements("[src*=/common/buttons/en/button_]");
		}

		// universal buttons and universal buttons from sprites
		if (brandConst.UNIVERSAL_BUTTON_CONTENT_PATH && brandConst.UNIVERSAL_BUTTON_CONTENT_PATH != "") {
			addElements("[src*=" + brandConst.UNIVERSAL_BUTTON_CONTENT_PATH + "]");
			addElements("[class*=universalButtonSprite]");
		}

		elements.each(gidLib.buttonEvents.setEvents.bind(gidLib.buttonEvents));
	},

	/**
	 * selectOption selects a form field option based on the provided value.
	 * Works for select, select-multiple, radio, and checkboxes.
	 * @param {object} element The DOM Form element we want to modify
	 * @param {string} value The value of the option we want to set as selected
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	selectOption:function(element,value) {
		var fieldType = element.type;
		if (!fieldType && element[0]) fieldType = element[0].type;
		if (fieldType == "select-one" || fieldType == "select-multiple") {
			for (i=0;i<element.options.length;i++) {
				if (element.options[i].value == value) {
					element.options[i].selected = true;
					break;
				}
			}
		} else if (fieldType == "radio" || fieldType == "checkbox") {
			for (i=0;i<element.length;i++) {
				if (element[i].value == value) {
					element[i].checked = true;
					break;
				}
			}
		}
	},

	/**
	 * setLabelOnClick fixes an IE defect where the label tag doesn't act to focus on the respective form element when clicked
	 *
	 * Modified 12/18/2007 Byung Kim - refactored to work better
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setLabelOnClick:function() {
		if (clientBrowser.isIE) {
			var labels = $$('label');

			var eventMethod = function(e) {
				var element = $(Event.element(e).htmlFor);
				if (element) gidLib.setFocus(element);
				return false;
			}

			var iterator = function(element) {
				Event.observe(element, "click", eventMethod.bindAsEventListener(this));
			}

			labels.each(iterator);
		}
	},


	/**
	 * setCookie sets a cookie to the document object
	 * @param {string} name The cookie name
	 * @param {string} value The cookie value.  This needs to be an escape() value under most cases.
	 * @param {object} expires The cookie expire date.  It should be a Date() object.  If its the millisecond representation, it is converted to the Date() object.
	 * @param {string} path The cookie path
	 * @param {string} domain The cookie domain
	 * @param {boolean} secure Whether the cookie is set secure
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setCookie:function(name,value,expires,path,domain,secure) {
		if (typeof expires != "object") expires = new Date(expires);
		document.cookie = name + "=" + value +
			((expires) ? "; expires=" + expires.toGMTString() : "") +
			((path) ? "; path=" + path : "") +
			((domain) ? "; domain=" + domain : "") +
			((secure) ? "; secure" : "");
	},

	/**
	 * getCookie retrieves the value of the specified document cookie
	 * @param {string} cookieName The name of the cookie we want to retrieve the value for
	 * @return the value of the cookie.  If no cookie is found, it returns zero (0).
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	getCookie:function(cookieName) {
		var cookie = document.cookie;
	  	var key = cookieName + "=";
	  	var value = 0;
		var keyMatch = cookie.match(new RegExp(key+"[^;]*"));
		if (keyMatch && keyMatch.length > 0) {
			value = unescape(keyMatch[0].substr(key.length));
		}

	 	return value;
	},

	/**
	 * getCookieVar returns the value of a key-value pair within the specified document.cookie
	 * @param {string} cookieName The cookie we want to get the value from
	 * @param {string} key The key to check in the cookie
	 * @return the value of the key-value pair.  If the key is not found, it returns a blank string ("")
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	getCookieVar:function(cookieName, key) {
		var cookieValue = gidLib.getCookie(cookieName);
		var keyValue = "";
		if (cookieValue != 0 && key && key != "") {
			var keyMatch = cookieValue.match(new RegExp(key+"=[^&]*"));
			if (keyMatch && keyMatch.length > 0) {
				keyValue = unescape(keyMatch[0].substr(key.length+1));
			}
		}
		return keyValue;
	},

	/**
	 * setCookieParamsHelper return the appropriate values for cookie expiration, domain, path, and secure flag.
	 * @param {string} cookieName The cookie name we want to use.
	 * @param {object} expDate The Date() object representing the date we want to expire the cookie
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setCookieParamsHelper : function(cookieName,expDate) {
		var exp = null;
		var domain = null;
		var path = "/";
		var isSecure = (location.protocol == "https:");

		// set cookieDomain
		var securePrefix = "secure-"
		var domain  = null;
		var  renderOnOldDomain = true;
		var h = location.hostname;
		if (window["gidBrandSiteConstruct"]) {
			if (gidBrandSiteConstruct.sslPrefix && gidBrandSiteConstruct.sslPrefix != "") securePrefix = gidBrandSiteConstruct.sslPrefix;
			domain = gidBrandSiteConstruct.cookieDomain;
			renderOnOldDomain = gidBrandSiteConstruct.renderOnCurrentBrandUrl;
			if(h.indexOf(domain) == -1){
				renderOnOldDomain =  true;
			}
		}

		if(renderOnOldDomain){
			securePrefix = "secure."
			domain = (h == "localhost" ?  null : "." + (h.indexOf(securePrefix) == 0 ? h.substr(securePrefix.length+(securePrefix.indexOf(".") != -1 ? 0 : 1)) : h));
		}
		// set cookieExp for persistent cookies
		if ((cookieName.toLowerCase()).indexOf("persist") != -1) {
			exp = (expDate != null ? expDate : gidLib.getFutureDate({"years":5}));
		}

		return {exp:exp,domain:domain,path:path,isSecure:isSecure};
	},

	/**
	 * setCookieVar sets a key value pair in the specified cookie.
	 * If the key already exists, it updates the key value.  If the cookie doesn't exist, it creates a new one.
	 * @param {string} cookieName The cookie name we want to use.  Include "persist" in the name to make it a persistent cookie.  Include "session" in the name to make it a session cookie.
	 * @param {string} cookieVarKey The key of the key-value pair we want to set
	 * @param {string} cookieVarValue The value of the key-value pair we want to set
	 * @param {object} expDate The Date() object representing the date we want to expire the cookie
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setCookieVar:function(cookieName,cookieVarKey,cookieVarValue,expDate) {
		/*
		if (window["cookieManagementService"]) {
			if (cookieManagementService.api.isCookieManagementServiceReady()) {
				
			} else {
				Event.observe(document, "cookieManagementService:ready", cookieManagementService.controller.cookieManagementServiceReadyHandler);
			}
		}
		*/
		// initialize params
		var newCookieValue = "";

		// Set newCookieValue
		var cookieValue = gidLib.getCookie(cookieName);
		var cookieKeyValuePair = cookieVarKey + "=" + escape(cookieVarValue);
		if (cookieValue == 0 || cookieValue == "") {
			newCookieValue = cookieKeyValuePair;
		} else {
			if (cookieValue.indexOf(cookieVarKey + "=") != -1) {
				newCookieValue = cookieValue.replace(new RegExp(cookieVarKey+"=[^&]*"),cookieKeyValuePair);
			} else {
				newCookieValue = cookieValue + "&" + cookieKeyValuePair;
			}
		}

		var cookieConfiguration = cookieManagementService.api.getCookieConfigurationByCookieName(cookieName);
		if (cookieConfiguration) {
			/* Cookie is managed by cookieManagementService so set all values based on cookieConfiguration */
			gidLib.setCookie(cookieConfiguration.name,
					escape(newCookieValue),
					cookieConfiguration.expirationDate,
					cookieConfiguration.path,
					cookieConfiguration.domain,
					cookieConfiguration.isSecure);
		} else {
			/* Cookie is not managed by cookieManagementService so proceed with default execution */
			var cookieParams = gidLib.setCookieParamsHelper(cookieName,expDate);
			/* Set cookie */
			gidLib.setCookie(cookieName,escape(newCookieValue),cookieParams.exp,cookieParams.path,cookieParams.domain,cookieParams.isSecure);
		}
	},

	/**
	 * getCookieKeyValuePairs returns an array of key value pairs for a specified cookie
	 * @param {string} cookieName The cookie name we want to use.
	 * @return an array of JSON objects that represent the key value pairs in the cookie
	 *
	 * @author Byung Kim
	 * @date 8/11/2008
	 */
	getCookieKeyValuePairs : function(cookieName) {
		var cookieValue = gidLib.getCookie(cookieName);
		var keyValuePairs = [];
		if (cookieValue != 0) {
			var cookieKeyValuePairs = unescape(cookieValue).split("&");
			cookieKeyValuePairs.each(function(keyValuePair) {
				var pair = keyValuePair.split("=");
				keyValuePairs.push({key:pair[0],value:pair[1]});
			});
		}
		return keyValuePairs;
	},

	/**
	 * removeCookieVar removes a key value pair from a cookie
	 * @param {string} cookieName The cookie name we want to use.
	 * @param {string} cookieVarKey The key of the key-value pair we want to remove
	 * @param {object} expDate The Date() object representing the date we want to expire the cookie
	 *
	 * @date 8/11/2008
	 * @author Byung Kim
	 */
	removeCookieVar : function(cookieName, cookieVarKey, expDate) {
		var cookieValue = gidLib.getCookie(cookieName);
		if (cookieValue != 0) {
			cookieValue = cookieValue.replace(new RegExp(cookieVarKey+"=[^&]*&?"),"").replace(/&$/,""); // 2nd replace() removes the trailing "&" if necessary
			var cookieParams = gidLib.setCookieParamsHelper(cookieName,expDate);
			gidLib.setCookie(cookieName,escape(cookieValue),cookieParams.exp,cookieParams.path,cookieParams.domain,cookieParams.isSecure);
		}
	},

	/**
	 * getFutureDate gets the future time based on the current time and a given incremented time.
	 * @param {object} futureDate A JSON Object that represents the incremented time.
	 * 		Valid parameters are: days, weeks, years, hours, minutes, seconds, milliseconds
	 * 		e.g  {"days":7,"years":1,"months":5}  -- denotes a date one year, five months, and seven days in the future
	 * @return newDate The future time in milliseconds
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	getFutureDate:function(futureDate) {
		var newDate = new Date();
		var c = [];
		c["milliseconds"] = 1;
		c["seconds"] = c["milliseconds"]*1000;
		c["minutes"] = c["seconds"]*60;
		c["hours"] = c["minutes"]*60;
		c["days"] = c["hours"]*24;
		c["weeks"] = c["days"]*7;
		c["years"] = c["weeks"]*52;
		var ms = 0;
		var params = Object.keys(futureDate);
		if (params && params.length) {
			params.each(function(param) {
				ms += c[param]*futureDate[param];
			});
		}
		newDate = newDate.setTime(newDate.getTime() + ms);
		return newDate;
	},

	/**
	 * loadImage loads an image into memory. Useful for postloading images.
	 * Not to be used if you only want to create an image object.  Use {src:"/image/path/imageName.gif"} for that.
	 * @param {string} src The path to the image
	 * @return the Image object with the src set
	 *
	 * @author Byung Kim
	 * @date 10/25/2007
	 */
	 loadImage:function(src) {
	 	var obj = document.createElement("img");
	 	$(obj).setSrc(src);
	 	return obj;
	 },

	/**
	 * setObjPosition sets a DOM elements style.top and style.left CSS properties
	 * @param {object} element The DOM element we want to set position
	 * @param {integer} intX The X coordinate value
	 * @param {integer} intY The Y coordinate value
	 *
	 * Modified 12/19/2007 Byung Kim - refactored to use setStyle
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setObjPosition:function(element,intX,intY) {
		strX = String(intX);
		strY = String(intY);
		$(element).setStyle({
			top: intY + (strY.indexOf("px") == -1 ? "px" : ""),
			left: intX + (strX.indexOf("px") == -1 ? "px" : "")
		});
	},

	/**
	 * setObjCenter sets a DOM element absolutely positioned to the center of the screen
	 * @param {object} target The DOM element we want to modify
	 *
	 * Modified 12/19/2007 Byung Kim - refactored to use setStyle
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setObjCenter:function(target) {
	    var halfW = (document.body.clientWidth ? document.body.clientWidth : window.innerWidth) * 0.5;
	    var halfH = (document.body.clientHeight ? document.body.clientHeight : window.innerHeight)  * 0.5;
	    $(target).setStyle({
	    	position:"absolute",
	    	left:(halfW - target.offsetWidth * 0.5) + 'px',
	    	top:(halfH - target.offsetHeight * 0.5) + 'px'
	    });
	},

	/**
	 * getQuerystringParam gets the value of the querystrings key-value pair
	 * @param {string} keyArg The key of the key-value pair
	 * @param {boolean} preserveCase Whether to preserve the case of the querystring & keyArg
	 * @return the value of the key-value pair
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	getQuerystringParam:function(keyArg,preserveCase) {
		var query = (preserveCase ? location.search : location.search.toLowerCase());
		var paramKey = (preserveCase ? keyArg : keyArg.toLowerCase());
		var paramVal = "";

	  	var regex = new RegExp("[\\?&]"+paramKey+"=([^&#]*)");
	  	var keyVal = regex.exec(query);
	  	if (keyVal != null) paramVal = keyVal[1];

	  	paramVal = unescape(paramVal);
	  	return paramVal;
	},

	/**
	 * removeQueryStringParam removes a key-value pair from the specified string
	 * @param {string} url The url string to remove the key-value pair from
	 * @param {string} param The key of the key-value pair to remove
	 * @return the modified string
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	removeQueryStringParam:function(url,param) {
		var regExp =  new RegExp("&"+param+"=[^&]*");
		var regExp2 =  new RegExp("/?"+param+"=[^&]*&");

		// param to remove may be a first (preceeded by ?) or subsequent parameter (preceeded by &)
		return (url.match(regExp) ? url.replace(regExp,"") : url.replace(regExp2,""));
	},

	/**
	 * addCurrentDomain takes a relative url and returns a fully qualified url string
	 * @param {string} url The relative url to modify
	 * @return a fully qualified absolute path url based on the current page location protocol & location.host
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	addCurrentDomain:function(url) {
		return location.protocol + "//" + location.host + url;
	},

	/**
	 * openWindow opens a popup window
	 * @param {string} url The url of the popup
	 * @param {integer} width The width of the popup.  If one isn't specified, it defaults to the value in brandConst
	 * @param {integer} height The height of the popup.  If one isn't specified, it defaults to the value in brandConst
	 * @param {string} title The name of the popup window
	 * @param {integer} left The X coordinate for where the popup should appear on the screen. If one isn't specified, it defaults to the value in brandConst
	 * @param {integer} top The Y coordinate for where the popup should appear on the screen. If one isn't specified, it defaults to the value in brandConst
	 * @param {boolean} location Whether to display the location bar in the popup. If this isn't specified, it defaults to no.
	 * @param {boolean} resizable Whether the popup is resizable. If this isn't specified, it defaults to no.
	 * @param {boolean} scrollbars Whether to show scrollbars. If this isn't specified, it defaults to no.
	 * @param {boolean} status Whether to display the status bar. If this isn't specified, it defaults to no.
	 * @param {boolean} toolbar Whether to display the toolbar. If this isn't specified, it defaults to no.
	 * @param {boolean} menubar Whether to display the menubar. If this isn't specified, it defaults to no.
	 * @return the window object
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	openWindow:function(url,width,height,title,left,top,location,resizable,scrollbars,status,toolbar,menubar) {
		width = parseInt(width);
		height = parseInt(height);
		width = (isNaN(width))? brandConst.POPUP_DEFAULT_WIDTH : width;
		height = (isNaN(height))? brandConst.POPUP_DEFAULT_HEIGHT : height;
		title = (title)? title : "popup";
		if (!left) {
			screenWidth = screen.availWidth;
			left = (screenWidth)? Math.max((screenWidth/2 - width/2),0) : 100;
		}
		if (!top) {
			screenHeight = screen.availHeight;
			top = (screenHeight)? Math.max((screenHeight/2 - height/2),0) : 100;
		}
		var params = "width=" + width + ",height=" + height + ",left=" +left  + ",top=" + top;
		params += (resizable)? ",resizable=yes" : "";
		params += (scrollbars)? ",scrollbars=yes" : "";
		params += (status)? ",status=yes" : "";
		params += (location)? ",location=yes" : "";
		params += (toolbar)? ",toolbar=yes" : "";
		params += (menubar) ? ",menubar=yes" : "";

		var obj = window.open(url,title,params);
		gidLib.setFocus(obj);
		return obj;
	},

	/**
	 * set800pxUniversalNav sets the Tab Navigation to resize and hide the marketing content
	 * so the width of 5 Tabs can fit in a 800px Screen Width
	 *
	 * @author Keo Keonorasak
	 * @date 01/01/2009
	 */
	set800pxUniversalNav:function() {
	    var screenWidth = screen.width;
	    if((screenWidth != null) && (screenWidth <= 800)){
	    	if (gidBrandSiteConstruct.gidBrandSites){
	    		if (gidBrandSiteConstruct.gidBrandSites.compact().uniq().length > 4){
			    	var universalBarCenter = $("universalBarCenter");
			    	var universalBarCenterContainer = $("universalBarContainer");
			    	var universalMarketingContainerTop = $("universalMarketingContainerTop");
			    	universalBarCenter.className = "universalBarContainer800Screen";
			    	universalBarCenterContainer.className = "universalBarContainer800Screen";
			    	universalMarketingContainerTop.setStyle({width: "54px"});
			    	universalMarketingContainerTop.firstDescendant().setStyle({visibility: "hidden"});
			    }
	    	}
	    }
	},

	/**
	 * closeWindow closes the current window
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	closeWindow:function() {
		if (window.opener) window.opener.focus();
		window.close();
	},

	/**
	 * contentItemLink takes the user to a url but first appends values for reporting
	 * @param {string} domTarget The content item element id
	 * @param {string} strURL The url to modify and add the appropriate parameters
	 * @param {string} linkId Used to distinguish between similar links originating from the same content item
	 * @param {string} urlTarget Used to specify a window target
	 * @return a boolean depending on urlTarget.  It returns false if it needs to target a different window
	 *
	 * Modified 12/18/2007 Byung Kim - check objTarget is null before looking up the className
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	contentItemLink:function(domTarget,strURL,linkId,urlTarget) {
		var objTarget = null;
		var contentItemId = "";
		var strContentItemContainerPrefix = "contentItemContainer";
		var refBusinessId = null;
		var isHardCodedId = (typeof domTarget == "string" || typeof domTarget == "number");
		var isHardCodedURL = (strURL && strURL != '');
		if(!(reportingService||{}).isActive){
			refBusinessId = (window["omni"] ? omni.strCurrentBusinessId : "");
		}
		else {
			refBusinessId = reportingService.controller.viewManagers.commonViewManager.model.commonCurrentBusinessId;
		}
		if (isHardCodedId) {
			objTarget = (contentItemId = domTarget);
		} else {
			objTarget = domTarget;
			do {
				isFound = ((objTarget && objTarget.id && objTarget.id.match(new RegExp(strContentItemContainerPrefix))) || objTarget == null);
				if (!isFound) objTarget = objTarget.parentNode;
			} while (!isFound);
			if ($(objTarget)) contentItemId = $(objTarget).classNames();
		}
		var updateURLString = function(strURL) {
			strURL += (strURL.indexOf("?") == -1 ? "?" : "&") +
				"mlink=" +
				refBusinessId + "," +
				contentItemId +
				(linkId && linkId != "" ? "," + linkId : "") +
				"&clink=" + contentItemId;
			return strURL;
		}
		if (objTarget) {
			if (!isHardCodedURL) {
				if (domTarget.useMap && document.getElementsByName(domTarget.useMap.substr(1)).length > 0) {
					strLink = document.getElementsByName(domTarget.useMap.substr(1))[0].areas[0].href;
				} else if (domTarget.href) {
					strLink = domTarget.href;
				} else if (domTarget.parentNode && domTarget.parentNode.href) {
					strLink = domTarget.parentNode.href;
				}
				strURL = strLink.replace(new RegExp(".+"+location.host),"");
			}

			var isUrl = strURL.match(/^(\/|http|about)/);
			if (isUrl) {
				if (strURL.match(/^\//)) strURL = updateURLString(strURL);
			} else {
				var expURL = new RegExp(/[^']*\.do\?[^']*/g);
				var arrayURLs = strURL.match(expURL);
				if (arrayURLs) {
					for (i=0;i<arrayURLs.length;i++) {
						strURL = strURL.replace(arrayURLs[i],updateURLString(arrayURLs[i]));
					}
				}
			}
			if (urlTarget && urlTarget == "_new") {
					var newWindow = window.open(strURL,'');
					newWindow.focus();
			} else {
				var objTargetWindow = (urlTarget && window[urlTarget] ? window[urlTarget] : window);
					objTargetWindow.location.href = strURL;
			}
			if (!isHardCodedURL) return false;
		} else {
			if (!isHardCodedURL) return true;
		}
	},

	/**
	 * unUnicode converts all unicode characters to ascii
	 * @param {string} str The string to modify
	 * @return the modified string
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	unUnicode:function(str) {
		if (str) {
			var arrayMatch = str.match(/&#[0-9]+;/g);
			if (arrayMatch) {
				for (var i=0;i<arrayMatch.length;i++) {
					str = str.replace(arrayMatch[i],String.fromCharCode(arrayMatch[i].match(/[0-9]+/)));
				}
			}
		}
		return str;
	},

	/**
	 * openGiftCardWindow opens a layered popup with the giftcard balance page
	 * @param {boolean} hasDisplayButton determines whether to show the close button
	 *
	 * Modified 2/15/2008 Byung Kim - updated openLayeredPopup call to new API
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	openGiftCardWindow:function(hasDisplayButton) {
		var shouldDisplayButton = "";
		if (hasDisplayButton) shouldDisplayButton = 'displayButton=true';
		gidLib.layeredPopup.openLayeredPopup({
			str:'/buy/giftCardBalance.do?'+shouldDisplayButton,
			id:'giftCardPopupContent',
			width:brandConst.POPUP_GIFTCARD_WIDTH,
			height:brandConst.POPUP_GIFTCARD_HEIGHT,
			title:'GiftCard'
		});
	},

	/**
	 * isMouseOut
	 *
	 * TODO: Yoshi to add documentation
	 *
	 */
    isMouseOut: function(x, y, boundBox) {
		return (x < boundBox.left ) || (x > boundBox.right) || (y < boundBox.top ) || (y > boundBox.btm) ;
	},

	/**
	 *  isTrueMouseOutEvent is used to determine if the mouseout event occured
	 *  as the user moused out of the given element and not by mousing
	 *  over a childNode within the element.
	 *
	 *  returns true when the user moused out of the element
	 *  returns false when the user moused over a childNode within element
	 *
	 *  accepts optional method to execute when true
	 */
	isTrueMouseOutEvent : function(e,element,method) {
		var eventElement = Event.element(e);
		var mouseOutTarget = $(e.relatedTarget);
		var parentNodes = mouseOutTarget.ancestors();
		var isChildNode = parentNodes.include(element);
		var isTrueMouseOut = (eventElement == element && isChildNode == false);
		if (isTrueMouseOut && method) {
			method();
		}
		return isTrueMouseOut;
	},


	/**
	 * loadScript uses a dynamic script tag to load JS into the site.  Used for cross-domain AJAX.
	 * @param {object} param JSON object with all the required parameters
	 * 			callerObject : The class/object that's calling loadScript
	 * 			src : The src attribute value of the script tag.
	 * 			timeout : JSON object with the handler and args for timing out
	 * 			callBack: JSON object with the handler and args for the callback
	 *
	 * @author Byung Kim
	 * @date 11/2/2007
	 */
	loadScript:function(param) {
		var script = $(document.createElement("script"));
		var targetId = "dynamicScriptLoader";
		script.type = "text/javascript";
		script.src = param.src;
		var target = $(targetId);
		if (!target) {
			target = $(document.createElement("div"));
			target.id = targetId;
			target.setStyle({display:"none"});
		}
		document.body.appendChild(target);
		target.appendChild(script);

		param.callerObject.loadScriptTimedOut = false;
		param.callerObject.loadScriptSuccess = false;

		// Timeout handler
		var timeout = param.timeout;
		if (timeout && timeout.handler) {
			new PeriodicalExecuter(function(periodicalExecuterRef) {
				if (!param.callerObject.loadScriptSuccess) {
					param.callerObject.loadScriptTimedOut = timeout.handler(timeout.args);
				}
				periodicalExecuterRef.stop();
			},timeout.timeDelay);
		}

		// Callback handler
		var callback = param.callback;
		if (callback && callback.handler) {
			new PeriodicalExecuter(function(periodicalExecuterRef) {
				if (param.callerObject.loadScriptTimedOut) {
					periodicalExecuterRef.stop();
				} else {
					param.callerObject.loadScriptSuccess = callback.handler(callback.args,periodicalExecuterRef);
				}
			},callback.timeDelay);
		}
	},

    /**
	 * check if target element contains ele, use for mouseout and mouse over testing
	 * @base GidLib
	 *
	 * @author yoshi
	 * @date 10/21/2008
	 */
    checkMouseEvent: function(ele, target) {
        var isContain;
        if(target.contains) { isContain = !target.contains(ele); }
        if(isContain == undefined && target.compareDocumentPosition) { isContain = target.compareDocumentPosition(ele); }

        return target.contains ? isContain : isContain < 16;
    },

	/**
	 * layeredPopup is a subclass of GidLib for all things related to layered popup functionality
	 * @base GidLib
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	layeredPopup: {
		dragger:null,
		hiddenDropDowns:null,
		effects:null,
		isOpen:false,
		popupId:"",

		/**
		 * layeredPopups can have different styles.  Each object within styles contains everything required to display differently.
		 *
		 * @author Byung Kim
		 * @date 1/8/2008
		 */
		styles : {

			/**
			 * commonTemplates contains templates that are common to all styles
			 *
			 * @author Byung Kim
			 * @date 1/8/2008
			 */
			commonTemplates : {
				iFrameContentTemplate : new Template('<iframe id="#{id}PopupContent" scrolling="#{scrolling}" name="#{id}PopupContent" src="#{src}" class="content" style="width:#{width}px;height:#{height}px;" frameborder="0"></iframe>'),
				markupContentTemplate : new Template('<div id="#{id}PopupContent" class="content" style="width:#{width}px;height:#{height}px;">#{content}</div>'),
				closeAnchorTemplate : new Template('<a href="#" onclick="return gidLib.layeredPopup.closeLayeredPopup();">#{text}</a>'),
				universalCloseButtonTemplate : new Template('<img src="/assets/common/clear.gif" alt="#{altText}" class="universalButtonSprite universalButtonSpriteCloseWindowOn"/>')
			},

			/**
			 * brandedPopup is a style type for layeredPopup display
			 *
			 * @author Byung Kim
			 * @date 1/8/2008
			 */
			brandedPopup : {
				constants : brandConst,
				templates : {
					mainTemplate : new Template(
						'<div id="popupMain" style="width:#{totalWidth}px;">' +
							'<div class="topBorder clearfix">' +
								'<div class="topLeftCorner"></div>' +
								'<div id="layeredPopup#{topId}FrameTop" class="topMiddle#{topClass}" style="width:#{topWidth}px;">#{title}</div>' +
								'<div class="topCloseButton"><a href="#" onclick="return gidLib.layeredPopup.closeLayeredPopup();">&#160;</a></div>' +
								'<div class="topRightCorner"></div>' +
							'</div>' +
							'<div class="mainContent clearfix">' +
								'<div class="leftBar" style="height:#{borderHeight}px;"></div>' +
								'#{content}' +
								'<div class="rightBar" style="height:#{borderHeight}px;"></div>' +
							'</div>' +
							'<div class="bottom clearfix ">' +
								'<div class="bottomLeftCorner"></div>' +
								'<div class="bottomMiddle" style="width:#{innerContentWidth}px;">#{bottonCloseButton}</div>' +
								'<div class="bottomRightCorner"></div>' +
							'</div>' +
						'</div>'
					)
				},

				/**
				 * getMarkup returns the markup for brandedPopup styled popups
				 *
				 * @author Byung Kim
				 * @date 1/8/2008
				 */
				getMarkup:function(useIFrame, width, height, title, strContent, id, style, scrolling) {
					var constants = this.constants;
					var templates = this.templates;
					var commonTemplates = gidLib.layeredPopup.styles.commonTemplates;
					var topId = ""
					var topClass = "";
					var content = "";
					var bottomCloseButton = (brandConst.LAYERED_POPUP_CLOSE_COPY != "" ? commonTemplates.closeAnchorTemplate.evaluate({copy:brandConst.LAYERED_POPUP_CLOSE_COPY}) : "");
					var topTitle = brandConst.LAYERED_POPUP_BRAND_NAME + (title && title != "" ? " - " + title : "");
					if (!topTitle || topTitle == "") topTitle = "&#160;";

					var innerContentWidth = width - constants.LAYERED_POPUP_SIDE_IMAGE_WIDTH;
					var borderHeight = height + constants.LAYERED_POPUP_CONTENT_COMBINED_BORDER_HEIGHT;

					if (useIFrame) {
						topId = "NoDrag";
						content = commonTemplates.iFrameContentTemplate.evaluate({
							id:id,
							src:gidLib.addCurrentDomain(strContent),
							width:innerContentWidth,
							height:height,
							scrolling:scrolling
						});
					} else {
						topClass = " cursorMove";
						content = commonTemplates.markupContentTemplate.evaluate({
							id:id,
							content:strContent,
							width:innerContentWidth,
							height:height
						});
					}

					return templates.mainTemplate.evaluate({
						topId:topId,
						topClass:topClass,
						totalWidth:width+constants.LAYERED_POPUP_COMBINED_BORDER_WIDTH+constants.LAYERED_POPUP_CONTENT_COMBINED_BORDER_WIDTH+constants.LAYERED_POPUP_SHADOW_WIDTH,
						topWidth:width-constants.LAYERED_POPUP_MAIN_WIDTH_OFFSET,
						innerContentWidth:innerContentWidth,
						borderHeight:borderHeight,
						title:topTitle,
						content:content,
						bottomCloseButton:bottomCloseButton
					});
				}
			},

			/**
			 * universalPopup is a style type for layeredPopup display
			 *
			 * @author Byung Kim
			 * @date 1/8/2008
			 */
			universalPopup : {
				constants : {
					LAYERED_POPUP_COMBINED_BORDER_WIDTH: 21, // left and right border combined widths
					LAYERED_POPUP_COMBINED_BORDER_HEIGHT: 59, // top and bottom border combined heights
					LAYERED_POPUP_MAIN_WIDTH_OFFSET:19 // close window button width
				},
				templates : {
					mainTemplate : new Template(
						'<div id="popupMain" class="universalLayeredPopup" style="width:#{popupWidth}px;">' +
							'<div class="topBorder clearfix">' +
								'<div class="pop-sprites topLeftCorner"></div>' +
								'<div id="layeredPopup#{topId}FrameTop" class=" pop-sprites topMiddle#{topClass}" style="width:#{topWidth}px;">#{title}</div>' +
								'<div class="topCloseButton">#{topCloseButton}</div>' +
								'<div class="pop-sprites topRightCorner"></div>' +
							'</div>' +
							'<div class="mainContent clearfix">' +
								'<div class="leftBar" style="height:#{borderHeight}px;"></div>' +
								'#{content}' +
								'<div class="rightBar" style="height:#{borderHeight}px;"></div>' +
							'</div>' +
							'<div class="bottom clearfix ">' +
								'<div class="pop-sprites bottomLeftCorner"></div>' +
								'<div class="pop-sprites bottomMiddle" style="width:#{innerContentWidth}px;"></div>' +
								'<div class="pop-sprites bottomRightCorner"></div>' +
							'</div>' +
						'</div>'
					)
				},

				/**
				 * getMarkup returns the markup for universalPopup styled popups
				 *
				 * @author Byung Kim
				 * @date 1/8/2008
				 */
				getMarkup:function(useIFrame, width, height, title, strContent, id, style, scrolling) {
					var constants = this.constants;
					var templates = this.templates;
					var commonTemplates = gidLib.layeredPopup.styles.commonTemplates;
					var topId = ""
					var topClass = "";
					var content = "";
					var topCloseButton = commonTemplates.closeAnchorTemplate.evaluate({text:commonTemplates.universalCloseButtonTemplate.evaluate({path:brandConst.UNIVERSAL_BUTTON_CONTENT_PATH,altText:resourceBundleValues.infoPopupsAltTextClose })});
					var topTitle = title;
					if (!topTitle || topTitle == "") topTitle = "&#160;";

					if (useIFrame) {
						topId = "NoDrag";
						content = commonTemplates.iFrameContentTemplate.evaluate({
							id:id,
							src:gidLib.addCurrentDomain(strContent),
							width:width,
							height:height,
							scrolling:scrolling
						});
					} else {
						topClass = " cursorMove";
						content = commonTemplates.markupContentTemplate.evaluate({
							id:id,
							content:strContent,
							width:width,
							height:height
						});
					}

					return templates.mainTemplate.evaluate({
						popupWidth:width + constants.LAYERED_POPUP_COMBINED_BORDER_WIDTH,
						topId:topId,
						topClass:topClass,
						topWidth:width-constants.LAYERED_POPUP_MAIN_WIDTH_OFFSET,
						innerContentWidth:width,
						borderHeight:height,
						title:topTitle,
						content:content,
						topCloseButton:topCloseButton
					});
				}
			},

			/**
			 * universalPanel is a style type for layeredPopup display
			 *
			 * @author Byung Kim
			 * @date 1/8/2008
			 */
			universalPanel : {
				constants : {
					PANEL_COMBINED_BORDER_WIDTH: 14, // left and right border combined widths
					PANEL_MAIN_WIDTH_OFFSET:27, // to calculate the main width from the content width
					PANEL_TITLE_WIDTH_OFFSET:50 // to calculate the title width
				},
				templates : {
					mainTemplate : new Template(
						'<div id="popupMain" class="universalPanel" style="width:#{mainWidth}px;">' +
							'<div class="row top clearfix" style="width:#{topBottomWidth}px;">' +
								'<div class="pop-sprites topLeft">&#160;</div>' +
								'<div class="pop-sprites topCenter" style="width:#{topBottomCenterWidth}px;">&#160;</div>' +
								'<div class="pop-sprites topRight">&#160;</div>' +
							'</div>' +
							'<div class="row">' +
								'<div class="leftCenter clearfix" style="width:#{leftCenterWidth}px;">' +
									'#{calloutLeft}' +
									'<div class="rightCenter" style="width:#{rightCenterWidth}px;#{offset}">' +
										'<div class="close" style="width:#{closeWidth}px;">' +
											'#{topBar}' +
											'<div class="panelContent" style="width:#{contentWidth}px;height:#{contentHeight}px;">#{content}</div>' +
										'</div>' +
									'</div>' +
								'</div>' +
							'</div>' +
							'<div class="row bottom clearfix" style="width:#{topBottomWidth}px;">' +
								'<div class="pop-sprites bottomLeft">&#160;</div>' +
								'<div class="pop-sprites bottomCenter" style="width:#{topBottomCenterWidth}px;">&#160;</div>' +
								'<div class="pop-sprites bottomRight">&#160;</div>' +
							'</div>' +
							'#{calloutBottom}' +
						'</div>'
					),

					panelCalloutBottomTemplate : new Template('<div class="pop-sprites row callout" style="#{offset}">&#160;</div>'),
					panelCalloutLeftTemplate : new Template('<div id="universalPanelLeftCallout" class="pop-sprites calloutLeft" style="#{offset}">&#160;</div>'),
					panelTitleTemplate : new Template('<div class="panelTitle" style="width:#{width}px;">#{title}</div>'),
					panelCloseButtonTemplate : new Template('<div class="closeButton">#{topCloseButton}</div>'),
					panelTopBarTemplate : new Template('<div class="clearfix">#{titleTemplate}#{buttonTemplate}</div>')
				},

				/**
				 * getMarkup returns the markup for universalPanel styled popups
				 *
				 * @author Byung Kim
				 * @date 1/8/2008
				 */
				getMarkup:function(useIFrame, width, height, title, strContent, id, style, scrolling) {
					var constants = this.constants;
					var templates = this.templates;
					var commonTemplates = gidLib.layeredPopup.styles.commonTemplates;
					var hasCloseButton = true;
					var callout = null;
					var calloutOffset = null;
					var content = "";
					var mainWidth = width+constants.PANEL_MAIN_WIDTH_OFFSET;
					if (style && style.hasCloseButton != undefined) hasCloseButton = style.hasCloseButton;
					if (style && style.callout) callout = style.callout;
					if (style && style.calloutOffset) calloutOffset = style.calloutOffset;
					var topTitle = "";
					var topCloseButton = "";
					var calloutLeft = "";
					var calloutBottom = "";
					var topBar = "";

					if (title) {
						topTitle = templates.panelTitleTemplate.evaluate({
							title:title,
							width:mainWidth-constants.PANEL_TITLE_WIDTH_OFFSET
						})
					}

					if (hasCloseButton) {
						topCloseButton = templates.panelCloseButtonTemplate.evaluate({
							topCloseButton:commonTemplates.closeAnchorTemplate.evaluate({
								text:commonTemplates.universalCloseButtonTemplate.evaluate({
									path:brandConst.UNIVERSAL_BUTTON_CONTENT_PATH, altText:resourceBundleValues.infoPopupsAltTextClose
								})
							})
						})
					}

					if (title || hasCloseButton) {
						topBar = templates.panelTopBarTemplate.evaluate({
							titleTemplate:topTitle,
							buttonTemplate:topCloseButton
						});
					}

					if (callout == "left") {
						calloutOffset = "top:"+calloutOffset+"px;"
						calloutLeft = templates.panelCalloutLeftTemplate.evaluate({offset:calloutOffset});
					} else if (callout == "bottom") {
						calloutOffset = "left:"+calloutOffset+"px;"
						calloutBottom = templates.panelCalloutBottomTemplate.evaluate({offset:calloutOffset});
					}

					if (useIFrame) {
						topId = "NoDrag";
						content = commonTemplates.iFrameContentTemplate.evaluate({
							id:id,
							src:gidLib.addCurrentDomain(strContent),
							width:width,
							height:height,
							scrolling:scrolling
						});
					} else {
						topClass = " cursorMove";
						content = commonTemplates.markupContentTemplate.evaluate({
							id:id,
							content:strContent,
							width:width,
							height:height
						});
					}

					return templates.mainTemplate.evaluate({
						mainWidth:mainWidth,
						topBottomWidth:mainWidth,
						topBottomCenterWidth:(mainWidth-constants.PANEL_COMBINED_BORDER_WIDTH),
						leftCenterWidth:mainWidth,
						rightCenterWidth:mainWidth,
						closeWidth:width,
						topBar:topBar,
						contentWidth:width,
						contentHeight:height,
						content:content,
						calloutLeft:calloutLeft,
						calloutBottom:calloutBottom
					});
				}
			}
		},

		/**
		 * openLayeredPopup opens a DHTML layer that looks like a popup.
		 * @param {object} argsGraph The JSON object with values for the popup
		 * 		str : The content of the layered Popup.  This can be a string of HTML, a URL to open, or a form to submit
		 * 		id : The id for the layer parent element
		 * 		width : The width of the popup
		 * 		height : The height of the popup
		 * 		title : The title to display in the fake titlebar
		 * 		left : The x coordinate to position the popup
		 * 		top : The y coordinate to position the popup
		 * 		calleeElement : The calling element for the event
		 *		isDraggable : Whether the popover is draggable by clicking on it
		 * 		style : A JSON object for the look and feel of the popup.
		 * 			name : which template to use (optional, default is the brandedPopup template)
		 * 			hasCloseButton : whether to display the close button for the universalPanel (optional, default is true)
		 * 			callout : used for displaying a callout for the universalPanel. values are 'left' or 'bottom' (optional, default is no callout)
		 * 			calloutOffset : used to offset the callout on the left or top depending on which is used.
		 *			isDraggable : Whether the poopup is draggagle (defaults to true)
		 *		scrolling : The scrolling attribute of the iframe popup (eg: yes no, auto)
		 * 		interstitial : JSON object -- when it exists, display as an interstitial (rest of the page greys out)
		 * 			color: hex value color of the interstitial (e.g. "#fff")
		 * 			opacity: opacity of the interstitial (e.g. "0.7" is 70%)
		 * 			buzz: whether to buzz the popup if a user clicks on the interstitial (e.g. true or false)
		 * 		effects : JSON object describing how to show and hide the layeredPopup.  Default is CSS visibility visible/hidden
		 * 			show : JSON object for showing the layered popup
		 * 				method : Scriptaculous Effect method
		 * 				args : JSON object of arguments for the method
		 * 			hide : JSON object for hiding the layered popup
		 * 				method : Scriptaculous Effect method
		 * 				args : JSON object of arguments for the method
		 *
		 *
		 * Modified 12/19/2007 Byung Kim - converted method arguments to JSON with legacy API support
		 * Modified 1/1/2008 Byung Kim - reorganized to accept different styles
		 * Modified 1/8/2008 Byung Kim - updated FORM submit implementation
		 * Modified 1/31/2008 Byung Kim - added interstitial option
		 * Modified 7/25/2008 Byung Kim - added effects option for showing / hiding layered popup
		 * Modified 7/31/2008 Byung Kim - added callee element arg for returning focus to the callee once the popup closes
		 * Modified 9/17/2010 Prabhakar Doraisamy - added scrolling arg to turn on or off the iframe scrolling (for IEs)
		 *
		 * TODO: refactor implementation to use only JSON
		 * TODO: implement a dynamic root element for the popups
		 *
		 * @author Byung Kim
		 * @date 10/24/2007
		 */
		openLayeredPopup:function(argsGraph) {
			var args = arguments;
			if (args.length > 1) {
				// FOR LEGACY SUPPORT ONLY.  New implementations should use JSON object graph for arguments
				var str = args[0];
				var id = args[1];
				var width = args[2];
				var height = args[3];
				var title = args[4];
				var left = args[5];
				var top = args[6];
			} else {
				var str = argsGraph.str;
				var id = argsGraph.id;
				var width = argsGraph.width;
				var height = argsGraph.height;
				var title = argsGraph.title;
				var left = argsGraph.left;
				var top = argsGraph.top;
				var scrolling = argsGraph.scrolling;
				var isDraggable = argsGraph.isDraggable;
				var style = argsGraph.style;
				var interstitial = argsGraph.interstitial;
				this.effects = argsGraph.effects;
				this.calleeElement = argsGraph.calleeElement;
			}
			var popup = $("popupContent");

			var layerStyle = this.styles[(style && style.name ? style.name : "brandedPopup")];
			var constants = layerStyle.constants;
			var useIFrame = false;
			var isForm = false;
			var strContent = "";
			var strIgnoredFormTypes = "submit,button,image,file,reset";
			this.popupId = id;

			popup.setStyle({visibility:"hidden",display:"block"});

			if (str.match(/^(\/|http|about)/)) {
				useIFrame = true;
				strContent = str;
				var cid = str.substr(str.indexOf('?')+1, str.length);
			} else {
				var targetElement = $(str);
				if (targetElement) {
					if (targetElement.tagName && targetElement.tagName.toLowerCase() == 'form') {
						useIFrame = true;
						isForm = true;
						strContent = "/gid/html/en/blank.html";
						targetElement.target = id + "PopupContent";
					} else {
						// Copy the innerHTML
						strContent = targetElement.innerHTML;
					}
				} else {
					// Copy the string literally
					strContent = str;
				}
			}
			popup.update(layerStyle.getMarkup(useIFrame,width,height,title,strContent,id,style,scrolling));
			if (!useIFrame && isDraggable) {
				gidLib.setButtonEvents("popupContent");
				this.dragger = new Draggable('popupContent',{handle:'layeredPopupFrameTop'});
			}
			this.hiddenDropDowns = gidLib.hideDropDownsUnderElement(popup);
			if (interstitial) this.openInterstitialDisplay(interstitial);
			this.setPosition(popup,left,top);
			this.launchLayeredPopup(id);
			if (isForm) targetElement.submit();
			return false;
		},

		/**
		 * setPosition sets the position of the layeredPopup
		 *
		 * Modified 7/23/2008 - Byung Kim : removed scrollTop, scrollLeft calculation on x,y coordinates.  values passed to openlayeredpopup should account for the scroll values
		 *
		 * @author Byung Kim
		 * @date 1/8/2008
		 */
		setPosition : function(popup,x,y) {
			var popupX = parseInt(x);
			var popupY = parseInt(y);
			var dimensions = popup.getDimensions();
			if (isNaN(popupX) || isNaN(popupY)) {
				var clientWidth = (document.documentElement && document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth);
				var clientHeight = (document.documentElement && document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight);
				var scrollLeft = (document.documentElement && document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
				var scrollTop = (document.documentElement && document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
				if (clientBrowser.isSafari) {
					/*
					TD 14933: Safari does not seem to properly support the document.documentElement.clientHeight -
					reporting a constant value of 1330.
					*/
					var clientHeight = window.innerHeight;
				}
				popupX = Math.max(0,clientWidth/2 - dimensions.width/2)+scrollLeft;
				popupY = Math.max(0,clientHeight/2 - dimensions.height/2)+scrollTop;
				// alert("clientBrowser.isSafari = " + clientBrowser.isSafari + "\rwindow.innerHeight = " + window.innerHeight + "\rdocument.documentElement.clientHeight = " + document.documentElement.clientHeight + "\r\rclientHeight = " + clientHeight + "\rclientWidth = " + clientWidth + "\r\rpopupX = " + popupX + "\rpopupY = " + popupY);
			}
			gidLib.setObjPosition(popup,popupX,popupY);
		},

		/**
		 * launchLayeredPopup reveals the layered popup
		 *
		 * @author Byung Kim
		 * @date 7/25/2008
		 */
		launchLayeredPopup : function(id) {
			var effects = this.effects;
			var popup = $("popupContent");

			var afterFinish = function(id) {
				setTimeout("gidLib.setFocus($('popupContent'))",100);
				this.isOpen = true;
			};

			if (effects && effects.show && effects.show.method) {
				popup.setStyle({display:"none",visibility:"visible"});
				var method = effects.show.method;
				var args = effects.show.args;
				var aF = {afterFinish:afterFinish.bind(this,id)};
				if (args) {
					Object.extend(args,aF);
					method(popup,args);
				} else {
					method(popup,aF);
				}
			} else {
				popup.setStyle({visibility:"visible",display:"block"});
				afterFinish(id);
			}
		},

		/**
		 * closeLayeredPopup closes the layered popup
		 *
		 * Modified 7/25/2008 Byung Kim - added effect
		 * Modified 7/31/2008 Byung Kim - added returning focus to the callee element
		 *
		 * @author Byung Kim
		 * @date 10/24/2007
		 */
		closeLayeredPopup:function() {
			var popup = $("popupContent");
			this.closeInterstitialDisplay();
			var afterFinish = function() {
				while(popup.hasChildNodes()) {
					popup.removeChild(popup.firstChild);
				}
				gidLib.showDropDowns(this.hiddenDropDowns);
				this.hiddenDropDowns = null;
				if (this.dragger) this.dragger.destroy();
				if (this.calleeElement) {
					gidLib.setFocus(this.calleeElement);
				}
				this.isOpen = false;
				this.popupId = "";
			};

			var effects = this.effects;
			if (effects && effects.hide && effects.hide.method) {
				var method = effects.hide.method;
				var args = effects.hide.args;
				var aF = {afterFinish:afterFinish.bind(this)};
				if (args) {
					Object.extend(args,aF);
					method(popup,args);
				} else {
					method(popup,aF);
				}
			} else {
				popup.setStyle({visibility:"hidden",display:"block"});
				afterFinish();
			}
			return false;
		},

		/**
		 * openInterstitialDisplay adds an interstitial layer behind the layeredPopup to grey out the page.
		 *
		 * @author Byung Kim
		 * @date 1/30/2008
		 */
		openInterstitialDisplay:function(args) {
			var color = (args.color ? args.color : "#fff");
			var opacity = (args.opacity ? args.opacity : "0.65");
			this.hasBuzz = (args.buzz != undefined ? args.buzz : true);
			var id = "layeredPopupInterstitial";
			var interstitial = $(id);
			if (!interstitial) {
				var interstitial = $(document.createElement("div"));
				interstitial.id = id;
				document.body.appendChild(interstitial);
				Event.observe(interstitial,"click",this.interstitialBuzzEffect.bind(this));
			}
			var clientWidth = (document.documentElement && document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth);
			var clientHeight = (document.documentElement && document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight);
			var scrollLeft = (document.documentElement && document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
			var scrollTop = (document.documentElement && document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
			var docW = clientWidth + scrollLeft;
			var docH = (clientBrowser.isIE ? document.body.offsetHeight : (document.documentElement ? document.documentElement.offsetHeight : document.body.offsetHeight));
			if (clientHeight > docH) docH = clientHeight;
			interstitial.setStyle({
				position:"absolute",
				top:"0px",
				left:"0px",
				width:docW+"px",
				height:docH+"px",
				display:"block",
				background:color,
				zIndex:"98",
				opacity:opacity,
				filter:"alpha(opacity=" + (opacity*100) + ")"
			});
		},

		/**
		 * closeInterstitialDisplay hides the interstitial layer
		 *
		 * @author Byung Kim
		 * @date 1/30/2008
		 */
		closeInterstitialDisplay:function() {
			var interstitial = $("layeredPopupInterstitial");
			if (interstitial) {
				interstitial.setStyle({display:"none"});
				Event.stopObserving(interstitial,"click",this.interstitialBuzzEffect.bind(this));
			}
		},

		/**
		 * interstitialBuzzEffect does the effect for the popup
		 *
		 * @author Byung Kim
		 * @date 1/31/2008
		 */
		interstitialBuzzEffect:function(hasBuzz) {
			if (this.hasBuzz) Effect.Shake("popupContent");
		}
	},

	/**
	 * rolloverBubble is a subclass of GidLib for all functionality related to rollover bubbles.
 	 * Usage Example:
 	 * 		<a href="#" onmouseover="gidLib.rolloverBubble.openBubble(event,{content:'Hello World!',align:'left',vposition:'top'});" onmouseout="gidLib.rolloverBubble.closeBubble();" onclick="return false;"><img .../></a>
 	 *
 	 * Note on accessibility:
 	 * 		The text that goes in the bubble should also be represented in the alt tag of the image or as a visually hidden element.
 	 *
	 * @base GidLib
	 *
	 * @author Byung Kim
	 * @date 12/19/2007
	 * @edited Keo
	 * @date 03/21/2008
	 * Note: Added the vertical positioning of the bubble's location
	 */
	 rolloverBubble:{
	 	hiddenDropDowns:null,
	 	targetElement:null,
	 	bubbleElement:null,
	 	calloutSide:"",
	 	calloutVert:"",

	 	constants:{
	 		CONTAINER_ID:"rolloverBubbleContainer",
	 		CONTENT_ID:"rolloverBubbleContent",
	 		LEFT_POSITION_OFFSET:(clientBrowser.isIE ? 4 : 12),
	 		RIGHT_POSITION_OFFSET:(clientBrowser.isIE ? 30 : 23),
	 		TOP_POSITION_OFFSET:2,
	 		BOTTOM_POSITION_OFFSET:20
	 	},

	 	templates:{
		 	mainTemplate : new Template(
		 		'<div class="bubbleCallout callout#{calloutTop}"></div>' +
		 		'<div class="bubbleTop clearfix">' +
		 			'<div class="bubble-sprites left"></div>' +
		 			'<div class="bubble-sprites center"></div>' +
		 			'<div class="bubble-sprites right"></div>' +
		 		'</div>' +
		 		'<div id=#{contentId} class="bubbleMiddle #{calloutTextAlign} clearfix">#{content}</div>' +
		 		'<div class="bubbleBottom clearfix">' +
		 			'<div class="bubble-sprites left"></div>' +
		 			'<div class="bubble-sprites center"></div>' +
		 			'<div class="bubble-sprites right"></div>' +
		 		'</div>' +
		 		'<div class="bubble-sprites bubbleCallout callout#{calloutBottom}"></div>'
		 	)
	 	},

	 	/**
	 	 * openBubble opens the rollover bubble. It dynamically creates the containing element if it doesn't exist in the DOM.
	 	 * The content of the rollover bubble is not refreshed and the position is not recalculated if the user rolls over the same bubble again.
	 	 * We use visibility instead of display because the offsetHeight needs to be calculated.  display:none returns an offsetHeight of 0px.
	 	 * @param {object} e The event object
	 	 * @param {object} args A JSON object containing:
	 	 * 		content : the string to display in the bubble
	 	 *		align : optional - determines where the callout appears on the bubble.  Values are "left" or "right".  "left" is the default value
	 	 *		vposition : optional - determines to appear above or belove the element  "top" is the default value
	 	 *
	 	 * @author Byung Kim
	 	 * @date 12/20/2007
	 	 * @edit Keo
	 	 * @date 03/21/2008
	 	 */
	 	openBubble:function(e,args) {
	 		var constants = this.constants;
	 		var containerId = constants.CONTAINER_ID;
	 		var contentId = constants.CONTENT_ID;
	 		var content = $(contentId);
	 		var container = $(containerId);
	 		if (!container) {
	 			container = $(document.createElement("div"));
	 			container.id = containerId;
	 			container.setStyle({
	 				visibility:"hidden",
	 				position:"absolute"
	 			});
	 			document.body.appendChild(container);
	 		}
	 		if (!content || (content && content.innerHTML != args.content) ) {
		 		this.targetElement = Event.element(e);
		 		this.calloutSide = (args.align ? args.align.capitalize() : "Left");
		 		var calloutSide = this.calloutSide;
		 		var calloutTextAlign = (args.textalign ? args.textalign.capitalize() : "Left");
		 		var calloutTop = "None";
		 		var calloutBottom = "Bottom"+calloutSide;
		 		this.calloutVert = (args.vposition ? args.vposition.capitalize() : "Top");
		 		var calloutVert = this.calloutVert;
	 			if(calloutVert == "Bottom"){
	 				calloutTop = "Top"+calloutSide;
	 				calloutBottom = "None";
	 			}
	 			container.update(this.templates.mainTemplate.evaluate({
	 				contentId:contentId,
	 				content:args.content,
	 				calloutTop:calloutTop,
	 				calloutBottom:calloutBottom,
	 				calloutTextAlign:calloutTextAlign
	 			}));
	 			this.bubbleElement = container;

	 			this.setBubblePosition();
	 		}
			this.hiddenDropDowns = gidLib.hideDropDownsUnderElement(container);
 			container.setStyle({visibility:"visible"});

 			Event.observe(window,"resize",this.setBubblePosition.bind(this));
	 	},

	 	/**
	 	 * setBubblePosition sets the position of the bubble
	 	 *
	 	 * @author Byung Kim
	 	 */
	 	setBubblePosition:function() {
	 		var position = Position.cumulativeOffset(this.targetElement);
	 		var constants = this.constants;
	 		var bubbleElement = this.bubbleElement;
 			gidLib.setObjPosition(bubbleElement,
 				position[0]-(this.calloutSide == "Left" ? constants.LEFT_POSITION_OFFSET : (bubbleElement.offsetWidth - constants.RIGHT_POSITION_OFFSET)),
 				this.calloutVert == "Top" ? position[1]-bubbleElement.offsetHeight-constants.TOP_POSITION_OFFSET : position[1]+constants.BOTTOM_POSITION_OFFSET
 			);
	 	},

	 	/**
	 	 * closeBubble closes the rollover bubble.  Note that it does not clear the markup contents incase the user rolls over the same bubble again.
	 	 *
	 	 * @author Byung Kim
	 	 * @date 12/20/2007
	 	 */
	 	closeBubble:function() {
	 		var container = $(this.constants.CONTAINER_ID);
 			container.setStyle({visibility:"hidden"});
			gidLib.showDropDowns(this.hiddenDropDowns);
			this.hiddenDropDowns = null;
	 	}
	 },

	 inProgressIndicator : {
		 hiddenDropDowns : null,
		 args : null,

		 setPosition : function() {
		 	var args = gidLib.inProgressIndicator.args;
			var requestInProgressMaskNode = $("gidLibRequestInProgressMask");

			if (args.isFullScreen == true) {
				var maskNodeX = 0;
				var maskNodeY = 0;
				var maskNodeWidth = (document.body.clientWidth ? document.body.clientWidth : window.innerWidth);
				var maskNodeHeight = (document.body.clientHeight ? document.body.clientHeight : window.innerHeight);
			} else {
				var targetElement = $(args.targetElement);
				var targetElementDimensions = targetElement.getDimensions();
				var targetElementPosition = Position.cumulativeOffset(targetElement);
				var maskNodeX = targetElementPosition[0];
				var maskNodeY = targetElementPosition[1];
				var maskNodeWidth = targetElementDimensions.width;
				var maskNodeHeight = targetElementDimensions.height;
			}

			requestInProgressMaskNode.style.width = maskNodeWidth + "px";
			requestInProgressMaskNode.style.height = maskNodeHeight + "px";
			requestInProgressMaskNode.style.left = maskNodeX + "px";
			requestInProgressMaskNode.style.top = maskNodeY + "px";

			if (args.messageText) {
				var yOffset = ((maskNodeHeight / 2) + (args.messageYOffset ? Number(args.messageYOffset) : 0)) + "px";
				var xOffset = (args.messageXOffset ? args.messageXOffset : 0) + "px";
				$("gidLibRequestInProgressMaskMessage").setStyle({
					"position" : "relative",
					"top" :  yOffset,
					"left" : xOffset
				});
			}
		},

		createRequestInProgressNode : function() {
			var requestInProgressMaskNode = $("gidLibRequestInProgressMask");
			var args = gidLib.inProgressIndicator.args;
			if (!requestInProgressMaskNode) {
				var element = $(document.createElement("div"));
				element.id = "gidLibRequestInProgressMask";
				document.body.appendChild(element);
				requestInProgressMaskNode = $("gidLibRequestInProgressMask");
			}

			requestInProgressMaskNode.setStyle({
				"background" : "white url(/gid/assets/common/en_US/loadIndicator24.gif) no-repeat scroll center center",
				"height" : "100px",
				"width" : "250px",
				"left" : "0px",
				"top" : "0px",
				"opacity" : (args.opacity ? args.opacity : 0.7),
				"position" : "absolute",
				"zIndex" : 99,
				"textAlign" : "center"
			});

			return requestInProgressMaskNode;
		},

		setInProgressNodeMessage : function() {
			var requestInProgressMaskNode = $("gidLibRequestInProgressMask");
			var args = gidLib.inProgressIndicator.args;
			var message = "";
			if (args.messageText) {
				message = '<div id="gidLibRequestInProgressMaskMessage" style="' + (args.messageStyle ? args.messageStyle : "") + '">' + args.messageText + '</div>';
			}
			requestInProgressMaskNode.update(message);
		},

		/**
		 * parameters for args are as follows:
		 *   'isFullScreen'	 : whether to place the in progress indicator over the entire screen.  If omitted, it assumes we are targetting an element
		 *   'targetElement' : the element to place the in progress indicator over.  Not used when isFullScreen is true.
		 *   'messageText'       : adds a message below the spinny graphic.  Accepts HTML for custom styling.
		 *   'messageYOffset' : offsets the position of the message vertically (by default, it is centered)
		 *   'messageXOffset' : offsets the position of the message horizontally (by default, it is centered)
		 *   'messageStyle'  : additional CSS styling for the message
		 *   'opacity'       : sets the opacity of the interstitial
		 */
		open : function(args) {
			this.args = args;
			var requestInProgressMaskNode = this.createRequestInProgressNode();
			this.setInProgressNodeMessage();
			this.setPosition();
			requestInProgressMaskNode.setStyle({"display":"block"});
			this.hiddenDropDownNodes = gidLib.hideDropDownsUnderElement(requestInProgressMaskNode); // IE 6x layer bug - select inputs show through layers.
			Event.observe(window,"resize",gidLib.inProgressIndicator.setPosition);
	 	},
	 	close : function() {
	 		var requestInProgressMaskNode = $("gidLibRequestInProgressMask");
	 		if (requestInProgressMaskNode) {
		 		requestInProgressMaskNode.setStyle({"display":"none"});
		 		gidLib.showDropDowns(this.hiddenDropDownNodes);  // IE 6x layer bug - select inputs show through layers.
				Event.stopObserving(window,"resize",gidLib.inProgressIndicator.setPosition);
	 		}
	 	}
	 },

	/**
	 * reporting is a subclass of GidLib for functionality specifically for reporting purposes
	 * @base GidLib
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	reporting:{

		/**
		 * getContentItemIds returns contentItemIds array.  used by sOmni.js
		 * @return an array of all contentItemContainer elements in the DOM
		 *
		 * @author Byung Kim
		 * @date 10/24/2007
		 */
		getContentItemIds:function() {
			var elements = $$("[id^=contentItemContainer]");
			var contentItemIds = new Array();
			elementsIterator = function(element) {
				contentItemIds.push(element.className);
			}
			elements.each(elementsIterator);
			return contentItemIds;
		},

		/**
		 * getBrandSite returns the brand site object given the brandCode.  This reads from gidBrandSiteConstruct so it must exist for this to work.
		 * This should be used when trying to retrieve the brandSite object since it performs the check to see if gidBrandSiteConstruct exists or not.
		 * @param brandCode
		 * @return the brand object
		 *
		 * @author Byung Kim
		 * @date 1/29/2008
		 */
		getBrandSite:function(brandCode) {
			var brandSite = null;
			if (window["gidBrandSiteConstruct"] && brandCode) {
				if (gidBrandSiteConstruct.gidBrandSites) brandSite = gidBrandSiteConstruct.gidBrandSites[brandCode];
			}
			return brandSite;
		}
	}
}
var gidLib = new GidLib();


/**
 * InlineBag is a class for functionality related to the inline shopping bag.
 * Instantiated as inlineBag.
 * @constructor
 *
 * @author Byung Kim
 * @date 10/24/2007
 */
var InlineBag = Class.create();
InlineBag.prototype = {
	isOpen:false,
	doOpenBag:false,
	isAnimating:false,
	hasMarketing:false,
	closeDelay:60,
	marketingDelay:0.25,
	headerElementId:"inlineBagHeader",
	headerOpenElementId:"inlineBagHeaderOpen",
	contentClipElementId:"inlineBagClip",
	contentElementId:"inlineBagContent",
	marketingClipElementId:"inlineBagMarketingClip",
	marketingContentElementId:"inlineBagMarketingContent",
	priceElementId:"inlineBagTopPriceLayer",
	priceOpenElementId:"inlineBagTopPriceLayerOpen",
	universalBarElementId:"universalBar",
	contentDataElementId:brandConst.inlineBag.CONTENT_DATA_ELEMENT_ID,
	marketingDataElementId:brandConst.inlineBag.MARKETING_DATA_ELEMENT_ID,
	contentXOffset:brandConst.inlineBag.CONTENT_X_OFFSET,
	contentYOffset:brandConst.inlineBag.CONTENT_Y_OFFSET,
	marketingContentXOffset:brandConst.inlineBag.MARKETING_CONTENT_X_OFFSET,
	marketingContentYOffset:brandConst.inlineBag.MARKETING_CONTENT_Y_OFFSET,
	hiddenDropDowns:[],
	labelColor:brandProperties.inLineBag.labelColor,
	labelSize:brandProperties.inLineBag.labelSize,
	labelQuantity:brandProperties.inLineBag.labelQuantity,
	labelPrice:brandProperties.inLineBag.labelPrice,
	itemInSingular:brandProperties.inLineBag.itemInSingular,
	itemInPlural:brandProperties.inLineBag.itemInPlural,
	lineItemBag:brandProperties.inLineBag.lineItemBag,
	addedTo:brandProperties.inLineBag.addedTo,
	yourBag:brandProperties.inLineBag.yourBag,

	// Holds the data, controller, etc for inlineBag ajax requests
	model:{
	    data:{}
	},
	controller:{
	},
	constants:{
	},

	/**
	 * initialize inlineBag
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	initialize:function() {},

	setWindowResizeHandler:function() {
		if (objInlineBag.isOpen) {
		    objInlineBag.positionInlineBag()

		    if (objInlineBag.hasMarketing) {
			    objInlineBag.positionInlineBagMarketing();
			}
		}
	},

	/**
	 * initializeElements sets pointer references to DOM elements
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	initializeElements:function() {
		this.headerBlock = $(this.headerElementId);
		this.headerBlockOpen = $(this.headerOpenElementId);
		this.inlineBagWrapper = $(this.contentClipElementId);
		this.inlineBagContent = $(this.contentElementId);
	},

	/**
	 * positionInlineBag positions the inline bag display
	 *
	 * Modified 3/17/09 -- subtract the universalbar Y position to fix inlinebag positioning in IE when the preview bar is enabled
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	positionInlineBag:function() {
		if (this.inlineBagWrapper.style && this.inlineBagContent.style) {
			var position = Position.cumulativeOffset($(this.headerBlock));

            var xValue = position[0];
			var yValue = position[1];

			// Need to use the open header for position if the bag is already open
			if (this.isOpen) {
    			position = Position.cumulativeOffset($(this.headerBlockOpen));

	        	xValue = position[0];
			    yValue = position[1] + this.headerBlockOpen.offsetHeight;
	        } else {
    	        yValue += this.headerBlock.offsetHeight;
	        }

			var universalBarPositionY = 0;
	        if ($(this.universalBarElementId) != null && clientBrowser.isIE) {
		        universalBarPositionY = Position.cumulativeOffset($(this.universalBarElementId))[1];
	        }

			var x = xValue + "px";
			var y = (yValue-universalBarPositionY) + "px";
			gidLib.setObjPosition(this.inlineBagWrapper,x,y);
		} else {
			var inlineBag = this;
			new PeriodicalExecuter(function(pe) {
				inlineBag.positionInlineBag();
				pe.stop();
			},0.1);
		}
	},

	/**
	 * positionInlineBagMarketing positions the marketing portion of the inline bag display
	 *
	 * Modified 3/19/09 -- subtract the universalbar Y position to fix inlinebag positioning in IE when the preview bar is enabled
	 *
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	positionInlineBagMarketing:function() {
		var position = Position.cumulativeOffset(this.inlineBagContent);

		var xValue = position[0];
		var yValue = position[1];

		var universalBarPositionY = 0;
        if ($(this.universalBarElementId) != null && clientBrowser.isIE) {
	        universalBarPositionY = Position.cumulativeOffset($(this.universalBarElementId))[1];
        }

		var x = xValue + "px";
		var y = (yValue + this.inlineBagContent.offsetHeight - universalBarPositionY) + "px";
		gidLib.setObjPosition($(this.marketingClipElementId),x,y);
	},

	/**
	 * openInlineBag opens the inline bag display
	 * @param {boolean} isAuto ?? not sure what this is for
	 *
	 * Modified 12/04/2007 Byung Kim - added reference to openInlineBagAfterFinish
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	openInlineBag:function(isAuto) {
		if ((!this.isOpen || $(this.contentDataElementId).innerHTML != "") && (!this.isAnimating || isAuto)) {
			this.isAnimating = true;
			this.headerBlock.hide();
			this.headerBlockOpen.show();

            // Explicitly hide the search section under the inline bag
    		//if (IS_SISTERSITE_TABS_ON == "true") {
            var searchInputs = $('topSearchInputs');
            if(searchInputs) { searchInputs.hide(); }
            //}

            //     Handle dynamic repositioning if browser resized
    		Event.observe(
	    		window,
		    	"resize",
			    this.setWindowResizeHandler
		    );

			Effect.SlideDown(inlineBag.contentElementId,
				{
					duration:0.5,
					afterFinish:this.openInlineBagAfterFinish.bind(this)
				}
			);
		}
 	},

	/**
	 * openInlineBagAfterFinish executes after the inline bag opening animation finishes
	 *
	 * @author Byung Kim
	 * @date 12/04/2007
	 */
 	openInlineBagAfterFinish:function() {
		this.hiddenDropDowns = gidLib.hideDropDownsUnderElement(this.inlineBagWrapper);

		if (this.hasMarketing) {
			this.positionInlineBagMarketing();
			var inlineBag = this;
			new PeriodicalExecuter(function(pe) {
				inlineBag.openInlineBagMarketing();
				pe.stop();
			},this.marketingDelay);
		} else {
			this.openAfterFinish();
		}
 	},

 	/**
 	 * openInlineBagMarketing opens the marketing message portion of the inline bag display
 	 *
	 * Modified 12/04/2007 Byung Kim - added bind() to openAfterFinish

	 * 	 * @author Byung Kim
	 * @date 10/24/2007
 	 */
	openInlineBagMarketing:function() {
		Effect.SlideDown(inlineBag.marketingContentElementId,
			{
				duration:0.5,
				afterFinish:this.openAfterFinish.bind(this)
			}
		);
	},

	/**
	 * openAfterFinish is code executed after the completion of the opening effect
	 *
	 * Modified 12/04/2007 Byung Kim - replaced parent references with "this"
	 * Modified 12/10/2007 Byung Kim - wrapped setFocusToInlineBag call with a PE to fix an IE problem where the inline bag will blank out

	 * 	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	openAfterFinish:function() {
		this.isOpen = true;
		this.isAnimating = false;
		var inlineBag = this;
		new PeriodicalExecuter(function(pe) {
			inlineBag.closeInlineBag();
			pe.stop();
		},this.closeDelay);
		Event.observe(document,"mousedown",this.checkInlineBag.bind(this));
		new PeriodicalExecuter(function(pe) {
			inlineBag.setFocusToInlineBag();
			pe.stop();
		},0.1);
	},

	/**
	 * closeInlineBag closes the inline bag
	 * @param {boolean} isAuto ?? not sure what this is for
	 *
	 * Modified 12/04/2007 Byung Kim - added reference to closeInlineBagAfterFinish
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	closeInlineBag:function(isAuto) {
		if (this.isOpen && (!this.isAnimating || isAuto)) {
			this.isAnimating = true;
			if (this.hasMarketing) {
				this.closeInlineBagMarketing();
			} else {
				Effect.SlideUp(inlineBag.contentElementId,
					{
						duration:0.5,
						afterFinish:this.closeInlineBagAfterFinish.bind(this)
					}
				);
			}
		}
	},

	/**
	 * closeInlineBagAfterFinish executes after the inline bag closing animation finishes
	 *
	 * @author Byung Kim
	 * @date 12/04/2007
	 */
	closeInlineBagAfterFinish:function() {
		this.isOpen = false;
		this.isAnimating = false;
		this.headerBlock.show();
		this.headerBlockOpen.hide();

        // Explicitly redisplay search
        //if (IS_SISTERSITE_TABS_ON == "true") {
        var searchInputs = $('topSearchInputs');
        if(searchInputs) { $('topSearchInputs').show(); }
        //}

		gidLib.showDropDowns(this.hiddenDropDowns);

		Event.stopObserving(document,"resize",this.checkInlineBag.bind(this));

		Event.stopObserving(document,"mousedown",this.checkInlineBag.bind(this));
		this.removeFocusFromInlineBag();
	},

	/**
	 * closeInlineBagMarketing closes the marketing portion of the inline bag display
	 *
	 * Modified 12/04/2007 Byung Kim - added reference to closeInlineBagMarketingAfterFinish
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	closeInlineBagMarketing:function() {
		Effect.SlideUp(inlineBag.marketingContentElementId,
			{
				duration:0.5,
				afterFinish:this.closeInlineBagMarketingAfterFinish.bind(this)
			}
		);
	},

	/**
	 * closeInlineBagMarketingAfterFinish executes after the inline bag marketing closing animation finishes
	 *
	 * @author Byung Kim
	 * @date 12/04/2007
	 */
	closeInlineBagMarketingAfterFinish:function() {
		this.hasMarketing = false;
		var inlineBag = this;
		new PeriodicalExecuter(function(pe) {
			inlineBag.closeInlineBag(true);
			pe.stop();
		},this.marketingDelay);
	},

	/**
	 * setInlineShoppingBagData updates the DOM with the response from the inlineBag data call.
	 * All markup passed to this method is generated within the data call.
	 * @param {string} subTotal The markup for the subTotal
	 * @param {string} markup The markup for the inlinebag display
	 * @param {string} openTotal The markup for the total when the inlinebag is open
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setInlineShoppingBagData:function(subTotal,markup,openTotal) {
		this.setSubTotal(subTotal,openTotal);

		/*
		 *	Hacked resetting of "absolute" position for inside bag items.
		 *  This is a hacky workaround for a problem with IE6 in which the
		 *  absolutely styled elements were not having their height and width
		 *  set initially
		 */
		Element.setStyle(this.contentClipElementId,{position: 'absolute'});
		Element.setStyle(this.marketingClipElementId,{position: 'absolute'});

		if (markup) $(this.contentDataElementId).update(markup);
		this.positionInlineBag();
	},

	/**
	 * setInlineShoppingBagMarketing updates the marketing message portion of the inline bag display from the data call
	 * @param {string} markup The markup for the marketing message.  The markup is generated in the data call.
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setInlineShoppingBagMarketing:function(markup) {
		if (markup) $(this.marketingDataElementId).update(markup);
		this.hasMarketing = true;
	},

	/**
	 * setSubTotal sets the subTotal display in the close and open state of the inline bag header
	 * @param {string} subTotal The closed header markup
	 * @param {string} openTotal the open header markup
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setSubTotal:function(subTotal,openTotal) {
		$(this.priceElementId).update(subTotal);
		if (openTotal) $(this.priceOpenElementId).update(openTotal);
	},

	/**
	 * checkInlineBag checks if the inlinebag is open and closes it.  This is set as a document.mousedown event when the inline bag is open.
	 * This event is removed when the inline bag is closed.
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	checkInlineBag:function() {
		if (this.isOpen) {
			var inlineBag = this;
			new PeriodicalExecuter(function(pe) {
				inlineBag.closeInlineBag();
				pe.stop();
			},0.5);
		}
	},

	/**
	 * Set focus to inline bag for accessibilty story #216
	 *
	 * Modified 12/04/2007 Byung Kim - added local variable and updated references
	 * Modified 2/2/08 Amy Tang - changed focus to inlineBagItemMessage for screen reader
	 *
	 * @author Hillel Familant
	 * @date 12/1/2007
	 */
	setFocusToInlineBag:function() {
		gidLib.setFocus($('inlineBagItemMessageItem'));
	},

	/**
	 * Set focus to inline bag Item Message Item for screenreader story #747
	 *
	 *
	 * @author Amy Tang
	 * @date 2/2/2009
	 */
	setInLineBagMessage:function(message){
		$('inlineBagItemMessageItem').title = message;
	},


	/**
	 * Remove focus from inline bag to one of the main containers:
	 * mainContainer, bodyContainer, etc.. for accessibilty story #216
	 *
	 * Modified 12/04/2007 Byung Kim - added local variables and updated references
	 *
	 * @author Hillel Familant
	 * @date 12/1/2007
	 */
	removeFocusFromInlineBag:function() {
		var mainContent = $('mainContent');
		if (mainContent) gidLib.setFocus(mainContent);
	}
}
var inlineBag = new InlineBag();


/**
 * Site Navigation is a class for tools related to navigating the site.
 * Instantiated as siteNavigation.
 *
 * @constructor
 * @author Andrew Southwick
 * @date 10/24/2007
 */
var SiteNavigation = Class.create();
SiteNavigation.prototype = {
	shoppingBagUrl:"/buy/shopping_bag.do",
	signInUrlTemplate:new Template("#{url}?context=nav&targetURL=#{targetUrl}"),
	signOutUrlTemplate:new Template("#{url}?returnURL=#{returnUrl}&targetURL=#{targetUrl}"),
	joinEmailUrlTemplate:new Template("/profile/join_email_list_confirm.do?targetURL=#{returnUrl}&src=#{src}#{email}"),
	relativeUrl:(top != self ? "/browse/home.do" : window.location.pathname + window.location.search + window.location.hash),

	/**
	 * initialize SiteNavigation
	 *
	 * @author Andrew Southwick
	 * @date 10/24/2007
	 */
	initialize:function() {
		this.initializeSignInUrls();
	},

	/**
	 * initializeSignInUrls initializes the signIn Urls for profile
	 *
	 * @author Byung Kim
	 * @date 11/5/2007
	 */
	initializeSignInUrls:function() {

	    this.relativeUrl = this.relativeUrl.replace("sem=true","");
		this.signInReturnUrl = escape(this.relativeUrl);
		this.signInTargetUrl = escape(this.relativeUrl);
		this.signInShoppingUrl = escape(this.relativeUrl);
	},

	/**
	 * onLoadHandler declares SiteNavigation related methods to be called onload of the page.
	 *
	 * @author Byung Kim
	 * @date 10/30/2007
	 */
	onLoadHandler:function() {
		this.setPreviousPageUrl();
		this.footer.setFooterImages();
	},

	/**
	 * getAccessibleCheckout takes a user to the more accessible checkout path.  To be removed with the 4.2 release
	 * @deprecated
	 *
	 * @author Andrew Southwick
	 * @date 10/24/2007
	 */
	getAccessibleCheckout:function() {
		if (window.location.pathname == this.shoppingBagUrl) {
			if (checkout) {
				if (shoppingBag.stateMonitor.isBuyActive == true) {
					checkout();
				}
			}
		}
		return false;
	},

	/**
	 * goToShoppingBag sends the user to the shopping bag page.  This is currently used in the TopNav.
	 * Only use when an anchor tag cannot be used.  Try to write out the URL as much as possible for SEO & accessibility reasons.
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	goToShoppingBag:function() {
		if (location.pathname != this.shoppingBagUrl) {
			location.href = this.shoppingBagUrl;
		}
	},

	/**
	 * goSignIn takes the user to the sign in page
	 * @param {string} signInHref The url for the sign in page
	 * @return false for cancelling the href or form submit
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	goSignIn:function(signInHref) {
		parent.location.href = this.signInUrlTemplate.evaluate({
	    	url:signInHref,
	    	targetUrl:this.signInTargetUrl
	    });
		return false;
	},

	/**
	 * goSignOut takes the user to the sign out page
	 * @param {string} signOutHref The url for the signout page
	 * @return false for cancelling the href or form submit
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	goSignOut:function(signOutHref) {
		parent.location.href = this.signOutUrlTemplate.evaluate({
			url:signOutHref,
			returnUrl:this.signInReturnUrl,
			targetUrl:this.signInTargetUrl
		});
		return false;
	},

	/**
	 * showShipingOptInOverlay shows the overlay shipping country and currency selection
	 * @return false for cancelling the href or form submit
	 *
	 * @author Filipe Sabella
	 * @date 04/08/2010
	 */
	showShippingOptInOverlay: function() {
		function getLeftMargin() {
			return ((document.body.clientWidth ? document.body.clientWidth : window.innerWidth) / 2) - (width / 2);
		}

		var width = 1016;
		var top = 45;
	    var height = 664;

		new Ajax.Request('/profile/internationalShippingOptInList.do', {
			onComplete: function(response) {
				var currentPopUp = $('internationalShippingPopUp');
				if (currentPopUp) currentPopUp.remove();

				// we were facing some serious ie 6 css loading issues. this is the only place
				// to load the css in a way that works every time without adding the css
				// itself to all pages
				$$('head')[0].appendChild(new Element('link', {
					type : 'text/css',
					rel  : 'stylesheet',
					href : '/gid/css/profile/internationalShipping/en/internationalShipping.css',
					media: 'screen'
				}));

				var style = 'top:' + top + 'px;left:' + getLeftMargin() + 'px;width:' + width + 'px;height: ' + height + 'px';
				var div = new Element('div', {'id': 'internationalShippingPopUp', 'style': style})
							.update(response.responseText);

				document.body.appendChild(div);

				window.onresize = function () {
					div.style.left = getLeftMargin() + 'px';
				};
			}
		});

		return false;
	},
	/**
	 * setCurrentShippingToCountry sets the current selected shipping country on the header
	 * @param countryCode
	 * @param countryName to show on the alt text of the flag's icon
	 * @return false for cancelling the href or form submit
	 *
	 * @author Filipe Sabella
	 * @date 04/12/2010
	 */
	setCurrentShippingToCountry: function(countryCode, countryName) {
		var shippingToCountryFlag = $('shippingToCountryFlag');
		var src = shippingToCountryFlag.src;
		src = src.substring(0, src.lastIndexOf('/') + 1) + countryCode + '.gif';
		shippingToCountryFlag.src = src;

		shippingToCountryFlag.setAttribute('title', countryName);
	},
	/**
	 * goJoinEmail opens the join email url
	 * @param {string} ref The referring content item element id for contentItemLink
	 * @param {string} srcValue A value to pass to the popup url
	 * @param {string} emailAddress A email address value
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	goJoinEmail:function(ref,srcValue,emailAddress) {
			var optionalEmail = (emailAddress != null && emailAddress != "" ? "&emailAddress=" + emailAddress : "");
			var strGoURL = this.joinEmailUrlTemplate.evaluate({
				returnUrl:this.signInReturnUrl,
				src:srcValue,
				email:optionalEmail
			});
			gidLib.contentItemLink(ref,strGoURL);
	},

	/**
	 * setPreviousPageUrl creates a URL for the current page to be set in a cookie
	 * for use to navigate back to the page.  This is done ONLY if the page is one of
	 * the following:
	 *
	 *	home
	 *	division or subdivision
	 *	category
	 *	outfit
	 *
	 * 10/30/2007 Byung Kim - moved method to SiteNavigation Class.
	 *
	 * @author Aaron Liebling
	 * @date 10/25/2007
	 */
	setPreviousPageUrl:function() {
		var path = location.pathname;

		if (/^\/$/.exec(path) ||
			/\/browse\/home\.do/.exec(path) ||
			/division\.do/.exec(path) ||
			/subDivision\.do/.exec(path) ||
			/category.*\.do/.exec(path) ||
			/outfit\.do/.exec(path)) {
			var url = path + location.search;
			gidLib.setCookieVar("globalSession","previousPageUrl",url);
		}
	},


	/**
	 * footer is a subclass of SiteNavigation for footer related functionality.
	 * @base SiteNavigation
	 *
	 * @author Byung Kim
	 * @date 10/25/2007
	 */
	footer:{

		/**
		 * setFooterImages sets properties for footer image swapping
		 * Modified: Byung Kim 11/5/07 -- moved footer image definition from brandFunctions to brandConstants.  Now just read if the object exists in brandConst and extend properties.
		 *
		 * @author Byung Kim
		 * @date 10/25/2007
		 */
		setFooterImages:function() {
			if (brandConst.footer && brandConst.footer.IMAGES) Object.extend(this,brandConst.footer.IMAGES);
		}
	},

	/**
	 * topNav is a subclass of SiteNavigation for TopNav related functionality.
	 * @base SiteNavigation
	 *
	 * @author Byung Kim
	 * @date 10/31/2007
	 */
	topNav: {
		activeDivision:"",
		divisionTimer:0,
		subDivisionTimer:0,

		/**
		 * divisionsOver handles the mouseover event for Division Navigation.
		 * This is to be extended in brandFunctions.js
		 *
		 * @author Byung Kim
		 * @date 10/31/2007
		 */
		divisionOver:function() {},

		/**
		 * divisionsOut handles the mouseout event for Division Navigation.
		 * This is to be extended in brandFunctions.js
		 *
		 * @author Byung Kim
		 * @date 10/31/2007
		 */
		divisionOut:function() {},

		/**
		 * subDivisionOver handles the mouseover event for SubDivision Navigation.
		 * This is to be extended in brandFunctions.js
		 *
		 * @author Byung Kim
		 * @date 10/31/2007
		 */
		subDivisionOver:function() {},

		/**
		 * subDivisionOut handles the mouseout event for SubDivision Navigation.
		 * This is to be extended in brandFunctions.js
		 *
		 * @author Byung Kim
		 * @date 10/31/2007
		 */
		subDivisionOut:function() {},

		/**
		 * subDivisionsOver handles the mouseover event for the whole SubDivision Navigation Menu.
		 * This is to be extended in brandFunctions.js
		 *
		 * @author Byung Kim
		 * @date 10/31/2007
		 */
		subDivisionsOver:function() {},

		/**
		 * subDivisionsOut handles the mouseout event for the whole SubDivision Navigation Menu.
		 * This is to be extended in brandFunctions.js
		 *
		 * @author Byung Kim
		 * @date 10/31/2007
		 */
		subDivisionsOut:function() {},

		/**
		 * hideSubDivisions hides the subdivision navigation menu.
		 * This is to be extended in brandFunctions.js
		 *
		 * @author Byung Kim
		 * @date 10/31/2007
		 */
		hideSubDivisions:function() {}
	},

	/**
	 * searchBox is a subclass of SiteNavigation for functionality related to the site search form
	 * @base SiteNavigation
	 */
	searchBox: {
		hasSearchText:false,

		/**
		 * checkSearchText
		 * once you are on a search page the hasSearchText variable will aways be true - so check the default text explicitly
		 * on clearing the field - also get rid of any error class on the field - so when the user types, it is standard text
		 *
		 */
		checkSearchText:function(objFormField) {
			if (objFormField.value == this.strDefaultText) {
				if (objFormField.className != "searchTextStandard") {
					objFormField.className = "searchTextStandard";
				}
				objFormField.value = "";
			}
		},

		/**
		 * checkInput checks that the user didn't click go with an invalid search term (blank or the default text)
		 *
		 */
		checkInput:function (objForm) {
			//trim whitespace first
			var searchTextInput = objForm.searchText.value.trim();

			if ((searchTextInput == "") || (searchTextInput == this.strDefaultText)) {
				objForm.searchText.value = this.strDefaultText;
				objForm.searchText.className = "searchTextError";
				return false;
			} else {
				/**
 				* Modified:  Keo 07/28/08 - Added wrapper for G to H
 				*/
				if(!(reportingService||{}).isActive){
					omni.objSearchProcessor.setKeyword(objForm.searchText.value, objForm.searchDivName.options[objForm.searchDivName.selectedIndex].text, omni.strCurrentPageName);
				} else {
					var searchAppManager = reportingService.controller.appManagers.searchAppManager;
					var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
					var text = objForm && objForm.searchDivName ? objForm.searchDivName.options[objForm.searchDivName.selectedIndex].text : '';
					searchAppManager.setKeyword(objForm.searchText.value, text, commonModel.commonCurrentPageName);
				}
				return true;
			}
		}
	},

	/**
	 * sideNav is a subclass of SiteNavigation for functionality related to the sidenav.
	 *
	 * TODO: cleanup
	 */
	sideNav: {
		rootElement:$("sideNavCategories"),
		strSelectedHeaderNodeId:"5003",
		strUserExpandedHeaderNodeId:"",
		objCategoryListElement:null,
		objCategoryNavElement:null,

		setCategoryGroupDisplay:function(strCategoryGroupId, strElementExtension) {
			var strExtension = "";
			if (strElementExtension) strExtension = strElementExtension;
			if (strCategoryGroupId != this.strSelectedHeaderNodeId) {
				this.setPreviousCategoryElements(strElementExtension);
				this.setCategoryElements(strCategoryGroupId, strExtension);
				if (this.strUserExpandedHeaderNodeId == "") {
					this.setDisplay(this.objCategoryListElement, this.objCategoryNavElement, true, strElementExtension);
					this.strUserExpandedHeaderNodeId = strCategoryGroupId;
				}
				else if (strCategoryGroupId == this.strUserExpandedHeaderNodeId) {
					// User has expanded a node in the navigation, and now wants to close the same node.
					// setPreviousCategoryElements()
					this.setDisplay(this.objPreviousCategoryListElement, this.objPreviousCategoryNavElement, false, strElementExtension);
					this.strUserExpandedHeaderNodeId = "";
				}
				else if (strCategoryGroupId != this.strUserExpandedHeaderNodeId) {
					// User has expanded a node in the navigation, need to close this node, then expand their new selected node.
					// setPreviousCategoryElements()
					this.setDisplay(this.objPreviousCategoryListElement, this.objPreviousCategoryNavElement, false, this.strPreviousElementExtension);
					this.setDisplay(this.objCategoryListElement, this.objCategoryNavElement, true, strElementExtension);
					this.strUserExpandedHeaderNodeId = strCategoryGroupId;
				}
			}
		},

		setCategoryElements:function(strCategoryGroupId, strExtension) {
			this.objCategoryListElement = $("categoryList" + strCategoryGroupId + strExtension);
			this.objCategoryNavElement = $("categoryNav" + strCategoryGroupId + strExtension);
		},

		setPreviousCategoryElements:function(strElementExtension) {
			this.objPreviousCategoryListElement = this.objCategoryListElement;
			this.objPreviousCategoryNavElement = this.objCategoryNavElement;

			this.strPreviousElementExtension = strElementExtension;
		},

		setDisplay:function(objCategoryListElement, objCategoryNavElement, isVisible, strElementExtension) {
			// alert("inner setDisplay method running");
			if (objCategoryListElement && objCategoryNavElement) {
				var strCssClassSuffix = "";
				if (strElementExtension == "Scrolling") {
					// scrolling header category detected - set class suffix to "scrolling"
					strCssClassSuffix = " scrolling";
					// alert("strCssClassSuffix = " + strCssClassSuffix);
				}

				if (isVisible) {
					objCategoryNavElement.className = "header expanded" + strCssClassSuffix;
					objCategoryListElement.style.display = "list-item";
				}
				else {
					objCategoryNavElement.className = "header collapsed" + strCssClassSuffix;
					objCategoryListElement.style.display = "none";
				}
				// alert("objCategoryNavElement.className = " + objCategoryNavElement.className);
	 		}
		},

		setTrimHeaderSeeAll:function(strCategoryGroupId) {
			this.objCategoryNavElementLimited = $("categoryNav" + strCategoryGroupId + "Limited");
			this.objCategoryNavElementScrolling = $("categoryNav" + strCategoryGroupId + "Scrolling");

			if ((this.objCategoryNavElementLimited != null) || (this.objCategoryNavElementScrolling != null)) {
				this.objCategoryNavElementLimited.style.display = "none";
				this.objCategoryNavElementScrolling.style.display = "list-item";
			}
		},

		setSideNavHeight : function() {
			// standard categories
			var objSideNavCategories = $('sideNavCategories');

			// search categories
			if (!objSideNavCategories) {
				objSideNavCategories = $('sideNavCategoryHeaders');
			}

			// site search refinements
			if (!objSideNavCategories) {
				objSideNavCategories = $('sideNavRefinements');
			}

			var objSideNav = $('sideNav');
			var objContent = $('mainContent');
			
			// Reset styles for sidenav and content
			if (objSideNavCategories) {
				objSideNavCategories.style.height = "";
				objSideNavCategories.style.minHeight = "";
			}
			objContent.style.height = "";
			objContent.style.minHeight = "";
			
			if (objSideNavCategories && objSideNav && objContent) {
				if (objContent.offsetHeight > objSideNav.offsetHeight) {
					var intSideNavOuterHeight = objSideNav.offsetHeight - objSideNavCategories.offsetHeight;
					if (clientBrowser.isIE6) {
						objSideNavCategories.style.height = (objContent.offsetHeight-intSideNavOuterHeight) + 'px';
					} else {
						objSideNavCategories.style.minHeight = (objContent.offsetHeight-intSideNavOuterHeight) + 'px';
					}
				} else if (objSideNav.offsetHeight > objContent.offsetHeight) {
					var strHeight = objContent.offsetHeight + (objSideNav.offsetHeight-objContent.offsetHeight) + 'px';
					if (clientBrowser.isIE6) {
						objContent.style.height = strHeight;
					} else {
						objContent.style.minHeight = strHeight;
					}
				}
			}
		}
	}
};
var siteNavigation = new SiteNavigation();


/**
 * GIDBrandSiteConstructor is a class that is a data store for sister GID site data.
 * Instantiated as gidBrandSiteConstruct in JsGlobalFunctions.jsp by a method in NavLogicTag.java
 * @constructor
 *
 * @author Byung Kim
 * @date 10/24/2007
 */
var GIDBrandSiteConstructor = Class.create();
GIDBrandSiteConstructor.prototype = {

	/**
	 * initialize GIDBrandSiteConstructor
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	initialize:function(
		currentBrandCode,
		cookieDomain,
		currentBrandSiteId,
		sbsViewAllState,
		sitesVisited,
		sbsPreferences,
		sslPrefix,
		isCommonDomainOn,
		renderOnCurrentBrandUrl,
		sisterSiteActionPath,
		gidBrandSites) {
		this.currentBrandCode = currentBrandCode;
		this.cookieDomain  = cookieDomain;
		this.currentBrandSiteId = currentBrandSiteId;
		this.sbsViewAllState = sbsViewAllState;
		this.sbsPreferences = sbsPreferences;
		this.sslPrefix = sslPrefix;
		this.isCommonDomainOn  = isCommonDomainOn;
		this.renderOnCurrentBrandUrl  = renderOnCurrentBrandUrl;
		this.sisterSiteActionPath = sisterSiteActionPath;
		this.hasGIDBrandSites = (gidBrandSites && gidBrandSites.length > 0);
		this.sisterSiteUrlParams = "?isViewAll=" + this.sbsViewAllState + this.sbsPreferences;
		this.tempBrandSites = gidBrandSites;
		this.gidBrandSites = new Array();
		this.setBrandSites();
		this.visitedSites = sitesVisited;

		// Fire sisterSiteSync.syncUser when the DOM is ready except on checkout page
		if (this.hasGIDBrandSites && !this.isCommonDomainOn) {
			processingService.api.addApplicationMethodToRegistry(sisterSiteSync.syncUser.bind(sisterSiteSync), "gidFunctions sisterSiteSync");
		}
	},

	/**
	 * setBrandSites adds brand site objects to gidBrandSites array. The index is the brandCode.
	 * This is done for easy referencing.
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	setBrandSites:function() {
		for (var i=0;i<this.tempBrandSites.length;i++) {
			var brandSite = this.tempBrandSites[i];
			this.gidBrandSites[Number(brandSite.brandCode)] = brandSite;
			var badgePath = brandConst["BRAND"+brandSite.brandCode+"_BADGE_CONTENT_PATH"];
			if (badgePath) this.gidBrandSites[Number(brandSite.brandCode)].badgeContentPath = badgePath;
		}
	},

	/**
	 * openBrand opens another brand's site in the same browser window
	 * @param {integer} brandcode The brand to open
	 * @param {string} trackingId The tracking Id
	 * @return false to cancel the href
	 *
	 * @author Byung Kim
	 * @date 2/5/2008
	 */
	openBrand:function(brandcode,trackingId) {
		var visited = (this.visitedSites != null ? this.visitedSites : "");
		var brandSite = this.gidBrandSites[brandcode];
		var currentBrandSite = this.gidBrandSites[this.currentBrandCode];
		var url = "";
		if (brandSite) {
			if(this.renderOnCurrentBrandUrl){
				url = currentBrandSite.unsecureUrl + this.sisterSiteActionPath;
			}else{
				url = brandSite.unsecureUrl + this.sisterSiteActionPath;
			}
			url += "?ssiteID=" + (trackingId ? trackingId : this.currentBrandSiteId)+ "&lBrdCd=" + brandcode;

			parent.location.href = url;
		}
		return false;
	}
}


/**
 * SisterSiteSync is a class used for cross domain user data synchronization.
 * Instantiated as sisterSiteSync.
 * @constructor
 *
 * @author Byung Kim
 * @date 10/24/2007
 */
var SisterSiteSync = Class.create();
SisterSiteSync.prototype = {
	hasUserSyncFailed:false,
	synchronizationPage:"/gid/html/en/userSynchronization.html",
	serverCookies:{
		jSessionId:{param:"jsid",cookieName:"JSESSIONID",isPersist:false,isSecure:false,isServerCookie:true},
		gidSecureToken:{param:"gst",cookieName:"gidSecureToken",isPersist:false,isSecure:true,isServerCookie:true},
		unknownShopperId:{param:"usid",cookieName:"unknownShopperId",isPersist:true,isSecure:false,isServerCookie:true,expTime:{"days":72}},
		customerId:{param:"cid",cookieName:"customerId",isPersist:true,isSecure:false,isServerCookie:true,expTime:{"days":72}},
		customerIdVerified:{param:"cidv",cookieName:"customerIdVerified",isPersist:true,isSecure:false,isServerCookie:true,expTime:{"days":72}},
		mktUniversalPersist:{param:"mup",cookieName:"mktUniversalPersist",isPersist:true,isSecure:false,isServerCookie:false,expTime:{"years":5}},
        brandTidMap:{param:"brandTidMap",cookieName:"BRAND_TID_MAP",isPersist:true,isSecure:false,isServerCookie:true,expTime:{"days":7}},
		partner:{param:"partner",cookieName:"PARTNER",isPersist:true,isSecure:false,isServerCookie:true,expTime:{"days":7}},
        locale:{param:"locale",cookieName:"locale",isPersist:true,isSecure:false,isServerCookie:true,expTime:{"days":72}}
	},

	/**
	 * initialize for SisterSiteSync
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	initialize:function() {
	},


	/**
	 * createIFrame creates an IFrame and calls the userSynchronization.html page to synchronize data
	 * @param {object} gidBrandSite An object with all the relevant info about the brand
	 * @param {string} params The querystring parameters to attach to the url
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	createIFrame:function(gidBrandSite,params) {
		var frameID = "brand"+gidBrandSite.brandCode+"DataSync";
		var cont = document.body;
		var isSsl = (location.protocol == "https:");
		var brandSiteUrl = (isSsl ? gidBrandSite.secureUrl : gidBrandSite.unsecureUrl);
		if (brandSiteUrl && brandSiteUrl.length > 0 && cont) {
			var newIFrame = $(document.createElement("IFRAME"));
			newIFrame.id = frameID;
			newIFrame.style.display = "none";
			newIFrame.src = brandSiteUrl + this.synchronizationPage + "?" + params;
			cont.appendChild(newIFrame);
		}
	},

	/**
	 * syncUser synchronizes cookie information to all the GID sister brand sites
	 *
	 * Modified 2/12/2008 Byung Kim - added mktuniversalpersist cookie and cleaned up code
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	syncUser:function() {
		if (navigator.cookieEnabled && gidBrandSiteConstruct.hasGIDBrandSites && !gidBrandSiteConstruct.isCommonDomainOn) {
			var aParams = [];
  			var securePrefix = null;
			var unknownShopperId = this.serverCookies.unknownShopperId;
			var jSessionId = this.serverCookies.jSessionId;
			var customerId = this.serverCookies.customerId;
			var customerIdVerified = this.serverCookies.customerIdVerified;
			var mktUniversalPersist = this.serverCookies.mktUniversalPersist;
			var gidSecureToken = this.serverCookies.gidSecureToken;
            var partner = this.serverCookies.partner;
            var brandTidMap = this.serverCookies.brandTidMap;
            var locale = this.serverCookies.locale;

			// pass the originating brand host
			aParams.push("brand=" + escape(location.host));

			// unknownshopperid cookie
			var usid = gidLib.getCookie(unknownShopperId.cookieName);
			if (usid && usid != "") aParams.push(unknownShopperId.param+"="+escape(usid));

			// jsession cookie
			var jsid = gidLib.getCookie(jSessionId.cookieName);
			if (jsid && jsid != "") aParams.push(jSessionId.param+"="+escape(jsid));


			// customerid cookie
			var cid = gidLib.getCookie(customerId.cookieName);
			if (cid && cid != "") aParams.push(customerId.param+"="+escape(cid));

			// customeridverified cookie
			var cidv = gidLib.getCookie(customerIdVerified.cookieName);
			if (cidv && cidv != "") aParams.push(customerIdVerified.param+"="+escape(cidv));

			// mktUniversalPersist cookie
			var mup = gidLib.getCookie(mktUniversalPersist.cookieName);
			if (mup && mup != "") aParams.push(mktUniversalPersist.param+"="+escape(mup));

			// gidsecuretoken cookie
			var gst = gidLib.getCookie(gidSecureToken.cookieName);
			if (gst && gst != "") aParams.push(gidSecureToken.param+"="+escape(gst));

            // PARTNER cookie
            var partnerCookie = gidLib.getCookie(partner.cookieName);
            if (partnerCookie && partnerCookie != "") aParams.push(partner.param+"="+escape(partnerCookie));

            // BRANDTIDMAP cookie
            var brandTidMapCookie = gidLib.getCookie(brandTidMap.cookieName);
            if (brandTidMapCookie && brandTidMapCookie != "") aParams.push(brandTidMap.param+"="+escape(brandTidMapCookie));

            // localeCode cookie
			var localeCode = gidLib.getCookie(locale.cookieName);
			if (localeCode && localeCode != "") aParams.push(locale.param + "=" + escape(localeCode));

			// pass the flag states
			aParams.push("sslPrefix=" + gidBrandSiteConstruct.sslPrefix);

			var sParams = aParams.join("&");

			for (i=0;i<gidBrandSiteConstruct.gidBrandSites.length;i++) {
				var gidBrandSite = gidBrandSiteConstruct.gidBrandSites[i];
				if (gidBrandSite && gidBrandSite.brandCode != gidBrandSiteConstruct.currentBrandCode) {
					this.createIFrame(gidBrandSite,sParams);
				}
			}
		}
	},
	/**
	 * userSyncFailed displays a message stating that cookie synchronization failed.
	 * It also sets a global boolean flag hasUserSyncFailed.
	 * Finally it sets the appropriate reporting metrics for G and H code.
	 *
	 * Modified 8/19/2008 Andrew Southwick - moved to gidFunctions to re-enable broken
	 * functionality related to synch failure.  This was broken when this method was
	 * moved to userSynchronization.html which redirects in the event of failure to
	 * userSyncFailure.html which then calls the parent userSyncFailed method below.
	 *
	 * @author Byung Kim
	 * @date 10/24/2007
	 */
	userSyncFailed:function() {
		var message = $('thirdPartyCookieBlockedMessage');
		if (!this.hasUserSyncFailed) {
			if (message) {
				var cookieMessage = $(document.createElement("div"));
				cookieMessage.setStyle({
					padding:"10px",
					margin:"10px 0px",
					background:"#fff",
					border:"2px solid #000",
					fontSize:"12px",
					fontWeight:"bold",
					color:"#000"
				});
				cookieMessage.update(message.innerHTML);
				$('topNav').insertBefore(cookieMessage,$('universalTopNav'));
			}
			if (getCookieVar("globalSession","userSyncFailed") != "true") {
				setCookieVar("globalSession","userSyncFailed","true");
				if(!(reportingService||{}).isActive){
					// G code.
					var strViewType = omni.strViewType;
					omni.strViewType = "thirdPartyCookieBlocked";
					omni.setTransmitData();
					omni.strViewType = strViewType;
				}
				else {
					// H code.
					var commonViewManager = reportingService.controller.viewManagers.commonViewManager;
					var commonModel = reportingService.controller.viewManagers.commonViewManager.model;
					commonModel.commonIsThirdPartyCookieBlocked = true;
					commonViewManager.controller.getReportRequest();
				}
			}
		}
		this.hasUserSyncFailed = true;
	}
};
var sisterSiteSync = new SisterSiteSync();

/**
 * Survey is a class to manage all of the pixel tracking
 *
 * TODO: move survey code out of brandFunctions to this class.
 * TODO: instantiate the class
 *
 * @constructor
 *
 * @author Byung Kim
 * @date 11/1/2007
 */
var Survey = Class.create();
Survey.prototype = {
	cookieExpiration:null,

	/**
	 * initialize Survey
	 *
	 * @author Byung Kim
	 * @date 11/1/2007
	 */
	initialize:function() {
		this.cookieExpirationTime = gidLib.getFutureDate({"weeks":26}); // six months
	}

};


var DateUtils = Class.create();

DateUtils.prototype = {
	currentDate:null,
	expireDate:null,
	expireDateFromCookie:null,
	initialize:function() {
		//initialize properties here
	},

	getDateFromCookieVar:function(cookieName,varName) {
		var cookieVarTimeValue = gidLib.getCookieVar(cookieName,varName);
		var date = null;
		var currentDate = new Date();
		var dateMsValue = null;

		// Check if the value in the cookie is valid with isNaN.
		// If in No valid value has been set in the cookie - return null.
		if ((isNaN(cookieVarTimeValue) == false) && (cookieVarTimeValue != "")) {
			// Value from cookie is valid - create date object by parsing out ms value.
			dateMsValue = parseInt(cookieVarTimeValue);
			currentDate.setTime(dateMsValue);
			date = currentDate;
		}

		return date;

	},
	setDateToCookieVar:function(cookieName, varName, date) {
		// Convert time to ms, and then to string.
		var dateTimeString = date.getTime() + "";
		setCookieVar(cookieName, varName, dateTimeString);
	},
	getDateComparisonPresentOrFutureByDay:function(expirationDate,compareDate) {
		// Returns true if dates by day are equal and false otherwise.
		var isDatePresentOrFutureByDay = false;
		var expirationDateDay = expirationDate.getDate();
		var expirationDateMonth = expirationDate.getMonth();
		var expirationDateYear = expirationDate.getYear();
		var compareDateDay = compareDate.getDate();
		var compareDateMonth = compareDate.getMonth();
		var compareDateYear = compareDate.getYear();
		if ((expirationDateDay == compareDateDay) && (expirationDateMonth == compareDateMonth) && (expirationDateYear == compareDateYear)) {
			isDatePresentOrFutureByDay = true;
		}
		else if (compareDate > expirationDate) {
			isDatePresentOrFutureByDay = true;
		}
		return isDatePresentOrFutureByDay;
	},
	getFutureDateByDay:function(daysToAdd) {
		// Returns future Date object.
		var futureDate = new Date();
		futureDate.setDate(futureDate.getDate()+daysToAdd);
		return futureDate;
	}
}

gidLib.dateUtils = new DateUtils();

Event.observe(window, 'load', function() {
	gidLib.onLoadHandler();
	gidLib.set800pxUniversalNav();
	siteNavigation.onLoadHandler();
	inlineBag.initializeElements();
});

Event.observe(window, 'beforeunload', function() {
	gidLib.onUnloadHandler();
});

var LocaleUtils = Class.create();

LocaleUtils.prototype = {
	initialize:function() {
		//initialize properties here
	},
	constants : {
					EMPTY_STRING: "", // Empty String
					PARAMETER_LOCALE: "locale", // locale parameter
					QUESTIONMARK: "?", // URL Sparater
					PARAMETER_DELIMITER: "&", // parameter separator
					EQUAL_SIGN: "=",
					ASH_SIGN: "#"
	},
	/**
	*  toggle Locale function is used to switch the locale value.
	*/
	toggleLocale : function(locale){
	    var constants = this.constants;
	    var newUrl = constants.EMPTY_STRING;
	    var localeParameter = constants.PARAMETER_LOCALE+constants.EQUAL_SIGN;
	    var previousUrl = window.location;
	    var temporaryUrl = previousUrl+constants.EMPTY_STRING;
	    if(temporaryUrl.indexOf(constants.ASH_SIGN)!=-1){
	      temporaryUrl = temporaryUrl.substring(0,temporaryUrl.length-1);
	    }
		var removedLocaleUrl = gidLib.removeQueryStringParam(temporaryUrl,constants.PARAMETER_LOCALE);
		var urlArray = removedLocaleUrl.split(constants.QUESTIONMARK);
		if(urlArray.length>=2){
		    var parameterArray=urlArray[1].split(constants.PARAMETER_DELIMITER);
			if(parameterArray.length>=1 && parameterArray[0].indexOf(constants.PARAMETER_LOCALE)==-1){
			 newURL=removedLocaleUrl+constants.PARAMETER_DELIMITER+localeParameter+locale;
			}else{
				newURL=urlArray[0]+constants.QUESTIONMARK+localeParameter+locale;
			}
		}else{
		 newURL=removedLocaleUrl+constants.QUESTIONMARK+localeParameter+locale;
		}
		window.location=newURL;
	}
}

gidLib.localeUtils = new LocaleUtils();

var InternationalShippingService = Class.create();
InternationalShippingService.prototype = {
	initialize: function() {
	},
	constants: {
		FORM_ACTION_URI: '/profile/internationalShippingOptIn.do',
		SELECTED_COUNTRY_CLASS: 'selected-country',
		FIFTY_ONE_FORM: 'fiftyOneForm',
		EXPLANATORY_COPY: 'explanatoryCopy',
		SHOPPING_SITE_SELECTION: 'shoppingSiteSelection',
		CURRENCY_FIFTY_ONE_SELECT: 'currency51',
		SITE_SEL_CURRENCY_FIFTY_ONE_SELECT: 'siteSelectionCurrency51',
		DEFAULT_COUNTRY_CODE: 'us',
		DEFAULT_LANGUAGE: 'en',
		US_MARKET_CODE: 'US',
		LOCALE_PARAM_NAME: 'locale'
	},
	model: {
		selectedCountry: null,
		redirectCrossMarketUrl: null,
		businessUnitId: null,
		isShippCountryCodeInUrl: null,
		shoppingBagHeader: null,
		currentCountryCode: null,
		currentCurrencyCode: null,
		isCountryRedirectNeeded: false,
		isSiteSelectionAvailable: false,
		currentLanguageCode: false,
		marketCode: null,
		borderImagesSources: []

		//countryUrlMap:null
	},
	view: {
		hiddenSelects: [],
		initializeHandlersToDomElements: function() {
			var lis = $$('#countries51 li');
			lis.each(function(e) {
				if (!e.classNames().include('disable')) {
					e.observe('click', function() {
						internationalShippingService.view.selectCountry(e);
					});

					// disables the links
					$(e.children[0]).observe('click', function(e) {
						e.preventDefault();
						return false;
					});
					if (clientBrowser.isIE6Up) {
						e.observe('mouseover', function() {
							e.classNames().add('hover');
						});
						e.observe('mouseout', function() {
							e.classNames().remove('hover');
						});
					}
				}
			});
			if (clientBrowser.isIE6) {
				var shopSiteSel = $('shoppingSiteSelection');
				if (shopSiteSel) {
					shopSiteSel.classNames().add('fixedShopSiteSelHeight');
				}
			}
			var goButtons = $('buttons51').getElementsByClassName('goButton');
			for (i = 0; i < goButtons.length; i++) {
				goButtons[i].observe('click', function(e) { internationalShippingService.controller.submit51(); e.preventDefault(); });
			}

			$('shopCountry').observe('click', function(e) { internationalShippingService.view.setSelectedSite(e); });
			$('shopUS').observe('click', function(e) { internationalShippingService.view.setSelectedSite(e); });

			var closeFunction = function(e) { internationalShippingService.controller.closeLayer(); e.preventDefault(); };
			$('cancel51').observe('click', closeFunction);
			$('closeButton').observe('click', closeFunction);
		},
		handler:{
		},
		selectCurrency: function(currencyCode) {
			$('currency51').value = currencyCode;
		},
		showRememberMySelection: function() {
			$('rememberMySelectionLabel').classNames().remove('invisible');
		},
		hideRememberMySelection: function() {
			$('rememberMySelectionLabel').classNames().add('invisible');
		},
		getCalloutText: function() {
			var calloutText = '';
			var messageValue = resourceBundleValues.intlShipSiteRedirectContent[internationalShippingService.model.selectedCountry.code];
			if (messageValue) {
				calloutText = messageValue.calloutMessage;
			}
			$('countryCalloutImg').setAttribute('alt', calloutText);
			return calloutText;
		},
		setSelectedSite: function(e) {
			var countryCode = internationalShippingService.constants.DEFAULT_COUNTRY_CODE;

			if ($('shopCountry').checked) {
				countryCode = internationalShippingService.model.selectedCountry.code;
				internationalShippingService.view.hideRememberMySelection();
			} else {
				internationalShippingService.view.showRememberMySelection();
			}

			var doesRedirectURLExistForCountry = internationalShippingService.controller.doesRedirectURLExistForCountry(countryCode);

			if (doesRedirectURLExistForCountry) {
				internationalShippingService.model.isCountryRedirectNeeded = true;
				internationalShippingService.controller.setRedirectToCrossMarketUrl(countryCode, internationalShippingService.model.currentLanguageCode, internationalShippingService.model.selectedCountry.code);
			} else {
				internationalShippingService.model.isCountryRedirectNeeded = false;
			}
			internationalShippingService.view.showButtonForSelection();
		},
		setShoppingSiteSelectionContent: function(countryCode) {
			var messageValue = resourceBundleValues.intlShipSiteRedirectContent[countryCode];
			if (messageValue) {
				$('shopOtherSiteLabel').update(messageValue.siteSelectorLabel);
				$('shopOtherSiteContentMsg1').update(messageValue.contentMessage1);
				$('shopOtherSiteContentMsg2').update(messageValue.contentMessage2);
				$('shopOtherSiteContentMsg3').update(messageValue.contentMessage3);
			}
		},
		setShoppingSiteSelectionCurrencyCode: function(currencyCode) {
			$('siteSelectionCurrency51').value = currencyCode;
		},
		enableSiteSelectionArea: function(countryCode, currencyCode, languageCode) {
			//set country specific message content in selection area
			internationalShippingService.view.setShoppingSiteSelectionContent(countryCode);

			var constants = internationalShippingService.constants;
			$(constants.EXPLANATORY_COPY).hide();
			$(constants.SHOPPING_SITE_SELECTION).show();
			$(constants.FIFTY_ONE_FORM).classNames().add('hidden');

			if ($(constants.SITE_SEL_CURRENCY_FIFTY_ONE_SELECT).childElements().length == 0) {
				//copy currency select options to select in site redirect area
				$(constants.SITE_SEL_CURRENCY_FIFTY_ONE_SELECT).update($(internationalShippingService.constants.CURRENCY_FIFTY_ONE_SELECT).innerHTML);
			}

			//set default selection to Shop Country Site
			$('shopCountry').checked = true;
			internationalShippingService.view.hideRememberMySelection();
			var doesRedirectURLExistForCountry = internationalShippingService.controller.doesRedirectURLExistForCountry(countryCode);
			if (doesRedirectURLExistForCountry) {
				internationalShippingService.model.isCountryRedirectNeeded = true;
				internationalShippingService.controller.setRedirectToCrossMarketUrl(countryCode, languageCode, countryCode);
			} else {
				internationalShippingService.model.isCountryRedirectNeeded = false;
				//set default redirect url to us
				internationalShippingService.controller.setRedirectToCrossMarketUrl(internationalShippingService.constants.DEFAULT_COUNTRY_CODE, languageCode, countryCode);
			}

			internationalShippingService.view.setShoppingSiteSelectionCurrencyCode(currencyCode);
		},
		showButtonForSelection: function() {
			var goButtons = $('buttons51').getElementsByClassName('goButton');
			for (i = 0; i < goButtons.length; i++) {
				goButtons[i].hide();
			}
			if (internationalShippingService.model.isCountryRedirectNeeded) {
				var countryCode = internationalShippingService.model.selectedCountry.code;
				if (internationalShippingService.model.isSiteSelectionAvailable && $('shopUS').checked) {
					countryCode = internationalShippingService.constants.DEFAULT_COUNTRY_CODE;
				}
				var doesRedirectURLExistForCountry = internationalShippingService.controller.doesRedirectURLExistForCountry(countryCode);
				if (doesRedirectURLExistForCountry) {
					switch (countryCode) {
						case 'ca':
							$('goToCanada').show();
							break;
						case 'gb':
							$('goToEU').show();
							break;
						default:
							$('goToUS').show();
					}
				} else {
					$('goToUS').show();
				}
			} else {
				$('go51').show();
			}
		},
		selectCountry: function(li) {
			var link = li.childNodes[0];
			var countryName = link.innerHTML;
			var countryCode = link.id;
			if (countryCode.indexOf("_") != -1) {
				var countryCodeIDArray = countryCode.split("_");
				countryCode = countryCodeIDArray[0];
			}

			internationalShippingService.model.selectedCountry = {name: countryName, code: countryCode};

			var constants = internationalShippingService.constants;
			var selectedCountryClass = constants.SELECTED_COUNTRY_CLASS;
			$$('#countries51 li.' + selectedCountryClass).each(function(e) { e.removeClassName(selectedCountryClass); });

			li.addClassName(selectedCountryClass);
			// hides the forms
			$(constants.FIFTY_ONE_FORM).classNames().add('hidden');
			$(constants.EXPLANATORY_COPY).classNames().add('invisible');
			$(constants.EXPLANATORY_COPY).show();
			$(constants.SHOPPING_SITE_SELECTION).hide();

			// remove other currencies if it is US
			var isUsd = countryCode == internationalShippingService.constants.DEFAULT_COUNTRY_CODE;
			var isCountryRedirectNeeded = internationalShippingService.controller.isCountryRedirectNeeded(countryCode);
			var isSiteSelectionAvailable = (countryCode == 'ca' || countryCode == 'gb');
			internationalShippingService.model.isCountryRedirectNeeded = isCountryRedirectNeeded;
			internationalShippingService.model.isSiteSelectionAvailable = isSiteSelectionAvailable;
			var isCurrencyNeeded =  internationalShippingService.controller.isCurrencyNeeded(countryCode);
			internationalShippingService.view.showRememberMySelection();
			var isShipCountryInUrl = internationalShippingService.controller.isShipCountryCodeInRedirectedURL();
			internationalShippingService.model.isShippCountryCodeInUrl = isShipCountryInUrl;
			var currencyCode = 'usd';
			var languageCode = 'en';
			if (!isUsd) {
				// class names for the <li>s include a currency_<currencyCode>
				link.classNames().each(function(e) {
					if (/currency_/.test(e)) {
						currencyCode = e.split('_')[1];
					}
				});
				if (isCurrencyNeeded) {
					$(constants.FIFTY_ONE_FORM).classNames().remove('hidden');
				}
				$(constants.EXPLANATORY_COPY).classNames().remove('invisible');
			}
			if (isCountryRedirectNeeded) {
				// class names for the <li>s include a language_<languageCode>
				link.classNames().each(function(e) {
					if (/language_/.test(e)) {
						languageCode = e.split('_')[1];
					}
				});
			}
			if (isCurrencyNeeded) {
				internationalShippingService.view.selectCurrency(currencyCode);
			}
			internationalShippingService.model.currentLanguageCode = languageCode;

			if (isSiteSelectionAvailable) {
				internationalShippingService.view.enableSiteSelectionArea(countryCode, currencyCode, languageCode);
			} else {
				if (isCountryRedirectNeeded) {
					internationalShippingService.controller.setRedirectToCrossMarketUrl(countryCode, languageCode, countryCode);
				} else if (isShipCountryInUrl) {
					internationalShippingService.controller.updateShipCountryCodeInUrl(countryCode, currencyCode);
				}
			}

			internationalShippingService.view.showButtonForSelection();

			internationalShippingService.controller.setMarketingContentForCountry(countryCode, languageCode, currencyCode);
		}
	},
	controller: {
		init: {
			main: function() {
				internationalShippingService.view.initializeHandlersToDomElements();

				var currentLocaleCode = brandConst.BRAND_LOCALE;
				var currentCountry = internationalShippingService.constants.DEFAULT_COUNTRY_CODE;
				var currentLanguage = internationalShippingService.constants.DEFAULT_LANGUAGE;

				if (currentLocaleCode != -1) {
					var currentLocaleCodeArray = currentLocaleCode.split("_");
					currentLanguage = currentLocaleCodeArray[0];
					currentCountry = currentLocaleCodeArray[1].toLowerCase();
				}
				currentCountry = internationalShippingService.model.currentCountryCode || currentCountry;
				var countryId = currentCountry;
				var isCountryCodeCA = currentCountry == 'ca';
				if (isCountryCodeCA) {
					countryId = currentCountry + "_" + currentLanguage;
				}
				if ($(countryId) != null) {
				internationalShippingService.view.selectCountry($(countryId).parentNode);
				}
				var currentCurrency = internationalShippingService.model.currentCurrencyCode || 'usd';
				internationalShippingService.view.selectCurrency(currentCurrency);
				if (internationalShippingService.model.isSiteSelectionAvailable && internationalShippingService.model.currentCurrencyCode) {
					$('siteSelectionCurrency51').value = currentCurrency;
				}

				gidLib.setButtonEvents('internationalShippingPopUp');

				var borderImagesSources = internationalShippingService.model.borderImagesSources;
				var preloadedImages = 0;
				for (var i = 0; i < borderImagesSources.length; i++) {
					var img = new Image();
					img.onload = function() {
						if (++preloadedImages == borderImagesSources.length) {
							internationalShippingService.view.hiddenSelects = gidLib.hideDropDownsUnderElement($('internationalShippingPopUp'), 'select:not(select[id=currency51])'); // to not hide the currency select
						}
					}
					img.src = borderImagesSources[i];
				}
				$('currency51').setStyle({visibility:'visible'});
				$('siteSelectionCurrency51').setStyle({visibility:'visible'});
			}
		},
		setRedirectToCrossMarketUrl: function(countryCode, languageCode, shipToCountryCode) {
			var redirectCrossMarketUrl = null;
			var shipToCurrencyCode = null;
			var isDefaultRedirectUrlActive = false;
			var businessUnitId = internationalShippingService.model.businessUnitId;
			var brandCode = brandConst.BRAND_ID.toLowerCase();
			var localeCode = languageCode + "_" + countryCode.toUpperCase();
			var ssiteId = "cs_" + countryCode + "_" + languageCode + "_" + brandCode;
			var baseUrl = internationalShippingService.model.countryUrlMap[businessUnitId + "_" + countryCode];
			if (baseUrl == null) {
				baseUrl = internationalShippingService.model.countryUrlMap[businessUnitId + "_" + "default"];
				if (baseUrl != null) {
					isDefaultRedirectUrlActive = true;
				}
			}
			redirectCrossMarketUrl = baseUrl + "?";
				shipToCurrencyCode = $('currency51').value;
				redirectCrossMarketUrl += "ssiteID=" + ssiteId ;

				//cannot pass locale currently because US site can only pass 'en',
				//which will reset any previously selected locale on an international site
//				if (!isDefaultRedirectUrlActive) {
//					redirectCrossMarketUrl += "&locale=" + localeCode;
//				}
			redirectCrossMarketUrl += (shipToCountryCode ? "&shipToCountryCode=" + shipToCountryCode : '') +
									  (shipToCurrencyCode ? "&shipToCurrencyCode=" + shipToCurrencyCode : '');
			internationalShippingService.model.redirectCrossMarketUrl = redirectCrossMarketUrl;
		},
		redirectToCrossMarketUrl: function() {
			window.location = internationalShippingService.model.redirectCrossMarketUrl;
		},
		updateShipCountryCodeInUrl: function(countryCode, currencyCode) {
			var constants = internationalShippingService.constants;
			var redirectCrossMarketUrl = null;
			var currentUrl = window.location+'';
			var removedCountryCodeUrl = gidLib.removeQueryStringParam(currentUrl,'shipToCountryCode');
			var removedCountryCurrencyCodeUrl = gidLib.removeQueryStringParam(removedCountryCodeUrl,'shipToCurrencyCode');
			var contryCode = countryCode;
			var currencyCode = null;
			if (!$(constants.FIFTY_ONE_FORM).classNames().include('hidden')) {
				currencyCode = $('currency51').value;
			}
			redirectCrossMarketUrl = removedCountryCurrencyCodeUrl + (contryCode ? "&shipToCountryCode=" + contryCode : '') +
			  (currencyCode ? "&shipToCurrencyCode=" + currencyCode : '');
			internationalShippingService.model.redirectCrossMarketUrl = redirectCrossMarketUrl;

		},
		updateShipCurrencyCodeInUrl: function(currencyCode, rememberMySelection) {
			var redirectCrossMarketUrl = null;
			var currentUrl = internationalShippingService.model.redirectCrossMarketUrl;
			var removedMySelection = gidLib.removeQueryStringParam(currentUrl,'rememberMySelection');
			var removedCurrencyCodeUrl = gidLib.removeQueryStringParam(removedMySelection,'shipToCurrencyCode');

			redirectCrossMarketUrl = removedCurrencyCodeUrl +
			  (currencyCode ? "&shipToCurrencyCode=" + currencyCode : '') +
			  (rememberMySelection ? "&rememberMySelection=" + rememberMySelection : '');
			internationalShippingService.model.redirectCrossMarketUrl = redirectCrossMarketUrl;

		},
		getRefreshUrl: function() {
			var refreshUrl = window.location+'';
			var queryStringBegPos = refreshUrl.indexOf('?');
			//remove existing query strings
			if (queryStringBegPos > 0) {
				refreshUrl = refreshUrl.substr(0, queryStringBegPos);
			}
			var redirectUrl = internationalShippingService.model.redirectCrossMarketUrl;
			queryStringBegPos = redirectUrl.indexOf('?');
			if (queryStringBegPos > 0) {
				refreshUrl+= redirectUrl.substr(queryStringBegPos);
			}
			return refreshUrl;
		},
		isCountryRedirectNeeded: function(countryCode) {
			var isCountryRedirectNeeded = false;
			var businessUnitId = internationalShippingService.model.businessUnitId;
			var crossMarketUrl = internationalShippingService.model.countryUrlMap[businessUnitId + "_" + countryCode];
			var crossMarketDefaultUrl = null;
			if ( crossMarketUrl != null ) {
				isCountryRedirectNeeded = true;
			} else {
				crossMarketDefaultUrl = internationalShippingService.model.countryUrlMap[businessUnitId + "_" + "default"];
				//default is used for FiftyOne countries to redirect to US site
				if (crossMarketDefaultUrl) {
					isCountryRedirectNeeded = true;
				}
			}
			return isCountryRedirectNeeded;
		},
		doesRedirectURLExistForCountry: function(countryCode) {
			var doesRedirectURLExistForCountry = false;
			var businessUnitId = internationalShippingService.model.businessUnitId;
			var crossMarketUrl = internationalShippingService.model.countryUrlMap[businessUnitId + "_" + countryCode];
			if ( crossMarketUrl != null ) {
				doesRedirectURLExistForCountry = true;
			}
			return doesRedirectURLExistForCountry;
		},
		isCurrencyNeeded: function(countryCode) {
			var isCurrencyNeeded = true;
			var businessUnitId = internationalShippingService.model.businessUnitId;
			var crossMarketUrl = internationalShippingService.model.countryUrlMap[businessUnitId + "_" + countryCode];
			if ( crossMarketUrl != null ) {
				isCurrencyNeeded = false;
			}
			return isCurrencyNeeded;
		},
		isShipCountryCodeInRedirectedURL: function() {
			var isRedirectedSite = false;
			var currentURL = window.location + '';
			if(currentURL.indexOf('shipToCountryCode')!=-1) {
				isRedirectedSite = true;
			}
			return isRedirectedSite;
		},
		submit51: function() {
			internationalShippingService.controller.submit51 = function() { return false; }
			var selectedCountry = internationalShippingService.model.selectedCountry;
			var isShipCountryInURL = internationalShippingService.model.isShippCountryCodeInUrl;
			var newCurrencyCode = null;
			var rememberMySelection = 'false';
			if (internationalShippingService.model.isCountryRedirectNeeded || isShipCountryInURL) {
				var constants = internationalShippingService.constants;
				if (internationalShippingService.model.isSiteSelectionAvailable) {
					newCurrencyCode = $(constants.SITE_SEL_CURRENCY_FIFTY_ONE_SELECT).value;
				} else {
					if (!$(constants.FIFTY_ONE_FORM).classNames().include('hidden')) {
						newCurrencyCode = $('currency51').value;
					}
				}
				rememberMySelection =  $('rememberMySelection').checked+'';
				internationalShippingService.controller.updateShipCurrencyCodeInUrl(newCurrencyCode, rememberMySelection);
				if (internationalShippingService.model.isCountryRedirectNeeded) {
					internationalShippingService.controller.redirectToCrossMarketUrl();
				} else {
					var refreshUrl = internationalShippingService.controller.getRefreshUrl();
					window.location = refreshUrl;
				}
			} else {
				//We only want to update country and currency if US site selected
				if (internationalShippingService.model.marketCode != internationalShippingService.constants.US_MARKET_CODE) {
			    	internationalShippingService.controller.saved();
					return false;
				}
				var currencyCode = null;
				if (internationalShippingService.model.isSiteSelectionAvailable) {
					currencyCode = $('siteSelectionCurrency51').value;
				} else {
					currencyCode = $('currency51').value;
				}
				new Ajax.Request(internationalShippingService.constants.FORM_ACTION_URI, {
				    method: 'post',
				    parameters: {
				        currencyCode: currencyCode,
				        countryCode: selectedCountry.code,
				        remember: $('rememberMySelection').checked
				    },
				    onComplete: function() {
					    // function is defined on the parent page
				    	siteNavigation.setCurrentShippingToCountry(selectedCountry.code, selectedCountry.name);
				    	internationalShippingService.controller.saved();
				    }
				});
			}
			return false;
		},
		saved: function() {
			var header = $$('.pageHeader');
			header = header.length > 0 ? header[0].innerHTML.trim() : '';
			if (header == internationalShippingService.model.shoppingBagHeader) {
				window.location = siteNavigation.shoppingBagUrl;
			} else {
				/* ideally we should only close the layer, but the current page layout
					might be affected by the currency choice. We'll refresh the page
					until a better solution comes up.

					//internationalShippingService.controller.closeLayer();
				*/
					window.location.reload();
				}
		},
		closeLayer: function() {
			gidLib.showDropDowns(internationalShippingService.view.hiddenSelects);
	       	$('internationalShippingPopUp').remove();
		},
		setMarketingContentForCountry: function(countryCode, languageCode, currencyCode) {

		}
	}
}
var internationalShippingService = new InternationalShippingService();

/*********************************************
DEPRECATED METHODS - do not use. do not remove
*********************************************/

/**
 * returnImg - returns an object with a src property
 * @deprecated - use JSON syntax to create this instead (e.g. {src:"/image/path/image.jpg"}  ).  Wrapper function is redundant.
 * 				 To preload an image, use gidLib.loadImage()
 */
function returnImg(strSrc) {
	return gidLib.loadImage(strSrc);
}

/**
 * returnObjById
 * @deprecated - use prototype method $() instead
 */
function returnObjById(strId) {
	return $(strId);
}

/**
 * replaceHTML
 * @deprecated
 */
function replaceHTML(obj, html) {
	if(obj) obj.update(html);
	return obj;
}

/**
 * setObjInnerHTML
 * @deprecated - use prototype method update() instead
 */
function setObjInnerHTML(objLayer,strHTML) {
	if(typeof(objLayer) == 'string') {
        $(objLayer).innerHTML = strHTML;
    } else { objLayer.innerHTML = strHTML; };
}

/**
 * returnObjPosition
 * @deprecated - use prototype method Position.cumulativeOffset()
 */
function returnObjPosition(target){
	var pos = Position.cumulativeOffset($(target));
	return {x:pos[0], y:pos[1]};
}

/**
 * isPrototypeSafe
 * @deprecated - use hasOwnProperty instead to check if the key isn't a method.
 */
function isPrototypeSafe(key, object) {
	return object.hasOwnProperty(key);
}

/**
 * getElementsByAttribute
 * @deprecated - use prototype methods $$() or Element.getElementsBySelector() instead
 */
function getElementsByAttribute(objElement,strAttribute,searchValue,arrayResults) {
	if (clientBrowser.isIE && strAttribute == "class") strAttribute = "className";
	if (!arrayResults) arrayResults = new Array();
	if (objElement.hasChildNodes()) {
		for (var i in objElement.childNodes) {
			if (objElement.childNodes.hasOwnProperty(i)) {
				var objChild = objElement.childNodes[i];
				if (objChild.getAttribute &&
						objChild.getAttribute(strAttribute) &&
						(typeof searchValue != "string" ? objChild.getAttribute(strAttribute).search(searchValue) != -1 : objChild.getAttribute(strAttribute) == searchValue))
						arrayResults[arrayResults.length] = objChild;
				if (objChild.hasChildNodes && objChild.hasChildNodes()) getElementsByAttribute(objChild,strAttribute,searchValue,arrayResults);
			}
		}
	}
	return arrayResults;
}

/**
 * stringFilter - removes certain characters from a form value
 * @deprecated - use regExp matching instead
 */
function stringFilter (input, filteredValues) {
	s = input.value;
	var i;
	var returnString = "";
		for (i = 0; i < s.length; i++) {
		var c = s.charAt(i);
			if (filteredValues.indexOf(c) == -1) returnString += c;
		}
	input.value = returnString;
}

/**
 * getElementsWithMatchingId
 * @deprecated - use prototype method $$() or Element.getElementsBySelector()
 */
function getElementsWithMatchingId(idName) {
	var objContentItemContainerElements = new Array();
	var divs = document.getElementsByTagName("div");
	var y = 0;
	for (var i = 0; i < divs.length; i++) {
		var id = divs[i].getAttribute("id");
		if (id && id.match(idName)) {
			objContentItemContainerElements[y] = divs[i];
			y++;
		}
	}
	return objContentItemContainerElements;
}

/**
 * changeFormDropDownVisibility
 * @deprecated use gidLib.hideDropDownsUnderElement() instead
 */
function changeFormDropDownVisibility(state) {
	if (clientBrowser.isIE6 || clientBrowser.isIE55) {
		var arrayDropDowns = $$("select");
		var iterator = function(element) {
			if (element && element.style) element.style.visibility = state;
		}
		arrayDropDowns.each(iterator);
	}
}

/**
 * setObjVisibility
 * @deprecated use Prototype method setStyle instead
 */
function setObjVisibility(element,state) {
	if (element) element.setStyle({visibility:state});
}

/**
 * setImgSrc
 * @deprecated use prototype method setSrc() native to IMG and INPUT elements instead
 */
function setImgSrc(id, src) {
	$(id).setSrc(src);
}


var pageOnLoadFunctions = function() {}
var pageOnUnloadFunctions = function() {}
/*
 * Legacy pointer references to new gidLib methods
 */
var setCookieVar = gidLib.setCookieVar.bind(gidLib);
var setCookie = gidLib.setCookie.bind(gidLib);
var getCookie = gidLib.getCookie.bind(gidLib);
var getCookieVar = gidLib.getCookieVar.bind(gidLib);
var setObjPosition = gidLib.setObjPosition.bind(gidLib);
var setObjCenter = gidLib.setObjCenter.bind(gidLib);
var getQuerystringParam = gidLib.getQuerystringParam.bind(gidLib);
var removeQueryStringParam = gidLib.removeQueryStringParam.bind(gidLib);
var openLayeredPopup = function(a1,a2,a3,a4,a5,a6,a7,a8) {
	/* This is to capture the "return false" of the new method.
	 * Legacy implementations aren't handling the return properly.
	 */
	gidLib.layeredPopup.openLayeredPopup(a1,a2,a3,a4,a5,a6,a7,a8);
}
var closeLayeredPopup = function() {
	/* This is to capture the "return false" of the new method.
	 * Legacy implementations aren't handling the return properly.
	 */
	gidLib.layeredPopup.closeLayeredPopup();
}
var addCurrentDomain = gidLib.addCurrentDomain.bind(gidLib);
var openWindow = gidLib.openWindow.bind(gidLib);
var closeWindow = gidLib.closeWindow.bind(gidLib);
var contentItemLink = gidLib.contentItemLink.bind(gidLib);
var unUnicode = gidLib.unUnicode.bind(gidLib);
var openGiftCardWindow = gidLib.openGiftCardWindow.bind(gidLib);
var setButtonEvents = gidLib.setButtonEvents.bind(gidLib);
var setLabelOnClick = gidLib.setLabelOnClick.bind(gidLib);
var getContentItemIds = gidLib.reporting.getContentItemIds.bind(gidLib.reporting);
var selectOption = gidLib.selectOption.bind(gidLib);

var objInlineBag = inlineBag;

var goSignIn = siteNavigation.goSignIn.bind(siteNavigation);
var goSignOut = siteNavigation.goSignOut.bind(siteNavigation);
var goJoinEmail = siteNavigation.goJoinEmail.bind(siteNavigation);
var goToShoppingBag = siteNavigation.goToShoppingBag.bind(siteNavigation);
var objFooter = siteNavigation.footer;
var strRelativeURL = siteNavigation.relativeUrl;
var strSignInReturnURL = siteNavigation.signInReturnUrl;
var strSignInTargetURL = siteNavigation.signInTargetUrl;
var strSignInShoppingURL = siteNavigation.signInShoppingUrl;
var objTopNavController = siteNavigation.topNav;
var objSearchBox = siteNavigation.searchBox;
var objSideNav = siteNavigation.sideNav;
var setSideNavHeight = siteNavigation.sideNav.setSideNavHeight;

/*
 * The following Legacy references are wrapped with anonymous functions because the instantiated object is extended in brandFunctions.js
 */
var setFooterImages = function() {
	siteNavigation.footer.setFooterImages();
}
var divisionOver = function(a,b) {
	siteNavigation.topNav.divisionOver(a,b);
}
var divisionOut = function(a,b) {
	siteNavigation.topNav.divisionOut(a,b);
}
var subDivisionOver = function(a,b) {
	siteNavigation.topNav.subDivisionOver(a,b);
}
var subDivisionOut = function (a,b) {
	siteNavigation.topNav.subDivisionOut(a,b);
}
var subDivisionsOver = function(a,b) {
	siteNavigation.topNav.subDivisionsOver(a,b);
}
var subDivisionsOut = function(a,b) {
	siteNavigation.topNav.subDivisionsOut(a,b);
}
var hideSubDivisions = function(a,b) {
	siteNavigation.topNav.hideSubDivisions(a,b);
}



/*
 * Legacy Browser Detection & Properties
 */
var objClient = clientBrowser;
Object.extend(clientBrowser,{
		intFlashVer:parseInt(clientBrowser.verFlash),
		isNav:clientBrowser.isNetscape,
		isNav7:clientBrowser.isNetscape7,
		isNav7up:clientBrowser.isNetscape7Up,
		intGver:clientBrowser.verGecko,
		isIE6up:clientBrowser.isIE6Up,
		isIE5_5:clientBrowser.isIE55,
		isIE5_5up:clientBrowser.isIE55Up
	}
);

