/* changelog
Jan 31, 2008
- added label functionality originally from CBS
- changed validate- to validation-
- fixed Nan to NaN
Aug 6, 2009
- fixed bug in date validation
Aug 13, 2009
- made compatible with jQuery
*/

CM = {};
CM.validator = function() {
	function $getElem(s) {return document.getElementById?document.getElementById(s):false;}

	/*
	* @Date: Feb 21, 2009
	* @Description: This function strips defined chars from a string
	* useful in validation routines
	* example: stripChars(str, "()-") returns the string with any
	* brackets and dashes removed
	**/
	function stripChars(str, chars) {
		var i;
		var newstring = "";
		for (i = 0; i < str.length; i++) {
			mychar = str.charAt(i);
			if (chars.indexOf(mychar) == -1)
				newstring += mychar;
		}
		return newstring;
	}

	/**
	* @Date: Feb 21, 2009
	* @Description: This function extracts defined chars from a string
	* useful in validation routines
	* it is 'sort of' the reverse of stripChars
	* example: extractChars(str, "0123456789") returns all digits from the string
	**/
	function extractChars(str, chars) {
		var i;
		var newstring = "";
		for (i = 0; i < str.length; i++) {
			mychar = str.charAt(i);
			if (chars.indexOf(mychar) != -1)
				newstring += mychar;
		}
		return newstring;
	}

	/**
	* @Date: Dec 1, 2004
	* @Description: This function tests a credit card using the Luhn validation
	* The sum of digits must be divisible by 10
	* digits in odd positions are doubled, then sum of left+right digit is taken
	**/
	function isValidCreditCard(sCreditCardNumber)
	{
		var nTotal      = 0;
		var nPosition     = 0;
		var bValid      = true; // innocent until proven guilty
		sCreditCardNumber = extractChars(sCreditCardNumber, "0123456789");
		if (sCreditCardNumber.length < 13)
			bValid = false;
		else
		{
			for ( i = sCreditCardNumber.length; i > 0 ;  i--)
			{
				var nDigit      = 0;
				// advance
				nPosition++;
				// get digit
				nDigit= parseInt(sCreditCardNumber.charAt(i-1));
				// odd position: add to total
				if ((nPosition % 2) !== 0)
					 nTotal += parseInt(nDigit);
				else
				// even position: add sum of digits to total
				{
					// calc sum of left and right digit (use parseInt to cut off fractional parts
					var nDoubled = nDigit * 2;
					var nLeftDigit  = parseInt(nDoubled / 10);
					var nRightDigit = nDoubled % 10;
					// get sum of left and right
					nTotal += nLeftDigit + nRightDigit;
				}
			}
			// if divisible by 10 it is valid
			bValid = (nTotal % 10) === 0;
		}
		return (bValid);
	}

	/* check if d is a date value
	 * formatted means it is in fact a string, formatted as mm/dd/yyyy
	*/
	function isDate(d, formatted)
	{
		var ret = false;

		var date = new Date(d);

		// it's at least a number
		if (!isNaN(date))
		{
			if (formatted)
			{
				day = date.getDate();
				month = date.getMonth()+1;
				year = date.getFullYear();
				dateString = '' + month + '/' + day + '/' + year;
				pattern = new RegExp('' + month + '.' + day + '.' + year);
				ret = pattern.test(dateString);
			}
			else
				ret = true;
		}
		return ret;
	}

	/* generic validation routine */
	function validateForm(frm, target)
	{
		var i;
		var ret = true;
		var field;
		var out='';
		var errorFields = [];
		var otherFieldName;
		var otherField;

		if (!target)
			target = "errors";
		if (!$getElem(target))
		{
			alert('validateForm: element ' + target + ' not found on page');
			return false;
		}

		if ($getElem(frm)) frm = $getElem(frm);
		if (!frm)
		{
			alert('validateForm: frm parameter is required');
			return false;
		}

		// loop through all fields
		// check if they have validation class name
		// and do appropriate validation
		// @return true, if all validation passes
		for (i=0; i<frm.elements.length; i++)
		{
			field = frm.elements[i];
			// skip disabled fields and fields without a name
			if (field.disabled || field.name==undefined)
				continue;

			if ($getElem('label-'+field.name))
				field.label = $getElem('label-'+field.name).innerHTML;
			else
				field.label = field.name;

			// validate required fields
			filter = /validation-required/;

			// validate required fields
			filter = /validation-required/;
			if (filter.test(field.className))
			{
				if (field.type=='checkbox')
				{
					if (!field.checked)
					{
						out += 'Please check the ' + field.label + ' box<br />';
						errorFields.push(field);
						ret = false;
					}
				}
				else
					if (field.value==='')
					{
						out += 'Please enter a value for ' + field.label + '<br />';
						errorFields.push(field);
						ret = false;
					}
			}
			// validate numeric fields
			filter = /validation-number/;
			if (filter.test(field.className))
			{
				if (field.value!=='' && isNaN(field.value))
				{
					out += 'Please enter a number for ' + field.label + '<br />';
					errorFields.push(field);
					ret = false;
				}
			}
			// validate digit only fields
			filter = /validation-digits/;
			if (filter.test(field.className))
			{
				var filter  = /^[0-9]+$/;
				if (field.value!=='' && !filter.test(field.value))
				{
					out += 'Please enter a number for ' + field.label + '<br />';
					errorFields.push(field);
					ret = false;
				}
			}
			// validate date fields
			filter = /validation-date/;
			if (filter.test(field.className))
			{
				if (field.value!=='' && !isDate(field.value, true))
				{
					out += 'Please enter a valid date for ' + field.label + '<br />';
					errorFields.push(field);
					ret = false;
				}
			}
			// validate this date later than other field's date
			filter = /validation-date-after/;
			if (filter.test(field.className))
			{
				otherFieldName = field.className.replace(/.*validation-date-after-/,'').replace(/ .*/,'');
				if (field.value!=='' && $getElem(otherFieldName))
				{
					otherField = $getElem(otherFieldName);
					if ($getElem('label-'+field.name))
						otherField.label = $getElem('label-'+otherFieldName).innerHTML;
					else
						otherField.label = field.name;

					if (isDate(field.value) && isDate(otherField.value))
					{
						var date1	= new Date(field.value);
						var date2 = new Date(otherField.value);
					}
					if (+date1 <= +date2) // + converts to number
					{
						out += 'Please enter a value for ' + field.label + ' after ' + otherField.label  + '<br />';
						errorFields.push(field);
						ret = false;
					}
				}
			}
			// validate email addresses
			filter = /format-email/;
			if (filter.test(field.className))
			{
				filter  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
				if (field.value!=='' && !filter.test(field.value))
				{
					out += 'Please enter a valid email address in ' + field.label + '<br />';
					errorFields.push(field);
					ret = false;
				}
			}
			// validate credit cards
			// this uses Luhn validation
			// which is a numeric algorithm
			filter = /format-creditcard/;
			if (filter.test(field.className))
			{
				filter  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
				if (field.value!=='' && !isValidCreditCard(field.value))
				{
					out += 'Please enter a valid credit card number'  + '<br />';
					errorFields.push(field);
					ret = false;
				}
			}
			// validate urls
			filter = /format-url/;
			if (filter.test(field.className))
			{
				filter  = /^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$/i;
				if (field.value!=='' && !filter.test(field.value))
				{
					out += 'Please enter a valid url in ' + field.label + '<br />';
					errorFields.push(field);
					ret = false;
				}
			}
			// validate verification fields
			filter = /validation-match/;
			if (filter.test(field.className))
			{
				otherFieldName = field.className.replace(/.*validation-match-/,'').replace(/ .*/,'');
				otherField = $getElem(otherFieldName);
				if ($getElem('label-'+field.name))
					otherField.label = $getElem('label-'+otherFieldName).innerHTML;
				else
					otherField.label = field.name;
				if (field.value!=='' && field.value!=otherField.value)
				{
					out += 'The value in ' + field.label + ' does not match ' + otherField  + '<br />';
					errorFields.push(field);
					ret = false;
				}
			}
			field.className = field.className.replace(" error","");
		}
		$getElem(target).innerHTML = out;
		$getElem(target).className = $getElem(target).className.replace('hide','');
		// indicate errors
		for (i = 0; i < errorFields.length; i++) 
			errorFields[i].className += " error";
		window.scroll(0,0);
		return ret;
	}

	window.validateForm = validateForm;
	window.isDate = isDate;
}();


