/*----------------------------------------------------------------------------*\
	File:			general.js
	Abstract:	javascript functions for use in UniversityHeights.ca web forms

  Copyright (c) Benjamin Keen 2005 [BenjaminKeen.org]
\*----------------------------------------------------------------------------*/


/*----------------------------------------------------------------------------*\
	Function: validateFields()
	Purpose:  generic form validation. Contact benjaminkeen.org for a fuller 
	        explant
\*----------------------------------------------------------------------------*/
function validateFields(form, fieldInfo)
{
	// loop through fieldInfo
	for (var i=0; i<fieldInfo.length; i++)
	{

		// split row into component parts 
		var row = fieldInfo[i].split(",");

		// while the row begins with "if:..." test the condition. If true, strip the if:..., part 
		// and continue evaluating the rest of the line. Keep repeating this while the line begins 
		// with an if-condition. If it fails any of the conditions, it doesn't bother validating the 
		// rest of the line.
		var satisfiesIfConditions = true;
		while (row[0].match("^if:"))
		{
			var condition = row[0];
			condition = condition.replace("if:", "");
			var parts = condition.split("=");
			var fieldToCheck = parts[0];
			var valueToCheck = parts[1];

			// find value of FIELDNAME for conditional check
			var fieldnameValue = "";			
			if (form[fieldToCheck].type == undefined)	// RADIO
			{
				for (var j=0; j<form[fieldToCheck].length; j++)
				{
					if (form[fieldToCheck][j].checked)
						fieldnameValue = form[fieldToCheck][j].value;
				}
			}
			else 										// ALL OTHER FIELD TYPES
				fieldnameValue = form[parts[0]].value;

			// if the VALUE is NOT the same, we don't need to validate this field. Return.
			if (fieldnameValue != valueToCheck)
			{
				satisfiesIfConditions = false;
				break;
			}
			else 
				row.shift();		// remove this if-condition from line, and continue validating line
		}

		if (!satisfiesIfConditions)
			continue;


		var requirement = row[0];
		var fieldName   = row[1];

		// depending on the validation test, store the incoming strings for use later...
		if (row.length == 6)				// valid_date
		{
			var fieldName2   = row[2];
			var fieldName3   = row[3];
			var date_flag    = row[4];
			var errorMessage = row[5];
		}
		else if (row.length == 4)			// same_as
		{
			var fieldName2   = row[2];
			var errorMessage = row[3];
		}
		else
			var errorMessage = row[2];		// everything else!


		// if the requirement is "length=...", rename requirement to "length" for switch statement
		if (requirement.match("^length="))
		{
			var lengthRequirements = requirement;
			requirement = "length";
		}

		// if the requirement is "range=...", rename requirement to "range" for switch statement
		if (requirement.match("^range="))
		{
			var rangeRequirements = requirement;
			requirement = "range";
		}


		// now, validate whatever is required of the field
		switch (requirement)
		{
			case "required":
				// if radio buttons, do separate check:
				if (form[fieldName].type == undefined)
				{
					var oneIsChecked = false;
					for (var j=0; j<form[fieldName].length; j++)
					{
						if (form[fieldName][j].checked)
							oneIsChecked = true;
					}
					if (!oneIsChecked)
					{
						alertMessage(form[fieldName], errorMessage);
						return false;						
					}
				}
				// otherwise, just perform ordinary "required" check.
				else if (!form[fieldName].value)
				{					
					alertMessage(form[fieldName], errorMessage);
					return false;
				}
				break;

			case "digits_only":				
				if (form[fieldName].value && form[fieldName].value.match(/\D/))
				{
					alertMessage(form[fieldName], errorMessage);
					return false;
				}
				break;

			case "length":
				var result  = lengthRequirements.replace("length=", "");
				var range_or_exact_number = result.match(/[^_]+/);
				var fieldCount = range_or_exact_number[0].split("-");

				// if the user supplied two length fields, make sure the field is within that range
				if (fieldCount.length == 2)
				{
					if (form[fieldName].value.length < fieldCount[0] || form[fieldName].value.length > fieldCount[1])
				    {
						alertMessage(form[fieldName], errorMessage);
						return false;
					}
				}

				// otherwise, check it's EXACTLY the size the user specified 
				else
				{
					if (form[fieldName].value.length != fieldCount[0])
					{
						alertMessage(form[fieldName], errorMessage);
						return false;
					}
				}				
				break;

			// this is also true if field is empty [should be same for digits_only]
			case "valid_email":
				if (form[fieldName].value && !isValidEmail(form[fieldName].value))
				{
					alertMessage(form[fieldName], errorMessage);
					return false;					
				}
				break;

			case "valid_date":
				// this is written for future extensibility of isValidDate function to allow 
				// checking for dates BEFORE today, AFTER today, IS today and ANY day.
				var isLaterDate = false;
				if 		(date_flag == "later_date")
					isLaterDate = true;
				else if (date_flag == "any_date")
					isLaterDate = false;

				if (!isValidDate(form[fieldName].value, form[fieldName2].value, form[fieldName3].value, isLaterDate))
				{
					alertMessage(form[fieldName], errorMessage);
					return false;
				}
				break;

			case "same_as":
				if (form[fieldName].value != form[fieldName2].value)
				{
					alertMessage(form[fieldName], errorMessage);
					return false;
				}				
				break;

			case "range":
				var result  = rangeRequirements.replace("range=", "");
				var rangeValues = result.split("-");
				
				// if the user supplied two length fields, make sure the field is within that range
				if ((form[fieldName].value < Number(rangeValues[0])) || (form[fieldName].value > Number(rangeValues[1])))
				{
					alertMessage(form[fieldName], errorMessage);
					return false;
				}
				break;

			default:
				alert("Unknown requirement flag in validateFields(): " + requirement);
				return false;
		}
	}
  
  return true;

}


/*--------------------------------------------------------------------------------------------*\
	Function: alertMessage()
	Purpose:	simple helper function which alerts a message, then focuses on and highlights 
				a particular field.
\*--------------------------------------------------------------------------------------------*/
function alertMessage(obj, message)
{	
  var backgroundColor = "#F2F9FF";

	alert(message);

	// if "obj" is an array: it's a radio button. Focus on the first element.
	if (obj.type == undefined)
		obj[0].focus();
	else
	{
		obj.style.background = backgroundColor;
		obj.focus();
	}
	return false;
}


function isValidEmail(str)
{
	// trim starting / ending whitespace
	str = str.replace(/^\s*/, "");
	str = str.replace(/\s*$/, "");

	var at="@"
	var dot="."
	var lat=str.indexOf(at)
	var lstr=str.length
	var ldot=str.indexOf(dot)

	if (str.indexOf(at)==-1)
		return false
	
	if (str.indexOf(at)==-1 || str.indexOf(at)==0 || str.indexOf(at)==lstr)
		return false
	
	if (str.indexOf(dot)==-1 || str.indexOf(dot)==0 || str.indexOf(dot)==lstr)
		return false

	if (str.indexOf(at,(lat+1))!=-1)
		return false

	if (str.substring(lat-1,lat)==dot || str.substring(lat+1,lat+2)==dot)
		return false

	if (str.indexOf(dot,(lat+2))==-1)
		return false

	if (str.indexOf(" ")!=-1)
		return false

	return true;
}


// helper function to check to see if a string is empty
function isEmpty(str)
{  
	return ((str == null) || (str.length == 0));
}


/*--------------------------------------------------------------------------------------------*\
	Function: 	isWhitespace()
	Purpose:	Returns true if string parameter is empty or whitespace characters only.
\*--------------------------------------------------------------------------------------------*/
function isWhitespace(s)
{
	var i;

    // Is s empty?
    if (isEmpty(s)) return true;

    for (var i=0; i<s.length; i++)
    {   
        var c = s.charAt(i);
        if (whitespace.indexOf(c) == -1)
			return false;
    }

    return true;
}


/*--------------------------------------------------------------------------------------------*\
	Function: isValidDate()
	Purpose:	to check an incoming date is valid. If any of the date parameters fail, it 
				returns	a string message denoting the problem.
	Parameters: month 	- 	should be an integer between 1 and 12
				day		-	should be an integer between 1 and 31 (depending on month)
				year	-	should be a 4-digit integer value
				isLaterDate 	- a boolean value. If true, the function verifies the date being 
							     passed in is LATER than the current date.
\*--------------------------------------------------------------------------------------------*/
function isValidDate(month, day, year, isLaterDate)
{

	// depending on the year, calculate the number of days in the month
	if (year % 4 == 0)			// LEAP YEAR 
		var daysInMonth = new Array(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
	else
		var daysInMonth = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);


	// first, check the incoming month and year are valid. 
	if (!month || !day || !year)					return false;
	if (1 > month || month > 12)					return false;
	if (year < 0)									return false;
	if (1 > day || day > daysInMonth[month-1])		return false;


	// if required, verify the incoming date is LATER than the current date.
	if (isLaterDate)
	{
		// get current date
		var today = new Date();
		var currMonth = today.getMonth() + 1; // since returns 0-11
		var currDay   = today.getDate();
		var currYear  = today.getFullYear();

		// zero-pad today's month & day
		if (String(currMonth).length == 1) 	currMonth = "0" + currMonth;
		if (String(currDay).length == 1) 	currDay   = "0" + currDay;		
		currDate = String(currYear) + String(currMonth) + String(currDay);
		
		// zero-pad incoming month & day
		if (String(month).length == 1) 	month = "0" + month;
		if (String(day).length == 1) 	day   = "0" + day;
		incomingDate = String(year) + String(month) + String(day);

		if (Number(currDate) > Number(incomingDate))
			return false;
	}
	
	return true;
}
