/* All java script logic for the application. 
 *
 * The code relies on the jQuery JS library to
 * be also loaded.
 */

//create localized variable
var _app = (function(jQuery){
	if (!jQuery) {
		throw new Error("Cannot access jQuery Object. Object is invalid, null or does not exist.");
	}
	//PJN for zoom
	var extralargeURL = "";
	var largeURL = "";
	var zoomLoader = null;
	/**
	 * All script logic for product comparison.
	 * The code relies on the jQuery JS library to be also loaded.
	 */
	var products = [null, null, null, null, null, null];
	var count = 0;
	var emptyImgSrc = '';
	var emptyImgAlt = '';
	var baseButtonLabel = '';
	var confirmationMessage = '';
	var openUrl = '';
	var addUrl = '';
	var removeUrl = '';
	var suppressRefresh = false;
	var refresh = function() {
		if (suppressRefresh) {
			return;
		}
		
		var buttonLabel = baseButtonLabel;
		if (count > 0) {
			buttonLabel += ' (' + count + ')';
		}

		var compareItemsButton = jQuery('#compareItemsButton');
		compareItemsButton.html(buttonLabel);
		compareItemsButton.each(function() {
			jQuery(this).attr('disabled',(count < 2));
		});

		$('#clearComparedItemsButton').each(function() {
			jQuery(this).attr('disabled',(count < 1));
		});
		
			$('DIV.compare .compareBtn').removeClass('enabled').attr('disabled', true);
		if (count > 1) {
			$('DIV.compare input[type=checkbox]:checked').siblings('button').attr('disabled', false).addClass('enabled');
		}
		
		if (count > 0) {
			jQuery('#compareItems').show();
		} 
		else {
			jQuery('#compareItems').hide();
		}
		
		/**
		 * Gray out all unchecked compare check boxes if the maximum number of 
		 * comparable products is reached.
		 */
		if (count >= 4) {
			$('.compare input[type="checkbox"]').each(function (i) {
				if ( !this.checked ){
					this.disabled=true;
					$(this).siblings('button').attr('disabled', true);
				}
			});
		} else {
			$('.compare input[type="checkbox"]').each(function (i) {
				if ( !this.checked ){
					this.disabled=false;
					$(this).siblings('button').attr('disabled', false);
				}
			});
		}
		
	};

	var reset = function(options) {
		products = [null, null, null, null, null, null];
		count = 0;
		emptyImgSrc = options.emptyImgSrc;
		emptyImgAlt = options.emptyImgAlt;
		baseButtonLabel = options.baseButtonLabel;
		confirmationMessage = options.confirmation;
		openUrl = options.openUrl;
		addUrl = options.addUrl;
		removeUrl = options.removeUrl;

		refresh();
	};

	var addProduct = function(options) {
		var addIndex = count;
		products[addIndex] = {id:options.id, category:options.category};
		count++;
		return addIndex;
	};
	
	var setProductImage = function(options) {
		jQuery('#compareItemsProduct' + options.index).each(function() {
			var productImage = jQuery(this)[0];
			productImage.src = options.src;
			productImage.alt = options.alt;
		});

		jQuery('#compareItemsClear' + options.index).show();
	};
	/**
	 * End script logic for product comparison.
	 */
	// Global dw private data goes here	

	// dw scope public
	return {
		resources		: {},  // resource strings used in js
		constants		: {}, // platform constants, initialized in htmlhead.isml
		containerId		: "content",
		ProductCache	: null,  // app.Product object ref
		clearDivHtml	: "<div class='clear'><!-- W3C Clearing --></div>",
		currencyCodes	: {}, // holds currency code/symbol for the site
		cart			: {},
		
		// default dialog box settings	
		dialogSettings: jQuery.dialogOpt,

		// default tooltip settings
		tooltipSettings: {
				delay: 0,
				showURL: false,
				extraClass: "pretty fancy",
				top: 15,
				left: 5
		},

		// app initializations called from jQuery(document).ready at the end of the file
		init: function(base) {
			var root = base || document.body;
			
			// register initializations here
			// quick view dialog div
			if( jQuery("#QuickViewDialog").size()==0 )
				jQuery("<div/>").attr("id", "QuickViewDialog").html(" ").appendTo(document.body);

			// micicart object initialization
			app.minicart.init();

			// Add call to Demandware Javascript function for Active Merchandising
			// capture recommendation of each product when it becomes visible in the carousel on Page Load
			if (jQuery('.carousel.oneproduct .carousel-container .captureproductid').text() != "") {
				for (var i = 0 ; i < 1; i++) {
					var pid = jQuery('.carousel.oneproduct .carousel-container .captureproductid:eq(' + i + ')').text();
					try {
						dw.ac.capture({id:pid, type:dw.ac.EV_PRD_RECOMMENDATION});
					} catch(err) {
						if(window.location.href.indexOf('demandware.net') > -1)
							alert("Demandware dw.ac.capture failed: "+err);
					}
				}
			}
			
			if (jQuery('.carousel.twoproduct .carousel-container .captureproductid').text() != "") {
				for (var i = 0 ; i < 2; i++) {
					var pid = jQuery('.carousel.twoproduct .carousel-container .captureproductid:eq(' + i + ')').text();
					try {
						dw.ac.capture({id:pid, type:dw.ac.EV_PRD_RECOMMENDATION});
					} catch(err) {
						if(window.location.href.indexOf('demandware.net') > -1)
							alert("Demandware dw.ac.capture failed: "+err);
					}
				}
			}
			
			if (jQuery('.carousel.threeproduct .carousel-container .captureproductid').text() != "") {
				for (var i = 0 ; i < 3; i++) {
					var pid = jQuery('.carousel.threeproduct .carousel-container .captureproductid:eq(' + i + ')').text();
					try {
						dw.ac.capture({id:pid, type:dw.ac.EV_PRD_RECOMMENDATION});
					} catch(err) {
						if(window.location.href.indexOf('demandware.net') > -1)
							alert("Demandware dw.ac.capture failed: "+err);
					}
				}
			}	
			
			if (jQuery('.carousel.fourproduct .carousel-container .captureproductid').text() != "") {
				for (var i = 0 ; i < 4; i++) {
					var pid = jQuery('.carousel.fourproduct .carousel-container .captureproductid:eq(' + i + ')').text();
					try {
						dw.ac.capture({id:pid, type:dw.ac.EV_PRD_RECOMMENDATION});
					} catch(err) {
						if(window.location.href.indexOf('demandware.net') > -1)
							alert("Demandware dw.ac.capture failed: "+err);
					}
				}
			}
		},
	
		// 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('bad response, parser error='+request+", options.url="+options.url);
							}
							
							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) {


				//jQuery(options.selector).load(options.url, options.data, options.callback);

				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('bad response, parser error');
							}

							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+" .addtocart_button:last").html(a2cBtnLabel) : '');
			}});
		},

		// sub name space app.minicart.* provides functionality around the mini cart
		minicart: {
			state : 0, // flag, whether cart is open or not
			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').show();//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
			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;
					}
					
					if(req.indexOf('NoInventory_NoInventory') > 0)
					{
						//alert("no Inventory");
						//Dialog.alertNoButtons(req.responseText.replace('NoInventory_NoInventory', ''), {windowParameters: {className: '',height: 400, width: 500}, okLabel: 'Close'});
						//return false;
						req = req.replace('NoInventory_NoInventory', '');
					}
					// replace the content
					//jQuery('#QuickViewDialog').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();
						minicartShow(req);
						jQuery('#QuickViewDialog').fadeTo('fast',1.0);
						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;
		},

		// Product quick view object
		quickView: {

			bindEvents: function(options) {
				// hide quickview buttons
				jQuery(options.buttonSelector).hide();

				// hovering
				jQuery(options.imageSelector).hover(
					function(e) {
						jQuery(this).children(options.buttonSelector).show();
						return false;
					},
					function(e) {
						jQuery(this).children(options.buttonSelector).hide();
						return false;
					}
				);

				// click binding for quick view
				//jQuery(options.buttonLinkSelector).click(function(e) {
				//	app.quickView.show({url: this.href, source: "quickview"});
				//	return false;
				//});

				/*
				To make bookmarking and browser back-button work correctly the browser URL needs 
				to change. To force that change we do a full-page load (not AJAX) when going from 
				search result page to product detail page.
				The implementation supports loading the product detail content with AJAX: just 
				uncomment this code block to bind the event handler.
				
				// click binding for name link
				if(options.productNameLinkSelector) {
					jQuery(options.productNameLinkSelector).click(function(e) {
						app.getProduct({url: this.href, source: "search"});
						return false;
					});
				}
				*/
			},

			// show quick view dialog and send request to the server to get the product
			// options.source - source of the dialog i.e. search/cart
			// options.url - product url
			show: function(options) {
				app.createDialog({id: 'QuickViewDialog', options: {
					height: 530,
					width: 800,
					dialogClass: 'quickview',
					title: 'Product Quickview',
					resizable: false
				}});

			    jQuery('#QuickViewDialog').dialog('open');
			    app.getProduct({containerId: "QuickViewDialog", source: options.source, url: options.url, label: options.label});
			},
			// close the quick view dialog
			close: function() {
				jQuery('#QuickViewDialog').dialog('close');
			}
		},


		createDialog: function(options) {
			//id dialog container doesnt exists, then add
			if (jQuery("#"+options.id).length == 0) jQuery("<div id='"+options.id+"' />").appendTo(document.body);
			
			jQuery('#'+options.id).dialog(jQuery.extend(true, {}, app.dialogSettings, options.options));
		},

		// 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
		// TODO: Remove ShowBusy implementation Site wide
		showBusy : function(className) {
			var clazz = "busy";
			if (className) clazz += " " + className;
			return jQuery("<div class='" + clazz + "'/>");
		},

		/*
		 *	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
		 */
		openPopup: function( 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");
				}
			}
		},
		// 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.producttile div.quickview-button",
					imageSelector: "div.producttile div.image",
					buttonLinkSelector: "div.producttile div.quickview-button a"
				};
				//app.quickView.bindEvents(quickViewOptions);
				
				// prepare swatch palettes and thumbnails
				jQuery("div.producttile div.swatches div.hidden").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.hidden").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);
				});
				
				// color swatch selection
				jQuery("div.producttile div.swatches div.palette a.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(".quickview-button 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);
				
				// 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;
					
				// 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 and reset temp image
					var currentCopy = currentImg.clone().hide().removeClass("temp").addClass("hiddenthumbnail");
					var newCopy = newImg.clone().show().removeClass("original hiddenthumbnail");
					currentImg.replaceWith(newCopy[0]);
					newImg.replaceWith(currentCopy[0]);
				} else {
					// we create copies of each image, replace
					// the current with the copies and mark them with classes
					var currentCopy = currentImg.clone().hide().addClass("original hiddenthumbnail");
					var newCopy = newImg.clone().show().addClass("temp").removeClass("hiddenthumbnail");
					currentImg.replaceWith(newCopy[0]);
					newImg.replaceWith(currentCopy[0]);
				}
			}
		},

		// sub namespace app.util.* contains utility functions
		util : {
			multiUpdate : function(elemId, url, mapping) {
				var element = jQuery(elemId);
				jQuery.ajax({
					dataType: "html",
					url		: url,
					cache	: true,

					success: function(response, textStatus) {
						var responseNode = jQuery(response);
						var entries = mapping.split(",");
						for( var i=0, ii=entries.length; i<ii; i++ ) {
							var sourceTarget = entries[i].split(":");
							if( sourceTarget.length == 1 ) {
								jQuery(sourceTarget[0]).html(responseNode.find(sourceTarget[0]).html());
							} else if( sourceTarget == 2 ) {
								jQuery(sourceTarget[1]).html(responseNode.find(sourceTarget[0]).html());
							}
						}
					},

					error: function(request, textStatus, error) {
						if (textStatus === "parsererror") {								
							alert('bad response, parser error');
						}
					}
				});
				
			},
			
			// 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);
			}
		},

		// 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({
						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("destroy");
				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("Server connection failed!");
				   }
				});
			}
		},
		/*
		 * All java script logic for the search suggestions.
		 *
		 * The code relies on the jQuery JS library to
		 * be also loaded.
		 *
		 * The logic extends the JS namespace app.*
		 */
		searchsuggest : {
			// configuration parameters and required object instances
			acListTotal   :  0,
			acListCurrent : -1,
			acDelay       : 300,
			acURL         : null,
			acFormId      : null,
			acSearchId	  : null,
			acResultsId	  : null,
			acSearchField : null,
			acResultsDiv  : null,
			fieldDefault  : null,
			
			init : function(formId, fieldId, fieldDefault, resultsId, url) {
				// initialize vars
				app.searchsuggest.acFormId = "#" + formId;
				app.searchsuggest.acSearchId = "#" + fieldId;
				app.searchsuggest.acResultsId = "#" + resultsId;
				app.searchsuggest.acURL = url;
				app.searchsuggest.fieldDefault = fieldDefault;
				
				// disable browser auto comlete
				app.util.disableAutoComplete(fieldId);
				
				// create the results div
				jQuery("body").append("<div id='" + resultsId + "'></div>");
			
				// register mostly used vars (jQuery object)
				app.searchsuggest.acSearchField = jQuery(app.searchsuggest.acSearchId);
				app.searchsuggest.acResultsDiv = jQuery(app.searchsuggest.acResultsId);
			
				// reposition div
				app.searchsuggest.repositionResultsDiv();
			
				// on blur listener
				app.searchsuggest.acSearchField.blur(function(){ setTimeout("app.searchsuggest.clear()", 200) });
			
				// on key up listener
				app.searchsuggest.acSearchField.keyup(function(e) {
					// get keyCode (window.event is for IE)
					var keyCode = e.keyCode || window.event.keyCode;
					var lastVal = app.searchsuggest.acSearchField.val();
					// check an treat up and down arrows
					if(app.searchsuggest.updownArrow(keyCode)){
						return;
					}
					// check for an ENTER or ESC
					if(keyCode == 13 || keyCode == 27) {
						app.searchsuggest.clear();
						return;
					}
					
					// if is text, call with delay
					setTimeout(function() { app.searchsuggest.suggest(lastVal) }, app.searchsuggest.acDelay);
				});
				
				// on focus listener (clear default value)
				app.searchsuggest.acSearchField.focus(function() {
					var val = app.searchsuggest.acSearchField.val();
					if(val == app.searchsuggest.fieldDefault)
					{
						app.searchsuggest.acSearchField.val("");
					}
				});
			},
			
			// trigger suggest action
			suggest : function(lastValue)
			{
				// get the field value
				var part = app.searchsuggest.acSearchField.val();
			
				// if it's empty clear the resuts box and return
				if(part == "") {
					app.searchsuggest.clear();
					return;
				}
			
				// if it's equal the value from the time of the call, allow
				if(lastValue != part) {
					return;
				}
				
				// build the request url
				var reqUrl = app.util.appendParamToURL(app.searchsuggest.acURL, "q", part);
				
				// get remote data as JSON
				jQuery.getJSON(reqUrl, function(json) {
					// get the total of results
					var ansLength = app.searchsuggest.acListTotal = json.suggestions.length;
			
					// if there are results populate the results div
					if(ansLength > 0) {
			
						var newData = "";
						// create a div for each result
						for(i=0; i < ansLength; i++) {
							newData += "<div class='unselected'><div class='suggestionterm'>" + json.suggestions[i].suggestion + "</div>";
							newData += "<span class='hits'>" + json.suggestions[i].hits + "</span></div>";
						}
						// update the results div
						app.searchsuggest.acResultsDiv.html(newData);
						app.searchsuggest.acResultsDiv.css("display","block");
						
						// for all divs in results
						var divs = jQuery(app.searchsuggest.acResultsId + " > div");
						
						// on mouse over clean previous selected and set a new one
						divs.mouseover( function() {
							divs.each(function(){ this.className = "unselected"; });
							this.className = "selected";
						});
						
						// on click copy suggestion to search field, hide the list and submit the search
						divs.click( function() {
							app.searchsuggest.acSearchField.val(this.childNodes[0].innerHTML);
							app.searchsuggest.clear();
							jQuery(app.searchsuggest.acFormId).submit();
						});
					} else {
						app.searchsuggest.clear();
					}
				});
			},
			
			// clear suggestions
			clear : function()
			{
				app.searchsuggest.acResultsDiv.html("");
				app.searchsuggest.acResultsDiv.css("display","none");
			},
			
			// reposition the results div accordingly to the search field
			repositionResultsDiv : function()
			{
				// get the input position
				var inPos = app.searchsuggest.acSearchField.offset();
				var inTop = inPos.top;
				var inLeft = inPos.left;
				
				// get the field size
				var inHeight = app.searchsuggest.acSearchField.height();
				var inWidth = app.searchsuggest.acSearchField.width();
				
				// apply the css styles
				app.searchsuggest.acResultsDiv.addClass("suggestions");
				app.searchsuggest.acResultsDiv.css("position","absolute");
				app.searchsuggest.acResultsDiv.css("left", inLeft); // to tweak
				app.searchsuggest.acResultsDiv.css("top", inTop + inHeight + 4);
				app.searchsuggest.acResultsDiv.css("width", inWidth + 125); // to tweak
				app.searchsuggest.acResultsDiv.css("z-index", "7777");
			},
			
			// treat up and down key strokes defining the next selected element
			updownArrow : function(keyCode) {
				if(keyCode == 40 || keyCode == 38) {
					if(keyCode == 38) { // keyUp
						if(app.searchsuggest.acListCurrent == 0 || app.searchsuggest.acListCurrent == -1) {
							app.searchsuggest.acListCurrent = app.searchsuggest.acListTotal-1;
						} else {
							app.searchsuggest.acListCurrent--;
						}
					} else { // keyDown
						if(app.searchsuggest.acListCurrent == app.searchsuggest.acListTotal-1) {
							app.searchsuggest.acListCurrent = 0;
						} else {
							app.searchsuggest.acListCurrent++;
						}
					}
					
					// loop through each result div applying the correct style
					app.searchsuggest.acResultsDiv.children().each(function(i) {
						if(i == app.searchsuggest.acListCurrent) {
							app.searchsuggest.acSearchField.val(this.childNodes[0].innerHTML);
							this.className = "selected";
						} else {
							this.className = "unselected";
						}
					});
					return true;
				} else {
					// reset
					app.searchsuggest.acListCurrent = -1;
					return false;
				}
			}
		}, // end searchsuggest

		/**
		 * All script logic for product comparison.
		 * The code relies on the jQuery JS library to be also loaded.
		 */
		compare : {
			initialize: function(options) {
				reset(options);

				// Buttons to remove compared products individually	
				for (var i = 0; i < 6; i++) {
					// new Function() syntax ensures loop index is used correctly
					jQuery('#compareItemsClear' + i).click(new Function('app.compare.removeProduct({index: ' + i + '})'));
				}
	
				// Button to go to compare page
				jQuery('#compareItemsButton').click(function() {
					if (count>1) {
						window.location.href = options.openUrl;
					}
				});

				// Button to go to compare page
				$('.compareBtn').click(function() {
					if ($(this).hasClass('enabled') && count>1) {
						window.location.href = options.openUrl;
					}
				});

				// Button to clear all compared items
				jQuery('#clearComparedItemsButton').click(function() {
					suppressRefresh = true;

					jQuery('#compareItems').hide();
					
					for (var i = count - 1; i >= 0; i--) {
						app.compare.removeProduct({index: i});
					}

					suppressRefresh = false;

					refresh();
				});
	
				// Check checkboxes for compared products on the current page 
				for (var i = 0; i < options.products.length; i++) {
					var product = options.products[i];
					var addIndex = addProduct({id:product.id, category:product.category});
					setProductImage({index:addIndex, src:product.imgSrc, alt:product.imgAlt});
	
					jQuery('#compareCheck' + product.id).each(function() {
						jQuery(this)[0].checked = true;
						jQuery(this).addClass("compared");
					});
				}
	
				refresh();
			},
	
			addProduct: function(options){
				if (count >= 4) {
					if (!confirm(confirmationMessage)) {
						jQuery('#compareCheck' + options.id).each(function() {
							jQuery(this)[0].checked = false;
						});
						return;
					}
					app.compare.removeProduct({index: 0});
				} 
		
				var complete = function() {
					var addIndex = addProduct(options);
		
					jQuery('#productThumbnail' + options.id).each(function() {
						var thumbnail = jQuery(this)[0];
						setProductImage({index:addIndex, src:thumbnail.src, alt:thumbnail.alt});
					});
	
					refresh();
				};
		
				var uncheck = function() {
					jQuery('#compareCheck' + options.id).each(function() {
						jQuery(this)[0].checked = false;
						jQuery(this).removeClass("compared");
					});
				};
	
				jQuery.ajax({
					type: 'POST',
					url: addUrl,
					data: {'pid':options.id, 'category':options.category},
					dataType: 'json',
					success: function(data){
						if (data.success === true) {
							complete();
						}
						else {
							uncheck();
						}
					},
					failure: function(data) {
						uncheck();
					}
				});
			},
	
			removeProduct: function(options) {
				var index = null;
				if (options.index != null) {
					index = options.index;
				}
				else {
					for (var i = 0; i < count; i++) {
						if (products[i].id === options.id) {
							index = i;
						}
					}
				}
	
				var clearedProduct = products[index];
	
				var complete = function() {
					for (var i = index; i < count - 1; i++) {
						products[i] = products[i + 1];
	
						jQuery('#productThumbnail' + products[i].id).each(function() {
							var thumbnail = jQuery(this)[0];
	
							jQuery('#compareItemsProduct' + i).each(function() {
								var productImage = jQuery(this)[0];
								productImage.src = thumbnail.src;
								productImage.alt = thumbnail.alt;
							});
						});
					}
	
					var clearedIndex = count - 1;
					products[clearedIndex] = null;
					count--;
			
					jQuery('#compareCheck' + clearedProduct.id).each(function() {
						jQuery(this)[0].checked = false;
						jQuery(this).removeClass("compared");
					});
			
					jQuery('#compareItemsProduct' + clearedIndex).each(function() {
						var productImage = jQuery(this)[0];
						productImage.src = emptyImgSrc;
						productImage.alt = emptyImgAlt;
					});
	
					jQuery('#compareItemsClear' + clearedIndex).hide();
			
					refresh();
				};
	
				var check = function() {
					jQuery('#compareCheck' + clearedProduct.id).each(function() {
						jQuery(this)[0].checked = true;
					});
				};
	
				jQuery.ajax({
					type: 'POST',
					url: removeUrl,
					data: {'pid':clearedProduct.id, 'category':clearedProduct.category},
					dataType: 'json',
					success: function(data){
						if (data.success === true) {
							complete();
						} else {
							check();
						}
					},
					failure: function(data) {
						check();
					}
				});
			}
		}, // end compare
		/*
		 * All java script logic for the search result and refinement
		 * handling.
		 *
		 * It contains all the client side script code for the search
		 * result UI interactions.
		 *
		 * The code relies on the jQuery JS library to
		 * be also loaded.
		 * 
		 * The logic extends the JS namespace app.*
		 */
		search : {
			
			// the current search result
			result : null,
			currentPageSize : 12,
			
			// add click handler to each refinement link
			bindClickToAllRefineLinks : function(root) {
				var base = root||document.body;
								
				// bind breadcrumb clicks
				jQuery("div.breadcrumb a.relax",base).click(function(e) {
					var url = jQuery(this).attr("href");
					app.search.result.updateGrid( url );
					return false;
				});
				
				// non-category refinements
				jQuery("div.refine_attributes ul li a",base).click(function(e) {
					app.search.result.toggleRefine(this);
					return false;
				});
			
				// bind clear events
				jQuery("#search-refinements div.refinedclear a",base).click(function(e) {
					var refID = jQuery(this).parents()[1].id;
					app.search.result.clearRefinement(app.search.result.get(refID));
					return false;
				});
				
				// prepare swatch palettes and thumbnails
				jQuery("#content div.swatches div.hidden",base).hide();
				jQuery("#content div.swatches a.swatch img.hiddenthumbnail",base).hide();
				// show the palette
				jQuery("#content div.swatches > a",base).click(function(e) {
					var cont = jQuery(this).parent().find("div.palette");
					cont.show().focus();
					return false;
				});
				// hide the palette
				jQuery("#content div.swatches div.hidden",base).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;
				});
				// toggle thumbnail
				jQuery("#content div.swatches div.palette a.swatch",base).bind("mouseover mouseout", function(e) {
					var swatch = jQuery(this);
					app.producttile.toggleVariationThumbnail(swatch);
				});
				// change thumbnail
				jQuery("#content div.swatches div.palette a.swatch",base).click(function(e) {
					var swatch = jQuery(this);
					app.producttile.selectVariation(swatch);
					// omit following the swatch link
					return false;
				});
			},
			
			// add click handler to each pagination link (only refreshes grid)
			bindClickToPaginationLinks : function(root) {
				var base = root||document.body;
				
				jQuery("#search div.pagination a",base).click(function(e) {
					var pageName = jQuery(this).attr("class");
					app.search.result.pageTo( pageName );
					return false;
				});
			},
			
			// add change handler for sortby menu
			bindChangeToSortBy : function(root) {
				var base = root||document.body;
				
				jQuery("div.sortby select",base).change(function(e) {
					var url = jQuery(this).val();
					app.search.result.updateGrid( url );
					return false;
				});
			},
			
			// add change handler for items per page menu
			bindChangeToItemsPerPage : function(root) {
				var base = root||document.body;
				
				jQuery("div.itemsperpage select",base).change(function(e) {
					var url = jQuery(this).val();
				 	var hash = url.split("?")[1];
				 	if(!hash) hash = url;
				 	var hash_parts = hash.split('&');
				 	var parameter = null;
					for(var i=0, ii=hash_parts.length; i<ii; i++ ) {
						parameter = hash_parts[i];
						var parameterSplit = parameter.split('=');
						if (parameterSplit.length == 2 && parameterSplit[0] == 'sz') {
							app.search.currentPageSize = parameterSplit[1];
						}
					}
					app.search.result.updateGrid( url );
					return false;
				});
			},
			
			// updates all bindings for changed DOM elements
			updateRefineBindings : function(root) {
				var base = root||document.body;
				
				app.search.bindClickToAllRefineLinks(base);
				app.search.bindClickToPaginationLinks(base);
				app.search.bindChangeToSortBy(base);
				app.search.bindChangeToItemsPerPage(base);
			},

			// search result object
			SearchResult : function(keywordSearch, categoryID)
			{
				this.refinements = [];
				this.initialized = false;
				this.keywordSearch = keywordSearch;
				this.categoryID = null;
				this.loading = false;
			
				// retrieves a registered refinement by id
				this.get = function(id)
				{
					for(var i=0; i<this.refinements.length; i++)
					{
						if(this.refinements[i].id == id) return this.refinements[i];
					}
					return null;
				};
			
				// registers and adds the a refinement to the given position
				// if it is not already registered and renders the new refinement
				this.register = function(dwRefinement, pos)
				{		
					if(pos < 1) return;
					
					// check if registered at position
					if(this.isRegistered(dwRefinement, pos) && this.initialized)
					{
						this.refresh(dwRefinement);
						this.updateValues(dwRefinement);
						this.updateClear(dwRefinement);
						return;
					}
					// check if registered in general
					else if(this.isRegistered(dwRefinement) && this.initialized)
					{
						this.moveRefinement(dwRefinement, pos);
						this.refresh(dwRefinement);
						this.updateValues(dwRefinement);
						this.updateClear(dwRefinement);
						return;
					}
					
					// register the refinement at the correct position
					dwRefinement.refreshed = true;
					
					this.refinements.splice(pos-1, 0, dwRefinement);
					
					if(this.initialized)
					{
						// render the refinement based on the refinement type
						this.renderRefinement(dwRefinement, pos);
					}
				};
			
				// refreshes the given refinement
				this.refresh = function(dwRefinement)
				{
					for(var i=0; i<this.refinements.length; i++)
					{
						if(this.refinements[i].id != dwRefinement.id) continue;
						
						this.refinements[i].refreshed = true;
						return;
					}
				};
			
				// removes the given refinement from the DOM
				this.removeRefinement = function(dwRefinement)
				{
					jQuery("#"+dwRefinement.id).remove();
				};
			
				// checks if the refinement is registered at the (optional) given position
				// if the position is not given, the function check if the refinement is registered in general
				this.isRegistered = function(dwRefinement, pos)
				{
					for(var i=0; i<this.refinements.length; i++)
					{
						if((this.refinements[i].id == dwRefinement.id) && pos == null) return true;
						if((this.refinements[i].id == dwRefinement.id) && (pos-1) == i)  return true;
					}
					return false;
				};
			
				// renders an individual refinement at the given position into the DOM
				this.renderRefinement = function(dwRefinement, pos)
				{
					// insert at correct position
					var refSet = jQuery("div.refinement");
					if(refSet.length == 0 || refSet.length < pos-1) pos = 1;
						
					// get the correct predecessor refinement container
					var predecessorID = null;
					if( typeof(this.refinements[pos-2]) != "undefined" )
					{
						predecessorID = this.refinements[pos-2].id;
					}
							
					// insert after found predecessor
					if( predecessorID != null )
					{
						jQuery("#" + predecessorID).after( this.buildHtmlRefinement(dwRefinement) );
					}
					// insert at first position
					else
					{
						jQuery("#search-refinements").prepend( this.buildHtmlRefinement(dwRefinement) );
					}
					
					// bind toggling
					this.bindToggleEvent(dwRefinement);
					
					// append list update values
					this.updateValues(dwRefinement);
				};
			
				// binds the toggling event
				this.bindToggleEvent = function(dwRefinement)
				{
//						jQuery("#"+dwRefinement.id+" h3").click(function(e) {
//							jQuery(this).toggleClass("collapsed");
//							jQuery(this).nextAll("div.refine_attributes").toggle();
//						});
				};
			
				// bind event handlers to value list
				this.bindRefineEvents = function(dwRefinement)
				{
					if(dwRefinement.type == "category")
					{
						/*
						To make bookmarking and browser back-button work correctly the browser URL 
						needs to change. To force that change we do a full-page load (not AJAX) when 
						changing the category refinement.
						The implementation supports changing the category with AJAX: just uncomment 
						this code block to bind the event handlers.
					
						// category refinements
						jQuery("#"+dwRefinement.id+" ul li a").click(function(e) {
							var url = jQuery(this).attr("href");
							app.search.result.refine( "refinement-category", url );
							return false;
						});
						jQuery("div.search_category span a").click(function(e) {
							var url = jQuery(this).attr("href");
							app.search.result.refine( "refinement-category", url );
							return false;
						});
						*/
						return;
					}
					else
					{
						// non-category refinements
						jQuery("#"+dwRefinement.id+" ul li a").click(function(e) {			
							app.search.result.toggleRefine(this);
							return false;
						});
						return;
					}
				};
			
				// builds the actual html code for the refinement based on the type
				this.buildHtmlRefinement = function(dwRefinement)
				{
					var html = "";
					
					if(dwRefinement.type == "category")
					{
						html = "<div id='" + dwRefinement.id + "' class='search_categories refinement'>" +
							"<ul id='category-level-1' class='refinement_category'><\/ul><\/div>";
					}
					else
					{
						var html = "<div id='" + dwRefinement.id + "' class='navgroup refinement'>" +
							"<h3>" + dwRefinement.displayName + "</h3><div class='refinedclear'><\/div>";
						
						html += "<div class='refine_attributes'>";
						
						// type based html code here
						if(dwRefinement.swatchBased)
						{
							html += "<div class='swatches " + dwRefinement.displayName + "'><ul><\/ul><\/div><div class='clear'><\/div>";
						}
						else
						{
							html += "<div><ul><\/ul><\/div>";
						}
						
						html += "<\/div>";
					}
					return html;
				};
			
				// moves an already registered refinement (no need to render fully) from
				// one position to another in the refinements
				this.moveRefinement = function(dwRefinement, positionTo)
				{
					var currentIdx = null;
					var newIdx = positionTo-1;
					
					// new position exceeds array size
					if(newIdx > this.refinements.length) return;
					
					// find current position
					for(var i=0; i<this.refinements.length; i++)
					{
						if(this.refinements[i].id == dwRefinement.id)
						{
							currentIdx = i;
							break;
						}
					}
					
					// not found
					if(currentIdx == null) return;
					
					// exchange refinements in array
					this.refinements[currentIdx] = this.refinements[newIdx];
					this.refinements[newIdx] = dwRefinement;
				};
			
				// updates and renders the list of values of the given refinement
				this.updateValues = function(dwRefinement)
				{
					// remove the existing list of values
					jQuery("#"+dwRefinement.id+" ul").empty();
					
					// toggle scrollbox for long value lists
					if(dwRefinement.type == "attribute" && !dwRefinement.swatchBased && dwRefinement.cutoffThreshold != null)
					{
						if(dwRefinement.values.length > dwRefinement.cutoffThreshold)
						{
							jQuery("#"+dwRefinement.id+" div.refine_attributes").children().addClass("scrollable");
						}
						else
						{
							jQuery("#"+dwRefinement.id+" div.refine_attributes").children().removeClass("scrollable");
						}
					}
					
					// render refinement label for category
					if(dwRefinement.type == "category" && this.keywordSearch && this.categoryID == null)
					{
						jQuery("div.search_category").empty();
						jQuery("div.search_category").append("<span>"+dwRefinement.displayName+"</span>");
					}
					
					// no list element ID by default
					var listElemID = "";
					
					// add new values to the list
					for(var i=0; i<dwRefinement.values.length; i++)
					{
						var value = dwRefinement.values[i];
						
						// render top level category differrent in case we have a keyword search
						if(i == 0 && dwRefinement.type == "category" && this.keywordSearch && this.categoryID != null)
						{
							jQuery("div.search_category").empty();
							jQuery("div.search_category").append("<span><a href='"+value.refineUrl+"' class='search_categories'>"+value.displayValue+"</a></span> (<a href='"+dwRefinement.clearUrl+"' class='clear'>View All<\/a>)");
							continue;
						}
						
						// map correct classes
						var aClass = "refineLink";
						if(dwRefinement.swatchBased) aClass = "swatchRefineLink";
						
						var aID = dwRefinement.getValueElementID(value);
						var aUrl = value.refineUrl;
						var liClass = null;
						if(value.refined && dwRefinement.type != "category") { aUrl = value.relaxUrl; liClass = "selected"; }
						if(value.active && dwRefinement.type == "category") { aClass += " active"; liClass = "active"; }
						if(!value.selectable) { aUrl = null; liClass = "unselectable"; }
						
						if(dwRefinement.type == "category" && value.expandable)
						{
							if(liClass != null)
							{
								liClass += " expandable";
							}
							else
							{
								liClass = "expandable";
							}
						}
						
						// get the correct list element ID
						if(dwRefinement.type == "category")
						{
							listElemID = "category-level-" + value.level;
							
							// check on existence of this list element
							// create it on the fly if it does not not exist at present
							if(jQuery("#"+dwRefinement.id+" ul#"+listElemID).length == 0)
							{
								// get the correct node where to insert new list
								var parentListElemID = "category-level-" + (value.level - 1);
								jQuery("#"+dwRefinement.id+" ul#"+parentListElemID+" li.active").append("<ul id='"+listElemID+"' class='refinement_category'><\/ul>");
							}
						}
						else
						{
							listElemID = "";
						}
						
						jQuery("#"+dwRefinement.id+" ul" + (listElemID != "" ? "#" + listElemID : "")).append("<li" + (liClass != null ? " class='" + liClass + "'" : "") + "><a " + (aID != null ? " id='" + aID + "'" : "") + " class='" + aClass + "' " + (aUrl != null ? " href='" + aUrl + "'" : "") + ">" + value.displayValue + "<\/a><\/li>");
					}
					
					// bind event handlers to new value list
					this.bindRefineEvents(dwRefinement);
				};
			
				// updates the clear link of the given refinement
				this.updateClear = function(dwRefinement)
				{
					if(jQuery("#"+dwRefinement.id+" div.refinedclear a").length == 1)
					{
						jQuery("#"+dwRefinement.id+" div.refinedclear").empty();
						jQuery("#"+dwRefinement.id+" div.refinedclear").append("<a href='" + dwRefinement.clearUrl + "'>Clear All<\/a>");
					}
				}
			
				// cleans all refinements based on their refreshed state
				// removes all refinements which haven't been refreshed during
				// a call of method register(..)
				this.clean = function()
				{
					// set state to initialized
					// do nothing unless initialized
					if( !this.initialized )
					{
						this.initialized = true;
						return;	
					}
					
					var removeIdx = [];
					
					// find all refinements to remove
					for(var i=0; i<this.refinements.length; i++)
					{
						if(this.refinements[i].refreshed) continue;
						
						removeIdx[removeIdx.length] = i;
					}
					
					// remove orphaned clear links for refinements which have been relaxed completely
					for(var i=0; i<this.refinements.length; i++)
					{
						// only process active refinements
						if(this.refinements[i].refreshed) {
							// remove clear link, in case there are no other selected values for this refinement
							if(jQuery("#"+this.refinements[i].id+" ul li.selected").length == 0)
							{
								jQuery("#"+this.refinements[i].id+" div.refinedclear").empty();
							}
						}
					}
					
					// no refinements to remove
					if(removeIdx.length == 0) {
						return;
					}
					
					// remove refinements and clear their index positions
					for(var j=removeIdx.length-1; j>=0; j--)
					{
						// remove from DOM and from register
						this.removeRefinement(this.refinements[removeIdx[j]]);
						this.refinements.splice(removeIdx[j], 1);
					}
				};
			
				// sets the state of all refinements to be refreshed
				// this method must be called whenever the user changes the category refinement (refine or relax)
				this.outdate = function()
				{
					// mark all refinements as to be refreshed
					for(var i=0; i<this.refinements.length; i++)
					{
						this.refinements[i].refreshed = false;
					}
				};
			
				// toggles a clicked refinement value based on its given state (selected or not selected)
				// and executes the correct action (either a refine or a relax)
				this.toggleRefine = function(objRef)
				{
					if(typeof objRef == "undefined" || objRef == null) return;
					
					// omit toggling if currently loading
					if(this.loading) return;
					
					var url = jQuery(objRef).attr("href");
					var refID = jQuery(objRef).parents('.navgroup')[0].id;
					
					// avoid IE following non link anchors
					if(!url) return;
					
					if( refID=="refinement-gender-hand" ) {
						// handle relax	
						if(jQuery(objRef).parent().hasClass("selected"))
						{
							jQuery(objRef).parent().removeClass("selected");
							app.search.result.relax( refID, url );
							return;
						}
						
						// handle refine
						jQuery(objRef).parent().siblings().removeClass("selected");
						jQuery(objRef).parent().addClass("selected");
						app.search.result.refine( refID, url );
					} else {
						// handle relax	
						if(jQuery(objRef).parent().hasClass("selected"))
						{
							jQuery(objRef).parent().removeClass("selected");
							app.search.result.relax( refID, url );
							return;
						}
						
						// handle refine
						jQuery(objRef).parent().addClass("selected");
						app.search.result.refine( refID, url );
					}
				}
			
				// this method is called by the click event of a refine link
				this.refine = function(refinementID, url)
				{
					var dwRefinement = this.get(refinementID);
					if(dwRefinement == null) return;
					
					// outdate the refinement state in order to exchange old refinements
					// with respect to new refinements at other category level
					if(dwRefinement.type == "category") app.search.result.outdate();
					
					// update the grid
					this.updateGrid( url );
					
					// render "clear" link
					if(jQuery("#"+dwRefinement.id+" div.refinedclear:empty").length == 1 && dwRefinement.type != "category")
					{
						jQuery("#"+dwRefinement.id+" div.refinedclear").append("<a href='" + dwRefinement.clearUrl + "'>Clear All<\/a>");
						
						// bind clear event
						jQuery("#"+dwRefinement.id+" div.refinedclear a").click(function(e) {
							var refID = jQuery(this).parents()[1].id;
							app.search.result.clearRefinement(app.search.result.get(refID));
							return false;
						});
					}
				};
			
				// this method is called by the click event of a relax link (selected refinement value)
				this.relax = function(refinementID, url)
				{
					var dwRefinement = this.get(refinementID);
					if(dwRefinement == null) return;
					
					// remove clear link, in case there are no other
					// selected values for this refinement
					if(jQuery("#"+dwRefinement.id+" ul li.selected").length == 0)
					{
						jQuery("#"+dwRefinement.id+" div.refinedclear").empty();
					}
					
					// update the grid
					this.updateGrid( url );
				};
			
				// clears the given refinement, all selected values become unselected
				this.clearRefinement = function(dwRefinement)
				{
					if(dwRefinement == null) return;
					
					// omit clearing if currently loading
					if(this.loading) return;
					
					// get the clear url
					var url = jQuery("#"+dwRefinement.id+" div.refinedclear a").attr("href");
					
					// remove "clear" link and value selections
					jQuery("#"+dwRefinement.id+" div.refinedclear").empty();
					jQuery("#"+dwRefinement.id+" ul li").removeClass("selected");
					
					// update the grid
					this.updateGrid(url);
				}
			
				// updates the product grid using the specified url
				this.updateGrid = function(url)
				{
					if(url == null) return;
					
					
					// append "format" parameter
					//url = app.util.appendParamToURL(url, "format", "ajax");
										
				 	var hash = url.split("?")[1];
				 	if(!hash) hash = url;

				 	//carry last know page size through to all requests
					if (hash.indexOf('sz=')==-1) {
						hash += '&sz=' + app.search.currentPageSize;
					}

					location.href = url.split("?")[0]+"?"+hash;
				};
			
				// refreshes the product grid to the given pageName
				this.pageTo = function(pageName)
				{
					if(typeof pageName == "number")
					{
						if(pageName < 1) return;
						pageName = "page-" + pageName;
					}
					else if(pageName.match(/^page[-]/) == null)
					{
						pageName = "page-" + pageName;
					}
					
					if(jQuery("."+pageName).length == 0) return;
					app.search.result.updateGrid( jQuery("."+pageName)[0].href );
				};
			},
			
			// refinements object
			Refinement : function(id, displayName, type, swatchBased, clearUrl, cutoffThreshold)
			{
				this.id = id;
				this.displayName = displayName;
				this.values = [];
				this.type = type;
				this.swatchBased = swatchBased;
				this.clearUrl = clearUrl;
				this.cutoffThreshold = cutoffThreshold;
				this.refreshed = false;
			
				this.add = function(id, value, presentationID, displayValue, refineUrl, relaxUrl, refined, selectable, expandable, level, active)
				{
					this.values[this.values.length] = { "value": value, "presentationID": presentationID, "displayValue": displayValue, "refineUrl": refineUrl, "relaxUrl": relaxUrl, "refined": refined, "selectable": selectable, "expandable": expandable, "level": level, "active": active };
				};
			
				this.get = function(id)
				{
					for(var i=0; i<this.values.length; i++)
					{
						if(this.values[i].id == id) return this.values[i];
					}
					return null;
				};
				
				this.getValueElementID = function(value)
				{
					if(value == "undefined") {
						return null;
					} 
					var elementID = null;
					if(this.swatchBased) {
						elementID = "swatch-";
						if(value.presentationID != null) {
							elementID += value.presentationID;
						} else {
							elementID += value.displayValue;
						}
					}
					return elementID;
				}
			}
		}, // end search
		Product : function(response) {
			// product private data

			var model 			= response.data;

			var mainAttrGroup	= null;

			var myContainerId	= "";

			var isLoadingVar	= false;

			var loadVariants	= function(thisProduct) {
				isLoadingVar = true;
				// build the url and load variants data
				var url = app.util.appendParamToURL(app.util.appendParamToURL(app.URLs.getVariants, "pid", thisProduct.pid), "format", "json")
				var result = app.ajax.getJson({
					url: url,
					data: {},
					callback: function(data){

						if (!data.Success) {
							return;
						}
						model.variations.variants = data.variations.variants;
						isLoadingVar = false; // we have loaded the variants
						jQuery(thisProduct).trigger("VariationsLoaded", ["loadVariants"]);
					}
				});
			}

			var loadRecommendations = function(containerId) {
				if (jQuery(containerId+" #pdpCarouselDiv ul li").length > 0) {
					//jcarousel no longer used. Also pdpCarouselDiv not supported.
					//jQuery(containerId+" #pdpCarouselDiv ul").jcarousel({scroll: 1});
					//unnecessary with error messaging
					// create tooltips event handler
					//app.tooltip({id: containerId+" #pdpCarouselDiv ul li", options: {
					//		bodyHandler: function() {
					//			return jQuery(this).children(".pdpTooltip").html();
					//		}
					//}});
					
				}				
			}

			var getOptionsDiv	= function(thisProduct) {

				if (model.isOption && !model.master) {

					var pdpOpt = jQuery(thisProduct.containerId+" .product_options:last select");

					pdpOpt.change(function(e){
						var vals = this.options[this.selectedIndex].value.split("%?%"); // 0 = value, 1 = price
						e.data = {id: this.id, val: vals[0], price: vals[1]};
						thisProduct.optionSelected(e);
					});
					
					// let us get the currently selected value and intilize the ui
					pdpOpt.each(function(i) {
						var vals = this.options[this.selectedIndex].value.split("%?%"); // 0 = value, 1 = price					
						thisProduct.optionSelected({data : {id: this.id, val: vals[0], price: vals[1]}});
					});
				}
			}

			var disableAddToCart = function(thisProduct) {
				jQuery(thisProduct.containerId+" div.addtocart .addtocartBtn").attr("disabled", "disabled");
				jQuery(thisProduct.containerId+" div.addtocart").addClass("disabled");
			};
			
			var enableAddToCart = function(thisProduct) {
				jQuery(thisProduct.containerId+" div.addtocart .addtocartBtn").attr("disabled", null);
				jQuery(thisProduct.containerId+" div.addtocart").removeClass("disabled");
			};
			
			// We don't use this
			var getAddToCartBtn = function(thisProduct) {
				var addToCartBtn = jQuery(thisProduct.containerId+" .addtocartBtn:last");
				return addToCartBtn;
			}

			var getQtyBox = function(thisProduct) {				
				
				jQuery(thisProduct.containerId+" .quantityinput:last").keyup(function(e){
					var val = null;
					try {
						val = parseInt(jQuery(thisProduct.containerId+" .quantityinput:last").val());
					} catch(e){val = null};

					if (val != null) {
						thisProduct.selectedOptions.Quantity = val;
						
						setAvailabilityMsg(createAvMessage(thisProduct, val));
						
						jQuery(thisProduct).trigger("AddtoCartEnabled");
					}
				});
				if( thisProduct.variant ) {
					thisProduct.selectedOptions.Quantity = 1;
				} else {
					thisProduct.selectedOptions.Quantity = jQuery(thisProduct.containerId+" .quantityinput:last").val();
				}
				setAvailabilityMsg(createAvMessage(thisProduct, thisProduct.selectedOptions.Quantity));
			}

			var getTabs = function(containerId) {

				var tabsDiv = jQuery(containerId+" #pdpTabsDiv");
				tabsDiv.tabs();

				// tab print handler
				jQuery("a.printpage").click(function() {
					window.print();
					return false;
				});
			}

			var getMiscLinks = function(thisProduct) {
			
				if ((model.master || model.variant) && thisProduct.selectedVar == null) {
					// disable wishlist/gift registry links for master products
					jQuery(thisProduct.containerId+" .addtowishlist, "+thisProduct.containerId+" .addtoregistry").addClass("unselectable");
				}
				
				jQuery(thisProduct).bind("AddtoCartEnabled", {}, function(e, source){
					// enable wishlist/gift registry links for variant products
					jQuery(thisProduct.containerId+" .addtowishlist, "+thisProduct.containerId+" .addtoregistry").removeClass("unselectable");
				});
				
				// Add to wishlist, Add to gift registry click handler
				jQuery(thisProduct.containerId+" .addtowishlist a, "+thisProduct.containerId+" .addtoregistry a").click(function(e) {
					// append the currect selectied options to the url
					
					// create a local copy of the selected options
					var selectedOptions = jQuery.extend({}, {}, thisProduct.selectedOptions);
					
					if (model.master || model.variant) {
						if (thisProduct.selectedVar != null) {
							selectedOptions.pid = thisProduct.selectedVar.id;
						}
						else {
							return false; // do not allow master product to be added to gift registry/wishlist
						}
					}
					else {
						selectedOptions.pid = thisProduct.pid;
					}
					
					var tempUrl = this.href;
					
					if (!(tempUrl.indexOf("?") > 0)) {
						tempUrl = tempUrl + "?";
					}
					// serialize the name/value into url query string and append it to the url, make request
					window.location = tempUrl + "&" + jQuery.param(selectedOptions);
					return false;
				} );			
				
				jQuery(thisProduct.containerId+" #pdpSendToAFriend").click(function(e) {
					app.dialog.open(app.URLs.sendToFriend, app.resources.SEND_TO_FRIEND);
					return false;
				} );
			}
			
			var getRatingSection = function(containerId) {

				jQuery(containerId+" #pdpReadReview").click(function(e) {
					jQuery(containerId+" #pdpTabsDiv").tabs("select", "pdpReviewsTab");
				} );

				jQuery(containerId+" #pdpWriteReview").click(function(e) {
				} );
			}

			// based on availability status, creates a message
			// param val - the stock value to compare i.e. qty entered by user
			var createAvMessage = function(thisProduct, val) {
					
				var avStatus 	= thisProduct.getAvStatus(); // availability status
				var avMessage 	= app.resources[avStatus];
				var ats 		= thisProduct.getATS(); // get available to sell qty
				
				if (avStatus === app.constants.AVAIL_STATUS_BACKORDER) {						
					avMessage = avMessage + "<br/>" + jQuery.format(app.resources["IN_STOCK_DATE"], (new Date(thisProduct.getInStockDate())).toDateString() );
				}
				else if (val > ats && avStatus !== app.constants.AVAIL_STATUS_NOT_AVAILABLE) {
					// display quantity left message
					avMessage = jQuery.format(app.resources["QTY_"+avStatus], ats);

					//avMessage += ", " + jQuery.format(app.resources["REMAIN_"+avStatus], val - model.ATS);						
				}
				
				return avMessage;
			}

			var setAvailabilityMsg = function(msg) {
				jQuery(myContainerId+" .availability:last .value").html(msg);
			}

			var computePrice = function(thisProduct) {

				var price = thisProduct.selectedVar != null ? thisProduct.selectedVar.pricing.sale : model.pricing.sale;
				// calculate price based on the selected options prices
				jQuery.each(thisProduct.selectedPrice, function(){
					price = (new Number(price) + new Number(this)).toFixed(2);
				});

				return price;
			}

			// bind click handlers for prev or next buttons on pdp from search
			var getNavLinks = function() {
				// bind events
				jQuery(".productnavigation a").click(function(e) {
					app.getProduct({url: this.href, source: "search"});
					return false;
				});
			}
			
			// size chart link click binding
			var getSizeChart = function() {
				jQuery(".sizeChartLink").click(function(e){
					if (jQuery("#sizeChartDialog").length == 0) {
						jQuery("<div/>").attr("id", "sizeChartDialog").appendTo(document.body);
					}
					
					app.createDialog({id: 'sizeChartDialog', options: {
						height: 530,
				 		width: 800,
				 		title: 'Size Chart'
					}});
					
					jQuery('#sizeChartDialog').dialog('open');
					
					jQuery("#sizeChartDialog").load(this.href);
					
					return false;
				});
			}
			
			// Product instance
			return {
				pid					: model.ID,
				variant				: model.variant,
				master				: model.master,
				bundled				: model.bundled,
				selectedVarAttribs	: {},
				varAttributes		: {},
				selectedVar			: null,
				selectedOptions		: {}, // holds currently selected options object {optionName, selected val}
				selectedPrice		: {}, // holds price for selected options
				containerId			: null, // holds html container id of this product
				subProducts			: [], // array to keep sub products links 

				showSelectedVarAttrVal: function(varId, val) {
					jQuery(this.containerId+" .variation_attributes div span[id='pdp"+varId+"selected']").html(val);
				},
				
				readReviews: function() {
					jQuery(this.containerId+" #pdpTabsDiv").tabs("select", "pdpReviewsTab");
				},
				// shows product images and thumbnails
				// @param selectedVal - currently selected variation attr val
				// @param vals - total available variation attr values
				showImages: function(selectedVal, vals)  {
					var that = this;
					vals = vals || {};
					
					// show swatch related images for the current variation value					
					jQuery.each(vals, function(){
						var imgCounter = -1;
						var thisVal = this;
						if (this.val === selectedVal && this.images) {
							if (this.images.thumbnail.length > 0) {
								//if we have a large images for zoom treat them different.
								//jQuery(that.containerId+" .product_thumbnails:last").html("");
								//pjn
								extralargeURL = thisVal.images.extralarge[0];
								largeURL = thisVal.images.large[0];
								
							}
							// make sure to show number of images based on the smallest of large or small as these have to have 1-1 correspondence.
							var noOfImages = this.images.large.length >= this.images.thumbnail.length ? this.images.thumbnail.length : this.images.large.length;
							
							jQuery.each(this.images.thumbnail, function(){
								imgCounter++;
								var imageInd = imgCounter;
								if (imgCounter > noOfImages - 1) {
									return;
								}
							
								if(jQuery('#Zoomer').length <= 0 )
								{
										if(jQuery(that.containerId+" .thumb_view li").length > 0)
										{
											jQuery(jQuery(that.containerId+" .thumb_view li")[imageInd]).mouseenter(function(e){
												jQuery(this).addClass('hover');
											}).mouseleave(function(){
												jQuery(this).removeClass('hover');
											}).click(function(){
												jQuery(that.containerId+" .product_image img").attr("src", thisVal.images.large[imageInd]);									
											});
										}
								}
								
							});
						}
					});
				},

				/**
				* Event handler when a variation attr is selected.
				*/
				varAttrSelected: function(e) {
					// update the selected value node
					this.showSelectedVarAttrVal(e.data.id, e.data.val);

					this.selectedVarAttribs[e.data.id] = e.data.val;				
					
					// store this ref
					var that = this;

					// trigger update event which will update every other variation attribute i.e. enable/disable etc.

					// first reset the contents of each attribute display
					// when we have got the varriations data
					if (!isLoadingVar) {
						jQuery.each(model.variations.attributes, function () {
							if (this.id != e.data.id) {
								jQuery.each(jQuery(that.containerId+" #pdp"+this.id+"var li a"), function(){
									var dataa = jQuery(this).data("data");
									if( !dataa ) {
										
									} else {
										// find A variation with this val
										var val = $(this).attr("title");
										if (that.isVariation({id:e.data.id, val:e.data.val}, {id:dataa.id, val:val})) {
											// found at least 1 so keep it enabled
											jQuery(this).parent().removeClass("unselectable");
										}
										else {
											jQuery(this).parent().addClass("unselectable");
											jQuery(this).parent().removeClass("selected");
										}
									}
								});
							}
							else {
								
								extralargeURL = "";
								largeURL = "";
								// show swatch related images for the current value								
								that.showImages(e.data.val, this.vals);
								//PJN Color Attribute
								if(e.data.id == "a1")
								{
									if(jQuery('#Zoomer').length > 0)
									{
											if(extralargeURL.length > 0) //do we have an extralarge image? okay..then clear the disable attributes
												jQuery("#Zoomer").attr("rel", (jQuery("#Zoomer").attr("rel").replace(/ disable-zoom:true; disable-expand:true;/g,"")) );
											jQuery(".product_image #Zoomer").attr("href", extralargeURL);
											jQuery(".product_image #Zoomer img").attr("src", largeURL).one("load",function(){
											MagicZoomPlus.refresh();
											});
											
									}
									else
									{
										
										jQuery(that.containerId+" .product_image img").attr("src", largeURL);
									}
								}								
								
							}
						});

						// find a matching variation and update the screen
						this.selectedVar = this.findVariation(this.selectedVarAttribs);
					}

					// lets fire refresh view event to enable/disable variations attrs
					jQuery(this).trigger("VariationsLoaded");
				},

				/**
				* go thru all variations attr and disable which are not available
				*/
				resetVariations: function() {
					if (isLoadingVar) {
						return ; // we don't have the complete data yet
					}
					var that = this;

					jQuery.each(model.variations.attributes, function () {
						jQuery.each(jQuery(that.containerId+" #pdp"+this.id+"var li a"), function(){
							var dataa = jQuery(this).data("data");
							// find A variation with this val
							var li = null;
							if( dataa ){ 
								var val = $(this).attr('title');
							
								if (that.isVariation({id:dataa.id, val:val})) {
									// found at least 1 so keep it enabled
									li = jQuery(this.parentNode);
									li.removeClass("unselectable");
								}	else {
									li = jQuery(this.parentNode);
									li.addClass("unselectable");
									li.removeClass("selected");
								}
							}
						});
					});
				},

				refreshView: function() {
					var thisProduct = this;

					if (!isLoadingVar && this.selectedVar == null) {
						// if we have loaded the variations data then lets if the user has already selected some values
						// find a matching variation
						this.selectedVar = this.findVariation(this.selectedVarAttribs);
					}

					//if( app.ProductCache.variant ) return;
					
					if (!isLoadingVar && this.selectedVar != null) {
						// update availability
						setAvailabilityMsg(createAvMessage(thisProduct, 1));
						// update price
						this.showUpdatedPrice(this.selectedVar.pricing.sale, this.selectedVar.pricing.standard);

						if (!(!this.selectedVar.inStock && this.selectedVar.avStatus === app.constants.AVAIL_STATUS_NOT_AVAILABLE)) {
							// enable add to cart button
							enableAddToCart(thisProduct);
							jQuery(this).trigger("AddtoCartEnabled");
						}
						else {
							disableAddToCart(thisProduct);
						}
					}
					else {
						if (isLoadingVar) {
						// update availability
							setAvailabilityMsg(app.showProgress("productloader"));
						}
						else {
							setAvailabilityMsg(app.resources["NON_SELECTED"]);
						}
						// disable add to cart button
						disableAddToCart(thisProduct);
					}
					
					var nonSelectedVars = [];
					
					// update selected var attr vals
					jQuery.each(model.variations.attributes, function(){
						thisProduct.showSelectedVarAttrVal(this.id, thisProduct.selectedVarAttribs[this.id]);
						
						if (!thisProduct.selectedVarAttribs[this.id] || thisProduct.selectedVarAttribs[this.id] == "" ) {
							nonSelectedVars.push(this.name);
						} 
					});
					
					// process non-selected vals and show updated tooltip for A2C button as a reminder
					var tooltipStr = nonSelectedVars.join(" & ");
					
					if (nonSelectedVars.length > 0) {
						var availMsg = jQuery.format(app.resources["MISSING_VAL"], tooltipStr);
						setAvailabilityMsg(availMsg);
						jQuery(thisProduct.containerId+" .addtocartBtn:last").attr("title", availMsg);
					}					
				},

				showUpdatedPrice: function(sale, standard) {
                    standard = standard || 0;
                    
                    sale = (new Number(sale)).toFixed(2);
                    standard = (new Number(standard)).toFixed(2);
                    
                    var priceHtml = app.currencyCodes[model.pricing.currencyCode] + sale;
                    jQuery(this.containerId+' .productdetail-box .leftPrice .label:hidden').show()
                    
                    if (standard > 0 && standard > sale) {
                          jQuery(this.containerId+' .productdetail-box .leftPrice .label:visible').hide()
                          // show both prices
                          priceHtml = '<span class="standardprice">' + app.currencyCodes[model.pricing.currencyCode] + standard + '</span> <span class="salesprice">' + priceHtml + '</span>';
                    }                             
                    
                    var priceContainer = jQuery(this.containerId+" .productdetail-box .leftPrice .pricing:first").html(priceHtml);
                    // containerId contains #, get rid of it before finding the right price div
                    jQuery(this.containerId+" #pdpATCDiv"+this.containerId.substring(1)+" .price").html(priceHtml);
                    if(sale == 0.00) priceContainer.addClass('zero-value');
                    else priceContainer.removeClass('zero-value');                   
				},

				optionSelected: function(e) {
					this.selectedOptions[e.data.id] = e.data.val;
					this.selectedPrice[e.data.id] = e.data.price;

					// update price and show
					this.showUpdatedPrice(computePrice(this), model.pricing.standard);
				},

				getPrice: function() {
					return computePrice(this);
				},

				/*
				* receives 2 or 1 var attrib values and tries to figure out if there is a variant with these values.
				* returns true/false
				*/
				isVariation: function(val1, val2) {
					var variant = null, found = false;

					for (var i=0; i<model.variations.variants.length; i++) {
						variant = model.variations.variants[i];
						if (variant != undefined) {
							if ('attributes' in variant) {
								if ( (variant.attributes[val1.id] == val1.val) && ( (val2 == undefined || variant.attributes[val2.id] == val2.val)) ) {
									found = true;
								}
							}
						}
					}
					return found;
				},

				/*
				* find a variant with the given attribs object
				* return null or found variation json
				*/
				findVariation: function(attrs) {
					if (!this.checkAttrs(attrs)) {
						return null;
					}

					var attrToStr = function(attrObj) {
						var result = "";
						jQuery.each(model.variations.attributes, function(){
							result += attrObj[this.id];
						});
						return result;
					}

					var attrsStr = attrToStr(attrs);

					for (var i = 0; i < model.variations.variants.length; i++) {
						variant = model.variations.variants[i];
						if (variant != undefined) {
							if ('attributes' in variant) {
								if (attrToStr(variant.attributes) === attrsStr) {
									return variant;
								}
							}
						}
					}
					return null;
				},

				findVariationById: function(id) {

					if (model.variations.variants.length > 1) {	
						for (var i=0; i < model.variations.variants.length; i++) {
							var variation = model.variations.variants[i];
							if (variation && ('id' in variation)) {
								if (variation.id == id) {
									return variation;
								}
							}
						}
					}

					return {};
				},

				/*
				* see if the specified attrs object has all the variation attributes present in it
				* return true/false
				*/
				checkAttrs: function(attrs) {
					for (var i=0; i<model.variations.attributes.length; i++) {
						if (attrs[model.variations.attributes[i].id] == null) {
							return false;
						}
					}
					return true;
				},
				
				// given an id, return attr definition from model.variations.attributes
				getAttrByID: function(id) {
					for (var i=0; i<model.variations.attributes.length; i++) {
						if (model.variations.attributes[i].id === id) {
							return model.variations.attributes[i];
						}
					}
					return {};
				},
				
				// returns current availability status e.g. in_stock, preorder etc.
				getAvStatus: function() {
					if ((this.variant || this.master) && this.selectedVar != null) {
						return this.selectedVar.avStatus;
					}
					else {
						return model.avStatus;
					}
				},
				
				// return available to sell qty
				getATS: function() {
					if ((this.variant || this.master) && this.selectedVar != null) {
						return this.selectedVar.ATS;
					}
					else {
						return model.ATS;
					}
				},
				
				// returns in stock date 
				getInStockDate: function() {
					if ((this.variant || this.master) && this.selectedVar != null) {
						return this.selectedVar.inStockDate;
					}
					else {
						return model.inStockDate;
					}
				},
				
				// determine if A2C button is enabled or disabled
				// true if enabled, false otherwise
				isA2CEnabled: function() {
					if (this.variant || this.master) {
						if (this.selectedVar != null) {
							return this.selectedVar.avStatus === app.constants.AVAIL_STATUS_IN_STOCK;
						}
						else {
							return false;
						}
					}
					else {
						return model.avStatus === app.constants.AVAIL_STATUS_IN_STOCK;;
					}
				},
				
				show: function(options) {
					// preserve this instance
					var thisProduct = this;
					// bind events
					jQuery(this).bind("VariationsLoaded", {}, function(e, source){
						if (thisProduct.variant && thisProduct.selectedVar == null) {
							thisProduct.selectedVar = thisProduct.findVariationById(thisProduct.pid);
							thisProduct.selectedVarAttribs = thisProduct.selectedVar == null ? {} : jQuery.extend({}, {}, thisProduct.selectedVar.attributes);
						}
						
						// enable/disable unavailable values
						if (source && source == "loadVariants") {
							thisProduct.resetVariations();
						}
						thisProduct.refreshView();
					});

					this.containerId 	= "#"+options.containerId;
					var container		= jQuery(this.containerId);
					var append			= options.append;
					var isQuickView 	= false;

					if (options.source && options.source == "quickview") {
						isQuickView = true;
					}

					if (append) {
						this.containerId = "#"+this.pid+"Div";
					}
					myContainerId = this.containerId;
					
					// bind click handlers for prev or next links
					getNavLinks();
					
					// size chart click binding
					getSizeChart();

					// variation attributes
					if (model.master || model.variant) {
						loadVariants(this); // make a server call to load the variants, this is due to the performance reasons
						// meanwhile display the available variation attributes
						jQuery.each(model.variations.attributes, function(){

							var thisAttr = this;

							thisProduct.varAttributes[this.id] = {};

							var pdpVarId = this.id;
							var singleValue = jQuery(thisProduct.containerId + " #pdpVarAttrDiv input#pdp"+pdpVarId+"var");
							
							if( singleValue.size()==1 ) {
								var e = {'data' : { 'id' : pdpVarId, 'val':singleValue.val() } };
								thisProduct.varAttrSelected(e);								
							} else if( this.ui == 0 ) {
								var ele = jQuery(thisProduct.containerId + " #pdp"+this.id+"var")
								if(ele[0] && ele[0].selectedIndex >= 0 && ele[0].options[ele[0].selectedIndex].value != "") {
									// grab the currently selected val
									thisProduct.selectedVarAttribs[this.id] = ele[0].options[ele[0].selectedIndex].value;
								}
								
								// default ui i.e. drop down
								if( ele ) {
									ele.data("data", {id: this.id, val: ''}).change(function(e){
										if (this.selectedIndex == 0) { return; }

										e.data = jQuery(this).data("data");
										e.data.val = this.options[this.selectedIndex].value;
										thisProduct.varAttrSelected(e);
									});
								}
								
							} else {
								// color, width, size
								// its a custom ui with div controlled via css
								var pdpVarId = this.id;
								
								// grab the currently selected attr val
								thisProduct.selectedVarAttribs[pdpVarId] = jQuery(thisProduct.containerId + " #pdpVarAttrDiv #pdp"+pdpVarId+"var .selected a").attr("title");								
								
								var varEventHandler = function(e){
									var thisObj = jQuery(this);
									
									if (thisObj.parent().hasClass("selected") ||
										thisObj.parent().hasClass("unselectable")) {
										return false;
									}
									
									var val = $(this).attr("title");
									e.data = {id: pdpVarId, val: val};

									// remove the current selection
									jQuery(thisProduct.containerId + " #pdpVarAttrDiv #pdp"+pdpVarId+"var .selected").removeClass("selected");

									thisObj.parent().addClass("selected");
									thisProduct.varAttrSelected(e);
									return false;
								}
								
								var varJqryObjs = jQuery(thisProduct.containerId + " #pdp"+pdpVarId+"var a");
								// if its a color attr then render its swatches
								var colorVal = '';
								if (pdpVarId === "a1") {
									var colorAttrDef = thisProduct.getAttrByID('a1');
									varJqryObjs.each(function(){
									
										// given a variation attr value, find its swatch image url
										var findSwatch = function(val) {
											for (var i=0; i<colorAttrDef.vals.length; i++){
												if (colorAttrDef.vals[i].val === val) {													
													return colorAttrDef.vals[i].images.swatch;
												}
											}
											return ""; // no swatch image found
										}
										
										// PJP: Changed to use the title, not innerHTML
										colorVal = jQuery(this).attr("title");
										var swatchUrl = findSwatch(colorVal); // find swatch url
										
										if (swatchUrl && swatchUrl != "") {
											jQuery(this).find("span:first-child").css("background", "url(" + swatchUrl + ")");
										}
										else {
											//jQuery(this).css("color", "transparent"); // no swatch image found
										}
									});
									
									varJqryObjs.data("data", {id: pdpVarId}).click(varEventHandler).hover(function(e){
										var colorVal = $(this).attr("title");
										thisProduct.showSelectedVarAttrVal("a1", colorVal);// changed from innerHTML										
										thisProduct.showImages(colorVal, colorAttrDef.vals);// changed from innerHTML
									}).mouseleave(function(e) {
										if (thisProduct.selectedVarAttribs["a1"]) {
											thisProduct.showImages(thisProduct.selectedVarAttribs["a1"], colorAttrDef.vals)
										}
										else {
											thisProduct.showImages("", [{val: "", images: model.images}]);
										}
										
										thisProduct.showSelectedVarAttrVal("a1", thisProduct.selectedVarAttribs["a1"] || "&nbsp;");
									});
								}
								else {								
									varJqryObjs.data("data", {id: pdpVarId}).click(varEventHandler);
								}
							}
						});
						
						if (thisProduct.selectedVarAttribs["a1"]) {
							// show swatch related images for the current value								
							thisProduct.showImages(thisProduct.selectedVarAttribs["a1"], thisProduct.getAttrByID('a1').vals);
						}
						//TODO: Remove this "else" code after 6/15/11 if no reports of broken images are found.
						//else {
							// show images and bind hover event handlers for small/thumbnails to toggle large image								
							//thisProduct.showImages("", [{val: "", images: model.images}]);
						//}
					}
					else {
						// show images and bind hover event handlers for small/thumbnails to toggle large image								
						thisProduct.showImages("", [{val: "", images: model.images}]);
					}
					
					// bind product options event(s)
					getOptionsDiv(this);

					if(!model.productSet) {
						// quantity box
						if (!model.bundle) {
							getQtyBox(this);
						}// update avaiability for a bundle product, for everything else its done inside getQtyBox
						else if (model.bundle) {
							setAvailabilityMsg(createAvMessage(this, 1));
						}
					}

					// Add to cart button
					var addToCartBtn = getAddToCartBtn(this);
					if (model.master || model.productSet || model.bundle || (!model.inStock && model.avStatus === app.constants.AVAIL_STATUS_NOT_AVAILABLE && !model.productSet)) {
						jQuery("div.addtocart button").attr("disabled", "disabled");
						jQuery("div.addtocart").addClass("disabled");
					}
																			
					if (model.bundle) {
						// if the bundled products are standard prodcuts then determine disability of the a2c button by checking each producgt's availability
						var bundleA2CEnabled = false;
						for (var i = 0; i < thisProduct.subProducts.length; i++) {
							var subProduct = thisProduct.subProducts[i];
							bundleA2CEnabled = subProduct.isA2CEnabled();
							if (!bundleA2CEnabled) {
								break;
							}
						}
						if (!bundleA2CEnabled) {
							disableAddToCart(thisProduct);
						} 
						else {
							enableAddToCart(thisProduct);
						}
					}						

					if (!model.subProduct && !model.bundled) {

						if (!model.productSet && !isQuickView && !model.bundle) {
							// customer rating
							getRatingSection(this.containerId);							
						}
					}
					
					// wish list, sent to friend, add to gift
					getMiscLinks(this);
							
					// recommendations carosel
					loadRecommendations(this.containerId);

					// tabs
					getTabs(this.containerId);										
					
					// see if have any subproducts and bind AddtoCartEnabled event
					jQuery.each(thisProduct.subProducts, function(){
						jQuery(this).bind("AddtoCartEnabled", {},
							/**
							* Event handler when a subproduct of a product set or a bundle is selected.
							* Basically enable the add to cart button or do other screen refresh if needed like price etc.
							*/
							function() {
								// enable Add to cart button if all the sub products have been selected
								var enableAdd2Cart = true;
								var subProducts = thisProduct.subProducts;
								var price = new Number();

								for (var i = 0; i < subProducts.length; i++) {
									if (((subProducts[i].variant || subProducts[i].master) && subProducts[i].selectedVar == null) ||
										(!subProducts[i].bundled && (subProducts[i].selectedOptions["Quantity"] == undefined ||
										subProducts[i].selectedOptions["Quantity"] <= 0))) {
										enableAdd2Cart = false;
										break
									}
									else {
										if (subProducts[i].selectedVar != null) {
											subProducts[i].selectedOptions.pid = subProducts[i].selectedVar.pid;
										}
										else {
											subProducts[i].selectedOptions.pid = subProducts[i].pid;
										}

										price = price + new Number(subProducts[i].getPrice());
									}
								}

								if (enableAdd2Cart && (model.productSet || model.inStock)) {
									enableAddToCart(thisProduct);

									// show total price
									thisProduct.showUpdatedPrice(price);
								} else {
									disableAddToCart(thisProduct);
								}
							}
						);
					});
				},

				toString: function() {
					return this.model;
				}
			}
		} // Product defintion end
	}
})(jQuery);

//Create final app object
if(typeof app === 'undefined') window.app = {};
jQuery.extend(true, app, _app);


//application initialization on dom ready
window.DOMUtils = { 
	_listeners : []
};
DOMUtils.registerBehaviors = function(listener){
	var index = jQuery.inArray(listener,DOMUtils._listeners);
	if( index < 0 ) {
		DOMUtils._listeners.push(listener);
	}
};
DOMUtils.unregisterBehaviors = function(listener){
	var index = jQuery.inArray(listener,DOMUtils._listeners);
	if( index > -1 ) {
		DOMUtils._listeners.splice(index,1);
	}
};
DOMUtils.enhanceDOM = function(root) {
	var base = root || document.body;
		
	// Enable base functionality
	app.init(base);
	
	//PJN added for zoom. A only around zoom enabled thumbs. Is it the same image as current? dont load then.
	jQuery(".thumb_view li a").click(function(){ 
		var image = jQuery(".product_image img");
		var src = image[0].src;		
		var myAltSrc = jQuery("img",this).data("altSrc");
		if( src != myAltSrc ){

			image[0].src = myAltSrc;
			if(jQuery("#Zoomer").length > 0)
			{
				if(jQuery(this).attr("href").length > 0) //do we have an extralarge image? okay..then clear the disable attributes
				jQuery("#Zoomer").attr("rel", (jQuery("#Zoomer").attr("rel").replace(/ disable-zoom:true; disable-expand:true;/g,"")) );
				jQuery(".product_image #Zoomer").attr("href", jQuery(this).attr("href"));
				jQuery(".product_image #Zoomer img").attr("src", jQuery("img", this).data("altSrc")).one("load",function(){
				MagicZoomPlus.refresh();
					
				});
			}
		}
			
	});

	// Home page behavior
	if(jQuery(".carousel").length) {
	    jQuery('.carousel.oneproduct').carouselify({itemWidth:700,batchSize:1,hitBox:'.carousel-container .product-module'});
		jQuery('.carousel.twoproduct').carouselify({itemWidth:350,batchSize:2,hitBox:'.carousel-container .product-module'});
		jQuery('.carousel.threeproduct').carouselify({itemWidth:233,batchSize:3,hitBox:'.carousel-container .product-module'});
		jQuery('.carousel.fourproduct').carouselify({itemWidth:175,batchSize:4,hitBox:'.carousel-container .product-module'});
	}
    
    // product detail enhancements
	PDPUtils.enhanceDOM(base);
	
	// search results enhancements
	SRPUtils.enhanceDOM(base);
	
	//any thumbnail without extralarger version set properties
	jQuery("#main .thumb_view li").not(".extralarge").mouseenter(function(){
		jQuery(this).addClass('hover');
	}).mouseleave(function(){
		jQuery(this).removeClass('hover');
	}).click(function(){	
		

		if(jQuery("#Zoomer").length > 0)
		{
			if(jQuery("#Zoomer").attr("rel").indexOf(" disable-zoom:true; disable-expand:true;") == -1)
			{
				jQuery("#Zoomer").attr("rel", (jQuery("#Zoomer").attr("rel") + " disable-zoom:true; disable-expand:true;") );
				MagicZoomPlus.refresh();
			}
		}
			
		var image = jQuery(".product_image img");
		var src = image[0].src;
		var myAltSrc = jQuery("img",this).data("altSrc");
		if( src != myAltSrc ) {
			
			image[0].src = myAltSrc;
		}
	});
};

window.PDPUtils = {
	currentSubCategory : 'empty',
	_visibleRows : null,
	
	scrollify : function(wrappedset) {
		if( jQuery('#productlist tr:visible',wrappedset[0]).size()<=5 ) {
			wrappedset.find(".scrollable").addClass("noscroll");
		} else {
			wrappedset.find(".scrollable").removeClass("noscroll");
		}	
	},

	expandSection : function(expandosection, callback) {
		/*Currently closed*/
		/*Check for gender-hand, return if there is no current selection*/
		var selGenderHandElement = jQuery('#pdpVarAttrDiv .gender-hand li.selected')[0];
		if( selGenderHandElement==null ) {
			if( expandosection.parents('.shafts').size()<1 ) 
				return false;
		}
		jQuery('.addtocartbar div.callout').hide('fast');
		var esection = expandosection;
		var ecallback = callback;
		PDPUtils.fixMissingColumns();
		var options = {'width':'940px'};
		if( jQuery.browser.msie && jQuery.browser.version<8 ) {
			options['marginLeft'] = "-400px";
		}
		expandosection.stop().animate(options,function(){
			//PDPUtils.scrollify(jQuery(this));
			expandosection.addClass('expanded-section');
			jQuery(this).find('.scrollable').show();
			expandosection.removeClass('expandable-section');
			if( ecallback ) ecallback();
			expandosection.css({left:"0px"});
		});//.find('.scrollable').fadeIn('normal');
	},

	compressSection : function(expandosection) {
		/*Currently open*/
		expandosection.find('.scrollable').hide();//,function(){
		PDPUtils.unfixMissingColumns();
		var options = {'width':'569px'};
		if( jQuery.browser.msie && jQuery.browser.version<8 ) {
			options['marginLeft'] = "0px";
		}

		expandosection.css({overflow:'visible'});
		expandosection.stop().animate({'width':'569px'},function(){
			if( jQuery('.gender-hand li.selected')[0]==null ) {
				// Make sure that the first callout has the gender/hand message
				jQuery(jQuery('.addtocartbar div.callout')[0]).addClass('nogenderhand');
			} else {
				// Make sure that the first callout has the press the + button message
				jQuery(jQuery('.addtocartbar div.callout')[0]).removeClass('nogenderhand');
			}
			jQuery('.addtocartbar div.callout').show('fast');
			
			expandosection.removeClass('expanded-section');
			expandosection.addClass('expandable-section');
			 
			expandosection.css({overflow:'visible'});
		});//});
	},
	resetAllAttributeRefinements : function() {
		jQuery('#productdetail-box .refinement_category li.selected').removeClass('selected');
		jQuery('#productdetail-box .refinement_category li').addClass('unorderable').css({'opacity':'0.4'}).attr("disable","disable");	
//		PDPUtils.greyVariantAttributes();
	},
	resetSubcategorySpecificRefinements : function( subcatName ) {
//		var startTime = new Date();
		// Swap subcat specific column visibility
		if( subcatName.indexOf('Sets')>-1 ) {
			jQuery('#pdpVarAttrDiv div.v1258').show();
			jQuery('#productlist td.v1258').show();
			jQuery('#pdpVarAttrDiv div.v1267').hide();
			jQuery('#productlist td.v1267').hide();
		} else {
			jQuery('#pdpVarAttrDiv div.v1258').hide();
			jQuery('#productlist td.v1258').hide();
			jQuery('#pdpVarAttrDiv div.v1267').show();
			jQuery('#productlist td.v1267').show();

			if( subcatName.indexOf('Individual')>-1 ) {
				jQuery('#pdpVarAttrDiv li.v1267:has(a:contains("Iron"))').show();
				jQuery('#pdpVarAttrDiv li.v1267:has(a:contains("Wedge"))').hide();
			} else {
				jQuery('#pdpVarAttrDiv li.v1267:has(a:contains("Iron"))').hide();
				jQuery('#pdpVarAttrDiv li.v1267:has(a:contains("Wedge"))').show();
			}
		}
//		var endTime = new Date();
//		alert("resetSubcategorySpecificRefinements:"+(endTime.getTime()-startTime.getTime())+"ms");
	},
	filterSubcategory : function(el, origEl) {
		//alert('FILTER SUBCATEGORY!');
		var attrValue = jQuery(el).text();
		// If we already have the tab active, then forget about it
		if (PDPUtils.currentSubCategory != attrValue){
			// Don't change the gender hand ...
			// Cler all attribute refinements
			PDPUtils.resetAllAttributeRefinements();

			PDPUtils.resetSubcategorySpecificRefinements(attrValue);
			
			// Hide all rows except ones with this subcat
			PDPUtils.showSubcategorySpecificRows( attrValue );
			
			PDPUtils.currentSubCategory = attrValue;
			
			PDPUtils.filterForCurrentGenderHandSelection();
			
			// Hide the link
			if( jQuery('li.selected .vGenderHand').length>0 ) {
				PDPUtils.updateSearchResultsText();
			} else {
				jQuery('#productdetail-box .callout').hide();
			}

			if( !origEl ) PDPUtils.filterRefinemetOptions();
		} else {
			// Hide all rows except ones with this subcat
			PDPUtils.showSubcategorySpecificRows(attrValue);
		}
		PDPUtils.oddifyRows();
	},
	showSubcategorySpecificRows : function( subcategory ) {
	},
	/* Find all rows which have a td containing "subcat" */
	rowsForSubcategory : function( subcat ) {
		var rows = jQuery("#productlist tr");
		
		return rows;
	},
	visibleRows : function() {
		if( PDPUtils._visibleRows ) return PDPUtils._visibleRows;
		var startTime = new Date();
		var rows = [];
		jQuery('#productlist tr').each(function(){
			if(jQuery(this).css('display')!='none') {
				rows.push(this);
			}
		});
		PDPUtils._visibleRows = jQuery(rows);
		var endTime = new Date();
		return PDPUtils._visibleRows;
	},
	filterForCurrentGenderHandSelection : function(overlay) {
		// Check all gender/hands and update unorderable as needed

	},
	filterVariants : function(el, mutuallyExclude) {
		
		PDPUtils.disableCustomize();
		PDPUtils.disableAddToCart();

		
		var that = jQuery(el);
		var ele = that.parents('li');
		if( ele && ele.size()>0 && ele.hasClass('unorderable') ) return false;

		var attrID = el.className.split(" ")[0];
		var attrValue = el.id;
		var mux = !!mutuallyExclude;
		
		// Check if selected
		var wasChecked = jQuery('#'+attrValue).parents('li').hasClass('selected');
		var isChecked = !wasChecked;
		
		var newID = null;
		if( !wasChecked && mux ) {
			jQuery('#'+attrValue).parents('li').siblings().removeClass('selected');
			// Look for associated product list
			newID = "productlist"+attrValue.split("-")[1];

			// And copy it in
			jQuery('#productlist').html(jQuery('#'+newID).html());
			PDPUtils._visibleRows = null;
			jQuery('#'+attrValue).parents('li').addClass('selected');
			that = jQuery('#'+attrValue);
			PDPUtils.clearAllRefinements();
			PDPUtils.enhanceDOM(jQuery('#expando')[0]);
		} else if(attrID=='vGenderHand') {
			jQuery('#'+attrValue).parents('li').removeClass('selected');
			PDPUtils.resetAllAttributeRefinements();
		} else {
			ele.toggleClass('selected');
			jQuery('#productlist tr').show();
			PDPUtils.applyRefinementsToProductGrid();
			jQuery('#productdetail-box .callout').hide();
		}
		
		// PJP_JS
		// Add new hook to simplify CSS element collection
		// if any 'li' is selected, then add 'filtered' to .var-attr
		var ul = that.parents('ul');
		if( ul.find('li.selected').size()>0 ) {
			ul.addClass('filtered');
		} else {
			ul.removeClass('filtered');
		}
				
		// Make sure gender hand is selected before we expose the grid
		// Otherwise, close it
		var expandosection = jQuery('#expando');
		if( attrID == 'vGenderHand' && isChecked ) {
			// GenderHand already selected, so open the grid
			PDPUtils.expandSection(expandosection, function(){
				var sorts = jQuery('#options-header .asc,#options-header .des');
				if( sorts.size()==0 ){
					// Comes from server sorted
					//PDPUtils.sortRows('natural-sort','asc');
				} else {
					PDPUtils._visibleRows = null;
					PDPUtils.oddifyRows(jQuery('#productlist')[0]);
				}
				PDPUtils.filterRefinemetOptions(attrID,wasChecked);
			});
		} else if( attrID == 'vGenderHand' && jQuery('.gender-hand li.selected')[0]==null ) {
			jQuery("#productdetail-box .callout").hide();
			PDPUtils.compressSection(expandosection);
			PDPUtils._visibleRows = null;
		} else {
			PDPUtils._visibleRows = null;
			PDPUtils.oddifyRows(jQuery('#productlist')[0]);
			PDPUtils.filterRefinemetOptions(attrID,wasChecked);
		}
		PDPUtils.updateSearchResultsText();
		return false;
	},
	clearAllAttributeRefinements : function() {
		PDPUtils._visibleRows=null; 
		PDPUtils.clearAllRefinements(true); 
		jQuery('.refinement_category li').removeClass('selected');
		PDPUtils.updateSearchResultsText(); 
	},
	clearAllRefinements : function(isFirstCall) {
		// Show all productlist rows
		jQuery('#productlist tr').show();

		// Hide all refinements
		if( !isFirstCall ) PDPUtils.resetAllAttributeRefinements();
		if( !jQuery('#main').hasClass('shafts') ) {
			// Hide non-applicable productlist rows for gender-hand
			PDPUtils.filterRefinemetOptions();
		}
		PDPUtils.oddifyRows();
	},
	hideNonconformingRowsForAttribute : function( attrID ) {
		jQuery('#productlist tr:has(td.NA.'+attrID+')').hide();
	},
	applyRefinementsToProductGrid : function() {
//		var startTime = new Date();
		// We start with all visible, so we hide the "unchcecked ones"
		var varAttrs = jQuery('#pdpVarAttrDiv li.selected a').parents('ul').find('a');
		var unselectedSiblings = varAttrs.map(function(){
			if( this.parentNode.className.indexOf('selected')<0 )
				return this;
		});
		
		var usSize = unselectedSiblings.size();
		if( usSize > 0 ) {
			// Hide unselected
			unselectedSiblings.each(function(){
				var trs = jQuery('#productlist td.'+this.id).map(function(){
					return this.parentNode;
				});
				jQuery(trs).hide();
			});
		}
//		PDPUtils.oddifyRows(jQuery('#productlist')[0]);
//		PDPUtils.updateSearchResultsText();
//		var endTime = new Date();
//		alert(endTime.getTime()-startTime.getTime()+"ms: apply");
	},
	updateSearchResultsText : function(){
		var totalRows = jQuery(".productlist-store tr").length;
		 
		var visibleRows = PDPUtils.visibleRows().length;
		if( totalRows> 0 ) {
			jQuery('#productdetail-box .callout .results').text(visibleRows+ " of "+totalRows+ " items match your selections.");
			jQuery('#productdetail-box .callout').show();
			if( visibleRows==0 ) {
				jQuery('#productdetail-box .callout').addClass("zero-results");
			} else {
				jQuery('#productdetail-box .callout').removeClass("zero-results");
			}
		} else {
			jQuery('#productdetail-box .callout').hide();
		}
	},
	/* This function sets up the refinement check boxes based on what is visible in the table */
	filterRefinemetOptions : function(clickedAttributeID, waschecked){
		var visibleRows = PDPUtils.visibleRows();
		
		// Ignore if the table is not visible
		if( visibleRows.size()<1 ) {
			return;
		} else {
			//var genderHandID = jQuery('.refine_attributes .vGenderHand.selected').attr("id");

			// For each variation attribute element
			jQuery('#pdpVarAttrDiv .refinement_category').each( function(){
				var attributeID = this.id;
								
				// Never act on the row we are checking
				if( !waschecked && clickedAttributeID==attributeID ) {
					return;
				}
				
				// If the variation attribute value is not selected
				jQuery("a",this).each(function(){
					// Only if unselected ...
					if( this.parentNode.className.indexOf('selected')>-1 ) return;

					var innerElement = jQuery(this);
					
					var attributeValue = this.id;//va
					var selector = 'td.'+ attributeValue;
					if (visibleRows.find(selector).length < 1) {
								innerElement.parents('li').addClass('unorderable').css({'opacity':0.4}); // PJP_API
					} else {
						//PDPUtils.greyVariantAttributes();							
						innerElement.parents('li').removeClass('unorderable').removeAttr('disabled').css({'opacity':1.0}); // PJP_API
					}
				});
			});
		} 
	},
	
	unfixMissingColumns : function(){
		jQuery('#options-header .v54').css('width',null);
		jQuery('#options-header .v1656').css('width',null);
		jQuery('#options-header .v1644').css('width',null);
		jQuery('#options-header .v3').css('width',null);
		jQuery('#options-header .vCondition').css('width',null);
		jQuery('#options-header .pricings').css('width',null);
	},
	fixMissingColumns : function(){
		jQuery('#options-header td:empty').each( function(){
			var ie = $(this);
			var className = ie.attr('class');
			jQuery('#productlist td.'+className).css({display:'none'});
			jQuery(this).css({display:'none'});
			if( className.indexOf( 'v1664' )>-1 ) {
				jQuery('#productlist td.v54').css({width:'295px'});
				jQuery('#options-header td.v54').css({width:'295px'});
			} else if( className.indexOf( 'v1644' )>-1 ) {
				jQuery('#productlist td.v1656').css({'padding-left':'5px','width':'270px'}); 
				jQuery('#options-header td.v1656').css({width:'273px'});
				jQuery('#productlist td.v3').css({width:'204px'});
				jQuery('#options-header td.v3').css({width:'204px'});
				jQuery('#productlist td.vCondition').css({width:'244px'});
				jQuery('#options-header td.vCondition').css({width:'244px'});
				jQuery('#productlist td.pricings').css({width:'168px'});
				jQuery('#options-header td.pricings').css({width:'178px'});
			} else if( className.indexOf( 'v1656' )>-1 ) {
				jQuery('#productlist td.v1644').css({'padding-left':'5px','width':'270px'});
				jQuery('#options-header td.v1644').css({width:'273px'});
				jQuery('#productlist td.v3').css({width:'204px'});
				jQuery('#options-header td.v3').css({width:'204px'});
				jQuery('#productlist td.vCondition').css({width:'244px'});
				jQuery('#options-header td.vCondition').css({width:'244px'});
				jQuery('#productlist td.pricings').css({width:'168px'});
				jQuery('#options-header td.pricings').css({width:'178px'});
			}
		});
	},
	formatNumber : function(number,digits) {
		var digs = digits || 10;
		var num = ""+number;
		var out = "";
		var dif = digs-num.length;
		while( dif > 0 ) {
			out+="0";
			dif--;
		}
		return out+num;
	},
	sortBy : function( el ) {
		var descending = jQuery(el).parents('td').hasClass('des');
		var colName = jQuery(el).parents('td')[0].className.split(' ')[0];
		PDPUtils.sortRows(colName,descending ? 'des' : 'asc');
	},

	sortRows : function( column, direction ) {
		var table = jQuery('#productlist').remove();
		var cName = column;
		
		if( cName=='v1258' && PDPUtils.currentSubCategory!='Iron Sets' ) {
			cName = 'v1267';
		}
		
		var columnValues = jQuery.map(table.find('td.'+cName),function(node){
			return out = node.className.split(" ")[2];
		});
		
		// unique it first 
		var uniqueValues = PDPUtils.unique(columnValues);
		
		// sort on unique values - on smaller set
		var sortedValues = uniqueValues.sort();

		if( direction=='asc' ) {
			jQuery('#options-header td.'+column).addClass('des');
			jQuery('#options-header td.'+column).removeClass('asc');
		} else {
			sortedValues.reverse();
			jQuery('#options-header td.'+column).addClass('asc');
			jQuery('#options-header td.'+column).removeClass('des');
		}
		
		jQuery('#options-header td.'+column).siblings().removeClass('des');
		jQuery('#options-header td.'+column).siblings().removeClass('asc');

		// unique it
		//sortedValues = PDPUtils.unique(sortedValues);
		
		var _table = jQuery("<table id='productlist' />").addClass("productlist");
		
		// Remove all rows and replace them by text value
		var signature;

		jQuery.each(sortedValues, function(val){
			signature = 'td.'+cName+'.'+this;	
			
			var rows2 = table.find(signature);
			_table.append(rows2.parent());		

		});
		
		jQuery('div.scrollable').append(_table[0]);
		PDPUtils._visibleRows = null;
		PDPUtils.oddifyRows(_table[0]);
		
		// Allows for selection of variant rows for add to cart
		jQuery('#productlist tr').rolloverify().click(function(){
			jQuery(this).toggleClass("selected").siblings().removeClass("selected");
			if( jQuery(this).hasClass("selected") ) {
				var sku = this.id.split("-")[1];
				jQuery(".selectedSKU").html("<span class='bold'>Product SKU:</span> <span class='sku'>"+sku+"</span>");
				PDPUtils.enableAddToCart();
				PDPUtils.enableCustomize(sku);
			} else {
				PDPUtils.disableAddToCart();
				PDPUtils.disableCustomize();
				jQuery(".selectedSKU").html("");
			}
		});
	},
	activateGrid : function(){
		// Allows for selection of variant rows for add to cart
		jQuery('#productlist tr').rolloverify().click(function(){
			jQuery(this).toggleClass("selected").siblings().removeClass("selected");
			if( jQuery(this).hasClass("selected") ) {
				var sku = this.id.split("-")[1];
				jQuery(".selectedSKU").html("<span class='bold'>Product SKU:</span> <span class='sku'>"+sku+"</span>");
				PDPUtils.enableAddToCart();
				PDPUtils.enableCustomize(sku);
			} else {
				PDPUtils.disableAddToCart();
				PDPUtils.disableCustomize();
				jQuery(".selectedSKU").html("");
			}
		});
	},
	unique : function(inArray) {
		var arr = inArray || [];
		var result = [];
		for( var i=0, ii=arr.length; i<ii; i++ ) {
			if( !PDPUtils.contains(result, arr[i]) ) {
				result.push(arr[i]);
			}
		}
		return result;
	},

	contains : function(arr, elem) {
		var inArr = arr || [];
		for( var i=0, ii=inArr.length; i<ii; i++ ) {
			if( inArr[i]==elem ) return true;
		}
		return false;
	},

	enableAddToCart : function() {
		jQuery("#main div.addtocart .addtocartBtn").removeAttr("disabled");
		jQuery("#main div.addtocart").removeClass("disabled"); 
	},

	disableAddToCart : function() {
		jQuery("#main div.addtocart .addtocartBtn").attr("disabled", "disabled");
		jQuery("#main div.addtocart").addClass("disabled"); 
	},
		
	enableCustomize : function(sku) {
		jQuery("#CustomizeClubLink").removeClass("balloon-link").parent().removeClass("disabled"); 
	},
	
	disableCustomize : function(sku) {
		jQuery("#CustomizeClubLink").addClass("balloon-link").parent().addClass("disabled");
	},

	oddifyRows : function( table ) {
		var visRows = PDPUtils.visibleRows();
		visRows.filter(':odd').removeClass('odd').addClass('odd');
		visRows.filter(':even').removeClass('odd');
	},
	
	addSelectionToCart : function(el, wrappedSet ) {
		jQuery("#addToCartInstruction").css({display:'none'});
		PDPUtils.disableAddToCart();
		// Look at each one, collect id's and add each to the cart with quantity 1
		var pids = "";
		for( var i=0, ii=wrappedSet.length; i<ii; i++ ) {
			pids+=wrappedSet[i].id.split('-')[1];
			if( i<(ii-1) ) pids+="|";
		}
		if( pids.length==0 ) return false;
		
		var form = jQuery(jQuery(el).parents('form')[0]);
		form.find('input.pid').val(pids);		
		form.submit();		
		return false;
	},
		
	addSelectionToCartLightBox : function(wrappedSet, cgid) {
		PDPUtils.disableAddToCart();
		// Look at each one, collect id's and add each to the cart with quantity 1
		var pids = "";
		for( var i=0, ii=wrappedSet.length; i<ii; i++ ) {
			pids+=wrappedSet[i].id.split('-')[1];
			if( i<(ii-1) ) pids+="|";
		}
		if( pids.length==0 ) return false;
		pids = "pid=" + pids + "&Quantity=1";
		if( cgid ) {
			pids += "&cgid="+cgid;
		}
		pids = encodeURI(pids);

		app.createDialog({
			id:'QuickViewDialog',
			options:{
				width:700,
				height:600,
				dialogClass:"minicart",
				title:"Items Added to Your Cart",
				bgiframe:true,
				draggable:false,
				autoResize:true,
				close:function(e, ui){
					window.location.reload();
					return false;
				},
				open:null
			}
		});
		
		jQuery('#QuickViewDialog').load(app.minicart.url,pids);
		jQuery('#QuickViewDialog').empty().append(app.showProgress());
		jQuery('#QuickViewDialog').dialog('open');
		wrappedSet.removeClass("selected");		
		return false;
	},	
	
	addSelectedVariantToCart : function(el,pid) {
		if (!pid){
			return false;
		}
		var qty = jQuery('#productQty').val()||"1";
		
		var form = jQuery(jQuery(el).parents('form')[0]);
		form.find('input.Quantity').val(qty);
		form.find('input.pid').val(pid);
		
		form.submit();		
		return false;
	},
	
	addSelectedVariantToCartLightbox : function(pid, cgid, cluboptions) {
		PDPUtils.disableAddToCart();
		
		var queryString = "pid=" + pid + "&Quantity=1";
	
		if ( cgid ) {
			queryString += "&cgid="+cgid;
		}
		
		if (cluboptions && (cluboptions.length > 0)) {
			queryString += "&" + cluboptions.join("&"); 
		}
		
		queryString = encodeURI(queryString);

		app.createDialog({
			id:'QuickViewDialog',
			options:{
				width:700,
				height:600,
				dialogClass:"minicart",
				title:"Items Added to Your Cart",
				bgiframe:true,
				draggable:false,
				autoResize:true,
				close:function(e, ui){
					window.location.reload();
					return false;
				},
				open:null
			}
		});
		
		jQuery('#QuickViewDialog').load(app.minicart.url, queryString);
		jQuery('#QuickViewDialog').empty().append(app.showProgress());
		jQuery('#QuickViewDialog').dialog('open');
		return false;
	},
	
	showProductToolTip: function( element ){
		if (element) {
			var toolTipDiv = $('.image_container .productDetailToolTip');
			//if the tool tip div does not exist we just create it
			if (toolTipDiv.size()==0) {
				var imageDiv = $('.image_container').append('<div class="productDetailToolTip"></div>');
				toolTipDiv = $('.image_container .productDetailToolTip');
			}
			var productToolTip = $(element).find('div.productToolTip');
			if (productToolTip.size()>0) {
				toolTipDiv.html(productToolTip.html()).show();
			}
		}
	},

	hideProductToolTip: function( ){
		var toolTipDiv = $('.image_container .productDetailToolTip');
		//if the tool tip div does not exist we just create it
		if (toolTipDiv.size()>0) {
			toolTipDiv.hide();
		}
	},
	
	enhanceDOM : function(root) {
		var base = root||document.body;
		
		//PDPUtils.clearAllRefinements();
		//if( jQuery("#tabs").length>0 ) {
		//	PDPUtils.currentSubCategory = jQuery(jQuery("#tabs a")[0]).text();
		//}
		//PDPUtils.filterForCurrentGenderHandSelection(true);
		jQuery('li.unorderable',base).css({'opacity':0.4}).attr('disabled','disabled');
		
		jQuery("#CustomizeClubLink").unbind(".customizeClub").bind("click.customizeClub", function(){
			if(!jQuery(this).parent().hasClass("disabled")) {
				var customize = {};
				try {
					customize.pid = jQuery('.productlist tr.selected')[0].id.split("-")[1];
					customize.url = app.util.appendParamToURL(app.URLs.getProductCustomizeUrl, "pid", customize.pid);
		
					if ( (jQuery('.productlist tr.selected td.v3') != null) && (jQuery('.productlist tr.selected td.v3').attr("class") != null) )
					{
						var length_av_id = jQuery('.productlist tr.selected td.v3').attr("class").split(" ")[0];
						customize.length_av_name = jQuery('.productlist tr.selected td.v3').text();
						customize.url = app.util.appendParamToURL(customize.url, "length_av_name", customize.length_av_name);
					}
					
					if (  (jQuery('.productlist tr.selected td.v1589') != null) && (jQuery('.productlist tr.selected td.v1589').attr("class") != null) )
					{
						var lie_av_id = jQuery('.productlist tr.selected td.v1589').attr("class").split(" ")[0];
						customize.lie_av_name = jQuery('.productlist tr.selected td.v1589').text();
						customize.url = app.util.appendParamToURL(customize.url, "lie_av_name", customize.lie_av_name);
					}
						
					var customizeDialog = jQuery("#CustomizeClubDialog");
					if(customizeDialog.length == 0) jQuery("#CustomizeClubDialog").remove();
					customizeDialog = jQuery("<div id='CustomizeClubDialog'></div>").appendTo(document.body);
					customizeDialog.load(customize.url);
					customizeDialog.dialog(jQuery.extend(true, {}, app.dialogSettings, {title:"Customize Your Club",width:690,height:500,resizable:false,autoOpen:true}));
					
				} catch(err) {
					jQuery.reportError({
						scriptLog : "Customize Club Launch",
						error : err,
						origin : "app.js",
						data : {
							customizeDialog : customize
						}
					});
					//remove uncooperative link and notify error logs.
					jQuery(this).remove();
				}
			}
		});		
		
		PDPUtils.disableCustomize();
	
		// Opens/closes the variant list
		jQuery('td.plusBtn', base).click(function(){
			var expandosection = jQuery('#expando');
			if( expandosection.hasClass('expandable-section') ) {
				PDPUtils.expandSection(expandosection, function(){PDPUtils.oddifyRows(jQuery('#productlist')[0]);});
			} else {
				PDPUtils.compressSection(expandosection);
			}
			return false;
		});
		
		// Allows for selection of variant rows for add to cart
		jQuery(document).ready(PDPUtils.activateGrid);
		
		// Make unorderable variation attribtues look disabled (PJP_API changed friday)
		//jQuery('.unorderable',base).fadeTo("fast",0.4);
	
		// Filterables
		jQuery('.filterable',base).click(function(){
			if( jQuery(this).hasClass('vGenderHand') ) {
				PDPUtils.filterVariants(this,true);
			} else {
				var enableAdditionalFiltering = false;
				//if vGender attribute is present we check if any of the values was already selected
				//if not we don't allow additional filtering
				if (jQuery('.vGenderHand.filterable').length>0 ) {
					jQuery('.vGenderHand.filterable').each( function(i) {
						if (jQuery(this).parent('li').hasClass('selected')) {
							enableAdditionalFiltering = true;
						}
					})
				} else {
					enableAdditionalFiltering = true;
				}
				if (enableAdditionalFiltering) {
					PDPUtils.filterVariants(this);
				} else {
					alert('Additional filtering is not allowed until you have chosen your Gender/Hand.');
				}
			}
			return false;
		});
	}
};

window.SRPUtils = {};
SRPUtils.enhanceDOM = function(root){
	// init all refinement bindings
	var base = root||document.body;
	
	if(app.search) app.search.updateRefineBindings(base);
	
	// init refinement toggling
//	jQuery("#search-refinements div.navgroup h3",base).click(function(e) {
//		jQuery(this).toggleClass("collapsed");
//		jQuery(this).nextAll("div.refine_attributes").toggle();
//	});
	
	// Search result page behavior
	jQuery('.click-box',base).mouseenter(function(){
		jQuery(this).parents('.product-module').addClass('hover');
	}).mouseleave(function(){
		jQuery(this).parents('.product-module').removeClass('hover');
	});
};

window.minicartShow = function(content) {
	app.createDialog({id: 'QuickViewDialog', options: {
	 	height: 441,
	 	width: 660,
	 	dialogClass: 'minicart',
	 	title: 'Items Added to Your Cart',
	 	resizable: false
	}});
	
	// indicate progress
	jQuery("#content").fadeTo("normal",0.4);

	jQuery('#QuickViewDialog').html(content);

	jQuery('#QuickViewDialog').dialog('open');
};

window.toggleLoginRadio = function( el ) {
	if( el && el.id=="create_account_radio" ) {
		if( el.checked ) {
			jQuery('.checkoutSave').show('normal');
		} else {
			jQuery('.checkoutSave').hide('normal');

		}
	} else if( el && el.id=="guest_checkout_radio" ) {
		if( el.checked ) {
			jQuery('.checkoutSave').hide('normal');
		} else {
			jQuery('.checkoutSave').show('normal');

		}
	}
};

/**
 * A small util to use labels floating over their input fields.
 * This file replaces the unsettling behavior of writing the label's text
 * directly into the input fields. Instead the labels are placed and hidden
 * over their respective input field.
 * 
 * For showing the label's hover effects you can style the div.label.hovered 
 * CSS class.
 */
window.Overlabels = {
	namespace: "Overlabels",
	targets: [".overlabel .value input[type='text']", 
	          ".overlabel .value input[type='password']",
	          ".overlabel .value textarea",
	          ".overlabel .value select"],
	parentClass: ".overlabel",
	labelClass: ".label",
	change: function(e) {
		e.stopPropagation();
		// on blur, re-show it, if the field still has no value
		if (this.value.replace(/^\s*|\s*$/gi, "").length <= 0)
			e.data.label.css({display:"block",opacity:1});
		else if (this.nodeName.toUpperCase() == "SELECT")
			e.data.label.css({display:"none"});
		e.data.parent.removeClass("focus");
	},
	focus: function(e) {
		e.stopPropagation();
		e.data.parent.addClass("focus");
		// on focus, hide/minimize the label too
		if (this.value.replace(/^\s*|\s*$/gi, "").length <= 0)
			e.data.label.css({opacity:0.75});
	},
	keyUp: function(e) {
		e.stopPropagation();
		if (this.value.replace(/^\s*|\s*$/gi, "").length > 0)
			e.data.label.css({display:"none"});
		else e.data.label.css({display:"block"});	
	},
	mouseEnter: function(e) {
		e.stopPropagation();
		e.data.parent.addClass("hover");
	},
	mouseLeave:function(e) {
		e.stopPropagation();
		e.data.parent.removeClass("hover");
	},
	init: function() {
	
		// find all form_fields that are text fields and have a label
		$(Overlabels.targets.join(", ")).each(function(index, element){
			var e_data = {
					parent: $(element).parents(Overlabels.parentClass+":first"), 
					label: null
			};
			e_data.label = e_data.parent.find(Overlabels.labelClass+":first");
			
			// if the field has a value
			if ($(element).val() && $(element).val().replace(/^\s*|\s*$/gi, "").length > 0) {
				e_data.label.css({display:"none"});
			} else {
				e_data.label.show();
			}
			
			// Don't re-apply all the events... 
			if (e_data.parent.hasClass(".applied")) {
				return true;
			}
			
			$(element)
				.bind("focus." + Overlabels.namespace, e_data, Overlabels.focus)
				.bind("change." + Overlabels.namespace, e_data, Overlabels.change)
				.bind("blur." + Overlabels.namespace, e_data, Overlabels.change)
				.bind("keyup." + Overlabels.namespace, e_data, Overlabels.keyUp)
				.bind("mouseenter." + Overlabels.namespace, e_data, Overlabels.mouseEnter)
				.bind("mouseleave." + Overlabels.namespace, e_data, Overlabels.mouseLeave)
				.attr("autocomplete","off");	

			// for the label of the current input field, when mouseover label hides to enable form clicks
			e_data.label
				.bind("mouseenter." + Overlabels.namespace, e_data, Overlabels.mouseEnter)
				.bind("mouseleave." + Overlabels.namespace, e_data, Overlabels.mouseLeave)
				.bind("contextmenu." + Overlabels.namespace, function(e){ //Prevent right-click on label
					e.preventDefault();
					$(element).focus();
					$(this).css({display:"none"});
				})
				.bind("dblclick." + Overlabels.namespace, function(e){$(element).trigger("click");})
				.bind("click." + Overlabels.namespace, function(e){$(element).trigger("click");});
				
			e_data.parent.addClass("applied");
		});
	}
};

$(document).ready(function() {
	Overlabels.init();	
});

/**
 * A small util to use labels floating over their input fields.
 * This file replaces the unsettling behavior of writing the label's text
 * directly into the input fields. Instead the labels are placed and hidden
 * over their respective input field.
 * 
 * For showing the label's hover effects you can style the div.label.hovered 
 * CSS class.
 */
window.ScrollPins = {
	classes: [".scrollpin .pin" ],
		
	init: function() {
		var isIE6 = /msie|MSIE 6/.test(navigator.userAgent);
		var isIE7 = /msie|MSIE 7/.test(navigator.appVersion);
		if(isIE6 || isIE7) return false;
	
		// find all form_fields that are text fields and have a label
		$(ScrollPins.classes.join(", ")).each(function(index, element){
			/* Fix width of fixed area explicit to current inherited width */
			element = $(element).width( $(element).width() ).removeClass("pin").addClass("pinned");
			
			var scrollArea = element.parents(".scrollpin:first");
			var scrollAreaParent = scrollArea.parent().css({position:"relative"});
			scrollArea.css({top:scrollArea.position().top+"px",left:scrollArea.position().left+"px",position:"absolute"});
			
			$(window).bind("scroll.scrollPin", function(e){
				
				if (element.outerHeight(true) + Math.round(scrollArea.position().top) >= scrollAreaParent.height()) {
					scrollArea.css({height:"auto",position:"static"});
					element.removeClass("pinned pinned-bottom pinned-top").addClass("unpinned");
					return true;
				} else if (scrollArea.outerHeight(true) + Math.round(scrollArea.position().top) != scrollAreaParent.innerHeight()) {
					scrollArea.height(scrollAreaParent.innerHeight() - Math.round(scrollArea.position().top) - scrollArea.outerHeight(true) + scrollArea.height());
					scrollArea.css({position:"absolute"});
					element.removeClass("unpinned").addClass("pinned");
				}
				
				if($(this).scrollTop() > (scrollArea.offset().top + scrollArea.outerHeight(true) - element.outerHeight(true)) )
					$(element).addClass("pinned-bottom").removeClass("pinned-top floating");
				else if ($(this).scrollTop() > scrollArea.offset().top)
					$(element).addClass("floating").removeClass("pinned-top pinned-bottom");
				else $(element).addClass("pinned-top ").removeClass("floating pinned-bottom");
				return true;				
			});
			$(window).bind("unload.scrollPin", function(e){
				element.removeClass("pinned pinned-bottom pinned-top").addClass("pin unpinned");				
			});

		});
	}
};

$(document).ready(function() {
	ScrollPins.init();
});
