	// mask the shipping fields with an overlay div if we're shipping to the billing address
	function showHideFieldShipping(divObj,chkObj){
		var bToggleShipAddress = false;

		if (chkObj.type == "checkbox"){
			var oShippingStyle = jQuery("#shippingStyle");
			if (chkObj.checked) {
				oShippingStyle.val("SHIPTO_BILLING");
				bToggleShipAddress = true;
			} else {
				oShippingStyle.val("SHIPTO_SINGLE");
				bToggleShipAddress = false;
			}
		} else {
			bToggleShipAddress = (chkObj.value=="SHIPTO_SINGLE")? false:true; 

			var bToggleShipOptions = (chkObj.value=="SHIPTO_MULTIPLE")? false:true;
			jQuery("#shipOptionsContainer").toggle( bToggleShipOptions );		
		}

		jQuery("#shippingAddressMask").toggleClass("disableOverlayDiv", bToggleShipAddress );
		jQuery("#shipping").find("select").attr("disabled", bToggleShipAddress);
		jQuery("#shippingForm").find(":input").attr("disabled", bToggleShipAddress);

		// enable/disable form address based on the address book selector
		if (jQuery("#condensedAddressBookSelector").length) {
			var iSelectedIndex = jQuery("#condensedAddressBookSelector").attr("selectedIndex");
			var oShippingFormMask = jQuery("#shippingFormMask");
			if (bToggleShipAddress && iSelectedIndex > 0){
				oShippingFormMask.toggleClass("disableOverlayDiv", false);
			} else if (!bToggleShipAddress && iSelectedIndex > 0){
				oShippingFormMask.toggleClass("disableOverlayDiv", true);
				jQuery("#shippingForm").find(":input").attr("disabled", true);
			}
		}
	}
	
	// diable all shipping fields
	function toggleShipForm(oObj){
		jQuery("#shippingForm").find(":input").attr("disabled", oObj.selectedIndex > 0);
		jQuery("#shippingFormMask").toggleClass("disableOverlayDiv", oObj.selectedIndex > 0);
	}

	// refresh the shipping options tile via Ajax
	function refreshShippingOptions(SHIP_TO_OBJ){
		jQuery.post("/checkout/singlepagecheckout.do?method=createShipTo&r="+ Math.random(), SHIP_TO_OBJ,
		  function(data){
			// get the text content of the current and new ship options
			var sCurrentShipHTML = (jQuery("#shipOpts").text() + "").replace(/\s/g, "");
			var sNewShipHTML = (jQuery(data).text() + "").replace(/\s/g, "");
			
			// only update the display if there is a diff between the current and new ship options 
			if ((sCurrentShipHTML != sNewShipHTML)) {
				jQuery("#shipOpts").html(data);
				shipMethodChange();
			}
		});
	}

	// global shipping objects
	var	SHIP_TO_BILLING = {shipToState: null, shipToCountry: null, shipToZip: null, shipToPoBox: null};
	var SHIP_TO_SHIPPING = {shipToState: null, shipToCountry: null, shipToZip: null, shipToPoBox: null};
	var SHIP_TO_ADDRESSBOOK = {shipToPk: null};
	var SHIP_MAP = {state:"shipToState", country:"shipToCountry", postalCode:"shipToZip", postOfficeBox:"shipToPoBox", selectedContact:"shipToPk"};
	var FIELDS_TO_WATCH = new Array();

	// sets a given field's value in the matching property for the given shipping object 
	function updateShipObj(shipObj, oField){
		// split out the important part of the field name
		var sFieldName = oField.name.split(".");
		sFieldName = sFieldName[sFieldName.length -1];

		// grab the field value
		var sFieldValue = oField.value;

		// if it's a checkbox store the checked status rather than the value
		if (oField.type == "checkbox"){
			sFieldValue = (oField.checked);
		}

		// set oject property, based on its mapped name
		shipObj[SHIP_MAP[sFieldName]] = sFieldValue;

		// if we've got a valid shipObj, try posting the data
		if (validateShipObj(shipObj)) postShipObj();
	}

	// checks to see if all of the properties for the given object have been set
	function validateShipObj(shipObj){
		// validate oject properties
		var bValidShipObj = true;

		// make sure we've gathered all data
		gatherAllData();

		// if any property is null in the object, set validation to false
		for(key in shipObj) {
			if (key == "shipToZip"){
				if (checkShipToState(shipObj) && shipObj[key] == null) bValidShipObj = false;
			} else {
				if (shipObj[key] == null) bValidShipObj = false;
			}
		}

		return bValidShipObj;
	}

	// checks the state to see if it needs a supporting zip, returns true if it does or false if it doesn't 
	function checkShipToState(shipObj){
		var aNoZipRequired = new Array("US/AA", "US/AP", "US/AE", "NA/NA");
		var bReturnVal = true;
		
		if (shipObj["shipToState"] && jQuery.inArray(shipObj["shipToState"], aNoZipRequired) != -1 ) {
			bReturnVal = false;
		}

		return bReturnVal;
	}

	// determin which data to post: billing, shipping, or addressbook, and post only if we've got data in each required field
	function postShipObj(){
		// check what to post
		var bShippingStyleCheckbox = jQuery("#shippingStyleCheckbox").attr("checked");
		var shipType = (bShippingStyleCheckbox)? "billing":"shipping";
		var oABSelector = jQuery("#condensedAddressBookSelector").attr("selectedIndex");
		shipType = (oABSelector != "undefined" && oABSelector > 0 && !bShippingStyleCheckbox)? "addressbook":shipType;  

		// post only the determined ship object if it's valid
		switch(shipType){
			case "billing":
				if (validateShipObj(SHIP_TO_BILLING)) refreshShippingOptions(SHIP_TO_BILLING);
				break;
			case "shipping":
				if (validateShipObj(SHIP_TO_SHIPPING)) refreshShippingOptions(SHIP_TO_SHIPPING);
				break;
			case "addressbook":
				if (validateShipObj(SHIP_TO_ADDRESSBOOK)) refreshShippingOptions(SHIP_TO_ADDRESSBOOK);
				break;
		}		
	}

	// run through the required form fields, checking their data, and updating their objects 
	function gatherAllData(){
		// for each field set the corresponding objects properties, based on the field value 
		for (var i=0; i<FIELDS_TO_WATCH.length; i++){
			FIELDS_TO_WATCH[i].each( function(){
				var tempObj = jQuery(this);
				var sName = tempObj.attr("name");
				var sFieldValue =  tempObj.attr("value");

				var sFieldName = sName.split(".");
				sFieldName = sFieldName[sFieldName.length -1];

				sFieldValue = (sFieldValue.length > 0)? sFieldValue:null;
				sFieldValue = (sFieldValue == "on")? tempObj.attr("checked"):sFieldValue;

				if (this.name.indexOf("billContact.") != -1){
					SHIP_TO_BILLING[SHIP_MAP[sFieldName]] = sFieldValue;
				} else if (this.name == "selectedContact"){
					SHIP_TO_ADDRESSBOOK[SHIP_MAP[sFieldName]] = sFieldValue;
				} else {
					SHIP_TO_SHIPPING[SHIP_MAP[sFieldName]] = sFieldValue;
				}

			});
		}	
	}

	// perform the following tasks on load
	jQuery(document).ready(function($){
		// set the global list of fields to watch
		FIELDS_TO_WATCH = new Array( jQuery(":checkbox[name$='postOfficeBox']"),
			jQuery("select[name$='state']"),
			jQuery("select[name$='country']"),
			jQuery("input[name$='postalCode']"),
			jQuery("#condensedAddressBookSelector"));
	
		var oShippingStyleCheckbox = jQuery('#shippingStyleCheckbox');
		var bShippingStyleCheckboxChecked = oShippingStyleCheckbox.attr("checked");

		// bind click even to the ship style checkbox
		jQuery("#shippingStyleCheckbox").click(function (){
			postShipObj();
		});

		// bind change events to all of the fields we want to watch
		for (var i=0; i<FIELDS_TO_WATCH.length; i++){
			FIELDS_TO_WATCH[i].each(function(){
				jQuery(this).change(function (){
					if (this.name.indexOf("billContact.") != -1){
						updateShipObj(SHIP_TO_BILLING, this);
					} else if (this.name == "selectedContact"){
						updateShipObj(SHIP_TO_ADDRESSBOOK, this);
					} else {
						updateShipObj(SHIP_TO_SHIPPING, this);	
					}							
				});
			});
		}

		// disable shipping fields if the *ship to billing* checkbox is checked 
		jQuery("#shipping").find("select").attr("disabled", bShippingStyleCheckboxChecked);
		jQuery("#shippingForm").find(":input").attr("disabled", bShippingStyleCheckboxChecked);

		// if there's an address book selector, run toggle to see if the form needs to be disabled 
		var oAddressBookSelector = jQuery("#condensedAddressBookSelector");
		if (oAddressBookSelector.length > 0 && !bShippingStyleCheckboxChecked){
			toggleShipForm(oAddressBookSelector[0]);
		}

		// clear gc and sc messages on field focus
		jQuery("#giftCertificateField").focus(function () {
			jQuery("#gcErrorMsg").hide(); 
			jQuery("#gcSuccessMsg").hide();			
		});
		jQuery("#sourceCodeField").focus(function () {
			jQuery("#scErrorMsg").hide(); 
			jQuery("#scSuccessMsg").hide();			
		});

	});

	// refresh the order review tile via Ajax
	function shipMethodChange(){
		// get the checked radio button's value 
		var shipMethodValue = jQuery(":radio[name$='shippingMethod']").filter(":checked").val();

		// post and refresh tile on success
		jQuery.post("/checkout/singlepagecheckout.do?method=refreshOrderReview&r="+ Math.random(), {shipMethodPk: shipMethodValue},
		  function(data){
			jQuery("#orderReview").html(data);
		});
	}

	// refresh the order review tile via Ajax for Source Code changes
	function sourceCodeChange(){
		// get the checked radio button's value
		var oSourceCodeField = jQuery("#sourceCodeField");
		var sourceCodeValue = oSourceCodeField.val();
		sourceCodeValue = jQuery.trim(sourceCodeValue);
		oSourceCodeField.val(sourceCodeValue);
		
		if (sourceCodeValue.length > 0) {
			// post and refresh tile on success
			jQuery.ajax({
				type: "POST",
				url: "/checkout/singlepagecheckout.do?method=refreshForSourceCode&r="+ Math.random(),
				data: {catalogCode: sourceCodeValue},
				success: function(data){
					jQuery("#orderReview").html(data);
					jQuery("#scErrorMsg").hide(); 
					jQuery("#scSuccessMsg").show();

					// call for reporting successful source code
					// additions, to Omniture.
		            sourceCodeReporting(sourceCodeValue);

					oSourceCodeField.val("");
				},
				error: function(){
					jQuery("#scSuccessMsg").hide();
					jQuery("#scErrorMsg").show(); 
				}
			});

		}
	}

	// refresh the order review tile via Ajax for Gift Certificate changes
	function giftCertificateChange(){
		// get the checked radio button's value
		var oGiftCertificateField = jQuery("#giftCertificateField");
		var giftCertificateValue = oGiftCertificateField.val();
		giftCertificateValue = jQuery.trim(giftCertificateValue);
		oGiftCertificateField.val(giftCertificateValue);
		
		if (giftCertificateValue.length > 0) {
			// post and refresh tile on success			
			jQuery.ajax({
				type: "POST",
				url: "/checkout/singlepagecheckout.do?method=refreshForGiftCertificate&r="+ Math.random(),
				data: {gcNumber: giftCertificateValue},
				success: function(data){
					jQuery("#orderReview").html(data);
					jQuery("#gcErrorMsg").hide(); 
					jQuery("#gcSuccessMsg").show();
					oGiftCertificateField.val("");
				},
				error: function(){
					jQuery("#gcSuccessMsg").hide();
					jQuery("#gcErrorMsg").show(); 
				}
			});

		}
	}

	// remove a Gift Certificate and refresh the review tile via Ajax
	function removeGiftCert(giftCertNumber){
		// post and refresh tile on success
		jQuery.post("/checkout/singlepagecheckout.do?method=removeGiftCertificate&r="+ Math.random(), {gcNumber: giftCertNumber},
		  function(data){
			jQuery("#gcSuccessMsg").hide();
			jQuery("#gcErrorMsg").hide();
			jQuery("#orderReview").html(data);
		});
	}

	/**
	 * Passes along source code addition data to reporting function(s) if they exist.
	 * @param {String} sourceCodeValue This is the source code that was added to the user session.
	 */
	function sourceCodeReporting(sourceCodeValue){
		// only make the call to report the source code addition
		// if reportSourceCodeAdditionToOmniture exists
		if (typeof(reportSourceCodeAdditionToOmniture) != "undefined") reportSourceCodeAdditionToOmniture(sourceCodeValue);
	}

