ONEHAT.widget.Tickets = function() {
	var obj = {
	 	init: function(){
			var oThis = this;
			
			this.form = Ext.get('ticketsForm');
			this.form.on('submit', function(e) {
				oThis.checkForm(e);
			});
			
			// section 1
			this.sect1 = {
				container: Ext.get('sectionOneContainer'),
				
				changeCruiseContainer: Ext.get('changeCruiseContainer'),
				cruiseTitle: Ext.get('cruiseTitle'),
				cruisePic: Ext.get('cruisePic'),
				cruiseDescription: Ext.get('cruiseDescription'),
				changeCruiseButton: Ext.get('changeCruiseButton'),
				
				selectCruiseContainer: Ext.get('selectCruiseContainer'),
				dateID: Ext.get(this.form.dom.dateID),
				selectCruiseButton: Ext.get('selectCruiseButton'),
				
				cruise: null,
				isValid: false // state
			};
			this.sect1.selectCruiseContainer.setVisibilityMode(Ext.Element.DISPLAY);
			this.sect1.changeCruiseContainer.setVisibilityMode(Ext.Element.DISPLAY);
			this.sect1.selectCruiseButton.on('click', function(e) {
				oThis._selectCruise();
			});
			this.sect1.changeCruiseButton.on('click', function(e) {
				oThis._changeCruise();
			});
			if (this.sect1.changeCruiseContainer.hasClass('hide')) {
				this.sect1.selectCruiseContainer.show();
				this.sect1.changeCruiseContainer.hide();
				this.sect1.selectCruiseContainer.removeClass('hide');
				this.sect1.changeCruiseContainer.removeClass('hide');
			} else {
				this.sect1.isValid = true;
				this.sect1.selectCruiseContainer.hide();
				this.sect1.changeCruiseContainer.show();
				this.sect1.selectCruiseContainer.removeClass('hide');
				this.sect1.changeCruiseContainer.removeClass('hide');
			}
			
			
			// section 2
			this.sect2 = {
				container: Ext.get('sectionTwoContainer'),
				custName: Ext.get(this.form.dom.custName),
				address1: Ext.get(this.form.dom.address1),
				address2: Ext.get(this.form.dom.address2),
				city: Ext.get(this.form.dom.city),
				stateAbbr: Ext.get(this.form.dom.stateAbbr),
				countryAbbr: Ext.get(this.form.dom.countryAbbr),
				zip: Ext.get(this.form.dom.zip),
				phone: Ext.get(this.form.dom.phone),
				email: Ext.get(this.form.dom.email),
				referral: Ext.get(this.form.dom.referral),
				continueButton: Ext.get(this.form.dom.continueButton1),
				isValid: false // state
			};
			this.sect2.container.setVisibilityMode(Ext.Element.DISPLAY);
			this.sect2.continueButton.on('click', function() {
				oThis.checkSect2();
			});
//			this.sect2.container.on('hide', function() {
//				oThis.sect3.container.hide();
//			});
			this.sect2.container.hide();
			this.sect2.container.removeClass('hide');
			
			
			// section 3
			this.sect3 = {
				container: Ext.get('sectionThreeContainer'),
				TH: {
					adult: Ext.get('adultTH'),
					adultSingle: Ext.get('adultSingleTH'),
					adultDouble: Ext.get('adultDoubleTH'),
					adultTriple: Ext.get('adultTripleTH'),
					adultQuad: Ext.get('adultQuadTH'),
					senior: Ext.get('seniorTH'),
					child: Ext.get('childTH'),
					infant: Ext.get('infantTH')
				},
				selections: {
					adult: Ext.get(this.form.dom.adult),
					adultSingle: Ext.get(this.form.dom.adultSingle),
					adultDouble: Ext.get(this.form.dom.adultDouble),
					adultTriple: Ext.get(this.form.dom.adultTriple),
					adultQuad: Ext.get(this.form.dom.adultQuad),
					senior: Ext.get(this.form.dom.senior),
					child: Ext.get(this.form.dom.child),
					infant: Ext.get(this.form.dom.infant)
				},
				rows: {
					adult: Ext.get('row1'),
					adultSingle: Ext.get('row2'),
					adultDouble: Ext.get('row3'),
					adultTriple: Ext.get('row4'),
					adultQuad: Ext.get('row5'),
					senior: Ext.get('row6'),
					child: Ext.get('row7'),
					infant: Ext.get('row8')
				},
				comments: Ext.get(this.form.dom.comments),
				chart: {
					adultQty: Ext.get('adultQty'),
					adultPrice: Ext.get('adultPrice'),
					adultTotal: Ext.get('adultTotal'),
					adultSingleQty: Ext.get('adultSingleQty'),
					adultSinglePrice: Ext.get('adultSinglePrice'),
					adultSingleTotal: Ext.get('adultSingleTotal'),
					adultDoubleQty: Ext.get('adultDoubleQty'),
					adultDoublePrice: Ext.get('adultDoublePrice'),
					adultDoubleTotal: Ext.get('adultDoubleTotal'),
					adultTripleQty: Ext.get('adultTripleQty'),
					adultTriplePrice: Ext.get('adultTriplePrice'),
					adultTripleTotal: Ext.get('adultTripleTotal'),
					adultQuadQty: Ext.get('adultQuadQty'),
					adultQuadPrice: Ext.get('adultQuadPrice'),
					adultQuadTotal: Ext.get('adultQuadTotal'),
					seniorQty: Ext.get('seniorQty'),
					seniorPrice: Ext.get('seniorPrice'),
					seniorTotal: Ext.get('seniorTotal'),
					childQty: Ext.get('childQty'),
					childPrice: Ext.get('childPrice'),
					childTotal: Ext.get('childTotal'),
					infantQty: Ext.get('infantQty'),
					infantPrice: Ext.get('infantPrice'),
					infantTotal: Ext.get('infantTotal'),
					grandTotal: Ext.get('total')
				},
				isValid: false, // state
				continueButton: Ext.get(this.form.dom.continueButton2),
				tickets: {} // This object keeps track of the number and type of all tickets currently selected.
			};
			this.sect3.container.setVisibilityMode(Ext.Element.DISPLAY);
			this.sect3.continueButton.on('click', function() {
				oThis.checkSect3();
			});
			this.sect3.selections.adult.on('change', function() {
				oThis.updateTickets();
			});
			this.sect3.selections.adultSingle.on('change', function() {
				oThis.updateTickets();
			});
			this.sect3.selections.adultDouble.on('change', function() {
				oThis.updateTickets();
			});
			this.sect3.selections.adultTriple.on('change', function() {
				oThis.updateTickets();
			});
			this.sect3.selections.adultQuad.on('change', function() {
				oThis.updateTickets();
			});
			this.sect3.selections.senior.on('change', function() {
				oThis.updateTickets();
			});
			this.sect3.selections.child.on('change', function() {
				oThis.updateTickets();
			});
			this.sect3.selections.infant.on('change', function() {
				oThis.updateTickets();
			});
			new Ext.ToolTip({ target: this.sect3.TH.adult, html: 'Regular adult ticket' });
			new Ext.ToolTip({ target: this.sect3.TH.adultSingle, html: 'Adult ticket, 1-person to a room' });
			new Ext.ToolTip({ target: this.sect3.TH.adultDouble, html: 'Adult ticket, 2-people to a room' });
			new Ext.ToolTip({ target: this.sect3.TH.adultTriple, html: 'Adult ticket, 3-people to a room' });
			new Ext.ToolTip({ target: this.sect3.TH.adultQuad, html: 'Adult ticket, 4-people to a room' });
			new Ext.ToolTip({ target: this.sect3.TH.senior, html: 'Senior citizen discount ticket' });
			new Ext.ToolTip({ target: this.sect3.TH.child, html: 'Child ticket (5&ndash;15)' });
			new Ext.ToolTip({ target: this.sect3.TH.infant, html: 'Infant ticket' });
//			this.sect3.container.on('hide', function() {
//				oThis.sect4.container.hide();
//			});
			this.sect3.container.hide();
			this.sect3.container.removeClass('hide');
			
			
			// section 4
			this.sect4 = {
				container: Ext.get('sectionFourContainer'),
				submitButton: Ext.get(this.form.dom.Submit),
				response: Ext.get('response')
			};
			this.sect4.container.setVisibilityMode(Ext.Element.DISPLAY);
			this.sect4.container.hide();
			this.sect4.container.removeClass('hide');
			this.sect4.response.setVisibilityMode(Ext.Element.DISPLAY);
			this.sect4.response.hide();

			
			// set initial state
			this._resetTickets();
			if (this.sect1.isValid) {
				setTimeout(function() {
					oThis._selectCruise(); // have to delay this because ONEHAT.widget.Cruises is not yet defined
				}, 250);
			}
		},
		
		_selectCruise: function() {
			var oThis = this;
			
			var prices;
			
			if (!ONEHAT.widget.Cruises) { // IE6 sometimes takes a while to find the Cruises object
				this.sect1.selectCruiseButton.dom.value = 'please wait...';
				this.sect1.changeCruiseButton.dom.value = 'please wait...'
				setTimeout(function() {
					oThis._selectCruise();
				}, 100);
				return;
			} else {
				this.sect1.selectCruiseButton.dom.value = 'Select Cruise';
				this.sect1.changeCruiseButton.dom.value = 'Select a Different Cruise';
			}

			this.cruise = ONEHAT.widget.Cruises[parseInt(this.sect1.dateID.dom.value, 10)];
			
			// set cruise HTML
			this.sect1.cruiseTitle.dom.innerHTML = this.cruise.selectedDate.startDateF + ' - ' + this.cruise.title;
			this.sect1.cruisePic.dom.src = 'images/cruises/small-icons/' + this.cruise.url + '.png',
			this.sect1.cruiseDescription.dom.innerHTML = this.cruise.description;
			this.sect1.isValid = true;
			
			this._changeSelector();
			this.sect2.container.show();
		//	this.sect2.container.dom.scrollIntoView();
			
			// update ticket availability (does this cruise allow for senior discounts?)
			prices = this.cruise.prices[this.cruise.selectedDate.startYear];

			this._resetTickets();
		},
		
		_changeCruise: function() {
			this.sect1.isValid = false;
			this._changeSelector();
		},
		
		
		/**
		 * Validates the section2 "YOUR INFORMATION" form
		 */
		checkSect2: function() {
			var el;
			
			try {
				el = this.sect2.custName;
				if (el.dom.value === '') { throw new Error('Please enter your name.'); }
				if (!el.dom.value.match(this.patterns.title.pattern)) { throw new Error('Problem with Name field. Please use ' + this.patterns.title.errmsg); }

				el = this.sect2.address1;
				if (el.dom.value == '') { throw new Error('Please enter an address.'); }
				if (!el.dom.value.match(this.patterns.paragraph.pattern)) { throw new Error('Problem with Address field. Please use ' + this.patterns.paragraph.errmsg); }

				el = this.sect2.address2;
				if (el.dom.value !== '') {
					if (!el.dom.value.match(this.patterns.paragraph.pattern)) { throw new Error('Problem with second Address field. Please use ' + this.patterns.paragraph.errmsg); }
				}

				el = this.sect2.city;
				if (el.dom.value == '') { throw new Error('Please enter your city.'); }
				if (!el.dom.value.match(this.patterns.title.pattern)) { throw new Error('Problem with City field. Please use ' + this.patterns.title.errmsg); }

				if (this.sect2.countryAbbr.dom.value == 'USA') {
					
					el = this.sect2.stateAbbr;
					if (el.dom.value == '') { throw new Error('Please select your state.'); }
					if (!el.dom.value.match(this.patterns.stateAbbr.pattern)) { throw new Error('Problem with State field. Please use ' + this.patterns.stateAbbr.errmsg); }
	
					el = this.sect2.zip;
					if (el.dom.value === '') { throw new Error('Please enter your zip code.'); }
					if (!el.dom.value.match(this.patterns.zip.pattern)) { throw new Error('Problem with Zip Code field. Please use ' + this.patterns.zip.errmsg); }

				} else {

					el = this.sect2.stateAbbr;
					if (el.dom.value == '') { throw new Error('Please select your state.'); }
					if (!el.dom.value.match(this.patterns.canadianStateAbbr.pattern)) { throw new Error('Problem with State field. Please use ' + this.patterns.canadianStateAbbr.errmsg); }
	
					el = this.sect2.zip;
					if (el.dom.value === '') { throw new Error('Please enter your zip code.'); }
					if (!el.dom.value.match(this.patterns.canadianZip.pattern)) { throw new Error('Problem with Zip Code field. Please use ' + this.patterns.canadianZip.errmsg); }
					
				}

				el = this.sect2.phone;
				if (el.dom.value === '') { throw new Error('Please enter your phone number.'); }
				if (!el.dom.value.match(this.patterns.phone.pattern)) { throw new Error('Problem with Phone field. Please use ' + this.patterns.phone.errmsg); }

				el = this.sect2.email;
				if (el.dom.value === '') { throw new Error('Please enter your email address.'); }
				if (!el.dom.value.match(this.patterns.email.pattern)) { throw new Error('Problem with Email field. Please use ' + this.patterns.email.errmsg); }

				el = this.sect2.referral;
				if (el.dom.value === '') { throw new Error('Please tell us how you heard about us.'); }
				if (!el.dom.value.match(this.patterns.alnumspace.pattern)) { throw new Error('Problem with referral field. Please select one of the provided options.'); }

			} catch(err) {
				this.sect2.isValid = false;
				Ext.Msg.alert('Error', err.message, function() { // show an alert box and then highlight the problem field
					el.focus();
					el.highlight('ffff55', { duration: 2.5 });
				});
				return;
			}
			
			this.sect2.isValid = true;
			this.updateTickets();
			this.sect3.container.show();
		//	this.sect3.container.dom.scrollIntoView();
		},
		
		
		/**
		 * Validates the section3 "TICKETS NEEDED" form
		 */
		checkSect3: function() {
			
			var el, t, total;
			
			try {
				t = this.sect3.tickets;
				total = t.adult + t.adultSingle + t.adultDouble + t.adultTriple + t.adultQuad + t.senior + t.child + t.infant;
				if (total < 1) { throw new Error('Please select the number and type of tickets you want.'); }

				el = this.sect3.comments;
				if (el.dom.value !== '') {
					if (!el.dom.value.match(this.patterns.message.pattern)) { throw new Error('Problem with Comments field. Please use ' + this.patterns.message.errmsg); }
				}

			} catch(err) {
				this.sect3.isValid = false;
				Ext.Msg.alert('Error', err.message, function() { // show an alert box and then highlight the problem field
					el.focus();
					el.highlight('ffff55', { duration: 2.5 });
				});
				return;
			}
			
			this.sect3.isValid = true;
			this.sect4.container.show();
		//	this.sect4.container.dom.scrollIntoView();
		},
		
		
		/**
		 * Checks the whole form for validity. If invalid, it stops the form submission
		 */
		checkForm: function(e) {
		
			var oThis = this;

			if (!this.sect1.isValid) {
				e.stopEvent();
				return;
			}
			if (!this.sect2.isValid) {
				e.stopEvent();
				this.checkSect2();
				return;
			}
			if (!this.sect3.isValid) {
				e.stopEvent();
				this.checkSect3();
				return;
			}
			
			this.sect4.submitButton.dom.value = 'processing...';

			try {
				
				
				
				
				
				// send ajax request if no exceptions thrown
				this.ajax = Ext.Ajax.request({
					url: 'ajax/tickets/order/cruise',
					method: 'POST',
					success: function() {
						var data = eval(arguments[0].responseText);
						oThis.sect4.response.dom.innerHTML = data.status;
						oThis.sect4.response.show();
						oThis.sect4.submitButton.dom.value = 'Submit Form';
					},
					failure: function(a) {
						Ext.Msg.alert('Error', a.msg);
					},
					scope: oThis,
					params: {
						ajaxAction: 'submit',
						table: encodeURIComponent(document.getElementById('calculationTable').innerHTML) // Send the calculation table to the server as HTML so it can be printed in the submitted email
					},
					form: this.form
				});
			} catch (err) {
				Ext.Msg.alert('Error', err.message);
				this.sect4.submitButton.dom.value = 'Submit Form';
			} finally {
				e.stopEvent();
			}
		},
		
		
		updateTickets: function() {
			this.sect3.tickets.adult = parseInt(this.sect3.selections.adult.dom.value, 10);
			this.sect3.tickets.adultSingle = parseInt(this.sect3.selections.adultSingle.dom.value, 10);
			this.sect3.tickets.adultDouble = parseInt(this.sect3.selections.adultDouble.dom.value, 10);
			this.sect3.tickets.adultTriple = parseInt(this.sect3.selections.adultTriple.dom.value, 10);
			this.sect3.tickets.adultQuad = parseInt(this.sect3.selections.adultQuad.dom.value, 10);
			this.sect3.tickets.senior = parseInt(this.sect3.selections.senior.dom.value, 10);
			this.sect3.tickets.child = parseInt(this.sect3.selections.child.dom.value, 10);
			this.sect3.tickets.infant = parseInt(this.sect3.selections.infant.dom.value, 10);
			
			this.updateChart();			
		},
		

		/**
		 * Calculates ticket prices, totals, and then updates the display chart with the results
		 */
		// Alice always assumes double (or single) occupancy unless they request otherwise
			// 1 = single
			// 2 = double
			// 3 = 1x single, 1x double
			// 4 = 2x double
		updateChart: function() {
			
			var prices, adultTotal, adultSingleTotal, adultDoubleTotal, adultTripleTotal, adultQuadTotal, seniorTotal, childTotal, infantTotal, grandTotal;
			var oThis = this;

			prices = this.cruise.prices[this.cruise.selectedDate.startYear];
			
			// Calculate the ticket prices
			// This only works correctly for integer ticket prices (i.e. $20).
			// You'll have to scale by 100 if you want to use decimal fractional ticket prices
			// (i.e. convert $19.99 to 1999, perform your math, then scale down to $19.99 again)
			adultTotal = this.sect3.tickets.adult * prices.adult;
			adultSingleTotal = this.sect3.tickets.adultSingle * prices.adultSingle;
			adultDoubleTotal = this.sect3.tickets.adultDouble * prices.adultDouble;
			adultTripleTotal = this.sect3.tickets.adultTriple * prices.adultTriple;
			adultQuadTotal = this.sect3.tickets.adultQuad * prices.adultQuad;
			seniorTotal = this.sect3.tickets.senior * prices.senior;
			childTotal = this.sect3.tickets.child * prices.child;
			infantTotal = this.sect3.tickets.infant * prices.infant;
			
			grandTotal = adultTotal + adultSingleTotal + adultDoubleTotal + adultTripleTotal + adultQuadTotal + seniorTotal + childTotal + infantTotal;
			
			
			// display the ticket quantities
			this.sect3.chart.adultQty.dom.innerHTML = this.sect3.tickets.adult;
			this.sect3.chart.adultSingleQty.dom.innerHTML = this.sect3.tickets.adultSingle;
			this.sect3.chart.adultDoubleQty.dom.innerHTML = this.sect3.tickets.adultDouble;
			this.sect3.chart.adultTripleQty.dom.innerHTML = this.sect3.tickets.adultTriple;
			this.sect3.chart.adultQuadQty.dom.innerHTML = this.sect3.tickets.adultQuad;
			this.sect3.chart.seniorQty.dom.innerHTML = this.sect3.tickets.senior;
			this.sect3.chart.childQty.dom.innerHTML = this.sect3.tickets.child;
			this.sect3.chart.infantQty.dom.innerHTML = this.sect3.tickets.infant;
			
			// display the ticket prices
			this.sect3.chart.adultPrice.dom.innerHTML = (prices.adult == null) ? 'N/A' : formatCurrency(prices.adult);
			this.sect3.chart.adultSinglePrice.dom.innerHTML = (prices.adultSingle == null) ? 'N/A' : formatCurrency(prices.adultSingle);
			this.sect3.chart.adultDoublePrice.dom.innerHTML = (prices.adultDouble == null) ? 'N/A' : formatCurrency(prices.adultDouble);
			this.sect3.chart.adultTriplePrice.dom.innerHTML = (prices.adultTriple == null) ? 'N/A' : formatCurrency(prices.adultTriple);
			this.sect3.chart.adultQuadPrice.dom.innerHTML = (prices.adultQuad == null) ? 'N/A' : formatCurrency(prices.adultQuad);
			this.sect3.chart.seniorPrice.dom.innerHTML = (prices.senior == null) ? 'N/A' : formatCurrency(prices.senior);
			this.sect3.chart.childPrice.dom.innerHTML = (prices.child == null) ? 'N/A' : formatCurrency(prices.child);
			this.sect3.chart.infantPrice.dom.innerHTML = (prices.infant == null) ? 'N/A' : formatCurrency(prices.infant);
			
			// display the totals
			this.sect3.chart.adultTotal.dom.innerHTML = formatCurrency(adultTotal);
			this.sect3.chart.adultSingleTotal.dom.innerHTML = formatCurrency(adultSingleTotal);
			this.sect3.chart.adultDoubleTotal.dom.innerHTML = formatCurrency(adultDoubleTotal);
			this.sect3.chart.adultTripleTotal.dom.innerHTML = formatCurrency(adultTripleTotal);
			this.sect3.chart.adultQuadTotal.dom.innerHTML = formatCurrency(adultQuadTotal);
			this.sect3.chart.seniorTotal.dom.innerHTML = formatCurrency(seniorTotal);
			this.sect3.chart.childTotal.dom.innerHTML = formatCurrency(childTotal);
			this.sect3.chart.infantTotal.dom.innerHTML = formatCurrency(infantTotal);
			this.sect3.chart.grandTotal.dom.innerHTML = formatCurrency(grandTotal);
			
			
		},
		
		
		/**
		 * For sect1, do we show the currently selected cruise or the cruise selector?
		 */
		_changeSelector: function() {
			if (this.sect1.isValid) {
				this.sect1.changeCruiseContainer.show();
				this.sect1.selectCruiseContainer.hide();
			} else {
				this.sect1.changeCruiseContainer.hide();
				this.sect1.selectCruiseContainer.show();
			}
		},
		
		
		/**
		 * Resets our state to zero out any ticket selections
		 */
		_resetTickets: function() {
			
			this.sect3.tickets = { // This object keeps track of the number and type of all tickets currently selected.
				adult: 0,
				adultSingle: 0,
				adultDouble: 0,
				adultTriple: 0,
				adultQuad: 0,
				senior: 0,
				child: 0,
				infant: 0
			};
			
			// set the SELECT values
			this.sect3.selections.adult.dom.value = 0;
			this.sect3.selections.adultSingle.dom.value = 0;
			this.sect3.selections.adultDouble.dom.value = 0;
			this.sect3.selections.adultTriple.dom.value = 0;
			this.sect3.selections.adultQuad.dom.value = 0;
			this.sect3.selections.senior.dom.value = 0;
			this.sect3.selections.child.dom.value = 0;
			this.sect3.selections.infant.dom.value = 0;
			
			if (this.cruise) {
				
				var prices = this.cruise.prices[this.cruise.selectedDate.startYear];

				// enable/disable the correct SELECTs for the current cruise
				this.sect3.selections.adult.dom.disabled = prices.adult ? false : true;
				this.sect3.selections.adultSingle.dom.disabled = prices.adultSingle ? false : true;
				this.sect3.selections.adultDouble.dom.disabled = prices.adultDouble ? false : true;
				this.sect3.selections.adultTriple.dom.disabled = prices.adultTriple ? false : true;
				this.sect3.selections.adultQuad.dom.disabled = prices.adultQuad ? false : true;
				this.sect3.selections.senior.dom.disabled = prices.senior ? false : true;
				this.sect3.selections.child.dom.disabled = prices.child ? false : true;
				this.sect3.selections.infant.dom.disabled = prices.infant ? false : true;

				// enable/disable the correct THs for the current cruise
				if (prices.adult) { this.sect3.TH.adult.removeClass('disabled'); } else { this.sect3.TH.adult.addClass('disabled'); }
				if (prices.adultSingle) { this.sect3.TH.adultSingle.removeClass('disabled'); } else { this.sect3.TH.adultSingle.addClass('disabled'); }
				if (prices.adultDouble) { this.sect3.TH.adultDouble.removeClass('disabled'); } else { this.sect3.TH.adultDouble.addClass('disabled'); }
				if (prices.adultTriple) { this.sect3.TH.adultTriple.removeClass('disabled'); } else { this.sect3.TH.adultTriple.addClass('disabled'); }
				if (prices.adultQuad) { this.sect3.TH.adultQuad.removeClass('disabled'); } else { this.sect3.TH.adultQuad.addClass('disabled'); }
				if (prices.senior) { this.sect3.TH.senior.removeClass('disabled'); } else { this.sect3.TH.senior.addClass('disabled'); }
				if (prices.child) { this.sect3.TH.child.removeClass('disabled'); } else { this.sect3.TH.child.addClass('disabled'); }
				if (prices.infant) { this.sect3.TH.infant.removeClass('disabled'); } else { this.sect3.TH.infant.addClass('disabled'); }
				
				// enable/disable the price rows for the current cruise
				if (prices.adult) { this.sect3.rows.adult.removeClass('disabled'); } else { this.sect3.rows.adult.addClass('disabled'); }
				if (prices.adultSingle) { this.sect3.rows.adultSingle.removeClass('disabled'); } else { this.sect3.rows.adultSingle.addClass('disabled'); }
				if (prices.adultDouble) { this.sect3.rows.adultDouble.removeClass('disabled'); } else { this.sect3.rows.adultDouble.addClass('disabled'); }
				if (prices.adultTriple) { this.sect3.rows.adultTriple.removeClass('disabled'); } else { this.sect3.rows.adultTriple.addClass('disabled'); }
				if (prices.adultQuad) { this.sect3.rows.adultQuad.removeClass('disabled'); } else { this.sect3.rows.adultQuad.addClass('disabled'); }
				if (prices.senior) { this.sect3.rows.senior.removeClass('disabled'); } else { this.sect3.rows.senior.addClass('disabled'); }
				if (prices.child) { this.sect3.rows.child.removeClass('disabled'); } else { this.sect3.rows.child.addClass('disabled'); }
				if (prices.infant) { this.sect3.rows.infant.removeClass('disabled'); } else { this.sect3.rows.infant.addClass('disabled'); }

				this.updateChart();
			}
			
			
		},
		
		patterns: {
			alnumspace: {
				pattern	: /^[\w\s]{1,}$/,
				errmsg	: 'only letters, numbers, or spaces'
			},
			title: {
				pattern	: /^[\w\s\-\/\'\",\.!]{1,}$/,
				errmsg	: 'only letters, numbers, dashes, quotes, exclamation marks, or the underscore'
			},
			paragraph: {
				pattern	: /^[\w\s\.\(\)\'\"\!\?\$\/,:;&@<>#\-]{1,}$/,
				errmsg	: 'letters, numbers, spaces, or standard punctuation'
			},
			email: {
				pattern	: /^([\w\.\-\_]+)@[a-zA-Z0-9\.\-]+\.[a-zA-Z]{2,4}$/,
				errmsg	: 'a valid email address'
			},
			zip: {
				pattern	: /^([0-9]{5})(-[0-9]{4})?$/,
				errmsg	: 'a valid zip code'
			},
			canadianZip: {
				pattern	: /^[a-z0-9]{3}\s[a-z0-9]{3}$/i,
				errmsg	: 'a valid Canadian postal code in the form A0A 0A0'
			},
			message: {
				pattern	: /^[\w\s\.\,\'\"\?\!]{1,}$/,
				errmsg	: 'letters, numbers, spaces, or the following: .,\'"?!'
			},
			stateAbbr: {
				pattern	: /^A[LKSZR]|C[AOT]|D[EC]|F[ML]|G[AU]|HI|I[DLNA]|K[SY]|LA|M[EHDAINSOT]|N[EVHJMYCD]|MP|O[KHR]|P[WAR]|RI|S[CD]|T[NX]|UT|V[TIA]|W[AIVY]$/,
				errmsg	: 'a valid US state abbreviation'
			},
			canadianStateAbbr: {
				pattern	: /^AB|BC|MB|N[BLSTU]|ON|PE|QC|SK|YT$/i,
				errmsg	: 'a valid Canadian state abbreviation'
			},
			street: {
				pattern	: /^[\w\s\.\-\/]{1,128}$/,
				errmsg	: 'a valid street address'
			},
			phone: {
				pattern	: /^(\(?[0-9]{3}\)?)?[\s\.\-]?[0-9]{3}[\s\.\-]?[0-9]{4}$/,
				errmsg	: 'a valid US phone number'
			}
		} // end patterns
		
	
	};
	obj.init();
	return obj;
}();

function formatCurrency(num) {
	num = num.toString().replace(/\$|\,/g,'');
	if(isNaN(num)) {
		num = "0";
	}
	sign = (num == (num = Math.abs(num)));
	num = Math.floor(num*100+0.50000000001);
	cents = num%100;
	num = Math.floor(num/100).toString();
	if(cents<10) {
		cents = "0" + cents;
	}
	for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++) {
		num = num.substring(0,num.length-(4*i+3))+','+num.substring(num.length-(4*i+3));
	}
	return (((sign)?'':'-') + '$' + num + '.' + cents);
}
