/* documented in formValidation.txt */

	// these can be overridden in code:
	var customErrorSuffix = '';				// text shown at the end of any error message
	var customErrorDiv = 'customErrMsg';	// which DIV to pop open for errors (override when you have >1 form per page)
	var formValidatorSetFocus = true;		// if true, we focus the error object
	
	// 	-	-	- error messages

	// do not look up directly into this object; use the access function below
	var formErrorsEN = {

			"requiredFields": "Please fill in the highlighted fields; they are required information.",

			"emailFormat"	: "It looks like your email address is not formatted correctly. " +
								"Please double-check it and resubmit the form.",
			"emailEmpty"	: "Please enter your email address.",
			"emailTwice"	: "Please make sure you have entered your email address in both fields.",

			"pswMismatched" : "Your passwords don't match. " +
								"Please make sure you have entered exactly the same password in both fields.",

			"licenseAgr"	: "Please read the terms and conditions below, and check the box to indicate " +
								"that you agree to them.",
			"nameChars"		: "Your name has unusual characters in it. Please use only letters.",

			"phoneErr"		: "Please ensure that your phone number is formatted correctly " +
								"(without a separate extension or other comments).",

			"faxErr"		: "Please ensure that your fax number is formatted correctly.",

			"ccNum"			: "Please check your credit card number.",
			
			"unspecified"	: "Unknown error code: "
		};
	
	var formErrors = formErrorsEN;	// can reassign this master variable to other languages (formErrors = formErrorsFR)
	
	function formErrorMessage( msgCode) {
		var msg = formErrors[msgCode];
		if ( !msg) msg = formErrors["unspecified"] + msgCode;	// unspecified error message
		return msg;
	};
	
	/* support routines. do not delete. */
	function xGetElementById(e) { return $(e); };
	function xCollapse(e) {
		e = $(e);
		e.style.display = 'none';
		return e;
	};
	function xExpand(e) {
		e = $(e);
		e.style.display = 'block';
		return e;
	};

	// returns NULL if none are selected
	function getRadioValue(f,fldName)
	{
		var i, ct = f[fldName].length, rad;
		
		if ( ct == undefined) {
			// only a single radio in the group, or none
			if ( f[fldName] )
				return f[fldName].value;
			else
				return null;
		}
			
		for (i = 0; i < ct; ++i) {
			rad = f[fldName][i];
			if (rad.checked)
				return rad.value;
		}
		return null;
	};

	// set focusObj or lblObj to ZERO (0) to suppress the behavior on that element.
	//	FOCUSOBJ: gets the focus, and its class is set to "err"
	//	LBLOBJ: class is set to "lblErr"
	// also, the custom error message div is shown, and the required field div is hidden
	function showCustomError(msg,focusObj,lblObj)
	{
		// show the main error message
		var obj = $(customErrorDiv);
		obj.innerHTML = msg + customErrorSuffix;
		if ( !obj.errorShowing) {
			xExpand(obj);
			obj.errorShowing = true;
		}
		// hilite the error field
		if ( focusObj != 0) {
			if ( formValidatorSetFocus) focusObj.focus();
			focusObj.className = 'err';
		}
		// and the label
		if ( lblObj) lblObj.className = 'lblErr';
	};
	
	function clearCustomError() {
		var obj = $(customErrorDiv);
		obj.innerHTML = '';
		if ( obj.errorShowing) {
			xCollapse(obj);
			obj.errorShowing = false;
		}
	};

// at this point, the 2 pw's must not be empty. pass in
// the field names (strings). the associated labels are 
// assumed to be the same, with "_lbl" at the end
function validatePasswords(f,passField1,passField2)
{
	var p1 = f[passField1].value,
		p2 = f[passField2].value;
	
	if ( p1 != p2 ) {
		showCustomError(formErrorMessage("pswMismatched"),f[passField1],$(passField1+'_lbl'));
		return false;
	}
	return true;
};

	// ensure the number doesn't have invalid chars
	function checkOnePhoneNumber(num)
	{
		var i = 0, ct = num.length, c;
		for ( ; i < ct; ++i) {
			c = num.charAt(i);
			if (!( c == '(' || c == ')' || c == ' ' || c == '-' || c == '+' || (c >= '0' && c <= '9')))
				return false;
		}
		return true;
	};
	
// pass in 2 phones, as field names (strings).
// the associated labels are assumed to be the same, with "_lbl" at the end
function validatePhones(f,phoneField,faxField)
{
	var ph = f[phoneField].value,
		fax = faxField ? f[faxField].value : 0,
		errObj = 0;
	
	if ( !checkOnePhoneNumber(ph))
		errObj = 1;
	else if ( faxField && !checkOnePhoneNumber(fax))
		errObj = 2;
	
	if ( errObj != 0)
	{
		var focusObj = (errObj == 1 ? f[phoneField] : f[faxField]),
			lblObj = $( (errObj == 1 ? phoneField : faxField) + '_lbl');
		showCustomError(formErrorMessage( errObj == 1 ? "phoneErr" : "faxErr"),focusObj,lblObj)
		return false;
	}
	return true;
};


// ensure that the field looks like a person's name (according to iSubscribe)
function validateName(f,nameField)
{
	var nm = f[nameField].value,
		re = /^[a-zA-Z ''-]+$/;		// letters, spc, apostr, hyph
	
	if ( !re.test(nm) ) {
		showCustomError(formErrorMessage("nameChars"),f[nameField],$(nameField+'_lbl'));
		return false;
	}
	return true;
};


// see [http://www.breakingpar.com/] in the tips/regExp section
function checkOneEmail(emailAddress)
{
    var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
    return re.test(emailAddress);
};

// pass in up to 2 email addresses, as field names (strings).
// the associated labels are assumed to be the same, with "_lbl" at the end
function validateEmails(f,emailField1,emailField2)
{
	var msg,errObj = 0;
	
	var e1 = f[emailField1].value,
		e2 = emailField2 ? f[emailField2].value : -1;
	if (e1=='') {
		msg = formErrorMessage("emailEmpty");
		errObj = 1;
	}
	else if ( !checkOneEmail(e1)) {
		msg = formErrorMessage("emailFormat");
		errObj = 1;
	}
	else if ( e2 != -1 && e1 != e2) {
		msg = formErrorMessage("emailTwice");
		errObj = 2;
	}
	
	if ( errObj != 0) {
		var focusObj = f[errObj == 1 ? emailField1 : emailField2],
			lblObj = $( (errObj == 1 ? emailField1 : emailField2) + '_lbl');
		showCustomError(msg,focusObj,lblObj)
		return false;
	}
	return true;
};

function checkLicenseAgreement(f) {
	if ( !f.agree.checked) {
		showCustomError(formErrorMessage("licenseAgr"),0,$('agree_lbl'));
		return false;
	}
	return true;
};

	function checkVisa(ccnum) {
		return /^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/.test(ccnum)
	};
	function checkMC(ccnum) {
		return /^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/.test(ccnum)
	};
	function checkAMEX(ccnum) {
		return /^3[4,7]\d{13}$/.test(ccnum)
	};
		
// see [http://www.breakingpar.com/] in the tips/regExp section
function checkCreditCard(type, ccnum) {
	var re = 0, checksum = 0, i;
	if (type == "visa")
		{ if ( !checkVisa(ccnum)) return false; }
	else if (type == "mastercard")
		{ if ( !checkMC(ccnum)) return false; }
	else if (type == "discover")
		{ re = /^6011-?\d{4}-?\d{4}-?\d{4}$/; }
	else if (type == "amex")
		{ if ( !checkAMEX(ccnum)) return false; }
	else if (type == "diners")
		{ re = /^3[0,6,8]\d{12}$/; }
	else if (type == "all") {		// allows MC/Visa/Amex
		if ( !( checkVisa(ccnum) || checkMC(ccnum) || checkAMEX(ccnum) )) return false;
	}
	else
		return false;
	if (re != 0 && !re.test(ccnum)) return false;
	// eliminate dashes before continuing
	ccnum = ccnum.replace(/-/g,'');
	// Checksum ("Mod 10"): Add even digits in even length strings or odd digits in odd length strings.
	for (i=(2-(ccnum.length % 2)); i<=ccnum.length; i+=2)
		checksum += parseInt(ccnum.charAt(i-1));
	// Analyze odd digits in even length strings or even digits in odd length strings.
	for (i=(ccnum.length % 2) + 1; i<ccnum.length; i+=2) {
		var digit = parseInt(ccnum.charAt(i-1)) * 2;
		if (digit < 10) { checksum += digit; } else { checksum += (digit-9); }
	}
	return ((checksum % 10) == 0);
};

// this version takes the field name of the cc type
function validateCreditCard(f,typeField,valueField) {
	return validateCreditCard2(f,f[typeField].value,valueField);
};

// this version takes the type directly (pass "all" for ccType to allow all CC types - mc,v,amex)
function validateCreditCard2(f,ccType,valueField)
{
	var reNoSpaces = / /g,	// rmv spcs
		fld = f[valueField],
		val = fld.value.replace(reNoSpaces,'');

	if ( checkCreditCard(ccType,val))
		return true;
	else {
		showCustomError(formErrorMessage("ccNum"),fld,$(valueField+'_lbl'));
		return false;
	}
};

// pass in an array of which fields are required: e.g., Array("x_name", "x_passwd")
function validateRequiredFields(f,fieldsRequired){
	var errCt = 0, fld, isErr, obj, lblObj, i, ct, j, selIx, selVal;
	
	// first stop: check required fields
	for (i = 0, ct = fieldsRequired.length; i < ct; ++i) {
		isErr = false;
		obj = f.elements[fld=fieldsRequired[i]];
		lblObj = $(fld+'_lbl');
		
		if (obj){
			if (obj.type == null){
				var blnchecked = false;
				for (j = 0; j < obj.length; ++j){
					if (obj[j].checked)
						blnchecked = true;
				}
				if (!blnchecked) isErr = true;
				continue;
			}

			switch(obj.type){
			case "select-one":
				selIx = obj.selectedIndex;
				if ( selIx >= 0) {
					selVal = obj.options[selIx].value;
					if ( selVal.length == 0)
						isErr = true
				}
				else
					isErr = true;
				break;
			case "select-multiple":
				if (obj.selectedIndex == -1)
					isErr = true;
				break;
			case "text":
			case "textarea":
			case "password":
				if (obj.value == "" || obj.value == null)
					isErr = true;
				break;
			default:
			}
		}
		
		if ( isErr) {
			if (obj) obj.className = 'err';
			if ( lblObj) lblObj.className = 'lblErr';
			++errCt;
		}
		else {
			if (obj) obj.className = 'noErr';
			if ( lblObj) lblObj.className = 'lbl';
		}
	}

	if ( errCt == 0)
		return true;
	else{
		showCustomError(formErrorMessage("requiredFields"),0,lblObj);
		return false;
	}
};

