ECommerce_DF = new function()
{
	/**************************** Private Properties **************************/
	
	var myRunning = false;
	
	var myLoadSettingsUrl = 'cart_settings.php';
	var myReportErrorUrl = 'report_error.php';
	
	var myUrlPrefix = '';
	
	var myViewCartMaskDiv = false;
	var myViewCartContainerDiv = false;
	var myViewCartInnerDiv = false;
	var myCartDiv = false;
	
	var myCartDivStatus = false;
	
	var myLoginContainerDiv = false;
	var myLoginDiv = false;
	
	var myLoginDivStatus = false;
	
	var myEditItemForm = false;
	
	var myCartItems = false;
	var myCartData = false;
	
	var myCartTotal = 0;
	var myTax = 0;
	
	var myCouponDiscount = 0;
	
	var myShipment = {};
	
	var myStrings = {};
	var myUrls = {};
	
	var myAddLoginLink, myUseImageButtons;
	
	var myItemIndex = false;
	
	var myUserInformation;
	
	var myCheckoutForm = false;
	
	var myCheckoutFormParts = {};
	
	var myHandlers = {
		loaded: $A(),
		cartDisplay: $A(),
		cartItemAdded: $A(),
		cartHide: $A(),
		cartRefreshed: $A(),
		showLogin: $A(),
		loginShown: $A()
	};
	
	var myShippingMethods = false;
	
	var myECommerce_DF_button_click_handler;
	
	var mySuspendSaveCheckoutInfo = false;
	
	
	/***************************** Private Methods ****************************/
	
	var myRunHandlers = function(type,args)
	{	
		if(myHandlers[type])
		{
			myHandlers[type].each(function(h){h(args);}.bind(this));
		}
	};
	
	var myReportError = function(e)
	{
		try
		{	
			var report = {
				running: myRunning,
				myCartDivStatus: myCartDivStatus,
				myLoginDivStatus: myLoginDivStatus,
				myCartItems: myCartItems,
				myCartData: myCartData,
				myCartTotal: myCartTotal,
				myTax: myTax,
				myShipment: myShipment
			};
			
			if(e.error_function) { report.errorFunction = e.error_function; }
			if(e.error_handler) { report.errorHandler = e.error_handler; }
			if(e.extraInfo) { report.extraInfo = e.extraInfo; }
			
			
			DFTools.reportError(e,'ECommerce_DF',report);
		}
		catch (error_error)
		{
			// Damn, we just can't win. Error when reporting an error!
			// Just give up and ignore this error.
			
			DFTools.console.log("Error while reporting en error!!",error_error);
		}
	};
	
	var myLoginErrorHandler = function(t,e)
	{
		myLoginDiv.setStyle({cursor:'auto'});
		
		DFTools.console.error("Error in login AJAX request. Requester object: ",t);
		if(e){ DFTools.console.log("Exception: ",e); }
		alert(myStrings.loginError);
	};
	
	var myProcessLoginAjax = function(t)
	{
		if(!t.responseJSON)
		{
			DFTools.console.error("myProcessLoginAjax: AJAX response was not in JSON format...");
			DFTools.console.log("Response was: "+t.status+' ('+t.statusText+')');
			DFTools.console.log("Response text was: "+t.responseText);
			throw new TypeError("AJAX request did not return JSON data");
		}
		
		if(t.responseJSON.success)
		{
			var form = myLoginDiv.down('form');
			
			form.action = t.responseJSON.postToUrl;
			
			form.submit();
		}
		else
		{
			if(!t.responseJSON.error && t.responseJSON.reason)
			{
				alert(myStrings["loginError_"+t.responseJSON.reason]);
			}
			else
			{
				DFTools.console.log("AJAX login request returned success:false. Requester object: ",t); 
				
				myLoginDiv.setStyle({cursor:'auto'});
				
				var msg = myStrings.loginError;
				
				if(t.responseJSON.message)
					msg += " " + t.responseJSON.message;
				
				alert(msg);
			}
		}
		
	}.bind(this);
	
	var myLoginFormSubmitHandler = function(e)
	{
		var el = Event.element(e);
		
		var form = el.up().hasClassName('container') ? el : el.up('form');
		
		
		var accountType = $F(form.account);
		
		
		// Pre-Check the form:
		
		try
		{
			switch(accountType)
			{
				case 'existing':
					if($F(form.username).length < 3)
					{
						DFTools.console.log("ECommerce_DF: Existing login failed; username.length < 3");
						form.username.activate();
						throw new Error(myStrings.loginError_usernameRequired);
					}
					
					if($F(form.password).length < 1)
					{
						DFTools.console.log("ECommerce_DF: Existing login failed; password.length < 1");
						form.password.activate();
						throw new Error(myStrings.loginError_passwordRequired);
					}
					
					break;
					
				
				case 'create':
					if($F(form.newUsername).length < 3)
					{
						DFTools.console.log("ECommerce_DF: Create account failed; newUsername.length < 3");
						form.newUsername.activate();
						throw new Error(myStrings.loginError_usernameRequired);
					}
					
					if($F(form.newPassword).length < 1)
					{
						DFTools.console.log("ECommerce_DF: Create account failed; newPassword.length < 1");
						form.newPassword.activate();
						throw new Error(myStrings.loginError_passwordRequired);
					}
					
					if($F(form.newPassword) != $F(form.passwordRepeat))
					{
						DFTools.console.log("ECommerce_DF: Create account failed; newPassword != passwordRepeat");
						form.newPassword.clear();
						form.passwordRepeat.clear();
						form.newPassword.activate();
						throw new Error(myStrings.loginError_passwordMatch);
					}
					
					break;
					
				
				case 'dontcreate':
					break;
					
				default:
					throw new Error(myStrings.loginError_selectAccountType);
			}
			
			myLoginDiv.setStyle({cursor:'wait'});
			
			DFTools.console.log("ECommerce_DF: Submitting AJAX login request...");
			
			form.request({
				parameters: {
					password: SHA1($F(form.password)),
					newPassword: SHA1($F(form.newPassword)),
					passwordRepeat: ''
				},
				onSuccess: myProcessLoginAjax,
				onException: myLoginErrorHandler,
				onFailure: myLoginErrorHandler
			});
		}
		catch(e)
		{
			alert(e.message);
		}
		
		Event.stop(e);
	};
	
	var myLoginInputClickHandler = function(e)
	{
		myLoginDiv.down('form').account.value = Event.element(e).up('div').className;
	}.bindAsEventListener(this);
	
	var myLoginInputKeydownHandler = function(e)
	{
		if(e.keyCode == Event.KEY_RETURN)
		{
			myLoginFormSubmitHandler(e);
		}
	}.bindAsEventListener(this);
	
	var myBuildLoginDiv = function()
	{
		var closeDiv,closeLink,btn;
		
		myLoginContainerDiv = new Element('div',{id:'eCommerce_DF_login_div'});
		myLoginDiv = new Element('div').addClassName('container');
		
		myLoginContainerDiv.appendChild(myLoginDiv);
		
		closeDiv = new Element('div').addClassName('close');
		closeLink = new Element('a',{href:'#'}).update(myStrings.loginCloseLinkText);
		closeLink.observe('click',myCloseLoginLinkHandler);
		closeDiv.appendChild(closeLink);
		
		myLoginContainerDiv.appendChild(closeDiv);
		
		
		myLoginDiv.appendChild(new Element('h1').update(myStrings.loginHeading));
		
		var form = new Element('form',{method:'post',action:myUrls.login});
		form.observe('submit',myLoginFormSubmitHandler);
		
		// Type of account:
		
		var accountType = new Element('input',{type:'hidden',name:'account'});
		form.appendChild(accountType);
		
		// Temporary local vars to hold labels and groups we'll be creating
		
		var label, group;
		
		
		// Existing account username and password fields:
		
		group = new Element('div').addClassName('existing');
		
		group.appendChild(new Element('h2').update(myStrings.loginLabelFor_existingAccount));
		
		label = new Element('label').update(myStrings.loginLabelFor_existingAccountUsername);
		label.insert({bottom: new Element('input',{type:'text',name:'username'})});
		group.appendChild(label);
		
		label = new Element('label').update(myStrings.loginLabelFor_existingAccountPassword);
		label.insert({bottom: new Element('input',{type:'password',name:'password'})});
		group.appendChild(label);
		
		if(myUseImageButtons)
		{
			btn = new Element('a',{href:'#'}).addClassName('button').update(myStrings.login_existingAccountButtonText);
			btn.stopObserving('click',myECommerce_DF_button_click_handler).observe('click',function(e)
			{
				this.value='existing';
				myECommerce_DF_button_click_handler(e)
			}.bindAsEventListener(accountType));
			group.appendChild(btn.wrap(new Element('span').addClassName('ECommerce_DF').addClassName('button')))
		}
		else
		{
			group.appendChild(new Element('input',{type:'submit',value:myStrings.login_existingAccountButtonText}));
		}
		
		form.appendChild(group);
		
		
		// Create account username and password fields:
		
		group = new Element('div').addClassName('create');
		
		group.appendChild(new Element('h3').update(myStrings.login_textBetweenMethods));
		
		group.appendChild(new Element('h2').update(myStrings.loginLabelFor_createAccount));
		
		label = new Element('label').update(myStrings.loginLabelFor_createAccountUsername);
		label.insert({bottom: new Element('input',{type:'text',name:'newUsername'})});
		group.appendChild(label);
		
		label = new Element('label').update(myStrings.loginLabelFor_createAccountPassword);
		label.insert({bottom: new Element('input',{type:'password',name:'newPassword'})});
		group.appendChild(label);
		
		label = new Element('label').update(myStrings.loginLabelFor_createAccountPasswordRepeat);
		label.insert({bottom: new Element('input',{type:'password',name:'passwordRepeat'})});
		group.appendChild(label);
		
		
		if(myUseImageButtons)
		{
			btn = new Element('a',{href:'#'}).addClassName('button').update(myStrings.login_createAccountButtonText);
			btn.stopObserving('click',myECommerce_DF_button_click_handler).observe('click',function(e)
			{
				this.value='create';
				myECommerce_DF_button_click_handler(e);
			}.bindAsEventListener(accountType));
			group.appendChild(btn.wrap(new Element('span').addClassName('ECommerce_DF').addClassName('button')))
		}
		else
		{
			group.appendChild(new Element('input',{type:'submit',value:myStrings.login_createAccountButtonText}));
		}
		
		form.appendChild(group);
		
		
		// Don't create account radio button:
		
		group = new Element('div').addClassName('dontcreate');
		
		group.appendChild(new Element('h3').update(myStrings.login_textBetweenMethods));
		
		group.appendChild(new Element('h2').update(myStrings.loginLabelFor_dontCreateAccount));
		
		if(myUseImageButtons)
		{
			btn = new Element('a',{href:'#'}).addClassName('button').update(myStrings.login_dontCreateAccountButtonText);
			btn.stopObserving('click',myECommerce_DF_button_click_handler).observe('click',function(e)
			{
				this.value='dontcreate';
				myECommerce_DF_button_click_handler(e);
			}.bindAsEventListener(accountType));
			group.appendChild(btn.wrap(new Element('span').addClassName('ECommerce_DF').addClassName('button')))
		}
		else
		{
			group.appendChild(new Element('input',{type:'submit',value:myStrings.login_dontCreateAccountButtonText}));
		}
		
		form.appendChild(group);
		
		
		// Hide the "Don't create account" group (by default)
		
		form.select('.dontcreate').invoke('hide');
		
		
		
		form.select('input').invoke('observe','click',myLoginInputClickHandler).invoke('observe','keydown',myLoginInputKeydownHandler);
		
		
		// Hidden field for where to redirect to after logging in:
		
		form.appendChild(new Element('input',{type:'hidden',name:'redirect',value:window.location.pathname+window.location.search}));
		
		myLoginDiv.appendChild(form);
		
		// Cart is hidden:
		myLoginDivStatus = 'H';
		myLoginContainerDiv.hide();
		
		// Add to page:
		myViewCartMaskDiv.appendChild(myLoginContainerDiv);
	};
	
	var myCheckoutLinkHandler = function(e)
	{
		try
		{
			if(myCartItems.length)
			{
				if(myUserInformation)
				{
					window.location = myUrls.checkout;
				}
				else
				{
					myLoginDiv.down('form').select('.dontcreate').invoke('show');
					myLoginDiv.down('form').redirect.value = myUrls.checkout;
					this.showLogin();
				}
			}
			else
			{
				alert(myStrings.cartIconEmptyTitle);
			}
		}
		catch(e)
		{
			myReportError(e);
			alert('Sorry, something went wrong. We have emailed an error report to our staff. Please contact us for support.');
		}
		
		Event.stop(e);
	}.bind(this);
	
	/**
	 * Build the view cart div
	 *
	 * @return void
	 */
	var myBuildViewCartDiv = function()
	{
		var closeDiv,closeLink;
		
		myViewCartMaskDiv = new Element('div',{id: 'eCommerce_DF_mask_div'});
		
		if(BrowserDetect.browser == 'Explorer' && BrowserDetect.version == 6)
		{
			myViewCartMaskDiv.setStyle({filter:'progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=scale, src=\''+myUrlPrefix+this.cartMaskImage+'\')'});
		}
		else
		{
			myViewCartMaskDiv.setStyle({backgroundImage: 'url(' + myUrlPrefix + this.cartMaskImage + ')'});
		}
		
		myViewCartContainerDiv = new Element('div',{id:'eCommerce_DF_cart_div'});
		myViewCartInnerDiv = new Element('div').addClassName('container');
		
		myViewCartContainerDiv.appendChild(myViewCartInnerDiv);
		
		closeDiv = new Element('div').addClassName('close');
		closeLink = new Element('a',{href:'#'}).update(myStrings.cartCloseLinkText);
		closeLink.observe('click',myCloseCartLinkHandler);
		closeDiv.appendChild(closeLink);
		
		myViewCartContainerDiv.appendChild(closeDiv);
		myViewCartInnerDiv.appendChild(new Element('h1').update(myStrings.cartHeading));
		
		myCartDiv = new Element('div').addClassName('cart');
		myCartDiv.appendChild(new Element('p').update(myStrings.cartIsLoadingText));
		
		myCartTotalDiv = new Element('div').addClassName('total');
		
		myViewCartInnerDiv.appendChild(myCartDiv);
		myViewCartInnerDiv.appendChild(myCartTotalDiv);
		
		var linksDiv = new Element('div').addClassName('links');
		
		linksDiv.appendChild(new Element('a',{href:'#'}).addClassName('continue').update(myStrings.cartContinueLinkText).observe('click',myCloseCartLinkHandler));
		linksDiv.appendChild(new Element('a',{href:'#'}).addClassName('checkout').update(myStrings.cartCheckoutLinkText).observe('click',myCheckoutLinkHandler));
		
		myViewCartInnerDiv.appendChild(linksDiv);
		
		
		// Form for editing a product:
		myEditItemForm = new Element('form',{method:'post',action:myUrls.editItem}).hide();
		myViewCartInnerDiv.appendChild(myEditItemForm);
		
		// Cart is hidden:
		myCartDivStatus = 'H';
		myViewCartContainerDiv.hide();
		myViewCartMaskDiv.hide();
		
		// Add to page:
		myViewCartMaskDiv.appendChild(myViewCartContainerDiv);
		if(false && parent) {
			parent.document.body.appendChild(myViewCartMaskDiv);
		}
		else {
			document.body.appendChild(myViewCartMaskDiv);
		}
		
		new Draggable(myViewCartContainerDiv,{handle:myViewCartContainerDiv.down('div.close')});
	}.bind(this);
	
	var myDrawCartItem = function(cartItem)
	{
		myCartTotal += cartItem.price;
		cartItem.renderTo(myCartDiv.down('div.body'));
	}.bind(this);
	
	var myStylizeOddCartRows = function()
	{
		$$('div#eCommerce_DF_cart_div tr.odd').invoke('removeClassName','odd');
		$$('div#eCommerce_DF_cart_div tr.ECommerce_DF_CartItem:nth-child(odd)').invoke('addClassName','odd');
	};
	
	var myUpdateCartIcon = function()
	{
		var img = $('eCommerce_DF_viewCart_div').down('img');
		
		if(myCartItems.length)
		{
			img.src=img.src.replace(/_icon/,'_full');
			
			if(BrowserDetect.browser == 'Explorer' && BrowserDetect.version == 6) {
				img.runtimeStyle.filter = img.runtimeStyle.filter.replace(/_icon/,'_full');
			}
			
			img.title = myStrings[myCartItems.length>1 ? 'cartIconItemsTitle' : 'cartIconItemTitle'].interpolate({count:myCartItems.length});
		}
		else
		{
			img.src=img.src.replace(/_full/,'_icon');
			
			if(BrowserDetect.browser == 'Explorer' && BrowserDetect.version == 6) {
				img.runtimeStyle.filter = img.runtimeStyle.filter.replace(/_full/,'_icon');
			}
			
			img.title = myStrings.cartIconEmptyTitle;
		}
		
	};
	
	var myRefreshCartDiv = function()
	{
		DFTools.console.debug("ECommerce_DF: myRefreshCartDiv called...");
		
		myUpdateCartIcon();
		
		myCartDiv.childElements().invoke('remove');
		
		myCartTotal = 0;
		
		//var table = new Element('table',{cellspacing:this.cartCellSpacing});
		//var thead = new Element('thead');
		//var tbody = new Element('tbody');
		//var header = new Element('tr',{'class':'ECommerce_DF_CartHeader'});
		
		var header = new Element('div',{'class':'header'});
		var body = new Element('div',{'class':'body'});
		var footer = new Element('div',{'class':'footer'});
		
		header.appendChild(new Element('div').addClassName(this.cartColumnClasses[0]).update('Image'));
		header.appendChild(new Element('div').addClassName(this.cartColumnClasses[1]).update('Item'));
		header.appendChild(new Element('div').addClassName(this.cartColumnClasses[2]).update('Quantity'));
		header.appendChild(new Element('div').addClassName(this.cartColumnClasses[3]).update('Price'));
		header.appendChild(new Element('div').addClassName(this.cartColumnClasses[4]).update('Action'));
		
		myCartDiv.appendChild(header);
		myCartDiv.appendChild(body);
		
		myCartItems.each(myDrawCartItem);
		
		myStylizeOddCartRows();
		
		$$('div#eCommerce_DF_cart_div a.edit').invoke('update',myStrings.cartEditItemLinkText);
		$$('div#eCommerce_DF_cart_div a.remove').invoke('update',myStrings.cartRemoveItemLinkText);
		
		myCartTotalDiv.childElements().invoke('remove');
		
		footer.appendChild(new Element('div').addClassName(this.cartColumnClasses[0]).update('&nbsp;'));
		footer.appendChild(new Element('div').addClassName(this.cartColumnClasses[1]).update('&nbsp;'));
		footer.appendChild(new Element('div').addClassName(this.cartColumnClasses[2]).update('Total'));
		footer.appendChild(new Element('div').addClassName(this.cartColumnClasses[3]).update(myCartTotal.numberFormat('$#,###.##')));
		footer.appendChild(new Element('div').addClassName(this.cartColumnClasses[4]).update('&nbsp;'));
		
		myCartTotalDiv.appendChild(footer);
		
		this.updateCartHeight();
		
		myRunHandlers('cartRefreshed',{items:myCartItems, cartDiv:myCartDiv, totalDiv:myCartTotalDiv});

	}.bind(this);
	
	var myLoadCartError = function(requester,e)
	{
		if(!DFTools.pageUnload.isHappening())
		{
			e.error_handler = 'myLoadCartError';
			DFTools.console.log("Could not load cart. Requester object: ",requester);
			if(e){ DFTools.console.log("Exception: ",e); }
			
			try
			{
				DFTools.console.log("Dumping debug info...");
				DFTools.console.log("DFTools.pageUnload.isHappening: ",DFTools.pageUnload.isHappening());
				DFTools.console.log("Response State: ",requester.readyState);
				DFTools.console.log("Response Status: ",requester.status,requester.statusText);
				DFTools.console.log("Response Text: ",requester.responseText);
			}
			catch(ex_e)
			{
				DFTools.console.log("Dump aborted due to error:",ex_e);
			}
			
			myReportError(e);
			alert(myStrings.loadCartError);
		}
	};
	
	var myProcessCartData = function(requester)
	{
		try
		{
			DFTools.console.log("Load cart request returned; processing response");
			
			if(!requester.responseJSON)
			{
				DFTools.console.error("myProcessCartData: AJAX response was not in JSON format...");
				DFTools.console.log("Response was: "+requester.status+' '+requester.statusText);
				DFTools.console.log("Response text was: "+requester.responseText);
				throw new TypeError("AJAX request did not return JSON data");
			}
			
			if(!requester.responseJSON.success)
			{
				throw new Error(requester.responseJSON.err);
			}
			
			myCartItems = $A();
			
			myUserInformation = requester.responseJSON.loggedOnUser;
			
			if(!myUserInformation.first_name) {
				myUserInformation.first_name = myUserInformation.username;
			}
			
			if(myAddLoginLink)
			{
				var loginLinkDiv = new Element('div',{id:'eCommerce_DF_loginLink_div'});
				
				if(myUserInformation)
				{
					var welcomeBack = new Element('span').addClassName('welcome');
					
					welcomeBack.appendChild(new Element('span').update(myStrings.welcomeBackText.interpolate(myUserInformation)));
					
					var logoutLink = new Element('a',{href:myUrls.logout.interpolate(myUserInformation)});
					logoutLink.update(myStrings.logoutLinkText);
					
					welcomeBack.appendChild(logoutLink);
					
					loginLinkDiv.appendChild(welcomeBack);
				}
				else
				{
					var loginLink = new Element('a',{href:'#'});
					loginLink.update(myStrings.loginLinkText);
					loginLink.observe('click',myLoginLinkHandler);
					
					loginLinkDiv.appendChild(loginLink);
				}
				
				var eCommerce_DF_viewCart_div = $('eCommerce_DF_viewCart_div');
				
				if(eCommerce_DF_viewCart_div) {
					Element.insert('eCommerce_DF_viewCart_div',{after:loginLinkDiv});
				}
			}
			
			$A(requester.responseJSON.items).each(function(data)
			{
				var cart_item = new ECommerce_DF.CartItem(data);
				
				if(cart_item.ok)
				{
					myCartItems.push(cart_item);
				}
			});
			
			DFTools.console.log("Finished processing cart data, found "+myCartItems.length+" items.");
			
			myRefreshCartDiv();
		}
		catch(e)
		{
			myLoadCartError(requester,e);
		}
	};
	
	var myLoadCartData = function(reload)
	{
		if(reload || !myCartItems)
		{
			DFTools.console.log("ECommerce_DF: Submitting AJAX request to load cart data");
			
			new Ajax.Request(myUrls.getCart,{
				method:'post',
				parameters: {'t':new Date().getTime()},
				onSuccess: myProcessCartData,
				onException: myLoadCartError,
				onFailure: myLoadCartError
			});
		}
	};
	
	var myViewCartLinkHandler = function(e)
	{
		try
		{
			this.showCart();
		}
		catch(err)
		{
			DFTools.console.log(err);
			err.error_function = 'myViewCartLinkHandler';
			myReportError(err);
			alert('Sorry, the cart could not be displayed. Please email us for support');
		}
		
		Event.stop(e);
	}.bind(this);
	
	var myCloseCartLinkHandler = function(e)
	{
		try{ this.hideCart() } catch(err){ DFTools.console.log(error); }
		
		Event.stop(e);
	}.bindAsEventListener(this);
	
	var myLoginLinkHandler = function(e)
	{
		try
		{
			this.showLogin();
		}
		catch(err)
		{
			err.error_function = 'myLoginLinkHandler';
			myReportError(err);
			DFTools.console.log(err);
			alert('Sorry, the login box could not be displayed. Please email us for support');
		}
		
		Event.stop(e);
	}.bindAsEventListener(this);
	
	var myCloseLoginLinkHandler = function(e)
	{
		try{ this.hideLogin() } catch(err){ DFTools.console.log(error); }
		
		Event.stop(e);
	}.bindAsEventListener(this);
	
	var myAddToCartError = function(requester,e)
	{
		if(!DFTools.pageUnload.isHappening())
		{
			DFTools.console.log("Could not add to cart. Requester object: ",requester);
			
			if(!e)
				e = new Error("Unknown error!");
			
			DFTools.console.log("Exception: ",e);
			
			e.error_handler = 'myAddToCartError';
			myReportError(e);
			
			
			alert(myStrings.addToCartError);
		}
	};
	
	var myRemoveFromCartError = function(requester,e)
	{
		if(!DFTools.pageUnload.isHappening())
		{
			DFTools.console.log("Could not remove from cart. Requester object: ",requester);
			if(e) DFTools.console.log("Exception: ",e);
			
			e.error_handler = 'myRemoveFromCartError';
			myReportError(e);
			
			alert(myStrings.removeFromCartError);
		}
	};
	
	var myItemAddedToCartHandler = function(requester)
	{
		try
		{
			if(!requester.responseJSON)
			{
				DFTools.console.error("myItemAddedToCartHandler: AJAX response was not in JSON format...");
				DFTools.console.log("Response was: "+requester.status+' '+requester.statusText);
				DFTools.console.log("Response text was: "+requester.responseText);
				throw new TypeError("AJAX request did not return JSON data");
			}
			
			var result = requester.responseJSON;
			
			if(!result.success)
			{
				var errMsg = result.errorMsg;
				
				if(!errMsg)
					errMsg = myStrings.addToCartError;
				
				alert(errMsg);
			}
			else
			{
				var appear_delay = (this.cartAnimationDuration*1000) + 100;
				
				if(result.update)
				{
					$A(result.itemData).each(function(newItemData)
					{
						var cart_index = myCartItems.pluck('cartItemId').indexOf(newItemData.id);
						
						if(cart_index >= 0)
						{
							var cart_item = myCartItems[cart_index];
							cart_item.loadData(newItemData);
							
							//if(cart_item.ok)
							//{
							//	window.setTimeout(cart_item.highlight.bind(cart_item,2),appear_delay);
							//}
							
							var returnLink = $('breadcrumb_div').down('a.return');
							
							if(returnLink)
							{
								myViewCartInnerDiv.down('div.links a.continue').stopObserving('click',myCloseCartLinkHandler).href = returnLink.href;
							}
						}
					});				
				}
				else
				{
					$A(result.itemData).each(function(data)
					{
						var cart_item = new ECommerce_DF.CartItem(data);
						
						if(cart_item.ok)
						{
							cart_item.hide();
							myCartItems.push(cart_item);
							
							myRunHandlers('cartItemAdded',{item:cart_item});
							
							window.setTimeout(function()
							{
								cart_item.appear();
								myCartDiv.down('div.body').scrollTop = 99999;
							},appear_delay);
						}
					});
				}
				
				myRefreshCartDiv();
				
				this.showCart();
			}
		}
		catch(e)
		{
			myAddToCartError(requester,e);
		}
	}.bind(this);
	
	var myAddToCartFormHandler = function(e)
	{
		try
		{
			form = e.element();
			data = form.serialize(true);
			
			DFTools.console.log("ECommerce_DF: Submitting AJAX request to add item to cart");
			
			new Ajax.Request(myUrls.addToCart,{
				parameters: data,
				onSuccess: myItemAddedToCartHandler,
				onException: myAddToCartError,
				onFailure: myAddToCartError
			});
		}
		catch(err)
		{
			myAddToCartError(null,err);
		}
		
		Event.stop(e);
	}.bindAsEventListener(this);
	
	var myItemRemovedFromCartHandler = function(requester)
	{
		try
		{
			if(!requester.responseJSON)
			{
				DFTools.console.error("myItemRemovedFromCartHandler: AJAX response was not in JSON format...");
				DFTools.console.log("Response was: "+requester.status+' '+requester.statusText);
				DFTools.console.log("Response text was: "+requester.responseText);
				throw new TypeError("AJAX request did not return JSON data");
			}
			
			var result = requester.responseJSON;
			
			if(!result.success)
			{
				var errMsg = result.errorMsg;
				
				if(!errMsg)
					errMsg = myStrings.RemoveFromCartError;
				
				alert(errMsg);
			}
			else
			{
				var item = myCartItems[myItemIndex];
				myCartItems[myItemIndex] = null;
				myCartItems = myCartItems.compact();
				
				myRunHandlers('cartItemRemoved',item);
				
				item.fade();
				
				this.refreshCartTotal();
				myUpdateCartIcon();
				
				window.setTimeout(function()
				{
					item.remove();
					myStylizeOddCartRows();
				},1100);
			}
		}
		catch(e)
		{
			myRemoveFromCartError(requester,e);
		}
	}.bind(this);
	
	// -- Checkout Functions_______________________________________________________
	
	var myRefreshPaymentSummary = function()
	{
		myCheckoutForm.down('.paymentSummary .subtotal h3').update(myCartTotal.numberFormat('$#,###.##'));
		
		var shippingCost = 0;
		
		if(myShipment && typeof(myShipment.shippingCost) != 'undefined') {
			shippingCost = myShipment.shippingCost + myShipment.handlingCost + myShipment.insuranceCost;
		}
		
		myCheckoutForm.down('.paymentSummary .shipping h3').update(shippingCost.numberFormat('$#,###.##'));
		
		myCheckoutForm.down('.paymentSummary .discount h3').update(myCouponDiscount.numberFormat('$#,###.##'));
		
		myCheckoutForm.down('.paymentSummary .tax h3').update(myTax.numberFormat('$#,###.##'));
		
		var total = myCartTotal + shippingCost + myTax - myCouponDiscount;
		
		myCheckoutForm.down('.paymentSummary .total h3').update(total.numberFormat('$#,###.##'));
	};
	
	var myValidateZip = function(val)
	{
		var zip = val.split('-');
		
		if(zip.length > 2)	{ throw 'is not valid'; }
		
		if(!/[0-9]{5}(-?[0-9]{4})?/.test(val))
		{
			throw 'must be either 5 or 9 digits.';
		}
		
		if(zip.length == 1) { zip[1] = ''; }
		
		
		if(zip[0].length > 5)
		{
			zip[1] = zip[0].substr(5) + zip[1];
			zip[0] = zip[0].substr(0,5);
		}
		
		if(zip[0].length != 5) { throw 'is too short'; }
		
		if(!(zip[1].length == 0 || zip[1].length == 4)) { throw 'should either be 5 or 9 digits'; }
		
		
		if(zip[1].length) { zip[0] += '-' + zip[1]}
		
		return zip[0];
	}
	
	var myValidatePhone = function(val)
	{
		//var phone = val.split(/[(]?([0-9]{3})[ )]*([0-9]{3})-?([0-9]{4})[ ]*x?[ ]*([0-9]*)/)
		
		//var areacode = phone[1];
		//var exchange = phone[2];
		//var number = phone[3];
		//var extension = phone[4];
		
		var phone = val.replace(/[)( .xX-]*/g,'').replace(/[ABC]/ig,2).replace(/[DEF]/ig,3).replace(/[GHI]/gi,4).replace(/[JKL]/gi,5).replace(/[MNO]/gi,6).replace(/[PQRS]/gi,7).replace(/[TUV]/gi,8).replace(/[WYZ]/gi,9);
		
		if(phone.length && phone != parseInt(phone)) { throw 'contains invalid characters. Please enter digits only.'; }
		
		if(phone.length < 10) { throw 'is too short. Please enter all 10 digits.'; }
		
		phone = '('+phone.substr(0,3)+') '+phone.substr(3,3)+'-'+phone.substr(6,4)+(phone.length>10?'x'+phone.substr(10):'');
		
		return phone;
	};
	
	var myValidatePaymentNumberField = function(field)
	{
		var val = $F(field);
		
		parts = val.match(/([0-9]{2,4})[- ]?([0-9]{2,4})[- ]?([0-9]{2,4})[- ]?([0-9]{2,4})[- ]?/);
		
		if(!parts || parts[0] != val)
		{
			throw 'is not a valid credit card number. Please check to make sure you have entered all digits.';
		}
		
		var number = parts[1]+parts[2]+parts[3]+parts[4];
		
		if(number[0] == '4')
		{
			// Visa
			
			if($F('ECommerce_DF_paymentMethodName') != 'Visa')
			{
				
			}
		}
	};
	
	
	var myCheckoutFormErrors = $A();
	
	/**
	 * Validate a checkout form field, optionally indicating it failed to validate.
	 * 
	 * This private function will validate the first parameter passed to it which
	 * should be a field from the checkout form. If the field validates, true will
	 * be returned. Otherwise, false will be returned and, if a function was
	 * passed as the second parameter, the function will be called with a message
	 * explaining why validation failed. If the indicator parameter is omitted,
	 * then the default indicator is run.
	 *
	 *@return bool
	 */
	var myValidateCheckoutField = function(field,indicate)
	{
		var val=$F(field), div=field.up(), label=field.previous('label');
		
		var msg = '';
		
		if(typeof(indicate) != 'function')
		{
			indicate = function(msg)
			{
				msg = label.innerHTML.replace(/^\s+|:?\s+$/g, '').replace(/^<.*>/, '') + ' ' + msg;
				field.up().title = msg;
				field.up().addClassName('error');
				myCheckoutFormErrors.push(msg);
			};
		}
		
		try
		{
			if(!field.ancestors().invoke('visible').all())
			{
				// Field is not visible, do not validate
				return true;
			}
			
			if(div.hasClassName('required') && (val.blank() || val == 0)) { throw 'is required'; }
			
			if((div.hasClassName('billingEmail') || div.hasClassName('billingEmail')) && !DFTools.isValidEmail(val)) {
				throw 'is not a valid Email address';
			}
			
			if(div.hasClassName('billingNameFirst') || div.hasClassName('billingNameLast') || 
			   div.hasClassName('shippingNameFirst') || div.hasClassName('shippinggNameLast'))
			{
				if(/^[0-9 ]*$/.test(val)) throw 'is not valid';
			}
			
			if(div.hasClassName('shippingAddressPostalcode') || div.hasClassName('billingAddressPostalcode'))
			{
				var countryInput = $(field.id.replace(/Postalcode/,'Country'));
				
				if($F(countryInput) == 'USA')
				{
					field.value = myValidateZip(val);
				}
			}
			
			if(div.hasClassName('billingPhoneDefault') || div.hasClassName('shippingPhoneDefault') ||
				div.hasClassName('billingPhoneAlternate') || div.hasClassName('shippingPhoneAlternate'))
			{
				var countryInput = $(field.id.replace(/Phone.*/,'AddressCountry'));
				
				if($F(countryInput) == 'USA' && !val.blank())
				{
					field.value = myValidatePhone(val);
				}
			}
			
			if(div.hasClassName('couponCode'))
			{
				this.applyCouponCode(val);
			}
			
			if(div.hasClassName('paymentMethod_number'))
			{
				myValidatePaymentNumberField(field)
			}
		}
		catch(e)
		{
			if(typeof(e) == 'string') { indicate(e,true); }
			
			return false;
		}
		
		field.up().title = '';
		field.up().removeClassName('error');
		
		return true;
	}.bind(this);
	
	var myVoidFunc = function(){};
	
	/**
	 * Calls myValidateCheckoutField with no indicator
	 *
	 */
	var myValidateCheckoutFieldNoIndicate = function(f){ return myValidateCheckoutField(f,myVoidFunc); };
	
	var myParseCheckoutShippingMethodsAjax = function(t)
	{
		try
		{
			if(!t.responseJSON)
			{
				DFTools.console.error("myParseCheckoutShippingMethodsAjax: AJAX response was not in JSON format...");
				DFTools.console.log("Response was: "+t.status+' '+t.statusText);
				DFTools.console.log("Response text was: "+t.responseText);
				throw new TypeError("AJAX request did not return JSON data");
			}
			
			var result = t.responseJSON;
			
			var menu = myCheckoutForm.down('.shippingMethod').down('select');
			
			if(!result.success)
			{
				var errMsg = result.errorMsg;
				
				if(!errMsg)
					errMsg = myStrings.refreshShippingMethodsError;
				
				alert(errMsg);
			}
			else
			{
				menu.childElements().invoke('remove');
				
				menu.appendChild(new Element('option',{value: 0}).update(myStrings.checkout_selectShippingMethodText))
				
				myShippingMethods = result.methods;
				
				myShippingMethods.each(function(method,index)
				{
					var opt = new Element('option',{value: 'm_'+index}).update(method.name);
					
					if(method.estimate)
						opt.insert(method.estimate.numberFormat(' - $#,###.##'));
					
					menu.appendChild(opt);
				},this);
			}
		}
		catch(e)
		{
			myRefreshCheckoutShippingMethodsError(t,e);
		}
	};
	
	var myRefreshCheckoutShippingMethodsError = function(t,e)
	{
		if(!DFTools.pageUnload.isHappening())
		{
			DFTools.console.log("Could not refresh shipping methods: ",t);
			if(e) DFTools.console.log("Exception: ",e);
			
			e.error_handler = 'myRefreshCheckoutShippingMethodsError';
			myReportError(e);
			
			alert(myStrings.refreshShippingMethodsError);
		}
	};
	
	/**
	 * Refreshes the "Shipping method" div of the checkout form 
	 *
	 */
	var myRefreshCheckoutShippingMethods = function()
	{
		// First, see if we have enough shipping information filled in to
		// even retrieve shipping methods. We need at a minimum the shipping
		// address country and postal code
		
		var required = myCheckoutForm.down('.shippingInformation').select('.shippingAddressCountry select','.shippingAddressPostalcode input');
		
		var menu = myCheckoutForm.down('.shippingMethod').down('select');
		
		if(required.all(myValidateCheckoutFieldNoIndicate))
		{
			// We have enough to fetch the shipping methods
			
			menu.childElements().invoke('remove');
			menu.appendChild(new Element('option').update(myStrings.checkout_fetchingShippingMethodsText));
			
			new Ajax.Request(myUrls.getShippingMethods,{
				method: 'post',
				parameters: {
					'country':		$F('ECommerce_DF_shippingAddressCountry'),
					'postalcode':	$F('ECommerce_DF_shippingAddressPostalcode')
				},
				onSuccess: myParseCheckoutShippingMethodsAjax,
				onFailure: myRefreshCheckoutShippingMethodsError,
				onException: myRefreshCheckoutShippingMethodsError
			});
		}
		else
		{
			menu.childElements().invoke('remove');
			menu.appendChild(new Element('option').update(myStrings.checkout_completeShippingToSeeMethodsText));
		}
	};
	
	var mySaveCheckoutBillingInfoError = function(t,e)
	{
		DFTools.console.log("Could not save billing information: ",t);
		if(e) DFTools.console.log("Exception: ",e);
		
		alert(myStrings.saveCheckoutBillingInfoError);
	};
	
	var myParseCheckoutSaveBillingInfoAjax = function(t)
	{
		try
		{
			DFTools.console.debug("ECommerce_DF::myParseCheckoutSaveBillingInfoAjax() called");
			
			if(!t.responseJSON)
			{
				DFTools.console.error("myParseCheckoutSaveBillingInfoAjax: AJAX response was not in JSON format...");
				DFTools.console.log("Response was: "+t.status+' '+t.statusText);
				DFTools.console.log("Response text was: "+t.responseText);
				throw new TypeError("AJAX request did not return JSON data");
			}
			
			var result = t.responseJSON;
			
			if(!result.success)
			{
				var errMsg = result.errorMsg;
				
				if(!errMsg)
					errMsg = myStrings.saveCheckoutBillingInfoError;
				
				alert(errMsg);
			}
			else
			{
				myTax = result.tax;
				myRefreshPaymentSummary();
			}
		}
		catch(e)
		{
			mySaveCheckoutShippingInfoError(t,e);
		}
	};

	
	var mySaveCheckoutShippingInfoError = function(t,e)
	{
		DFTools.console.log("Could not save shipping method: ",t);
		if(e) DFTools.console.log("Exception: ",e);
		
		e.error_handler = 'mySaveCheckoutShippingInfoError';
		myReportError(e);
		
		//TODO: have a better message here
		alert(myStrings.refreshShippingMethodsError);
	
	};
	
	var myParseCheckoutSaveShippingInfoAjax = function(t)
	{
		try
		{
			if(!t.responseJSON)
			{
				DFTools.console.error("myParseCheckoutSaveShippingInfoAjax: AJAX response was not in JSON format...");
				DFTools.console.log("Response was: "+t.status+' '+t.statusText);
				DFTools.console.log("Response text was: "+t.responseText);
				throw new TypeError("AJAX request did not return JSON data");
			}
			
			var result = t.responseJSON;
			
			if(!result.success)
			{
				var errMsg = result.errorMsg;
				
				if(!errMsg)
					errMsg = myStrings.refreshShippingMethodsError;
				
				alert(errMsg);
			}
			else
			{
				myShipment = result.shipment;
				myTax = result.tax;
				myRefreshPaymentSummary();
				var couponCode = $F(myCheckoutForm.down('div.couponCode input'))
				this.applyCouponCode(couponCode);
			}
		}
		catch(e)
		{
			mySaveCheckoutShippingInfoError(t,e);
		}
	};
	
	myParseCheckoutSaveShippingInfoAjax = myParseCheckoutSaveShippingInfoAjax.bind(this);
	
	/**
	 * Event handler for when a field on the checkout form loses focus
	 *
	 */
	var myCheckoutFormFieldBlurHandler = function(e)
	{
		var field = Event.element(e);
		
		var valid = myValidateCheckoutField(field,true);
	}
	
	var myAddToAddressBook = function(type)
	{
		var select = $('ECommerce_DF_'+type+'Select');
		
		var div = select.up();
		
		try
		{
			// First confirm that all fields are filled in properly
			
			var fields = select.up(1).select('input','select');
			
			// First confirm that all fields are filled in properly
			if(!fields.all(myValidateCheckoutField))
				throw {errString: myStrings.addressBookFixErrorsFirst};
			
			var name = prompt(myStrings.newAddressBookEntryPrompt);
			
			if(!name || name.blank())
				throw {cancel: true};
			
			var params = Form.serializeElements(fields);
			
			params.action = 'set';
			params.label = name;
			
			new Ajax.Request(myUrls.checkoutAddressbook,{
				parameters: params
			});
		}
		catch(e)
		{
			if(!e.cancel)
			{
				if (e.errString){ alert(e.errString); }
			}
		}
	};
	
	var myAddressBookFieldChangeHandler = function(e)
	{
		var field = Event.element(e);
		
		var type = field.name.indexOf('billing') == -1 ? 'shipping' : 'billing';
		
		var value = $F(field);
		
		if(value == 1)
		{
			// Add to address book
			
			myAddToAddressBook(type);
			field.selectedIndex = 0;
		}
	};
	
	var myUpdateCheckoutInternationalFields = function(kind,country)
	{
		if(typeof(myCheckoutFormParts[kind+'AddressUSAStateSelect']) == 'undefined')
		{
			myCheckoutFormParts[kind+'AddressUSAStateSelect'] = myCheckoutForm.down('.'+kind+'AddressState').down('select');
			
			myCheckoutFormParts[kind+'AddressCAProvinceSelect'] = new Element('select',
			{
				name:myCheckoutFormParts[kind+'AddressUSAStateSelect'].name,
				id:myCheckoutFormParts[kind+'AddressUSAStateSelect'].id
			}).observe('change',myCheckoutFormFieldChangeHandler);
			
			var CAProvinces = $H({
				AB:'Alberta',
				BC:'British Columbia',
				MB:'Manitoba',
				NB:'New Brunswick',
				NL:'Newfoundland and Labrador',
				NT:'Northwest Territories',
				NS:'Nova Scotia',
				NU:'Nunavut',
				ON:'Ontario',
				PE:'Prince Edward Island',
				QC:'Quebec',
				SK:'Saskatchewan',
				YT:'Yukon'
			});
			
			CAProvinces.each(function(province)
			{
				this.appendChild(new Element('option',{value:province.key}).update(province.value));
			},myCheckoutFormParts[kind+'AddressCAProvinceSelect']);
			
			myCheckoutFormParts[kind+'AddressInternationalProvince'] = new Element('input',
			{
				name:myCheckoutFormParts[kind+'AddressUSAStateSelect'].name,
				id:myCheckoutFormParts[kind+'AddressUSAStateSelect'].id
			});
		}
		
		var stateSelect = myCheckoutFormParts[kind+'AddressUSAStateSelect'];
		var caProvinceSelect = myCheckoutFormParts[kind+'AddressCAProvinceSelect'];
		var provinceInput = myCheckoutFormParts[kind+'AddressInternationalProvince'];
		
		var postalLabel = myCheckoutForm.down('.'+kind+'AddressPostalcode').down('label');
		var stateLabel = myCheckoutForm.down('.'+kind+'AddressState').down('label');
		
		if(country == 'USA')
		{
			postalLabel.up('div').addClassName('required');
			stateLabel.up('div').addClassName('required');
			
			postalLabel.update('<span class="required">*</span>'+myStrings['checkout_labelFor_'+kind+'AddressPostalcode']);
			stateLabel.update('<span class="required">*</span>'+myStrings['checkout_labelFor_'+kind+'AddressState']);
			
			if(provinceInput.up('form'))
			{
				Element.insert(provinceInput,{after:stateSelect});
				Element.remove(provinceInput);
			}
			else if (caProvinceSelect.up('form'))
			{
				Element.insert(caProvinceSelect,{after:stateSelect});
				Element.remove(caProvinceSelect);
			}
		}
		else if(country == 'CAN')
		{
			postalLabel.up('div').addClassName('required');
			stateLabel.up('div').addClassName('required');
			
			postalLabel.update('<span class="required">*</span>'+myStrings['checkout_labelFor_'+kind+'AddressPostalcodeInternational']);
			stateLabel.update('<span class="required">*</span>'+myStrings['checkout_labelFor_'+kind+'AddressStateInternational']);
			
			
			if(stateSelect.up('form'))
			{
				Element.insert(stateSelect,{after:caProvinceSelect});
				Element.remove(stateSelect);
			}
			else if (provinceInput.up('form'))
			{
				Element.insert(provinceInput,{after:caProvinceSelect});
				Element.remove(provinceInput);
			}
		}
		else
		{
			postalLabel.up('div').removeClassName('required');
			stateLabel.up('div').removeClassName('required');
			
			postalLabel.update(myStrings['checkout_labelFor_'+kind+'AddressPostalcodeInternational']);
			stateLabel.update(myStrings['checkout_labelFor_'+kind+'AddressStateInternational']);
			
			if(stateSelect.up('form'))
			{
				Element.insert(stateSelect,{after:provinceInput});
				Element.remove(stateSelect);
			}
			else if (caProvinceSelect.up('form'))
			{
				Element.insert(caProvinceSelect,{after:provinceInput});
				Element.remove(caProvinceSelect);
			}
		}
	}
	
	/**
	 * Function to take action based on value of checkout fields, EG refresh shipping methods.
	 *
	 */
	var myActionForCheckoutFieldValue = function(field)
	{
		DFTools.console.debug("ECommerce_DF::myActionForCheckoutFieldValue called for field",field);
		
		var div = field.up();
		
		var group = div.up();
		
		var fieldValue = $F(field);
		
		if(div.hasClassName('shippingAddressCountry') || div.hasClassName('billingAddressCountry'))
		{
			// The billing or shipping country has been changed, so we need to make sure Postal Code and
			// "State" fields are internationally friendly...
			
			var kind = $w(group.className)[0] == 'billingInformation' ? 'billing' : 'shipping';
			
			DFTools.console.debug("Country was changed, updating fields for international locations");
			
			myUpdateCheckoutInternationalFields(kind,fieldValue);
		}
		
		if(group.hasClassName('shippingInformation'))
		{
			// The field is a valid shipping information field, so we should
			// try to refresh shipping methods.
			
			myRefreshCheckoutShippingMethods();
		}
		
		if(group.hasClassName('shippingMethod'))
		{
			// The field is a shipping method field, so we should save changes to
			// the shipping methods.
			
			this.saveShippingInfo();
		}
		
		if(group.hasClassName('billingInformation'))
		{
			// The field is a billing info field, so we should save changes to
			// the billing info.
			
			DFTools.console.debug("This is a billing field, saving billing info");
			
			this.saveBillingInfo();
			
			// Check to see if we need to update shipping info also
			if(myCheckoutForm.down('div.shippingSameAs input').checked)
			{
				this.shippingSameAsBilling(true);
				myRefreshCheckoutShippingMethods();
			}
		}
		
		if(field.id=='ECommerce_DF_paymentMethodName')
		{
			// Payment method has changed, need to change which payment options we display
			
			$('ECommerce_DF_paymentMethodDiv').childElements().invoke('hide');
			if(field.selectedIndex) {
				$('ECommerce_DF_paymentMethodDiv').select('div.'+field.down(field.selectedIndex).className).invoke('show'); }
		}
		
		return true;
	}.bind(this);
	
	
	/**
	 * Event handler for when a field on the checkout form is changed
	 *
	 */
	var myCheckoutFormFieldChangeHandler = function(e)
	{
		var field = Event.element(e);
		
		DFTools.console.debug("ECommerce_DF::myCheckoutFormFieldChangeHandler called for field",field);
		
		var valid = myValidateCheckoutField(field,true);
		
		var div = field.up();
		
		var group = div.up();
		
		if(div.hasClassName('addressBook'))
		{
			// The field is an address book field, let's pass the event off to our
			// Address book field handler
			
			myAddressBookFieldChangeHandler(e);
		}
		
		if(div.hasClassName('shippingSameAs'))
		{
			this.shippingSameAsBilling(field.checked);
		}
		
		myActionForCheckoutFieldValue(field);
		
	}.bindAsEventListener(this);
	
	
	var myCheckoutError = function(t,e)
	{
		DFTools.console.log("Checkout Error! Requester object: ",t);
		if(e) DFTools.console.log("Exception: ",e);
		
		e.error_handler = 'myCheckoutError';
		myReportError(e);
		
		if(e.userError) {
			alert(e.userError)
		} else {
			alert(myStrings.checkoutError);
		}
	};
	
	var myParseCheckoutCompleteResponse = function(t)
	{
		try
		{
			if(!t.responseJSON)
			{
				DFTools.console.error("myParseCheckoutCompleteResponse: AJAX response was not in JSON format...");
				DFTools.console.log("Response was: "+t.status+' '+t.statusText);
				DFTools.console.log("Response text was: "+t.responseText);
				throw new TypeError("AJAX request did not return JSON data");
			}
			
			var result = t.responseJSON;
			
			if(result.success)
			{
				window.location = result.redirect;
			}
			else
			{
				myCheckoutForm.select('div.complete input[type="submit"]').invoke('enable');
				
				if(!result.errMsg) { throw new TypeError("Checkout AJAX failed but didn't send error message!"); }
				
				alert(result.errMsg);
			}
		}
		catch(e)
		{
			myCheckoutError(t,e);
		}
		
	};
	
	var myParseApplyCouponCode = function(t)
	{
		var coupField = $('ECommerce_DF_couponCode');
		
		try
		{
			if(!t.responseJSON)
			{
				DFTools.console.error("myParseApplyCouponCode: AJAX response was not in JSON format...");
				DFTools.console.log("Response was: "+t.status+' '+t.statusText);
				DFTools.console.log("Response text was: "+t.responseText);
				throw new TypeError("AJAX request did not return JSON data");
			}
			
			var result = t.responseJSON;
			
			if(result.success)
			{
				myCouponDiscount = result.discount;
				myTax = result.tax;
				myRefreshPaymentSummary();
			}
			else
			{
				myCouponDiscount = 0;
				if(result.tax)
					myTax = result.tax;
				
				myRefreshPaymentSummary();
				
				if(result.message)
				{
					if(coupField && $F(coupField) == result.code)
					{
						coupField.up().addClassName('error').title = result.message;
					}
					else
					{
						alert(result.message);
						throw new Error(result.message);
					}
				}
				else
				{
					throw new Error('Server side error while applying coupon: '+result.err);
				}
			}

		}
		catch(e)
		{
			myApplyCouponCodeError(t,e);
		}
	};
	
	var myApplyCouponCodeError = function(t,e)
	{
	
	};
	
	var myCheckoutFormHandler = function(e)
	{
		try
		{
			form = Event.element(e);
			
			myCheckoutFormErrors = $A();
			
			form.select('input','select').each(function(f)
			{
				myValidateCheckoutField(f,true);
			});
			
			if(!myCheckoutFormErrors.length)
			{
				data = form.serialize(true);
				
				DFTools.console.log("ECommerce_DF: Submitting AJAX request for checkout complete");
				
				form.select('div.complete input[type="submit"]').invoke('disable');
				
				new Ajax.Request(myUrls.checkoutComplete,{
					parameters: data,
					method: 'post',
					onSuccess: myParseCheckoutCompleteResponse,
					onException: myCheckoutError,
					onFailure: myCheckoutError
				});
			}
			else
			{
				var msg = myStrings.checkoutFixErrorsFirst + myCheckoutFormErrors.join("\n");
				
				alert(msg);
			}
		}
		catch(err)
		{
			myCheckoutError(null,err);
		}
		
		Event.stop(e);
	}.bindAsEventListener(this);
	
	
	
	var myInitializeStageTwo = function(requester)
	{
		try
		{
			DFTools.console.debug("ECommerce_DF: in myInitializeStageTwo");
			
			if(!requester.responseJSON)
			{
				DFTools.console.error("myInitializeStageTwo: AJAX response was not in JSON format...");
				DFTools.console.log("Response was: "+requester.status+' '+requester.statusText);
				DFTools.console.log("Response text was: "+requester.responseText);
				throw new TypeError("AJAX request did not return JSON data");
			}
			
			myStrings = requester.responseJSON.strings;
			
			if(typeof(myStrings) != 'object')
				throw new TypeError("Could not load strings");
				
			myUrls = requester.responseJSON.urls
			
			if(typeof(myUrls) != 'object')
				throw new TypeError("Could not load URL Config");
			
			myAddLoginLink = requester.responseJSON.addLoginLink ? true : false;
			
			myUseImageButtons = requester.responseJSON.useImageButtons ? true : false;
			
			myBuildViewCartDiv();
			
			myBuildLoginDiv();
			
			myRunning = true;
			
			if(myCheckoutForm)
			{
				myRefreshCheckoutShippingMethods();
				
				
				// For people using autofill, their browser won't send a change event when the fields
				// are autofilled. For this reason we need to periodically observe any changes to the
				// checkout fields:
				
				//new PeriodicalExecuter(function(){
				//	myCheckoutForm.select('input','select').select(myValidateCheckoutFieldNoIndicate).all(myActionForCheckoutFieldValue);
				//},2);
			}
			
			myRunHandlers('loaded',{});
			
			DFTools.console.log("ECommerce_DF: Stage 2 Init complete");
			
			myLoadCartData();
		}
		catch(e)
		{
			if(!DFTools.pageUnload.isHappening())
			{
				e.error_function = 'myInitializeStageTwo';
				DFTools.console.log("Exception in myInitializeStageTwo",e,"Requester object:",requester);
				
				myReportError(e);
				
				alert("Error: The shopping cart could not complete Stage 2 Initialization. Please contact us for support");
			}
		}
		
	}.bind(this);
	
	
	var myWindowResizeHandler = function(e)
	{
		if(this.running())
		{
			this.updateCartHeight();
		}
		else
		{
			myWindowResizeHandler.delay(100,e)
		}
	}.bindAsEventListener(this);
	
	
	var myInitializeError = function(requester,ex)
	{
		DFTools.console.log("Ajax request to fetch config failed. Requester object:",requester);
		if(ex) DFTools.console.log("Exception:",ex);
		alert("The shopping cart could not be initialized because of an error. Please contact us for support");
	}.bind(this);
	
	var myFastInitHandler = function()
	{
		DFTools.console.debug("ECommerce_DF: now in myFastInitHandler");
		
		// Observe the view cart link (if found)
		try {
			$('eCommerce_DF_viewCart_div').select('a').invoke('observe','click',myViewCartLinkHandler).invoke('writeAttribute',{href:'#'});
			$('eCommerce_DF_viewCart_div').select('img').invoke('observe','click',myViewCartLinkHandler).title = 'Loading...';
		} catch(e) {}
		
		
		// Observe the "Add To Cart" form submit (if found)
		try {
			
			var addToCartForm;
			
			if(addToCartForm = $('ECommerce_DF_addToCart'))
			{
				addToCartForm.observe('submit',myAddToCartFormHandler);
				
				DFTools.defaultFieldText(addToCartForm.select('input[title]','textarea[title]'));
			}
		} catch(e) {}
		
		
		// Find checkout form (if it exists)
		
		myCheckoutForm = $('ECommerce_DF_Checkout_Form');
		
		// Determine the path to our settings URL:
		
		myUrlPrefix = '/module_support/ECommerce_DF/';
		
		$A(document.getElementsByTagName("script")).findAll( function(s) {
			return (s.src && s.src.match(/ECommerce_DF\.js(\?.*)?$/))
		}).each( function(s) {
			myUrlPrefix = s.src.replace(/ECommerce_DF\.js(\?.*)?$/,'');
		});
		
		myLoadSettingsUrl = myUrlPrefix + myLoadSettingsUrl;
		myReportErrorUrl = myUrlPrefix + myReportErrorUrl;
		
		// Observe the checkout form submit (if found). This must be done before the
		// cart settings are loaded because the checkout form may fire change events
		// as soon as we fire our loaded event.
		
		try {
			if(myCheckoutForm)
			{
				myCheckoutForm.observe('submit',myCheckoutFormHandler);
				
				var checkoutFields = myCheckoutForm.select('input','select');
				checkoutFields.invoke('observe','blur',myCheckoutFormFieldBlurHandler);
				checkoutFields.invoke('observe','change',myCheckoutFormFieldChangeHandler);
				checkoutFields.invoke('observe','digitalfruition:change',myCheckoutFormFieldChangeHandler);
				
				// Insert red required asterisks:
				myCheckoutForm.select('div.required label').invoke('insert',{top:'<span class="required">*</span>'});
			}
		} catch(e) {}
		
		myECommerce_DF_button_click_handler = function(e)
		{
			var form = e.element().up('form');
			
			if(form)
			{
				DFTools.submitForm(form);
			}
			
			e.stop();
		}.bindAsEventListener(this);
		
		// Also observe any eCommerce button links and make sure they submit their corresponding form
		$$('span.ECommerce_DF.button a').invoke('observe','click',myECommerce_DF_button_click_handler);
		
		
		// AJAX request to load the settings, this enabled the javascript to remain static
		// and be cacheable while only the settings are fetched on every request.
		// State 2 initialization will commence when the request completes, and that
		// will parse the settings and continue to set up the system
		
		new Ajax.Request(myLoadSettingsUrl,{
			method: 'get',
			
			onSuccess: myInitializeStageTwo,
			onException: myInitializeError,
			onFailure: myInitializeError
		});
		
		// If the window is resized, we need to update our cart div:
		
		Event.observe(window,'resize',myWindowResizeHandler);
		
		if(BrowserDetect.browser == "Explorer" && BrowserDetect.version == 6)
		{
			Element.insert(document.body,'<style type="text/css">\n'+
				'div#eCommerce_DF_mask_div {'+
					'position: absolute; left: 0px; top: 0px;'+
					'left: expression( ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) + \'px\' );'+
					'top: expression( ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) + \'px\' );' +
				'}\n' +
			'</style>');
		}
		
		DFTools.console.debug("ECommerce_DF: myFastInitHandler finished");
		
	}.bind(this);
	
	
	
	/**************************** Public Properties ***************************/
	
	this.cartAnimationDuration = 0.8;
	this.cartCellSpacing = 2;
	
	this.cartBottomMargin = 126;
	
	this.cartMaskImage = 'cart_mask.png';
	
	this.cartColumnClasses = $A(['productThumb','productName','qty','price','commands']);
	
	/*************************** Privileged Methods ***************************/
	
	this.running = function()
	{
		return myRunning;
	};
	
	this.showEnlargedProductImage = function(url)
	{
		var width=600,height=600,id='unknown';
		var match = url.match(/^.*\/img\/.*\/([0-9]+)x([0-9]+)\/.*\/([0-9]+)\.[a-z]{3,}$/);
		if(match && match.length>=4)
		{
			width = match[1];
			height = match[2];
			id = match[3];
		}
		
		var options = 'status=1,toolbar=0,location=0,menubar=1,directories=0,resizable=1';
		options += ',height='+height;
		options += ',width='+width;
		
		return window.open(url,'ecommerce_image_'+id,options);
	};
	
	this.showProdutImage = function(imgUrl,bigUrl,caption)
	{
		var imgContainer = $('main_product_image');
		
		if(!imgContainer)
			imgContainer = $('main_category_image');
		
		var link = imgContainer.down('a');
		var loading = imgContainer.down('img');
		var img = link.down('img');
		var captionDiv = imgContainer.down('div.storeImageCaption');
		
		link.href=bigUrl;
		if(img.src != imgUrl)
		{
			img.hide();
			loading.show();
			img.src=imgUrl;
			img.hide();
			window.setTimeout(function(){if(img.complete){loading.hide();img.show();}},10);
		}
		captionDiv.update(caption);
	};
	
	this.showCart = function(force)
	{
		if(!this.running() || !myViewCartContainerDiv)
			throw new Error("Cart Not Setup");
		
		try
		{
			if(myCartItems.length)
			{
				// Showing the cart:
				myCartDivStatus = 's';
				
				myViewCartMaskDiv.show();
				
				if(BrowserDetect.browser == 'Explorer' && BrowserDetect.version == 6)
				{
					// #308: In IE 6, <select>s will show through the cart. So hide them.
					$('container').select('select').invoke('setStyle',{'visibility':'hidden'});
				}
				
				myCartDiv.down('div.body').setStyle({overflow:'hidden'});
				
				new Effect.SlideDown(myViewCartContainerDiv,{
					duration:this.cartAnimationDuration,
					afterFinish: function()
					{
						// Cart is now shown:
						myCartDivStatus = 'S';
						
						myCartDiv.down('div.body').setStyle({overflow:'auto'});
						myViewCartContainerDiv.down('div.close').show();			
					}
				});
			
				$$('.hideOnShowCart').invoke('setStyle',{visibility:'hidden'});
			}
			else
			{
				alert(myStrings.cartIconEmptyTitle);
			}
		}
		catch (e)
		{			
			e.error_function = 'this.showCart';
			myReportError(e);
		}
	};
	
	this.hideCart = function()
	{
		if(!this.running() || !myViewCartContainerDiv)
			throw new Error("Cart Not Setup");
		
		if(myCartDivStatus == 'h' || myCartDivStatus == 'H')
		{
			// Cart is hiding or supposed to be hidden. Something's wrong;
			// Hide the cart immedietly.
			
			myCartDivStatus = 'H';
			myViewCartContainerDiv.hide();
			myViewCartMaskDiv.hide();
		}
		else
		{		
			// Hidng the cart:
			myCartDivStatus = 'h';
						
			if(BrowserDetect.browser == 'Explorer' && BrowserDetect.version == 6)
			{
				// #308: In IE 6, <select>s will show through the cart. We hid them in show(), now show them again
				$('container').select('select').invoke('setStyle',{'visibility':'visible'});
			}
			
			//myViewCartContainerDiv.down('div.close').hide();
			
			myCartDiv.down('div.body').setStyle({overflow:'hidden'});
			
			new Effect.SlideUp(myViewCartContainerDiv,{
				duration:this.cartAnimationDuration,
				afterFinish: function()
				{
					// Cart is now hidden:
					myCartDivStatus = 'H';
					myViewCartMaskDiv.hide();
				}
			});
			
			$$('.hideOnShowCart').invoke('setStyle',{visibility:'inherit'});
		}
	};
	
	this.showLogin = function(force)
	{
		if(!this.running() || !myLoginContainerDiv)
			throw new Error("ECommerce System Not Running");
		
		// Showing the cart:
		myLoginDivStatus = 's';
		
		if(myCartDivStatus.toLowerCase() != 'h')
		{
			// The cart is not hidden (or being hidden) so we need to hide it
			
			myCartDivStatus = 'h';
			
			myCartDiv.down('div.body').setStyle({overflow:'hidden'});
			
			new Effect.SlideUp(myViewCartContainerDiv,{
				duration:this.cartAnimationDuration,
				queue: 'end',
				afterFinish: function()
				{
					// Cart is now hidden:
					myCartDivStatus = 'H';
				}
			});
			
		}
		
		myRunHandlers('showLogin');
		
		myViewCartMaskDiv.show();
		
		if(BrowserDetect.browser == 'Explorer' && BrowserDetect.version == 6)
		{
			// #308: In IE 6, <select>s will show through the cart. So hide them.
			$('container').select('select').invoke('setStyle',{'visibility':'hidden'});
			
			// Also sometimes the cart is still visible even though it's set display:none;
			myViewCartContainerDiv.setStyle({'visibility':'hidden'});
		}
		
		//myLoginContainerDiv.down('div.close').hide();
		
		new Effect.SlideDown(myLoginContainerDiv,{
			duration:this.cartAnimationDuration,
			queue: 'end',
			afterFinish: function()
			{
				// Login is now shown:
				myLoginDivStatus = 'S';
				
				myLoginContainerDiv.down('div.close').setStyle({bottom:'auto'}).show();
				
				myRunHandlers('loginShown');
			}
		});
	};
	
	this.hideLogin = function()
	{
		if(!this.running() || !myLoginContainerDiv)
			throw new Error("ECommerce System Not Running");
		
		// Hiding the cart:
		myLoginDivStatus = 'h';
		
		if(BrowserDetect.browser == 'Explorer' && BrowserDetect.version == 6)
		{
			// #308: In IE 6, <select>s will show through the cart. We hid them in show(), now show them again
			$('container').select('select').invoke('setStyle',{'visibility':'visible'});
		}
		
		//myLoginContainerDiv.down('div.close').hide();
		
		new Effect.SlideUp(myLoginContainerDiv,{
			duration:this.cartAnimationDuration,
			afterFinish: function()
			{
				// Cart is now hidden:
				myLoginDivStatus = 'H';
				
				myViewCartMaskDiv.hide();
				
				// Also sometimes the cart is still visible even though it's set display:none;
				myViewCartContainerDiv.setStyle({'visibility':'visible'});
			}
		});
	};
	
	this.editItem = function(item)
	{
		var index = item;
		
		if(typeof(item)!="number")
		{
			index = -1;
			myCartItems.each(function(thisItem,i){
				if(thisItem === item)
				{
					index = i;
					throw $break;
				}
			});
		}
		
		if(index < 0 || index > myCartItems.length || !myCartItems[index])
		{
			throw new RangeError("Requested item was not found in the cart");
		}
		
		item = myCartItems[index];
		
		var postVars = $H();
		
		postVars.set('updateCartItemId',item.cartItemId);
		
		var urlbase = '';
		try{ urlbase = window.location.protocol+'//'+window.location.host } catch(e) {}
		try
		{
			var returnFound = false;
			var bcArray = $('breadcrumb_div').childElements().collect(function(el)
			{
				if(!returnFound)
				{
					var val = [el.href.replace(urlbase,''),el.innerHTML];
					
					returnFound = el.hasClassName('return');
					
					return val;
				}
			});
				
			postVars.set('breadcrumb',bcArray.toJSON());
		} catch(e) {}
		
		postVars.each(function(pair)
		{
			myEditItemForm.appendChild(new Element('input',{type:'hidden',name:pair.key,value:pair.value}));
		});
		
		myEditItemForm.submit();
	};
	
	this.removeItem = function(item)
	{
		var index = item;
		
		if(typeof(item)!="number")
		{
			index = -1;
			myCartItems.each(function(thisItem,i){
				if(thisItem === item)
				{
					index = i;
					throw $break;
				}
			});
		}
		
		if(index < 0 || index > myCartItems.length || !myCartItems[index])
		{
			throw new RangeError("Requested item was not found in the cart");
		}
		
		myItemIndex = index;
		item = myCartItems[index];
		
		new Ajax.Request(myUrls.removeFromCart,{
			method:'post',
			parameters:{id:item.cartItemId},
			onException: myRemoveFromCartError,
			onFailure: myRemoveFromCartError,
			onSuccess: myItemRemovedFromCartHandler
		});
	};
	
	this.refreshCartTotal = function()
	{
		myCartTotal = myCartItems.pluck('price').inject(0, function(acc, n) { return acc + n; });
		
		myCartTotalDiv.down('div.'+this.cartColumnClasses[3]).update(myCartTotal.numberFormat('$#,###.##'));
	};
	
	this.observe = function(event,handler)
	{
		if(myHandlers[event])
			myHandlers[event].push(handler);
		
		if(event == 'loaded' && this.running())
			handler.bind(this,{})();
	};
	
	this.stopObserving = function(event,handler)
	{
		if(myHandlers[event])
			myHandlers[event] = myHandlers[event].without(handler);
	};
	
	this.updateCartHeight = function()
	{
		if(myViewCartContainerDiv && myCartDiv)
		{
			var tableEl;
			
			if(myCartDivStatus == 'H' && myLoginDivStatus == 'H')
			{
				myViewCartMaskDiv.setStyle({visibility:'hidden'});
				
				tabelEl = myCartDiv.down('div.body');
				if(tableEl) {
					tableEl.setStyle({overflow:'hidden'});
				}
				
				myViewCartMaskDiv.show();
			}
			
			var ch = myViewCartContainerDiv.getHeight();
			tableEl = myCartDiv.down('div.body');
			if(tableEl)
				tableEl.setStyle({height:(ch-this.cartBottomMargin)+'px'});
			
			/*
			if(BrowserDetect.browser == 'Safari')
			{
				tableEl = myCartDiv.down('div.body');
				if(tableEl)
					tableEl.setStyle({height:'auto'});
				myCartDiv.setStyle({height:(ch-this.cartBottomMargin)+'px', overflowY:'auto'});
			}
			else if(BrowserDetect.browser == 'Explorer' && BrowserDetect.version == 6)
			{
				tableEl = myCartDiv.down('div.body');
				if(tableEl)
					tableEl.setStyle({height:'auto'});
				myCartDiv.setStyle({height:(ch-this.cartBottomMargin)+'px', overflowY:'auto'});
			}
			*/
			
			if(myCartDivStatus == 'H' && myLoginDivStatus == 'H')
			{
				myViewCartMaskDiv.hide();
				myViewCartMaskDiv.setStyle({visibility:'visible'});
				tableEl = myCartDiv.down('div.body');
				if(tableEl)
					tableEl.setStyle({overflow:'auto'});
			}
		}
	};
	
	this.shippingSameAsBilling = function(setTo)
	{
		var billingFields = myCheckoutForm.down('.billingInformation').select('select','input');
		var shippingFields;
		
		if(setTo)
		{
			myUpdateCheckoutInternationalFields('shipping',$F('ECommerce_DF_billingAddressCountry'));
			
			shippingFields = myCheckoutForm.down('.shippingInformation').select('select','input');
			
			shippingFields.each(function(field)
			{
				var copy = $(field.id.replace(/shipping/,'billing'));
				
				field.up('div').removeClassName('error');
				
				if(copy)
				{
					if(field.selectedIndex) { field.selectedIndex = copy.selectedIndex; }
					else { field.value = copy.value; }
					
					field.disable();
				}
				
				myValidateCheckoutFieldNoIndicate(field);
			});
			
			myCheckoutForm.down('div.shippingSameAs input').enable().checked = true;
		}
		else if(setTo === false)
		{
			shippingFields = myCheckoutForm.down('.shippingInformation').select('select','input');
			
			shippingFields.each(function(field)
			{
				var copy = $(field.id.replace(/shipping/,'billing'));
				
				if(copy)
				{
					if(field.selectedIndex) { field.selectedIndex = copy.selectedIndex; }
					else { field.value = copy.value; }
					
					field.enable();
				}
			});
			
			myCheckoutForm.down('div.shippingSameAs input').enable().checked = false;
		}
	};
	
	this.suspendSaveCheckoutInfo = function(value)
	{
		if(typeof value != 'undefined')
			mySuspendSaveCheckoutInfo = value ? true : false;
		
		return mySuspendSaveCheckoutInfo;
	};
	
	this.saveBillingInfo = function()
	{
		DFTools.console.debug("ECommerce_DF.saveBillingInfo() called");
		
		if(mySuspendSaveCheckoutInfo)
			DFTools.console.debug("Not saving info because saving is suspended");
		else if(myCheckoutForm)
		{
			try
			{
				var inputs = myCheckoutForm.down('.billingInformation').select('select','input').select(myValidateCheckoutFieldNoIndicate);
				var params = Form.serializeElements(inputs, true);
				
				new Ajax.Request(myUrls.saveBillingInfo,{
					method: 'post',
					parameters: params,
					onSuccess: myParseCheckoutSaveBillingInfoAjax,
					onFailure: mySaveCheckoutBillingInfoError,
					onException: mySaveCheckoutBillingInfoError
				});
			
				
			}
			catch (e)
			{				
				e.error_function = 'this.saveBillingInfo';
				myReportError(e);
				
				// todo: write error handler
				alert('error');
				throw e;
			}
		}
	};
	
	this.saveShippingInfo = function()
	{
		DFTools.console.debug("ECommerce_DF.saveShippingInfo() called");
		
		if(mySuspendSaveCheckoutInfo)
			DFTools.console.debug("Not saving info because saving is suspended");
		else if(myCheckoutForm)
		{
			try
			{
				var inputs = myCheckoutForm.down('.shippingMethod').select('select','input');
				var shippingFields = myCheckoutForm.down('.shippingInformation').select('select','input').select(myValidateCheckoutFieldNoIndicate);
				var method = myShippingMethods[parseInt($F(inputs[0]).substr(2))];
				var params = $H(Form.serializeElements(inputs, true));
				
				if(typeof(method) == 'undefined')
					return false;
				
				var shippingSameAs = myCheckoutForm.down('div.shippingSameAs input').checked;
				
				// Form.serializeElements won't serialize disabled elements, so temporarilly disable them
				if(shippingSameAs) { this.shippingSameAsBilling(false); }
				
				params.update(Form.serializeElements(shippingFields,true));
				
				if(shippingSameAs) { this.shippingSameAsBilling(true); }
				
				params.set('methodModule',method.module);
				params.set('methodIdentifier',method.identifier);
				
				DFTools.console.log("ECommerce_DF: Submitting Ajax request to save shipping");
				
				new Ajax.Request(myUrls.saveShippingInfo,{
					method: 'post',
					parameters: params,
					onSuccess: myParseCheckoutSaveShippingInfoAjax,
					onFailure: mySaveCheckoutShippingInfoError,
					onException: mySaveCheckoutShippingInfoError
				});
			
				
			}
			catch (e)
			{
				e.error_function = 'this.saveShippingInfo';
				myReportError(e);
				
				// todo: write error handler
				alert('error');
				throw e;
			}
		}
	};
	
	this.applyCouponCode = function(code)
	{
		try
		{
			new Ajax.Request(myUrls.applyCoupon,{
					method: 'post',
					parameters: {code:code},
					onSuccess: myParseApplyCouponCode,
					onFailure: myApplyCouponCodeError,
					onException: myApplyCouponCodeError
				});
		}
		catch(e)
		{
			myApplyCouponCodeError(false,e);
		}
	};
	
	/************************** Initialization Code ***************************/
	
	// Most of our initialization is done after the body has loaded (FastInit)
	
	FastInit.addOnLoad(myFastInitHandler);
};

ECommerce_DF.CartItem = Class.create(
{
	initialize: function(itemData)
	{
		this.loadData(itemData)
	},
	
	loadData: function(itemData)
	{
		this.ok = this.errState = false;
		
		try
		{
			this._myRow = false;
			
			if(itemData.id) this.cartItemId = itemData.id; else throw new Error("No ID set");
			
			if(itemData.qty) this.qty = itemData.qty; else throw new Error("Invalid Quantity");
			if(itemData.productName) this.productName = itemData.productName; else throw new Error("No product name set");
			this.productOptionsText = typeof(itemData.productChoicesText) == 'string' ? itemData.productChoicesText : '';
			
			if(itemData.productThumb) this.productThumb = itemData.productThumb;
			if(itemData.productId) this.productId = itemData.productId; else throw new Error("No product ID set");
			if(typeof(itemData.price) == "number") this.price = itemData.price; else throw new Error("Invalid price");
			if(itemData.formattedPrice) this.formattedPrice = itemData.formattedPrice; else throw new Error("No Formatted Price");
			
			if(itemData.addedAt) this.addedAt = itemData.addedAt;
			
			this.ok = true;
			
			if(this._myRow)
			{
				this._myRebuildCells();
			}
		}
		catch(e)
		{
			this.errState = e.message;
			this.ok = false;
		}
	},
	
	_myEditHandler: function(event)
	{
		try { ECommerce_DF.editItem(this); }
		catch(e) { DFTools.console.log("Could not edit item: ",e,this); alert("Sorry, there was an error. Please refresh the page and try again"); }
		
		Event.stop(event);
	},
	
	_myRemoveHandler: function(event)
	{
		try { ECommerce_DF.removeItem(this); }
		catch(e) { DFTools.console.log("Could not remove item: ",e,this); alert("Sorry, there was an error. Please refresh the page and try again"); }
		
		Event.stop(event);
	},
	
	_myInternalRender: function(force)
	{
		if(force && this._myRow)
		{
			this.remove();
		}
		
		if(!this._myRow)
		{
			this._myRow = new Element('div').addClassName('row');
			this._myRebuildCells();
		}
	},
	
	_myRebuildCells: function()
	{
		this._myRow.childElements().invoke('remove');
		
		this._myRow.appendChild(new Element('div').addClassName('cell').addClassName(ECommerce_DF.cartColumnClasses[0]));
		
		var descTd = new Element('div').addClassName('cell').addClassName(ECommerce_DF.cartColumnClasses[1]);
		descTd.appendChild(new Element('h2').update(this.productName.escapeHTML()));
		descTd.appendChild(new Element('h3').update(this.productOptionsText));
		this._myRow.appendChild(descTd);
		
		this._myRow.appendChild(new Element('div').addClassName('cell').addClassName(ECommerce_DF.cartColumnClasses[2]).update(this.qty));
		this._myRow.appendChild(new Element('div').addClassName('cell').addClassName(ECommerce_DF.cartColumnClasses[3]).update(this.formattedPrice));
		
		if(this.productThumb)
			this._myRow.down('div.'+ECommerce_DF.cartColumnClasses[0]).appendChild(new Element('img',{src:this.productThumb}));
		
		this._myCommandDiv = new Element('div');
		this._myCommandDiv.appendChild(new Element('a',{href:'#','class':'edit'}).update('&nbsp;').observe('click',this._myEditHandler.bindAsEventListener(this)));
		this._myCommandDiv.appendChild(new Element('a',{href:'#','class':'remove'}).update('&nbsp;').observe('click',this._myRemoveHandler.bindAsEventListener(this)));
		
		var ctd = new Element('div').addClassName('cell').addClassName(ECommerce_DF.cartColumnClasses[4]);
		ctd.appendChild(this._myCommandDiv)
		
		this._myRow.appendChild(ctd);
	},
	
	renderTo: function(table)
	{
		this._myInternalRender(false);
		
		table.appendChild(this._myRow);
	},
	
	hide: function()
	{
		this._myInternalRender();
		this._myRow.hide();
	},
	
	show: function()
	{
		this._myInternalRender();
		this._myRow.show();
	},
	
	appear: function()
	{
		this._myInternalRender();
		if(BrowserDetect.browser == 'Explorer' && BrowserDetect.version == 6)
		{
			new Effect.Appear(this._myRow,{duration: 0.75, queue:'end'});
		}
		else
		{
			new Effect.Appear(this._myRow,{duration: 0.75, queue:'end'});
			//this.highlight();
		}
	},
	
	highlight: function(number)
	{
		if(typeof(number) != "number")	number = 2;
		
		var myRow = this._myRow;
		
		// Don't highlight on IE, because it causes an exception and causes hiding the
		// cart to fail. TODO: Fix this.
		if(!BrowserDetect.browser != "Explorer")
		{
			number.times(function(){new Effect.Highlight(myRow,{queue:'end'});});
		}
	},
	
	fade: function()
	{
		this._myInternalRender();
		new Effect.Fade(this._myRow,{duration: 1});
	},
	
	remove: function()
	{
		try{ this._myRow.remove(); } catch(e) {}
		this._myRow = false;
	}
});
