window.onload = function(){
	// PRODUCT SELECTION
	if (document.getElementById('selectForm')) {
		boxen.init({
			formID:'selectForm', 
			totalItemsID:'totalItems', 
			totalCartonsID:'totalCartons', 
			productCostID:'productCost',
			totalPriceID:'totalPrice',
			highlightClass:'productLine',
			bonusNode:'bonus',
			cartonID:'cartons',
			cartonFlashParams:{url:'../_assets/images/boxen.swf', width:220, height:100, bgColor:'#DCDCB9'}
		});
	}
	// SHIPPING
	else if (document.getElementById('orderDelivery')) {
		happyError.init('orderDelivery');
		happyShipping.init('freight', 'totalShipping', 'totalPrice');
		
		var copyDetailsNode = document.getElementById('copyDetails');
		copyDetailsNode.onchange = copyFields;
	}
	// PAYMENT
	else if (document.getElementById('billingForm')) {
		happyError.init('billingForm');
		// happySwitch.init(['Customer_Payment_Type_Credit', 'Customer_Payment_Type_Cheque'], 'blockGroup');
		// happyCopy.init('copyDetails', 'shippingAddress');
		// Temp Hack
//		var copyDetails = document.getElementById('copyDetails');
//		copyDetails.onchange = insertAddress;
//		copyDetails.onclick = insertAddress;
//		if (copyDetails.checked == true) {
//			copyDetails.checked = false;
//		}
	}
}

/*
    Parses the class names of the each of the checkbox inputs in the form with the given 
    parentFormId looking for disables_<ID>, where ID is the html entity identifier that
    should be disabled when the associated element is unchecked. An onclick event is
    attached to the checkbox to control the enabled state of the nominated "disables"   
    nodes.
*/
function disables(parentFormId)
{
    form = document.getElementById(parentFormId);
    allInputs = form.getElementsByTagName('input');
    disableControllers = new Array();

	for (var i = 0; i < allInputs.length; i++) 
	{
	   if (allInputs[i].type == 'checkbox')
	   {
    	   if (allInputs[i].className.indexOf('disables_') != -1) 
    	   {
    	       var disableIds = new Array();
               var disableCount = 0;
               
    	       classes = allInputs[i].className.split(' ');
    		   for (var j = 0; j < classes.length; j++) 
    		   {
        	       if (classes[j].indexOf('disables_') != -1)
        	       {
            	       disableId = classes[j].split('disables_')[1];
            	       disableIds[disableCount] = disableId;
                       disableCount = disableCount + 1;
                   }
        	   }  
        	   
        	   // Store the ids to be controlled in an array keyed by the 
        	   // controlling nodes html identifier.
        	   this.disableControllers[allInputs[i].id] = {
						disableIds:disableIds, 
						node:allInputs[i]
			     };
        	   
        	   // Initialise the states.
        	   for (var disableId in disableIds) 
		      {
	              document.getElementById(disableIds[disableId]).disabled = !allInputs[i].checked;
	          } 
    
   	          allInputs[i].onclick = function()
    	      {     	   
    	          // Get the disable definition for this node.      	         	    
    	          disableDef = disableControllers[this.id];
    	          // For each of the nodes to be disabled.
    		      for (var disableId in disableDef.disableIds) 
    		      {    		          
    	              document.getElementById(disableDef.disableIds[disableId]).disabled = !this.checked;
    	          }
    	      };
    	   }
    	}
	}
}

/*	----------------------------------------------------------------------
	BOXEN 
	Fancy little script to drive our wine carton update interface.
	All packed into an object in order to avoid polluting the namespace. YAY!
	
	The following classes may be used:
		* unitprice_12-30		- Unit price.
		* maximum_12			- Maximum user can buy
		* minimum_2				- Minimum user can buy
		* modifier_1			- For items sold in cases only
		* caseprice_12-30		- Discounted price when user buys a case worth
		* casesize_12			- How large is a case then?
	---------------------------------------------------------------------- */
	// Expand this object in the future to allow us to pass in a literal containing all the ids,
	// and omit those missing when initialsed. Also perhaps some other config could go in.
boxen = {
	inputs: [],
	totalCartons: 0,
	totalItems: 0,
	totalPrice: 0,
	productCost: 0,
	totalPriceNode: 0,
	productCostNode: 0,
	totalItemsNode: 0,
	totalCartonsNode: 0,
	highlightClass: null,
	boxenNode: null,
	bonusNode: null,
	bonusSwitch: null,
	// INITALISE
	// Collect inputs, assign handlers, store config details
	init: function(options) {
		form = document.getElementById(options.formID);
		// Check for bonuses
		if (options.bonusNode) {
			this.bonusNode = document.getElementById(options.bonusNode);
			this.bonusSwitch = parseInt(form.className);
			//formClasses = form.className.split(' ');
			//for (var h = 0; h < formClasses.length; h++) {
			//	if (formClasses[h].lastIndexOf('bonusunits') != -1) {
			//		//test = /bonusunits_(\d+)/
			//		//alert(test(formClasses[h]))
			//		//bonus = formClasses[h]
			//		//bonus = bonus.split('_');
			//		//this.bonusSwitch = parseInt(bonus[1]);
			//	}
			//}
		}
		// Init the quantity inputs
		inputs = form.getElementsByTagName('input');
		for (var i = 0; i < inputs.length; i++) {
			if (inputs[i].className.lastIndexOf('quantity') != -1) {
				// set up local vars to store extracted values
				var maximum 	= null;
				var minimum 	= null;
				var unitPrice 	= null;
				var modifier 	= null;
				var casePrice 	= null;
				var caseSize 	= 12;
				// loop through all the classnames
				classes = inputs[i].className.split(' ');
				for (var j = 0; j < classes.length; j++) {
					// get the modifier
					if (classes[j].lastIndexOf('modifier') != -1) {
						modifier = classes[j].split('_');
						modifier = parseInt(modifier[1]);
					}
					// get the price
					else if (classes[j].lastIndexOf('unitprice') != -1) {
						unitPrice = classes[j].split('_');
						unitPrice = parseFloat(unitPrice[1].replace(/-/, '.')); // Swap out the hypen for a decimal
					}
					// get the maximum
					else if (classes[j].lastIndexOf('maximum') != -1) {
						maximum = classes[j].split('_');
						maximum = parseInt(maximum[1]);
					}
					// get the minimum
					else if (classes[j].lastIndexOf('minimum') != -1) {
						minimum = classes[j].split('_');
						minimum = parseInt(minimum[1]);
					}
					// Case size
					else if (classes[j].lastIndexOf('casesize') != -1) {
						caseSize = classes[j].split('_');
						caseSize = parseInt(caseSize[1]);
					}
					// Case price
					else if (classes[j].lastIndexOf('caseprice') != -1) {
						casePrice = classes[j].split('_');
						casePrice = parseFloat(casePrice[1].replace(/-/, '.'));
					}
				}
				// Check to see if the modifier is up to date, else set a default
				if (modifier == null) {
					modifier = 1;
				}
				// Store extracted values
				this.inputs[inputs[i].id] = {
						modifier:modifier, 
						node:inputs[i], 
						unitPrice:unitPrice, 
						maximum:maximum,
						minimum:minimum,
						caseSize:caseSize,
						casePrice:casePrice, 
						totalItems:0,
						totalPrice:0, 
						totalNode: document.getElementById(inputs[i].id + 'Total') // This is a bit brittle...
				};
				var boxObj = this;
				inputs[i].onkeyup = function(){boxObj.update(this)};
			}
		}
		// Collect the totals (add tests to see if they are there)
		this.totalPriceNode = document.getElementById(options.totalPriceID);	
		this.productCostNode = document.getElementById(options.productCostID);		
		this.totalItemsNode = document.getElementById(options.totalItemsID);
		this.totalCartonsNode = document.getElementById(options.totalCartonsID);
		// Insert our flash cartons
		if (options.cartonID && options.cartonFlashParams) {	
			var fo = new FlashObject(
				options.cartonFlashParams.url,
				'boxen',
				options.cartonFlashParams.width,
				options.cartonFlashParams.height,
				'6',
				options.cartonFlashParams.bgColor
				);
				//function(swf, id, w, h, ver, c)
			fo.write(options.cartonID);
			this.boxenNode = document.getElementById('boxen');	
		}	
		// See the highlightClass. This is the class used to pick the parent we need to highlight
		if (options.highlightClass) {
			this.highlightClass = options.highlightClass;
		}
		// Update the display... this deals with initial values, and firefox caching values
		this.updateAll();
	},
	// UPDATE TOTALS
	// This is called when a single input has had it's value changed
	update: function(node) {
		this.updateInput(node);
		this.updateTotals();
	},
	// UPDATE TOTALS FOR ONE INPUT
	updateInput: function(node) {
		input = this.inputs[node.id];
		// Basic validation
		if (isNaN(input.node.value)) {
			happyError.showErrors(node, ['Whole numbers only']);
			var newValue = parseInt(node.value);
			if (isNaN(newValue)) {
				node.value = 0;
			}
			else {
				node.value = parseInt(node.value);
			}			
		}
		// Validate maximum
		if (input.node.value > input.maximum && input.maximum != null) {
			input.node.value = input.maximum;
			// Display an error stating the maximum
		}
		// Validate minimum
		if (input.node.value < input.minimum && input.minimum != null && input.node.value != 0) {
			input.node.value = input.minimum;
			// Display an error stating the minimum
		}
		// Check to for case modifiers
		if (input.caseSize != null && input.casePrice != null) {
			if (input.node.value >= input.caseSize) {
				// Figure out how many cartons
				var remainder = input.node.value % input.caseSize;
				var cartons = (input.node.value - remainder) / input.caseSize;
				input.totalPrice = (cartons * input.casePrice) + (remainder * input.unitPrice);				
			}
			else {				
				input.totalPrice = input.unitPrice * input.node.value;
			}
		}
		else {
			input.totalPrice = input.unitPrice * input.node.value;
		}		
		// Then figure out the totals items
		input.totalItems = input.node.value * input.modifier;
		// Update the total node
		input.totalNode.innerHTML = currencyFormatter(input.totalPrice);
		// Check to see if we need to toggle the highlight too
		if (this.highlightClass != null) {
			if (node.value > 0) {
				this.toggleHighlight(node, true);
			}
			else {
				this.toggleHighlight(node, false);
			}
		}
	},
	toggleHighlight: function(node, state) {
		target = node;
		do {
			target = target.parentNode;
		} while(target.className.lastIndexOf(this.highlightClass) == -1)
		if (state == true) {
			addClass(target, 'highlight');
		}
		else {
			removeClass(target, 'highlight');
		}
	},
	// UPDATE TOTAL
	// This loops through all the stored inputs, builds the totals
	// and then updates the total display. Also calls the flash update
	updateTotals: function() {		
		this.totalItems = 0;
		this.totalPrice = 0;
		// May need to change depending if we use a has or literal
		for (var i in this.inputs) {			
			// IE 5 add an additional property to the hash table. We need to exclude it
			if (this.inputs[i].totalItems) {
				this.totalItems += this.inputs[i].totalItems;
				this.totalPrice += this.inputs[i].totalPrice;
			}
		}
		// Figure out total cartons
		remainder = this.totalItems % 12;
		this.totalCartons = (this.totalItems - remainder) / 12;
		if (remainder != 0) {
			this.totalCartons += 1;
		}
		// Update displays
		this.totalItemsNode.innerHTML = this.totalItems;
		this.totalCartonsNode.innerHTML = this.totalCartons;
		this.productCostNode.innerHTML = currencyFormatter(this.totalPrice);
		this.totalPriceNode.innerHTML = currencyFormatter(this.totalPrice);
		// Check to see if we need to update the flash
		if (this.boxenNode != null) {			
			this.updateCarton(this.totalItems);
		}
		// Do we need to display the bonus?
		if (this.bonusNode != null) {
			if (this.totalItems >= this.bonusSwitch) {
				this.bonusNode.style.visibility = 'visible';
			}
			else {
				this.bonusNode.style.visibility = 'hidden';
			}
		}
	},
	// UPDATE ALL THE INPUT TOTALS
	// Usually used when we first load up the page
	updateAll: function() {
		for (var i in this.inputs) {
			if (this.inputs[i].node) {
				this.updateInput(this.inputs[i].node);
			}
		}
		this.updateTotals();
	},
	// UPDATE CARTON
	// This updates the flash file. Pretty simple right now but
	// will get a bit more complicated when we change our flash file
	// For now it's not even checking if it's loaded
	updateCarton: function(items) {
		if (this.boxenNode.PercentLoaded() == 100) {
			this.boxenNode.GotoFrame(items);
		}
		else {
			box = this.boxenNode;
			window.setTimeout('box.GotoFrame(' + items + ')', 2000)
		}
	}
}

/*	----------------------------------------------------------------------
	VALIDATION 
	Some simple validations. These are packed into a literal object to
	prevent name collisions. The logic may improve in the future, but
	the interface will likely remain the same.
	
	May need something to deal with dependencies. IE, if we have a
	credit number, we need to know the card type to validate it.
	Same with checking the expiry on a card.
	
	Works with one form at a time.
	
	Handles the following validations
		* he-Required 		- Must not be empty
		* he-Integer		- Whole numbers only
		* he-Float			- Numbers only
		* he-Email			- Valid email address
		* he-Phone			- Valid australian phone number
		* he-CreditNumber	- Valid credit card
		* he-CreditType	- Credit card vendor
		* he-CreditYear	- Valid expiry for credit card
		* he-CreditMonth	- Valid expiry for credit card
		* he-Depend_ID 	- A dependent field
	---------------------------------------------------------------------- */
happyError = {
	formNode: null,
	errorNode: null,
	errorTimeout: null,
	inputs: [],
	required: [],
	hasRequiredFields: false,
	// INITALISE
	// Figures out what the items need to be validated against, and
	// stores em for later. Attaches the events.
	init: function(formID) {
		this.formNode = document.getElementById(formID);
		// Get the inputs
		var inputs = this.formNode.getElementsByTagName('input');
		this.extractValidations(inputs);
		// Get the textareas
		var textareas = this.formNode.getElementsByTagName('textarea');
		this.extractValidations(textareas);
		// Get the selects
		var selects = this.formNode.getElementsByTagName('select');
		this.extractValidations(selects);
		// If it has required fields, attach this 'ere event
		if (this.hasRequiredFields) {
			var errorObj = this;
			this.formNode.onsubmit = function(){errorObj.checkRequiredFields();return false;}
		}
	},
	// EXTRACT VALIDATIONS
	// This extracts the validations, and attaches events
	extractValidations: function(nodes) {
		for (var i = 0; i < nodes.length; i++) {
			if (nodes[i].className) {
				classes = nodes[i].className.split(' ');
				var validations = [];
				var dependencies = null;
				for (var j = 0; j < classes.length; j++) {
					// See if this class name has the validation prefix
					if (classes[j].lastIndexOf('he-') != -1) {
						validation = classes[j].split('-');
						validation = validation[1];
						// Check for required fields
						if (validation == 'required') {
							this.required.push(nodes[i]);
							this.hasRequiredFields = true;
						}
						// Check for dependency
						// Only supports single dependency for now
						else if (validation.lastIndexOf('depend') != -1) {
							dependencies = validation.split('_');
							dependencies = dependencies[1];
						}
						else {
							validations.push(validation);
						}
					}
				}
				// Only store it if we actually have any validations
				if (validations.length > 0) {
					this.inputs[nodes[i].id] = {
						node:nodes[i],
						validations:validations,
						dependencies:dependencies
					}
					var errorObj = this;
					nodes[i].onchange = function(){errorObj.validate(this);}
				}
			}
		}	
	},
	// VALIDATE AN INPUT
	// Node is required. Validations is option. Is an array of strings.
	// If it is provided, those validations are used instead of any cached.
	validate: function(node, optValidations) {
		// Check for optional validations (non-cached)
		var validations;
		if (optValidations) {
			validations = optValidations;
		}
		else {
			validations = this.inputs[node.id].validations;
		}
		// Add logic for checkboxes
		var value = node.value;
		var errors = [];
		// Validate
		for (var i = 0; i < validations.length; i++) {
			var check = this['valid_' + validations[i]](node);
			if (check != true) {
				errors.push(check);
			}
		}
		// Display errors if we have any
		// In the future have validations return an array.
		// true/false, message, node
		if (errors.length > 0) {
			this.showErrors(node, errors);
		}
	},
	// CHECK REQUIRED FIELDS
	checkRequiredFields: function() {
		var invalid = false;
		for (var i = 0; i < this.required.length; i++) {
			if (this.required[i].value == '' || this.required[i].value == ' ') {
				invalid = true;
				// highlight the field
				addClass(this.required[i], 'attention');
			}
		}
		if (invalid) {
			alert('There are required fields you need to fill out.\n These are highlighted.');
			return false;
		}
		else {
			this.formNode.submit();
		}
	},
	// ERROR CACHE
	addError: function() {
		
	},
	// SHOW ERRORS
	// Gets passed a node and an array of error messages. Displays em. Easy.
	// Optionally we may do some stuff to allow different displays.
	showErrors: function(node, messages) {
		// Collect the errors into a string
		var message = '';
		for (var i = 0; i < messages.length; i++) {
			message += messages[i] + '\n';
		}
		//alert(message);
		// Inialise the error display
		if (this.errorNode == null) {
			this.errorNode = document.createElement('div');
			this.errorNode.className = 'error';
		}
		else {
			// make sure it's not sitting in the document
			this.hideErrors();
		}
		this.errorNode.innerHTML = message;
		// Position it, stick it in the page
		node.parentNode.style.position = 'relative';
		this.errorNode.style.left = node.offsetLeft + node.offsetWidth + 'px';
		this.errorNode.style.top = node.offsetTop + 'px';
		node.parentNode.appendChild(this.errorNode);
		// Add events to hide the error 
		errorObj = this;
		node.onfocus = function() {errorObj.hideErrors();}
		// I HATE HAVING TO PASS A GOD_DAMN STRING!
		this.errorTimeout = window.setTimeout('happyError.hideErrors()', 4000);
	},
	hideErrors: function() {
		if (this.errorNode.parentNode) {
			parentNode = this.errorNode.parentNode;
			parentNode.removeChild(this.errorNode);
		}
		window.clearTimeout(this.errorTimeout);
	},
	// VALIDATIONS
	// Oh, lot's of 'em! Either return true, or a string, which is the error message
	valid_integer: function(node) {
		value = node.value
		// need to actually Check if this a WHOLE NUMBER!!! DERRR!
		if (isNaN(value) == true) {
			var error = 'Whole numbers only.';
			return error;
		}
		else {
			return true;
		}
	},
	valid_phone: function(node) {
		value = node.value;
		var filter  = /^(\(?\+?[0-9]*\)?)?[0-9_\- \(\)]*$/;
		if (!filter.test(value.toString())) {
			var error = 'Numbers only for your phone or fax number.';
			return error;
		} 
		else {
			return true;
		}
	},
	valid_email: function(node) {
		value = node.value;
		var x = value;
		var filter  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
		if (!filter.test(x)) {
			var error = 'This is not a valid email address.';
			return error;
		}
		else {
			return true;
		}
	},
	valid_creditNumber: function(node) {
		value = node.value;
		dependentID = this.inputs[node.id].dependencies;
		cardType = document.getElementById(dependentID).value;
		return this.checkCreditNumber(value, cardType);
	},
	valid_creditType: function(node) {
		value = node.value;
		numberNode = document.getElementById(this.inputs[node.id].dependencies);
		cardNumber = numberNode.value;
		return this.checkCreditNumber(cardNumber, value);
		// can't reset the target node with this style
	},
	checkCreditNumber: function(value, cardType) {
		if (isNaN(value)) {
			error = 'Please use numbers only (no spaces)';
			return error;
		}
		// Check the card type and do the checks based on that
		var cardNumber = value.toString();
		var startTest;
		switch(cardType) {
			case 'Visa':
				// VISA
				startTest = parseInt(cardNumber.slice(0, 1));
				if (startTest != 4) {
					error = 'This is not a valid Visa number';
					return error;
				}
			break;
			case 'MasterCard':
				// MASTERCARD
				startTest = parseInt(cardNumber.slice(0, 1));
				if (startTest != 5) {
					error = 'This is not a valid Mastercard number';
					return error;
				}
			break;
			case 'American Express':
				// AMERICAN EXPRESS
				startTest = parseInt(cardNumber.slice(0, 2));
				if (startTest != 37) {
					error = 'This is not a valid American Express number';
					return error;					
				}
			break;
			case 'BankCard':
				// BANKCARD
				startTest = parseInt(cardNumber.slice(0, 4));
				if (startTest != 5610) {
					error = 'This is not a valid BankCard number';
					return error;					
				}
			break;
			case 'Diners':
				// DINERS CLUB
				startTest = parseInt(cardNumber.slice(0, 1));
				if (startTest != 3) {
					error = 'This is not a valid Diners Club number';
					return error;					
				}
			break;
		}
		return true;
	},
	valid_creditYear: function(node) {
		value = node.value;
		if (isNaN(value) || value == 0) {
			error = 'This is not a valid year'
			return error;
		}		
		else {
			today = new Date();
			// turn the test value into an oughtie number
			var testValue = value.toString();
			testValue = parseInt(20 + testValue);
			// Now lets check it out
			if (testValue <= today.getFullYear() - 1) {
				error = 'This expiry date is invalid';
				return error;
			}
			else {
				return true;
			}
		}
	},
	valid_creditMonth: function(node) {
		value = node.value;
		if (value > 12 || isNaN(value) || value == 0) {
			error = 'Please use a value between 1 and 12'
			return error;
		}
		else {
			return true;
		}
	},
	// UTILITY METHODS FOR VALIDATION
	getDependentNode: function(nodeID) {
		return document.getElementById(this.inputs[nodeID].dependencies);
	}
}

/*	----------------------------------------------------------------------
	SHIPPING
	Doesn't do too much. It just updates the totals when the user changes
	the shipping.
	---------------------------------------------------------------------- */
happyShipping = {
	inputs: [],
	shippingNode: null,
	totalPriceNode: null,
	totalPrice: 0,
	init: function(optionBoxID, shippingID, totalID) {
		// Collect inputs and prices
		var box = document.getElementById(optionBoxID);
		var inputs = box.getElementsByTagName('input');
		for (var i = 0; i < inputs.length; i++) {
			if (inputs[i].className.lastIndexOf('shippingDestination') != -1) {
				var classes = inputs[i].className.split(' ');
				for (var j = 0; j < classes.length; j++) {
					if (classes[j].lastIndexOf('price') != -1) {
						price = classes[j].split('_');
						price = price[1];
					}
				}
				//priceIndex = inputs[i].className.lastIndexOf('price_');
				//price = parseFloat(inputs[i].className.slice(priceIndex, priceIndex + 5));
				
				//priceTest = /price_([^\s]*)\s*/;
				//price = priceTest(inputs[i].className);
				//price = parseFloat(price[1]); // Dirty hack coz it returns a match and sub-match
				this.inputs[inputs[i].id] = {node:inputs[i], price:parseFloat(price)};
				// Attach events
				shippingObj = this;
				inputs[i].onclick = function() {shippingObj.update(this);}
			}
		}
		// Get the total nodes and their prices
		this.shippingNode = document.getElementById(shippingID);
		this.totalPriceNode = document.getElementById(totalID);
		this.totalPrice = this.extractPrice(this.totalPriceNode.innerHTML);
	},
	extractPrice: function(oldPrice) {
		newPrice = parseFloat(oldPrice.replace(/[$,-]/g, ''));
		//newPrice = parseFloat(oldPrice.slice(1, oldPrice.length));
		return newPrice;
	},
	update: function(node) {
		shipping = this.inputs[node.id].price;
		newPrice = currencyFormatter(this.totalPrice + shipping);
		this.shippingNode.innerHTML = currencyFormatter(shipping);
		this.totalPriceNode.innerHTML = newPrice;
	}
}

/*	----------------------------------------------------------------------
	SHIPPING
	Doesn't do too much. It just updates the totals when the user changes
	the shipping.
	---------------------------------------------------------------------- */
happyCopy = {
	copiedIn: false,
	parentNode: null,
	storedValues: null,
	inputs: [],
	init: function(controlID, parentID) {
		var control = document.getElementById(controlID);
		copyObj = this;
		control.onchange = function() {copyObj.swap();}
		this.parentNode = document.getElementById(parentID);
	},
	swap: function() {
		// collect the stored values and inputs
		if (this.storedValues == null) {
			this.storedValues == [];
			var nodes = [];
			nodes = this.collectNodes(nodes, this.parentNode, 'hidden');
			nodes = this.collectNodes(nodes, this.parentNode, 'input');
			nodes = this.collectNodes(nodes, this.parentNode, 'textarea');
			nodes = this.collectNodes(nodes, this.parentNode, 'select');
			for (var i = 0; i < nodes.length; i++) {
				if (nodes[i].name.lastIndexOf('Stored_') != -1) {
					fieldName = nodes[i].name.slice(6, nodes[i].name.length);
					this.storedValues[fieldName] = nodes[i].value;
				}
				else {
					if (this.storedValues[nodes[i].name]) {
						this.inputs[nodes[i].name] = nodes[i];
					}
				}
			}
		}
		// Swap in or out
		if (this.copiedIn == false) {
			//for (var j = 0; j < this.)
		}
	},
	collectNodes: function(collection, boss, nodeName) {
		newCollection = boss.getElementsByTagName(nodeName);
		for (var i = 0; i < newCollection.length; i++) {
			collection.push(newCollection[i]);
		}
		return collection;
	}
}	
		

/*	----------------------------------------------------------------------
	HAPPY SWITCH
	This lets us toggle between a set of options. Only handles one for now
	but in the future this will expand to handle more.
	---------------------------------------------------------------------- */
happySwitch = {
	switches: [],
	init: function(switches, blockClass) {
		for(var i = 0; i < switches.length; i++) {
			node = document.getElementById(switches[i]); 
			block = node; 
			do {
				block = block.parentNode;
			} while(block.className.lastIndexOf(blockClass) == -1);
			var children = [];
			children = this.collectNodes(children, block, 'input');
			children = this.collectNodes(children, block, 'select');
			this.switches[switches[i]] = {children:children, id:switches[i], node:node};
			// attach event
			switchObj = this;
			node.onchange = function(){switchObj.flick(this);}
		}
		for (var j in this.switches) {
			if (this.switches[j].node.checked == true) {
				this.flick(this.switches[j].node);
			}
		}
	},
	collectNodes: function(collection, boss, nodeName) {
		newCollection = boss.getElementsByTagName(nodeName);
		for (var i = 0; i < newCollection.length; i++) {
			collection.push(newCollection[i]);
		}
		return collection;
	},
	flick: function(node) {
		for (var i in this.switches) {
			children = this.switches[node.id].children;
			// disable em
			if (this.switches[i].id != node.id) {
				for (var j = 0; j < children.length; j++) {
					if (children[j].id != this.switches[i]) {
						children[j].disabled = 'disabled';
						addClass(children[j], 'disabled');						
					}					
				}
			}
			// enable em
			else {
				for (var j = 0; j < children.length; j++) {
					children[j].enabled = true;
					removeClass(children[j], 'disabled');
				}
			}
		}
	}
}

/*	----------------------------------------------------------------------
	UTILITY FUNCTIONS
	Functions for some more common stuff. Mainly formatting, but also
	some short cuts to DOM methods.
	---------------------------------------------------------------------- */
// CURRENCY FORMATTER
function currencyFormatter(amount) {
	var i = parseFloat(amount);
	if(isNaN(i)) { i = 0.00; }
	var minus = '';
	if(i < 0) { minus = '-'; }
	i = Math.abs(i);
	i = parseInt((i + .005) * 100);
	i = i / 100;
	s = new String(i);
	if(s.indexOf('.') < 0) { s += '.00'; }
	if(s.indexOf('.') == (s.length - 2)) { s += '0'; }
	s = minus + s;
	return '$' + s;
}
// CREATE DOM NODE SHORTCUT
// ADD/REMOVE CLASSNAME
function addClass(element, className) {
	var classes = element.className.split('');
	// Check to see if it exists, If it does skip out
	for (var i = 0; i < classes.length; i++) {
		if (classes[i] == className) {
			return;
		}
	}
	// Otherwise add it
	element.className = element.className + ' ' + className;
}
function removeClass(element, className) {
	var newClass = ''
	var classes = element.className.split(' ');
	for (var i = 0; i < classes.length; i++) {
		if (classes[i] != className) {
			newClass += classes[i] + ' ';
		}
	}
	// set the class
	element.className = rightTrim(newClass);
}
// TRIM STRING
function leftTrim(sString) {
	while (sString.substring(0,1) == ' ') {
		sString = sString.substring(1, sString.length);
	}
	return sString;
}
function rightTrim(sString) {
	while (sString.substring(sString.length-1, sString.length) == ' ') {
		sString = sString.substring(0,sString.length-1);
	}
	return sString;
}

function copyFields(source, target)
{


}

/* 	
	----------------------------------------------------------------------
	STORED VALUE
	This just inserts the stored address into some fields
	---------------------------------------------------------------------- 
*/
var isAddressInserted = false;
function insertAddress() {
	if (isAddressInserted == true) {
		isAddressInserted = false;
	}
	else {
		var parent = this;
		do {
			parent = parent.parentNode;
		} while (parent.nodeName != 'FIELDSET');
		var inputs = parent.getElementsByTagName('input');
		for (var i = 0; i < inputs.length; ++i) {
			
			if (inputs[i].type == 'hidden') {
				if (inputs[i].name.lastIndexOf('tored_') == 1) {
					var updateName = inputs[i].name.slice(inputs[i].name.indexOf('_') + 1, inputs[i].name.length);
					// Update the inputs
					for (var j = 0; j < inputs.length; ++j) {
						if (inputs[j].name == updateName) {
							inputs[j].value = inputs[i].value;
						}
					}
					// update the select
					if (updateName == 'Customer_Billing_State') {
						var selects = parent.getElementsByTagName('select');
						for (var h = 0; h < selects.length; ++h) {
							if (updateName == selects[h].name) {
								for (var k = 0; k < selects[h].childNodes.length; ++k) {
									if (selects[h].childNodes[k].value == inputs[i].value) {
										selects[h].childNodes[k].selected = 'selected';
									}
								} 
							}
						}
					}
				}
			}
		}
		isAddressInserted = true;
	}
}

/*
    Copies fields in the fieldset identified by the parameter 'source=' that 
    should be contained in the class name of the node with an Id of 'copyDetails' 
    to the fields that are in the same fieldset as the 'copyDetails' node. 
*/
function copyFields()
{
    //  Find the copy node.
    var copyDetailsNode = document.getElementById('copyDetails');
    
    if (copyDetailsNode.checked)
    {
        // Find the parent fieldset of the copy node.
    	var parent = copyDetailsNode;
    	do {
    		parent = parent.parentNode;
    	} while (parent.nodeName != 'FIELDSET');
    	var targetFieldNode = parent;
    	
    	// Find all the selects and inputs in the parent field set. These are the 
    	// fields that are to be copied to.
    	var inputs = targetFieldNode.getElementsByTagName('input');
    	var targetInputs = new Array();	
    	targetIndex = 0;
    	for (var i = 0; i < inputs.length; ++i) 
    	{
    	  if (inputs[i].id != 'copyDetails' )
    	  {
    	      targetInputs[targetIndex] = inputs[i];
    	      targetIndex = targetIndex + 1;
    	  }
    	}		
    	var selects = targetFieldNode.getElementsByTagName('select');		
    	for (var i = 0; i < selects.length; ++i) 
    	{
    	      targetInputs[targetIndex] = selects[i];
    	      targetIndex = targetIndex + 1;	  
    	}			
    		
    	// Find the name of the fields from which the data should be copied from.	
    	var sourceClassRef = 'source=';		
        copySourceIndex = copyDetailsNode.className.indexOf(sourceClassRef);
        var sourceFieldID;
    	if (copySourceIndex != -1) 
    	{
    	     sourceFieldID = copyDetailsNode.className.substring(copySourceIndex + sourceClassRef.length, copyDetailsNode.className.length)
    	}	
    	
    	// Find all the selects and inputsto be copied from.
    	var sourceNode = document.getElementById(sourceFieldID);	
    	inputs = sourceNode.getElementsByTagName('input');
    	var sourceInputs = new Array();	
    	sourceIndex = 0;
    	for (var i = 0; i < inputs.length; ++i) 
    	{
    	      sourceInputs[sourceIndex] = inputs[i];
    	      sourceIndex = sourceIndex + 1;
    	}	
        selects = sourceNode.getElementsByTagName('select');		
    	for (var i = 0; i < selects.length; ++i) 
    	{
    	      sourceInputs[sourceIndex] = selects[i];
    	      source = sourceIndex + 1;	  
    	}	
    
        // Copy all the selects and inputs in the source to the target fields.
    	for (var i = 0; i < sourceInputs.length; ++i) 
    	{					     
    	     if (sourceInputs[i].tagName == 'SELECT')
    	     {
    	         targetInputs[i].selectedIndex = sourceInputs[i].selectedIndex;
    	     }
    	     else if (sourceInputs[i].tagName == 'INPUT')
    	     {
    	          targetInputs[i].value = sourceInputs[i].value;
    	     }
    	}
    }
}
