/*----------------------------------------------------------------------------*\
	Script:     entry.js
  Purpose:    Javascript for the 2007 Squamish Triathlon entry form. 


  All code copyright (c) Ben Keen 2005 [BenjaminKeen.org] 
\*----------------------------------------------------------------------------*/


var g_total_ind   = 0; 	// stores form total individual price
var g_total_relay = 0;  // stores form total relay team price
var g_relay_deduction = 0; 


/*----------------------------------------------------------------------------*\
	Function:	  initialize()
  Purpose:    initializes the 'total price' fields on page reload
  Parameter:  form - the form object ('document.form_name')
\*----------------------------------------------------------------------------*/
function initEntryForm(form)
{
	// set the form totals to the correct value (this is for cases when person 
  // clicked "back" in their browser from verification page)

  // Individual 
  if (form.Individual) {
    if (form.Individual.checked)          g_total_ind += Number(form.Individual.value);
    if (form.Youth.checked)               g_total_ind += Number(form.Youth.value);
    if (form.Late_Fee_Individual.checked) g_total_ind += Number(form.Late_Fee_Individual.value);
    if (form.tri_bc_member.checked)       g_total_ind += Number(form.tri_bc_member.value);
  }

  // Relay
  if (form.Three_Relay_Team) {
    if (form.Three_Relay_Team.checked) g_total_relay += Number(form.Three_Relay_Team.value);
    if (form.Two_Relay_Team.checked)   g_total_relay += Number(form.Two_Relay_Team.value);
    if (form.Late_Fee_Relay.checked)   g_total_relay += Number(form.Late_Fee_Relay.value);    
  }
  
  // add the Tri BC deduction
  if (form.relay_tribc_deduction) {
    for (i=0; i<form.relay_tribc_deduction.length; i++)
    {
      if (form.relay_tribc_deduction[i].checked)
      {
        g_total_relay += Number(form.relay_tribc_deduction[i].value);
        g_relay_deduction = form.relay_tribc_deduction[i].value;
      }
    }
  }

  // set form totals:
  if (form.total_ind && form.total_relay) {
    form.total_ind.value   = '$' + eval(g_total_ind);
    form.total_relay.value = '$' + eval(g_total_relay);
  }

	// focus on the first-name field
	form.bill_first_name.focus();
	toggleFormMode();
}

function toggleFormMode(mode) {
  if ( ! mode ) { mode = getMode(); }
  var toggles = ['individual', 'relay'];
  document.getElementById(mode + '_form_section').style.display='block';
  for( i in toggles ) {
    if ((toggles[i]) != mode) {
      document.getElementById(toggles[i] + '_form_section').style.display='none';      
    }
  }
}

function getMode() {
  var toggles = document.getElementById('entry_form').mode_toggle;
  for( m in toggles ) {
    if (toggles[m].checked) return toggles[m].value;
  }
  return 'individual';
}



/*----------------------------------------------------------------------------*\
	Function:	  calculateTotal
  Purpose:    updates the total price field based on the checkbox which was 
				  checked / unchecked.
\*----------------------------------------------------------------------------*/
function calculateTotal(form, section, fieldName)
{
  var val;
  // --------------------update individual section------------------------
  if (section == "ind")  
  {
    // find field value
    val = fieldName.value;

    if (fieldName.checked)
       g_total_ind += eval(val);
    else
       g_total_ind -= eval(val);
  
  	// update displayed total on form
   	form.total_ind.value = '$' + eval(g_total_ind);  
  }

  // --------------------update relay section------------------------
  else if (section == "relay")
  {
    // find field value
    val = fieldName.value;
  
    if (fieldName.checked)
       g_total_relay += eval(val);
    else 
       g_total_relay -= eval(val);
  
  	// update displayed total on form
  	form.total_relay.value = '$' + eval(g_total_relay);
  }
}


// additional function which functions like calculateTotal, only for radio 
// buttons
function toggleDeductions(form, newVal)
{ 
  // deduct the PREVIOUS value from total
  var updatedVal = eval(g_total_relay - g_relay_deduction);

  // add the new total
  updatedVal += eval(newVal);

  // store the current values
  g_relay_deduction = newVal;
  g_total_relay     = updatedVal;
  
  // update displayed total on form
  form.total_relay.value = '$' + eval(updatedVal);
}



/*--------------------------------------------------------------------------------------------*\
	Function: validateFields()
	Purpose:	this is the javascript counterpart to the ColdFusion validateFields function.
				It behaves in a very similar fashion, taking the same arguments for the fieldInfo
				array in the same ColdFusion "List" structure.
	Parameters:	form		- the name of the form to validate
				fieldInfo	- an array. Each index is a string of the form:
								"requirement,fieldname[,fieldname2 [,fieldname3, date_flag]],error message to be alerted".

					fieldName2 is optional, and only used for comparing one field to 
					another (like password fields).

					Valid "requirement" strings are: 
						"required"		- field must be filled in
						"digits_only"	- field must contain digits only
						"length=X" 		- field has to be X characters long
						"length=X-Y" 	- field has to be between X and Y (inclusive) characters long

						"valid_email"	- field has to be valid email address
						"valid_date"	- field has to be a valid date
											fieldname:	MONTH 
											fieldname2:	DAY 
											fieldname3: YEAR
											date_flag: 	"later_date" / "any_date"
						"same_as" 		- fieldname is the same as fieldname2 (for password comparison)
						"range=X-Y"		- field must be a number between the range of X and Y inclusive.

	One last option [can anyone say "feature creep"?] :
	
						if:FIELDNAME=VALUE,		This can come at the very beginning of the line before 
												"requirement". This allows us to only validate a field 
												IF a fieldname FIELDNAME has a value VALUE.
	
	Notes: With both digits_only and valid_email options, if the empty string is passed in it 
	won't generate an error (thus allowing validation of non-required fields).
\*--------------------------------------------------------------------------------------------*/
function validateFields(form, fieldInfo)
{
  var fieldName2, errorMessage, result;
	// 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 (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
		{
			fieldName2   = row[2];
			var fieldName3   = row[3];
			var date_flag    = row[4];
			errorMessage = row[5];
		}
		else if (row.length == 4)			// same_as
		{
			fieldName2   = row[2];
			errorMessage = row[3];
		}
		else
			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":
				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":
				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 = "#ccffcc";

	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 (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)
{
  var daysInMonth;
	// depending on the year, calculate the number of days in the month
	if (year % 4 == 0)			// LEAP YEAR 
		daysInMonth = new Array(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
	else
		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;
}

