/*
 * All java script logic for the application.
 *
 * The code relies on the jQuery JS library to
 * be also loaded.
 */

var app = (function(jQuery){

	if (!jQuery) {
		alert("jQuery is not loaded yet!");
		return null;
	}
	
	// Global dw private data goes here	

	// dw scope public
	return {
		URLs			: {}, // holds dw specific urls, check htmlhead.isml for some examples
		resources		: {},  // resource strings used in js
		constants		: {}, // platform constants, initialized in htmlhead.isml
		containerId		: "content",
		ProductCache	: null,  // app.Product object ref
		clearDivHtml	: "<div class=\"clear\"><!-- W3C Clearing --></div>",
		currencyCodes	: {}, // holds currency code/symbol for the site

		// default dialog box settings
		dialogSettings: {
				autoOpen: false,
				buttons: {},
				modal: true,
				overlay: {
		    		opacity: 0.9,
		     		background: "#666666"
				},
		    	height: 530,
		    	width: 800,
		    	title: '',
		    	// show: "slow", This is causing dialog to break in jquery 1.3.2 rel, show: "slide" works but not desired
		    	hide: "normal",
		    	resizable: false,
		    	bgiframe: true
		},

		// default tooltip settings
		tooltipSettings: {
				delay: 0,
				showURL: false,
				extraClass: "",
				top: 15,
				left: -100
		},

		// global form validator settings
		validatorSettings: {
			errorClass : 'error_message',
			errorElement: 'span',
			
			
			onfocusout: function(element) {
				if ( element.name in this.submitted || element == this.lastElement ) {
					this.element(element);
				}
			},
			onkeyup: false,
		    onsubmit: function(element) {
				if ( !this.checkable(element) ) {
					this.element(element);
				}
			}
		},

		// app initializations called from jQuery(document).ready at the end of the file
		init: function() {

			// register initializations here
			// quick view dialog div
			jQuery("<div/>").attr("id", "QuickViewDialog").html(" ").appendTo(document.body);

			// micicart object initialization
			app.minicart.init();
			
			// initialize form validator customizations
			app.validator();

			// extract hidden data and turn them into jQuery data objects
			app.hiddenData();
			
			// renders horizontal/vertical carousels for product slots
			jQuery('#horicarousel').jcarousel({
	        	scroll: 1,
	        	// Add call to Demandware Javascript function for Active Merchandising
	        	itemVisibleInCallback: app.captureCarouselRecommendations
		    });

		    jQuery('#vertcarousel').jcarousel({
		        scroll: 1,
				vertical: true,
	        	// Add call to Demandware Javascript function for Active Merchandising
	        	itemVisibleInCallback: app.captureCarouselRecommendations
		    });	
		    
		    // attach click handler to print window buttons/links.
		    jQuery(".printwindow").click(function(){
		    	window.print();
		    	return false;
		    });	
		},
	
		// 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
							}
							
							if ( 'callback' in options && options.callback )
								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
					var newOptions = {
						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);
						}
					};
					if ( options.type )
						newOptions.type = options.type; 
					jQuery.ajax( newOptions );
				}
			}
		},

		// 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;
			var a2cBtnLabelSelector = options.labelSelector || ".addtocart_button:last";
			
			

			// 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){
				if(options.callback != undefined) {
					options.callback();
				}
				
				// update the Add to cart button label if one provided
				(a2cBtnLabel != null ? jQuery("#"+cId+" "+ a2cBtnLabelSelector).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
			showCartUrl : '',
			timer : null, // timer for automatic close of cart item view
			
			currentItemIndex: 0,
			maxItemIndex: 0,

			// initializations
			init: function() {
				// reset all the existing event bindings
				app.minicart.reset();

				// bind click event to the cart total link at the top right corner
				jQuery(".minicarttotal").livequery( "click", function(e){
					
					if (typeof(IS_PT_CHECKOUT) != "undefined")
					{
						if (IS_PT_CHECKOUT){
						 return false;	
						}
					}
					window.location.href = app.minicart.showCartUrl;
					//alert(app.minicart.url);
					//(app.minicart.isShow() ? app.minicart.close(0) : app.minicart.slide());
					return false;
				} );
				jQuery(".minicarttotal")
					.livequery(function () {
			    	jQuery(this)
			    		.hover(function() {	    			
			    			if (typeof(IS_PT_CHECKOUT) != "undefined")
							{
								if (IS_PT_CHECKOUT){
								 return false;	
								}
							}
			    			(app.minicart.isShow() ? app.minicart.close(0) : app.minicart.slide());
							return false;
			    		}, function () {
			    			if (typeof(IS_PT_CHECKOUT) != "undefined")
							{
								if (IS_PT_CHECKOUT){
								 return false;	
								}
							}
			    			if(app.minicart.isShow())  { app.minicart.timer = setTimeout('app.minicart.close(0)', 100); }
			    			return false;
			    		});
			     }, function() { 
			        // unbind the mouseover and mouseout events 
			        jQuery(this) 
			            .unbind('mouseover') 
			            .unbind('mouseout'); 
			    });
			
				
				jQuery('#minicartcontent').livequery( "mouseenter", function(e) {					
					clearTimeout(app.minicart.timer);
					app.minicart.timer = null;
				}).livequery( "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').livequery( "click", function() {
					// reset all the events bindings
					//app.minicart.reset();
					app.minicart.close(0);
				});
				
				jQuery("div.minicartitem:not(.showing)").livequery( function(){
						// alert( this );
						jQuery( this ).hide();
				});
			},
			
			// 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("click");
				jQuery(".minicarttotal").unbind("mouseover");
				jQuery(".minicarttotal").unbind("mouseout");
				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;
				}
				if(app.searchsuggest.hasFocus)
				{
					app.searchsuggest.acSearchField.blur();					
				}
				
				// show the item
				 setTimeout( 'jQuery("#minicartcontent").slideDown("fast")', 30 ); //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()', 2000000 );
			},


			// adds a product to the mini cart
			add: function(progressImageSrc, postdata, callback)
			{
				//alert("Are We Getting Here?");
				// 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)	{
					document.body.style.cursor = "default";
					// hide progress indicator
					if (addButton != null) {
						addButton.src = previousImageSrc;
					}
					
					// replace the content
					
					jQuery('#minicart').html(req);
					

					if(app.minicart.suppressSlideDown && app.minicart.suppressSlideDown()) {
						// do nothing
						// the hook 'MiniCart.suppressSlideDown()' should have done the refresh
					}
					else {
						app.minicart.slide();

						if (callback) callback();
					}
				}

				// handles add to cart error
				var errFunc = function(req) {
					document.body.style.cursor = "default";
					// hide progress indicator
					if (addButton != null) {
						addButton.src = previousImageSrc;
					}				}

				// closes a previous mini cart
				app.minicart.close();
				
				document.body.style.cursor = "wait";

				// 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;	
					
					app.minicart.currentItemIndex = 0;
					jQuery('#minicartcontent').hide(); 
					//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;
			},
			
			// pages forth within the minicart items
			pageForth: function() {
				app.minicart.showItem(app.minicart.currentItemIndex + 1);
			},
			
			// pages back within the minicart items
			pageBack: function() {
				app.minicart.showItem(app.minicart.currentItemIndex - 1);
			},
			
			// helper function to show the minicart item with at given item index
			showItem: function(itemIndex) {
				if(itemIndex < 0 || itemIndex > app.minicart.maxItemIndex) {
					return;
				}
				
				app.minicart.currentItemIndex = itemIndex;
				jQuery("div.minicartitem").hide();
				jQuery("div#minicartitem-" + itemIndex).show();
				jQuery("div#minicartcontent span.current").html(itemIndex + 1);
			}
		},

		// 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, useLiveQuery ) {
				// hide quickview buttons
				if(options.buttonSelector != "")
				{ // if something got passed in, so I don't break existing functionality
					jQuery(options.buttonSelector).hide();
				}
				// hovering
				if(options.imageSelector != "")
				{ // if something got passed in, so I don't break existing functionality
					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
				if ( useLiveQuery )
				{
					jQuery(options.buttonLinkSelector).livequery( "click", function(e) {
						app.quickView.show({url: this.href, source: "quickview"});
						return false;
					});
				}
				else
				{
					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: 490,
					//height: 400,
			    	width: 560,
			    	dialogClass: 'quickview',
			    	title: '',
			    	resizable: false,
			    	hide: false
				}});

				// the Ajax request requires the same protocol as the 
				// main window.  There are some issues with the Bazaarvoice
				// that requires all the time a HTTP communication :-(
				// Sick and tired of the Same Origin issue as I am now,
				// I always set the protocol for the request to the 
				// same as in the main window.
				var ref = options.url;
				if ( ref.indexOf( window.location.protocol ) != 0 )
				{
					// URL doesn't start with the same protocol
					// see if we have the // in the URL
					if ( ref.indexOf( '://' ) != -1 )
					{
						ref = window.location.protocol + ref.substring( ref.indexOf( '://' ) + 1 );
					}
					
				}
			    jQuery('#QuickViewDialog').dialog('open');
			    jQuery(".ui-widget-overlay").click(function() {
			    	app.quickView.close();
			    });
			    app.getProduct({containerId: "QuickViewDialog", source: options.source, url: ref, label: options.label, labelSelector: options.labelSelector, callback: options.open});
			},
			// close the quick view dialog
			close: function() {
				jQuery('#QuickViewDialog').dialog('close');
			}
		},


		createDialog: function(options) {
			jQuery('#'+options.id).dialog(jQuery.extend({}, app.dialogSettings, options.options));
			jQuery('#'+options.id).dialog(jQuery.extend({}, app.dialogSettings, options.options));
		},


		tooltip: function(options) {
			if (options.id.charAt(0) !== '#') {
				options.id = "#"+options.id;
			}
			jQuery(options.id).tooltip(jQuery.extend({}, app.tooltipSettings, 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
		showProgress : function(className) {
			var clazz = "loading";
			if (className) clazz += " " + className;
			return jQuery("<div class=\"" + clazz + "\"/>").append(jQuery("<img/>").attr("src", app.URLs.loadingSmallImg));
		},
		// 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
		showSearchProgress : function(className) {
			var clazz = "loading";
			if (className) clazz += " " + className;
			return jQuery("<div class=\"" + clazz + "\"/>").append(jQuery("<img/>").attr("src", app.URLs.loadingSearchImg));
		},

		/*
		 *	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");
				}
			}
		},


		validator: function() {
			// override default required field message
			jQuery.validator.messages.required = function($1, ele, $3) {
				return 'Please Enter '+jQuery(ele).data("data");
			};

			// register form validator for form elements
			// except for those which are marked "suppress"
			jQuery.each(jQuery("form:not(.suppress)"), function() {
				jQuery(this).validate(app.validatorSettings);
			});
		},

		// grab anything inside a hidden span and append it to its immediate previous sibling
		hiddenData : function() {			
			jQuery.each(jQuery(".hidden"), function() {				
				var hiddenStr = jQuery(this).html();
				
				if (hiddenStr === "") {
					return;
				}
				
				// see if its a json string
				if (jQuery(this).hasClass("json")) {
					// try to parse it as a json
					try {
						hiddenStr = window["eval"]("(" + hiddenStr + ")");
					}
					catch(e) {}				
				}
				
				jQuery(this).prev().data("data", hiddenStr);
				
				//jQuery(this).remove();
			});
		},

		// capture recommendation of each product when it becomes visible in the carousel
		captureCarouselRecommendations : function(c, li, index, state) {
			jQuery(li).find(".captureproductid").each(function() {
				dw.ac.capture({id:this.innerHTML, type:dw.ac.EV_PRD_RECOMMENDATION});
			});
		},
		
		// sub namespace app.producttile.* contains utility functions for product tiles
		producttile : {
			// initializes all product tiles contained in the current page
			initAll: function() {
				// bind quick view button toggling and click
				var quickViewOptions = {
					buttonSelector: "div.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);
					alert('x');
					app.producttile.toggleVariationThumbnail(swatch);
				});
				
				// color swatch selection
				jQuery("div.producttile div.swatches div.palette a.swatch").click(function(e) {
					var swatch = jQuery(this);
					alert('c');
					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
			// we need to set it in a state as if the mouse would be out and current 
			// swatch is the selected one!
			selectVariation : function(swatch) {
				// get the new and the original visible big image
				
				var currentImg = jQuery(swatch.parents()[3]).find(".productimage img");
				if ( currentImg.length == 0 )
					currentImg = swatch.parents( '.repeatedItemA').find(".productImage img");
				
				// now get the new hidden big image
				var newImg = swatch.children("img.hiddenthumbnail.original");
				if(!currentImg || !newImg) return;
				if ( newImg.length == 1 )
				{
					// OK, here we have the situation that the saved thumbnail
					// image was a different variation then the currently selected one.
					// hence the hidden image is different then the currently showing.
					// we need to clone the currently showing back to the hidden
					// thumbnail.
					newImg.replaceWith( currentImg.removeClass( 'temp swatchimage' ).clone().hide().addClass("swatchimage hiddenthumbnail") );
					// newImg.replaceWith( currentImg.removeClass( 'temp' ) ).clone().hide(); // .removeClass("original temp hiddenthumbnail") );
				}
				else
				{
					
					// 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");
				}
				// remove all current markers
				jQuery(swatch.parents()[0]).find("a.swatch").removeClass("selected");

				// mark swatch as selected
				swatch.addClass("selected");

				// change the link url to the detail page and quick view
				var newUrl = swatch.attr("href");

				// get the anchors
				var nameAnchor = swatch.parents(".producttile").find(".name a");
				// var quickViewAnchor = swatch.parents(".producttile").find(".quickview-button a");
				var quickViewAnchor = swatch.parents( '.repeatedItemA').find(".hover a" );
				var imageAnchor = swatch.parents(".producttile").find(".productImage a");
				
				// set new href
				nameAnchor.attr("href", newUrl);
				quickViewAnchor.attr("href", newUrl);
				imageAnchor.attr("href", newUrl);
				
			},

			// 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");
				if ( currentImg.length == 0 )
				{
					currentImg = swatch.parents( '.repeatedItemA').find(".productImage img");
					// [AA] fix for featuredProduct:
					if ( currentImg.length == 0 )
					{
					    
						currentImg = swatch.parents( '.featuredProduct').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.attr("src", newCopy[0].src);
					//there is no need to replace the hidden image
					//newImg.attr("src", currentCopy[0].src);					
				} 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.attr("src", newCopy[0].src);
					//there is no need to replace the hidden image 
					//newImg.attr("src", currentCopy[0].src);					
				}
			}
		},
		// sub namespace app.dealer.* contains Dealer Locator 
		dealer : {
			// some constants
			startIndex : 0,
			index : 0,
			dealerCount : 0,
			totalDealers : 0,
			elementsThatFit : 1,
			elementWidth : 212, // this is the width of a local Dealer
			windowWidth : 212, // this is the width of the viewable area
			dealerWidgetWidth : 413 - 212,
			firstPassDone : false,
			offSet : 0,
			nextLinkVisible : true,
			url: null,
			
			// init function
			// wires up all the necessary event listeners
			init : function(options) {
				// but first check to see if there are dealers provided already
				if(jQuery("td.footerLocalDealersLeft").length == 0)
				{
					// then there was an error with the service don't bother
				}
				else 
				{
					// Always make this call
					var options = { };
					app.dealer.getDealers(options);
					app.dealer.bindEvents();
				}
			},
			// get the dealers
			getDealers : function (options) {
				var url = options.url || app.dealer.url;
				var data = options.data || {};
				// post the data and replace current content with response content
		  		jQuery.ajax({
				   type: "POST",
				   url: url,
				   data: data,
				   dataType: "html",
				   success: function(data){
		  				
		  				jQuery("td.footerLocalDealersLeft").addClass("footerLocalDealersRight");
		  				jQuery("td.footerLocalDealersRight").removeClass("footerLocalDealersLeft");
		  				
		  				//alert(jQuery("#leftFooter tr").html());
		  				jQuery("#leftFooter tr").prepend("<td class=\"footerLocalDealersLeft\">" + data + "</td>");
		  				app.dealer.bindEvents();
		  				
		  				
		  				//alert(jQuery("#leftFooter tr").html());
		  				if ( 'callback' in options && options.callback ){
		  					options.callback(data);
		  				}
				   },
				   failure: function(data) {

					   alert("Server connection failed!");
				   }
				});
			},
			// resize event
			handleResize : function () {
				var actualWidth = jQuery(window).width() - 50 - 50 - 425 - 145 - 30 - 40 - (app.dealer.elementsThatFit * app.dealer.elementWidth);
				
				// before we recalculate the number of elements that fit remove the ones that are there and put them back in the holding tank
				app.dealer.index = app.dealer.startIndex;
				
				if (actualWidth - 35 > app.dealer.elementWidth || (actualWidth) * -1 > app.dealer.elementWidth) {
					app.dealer.elementsThatFit = app.dealer.elementsThatFit + (Math.floor(actualWidth / app.dealer.elementWidth));
					app.dealer.elementsThatFit = app.dealer.elementsThatFit < 1 ? 1 : app.dealer.elementsThatFit;
					if(app.dealer.elementsThatFit >= app.dealer.totalDealers)
					{
						app.dealer.elementsThatFit = app.dealer.totalDealers;
						if(app.dealer.nextLinkVisible) {jQuery("#next").hide();app.dealer.nextLinkVisible=false;}
					}
					else
					{
						// undo that shit
						if(!app.dealer.nextLinkVisible) {jQuery("#next").show();app.dealer.nextLinkVisible=true; }
					}
					jQuery("#leftFooter").css("width", ((app.dealer.elementsThatFit * app.dealer.elementWidth) + app.dealer.dealerWidgetWidth) + "px");
					jQuery("#window").css("width", (app.dealer.elementsThatFit * app.dealer.elementWidth)+"px");
				}
				else if (actualWidth -35 < 0) {
					if(app.dealer.elementsThatFit > 1){
						app.dealer.elementsThatFit--;
					}else if (app.dealer.elementsThatFit < 1) {
						app.dealer.elementsThatFit = 1;
					}
					
					if(app.dealer.elementsThatFit >= app.dealer.totalDealers)
					{
						app.dealer.elementsThatFit = app.dealer.totalDealers;
						if(app.dealer.nextLinkVisible){jQuery("#next").hide();app.dealer.nextLinkVisible=false;}
					}
					else
					{
						// undo that shit
						if(!app.dealer.nextLinkVisible){jQuery("#next").show();app.dealer.nextLinkVisible=true;}
					}
					jQuery("#leftFooter").css("width", ((app.dealer.elementsThatFit * app.dealer.elementWidth) + app.dealer.dealerWidgetWidth) + "px");
					jQuery("#window").css("width", (app.dealer.elementsThatFit * app.dealer.elementWidth)+"px");
				}
			},
			bindEvents : function () {
				jQuery(window).resize(app.dealer.handleResize);
				app.dealer.handleResize();
				// wireup the next button
				jQuery("#next").click(app.dealer.handleNextClicked);
			},
			handleNextClicked : function () {
				app.dealer.offSet++;
				var leftString = (app.dealer.offSet*app.dealer.elementWidth*-1);
				jQuery("#dealerPane").animate({left: leftString + "px"}, 400, app.dealer.handleCallBack);
			},
			
			handleCallBack : function () {
				if(app.dealer.offSet >= app.dealer.totalDealers) {
					app.dealer.offSet = 0;
					jQuery("#dealerPane").css("left", "0px");
				}
			}
			
		},
		// sub namespace app.util.* contains utility functions
		util : {
			// disables browser auto completion for the given element
			disableAutoComplete : function(elemId) {
				jQuery("#"+elemId).attr("autocomplete", "off");
			},

			// trims a prefix from a given string, this can be used to trim
			// a certain prefix from DOM element IDs for further processing on the ID
			trimPrefix : function(str, prefix) {
				return str.substring(prefix.length);
			},

			// appends the parameter with the given name and
			// value to the given url and returns the changed url
			// allows the name to be an object.  two variations:
			// 1  { 'param1', 'value1', 'param2', 'value2' }
			// 2  [ { name: 'param1', value: 'value1'}, {name: 'param2', value: 'value2' }]
			appendParamToURL : function(url, name, value) {
				if ( typeof name == 'object' )
				{
					return url + '?' + jQuery.param( name );
				}
				
				var c = "?";
				if(url.indexOf(c) != -1) {
					c = "&";
				}
				return url + c + name + "=" + encodeURIComponent(value);
			},

			// dynamically loads a CSS file
			loadCSSFile : function(url) {
				var elem = document.createElement("link");
				elem.setAttribute("rel", "stylesheet");
				elem.setAttribute("type", "text/css");
				elem.setAttribute("href", url);

				if(typeof elem != "undefined") {
					document.getElementsByTagName("head")[0].appendChild(elem);
					app.util.loadedCSSFiles.push(url);
				}
			},

			// array to keep track of the dynamically loaded CSS files
			loadedCSSFiles : [],

			// removes all dynamically loaded CSS files
			clearDynamicCSS : function() {
				for(var i=0; i<app.util.loadedCSSFiles.length; i++) {
					app.util.unloadCSSFile(app.util.loadedCSSFiles[i]);
				}
			},

			// dynamically unloads a CSS file
			unloadCSSFile : function(url) {
				var candidates = document.getElementsByTagName("link");
				for(var i=candidates.length; i>=0; i--) {
					if(candidates[i] && candidates[i].getAttribute("href") != null && candidates[i].getAttribute("href").indexOf(url) != -1) {
						candidates[i].parentNode.removeChild(candidates[i]);
					}
				}
			},

			// checks if cookies are enabled
			cookiesEnabled : function() {
				var currentCookie = document.cookie;
				document.cookie = "Enabled=true";
				var cookieValid = document.cookie;
				var result = false;

				if(cookieValid.indexOf("Enabled=true") != -1) {
					result = true;
				}

				document.cookie = currentCookie;
				return result;
			},
			
			// changes the selection of the given form select to the given value
			changeFormSelection: function (selectElem, selectedValue) {
				if(!selectElem) return;
				var options = selectElem.options;
				if(options.length > 0) {
					// find index of value to select
					var idx = 0;
					for(var i=0; i<options.length; i++) {
						if(options[i].value != selectedValue) continue;
						idx = i; break;
					}
					selectElem.selectedIndex = idx;
				}
			}
		},

		// 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, dialogClass, myOptions) {
				// 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";
				
				//set a default class
				dialogClass = dialogClass || "";

				// finally load the dialog, set the dialog title
				var newOptions = {
					selector: "#dialogcontainer",
					url: url,
					callback: function() {
						app.dialog.checkOpen( myOptions );
						app.dialog.setTitle(title);
						app.dialog.setClass(dialogClass);
						if ( myOptions )
						{
							app.dialog.setSize( myOptions );
						}
						// wireup click event for when the user clicks on the greyed out area (outside of the popup)
						jQuery(".ui-widget-overlay").click(function() {
					    	app.dialog.close();
					    });
					}
				};
				if ( myOptions )
				{
					if ( myOptions.type )
						newOptions.type = myOptions.type;
					if ( myOptions.data )
						newOptions.data = myOptions.data;
				}
				app.ajax.load( newOptions );
				//alert("Length: " + jQuery(".ui-widget-overlay").length);
				
			},

			// 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);
			},
			
			// sets the class of the dialog
			setClass : function(className) {
				jQuery("#dialogcontainer").addClass(className);
				jQuery(".ui-dialog-titlebar").addClass(className);
				jQuery("#dialogcontainer").addClass(className+"-container");
				jQuery(".ui-dialog-titlebar").addClass(className+"-titlebar");
			},			
			

			// 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( myOptions) {
			
				if(!jQuery("#dialogcontainer").dialog("isOpen"))
				{
					if ( !myOptions )
					{
						myOptions = {};
					}
					jQuery("#dialogcontainer").dialog({
						autoOpen: false,
						modal: true,
						overlay: {
				    		opacity: 0.9,
				     		background: "#666666"
						},
				    	height: myOptions.outerHeight ? myOptions.outerHeight : 425,
				    	width: myOptions.outerWidth ? myOptions.outerWidth : 460,
				    	resizable: false,
				    	bgiframe: true
					});
					jQuery("#dialogcontainer").dialog("open");
				}
			},
			
			setSize: function( innerWidth, outerWidth, innerHeight, outerHeight )
			{
				if (typeof innerWidth == 'object' )
				{
					outerHeight = ( 'outerHeight' in innerWidth ? innerWidth['outerHeight'] : null );
					innerHeight = ( 'innerHeight' in innerWidth ? innerWidth['innerHeight'] : null );
					outerWidth  = ( 'outerWidth'  in innerWidth ? innerWidth['outerWidth']  : null );
					innerWidth  = ( 'innerWidth'  in innerWidth ? innerWidth['innerWidth']  : null );
				}
				if ( innerWidth != null )
				{
					jQuery( "#dialogcontainer" ).width( innerWidth );
				}
				if ( innerHeight )
				{
					jQuery( "#dialogcontainer" ).height( innerHeight );
				}
				// for the outer it is a little bit more sophisticated.
				if ( outerWidth != null )
				{
					jQuery( "#dialogcontainer" ).dialog( 'option', 'width', outerWidth );
				}
				if ( outerHeight )
				{
					jQuery( "#dialogcontainer" ).dialog( 'option', 'height', outerHeight );
				}
			},

			// closes the dialog and triggers the "close" event for the dialog
			close : function() {
				jQuery("#dialogcontainer").dialog("close");
				jQuery(document.body).trigger("dialogClosed");
			},

			// attaches the given callback function upon dialog "close" event
			onClose : function(callback) {
				if(callback != undefined) {
					jQuery(document.body).bind("dialogClosed", callback);
				}
			},

			// triggers the "apply" event for the dialog
			triggerApply : function() {
				jQuery(document.body).trigger("dialogApplied");
			},

			// attaches the given callback function upon dialog "apply" event
			onApply : function(callback) {
				if(callback != undefined) {
					jQuery(document.body).bind("dialogApplied", callback);
				}
			},

			// triggers the "delete" event for the dialog
			triggerDelete : function() {
				jQuery(document.body).trigger("dialogDeleted");
			},

			// attaches the given callback function upon dialog "delete" event
			onDelete : function(callback) {
				if(callback != undefined) {
					jQuery(document.body).bind("dialogDeleted", callback);
				}
			},

			// submits the dialog form with the given action
			submit : function(action) {
				// set the action
				jQuery("#dialogcontainer form").append("<input name=\"" + action + "\" type=\"hidden\" />");

				// serialize the form and get the post url
				var post = jQuery("#dialogcontainer form").serialize();
				var url = jQuery("#dialogcontainer form").attr("action");

				// post the data and replace current content with response content
		  		jQuery.ajax({
				   type: "POST",
				   url: url,
				   data: post,
				   dataType: "html",
				   success: function(data){
		  				jQuery("#dialogcontainer").empty().html(data);
				   },
				   failure: function(data) {

					   alert("Server connection failed!");
				   }
				});
			}
		}
	}
})(jQuery);

// application initialization on dom ready
jQuery(document).ready(function(){
	app.init();
	// INIT dealers: this checks to see if there are dealers and setup the json call if it needs to happen
	app.dealer.init();
});
