/*
 * All java script logic for the application.
 *
 * The code relies on the jQuery JS library to
 * be also loaded.
 */

var app = (function(jQuery) {

	if (!jQuery) {
		alert(app.resources["MISSING_LIB"]);
		return null;
	}

	// Global dw private data goes here

	// dw scope public
	return {
		URLs : {}, // holds dw specific urls, check htmlhead.isml for some
					// examples
		resources : {}, // resource strings used in js
		constants : {}, // platform constants, initialized in htmlhead.isml
		containerId : "content",
		ProductCache : null, // app.Product object ref to the current/main
								// product
		clearDivHtml : "<div class=\"clear\"><!-- W3C Clearing --></div>",
		currencyCodes : {}, // holds currency code/symbol for the site

		// default dialog box settings
		dialogSettings : {
			bgiframe : true, // this is required mainly for IE6 where drop
								// downs bleed into dialogs!!! it depends on
			autoOpen : false,
			buttons : {},
			modal : false,
			overlay : {
				opacity : 0.5,
				background : "black"
			},
			height : 400,
			width : 200,
			title : '',
			// show: "slow", This is causing dialog to break in jquery 1.3.2
			// rel, show: "slide" works but not desired
			hide : "normal",
			resizable : false
		},

		// default tooltip settings
		tooltipSettings : {
			delay : 0,
			showURL : false,
			extraClass : "tooltipshadow tooltipshadow02",
			top : 15,
			left : 5
		},

		// global form validator settings
		validatorSettings : {
			errorClass : 'errorclient',
			errorElement : 'span',

			onfocusout : function(element) {
				if (!this.checkable(element)) {
					this.element(element);
				}
			}
		},

		// app initializations called from jQuery(document).ready at the end of
		// the file
		init : function() {
			// register initializations here

			// quick view dialog div
			var quickShopLayer = document.createElement('div');
			quickShopLayer.className = 'quick-shop-dialog';
			quickShopLayer.id = 'quick-shop-dialog';
			quickShopLayer.style.position = 'absolute';
			quickShopLayer.style.left = '-9999px';
			$(quickShopLayer).css('display', 'none');
			$('#container').append(quickShopLayer);
			// jQuery("<div/>").attr("id", "QuickViewDialog").html("
			// ").appendTo(jQuery("div#container"));
		},

		// sub namespace app.ajax.* contains application specific ajax
		// components
		ajax : {
			Success : "success",
			currentRequests : {}, // request cache

			// ajax request to get json response
			// @param - reqName - String - name of the request
			// @param - async - boolean - asynchronous or not
			// @param - url - String - uri for the request
			// @param - data - name/value pair data request
			// @param - callback - function - callback function to be called
			getJson : function(options) {
				var thisAjax = this;

				// do not bother if the request is already in progress
				// and let go null reqName
				if (!options.reqName || !this.currentRequests[options.reqName]) {
					this.currentRequests[options.reqName] = true;
					if (options.async == "undefined")
						options.async = true;
					// make the server call
					jQuery.ajax( {
						contentType : "application/json; charset=utf-8",
						dataType : "json",
						url : options.url,
						cache : true,
						async : options.async,
						data : options.data,

						success : function(response, textStatus) {
							thisAjax.currentRequests[options.reqName] = false;

							if (!response.Success) {
								// handle failure
					}

					options.callback(response, textStatus);
				},

				error : function(request, textStatus, error) {
					if (textStatus === "parsererror") {
						alert(app.resources["BAD_RESPONSE"]);
					}

					options.callback( {
						Success : false,
						data : {}
					});
				}
					});
				}
			},

			// ajax request to load html response in a given container
			// @param - reqName - String - name of the request
			// @param - url - String - uri for the request
			// @param - data - name/value pair data request
			// @param - callback - function - callback function to be called
			// @param - selector - string - id of the container div/span
			// (#mycontainer) - it must start with '#'
			load : function(options) {

				var thisAjax = this;

				// do not bother if the request is already in progress
				// and let go null reqname
				if (!options.reqName || !this.currentRequests[options.reqName]) {
					this.currentRequests[options.reqName] = true;
					// make the server call
					jQuery.ajax( {
						dataType : "html",
						url : options.url,
						cache : true,
						data : options.data,

						success : function(response, textStatus) {
							thisAjax.currentRequests[options.reqName] = false;

							if (options.selector) {
								jQuery(options.selector).html(response);
							}

							(options.callback != undefined ? options.callback(
									response, textStatus) : null)
						},

						error : function(request, textStatus, error) {
							if (textStatus === "parsererror") {
								alert(app.resources["BAD_RESPONSE"]);
							}

							options.callback(null, textStatus);
						}
					});
				}
			}
		},

		// loads a product into a given container div
		// params
		// containerId - id of the container div, if empty then global
		// app.containerId is used
		// source - source string e.g. search, cart etc.
		// label - label for the add to cart button, default is Add to Cart
		// url - url to get the product
		// id - id of the product to get, is optional only used when url is
		// empty
		getProduct : function(options) { // id, source, start
			var cId = options.containerId || app.containerId;
			var source = options.source || "";
			var a2cBtnLabel = options.label || null;

			// show small loading image
			jQuery("#" + cId).html(app.showProgress("productloader"));

			var productUrl = options.url ? options.url
					: app.util.appendParamToURL(app.URLs.getProductUrl, "pid",
							options.id);

			productUrl = app.util
					.appendParamToURL(productUrl, "source", source);

			app.ajax.load( {
				selector : "#" + cId,
				url : productUrl,
				callback : function(responseText, textStatus) {
					// update the Add to cart button label if one provided
				(a2cBtnLabel != null ? jQuery(
						"#" + cId + " .addtocartbutton:last").html(a2cBtnLabel)
						: '');
			}
			});
		},

		// sub name space app.minicart.* provides functionality around the mini
		// cart
		minicart : {
			url : '', // during page loading, the Demandware URL is stored
						// here
			timer : null, // timer for automatic close of cart item view

			// initializations
			init : function() {
				// reset all the existing event bindings
				app.minicart.reset();

				// bind hover event to the cart total link at the top right
				// corner
				jQuery(".minicarttotal").hover(function(e) {
					(app.minicart.isShow() ? '' : app.minicart.slide());
				});

				jQuery('.minicartcontent').mouseenter(function(e) {
					clearTimeout(app.minicart.timer);
					app.minicart.timer = null;
				}).mouseleave(function(e) {
					clearTimeout(app.minicart.timer);
					app.minicart.timer = null;
					// after a time out automatically close it
						app.minicart.timer = setTimeout('app.minicart.close()',
								30);
					});

				// register close button event
				jQuery('.minicartcontent .minicartclose').click(function() {
					// reset all the events bindings
						app.minicart.reset();
						app.minicart.close(0);
					});
			},

			// returns a boolean if a minicart is visible/shown or hidden
			isShow : function() {
				return jQuery('.minicartcontent').css('display') == 'none' ? false
						: true;
			},

			// reset minicart
			reset : function() {
				jQuery(".minicarttotal").unbind("hover");
				jQuery('.minicartcontent').unbind("mouseenter").unbind(
						"mouseleave");
				jQuery('.minicartcontent .minicartclose').unbind("click");
			},

			// shows the given content in the mini cart
			show : function(html) {
				jQuery('#minicart').html(html);

				// bind all the events
				app.minicart.init();

				if (app.minicart.suppressSlideDown
						&& app.minicart.suppressSlideDown()) {
					// do nothing
					// the hook 'MiniCart.suppressSlideDown()' should have done
					// the refresh
				} else {
					app.minicart.slide();
				}
			},

			// slide down and show the contents of the mini cart
			slide : function() {
				if (app.minicart.suppressSlideDown
						&& app.minicart.suppressSlideDown()) {
					return;
				}

				// show the item
				jQuery('.minicartcontent').slideDown('slow');// show("slide",
																// { direction:
																// "up" },
																// 1000);

				clearTimeout(app.minicart.timer);
				app.minicart.timer = null;

				// after a time out automatically close it
				app.minicart.timer = setTimeout('app.minicart.close()', 6000);
			},

			// adds a product to the mini cart
			// @params
			// progressImageSrc - source/url of the image to show when the item
			// is being added to the cart
			// postdata - form data containing the product information to be
			// added to mini-cart
			// callback - call back function/handler
			add : function(progressImageSrc, postdata, callback) {
				// get the data of the form as serialized string
				var postdata = postdata;

				// get button reference
				var addButtons = [];

				// the button to update
				var addButton = null;

				// it is an array of buttons, but we need only one all
				// other combinations are strange so far
				if (addButtons.length == 1) {
					addButton = addButtons[0];
				}

				var previousImageSrc = null;

				// show progress indicator
				if (addButton != null) {
					previousImageSrc = addButton.src;
					addButton.src = progressImageSrc;
				}

				// handles successful add to cart
				var handlerFunc = function(req) {
					// hide progress indicator
					if (addButton != null) {
						addButton.src = previousImageSrc;
					}

					// replace the content
					jQuery('#minicart').html(req);

					// bind all the events
					app.minicart.init();

					if (app.minicart.suppressSlideDown
							&& app.minicart.suppressSlideDown()) {
						// do nothing
						// the hook 'MiniCart.suppressSlideDown()' should have
						// done the refresh
					} else {
						app.minicart.slide();

						if (callback)
							callback();
					}
				}

				// handles add to cart error
				var errFunc = function(req) {
					// hide progress indicator
					if (addButton != null) {
						addButton.src = previousImageSrc;
					}
				}

				// closes a previous mini cart
				app.minicart.close();

				// add the product
				jQuery.ajax( {
					type : "POST",
					url : app.minicart.url,
					cache : true,
					data : postdata,
					success : handlerFunc,
					error : errFunc
				});
			},

			// closes the mini cart with given delay
			close : function(delay) {
				if (app.minicart.timer != null || delay == 0) {
					clearTimeout(app.minicart.timer);
					app.minicart.timer = null;
					jQuery('.minicartcontent').fadeOut(); // hide with "slide"
															// causes to fire
															// mouse enter/leave
															// events sometimes
															// infinitely thus
															// changed it to
															// fadeOut
				}
			},

			// hook which can be replaced by individual pages/page types (e.g.
			// cart)
			suppressSlideDown : function() {
				return false;
			}
		},

		// close quick view dialog if open and refresh the page
		refreshCart : function() {
			app.quickView.close();

			// refresh without posting
			location.href = location.href;
		},
//QUICK VIEW
		// Product quick view object
		quickView : {
			// bind browser events
			// options
			// buttonSelector - css selector for the quickview button
			// imageSelector - css selector for product image
			// buttonLinkSelector - css selector for quickview button link (a
			// tag)
			// productNameLinkSelector - css selector for product name link (a
			// tag)
			bindEvents : function(options) {
				// hide quickview buttons
				//jQuery(options.buttonSelector).hide();
				// hovering
				//prevent animation queue buildup
				$(options.imageSelector).live( "mouseover mouseout", function(event) {
					  if ( event.type == "mouseover" ) { //fadein/out does't work well with IE8 & PNG files
						  	if($.browser.msie && (parseInt($.browser.version) == 8 || parseInt($.browser.version) == 7))
						  		jQuery(this).children(options.buttonSelector).stop(true, true).show();	
						  	else
						  		jQuery(this).children(options.buttonSelector).stop(true, true).fadeIn();
							return false;
					  } else {
						  	if($.browser.msie && (parseInt($.browser.version) == 8 || parseInt($.browser.version) == 7))
						  		jQuery(this).children(options.buttonSelector).stop(true, true).hide();
						  	else
						  		jQuery(this).children(options.buttonSelector).stop(true, true).fadeOut();
							return false;
					  }
				});
				
				// THIS IS WHERE THE QUICKVIEW SOURCE IS SET
				// add methods for preventing animation queue buildup 
				// click binding for quick view
				jQuery(options.buttonLinkSelector).live( 'click',
								function(e) {
									
									var windowTargetWidth = parseInt($(window).width() / 2);
									var windowTargetHeight = parseInt($(window).height() / 2);
									
									var productUrl = jQuery(this).parent().siblings('div.qv_url').children('a').attr('href');
									
									var middlePos = parseInt(this.parentNode.parentNode
											.getElementsByTagName('img')[0].offsetHeight);
									var leftRightPos = parseInt(this.parentNode.parentNode
											.getElementsByTagName('img')[0].offsetWidth);
									// app.quickView.show({url: this.href,
									// source: "quickview"});

									
									$this = $(this.parentNode.parentNode);
									var p = $this.position();
									
									if (p.left > windowTargetWidth) {
										//right side
										$(options.quickViewDiv).css( {
											top : p.top - middlePos + 71, 
											left : p.left - 305
										}); //must remove class if
										if ($(options.quickViewDiv).hasClass('qv_left')){
											$(options.quickViewDiv).removeClass('qv_left');	
										}
										$(options.quickViewDiv).addClass('qv_right');
										if ( $("div#quick-shop-dialog").hasClass('qv_right') ) {
											$("div#quick-shop-dialog").html(function() {
												var newImg = "<img id='qv_load' src='"+app.URLs.loadingSmallImg+"' />";
												return '<div class="qv_rt_bg" style="display: block;"></div>' + newImg;
											});
										}
										
									} else {
										//left side
										$(options.quickViewDiv).css( {
											top : p.top - middlePos + 71,
											left : p.left + leftRightPos - 17
										});
										
										if ($(options.quickViewDiv).hasClass('qv_right')){
											$(options.quickViewDiv).removeClass('qv_right');
										}
										
										$(options.quickViewDiv).addClass('qv_left');
										
										if ( $("div#quick-shop-dialog").hasClass('qv_left') ) {
											$("div#quick-shop-dialog").html(function() {
												var newImg = "<img id='qv_load' src='"+app.URLs.loadingSmallImg+"' />";
												return '<div class="qv_lt_bg" style="display: block;"></div>' + newImg;
											});
										}
									}
									
									$.ajaxSetup( {
										async : false
									});
									
									//show div and then load content
									$(options.quickViewDiv).show(1, function() {
										// load quick shop into layer
										$(options.quickViewDiv).load(productUrl, function() {
											checkForSizable();
										});
									});
									// disabled synchronization
									$.ajaxSetup( {
										async : true
									});
									return false;
								});
			},
			// options.source - source of the dialog i.e. search/cart
			// options.url - product url
			show : function(options) {
				
				// THIS POPS UP THE QV BOX
				// jQuery('#QuickViewDialog').dialog('open');
			},
			// close the quick view dialog
			close : function() {
				
				// jQuery('.QuickViewDialog').dialog('close');
			}
		},

		// helper method to create a dialog with the given options
		// options - dialog box options along with id of the container
		createDialog : function(options) {
			jQuery('#' + options.id).dialog(
					jQuery.extend( {}, app.dialogSettings, options.options));
		},

		// shows tooltip popup
		// options
		// id - id of the container
		// options - tooltip popup options
		tooltip : function(options) {
			if (options.id.charAt(0) !== '#') {
				options.id = "#" + options.id;
			}
			jQuery(options.id).tooltip(
					jQuery.extend( {}, app.tooltipSettings, options.options));
		},

		/**
		 * Unobtrusively build tooltips on the page. it looks for a tooltip
		 * class anchor which contains a div with tooltip-body class as the body
		 * container.
		 */
		tooltipDefault : function() {
			jQuery(document).ready(
					function() {
						jQuery(".tooltip").tooltip(
								jQuery.extend( {}, app.tooltipSettings, {
									bodyHandler : function() {
										return jQuery(this).children(
												".tooltip-body").html();
									}
								}));
					});
		},

		// renders a progress indicator on the page; this function can be used
		// to indicate an ongoing progress to the user; the optional parameter
		// "className"
		// can be used to attach an additional CSS class to the container
		showProgress : function(className) {
			var clazz = "loading";
			if (className)
				clazz += " " + className;
			return jQuery("<div class=\"" + clazz + "\"/>").append(
					jQuery("<img/>").attr("src", app.URLs.loadingSmallImg));
		},

		// validation plugin intialization
		validator : function() {
			// override default required field message
			jQuery.validator.messages.required = function($1, ele, $3) {
				return "";
			};

			/**
			 * Add phone validation method to jQuery validation plugin. Text
			 * fields must have 'phone' css class to be validated as phone
			 * phoneUS is copied from
			 * http://docs.jquery.com/Plugins/Validation/CustomMethods/phoneUS
			 */
			jQuery.validator
					.addMethod(
							"phone",
							function(phone_number, element) {
								// find out the country code
								var data = jQuery(element).data("data");
								var country = (data && data.country && data.country != "") ? data.country
										: "US"; // default to US phone
												// validation

								// preserve this instance
								var that = this;

								// country specific phone validation handlers
								var phoneCA, phoneUS = phoneCA = function() {
									phone_number = phone_number.replace(/\s+/g,
											"");
									return that.optional(element)
											|| phone_number.length > 9
											&& phone_number
													.match(/^(1-?)?(\([2-9]\d{2}\)|[2-9]\d{2})-?[2-9]\d{2}-?\d{4}$/);
								}

								window["eval"]
										("var phoneHandler = (typeof phone"
												+ country
												+ " != 'undefined') ? phone"
												+ country + ": null;");

								// call the country specific phone validation
								// handler
								return (phoneHandler
										&& typeof phoneHandler == "function" ? phoneHandler()
										: true);
							}, app.resources["INVALID_PHONE"]);

			/**
			 * Add positive number validation method to jQuery validation
			 * plugin. Text fields must have 'positivenumber' css class to be
			 * validated as positivenumber it validates a number and throws
			 * error if it is below 0 or if it is not a number.
			 */
			jQuery.validator.addMethod("positivenumber", function(value,
					element) {
				if (value == '')
					return true;
				return /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(value)
						&& Number(value) >= 0;
			}, ""); // "" should be replaced with error message if needed

			// register form validator for form elements
			// except for those which are marked "suppress"
			jQuery.each(jQuery("form:not(.suppress)"), function() {
				jQuery(this).validate(app.validatorSettings);
			});
		},

		/**
		 * grab anything inside a hidden dom element and append it to its
		 * immediate previous sibling as data attribute i.e.
		 * jQuery().data("data", hiddenStr) if the hidden data specifies json in
		 * the class then this routine would attempt to convert the hidden data
		 * into json object before adding it as data attribute. after adding the
		 * data, the hidden span/element is removed from the DOM.
		 */
		hiddenData : function() {
			jQuery.each(jQuery(".hidden"), function() {
				var hiddenStr = jQuery(this).html();

				if (hiddenStr === "") {
					return;
				}

				// see if its a json string
					if (jQuery(this).hasClass("json")) {
						// try to parse it as a json
					try {
						hiddenStr = window["eval"]("(" + hiddenStr + ")");
					} catch (e) {
					}
				}

				jQuery(this).prev().data("data", hiddenStr);

				jQuery(this).remove();
			});
		},

		/**
		 * Process country drop downs and attach a change listener so that phone
		 * field can be validated properly based on the currently selected
		 * country.
		 */
		addCountryListener : function() {
			var countryHandler = function(e) {
				var selectedCountry = this.options[this.selectedIndex].value;
				// for each field of type phone in the current form, set its
				// country as a data attribute
				// to be used while doing phone field validatiaon see
				// app.validator addMethod.
				jQuery(this)
						.parents("form:first")
						.find("input.phone")
						.each(
								function() {
									var data = jQuery(this).data("data");
									var currentData = (data && typeof data == 'object') ? data
											: {};
									currentData.country = selectedCountry;
									jQuery(this).data("data", currentData);
								});
			}
			jQuery("select.country").change(countryHandler)
					.each(countryHandler);
		},

		/**
		 * Unobtrusive js api calls go here.
		 */
		execUjs : function() {
			// make global nav drop downs with superfish jquery plugin
		/*
		 * jQuery('.categorymenu ul').addClass('sf-menu');
		 * jQuery('ul.sf-menu').superfish({autoArrows : false, dropShadows :
		 * false}).find('ul').bgIframe();
		 */
		// process hidden data in the html markup and cnnvert it into data
		// object(s)
		this.hiddenData();

		// initialize form validator plugin
		this.validator();

		// process country form fields and attach listeners
		this.addCountryListener();

		// process tooltips on the page
		this.tooltipDefault();
	},

	// capture recommendation of each product when it becomes visible in the
	// carousel
		captureCarouselRecommendations : function(c, li, index, state) {
			jQuery(li).find(".captureproductid").each(function() {
				dw.ac.capture( {
					id : this.innerHTML,
					type : dw.ac.EV_PRD_RECOMMENDATION
				});
			});
		},

		// sub namespace app.producttile.* contains utility functions for
		// product tiles
		producttile : {
			// initializes all product tiles contained in the current page
			initAll : function() {
				// bind quick view button toggling and click
				var quickViewOptions = {
					buttonSelector : "div.product div.quickviewbutton",
					imageSelector : "div.product div.product_image",
					buttonLinkSelector : "div.product div.quickviewbutton img",
					quickViewDiv : "div#quick-shop-dialog"
				};

				// THIS PREPS QUICKVIEW
				app.quickView.bindEvents(quickViewOptions);
				
				// prepare swatch palettes and thumbnails
				jQuery("div.producttile div.swatches div.invisible").hide();
				jQuery("div.producttile div.swatches a.swatch img.hiddenthumbnail").hide();

				// show the palette
				jQuery("div.producttile div.swatches > a").click(function(e) {
					var cont = jQuery(this).parent().find("div.palette");
					cont.show().focus();
					return false;
				});

				// hide the palette
				jQuery("div.producttile div.swatches div.invisible").mouseout(
						function(e) {
							// fix for event bubbling
							// (http://www.quirksmode.org/js/events_mouse.html)
							if (!e)
								var e = window.event;
							var tg = (window.event) ? e.srcElement : e.target;
							if (tg.nodeName != 'DIV')
								return;
							var reltg = (e.relatedTarget) ? e.relatedTarget
									: e.toElement;
							while (reltg != tg && reltg.nodeName != 'BODY')
								reltg = reltg.parentNode
							if (reltg == tg)
								return;

							// mouseout took place when mouse actually left
							// layer
							// handle event now
							jQuery(this).hide();
							return false;
						});

				// thumb nail toggling
				jQuery("div.producttile div.swatches div.palette a.swatch")
						.bind("mouseover mouseout", function(e) {
							var swatch = jQuery(this);
							app.producttile.toggleVariationThumbnail(swatch);
						}).click(function(e) {
							var swatch = jQuery(this);
							app.producttile.selectVariation(swatch);
							// omit following the swatch link
								return false;
							});
			},

			// selects a certain variation in a product tile, replaces the
			// current image with
			// the correct variation image, changes the link to the detail
			// page and the quick view
			selectVariation : function(swatch) {
				// get the new and the original image
				var currentImg = jQuery(swatch.parents()[3]).find(
						".productimage img");
				var newImg = swatch.children("img.hiddenthumbnail");
				if (!currentImg || !newImg)
					return;

				// get the anchors
				var nameAnchor = swatch.parents(".producttile").find(".name a");
				var quickViewAnchor = swatch.parents(".producttile").find(
						".quickviewbutton a");
				var imageAnchor = swatch.parents(".producttile").find(
						".productimage a");

				// change the link url to the detail page and quick view
				var newUrl = swatch.attr("href");
				nameAnchor.attr("href", newUrl);
				quickViewAnchor.attr("href", newUrl);
				imageAnchor.attr("href", newUrl);

				// remove all current markers
				jQuery(swatch.parents()[0]).find("a.swatch").removeClass(
						"selected");

				// mark swatch as selected
				swatch.addClass("selected");
				// we just remove the markers at the images; the actual elements
				// are correct, since they were already swapped by mouse over
				currentImg.removeClass("temp original");
				newImg.removeClass("temp original");
			},

			// shows the thumb nail of a product; this function is used to
			// temporally display a new image and restore the original one
			toggleVariationThumbnail : function(swatch) {
				// get the new and the original image
				var currentImg = jQuery(swatch.parents()[3]).find(
						".productimage img");
				var newImg = swatch.children("img.hiddenthumbnail");
				if (!newImg || !currentImg)
					return;

				var selectedSwatch = jQuery(swatch.parents()[0]).find(
						"a.selected");
				var selectedImg = selectedSwatch
						.children("img.hiddenthumbnail");

				// we do nothing in case the swatch is already selected
				if (swatch.hasClass("selected"))
					return;

				if (currentImg.hasClass("temp")) {
					// current image is just a temp image, restore original
					var newCopy = selectedImg.clone().show().removeClass(
							"original hiddenthumbnail");
					currentImg.replaceWith(newCopy[0]);
				} else {
					// we create a copy of the swatch image, replace
					// the current and mark it with classes
					var newCopy = newImg.clone().show().addClass("temp")
							.removeClass("hiddenthumbnail");
					currentImg.replaceWith(newCopy[0]);
				}
			}
		},

		// sub namespace app.util.* contains utility functions
		util : {
			// disables browser auto completion for the given element
			disableAutoComplete : function(elemId) {
				jQuery("#" + elemId).attr("autocomplete", "off");
			},

			// trims a prefix from a given string, this can be used to trim
			// a certain prefix from DOM element IDs for further processing on
			// the ID
			trimPrefix : function(str, prefix) {
				return str.substring(prefix.length);
			},

			// appends the parameter with the given name and
			// value to the given url and returns the changed url
			appendParamToURL : function(url, name, value) {
				var c = "?";
				if (url.indexOf(c) != -1) {
					c = "&";
				}
				return url + c + name + "=" + encodeURIComponent(value);
			},

			// dynamically loads a CSS file
			loadCSSFile : function(url) {
				var elem = document.createElement("link");
				elem.setAttribute("rel", "stylesheet");
				elem.setAttribute("type", "text/css");
				elem.setAttribute("href", url);

				if (typeof elem != "undefined") {
					document.getElementsByTagName("head")[0].appendChild(elem);
					app.util.loadedCSSFiles.push(url);
				}
			},

			// array to keep track of the dynamically loaded CSS files
			loadedCSSFiles : [],

			// removes all dynamically loaded CSS files
			clearDynamicCSS : function() {
				for ( var i = 0; i < app.util.loadedCSSFiles.length; i++) {
					app.util.unloadCSSFile(app.util.loadedCSSFiles[i]);
				}
			},

			// dynamically unloads a CSS file
			unloadCSSFile : function(url) {
				var candidates = document.getElementsByTagName("link");
				for ( var i = candidates.length; i >= 0; i--) {
					if (candidates[i]
							&& candidates[i].getAttribute("href") != null
							&& candidates[i].getAttribute("href").indexOf(url) != -1) {
						candidates[i].parentNode.removeChild(candidates[i]);
					}
				}
			},

			// checks if cookies are enabled
			cookiesEnabled : function() {
				// first we'll split this cookie up into name/value pairs
				// note: document.cookie only returns name=value, not the other
				// components
				var all_cookies = document.cookie.split(';');
				var temp_cookie = '';
				var cookie_name = '';
				var cookie_value = '';
				var cookie_found = false; // set boolean t/f default f

				for (i = 0; i < all_cookies.length; i++) {
					// now we'll split apart each name=value pair
					temp_cookie = all_cookies[i].split('=');

					// and trim left/right whitespace while we're at it
					cookie_name = temp_cookie[0].replace(/^\s+|\s+$/g, '');

					// if the extracted name matches the session cookie name
					if (cookie_name == 'sid') {
						// we need to handle case where cookie has no value but
						// exists (no = sign, that is):
						if (temp_cookie.length > 1) {
							cookie_value = unescape(temp_cookie[1].replace(
									/^\s+|\s+$/g, ''));
						}

						if (cookie_value.length > 0) {
							cookie_found = true;
							break;
						}
					}
					temp_cookie = null;
					cookie_name = '';
				}
				return cookie_found;
			},

			/**
			 * IE 6 multiple button submit issue work around. when a form has
			 * multiple buttons of submit type, then IE 6 submits all of them
			 * whenever form is submitted. e.g. Remove on cart page would remove
			 * the wrong item (always first item in the cart) because IE 6
			 * submits all form data which includes all remove links!!! the
			 * workaorund is to disable all buttons except the one which is
			 * being clicked. it should only be called for IE 6 (check out
			 * htmlhead.isml for usage)
			 */
			ie6ButtonFix : function() {
				jQuery('button')
						.click(function() {
							// disable all buttons
								jQuery(this.form).find('button').attr(
										"disabled", true);
								// enable the one being clicked
								jQuery(this).attr("disabled", false);
							});
			}
		},

		// sub namespace app.dialog.* provides convenient functions to handle
		// dialogs
		// note, that this code relies on single dialog modals (multi dialog,
		// e.g. modal in modal is not supported)
		dialog : {
			// opens a dialog using the given url
			open : function(url, title) {
				// create the dialog container if not present already
				if (jQuery("#dialogcontainer").length == 0) {
					jQuery(document.body).append(
							"<div id=\"dialogcontainer\"></div>");
				}

				// set a default title
				title = title || "Dialog";

				// finally load the dialog, set the dialog title
				app.ajax.load( {
					selector : "#dialogcontainer",
					url : url,
					callback : function() {
						app.dialog.checkOpen();
						app.dialog.setTitle(title);
					}
				});
			},

			// initializes the dialog with common dialog actions, like closing
			// upon canceling
			// use this function in the dialog rendering template to re-bind
			// common actions
			// upon dialog reload
			init : function() {
				jQuery(document).ready(function() {
					// binds the action to all buttons defining an action
					// through the "name" attribute
						jQuery("#dialogcontainer button").each(function() {
							jQuery(this).click(function() {
								var action = jQuery(this).attr("name");
								if (action) {
									app.dialog.submit(action);
								}
								return false;
							});
						});

						// cancel button binding
						jQuery("#dialogCancelBtn").click(function() {
							app.dialog.close();
							return false;
						});
					});
			},

			// sets the title of the dialog
			setTitle : function(title) {
				jQuery("#dialogcontainer").dialog("option", "title", title);
			},

			// checks, if the dialog is in the state "open" and sets the state
			// if not presently set
			// this function is implicitly called by app.dialog.open(url, title)
			// in order to initialize
			// the dialog properly; use this function to recover the "open"
			// state of a dialog
			checkOpen : function() {
				if (!jQuery("#dialogcontainer").dialog("isOpen")) {
					jQuery("#dialogcontainer").dialog( {
						bgiframe : true,
						autoOpen : false,
						modal : true,
						overlay : {
							opacity : 0.5,
							background : "black"
						},
						height : 425,
						width : 460,
						resizable : false
					});
					jQuery("#dialogcontainer").dialog("open");
				}
			},

			// closes the dialog and triggers the "close" event for the dialog
			close : function() {
				jQuery("#dialogcontainer").dialog("close");
				jQuery(document.body).trigger("dialogClosed");
			},

			// attaches the given callback function upon dialog "close" event
			onClose : function(callback) {
				if (callback != undefined) {
					jQuery(document.body).bind("dialogClosed", callback);
				}
			},

			// triggers the "apply" event for the dialog
			triggerApply : function() {
				jQuery(document.body).trigger("dialogApplied");
			},

			// attaches the given callback function upon dialog "apply" event
			onApply : function(callback) {
				if (callback != undefined) {
					jQuery(document.body).bind("dialogApplied", callback);
				}
			},

			// triggers the "delete" event for the dialog
			triggerDelete : function() {
				jQuery(document.body).trigger("dialogDeleted");
			},

			// attaches the given callback function upon dialog "delete" event
			onDelete : function(callback) {
				if (callback != undefined) {
					jQuery(document.body).bind("dialogDeleted", callback);
				}
			},

			// submits the dialog form with the given action
			submit : function(action) {
				// set the action
				jQuery("#dialogcontainer form").append(
						"<input name=\"" + action + "\" type=\"hidden\" />");

				// serialize the form and get the post url
				var post = jQuery("#dialogcontainer form").serialize();
				var url = jQuery("#dialogcontainer form").attr("action");

				// post the data and replace current content with response
				// content
				jQuery.ajax( {
					type : "POST",
					url : url,
					data : post,
					dataType : "html",
					success : function(data) {
						jQuery("#dialogcontainer").empty().html(data);
					},
					failure : function(data) {
						alert(app.resources["SERVER_ERROR"]);
					}
				});
			}
		}
	}
})(jQuery);

/*
 * This function is called by liveperson after a user
 * has accepted a chat invitation.
 */
function jtvChatInviteAccepted(){
	if(mcgia_o){
		mcgia_o.chatInviteAccepted();
	}
}

// application initialization on dom ready
jQuery(document).ready(	function() {
	app.init();
	$('body').click( function(e) {
		var targ = e.target, i = 0;
		while (targ && ++i < 10) {
			var className = targ.className || '';
			if (className.indexOf('product_image') > -1 || className.indexOf('quick-shop-dialog') > -1) {
				return;
			}
			targ = targ.parentNode;
		}
		$('#quick-shop-dialog').css(
			'display', 'none');
		});
});

/*
 * Opens a new window with the provided url and dimension. Used for Scene7 and
 * other situations.
 * 
 * @param url the url to open @param width the window width @param height the
 * window height
 */
function openPopup(url, width, height) {
	if (url != null) {
		if (width != null && height != null) {
			window.open(url, "", "width=" + width + ", height=" + height
					+ ", scrollbars=no, resizable=yes");
		} else {
			window.open(url, "", "scrollbars=no, resizable=yes");
		}
	}
}
/* function to update parent opener */
function setOpener(url) {
	window.opener.location.href = url;
}

/*
 * Support for the compare window
 */
ProductCompare = {
	openPopup : function(url) {
		window.open(url, 'product_compare',
				'width=800,height=600,scrollbars=yes,resizable=yes', true /*
																			 * replace
																			 * history
																			 * in
																			 * the
																			 * popped
																			 * up
																			 * window
																			 */
		).focus();
	}
}

/*
 * Functionality around the mini cart.
 */
var MiniCart = {
	// flag, whether cart is open or not
	state : 0,
	formID : '',
	preventScrollDown : false,

	// during page loading, the Demandware URL is stored here
	url : '',
	
	// timer for automatic close of cart item view
	timer : null,

	setPreventScrollDown : function(boolPrevent) {
		MiniCart.preventScrollDown = boolPrevent;
	},

	cartAdd : function(form, progressImageSrc) {
		var oldsrc = jQuery(".cart_button").attr("src");
		var oldqbsrc = jQuery(".cart_button_qb").attr("src");
		var postdata = jQuery("#" + form).serialize();
		
		jQuery.ajax( {
			type : "post",
			url : MiniCart.url,
			data : postdata,
			beforeSend : function() {
				jQuery(".cart_button").attr("src", progressImageSrc).attr(
						"disabled", "disabled");
				
				jQuery(".cart_button_qb").attr("disabled", "disabled");
			},
			success : function(data, textStatus) {
				jQuery(".cart_button").attr("src", oldsrc).removeAttr("disabled");
				if (oldqbsrc != null){
					jQuery(".cart_button_qb").attr("src", oldqbsrc).removeAttr("disabled");
				}
				
			jQuery("#minicart").html( data );
		  	
		  	if(!MiniCart.preventScrollDown)
		  	{
		  		jQuery("#mc_content").slideDown();
		  		MiniCart.timer = setTimeout( 'MiniCart.cartClose()', 10000 );
		  	}
		  }
		});
		return false;
	},
	
	cartAddQB : function(form, progressImageSrc, num_items, qbURL, destination) {
		var oldsrc = jQuery(".cart_button").attr("src");
		var oldqbsrc = jQuery(".cart_button_qb").attr("src");
		
		jQuery("#qb_buttons").css('display','none');
		var postdata =  jQuery("#" + form).serialize();
		
		jQuery.ajax( {
			type : "post",
			url : qbURL,
			data : postdata,
			
			beforeSend : function() {
				jQuery(".cart_button").attr("disabled", "disabled");
				
				jQuery(".cart_button_qb").attr("disabled", "disabled");
			},
			
			success : function(data, textStatus) {
			//Note: Can end in Product Res failure.  Need to handle and populate accordingly.
				switch(data){
				case 'fail':
					//General unknown failure
					//This is the empty cart scenario, so I don't have a UI to message the user...  Let's make one.
					jQuery(".cart_button").attr("src", oldsrc).removeAttr("disabled");
					jQuery(".cart_button_qb").attr("src", oldqbsrc).removeAttr("disabled");
					
					jQuery("#QB_Message").html("").html(jQuery("#QB_Message_genfail").html());
					jQuery("#qb_buttons").css('display','none');
					jQuery("#pdblockui").html("");
					
					jQuery.blockUI({ message: $('#QB_Dialog'),
		                css: { 
			                top:  (jQuery(window).height() - 300) /2 + 'px', 
			                left: (jQuery(window).width() - 500) /2 + 'px', 
		                	width: '500px',
		                	height: '300px',
		                	cursor: 'auto'
		                }
		             });
					
					break;
				case 'success':
					//Either guest or not.  Ends well.
					window.location=destination;
					break;
				default:
					//The case for handling exceptions
					jQuery(".cart_button").attr("src", oldsrc).removeAttr("disabled");
					jQuery(".cart_button_qb").attr("src", oldqbsrc).removeAttr("disabled");
					
					
					jQuery("#pdblockui").html("");
					
					jQuery.blockUI({ message: $('#QB_Dialog'),
		                css: { 
			                top:  (jQuery(window).height() - 300) /2 + 'px', 
			                left: (jQuery(window).width() - 500) /2 + 'px', 
		                	width: '500px',
		                	height: '300px',
		                	cursor: 'auto'
		                }
		             });
					jQuery("#qb_buttons").css('display','none');
					
					jQuery('#QB_Message').html(data);
					jQuery("#minicart").html(jQuery("#pr_fail_mc").html());
					break;
				}
			}
		});
		return false;
	},
	
	cartClose: function()
	{
		if ( MiniCart.timer != null )
		{
			clearTimeout(MiniCart.timer);
			MiniCart.timer = null;
			jQuery("#mc_content").slideUp();
		}
	},

	// hook which can be replaced by individual pages/page types (e.g. cart)
	suppressSlideDown : function() {
		return false;
	}
}

var stockCheck = function() 
{
	jQuery("#hint").show();
	return false;
}

function stockHide()
{
	jQuery("#hint").hide();
}

function makeOrderable(urlSource)
{
	jQuery('.cart_button').attr('src',urlSource);
}

function showLV(url)
{
	if (navigator.userAgent != null && (navigator.userAgent.indexOf('Android') != -1 ||
			navigator.userAgent.indexOf('iPhone') != -1 || navigator.userAgent.indexOf('iPod') != -1 ||
			navigator.userAgent.indexOf('iPad') != -1))
	{
		// Don't do a popup window on Android, iPhone, iPod or iPad
		window.location = url;
	}
	else
	{
		//document.domain="jtv.com";
		//window.name="lvopener";
		window.open(url, "popup", "width=980, height=585, scrollbars=0, resizable=0, menubar=0, toolbar=0, status=0, createnew=0, location=0");
	}
	
	return false;
}
