// Created By: Chris Campbell
// www.particletree.com
//
// Modified By: Bobby Jack
// bobbykjack@yahoo.co.uk
//
// The way that required fields are currently handled is slightly confusing,
// mainly because the standard type checking is divorced from the 'required'
// checking.
window.onload = attachFormHandlers;
//
// Run an initial check on validating fields and attach validating function to
// their change event.
//
function attachFormHandlers()
{
    formId = 'standard';
    // Get the form
    var form = document.getElementById(formId);
    // Ensure we're on a DOM-compatible browser
    if (document.getElementsByTagName)
    {
        var elementsToCheck = new Array('input', 'select');
        for (var j = 0; j < elementsToCheck.length; j++)
        {
            elementsToCheck[j] = form.getElementsByTagName(elementsToCheck[j]);
        }
        for (var j = 0; j < elementsToCheck.length; j++)
        {
            var elements = elementsToCheck[j];
            for (var iCounter = 0; iCounter < elements.length; iCounter++)
            {
                var element = elements[iCounter];
                // Validate required inputs that already have a value. This fixes a
                // bug in which pre-filled values aren't recognised as being valid.
                var rules = parseRules(element.className);
                // Attach the onchange to each input field
                if (rules.type != 'radio' && rules.type != 'check')
                {
                    element.onchange = function() { return attach(this); }
                }
                /*
                if (rules.type == 'check')
                {
            
                }
                else
                */
                if (rules.type != 'radio' && rules.type != 'check' && rules.validate && element.value != '')
                {
                    var labelText = getLabelText(element.id);
                    var valid = validateByType(rules.type, element.value, labelText);
                    var feedback = document.getElementById(rules.feedback);
                    if (feedback)
                    {
                        feedback.innerHTML = valid;
                             feedback.style.color = '#F00';
                    }
                }
            }
        }
    }
    // Attach validate() to the form
    form.onsubmit = function() { return validateSubmit(form); }
}
//
// Validate form, on submission
//
function validateSubmit(form)
{
    var numErrors = 0;
    var valid = '';
    // Ensure we're on a DOM-compatible browser
    if (document.getElementsByTagName)
    {
        var elementsToCheck = new Array('input', 'select');
        // Replace each element type with an array of elements of that type
        for (var j = 0; j < elementsToCheck.length; j++)
        {
            elementsToCheck[j] = form.getElementsByTagName(elementsToCheck[j]);
        }
        for (var j = 0; j < elementsToCheck.length; j++)
        {
            var elements = elementsToCheck[j];
            for (var iCounter = 0; iCounter < elements.length; iCounter++)
            {
                var element = elements[iCounter];
                valid = '';
                // Horrible hack to allow for checking of minimum donation
                if (element.name.substring(0, 4) == 'min:')
                {
                    var lookFor = element.name.substring(4);
                    var lookForOther = false;
                    for (var tmpCounter = 0; tmpCounter < elements.length; tmpCounter++)
                    {
                        var tmpElement = elements[tmpCounter];
                        if (tmpElement.name == lookFor)
                        {
                            if (tmpElement.checked)
                            {
                                if (tmpElement.value == "generalother")
                                {
                                    lookForOther = true;
                                }
                                else
                                {
                                    //alert("[1] entered value [" + tmpElement.value + "], minimum value [" + element.value + "]");
    
                                    if (tmpElement.value < element.value)
                                    {
                                        valid = "Please make a minimum donation of £40";
                                        //alert("Please make a minimum donation of £40");
                                        //return false;
                                    }
    
                                    break;
                                }
                            }
                        }
                        
                        if (lookForOther && tmpElement.id == 'other-amount')
                        {
                            //alert("[2] entered value [" + tmpElement.value + "], minimum value [" + element.value + "]");
    
                            if (tmpElement.value < element.value)
                            {
                                valid = "Please make a minimum donation of £40";
                                //alert("!!" + valid + "::" + element.className);
                                //alert("Please make a minimum donation of £40");
                                //return false;
                            }
    
                            break;
                        }
                    }
                }
                var rules = parseRules(element.className);
                if (rules.validate)
                {
                    // Special-case handling for groups of radio buttons
                    if (rules.type == 'radio')
                    {
                        var oneChecked = false;
                        // Get all radio buttons whose name matches this one
                        for (var tmpCounter = 0; tmpCounter < elements.length; tmpCounter++)
                        {
                            var tmpElement = elements[tmpCounter];
                            if (tmpElement.type == 'radio' && tmpElement.name == element.name && tmpElement.checked)
                            {
                                oneChecked = true;
                                break;
                            }
                        }
                        if (!oneChecked)
                        {
                            valid = "Please select an option";
                        }
                    }
                    else if (rules.type == 'check' && rules.required && !element.checked)
                    {
                        numErrors++;
                        feedback = document.getElementById(rules.feedback);
                        feedback.innerHTML = '*';
                        feedback.style.color = '#F00';
                        continue;
                    }
                    else if (rules.type == 'minimum')
                    {
                    }
                    else
                    {
                        valid = validateByType(rules.type, element.value, '');
                    }
                    feedback = document.getElementById(rules.feedback);
                    if (feedback)
                    {
                        if (element.value != '')
                          {
                            feedback.innerHTML = valid;
                        }
                        feedback.style.color = '#F00';
                    }
                    if (valid != '' || (rules.required == 'required' && element.value == ''))
                    {
                        numErrors++;
                    }
                }
            }
        }
    }
    // If there are any errors give a message
    if (numErrors > 0)
    {
        alert ("Please make sure all fields are properly completed. Errors are marked in red!");
        return false;
    }
    return true;
}
//
// Convert a 'rule string' into its corresponding 'rule object'. Since we're
// going to be passing in the value of the 'class' attribute, let's do as much
// checking as possible to ensure this really looks like a valid 'rule string'.
//
function parseRules(str)
{
    var rules = str.split(' ');
    // Return an empty object if the string doesn't look like a valid 'rule
    // string'.
    if (rules.length != 4 || rules[0] != 'validate'
        || (rules[1] != 'required' && rules[1] != 'notrequired')
        || !validType(rules[2]))
    {
        return { };
    }
    return {
        validate: rules[0],
        required: rules[1],
        type:     rules[2],
        feedback: rules[3]
    };
    //return out;
}
// Global variable to control whether or not type-checking should take place
// following a 'required' check. This over-complicates things, so it would be
// nice to do away with.
var gContinue = true;
//
// Get the first element matching by name and attributes.
// NB IE seems to have problems with this!
//
function getElement(name, attrs)
{
    var els = document.getElementsByTagName(name);
    main:
    for (i = 0; i < els.length; i++)
    {
        var el = els.item(i);
        for (j in attrs)
        {
            if (el.getAttribute(j) != attrs[j])
            {
                continue main;
            }
        }
        return el;
    }
}
//
// Get the text of a label for a given input id.
//
// N.B. This is not currently used. It was intended to be used to display the
// input's label text in the error message, but this was changed in favour of
// using the type name instead (reason unknown). Moreover, getElement() appears
// to fail in IE.
//
function getLabelText(id)
{
    return '';
    var labelText = '';
    
    // Get the label associated with this input
    var label = getElement('label', {'for': id});
    // Assume label is of the simplest structure: <label>text</label>
    if (label.childNodes.length == 1)
    {
        var node = label.childNodes[0];
        // http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-1841493061
        // interface Node { const unsigned short TEXT_NODE = 3; }
        if (node.nodeType == 3)
        {
            labelText += node.nodeValue;
        }
    }
    return labelText;
}
//
// Validate given input object according to 'required' status and type.
//
function attach(objInput)
{
    var labelText = getLabelText(objInput.id);
    // Get value inside of input field
    sVal = objInput.value;
    gContinue = true; 
    var rules = parseRules(objInput.className);
    // validateRequired() checks if it is required and then sends back feedback
    var sFeedback = validateRequired(rules.required, sVal, rules.type);
    // If it is required and blank, gContinue is false and we don't validate
    // anymore. This is done because if it is blank it will also fail other
    // tests. We don't want to spam the user with INVALID EMAIL! if the field
    // is still blank.
    if (gContinue)
    {
        sFeedback = validateByType(rules.type, sVal, labelText);
    }
    // Once validation is complete, return the feedback
    var feedback = document.getElementById(rules.feedback);
    feedback.innerHTML = sFeedback;
    feedback.style.color = '#F00';
}
//
// Supported 'types' are all represented by Regular Expressions
//
var typePatterns = 
{
    age:      new Array('Age',       /.+/),
    date:     new Array('Date',      /.*/),
    email:    new Array('Email',     /^.+@.+\.[a-z]+$/i),
    name:     new Array('Name',      /^([a-zA-z\s|-]{1,32})$/),
    numeric:  new Array('Number',    /^(\d|-)?(\d|,)*\.?\d*$/),
    password: new Array('Password',  /.*/),
    phone:    new Array('Phone',     /^([0-9\s]{8,20})$/),
    postcode: new Array('PostCode',  /^([a-zA-z0-9\s]{1,12})$/),
    radio:    new Array('Option',    /.+/),
    check:    new Array('Option',    /.+/)
};
//
// Return true if the given type is recognised.
//
function validType(type)
{
    return type == 'none' || type == 'minimum' || typePatterns[type];
}
//
// Validate a value according to a specific type.
//
function validateByType(type, value, desc)
{
    var pattern = typePatterns[type];
    return !pattern || pattern[1].test(value) ? '' : 'Invalid ' + pattern[0];
}
//
// Validate value for required fields, return appropriate 'feedback'. Note that
// this is a complicated way of doing things which we should be able to
// simplify.
//
function validateRequired(sRequired, sVal, sTypecheck)
{
    // Check if required if not, continue validation script
    if (sRequired == "required")
    {
        // If it is required and blank then it is an error and continues to be
        // required
        if (sVal == "")
        {
            gContinue = false;
            return  "*";
        }
        // If it's not blank and has no other validation requirements the field
        // passes
        else if (sTypecheck == "none")
        {
            return "";
        }
    }
    // Non-required inputs that don't have a value should pass
    else if (sVal == '')
    {
        gContinue = false;
        return '';
    }
    // Get here if the value's not required OR it's required + has a value + has
    // other validation requirements.
}