/***********************************************************************************
 * File: validate.js
 * Description:
 *   Validation functions for forms
 *   Example of usage:
 *
 *   function validate(form) {
 *     // Field name, Caption
 *     val_formString(form.firstName, "First Name");
 *     // Field name, Caption, Required (true | false)
 *     val_formEmail(form.email, "Email", true);
 *     // Field name, Caption, Required (true | false)
 *     val_formDate(form.address3, "Date Of Birth", true);
 *
 *     // Displays error and returns false if an error occurred
 *     return val_checkError();
 *   }
 *
 *   ...
 *
 *   <form .... onsubmit="return validate(this);">
 *
 ***********************************************************************************/

  	// Mark page as included
  	var page_included_inc_client_validate_js = true;

 /**********************************************************
  * Message variables
  **********************************************************/
 	var val_msgCheckError 			= "Some fields were not entered correctly:\n\n";
 	var val_msgFormItem 			= "Script Error: Undefined field '$1'";
	var val_msgFormCommonRequired 	= "The field '$1' is a required field";
 	var val_msgFormStringMin 		= "The field '$1' requires at least $2 chars";
 	var val_msgFormStringMax 		= "The field '$1' may not be longer than $2 char(s)";
	var val_msgFormNumberNumber 	= "The field '$1' requires a valid number";
	var val_msgFormNumberDecimal 	= "The field '$1' may not contain decimal places";
	var val_msgFormPhoneFormat 		= "The field '$1' requires a valid phone number";
	var val_msgFormEmailFormat 		= "The field '$1' requires a valid email address";
	var val_msgFormDateFormat 		= "The field '$1' requires a valid date";
	var val_msgFormListMin 			= "The field '$1' requires at least $2 items selected";
	var val_msgFormListMax 			= "The field '$1' may not have more than $2 item(s) selected";
	
 /**********************************************************
  * String functions
  **********************************************************/
	// Trim string (left: "l|left", right: "r|right", both: "b|both" [default])
  	function val_strTrim(str, sides) {
		var i = 0, j = str.length - 1;
		
		// Trim left side
		if(!sides || (sides.charAt(0) != "r")) {
			while(i < j && str.charAt(i) == ' ')
				i++;
		}
		// Trim right side
		if(!sides || (sides.charAt(0) != "l")) {
			while(j > i && str.charAt(j) == ' ')
				j--;
		}
		// Return trimmed string
		return str.substring(i, j + 1);
	}

	// Change first letter of each word to upper case
	function val_strProperCase(str) {
		var prevIsSpace = true;
		var chr;
		for(var i = 0; i < str.length; i++) {
			chr = str.charAt(i);
			if(prevIsSpace && chr != ' ')
				str = str.substring(0, i) + chr.toUpperCase() + str.substring(i + 1);
			prevIsSpace = (chr == ' ');
		}
		return str;
	}
	
 /**********************************************************
  * Object functions
  **********************************************************/
	function val_itemValue(item, value) {
		if(item.options)
			return (item.selectedIndex >= 0) ? item.options[item.selectedIndex].value : "";
		else
			return item.value;
	}

 /**********************************************************
  * Boolean validation functions
  **********************************************************/
	// Check string
	function val_checkString(value, min, max) {
		var length = val_strTrim(value).length;
		if(!min) min = 1;
		
		if(length < min)
			return (min == 1) ? val_setError("required") : val_setError("min");
		if(max && length > max)
			return val_setError("max");
		return val_setError();
	}
	
	// Check number
	function val_checkNumber(value, required, decimal) {
		if(value == "")
			return required ? val_setError("required") : val_setError();
		
		if(isNaN(value))
			return val_setError("number");
		if(!decimal && value.indexOf(".") >= 0)
			return val_setError("decimal");
		return val_setError();
	}
	
	// Check phone number
	function val_checkPhone(value, required, extraChars) {
		if(value == "")
			return required ? val_setError("required") : val_setError();

		if(!extraChars)
			extraChars = "-+/ ";
		// Pattern: +353/087-434 43 43
		var pattern = new RegExp("^[" + extraChars.replace("(\-|\+|\/)", "\\\\$0") + "0-9]{5,25}$");
		return !pattern.test(value) ? val_setError("format") : val_setError();
	}

	// Checks email address
	function val_checkEmail(value, required) {
		if(value == "")
			return required ? val_setError("required") : val_setError();

		// Pattern: x@y.z
		//var pattern = new RegExp("^[-_.A-Za-z0-9]+@[-_A-Za-z0-9]+\.[-_.A-Za-z0-9]{2,5}$");
		// changed by mick 29 nov 04.  was not accepting x@y.f.z pattern
		var pattern = new RegExp("^[-_.a-z0-9]+@[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]+\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]{2,5}$", "i");

		return !pattern.test(value) ? val_setError("format") : val_setError();
	}
		
	// Check date (format: dd/mm/yyyy)
	function val_checkDate(value, required) {
		
		if(value == "")
			return required ? val_setError("required") : val_setError();

		var date_array = split(value, "/");
		return (value == "" && !required) || 
				(date_array.length == 3 && date_array[2].length == 4 && 
				checkdate(date_array[1], date_array[0], date_array[2]));
	}

 /**********************************************************
  * Form validation functions
  **********************************************************/
  	// Check if form item exists
	function val_formItem(item, caption) {
		if(!item)
			return val_errAdd(val_msgGet(val_msgFormItem, caption));
		return true;
	}
  
  	// Validate string
	function val_formString(item, caption, min, max) {
		if(!val_formItem(item, caption))
			return false;
			
		if(!val_checkString(val_itemValue(item), min, max)) {
			switch(val_lastError) {
			case "required":
				return val_errAdd(val_msgGet(val_msgFormCommonRequired, caption), item);
			case "min":
				return val_errAdd(val_msgGet(val_msgFormStringMin, caption, min), item);	
			case "max":			
				return val_errAdd(val_msgGet(val_msgFormStringMax, caption, max), item);
			}
		}
		return true;
	}
	
 	// Validate dropdown
	function val_formDropdown(item, caption) {
		if(!val_formItem(item, caption))
			return false;

		if(item.selectedIndex <= 0)
			return val_errAdd(val_msgGet(val_msgFormCommonRequired, caption), item);
		return true;
	}
	
	// Validate list
	function val_formList(item, caption, min, max) {
		if(!val_formItem(item, caption))
			return false;

		// Get selected count
		var selected = 0;
		for(var i = 0; i < item.options.length; i++) {
			if(item.options[i].selected)
				selected++;
		}
		
		if(!min) min = 1;
		
		if(selected < min) {
			if(min == 1)
				return val_errAdd(val_msgGet(val_msgFormCommonRequired, caption), item);
			else
				return val_errAdd(val_msgGet(val_msgFormListMin, caption), item);
		}
		if(max && selected > max)
			return val_errAdd(val_msgGet(val_msgFormListMax, caption), item);
		return true;
	}
		
  	// Validate number 
	function val_formNumber(item, caption, required, decimal) {
		if(!val_formItem(item, caption))
			return false;
			
		if(!val_checkNumber(val_itemValue(item), required, decimal)) {
			switch(val_lastError) {
			case "required":
				return val_errAdd(val_msgGet(val_msgFormCommonRequired, caption), item);
			case "number":
				return val_errAdd(val_msgGet(val_msgFormNumberNumber, caption), item);
			case "decimal":
				return val_errAdd(val_msgGet(val_msgFormNumberDecimal, caption), item);
			}
		}
		return true;
	}

  	// Validate phone number 
	function val_formPhone(item, caption, required, extraChars) {
		if(!val_formItem(item, caption))
			return false;
			
		if(!val_checkPhone(val_itemValue(item), required, extraChars)) {
			switch(val_lastError) {
			case "required":
				return val_errAdd(val_msgGet(val_msgFormCommonRequired, caption), item);
			case "format":
				return val_errAdd(val_msgGet(val_msgFormPhoneFormat, caption), item);
			}
		}
		return true;
	}
	
  	// Validate email 
	function val_formEmail(item, caption, required) {
		if(!val_formItem(item, caption))
			return false;
			
		if(!val_checkEmail(val_itemValue(item), required)) {
			switch(val_lastError) {
			case "required":
				return val_errAdd(val_msgGet(val_msgFormCommonRequired, caption), item);
			case "format":
				return val_errAdd(val_msgGet(val_msgFormEmailFormat, caption), item);
			}
		}
		return true;
	}	
	
  	// Validate date 
	function val_formDate(item, caption, required) {
		if(!val_formItem(item, caption))
			return false;
			
		if(!val_checkDate(val_itemValue(item), required)) {
			switch(val_lastError) {
			case "required":
				return val_errAdd(val_msgGet(val_msgFormCommonRequired, caption), item);
			case "format":
				return val_errAdd(val_msgGet(val_msgFormDateFormat, caption), item);
			}
		}
		return true;
	}
			
 /**********************************************************
  * Message functions
  **********************************************************/
	// Add attributes to message and return the result
	function val_msgGet(message) {
		for(var i = 1; i < arguments.length; i++)
			message = message.replace(new RegExp("\\$" + i), arguments[i]);
		return message;	
	}
	
 /**********************************************************
  * Error handling functions
  **********************************************************/
  	// Error message array
  	var val_errorArray;
	var val_lastError;
	
	// Sets last error for use by calling functions
	function val_setError(errId) {
		if(!errId) {
			val_lastError = null;
			return true;
		}
		// Set error
		val_lastError = errId;
		return false;
	}
	
	// Add error message to array
	function val_errAdd(message, item) {
		if(!val_errorArray)
			val_errorArray = new Array();
			
		var i = val_errorArray.length;
		val_errorArray.length += 2;
		val_errorArray[i] = message;
		val_errorArray[i + 1] = item;
		return false;
	}

	// Clear errors
	function val_errClear() {
		val_errorArray = null;
	}
	
	// Get error count
	function val_errCount() {
		return val_errorArray ? val_errorArray.length : 0;
	}
	
	// Displays errors, if any
	function val_checkError() {
		if(val_errCount() > 0) {
			var i, focusItem, 
			msg = val_msgCheckError;
		
			// Add errors to message
			for(i = 0; i < val_errorArray.length; i += 2) {
				msg += val_errorArray[i] + "\n";
			
				if(!focusItem && val_errorArray[i + 1] != null)
					focusItem = val_errorArray[i + 1];
			}	
			// Clear errors
			val_errClear(val_errorArray);
			// Display message
			alert(msg);
			// Set focus to first item with an error
			if(focusItem && !focusItem.disabled && focusItem.type != "hidden")
				focusItem.focus();
			return false;
		}
		return true;	
	}
	
 /**********************************************************
  * Date functions
  **********************************************************/
	
	// Return difference between two dates (l=milisecs, s=secs, n=mins, h=hours, d=days (default), m=months, y=years)
	function date_dateDiff(dateFrom, dateTo, range) {
		if(!dateFrom || !dateTo)
			return null;
	
		// Convert to date objects if strings
		if(typeof(dateFrom) != "object")
			dateFrom = new Date(dateFrom);
		if(typeof(dateTo) != "object")
			dateTo = new Date(dateTo);

		if(isNaN(dateFrom) || isNaN(dateTo))
			return null;
		
		
		// Get differences
		var diffYears = parseInt(dateTo.getFullYear() - dateFrom.getFullYear());
		var diffMonths = parseInt(dateTo.getMonth() - dateFrom.getMonth());
		var diffDays = parseInt(dateTo.getDate() - dateFrom.getDate());
		var diffMili = parseInt(dateTo.getTime() - dateFrom.getTime());
	
		if(range == null)
			range = "d";
		
		// Return difference
		var div = 1;
		switch(range) {
		case "d":		div = 24;
		case "h":		div *= 60;
		case "n":		div *= 60;
		case "s":		div *= 1000;
		case "l":
			result = parseFloat(diffMili / div);
			break;
		case "m":
			result = (diffYears * 12 + diffMonths);
			break;
		case "y":
			result = parseFloat((diffYears * 12 + diffMonths) / 12);
			break;
		default:
			return null;
		}
		
		result2 = ((diffYears * 12 + diffMonths) * 30 + diffDays) / 30;
		result3 = parseFloat((diffYears * 12 + diffMonths) / 12);
					
		alert(dateFrom.toGMTString() + "\n" + 
		dateTo.toGMTString() + "\nMili: "
		 + diffMili + "\nDays: "
		 + result + "\nMonths: "
		 + result2 + "\nYears: "
 		 + result3 + "\n");
		
		return result;
	}
	
	//date_dateDiff("Dec 1, 2001", "Feb 12, 2002", "d");

	// Return number of days in a month
	function date_daysInMonth(month, year) {
		return new Date(year ? year : 1970, month, 0).getDate();
	}
	
	// Is year a leap year?
	function date_isLeapYear(year) {
		return (year % 4 == 0 && year % 400 != 0);
	}
	
// Checks for a valid date
function isDate(date) {
	var valid = true;
	
	var i = date.indexOf('/');
	var i2 = date.indexOf('/', i + 1);
	
	day = date.substring(0, i);				// day
	month = date.substring(i + 1, i2);		// month
	year = date.substring(i2 + 1);			// year
	
	if(	isNaN(day) || isNaN(month) || isNaN(year) ||
			day < 1 || day > 31 || day.length > 2 || day.length < 1 ||
			month < 1 || month > 12 || month.length > 2 || month.length < 1 ||
			year < 0 || year.length > 4 || year.length < 2)
		return false;

	var mon = parseInt(month, 10);
	
	// April, June, September, November only 30 days
	if(mon == 4 || mon == 6 || mon == 9 || mon == 11) {
		if(parseInt(day, 10) > 30)
			valid = false;
	}
	else if(mon == 2) {
		if(parseInt(day, 10) > (28 + (parseInt(year, 10) % 4 == 0 && parseInt(year, 10) % 400 != 0 ? 1 : 0)) )
			valid = false;
	}
	else if(parseInt(day, 10) > 31)
		valid = false;
	
	return valid;
}

// Returns true if year is a leap year
function isLeapYear(year) {
	return (year % 4 == 0 && year % 400 != 0);
}