function validateFields(form, rules)
{
  // loop through rules
  for (var i=0; i<rules.length; i++)
  {
    // split row into component parts 
    var row = rules[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, don'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];

    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 "is_alpha": 
        if (form[fieldName].value && form[fieldName].value.match(/\W/))
        {
          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 "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
  Purpose:  tests a string is a valid email
\*--------------------------------------------------------------------------------------------*/
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;
}

