function isBlank(s)
{
    for (var i = 0; i < s.length; i++)
    {
        var c = s.charAt(i);
        if ((c != ' ') && (c != '\n') && (c != '\t')) return false;
    }
    return true;
}

// Remove all spaces from a string
function removeSpaces(string)
{
    var newString = '';
    for (var i = 0; i < string.length; i++)
    {
        if (string.charAt(i) != ' ') newString += string.charAt(i);
    }
    return newString;
}

// Remove leading and trailing whitespace from a string
function trimWhiteSpace(string)
{
    var newString  = '';
    var substring  = '';
    beginningFound = false;

    // copy characters over to a new string
    // retain whitespace characters if they are between other characters
    for (var i = 0; i < string.length; i++)
    {
        // copy non-whitespace characters
        if (string.charAt(i) != ' ' && string.charCodeAt(i) != 9)
        {

            // if the temporary string contains some whitespace characters, copy them first
            if (substring != '')
            {
                newString += substring;
                substring = '';
            }
            newString += string.charAt(i);
            if (beginningFound == false) beginningFound = true;
        } else if (beginningFound == true) {
            // hold whitespace characters in a temporary string if they follow a non-whitespace character
            substring += string.charAt(i);
        }
    }
    return newString;
}

function isValidDate(somedate)
{
    var checkstr = "0123456789";
    var tempdate = "";
    var separator = "/";
    var segment = new Array();
    var seg = "";
    var d;
    var m;
    var y;
    var leap = 0;
    var err = 0;

    /* Look for non-numbers */
    for (var i = 0; i < somedate.length; i++)
    {
      if (checkstr.indexOf(somedate.substr(i,1)) >= 0)
      {
        tempdate += somedate.substr(i,1);
        seg += somedate.substr(i,1);
      } else {
        var segnum = segment.length;
        segment[segnum] = seg;
        seg = "";
      }
    }

    if (segment.length)
    {
        var segnum = segment.length;
        segment[segnum] = seg;
        seg = "";
        if (segment.length==3)
        {
            m = segment[0];
            d = segment[1];
            y = segment[2];

            if (m.length==1) m = "0" + m;
            if (d.length==1) d = "0" + d;
            if (y.length==2) y = "20" + y;
            if (m.length!=2 || d.length!=2 || y.length!=4) return false;
            // DEBUG
            // alert(m);
            // alert(d);
            // alert(y);
            somedate = m + separator + d + separator + y;
        } else {
            return false;
        }
    } else {
        somedate = tempdate;
        /* Always change date to 8 digits - string*/
        /* if y is entered as 2-digit / always assume 20xx */
        if (somedate.length == 6) somedate = somedate.substr(0,4) + '20' + somedate.substr(4,2);
        if (somedate.length != 8) return false;

        m = somedate.substr(0,2);
        d = somedate.substr(2,2);
        y = somedate.substr(4,4);

        somedate = m + separator + d + separator + y;
    }

    if ((m < 1) || (m > 12)) return false;
    if (d < 1) return false;
    if (y <= 0) return false;

    /* Validation leap year / february / d */
    if ((y % 4 == 0) || (y % 100 == 0) || (y % 400 == 0)) leap = 1;
    if ((m == 2) && (leap == 1) && (d > 29)) return false;
    if ((m == 2) && (leap != 1) && (d > 28)) return false;

    if ((d > 31) && ((m == "01") || (m == "03") || (m == "05") || (m == "07") || (m == "08") || (m == "10") || (m == "12"))) return false;
    if ((d > 30) && ((m == "04") || (m == "06") || (m == "09") || (m == "11"))) return false;

    // DEBUG
    // alert(somedate);
    return somedate;
}

function isValidNumber(num, min, max)
{
    if (!isNumeric(num))
    {
        return false;
    }
    if (num < min || num > max)
    {
        return false;
    } else {
        return true;
    }
}

function isValidLength(string, min, max)
{
    if (string.length < min || string.length > max)
    {
        return false;
    } else {
        return true;
    }
}

// Check that a string contains only letters and numbers
function isAlphaNumeric(string, ignoreWhiteSpace)
{
    if (string.search)
    {
        if ((ignoreWhiteSpace && string.search(/[^\w\s]/) != -1) || (!ignoreWhiteSpace && string.search(/\W/) != -1)) return false;
    }
    return true;
}

// Check that a string contains only letters
function isAlphabetic(string, ignoreWhiteSpace)
{
    if (string.search)
    {
        if ((ignoreWhiteSpace && string.search(/[^a-zA-Z\s]/) != -1) || (!ignoreWhiteSpace && string.search(/[^a-zA-Z]/) != -1)) return false;
    }
    return true;
}

// Check that a string contains only numbers
function isNumeric(string, ignoreWhiteSpace)
{
    if (string.search)
    {
        if ((ignoreWhiteSpace && string.search(/[^\d\s]/) != -1) || (!ignoreWhiteSpace && string.search(/\D/) != -1)) return false;
    }
    return true;
}

// Check that a string contains uppercase char, lowercase char, and a number
function isValidPassword(string, min, max)
{
    if (!isValidLength(string, min, max)) return false; // minlen, maxlen

    if (string.search)
    {
        if (string.search(/[\s]/) != -1) return false;  // whitespace
        if (string.search(/[a-z]/) == -1) return false; // lowercase
        if (string.search(/[A-Z]/) == -1) return false; // uppercase
        if (string.search(/[\d]/) == -1) return false;  // number
    }
    return true;
}

// Returns a checksum digit for a number using mod 10
function getMod10(number)
{
    // convert number to a string and check that it contains only digits
    // return -1 for illegal input
    number = '' + number;
    number = removeSpaces(number);
    if (!isNumeric(number)) return -1;

    // calculate checksum using mod10
    var checksum = 0;
    for (var i = number.length - 1; i >= 0; i--)
    {
        var isOdd = ((number.length - i) % 2 != 0) ? true : false;
        var digit = number.charAt(i);

        if (isOdd)
        {
            checksum += parseInt(digit);
        } else {
            var evenDigit = parseInt(digit) * 2;
            if (evenDigit >= 10) checksum += 1 + (evenDigit - 10);
            else checksum += evenDigit;
        }
    }
    return (checksum % 10);
}

// Check that a credit card number is valid based using the LUHN formula (mod10 is 0)
function isValidCreditCard(number,cardtype)
{
    number = '' + number;
    if (number.length > 16 || number.length < 13 )
    {
        return false;
    } else if (getMod10(number) != 0) {
        return false;
    } else if (arguments[1]) {
        var type = arguments[1];
        var t = type.toLowerCase();
        var f2d = number.substring(0, 2); // first 2 digits
        var f4d = number.substring(0, 4); // first 4 digits

        if (t == 'visa' && number.substring(0, 1)==4 && (number.length==16 || number.length==13))
        {
            return true;
        } else if (t == 'mastercard' && number.length==16 && (f2d=='51' || f2d=='52' || f2d=='53' || f2d=='54' || f2d=='55')) {
            return true;
        } else if (t == 'amex' && number.length==15 && (f2d=='34' || f2d=='37')) {
            return true;
        } else if (t == 'discover' && number.length==16 && f4d=='6011') {
            return true;
        } else if (t == 'diners' && number.length==14 && (f2d=='30' || f2d=='36' || f2d=='38')) {
            return true;
        } else {
            // if the above card types are all the ones that the site accepts, change the line below to 'else return false'
            return false;
        }
    } else {
        return true;
    }
}

function isValidEmail(address)
{
    // regular expression
    email_re = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/
    if (email_re.test(address))
    {
        return true;
    } else {
        return false;
    }
}

function isValidZipcode(zipcode)
{
    zipcode = removeSpaces(zipcode);
    if (!(zipcode.length == 5 || zipcode.length == 9 || zipcode.length == 10)) return false;
    if ((zipcode.length == 5 || zipcode.length == 9) && !isNumeric(zipcode)) return false;
    if (zipcode.length == 10 && zipcode.search && zipcode.search(/^\d{5}-\d{4}$/) == -1) return false;
    return true;
}

// Check that a US or Canadian phone number is valid
function isValidPhone(areaCode, prefixNumber, suffixNumber) {
   if (arguments.length == 1) {
      var phoneNumber = arguments[0];
      phoneNumber = phoneNumber.replace(/\D+/g, '');
      var length = phoneNumber.length;
      if (phoneNumber.length >= 7) {
         var areaCode = phoneNumber.substring(0, length-7);
         var prefixNumber = phoneNumber.substring(length-7, length-4);
         var suffixNumber = phoneNumber.substring(length-4);
      }
      else return false;
   }
   else if (arguments.length == 3) {
      var areaCode = arguments[0];
      var prefixNumber = arguments[1];
      var suffixNumber = arguments[2];
   }
   else return true;

   if (areaCode.length != 3 || !isNumeric(areaCode) || prefixNumber.length != 3 || !isNumeric(prefixNumber) || suffixNumber.length != 4 || !isNumeric(suffixNumber)) return false;
   return true;
}

// Validate Form 'f'
function verify(f)
{
    var msg;
    var required_fields = "";
    var invalid_fields = "";
    var used = "";


    for (var i=0;i<f.elements.length;i++)
    {
        var e = f.elements[i];
        // DEBUG
        // alert("obj name: " + e.name + "\n" + "obj type: " + e.type + "\n" + "checked? " + e.checked);
        var thisMsg = "";        
        if (e.required_msg) thisMsg = e.required_msg;
        if (e.notnull_msg) thisMsg = e.notnull_msg;
        if (e.forceoption_msg) thisMsg = e.forceoption_msg;
        
/***** REQUIRED *****/
        /* hidden fields can be used like text or with radios and checkboxes with the prefix 'req_' */
        if (((e.type=="hidden" && e.name.substring(0,4)!='req_') || (e.type == "text") || (e.type == "password") || (e.type == "textarea")) && e.required)
        {
            e.value = trimWhiteSpace(e.value);
            // check if the field is empty
            if ((e.value == null) || (e.value == "") || isBlank(e.value))
            {
               if (required_fields.indexOf(thisMsg) == -1)
               {
                required_fields += "\n- " + e.required_msg;
               }
               
            }
        } else if (e.type == "select-one" || e.type == "select-multiple" || e.type == "select") {
            if (e.required && e.selectedIndex == -1)
            {
                if (required_fields.indexOf(thisMsg) == -1)
                {
                    required_fields += "\n- ";
                 required_fields += e.required_msg;
                }
            } else if (e.notnull && e.options[e.selectedIndex].value == '') {
                if (required_fields.indexOf(thisMsg) == -1)
                {
                    required_fields += "\n- ";
                    required_fields += e.notnull_msg;
                }
            } else if (e.forceoption && e.options.length <= 0) {
                if (required_fields.indexOf(thisMsg) == -1)
                {
                    required_fields += "\n- ";
                    required_fields += e.forceoption_msg;
                }
            }
        }
        if (e.type == "hidden" && e.required)
        {
            // strip out the 4 character 'required' prefix (req_)
            var basename = e.name.substring(4,e.name.length);

            for (var j=0;j<f.elements.length;j++)
            {
                var required_group = f.elements[j];
                // find any required objects
                if (((required_group.name == basename) && (basename != used)) && (required_group.type == "radio" || required_group.type == "checkbox"))
                {
                    var required_obj = f.elements[basename];
                    var checkedObj = -1;
                    //
                    if (required_obj.length==undefined) // object is solo and not an array of radios or checkboxes
                    {
                        if (required_obj.checked == true)
                        {
                          checkedObj = 1;
                        }
                    } else {
                        for (var k=0;k<required_obj.length;k++)
                        {
                            if (required_obj[k].checked == true)
                            {
                              checkedObj = k;
                              break;
                            }
                        }
                    }
                    if (checkedObj < 0)
                    {
                        required_fields += "\n- ";
                        required_fields += e.required_msg;
                    }
                    used = basename;
                }
            }
            
            
            continue;
        }

/***** FORMATTING *****/

        if (e.pattern)
        {
            var val = e.value;
            var pat = e.pattern.toLowerCase();
            var valid = true;

            if ((pat=='visa' || pat=='mastercard' || pat=='amex' || pat=='discover' || pat=='diners')&&(isValidCreditCard(val,pat)==false)) { valid=false;
            } else if ((pat=='validnum')&&(isValidNumber(val,e.minval,e.maxval)==false)) { valid=false;
            } else if ((pat=='minmax')&&(isValidLength(val,e.minlen,e.maxlen)==false)) { valid=false;
            } else if ((pat=='password')&&(isValidPassword(val,e.minlen,e.maxlen)==false)) { valid=false;
            } else if ((pat=='date')&&(isValidDate(val)==false)) { valid=false;
            } else if ((pat=='email')&&(isValidEmail(val)==false)) { valid=false;
            } else if ((pat=='zipcode')&&(isValidZipcode(val)==false)) { valid=false;
            } else if ((pat=='phone')&&((e.prefix && e.suffix && isValidPhone(val,form[e.prefix].value,form[e.suffix].value)==false) || (!e.prefix && !e.suffix && isValidPhone(val)==false))) { valid=false;
            } else if ((pat=='alphanumeric')&&(isAlphaNumeric(val,true)==false)) { valid=false;
            } else if ((pat=='numeric')&&(isValidNumber(val,true)==false)) { valid=false;
            } else if ((pat=='alphabetic')&& (isAlphabetic(val,true)==false)) { valid=false;
            } else if (pat=='password2') { valid=false;
            }
            if (!valid)
            {
                invalid_fields += "\n- ";
                invalid_fields += e.pattern_msg;
            }
        }
    
    
        
    }
    
    

    // Now, if there were any errors, display the messages, and
    // return false to prevent the form from being submitted.
    // Otherwise return true.

    if (!required_fields && !invalid_fields) return true;

    msg  = "The form was not submitted because of the following error(s).\n";
    msg += "Please correct the error(s) and re-submit.\n";
    msg += "________________________________________________________\n\n";

    if (required_fields)
    {
        msg += "THE FOLLOWING REQUIRED FIELD(S) ARE EMPTY:";
        msg += required_fields + "\n";
    }
    if (invalid_fields)
    {
        if (required_fields) msg += "________________________________________________________\n\n";
        msg += "THE FOLLOWING FIELD(S) ARE FORMATTED INCORRECTLY:";
        msg += invalid_fields + "\n";
        msg += "________________________________________________________\n\n";
    }
    alert(msg);
    return false;
}
