var Checkout = new Class({
	Extends: UltraCart,
	Implements: [Options,Events],
	options: {
		screenBrandingTheme: 'default',
		returnOnErrorUrl: 'http://www.domain.com/error.html',
		secureHostName: null,
		items: []
	},
	initialize: function(els, mid, options) {
		this.parent(mid,options);
		this.setOptions(options);

		// store user interface elements for later use
		this.ui = els;
		
		// create an instance of modal for dialog boxes
		this.modal = new Modal();
		
		// print cart and customer data to the page
		this.printCart();
		this.printAddress();
		this.printCreditCardTypes();
		this.printShippingMethods();
		this.printAdvertisingSources();
		
		// if items from the catalog have been requested, get them
		this.items = (this.options.items.length > 0)?this.getItems(items):[];

		// get the countries where shipping is allowed
		this.allowedCountries = this.getAllowedCountries();

		// when a country hasn't been selected, default to USA
		if (this.session.billToCountry == '') this.session.billToCountry = 'United States';
		if (this.session.shipToCountry == '') this.session.shipToCountry = 'United States';
		
		// print allowed countries
		if ($defined(this.ui.billToCountry)) this.printCountries(this.ui.billToCountry,this.session.billToCountry);
		if ($defined(this.ui.shipToCountry)) this.printCountries(this.ui.shipToCountry,this.session.shipToCountry);

		// print allowed states
		if ($defined(this.ui.billToState)) this.ui.billToState = this.printStateProvinces(this.ui.billToState,this.session.billToCountry,this.session.billToState);
		if ($defined(this.ui.shipToState)) this.ui.shipToState = this.printStateProvinces(this.ui.shipToState,this.session.shipToCountry,this.session.shipToState);
		
		// when a customer changes the billTo country, update the billToState select box
		if (this.ui.billToCountry && this.ui.billToState) {
			this.ui.billToCountry.addEvent('change',function(e) {
				this.ui.billToState = this.printStateProvinces(this.ui.billToState,e.target.value,this.session.billToState);
			}.bind(this));
		}

		// when a customer changes the shipTo country, update the shipToState select box
		if (this.ui.shipToCountry && this.ui.shipToState) {
			this.ui.shipToCountry.addEvent('change',function(e) {
				this.ui.shipToState = this.printStateProvinces(this.ui.shipToState,e.target.value,this.session.shipToState);
			}.bind(this));
		}
	},
	
	// return the Item object
	getLocalItem: function(itemId) {
		var item = null;
		for (i=0;i<this.items.length;i++) {
			if (this.items[i].itemId == itemId) {
				item = this.items[i];
				break;
			}
		}
		
		// item not found, try to retrieve the item from UltraCart
		if (!item) {
			var result = this.getItems([itemId]);
			if (result.length > 0) {
				item = result[0];
				this.items.push(item);
			}
		}

		// if we found the item, return the Item object
		if (item) {
			item.buyButton = this.getBuyButton(item);
			item.cost = (isNaN(item.cost))?item.cost:item.cost.toFixed(2);
			return item;
		}
		
		// despite our best efforts, the item was not found
		// return an empty Item object
		return {
			'itemOid':0,
			'itemId':itemId,
			'description':'Not Available',
			'extendedDescription':'Not Available',
			'cost':'NA',
			'weight':{
				'uom':this.constant.measure.weight.imperial,
				'value':0
			},
			'length':{
				'uom':this.constant.measure.length.imperial,
				'value':0
			},
			'width':{
				'uom':this.constant.measure.length.imperial,
				'value':0
			},
			'height':{
				'uom':this.constant.measure.length.imperial,
				'value':0
			},
			'attributes':[],
			'multimedias':[],
			'options':[],
			'allowBackorder':false,
			'availableQuantity':0,
			'inStock':false,
			'inventoryTracked':false,
			'preorder':false
		}
	},

	// returns a buy button or "out of stock" message
	getBuyButton: function(item,qty) {
	
		// if it's not in stock and backorder isn't allowed, no buy button is needed
		if (!item.inStock && !item.allowBackorder) return new Element('span',{'text':'Event Full'});

		// create our "add to cart" button
		var addToCart = new Element('span',{
			'text':(item.inStock)?'Sign Up':'Add to Waiting List',
			'class':(item.inStock)?'addToCart':'backorder',
			'events':{
				'click':function(e) {
					e.stop();

					// update the button to indicate processing
					var btn = e.target;
					btn.set('text','Please wait...').addClass('disabled');
	
					// post to UltraCart
					var errors = this.addItems([{
						'itemId':item.itemId,
						'quantity':(isNaN(qty))?1:qty,
						'options':($defined(item.options))?item.options:[]
					}]);
					
					// if there are errors, tell customers the item isn't available for purchase
					if(errors != null && errors.length > 0) {
						btn.set('text','Not Available');
						return false;

					// otherwise, allow people to continue shopping or checkout
					} else {
						btn.set('text',(item.inStock)?'Sign Up':'Add to Waiting List');

						// display a confirmation dialog box
						var el = new Element('div',{
							'class':'editItemDialog',
							'styles': {
									'display': 'block',
									'width': '420px'
							},
							'html': '<h2>' + item.description + '</h2><p>Your selection has been added to the shopping cart. What would you like to do next?'
						});
						var ul = new Element('ul');
						var li = new Element('li');
						li.adopt(new Element('a',{
							'text':'Continue Shopping',
							'href':'#',
							'events':{
								'click':function(e) {
									e.stop();
									this.modal.hide();
									return;
								}.bind(this)
							}
						}));
						
						var lii = new Element('li');
						lii.adopt(new Element('a',{
							'text':'Checkout',
							'href':'/checkout.html'
						}));
						ul.adopt(li,lii);						
						el.adopt(ul);
						this.modal.show(el);
					}
					
				}.bind(this)
			}
		},this);

		// if there aren't any options, simply return the "add to cart" button
		// if (item.options.length <= 0) return addToCart; 

		// we're still here, so there must be options. create a button to open the options dialog
		var openDialog = new Element('span',{
			'text':(item.inStock)?'Sign Up':'Event Full',
			'class':(item.inStock)?'addToCart':'backorder',
			'events':{
				'click':function(e) {
					e.stop();

					// create a dialog box
					var el = new Element('div',{
						'class':'editItemDialog',
						'styles': {
						        'display': 'block',
						        'width': '420px'
						    }
						});
					var h2 = new Element('h2',{'text':item.description});
					
					var div = new Element('div',{'class':'labelInput'});
					var label = new Element('label',{'for':'quantity','text':'Number of Seats'});
					var select = new Element('select',{'id':'quantity','styles':{'width':50},'events':{
						'change':function(e) {
							qty = e.target.value;
						}
					}});
					for (i=1;i<=8;i++) {
						select.adopt(new Element('option',{'value':i,'text':i}));
					}
					div.adopt(label,select);
					
					// get form inputs for all the item options
					var options = this.getItemOptions(item);
					
					// add all our elements to the dialog box
					el.adopt(h2,div,options,addToCart);

					// display the item options in a modal dialog box
					this.modal.show(el);
					
					// after showing the dialog, reset the button text
					e.target.set('text','Sign Up');
				}.bind(this)
			}
		},this);
		return openDialog;
	},
		
	// short cut to refresh the entire cart - does NOT refresh billing, shipping and payment
	printCart: function() {
		this.printItems();
		this.printTotals();
		this.printCoupons();
	},

	// display all the items in the cart
	printItems: function() {
		try {
			if(!$defined(this.session)) this.getCartInstance();
			if(!$defined(this.ui.items)) return false;
			
			this.ui.items.set('html','');
			this.ui.editItem = null; // in case the user clicks edit too quickly
			
			// if there aren't any items in the cart, display 'Your cart is empty'
			if (this.session.items.length <= 0) {
				var td = new Element('td',{
					'class':'emptyCart',
					'colspan':5,
					'text':'empty'
				});
				var tr = new Element('tr');
				tr.adopt(td);
				this.ui.items.adopt(tr);
				return false;
			}
					
			// print the item list
			this.session.items.each(function(item,index) {
				var tr = new Element('tr',{'class':'item'});
				
				// "remove" and "edit" butons will appear in the "Actions" column
				var actions = new Element('td',{'class':'actions'});
				
				// when a user clicks remove, update ultracart and remove the table row from the UI
				var remove = new Element('span',{
					'class':'remove',
					'text':'remove',
					'events':{
						'click':function(e) {
							try {
								// remove the item from ultracart
								var errors = this.removeItem(item.itemId);
								if(errors != null && errors.length > 0) {
									this.logError(errors);
									return false;
								}

								// if there aren't any items left in the cart, display "empty"
								if (this.session.items.length <= 0) {
									var td = new Element('td',{
										'class':'emptyCart',
										'colspan':5,
										'text':'empty'
									});
									tr.empty();
									tr.adopt(td);

								// otherwise, remove the item element from the page
								} else {
									tr.set('tween', {
										'onComplete': function() {
											tr.destroy();
										}.bind(this)
									});
									tr.fade('out');
								}

								this.printTotals();
							}
							catch(error) {
								this.logError(error);
							}
						}.bind(this)
					}
				});
				actions.adopt(remove);

				if (item.options.length > 0) {
					var edit = new Element('span',{
						'class':'edit',
						'text':'edit',
						'events':{
							'click': function(e) {
								var el = new Element('div',{'class':'editItemDialog'});
								var desc = new Element('h2',{'text':item.description});

								// create a button to save the option values and close the dialog box
								var save = new Element('span',{
									'class':'save',
									'text':'Update Item',
									'events': {
										'click':function(e) {
											var btn = e.target;
											btn.set('text','Saving...');
											btn.addClass('disabled');
											this.updateItems(this.session.items);
											this.printCart();
											this.modal.hide();
										}.bind(this)
									}
								},this);
								el.adopt(desc,this.getItemOptions(item),save);

								this.modal.show(el);
							}.bind(this)
						}
					},this);
					actions.adopt(edit);
				}

				// description equates to the product name
				var description = new Element('td',{
					'class':'description',
					'html':'<p>'+item.description+'</p>'
				});

				// display item options below product name 
				var dl = new Element('dl');
				item.options.each(function(option,index) {
					if (option.type == this.constant.inputType.hidden) return false;
					var dt = new Element('dt',{'text':option.label});
					var dd = new Element('dd',{'text':option.selectedValue});
					dl.adopt(dt,dd);
				}.bind(this));
				description.adopt(dl);
				
				// update the cart when the user changes item quantities
				var qty = new Element('td',{
					'class':'quantity'
				});
				var input = new Element('input',{
					'type':'text',
					'size':3,
					'value':item.quantity,
					'events':{
						'blur':function(e) {
							// make sure the user entered a number
							var qty = parseInt(e.target.value);
							if (isNaN(qty)) {
								qty = (item.quantity > 0)?item.quantity:1;
							}
							e.target.value = qty;
							
							// update the session with the new quantity and set all total to zero
							// the customer will need to hit "update cart" to recalculate the cart total
							if (this.session.items[index].quantity != qty) {
								this.session.items[index].quantity = qty;
								cost.set('text',(qty * item.unitCost).toFixed(2));
								this.zeroTotals();
								this.printTotals(true);
							}
						}.bind(this)
					}
				});
				qty.adopt(input);
				
				// line item totals go in the "cost" column
				var price = new Element('td',{
					'class':'price',
					'text': (item.unitCost).toFixed(2)
				});
				
				// line item totals go in the "cost" column
				var cost = new Element('td',{
					'class':'cost',
					'text': (item.quantity * item.unitCost).toFixed(2)
				});
				
				tr.adopt(actions,description,qty,price,cost);
				this.ui.items.adopt(tr);
			}.bind(this));
		} catch (error) {
			this.logError(error);
			return false;
		}
	},
	
	// when a user clicks on the "edit" button, display a dialog box immediately below the cart item with editable item attributes 
	getItemOptions: function(item) {
		var el = new Element('div',{'class':'itemOptions'});
		
		item.options.each(function(option) {
			el.adopt(this.getItemOption(option));
		},this);
		
		return el;
	},
	
	// print the appropriate input type for a single item option
	getItemOption: function(option) {
		var el;
		option.selectedValue = ($defined(option.selectedValue))?option.selectedValue:'';
		switch (option.type) {
			// create short text input for the item option
			case this.constant.inputType.single:
				el = new Element('div',{'class':'labelInput'});
				var label = new Element('label',{'text':option.label});
				var input = new Element('input',{
					'type':'text',
					'value':option.selectedValue,
					'events': {
						'blur': function(e) {
							option.selectedValue = e.target.value;
						}.bind(this)
					}
				});
				el.adopt(label,input);
				break;
				
			// create textarea input for the item option
			case this.constant.inputType.multiline:
				el = new Element('div',{'class':'labelInput'});
				var label = new Element('label',{'text':option.label});
				var input = new Element('textarea',{
					'text':(option.values.length > 0)?option.values[0].value:'',
					'value':option.selectedValue,
					'events': {
						'blur': function(e) {
							option.selectedValue = e.target.value;
						}
					}
				});
				el.adopt(label,input);
				break;
			
			// create select box input for the item option
			case this.constant.inputType.dropDown:
				el = new Element('div',{'class':'labelInput'});
				var label = new Element('label',{'text':option.label});
				var input = new Element('select',{
					'events': {
						'blur': function(e) {
							option.selectedValue = e.target.value;
						}.bind(this)
					}
				});
				var html = '<option value="">Select One<\/option>';
				option.values.each(function(op,index){
					html += '<option value="'+op.value+'">'+op.value+'<\/option>';
				});
				
				input.set('html',html);
				input.setSelected(option.selectedValue);
				el.adopt(label,input);
				break;

			// create hidden input for the item option
			case this.constant.inputType.hidden:
				el = new Element('input',{type:'hidden',value:option.values[0].value});
				break;

			// create radio input for the item option
			case this.constant.inputType.radio:
				el = new Element('div',{'class':'radio'});
				
				// create a radio button for each allowed value
				option.values.each(function(radio,index){
					var div = new Element('div',{'class':'labelInput'});
					var label = new Element('label',{'text':radio.value});
					var input = new Element('input',{
						'type':'radio',
						'name':'radio'+option.optionOid,
						'value':radio.value,
						'checked':(radio.value == option.selectedValue)?true:false,
						'events': {
							'change': function(e) {
								option.selectedValue = e.target.value;
							}.bind(this)
						}
					});
					input.inject(label,'top');
					div.adopt(label);
					el.adopt(div);
				}.bind(this));
				break;

			// create paragraph for the fixed item option
			case this.constant.inputType.fixed:
				el = new Element('p',{'text':option.selectedValue});
				break;

			// throw an error when we don't recognize the item option
			default:
				break;
		}
		return el;
	},
	
	// useful method for indicating that item options and quanities have changed, but we haven't posted the latest changes to UltraCart yet
	zeroTotals: function() {
		this.session.subtotal = 0;
		this.session.subtotalDiscount = 0;
		this.session.subtotalWithDiscount = 0;
		this.session.taxRate = 0;
		this.session.tax = 0;
		this.session.taxableSubtotal = 0;
		this.session.taxableSubtotalDiscount = 0;
		this.session.taxableSubtotalWithDiscount = 0;
		this.session.shippingHandling = 0;
		this.session.shippingHandlingDiscount = 0;
		this.session.shippingHandlingWithDicount = 0;
		this.session.giftCharge = 0;
		this.session.giftCartCertificateAmount = 0;
		this.session.giftWrapCost = 0;
		this.session.surcharge = 0;
		this.session.total = 0;
	},
	
	// print subtotal, tax, shipping/handling, etc. from session data
	printTotals: function(updateRequired) {
		if(!$defined(this.session)) this.session = this.getCartInstance();

		if (this.ui.subtotal) this.ui.subtotal.set('text',this.session.subtotal.toFixed(2));
		if (this.ui.subtotalDiscount) {
			if (this.session.subtotalDiscount > 0) {
				this.ui.subtotalDiscount.set('text',(-this.session.subtotalDiscount).toFixed(2));
			} else {
				this.ui.subtotalDiscount.empty();
			}
		}
		if (this.ui.subtotalWithDiscount) this.ui.subtotalWithDiscount.set('text',this.session.subtotalWithDiscount.toFixed(2));
		if (this.ui.taxRate) this.ui.taxRate.set('text',this.session.taxRate);
		if (this.ui.tax) this.ui.tax.set('text',this.session.tax.toFixed(2));
		if (this.ui.taxableSubtotal) this.ui.taxableSubtotal.set('text',this.session.taxableSubtotal.toFixed(2));
		if (this.ui.taxableSubtotalDiscount) this.ui.taxableSubtotalDiscount.set('text',(-this.session.taxableSubtotalDiscount).toFixed(2));
		if (this.ui.taxableSubtotalWithDiscount) this.ui.taxableSubtotalWithDiscount.set('text',this.session.taxableSubtotalWithDiscount.toFixed(2));
		if (this.ui.shippingHandling) this.ui.shippingHandling.set('text',this.session.shippingHandling.toFixed(2));
		if (this.ui.shippingHandlingDiscount) this.ui.shippingHandlingDiscount.set('text',(-this.session.shippingHandlingDiscount).toFixed(2));
		if (this.ui.shippingHandlingWithDiscount) this.ui.shippingHandlingWithDiscount.set('text',this.session.shippingHandlingWithDiscount.toFixed(2));
		if (this.ui.giftCharge) this.ui.giftCharge.set('text',this.session.giftCharge.toFixed(2));
		if (this.ui.giftCertificateAmount) this.ui.giftCertificateAmount.set('text',(-this.session.giftCertificateAmount).toFixed(2));
		if (this.ui.giftWrapCost) this.ui.giftWrapCost.set('text',this.session.giftWrapCost.toFixed(2));
		if (this.ui.surcharge) this.ui.surcharge.set('text',this.session.surcharge.toFixed(2));
		if (this.ui.total) this.ui.total.set('text',(this.session.total + this.session.shippingHandling).toFixed(2));
	},
	
	// print coupons
	printCoupons: function() {
		if (!$defined(this.ui.couponCodes)) return false;

		try {
			if(!$defined(this.session)) this.session = this.getCartInstance();
			this.ui.couponCodes.set('html','');
			if (this.session.coupons.length > 0) {
				var span;
				this.session.coupons.each(function(coupon,index) {
					span = new Element('span',{
						'class':'coupon',
						'text':coupon.couponCode,
						'title':'Click to remove from your order',
						'events':{
							'click':function(e) {
								this.removeCoupon(coupon.couponCode);
								this.printTotals();
								this.printCoupons();
							}.bind(this)
						}
					});
					this.ui.couponCodes.adopt(span);
				}.bind(this));
			}
		} catch (error) {
			this.logError(error);
		}
	},
	
	// apply a coupon to the cart or display an error
	validateCoupon: function(coupon) {
		if (!$defined(this.ui.couponMessage)) return false;

		var errors = this.applyCoupon(coupon);
		this.ui.couponMessage.empty();
		if(errors && errors.length > 0) {
			this.ui.couponMessage.set('class','error');
			errors.each(function(error,index) {
				this.ui.couponMessage.appendText(error);
			}.bind(this));
		} else {
			this.ui.couponMessage.set({
				'class':'success',
				'text':'Coupon applied'
			});
			this.printTotals();
			this.printCoupons();
		}
	},

	
	// apply gift certificate and update cart
	// we do this a little differently than coupons because there can only be 1 GC applied to an order
	printGiftCertificate: function(gc) {
		if (!$defined(this.ui.giftCertificateMessage)) return false;
		try {
			if(!$defined(this.session)) this.getCartInstance();
			this.ui.giftCertificateMessage.empty();

			// start by validating the gift certificate
			var errors = this.validateGiftCertificate(gc);
			if(errors && errors.length > 0 && this.ui.giftCertificateError) {
				this.ui.giftCertificateMessage.set('class','error');
				errors.each(function(error,index) {
					this.ui.giftCertificateMessage.appendText('text',error);
				}.bind(this));
				return false;
			}
			this.applyGiftCertificate(gc);
			this.updateCart();
			this.printTotals();
			this.ui.giftCertificateMessage.set({
				'class':'success',
				'text':'Gift Certificate applied'
			});
		} catch (error) {
			this.logError(error);
		}
	},
	
	// update the "country" select box with a list of allowed countries
	// if an option exists for the country store in the session, preselect it
	printCountries: function(el,value) {
		// populate allowed countries
		var options
		if ($defined(this.allowedCountries) && this.allowedCountries.length > 0) {
			options = '<option value="">Select One<\/option>';
			this.allowedCountries.each(function(country,index) {
				options += '<option value="' + country + '">' + country + '<\/option>';
			});
		} else {
			options = '<option value="">None Available<\/option>';
		}
		el.set('html',options);
		if ($defined(value)) el.setSelected(value);
	},
	
	// update the "state" select box with a list of country-specific states and provinces
	printStateProvinces: function(el,country,stateProvince) {
		var input;
		var result = this.getStateProvinceCodes(country);
		
		// if we have a list of allowed states, show a drop-down menu. Otherwise, show plain text input.
		if($defined(result) && result.length > 0) {
			input = new Element('select',{'id':el.id,'name':el.name,'class':el.get('class'),'disabled':el.disabled});
			var options = '<option value="">Select One<\/option>';
			result.each(function(state,index) {
				options += '<option value="' + state + '">' + state + '<\/option>';
			});
			input.set('html',options);
		} else {
			input = new Element('input',{'type':'text','class':el.get('class'),'id':el.id,'name':el.name,'value':stateProvince,'disabled':el.disabled});
		}
		input.replaces(el);
		input.setSelected(stateProvince);
		return input;
	},
	
	// populate the "credit card type" select box with a list of allowed credit cards
	printCreditCardTypes: function() {
		if($defined(this.ui.creditCardType) && this.session.creditCardTypes.length > 0) {
			var options = '<option value="">Select One<\/option>';
			this.session.creditCardTypes.each(function(creditCard,index) {
				options += '<option value="' + creditCard + '">' + creditCard + '<\/option>';
			}.bind(this));
			this.ui.creditCardType.set('html',options);
		}
	},

	// print the session billing and shipping address in the checkout form
	printAddress: function() {
		try {
			if(!$defined(this.session)) this.session = this.getCartInstance();
			
			// billing address
			if (this.ui.billToFirstName)	this.ui.billToFirstName.value	= this.session.billToFirstName;
			if (this.ui.billToLastName)		this.ui.billToLastName.value	= this.session.billToLastName;
			if (this.ui.billToTitle)		this.ui.billToTitle.value		= this.session.billToTitle;
			if (this.ui.billToCompany)		this.ui.billToCompany.value		= this.session.billToCompany;
			if (this.ui.billToAddress1)		this.ui.billToAddress1.value	= this.session.billToAddress1;
			if (this.ui.billToAddress2)		this.ui.billToAddress2.value	= this.session.billToAddress2;
			if (this.ui.billToCity)			this.ui.billToCity.value		= this.session.billToCity;
			if (this.ui.billToState)		this.ui.billToState.value		= this.session.billToState;
			if (this.ui.billToPostalCode)	this.ui.billToPostalCode.value	= this.session.billToPostalCode;
			if (this.ui.billToCountry)		this.ui.billToCountry.value		= this.session.billToCountry;
			if (this.ui.billToPhone)		this.ui.billToPhone.value		= this.session.billToPhone;

			// shipping address
			if (this.ui.shipToBillTo && this.ui.shipToBillTo.checked) {
				if (this.ui.shipToFirstName)	this.ui.shipToFirstName.value		= this.session.billToFirstName;
				if (this.ui.shipToLastName)		this.ui.shipToLastName.value		= this.session.billToLastName;
				if (this.ui.shipToTitle)		this.ui.shipToTitle.value			= this.session.billToTitle;
				if (this.ui.shipToCompany)		this.ui.shipToCompany.value			= this.session.billToCompany;
				if (this.ui.shipToAddress1)		this.ui.shipToAddress1.value		= this.session.billToAddress1;
				if (this.ui.shipToAddress2)		this.ui.shipToAddress2.value		= this.session.billToAddress2;
				if (this.ui.shipToCity)			this.ui.shipToCity.value			= this.session.billToCity;
				if (this.ui.shipToState)		this.ui.shipToState 				= this.printStateProvinces(this.ui.shipToState,this.session.billToCountry,this.session.billToState);
				if (this.ui.shipToPostalCode)	this.ui.shipToPostalCode.value		= this.session.billToPostalCode;
				if (this.ui.shipToCountry)		this.ui.shipToCountry.setSelected(this.session.billToCountry);
				if (this.ui.shipToDayPhone)		this.ui.shipToDayPhone.value		= this.session.billToPhone;
				if (this.ui.shipToEveningPhone)	this.ui.shipToEveningPhone.value	= this.session.billToPhone;

			} else {
				if (this.ui.shipToFirstName)	this.ui.shipToFirstName.value		= this.session.shipToFirstName;
				if (this.ui.shipToLastName)		this.ui.shipToLastName.value		= this.session.shipToLastName;
				if (this.ui.shipToTitle)		this.ui.shipToTitle.value			= this.session.shipToTitle;
				if (this.ui.shipToCompany)		this.ui.shipToCompany.value			= this.session.shipToCompany;
				if (this.ui.shipToAddress1)		this.ui.shipToAddress1.value		= this.session.shipToAddress1;
				if (this.ui.shipToAddress2)		this.ui.shipToAddress2.value		= this.session.shipToAddress2;
				if (this.ui.shipToCity)			this.ui.shipToCity.value			= this.session.shipToCity;
				if (this.ui.shipToState)		this.ui.shipToState 				= this.printStateProvinces(this.ui.shipToState,this.session.shipToCountry,this.session.shipToState);
				if (this.ui.shipToPostalCode)	this.ui.shipToPostalCode.value		= this.session.shipToPostalCode;
				if (this.ui.shipToCountry)		this.ui.shipToCountry.setSelected(this.session.shipToCountry);
				if (this.ui.shipToDayPhone)		this.ui.shipToDayPhone.value		= this.session.shipToDayPhone;
				if (this.ui.shipToEveningPhone)	this.ui.shipToEveningPhone.value	= this.session.shipToEveningPhone;
			}
			
			// email
			if (this.ui.email)					this.ui.email.value					= this.session.email;
			if (this.ui.mailingListOptIn)		this.ui.mailingListOptIn.checked	= this.session.mailingListOptIn;
			
			// special instructions
			if (this.ui.specialInstructions)	this.ui.specialInstructions.value	= this.session.specialInstructions;
			
			// gift wrapping
			if (this.ui.giftMessage)			this.ui.giftMessage.value			= this.session.giftMessage;
			
			// advertising source
			if (this.ui.advertisingSource)		this.ui.advertisingSource.setSelected(this.session.advertisingSource);
			
			// shipping method
			if (this.ui.shippingMethod)			this.ui.shippingMethod.setSelected(this.session.shippingMethod);
			
		} catch(error) {
			this.logError(error);
		}
	},
	
	// update the session with user input for shipping, billing and payment 
	updateSession: function() {
		if(!$defined(this.session)) this.session = this.getCartInstance();
		try {

			// shipping address
			if (this.ui.billToFirstName)		this.session.billToFirstName = this.ui.billToFirstName.value;
			if (this.ui.billToLastName)			this.session.billToLastName = this.ui.billToLastName.value;
			if (this.ui.billToTitle)			this.session.billToTitle = this.ui.billToTitle.value;
			if (this.ui.billToCompany)			this.session.billToCompany = this.ui.billToCompany.value;
			if (this.ui.billToAddress1)			this.session.billToAddress1 = this.ui.billToAddress1.value;
			if (this.ui.billToAddress2)			this.session.billToAddress2 = this.ui.billToAddress2.value;
			if (this.ui.billToCity)				this.session.billToCity = this.ui.billToCity.value;
			if (this.ui.billToState)			this.session.billToState = this.ui.billToState.value;
			if (this.ui.billToPostalCode)		this.session.billToPostalCode = this.ui.billToPostalCode.value;
			if (this.ui.billToCountry)			this.session.billToCountry = this.ui.billToCountry.value;
			if (this.ui.billToPhone)			this.session.billToPhone = this.ui.billToPhone.value;
			if (this.ui.billToDayPhone)			this.session.billToDayPhone = this.ui.billToDayPhone.value;
			if (this.ui.billToEveningPhone)		this.session.billToEveningPhone = this.ui.billToEveningPhone.value;

			// billing address
			if ($defined(this.ui.shipToBillTo) && this.ui.shipToBillTo.checked) {

				this.session.shipToFirstName	= this.session.billToFirstName;
				this.session.shipToLastName		= this.session.billToLastName;
				this.session.shipToTitle		= this.session.billToTitle
				this.session.shipToCompany		= this.session.billToCompany;
				this.session.shipToAddress1		= this.session.billToAddress1;
				this.session.shipToAddress2		= this.session.billToAddress2;
				this.session.shipToCity			= this.session.billToCity;
				this.session.shipToState		= this.session.billToState;
				this.session.shipToPostalCode	= this.session.billToPostalCode;
				this.session.shipToCountry		= this.session.billToCountry;
				this.session.shipToPhone		= this.session.billToDayPhone;
			} else {

				if (this.ui.shipToFirstName)	this.session.shipToFirstName = this.ui.shipToFirstName.value;
				if (this.ui.shipToLastName)		this.session.shipToLastName = this.ui.shipToLastName.value;
				if (this.ui.shipToTitle)		this.session.shipToTitle = this.ui.shipToTitle.value;
				if (this.ui.shipToCompany)		this.session.shipToCompany = this.ui.shipToCompany.value;
				if (this.ui.shipToAddress1)		this.session.shipToAddress1 = this.ui.shipToAddress1.value;
				if (this.ui.shipToAddress2)		this.session.shipToAddress2 = this.ui.shipToAddress2.value;
				if (this.ui.shipToCity)			this.session.shipToCity = this.ui.shipToCity.value;
				if (this.ui.shipToState)		this.session.shipToState = this.ui.shipToState.value;
				if (this.ui.shipToPostalCode)	this.session.shipToPostalCode = this.ui.shipToPostalCode.value;
				if (this.ui.shipToCountry)		this.session.shipToCountry = this.ui.shipToCountry.value;
				if (this.ui.shipToPhone)			this.session.shipToPhone = this.ui.shipToPhone.value;
			}
			
			// email
			if (this.ui.email) this.session.email = this.ui.email.value;
			if (this.ui.mailingListOptIn) this.session.mailingListOptIn = this.ui.mailingListOptIn.checked;

			// special instructions
			if (this.ui.specialInstructions) this.session.specialInstructions = this.ui.specialInstructions.value;
			
			// gift wrapping
			if (this.ui.giftMessage) this.session.giftMessage = this.ui.giftMessage.value;
			
			// advertising source
			if (this.ui.advertisingSource) this.session.advertisingSource = this.ui.advertisingSource.value;
			
			// shipping method
			if (this.ui.shippingMethod) this.session.shippingMethod = this.ui.shippingMethod.value;
			
			// payment method
			if (this.ui.paymentMethod) this.session.paymentMethod = this.ui.paymentMethod.value;
			if (this.ui.creditCardType) this.session.creditCardType = this.ui.creditCardType.value;
			if (this.ui.creditCardNumber) this.session.creditCardNumber = this.ui.creditCardNumber.value;
			if (this.ui.creditCardExpirationMonth) this.session.creditCardExpirationMonth = parseInt(this.ui.creditCardExpirationMonth.value);
			if (this.ui.creditCardExpirationYear) this.session.creditCardExpirationYear = parseInt(this.ui.creditCardExpirationYear.value);
			if (this.ui.creditCardVerificationNumber) this.session.creditCardVerificationNumber = this.ui.creditCardVerificationNumber.value;
			
		} catch(error) {
			this.logError(error);
		}
	},
	
	// retrieves and prints advertising sources as select box options in this.ui.advertisingSource
	printAdvertisingSources: function() {
		if (!$defined(this.ui.advertisingSource)) return false;
		var results = this.getAdvertisingSources();
		if ($defined(results) && results.length > 0) {
			var options = '<option value="">Select One<\/option>';
			results.each(function(source,index) {
				options += '<option value="' + source + '">' + source + '<\/option>';
			}.bind(this));
			this.ui.advertisingSource.set('html',options);
			this.ui.advertisingSource.setSelected(this.session.advertisingSource);
		}
	},

	// print the shipping method and costs available for the current session shipping address
	printShippingMethods: function() {
		if (!$defined(this.ui.shippingMethod)) return false;

		try {
			if(!$defined(this.session)) this.getCartInstance();

			this.ui.shippingMethod.set('html','');
			var ok = false;
			var results = (this.shippingEstimates && this.shippingEstimates.length >= 1)?this.shippingEstimates:this.estimateShipping();
			if(results != null && results.length > 0) {
				if (results.length == 1) this.session.shippingMethod = results[0].name;
				var option = new Element('option',{'value':'','text':'Select One'});
				this.ui.shippingMethod.adopt(option);
				var cost = [];
				results.each(function(method,index) {
					option = new Element('option',{'value':method.name,'text':method.displayName + ', $' + method.cost});
					cost[index] = method.cost;
					if (this.session.shippingMethod == method.name) {
						ok = true;
						option.setProperty('selected','selected');
					}
					this.ui.shippingMethod.adopt(option);
				}.bind(this));
				this.ui.shippingMethod.store('cost',cost);
			} else {
				var options = '<option value="">None available for this address<\/option>';
			}
			if (!ok) this.session.shippingMethod = null;
			
		}
		catch(error) {
			this.logError(error);
		}
	},
	
	// after validating the cart, print errors as an unordered list
	printCartErrors: function(errors) {
		if (!$defined(this.ui.cartErrors)) return false;
		this.ui.cartErrors.empty();
		var ul = new Element('ul');
		errors.each(function(error, index) {
			ul.adopt(new Element('li',{'html':error}));
		});
		this.ui.cartErrors.adopt(ul);
		this.ui.cartErrors.tween('height',ul.getSize().y);
	},
	
	// submit payment to ultracart
	submitPayment: function() {
		try {
			var result;
			if($defined(this.secureHostName)) {
				result = this.checkoutHandoffOnCustomSSL(this.options.secureHostName, this.options.returnOnErrorUrl, 'error');
			} else {
				result = this.checkoutHandoff(this.options.returnOnErrorUrl, 'error');
			}
			
			if ($defined(result.errors) && result.errors.length == 0) {
				window.location = result.redirectToUrl;
			} else {
				this.printCartErrors(result.errors);
			}
		} catch(error) {
			this.logError(error);
		}
	}
});
