// @author: Alexander Morales - December 14th 2007
// @desc: Form validation using the jQuery Framework vs 1.2.1
// UPDATES:
// 1 - updated function check_phone_number on april 22nd 2008 - us phone number proper format validation.
// 2 - corrected form sumbit - only checks the correct on form submittion to allow multiple forms on 1 page - aug 16 2008 and tested with jquery 1.2.6
// 3 - if validation_objects is undefined return false and exit... added on sept 15 2009.. tested with jquery 1.3.2
// 4 - added unordered list on submit that populates an empty div for usability. sept 18 2009

$(document).ready(function()
{
    //declare all possible validation types and methods
    // # 'validation-type' : 'validation-method'
    var validation_types = {
        'validate-empty'            : 'empty',
        'validate-email'            : 'check_email',
        'validate-phone-number'        : 'check_phone_number',
        'validate-digits'            : 'digits_only',
        'validate-date'                : 'date_format',
        'validate-number'            : 'number_format',
        'validate-url'                : 'url_format',
        'validate-credit-card'        : 'cc_check',
        'validate-select-one'        : 'make_selection',
        'validate-is-checked'        : 'is_checked',
        'maxLength'                    : 'length_max',
        'minLength'                    : 'length_min',
        'equalTo'                    : 'is_equal_to'
    };
    
    if (typeof(validation_objects) == 'undefined') { return false; }
    
    // Loop through the form and associate the types and method for events
    for (form_name in validation_objects) {
        var form_id = form_name;
        var parent_element = '';
        for (sub_name in validation_objects[form_name]) {
            if (sub_name.match(/^sub_val/)) {
                for (sub_validation_type in validation_objects[form_name][sub_name]) {
                    validate_secondary_elements(form_id, sub_validation_type, parent_element, validation_types[sub_validation_type], validation_objects[form_name][sub_name][sub_validation_type]);
                }
            }
            else {
                parent_element = sub_name;
                validate_primary_elements(form_id, validation_objects[form_name][sub_name], sub_name, validation_types[validation_objects[form_name][sub_name]]);
            }
        }
    }
    // Loop through the form on submit and associate the types and method for events
    $("#" + form_name).submit(function() {
        var found_error = 'no';
        var show_errors = '<a name="' + form_name + 'err_anchor"></a><ul style="color:red; padding-left:25px; padding-bottom:0px; margin-bottom:0px; list-style-image: url(/media/images/icons/form-error.gif);">';
        for (form_name in validation_objects) {
            var form_id = form_name;
            var parent_element = '';
            for (sub_name in validation_objects[form_name])    {
                if (sub_name.match(/^sub_val/)) {
                    for (sub_validation_type in validation_objects[form_name][sub_name]) {
                        var x = submit_secondary_elements(form_id, sub_validation_type, parent_element, validation_types[sub_validation_type], validation_objects[form_name][sub_name][sub_validation_type]);
                        if(x == false) {
                            show_errors += '<li style="padding-left:25px;">' + error[sub_name] +'</li>';
                            found_error = 'yes'; /* break; */
                        }
                    }
                }
                else {
                    parent_element = sub_name;
                    var x = submit_primary_elements(form_id, validation_objects[form_name][sub_name], sub_name, validation_types[validation_objects[form_name][sub_name]]);
                    if(x == false) {
                        show_errors += '<li>' + error[sub_name] +'</li>';
                        found_error = 'yes'; /* break; */
                    }
                }
            }
        show_errors += '</ul>';
        }
        if(found_error == 'yes') {
            var err_div = form_id + '_errors';
            $('#' + err_div).html(show_errors);
            $('#' + err_div).show(err_div);
            document.location = '#' + form_name + 'err_anchor';
            return false;
        }
    });
    // Validate parent level validation methods
    function validate_primary_elements(form_id, validation_type, form_element_id, val_func) {
        $('#' + form_id + ' ' + '[id=' + form_element_id + ']').focus(function() { setTo('note', $(this).attr('id'), 'visible'); });
        $('#' + form_id + ' ' + '[id=' + form_element_id + ']').blur(function() {
            if(validation_type == 'validate-empty') {
                if ($(this).val().length > 0) { setTo('note', $(this).attr('id'), 'hidden'); } 
                else { setTo('error', $(this).attr('id'), 'visible'); }
            }
            else {
                var msg = eval(val_func + "($(this))");
                if(msg == 'pass') { setTo('note', $(this).attr('id'), 'hidden'); }
                else { setTo('error', $(this).attr('id'), 'visible'); }
            }
        });
    }
    // Validate child level validation methods
    function validate_secondary_elements(form_id, validation_type, form_element_id, val_func, arg) {
        count_error_for_parent = 0;
        $('#' + form_id + ' ' + '[id=' + form_element_id + ']').focus(function() { setTo('note', $(this).attr('id'), 'visible'); });
        $('#' + form_id + ' ' + '[id=' + form_element_id + ']').blur(function() {
            var sub_msg = eval(val_func + "( $(this), arg)");
            if(sub_msg == 'pass') {
                if(count_error_for_parent == 0)
                    setTo('note', $(this).attr('id'), 'hidden');
            }
            else {
                count_error_for_parent++;
                setTo('error', $(this).attr('id'), 'visible');
            }
        });
    }
    // On submit - validate parent level validation methods
    function submit_primary_elements(form_id, validation_type, form_element_id, val_func) {
        var need_correction = 'no';
        $('#' + form_id + ' ' + '[id=' + form_element_id + ']').each(function() {
            if(validation_type == 'validate-empty') {
                if ($(this).val().length == 0) { setTo('error', $(this).attr('id'), 'visible'); need_correction = 'yes'; } 
            }
            else {
                var msg = eval(val_func + "($(this))");
                if(msg == 'error') { setTo('error', $(this).attr('id'), 'visible');  need_correction = 'yes';  }
            }
        });
        if(need_correction == 'yes') { return false; }
    }
    // On submit - validate child level validation methods
    function submit_secondary_elements(form_id, validation_type, form_element_id, val_func , arg) {
        var need_correction = 'no';
        count_error_for_parent = 0;
        $('#' + form_id + ' ' + '[id=' + form_element_id + ']').each(function() {
            var sub_msg = eval(val_func + "( $(this), arg)");
            if(sub_msg == 'error') {
                if(count_error_for_parent == 0) {
                    setTo('error', $(this).attr('id'), 'visible');
                    need_correction = 'yes';
                    count_error_for_parent ++;
                }
            }
        });
        if(need_correction == 'yes') { return false; }
    }
    // Show or hide a specific note, error or success message to user ( set id div.form-note )
    function setTo(type, id, show, txt) {
        $('#note-' + id).removeClass('form-note form-error form-success');
        $('#note-' + id).addClass('form-' + type);        
        $('#note-' + id).text(eval(type + '[' + 'id' + ']'));
        $('#note-' + id).css('visibility', show );
    }
    /*
    /*===========================
       - validation functions - 
    =============================*/
    // validate-email
    // Please use a real email address as we need to email you to confirm your account
    function check_email(email_addy) {
        if(/^[^\s,;]+@([^\s.,;]+\.)+[\w-]{2,}$/.test($(email_addy).val())) { return "pass"; }
        else { return "error"; }
    }
    // validate-phone-number
    // This field validate us proper hone number formats
    function check_phone_number(number) {
        if(/^\(?(\d{3})\)?[- ]?(\d{3})[- ]?(\d{4})$/.test($(number).val())) { return "pass"; }
        else { return "error"; }
    }
    // validate-digits
    // Please use numbers only in this field - avoid spaces or other characters such as dots or commas.
    function digits_only(number) {
        if(/^\d+$/.test( $(number).val() )){ return "pass"; }
        else { return "error"; }
    }
    // validate-date
    // Date must be in following format: ("yyyy/mm/dd") or ("yyyy-mm-dd") or ("mm.dd.yyy")
    function date_format(date_in) {
        if(/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test($(date_in).val())) { return "pass"; }
        else { return "error"; }
    }
    // validate-number-format
    // Please use international number format, eg. 100,000.59
    function number_format(obj) {
        if(/^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test($(obj).val())) { return "pass"; }
        else { return "error"; }
    }
    // validate-url
    // Please enter a valid http(s) or ftp url.
    function url_format(url_in) {
        if(/^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test( $(url_in).val() )){ return "pass"; }
        else { return "error"; }
    }
    // validate-credit-card
    // Please enter a valid card number.
    function cc_check(cc_in) {
        if( $(cc_in).val() == '') { return "error"; }
        this.ar = new Array($(cc_in).val().length);
        this.sum = 0;
        for(i = 0; i < $(cc_in).val().length; ++i){ this.ar[i] = parseInt($(cc_in).val().charAt(i)); }
        for(i = this.ar.length -2; i >= 0; i-=2) {
            this.ar[i] *= 2;
            if(this.ar[i] > 9){ this.ar[i]-=9; }
        }
        for(i = 0; i < this.ar.length; ++i){ this.sum += this.ar[i]; }
        if(this.sum%10 != 0){ return "error"; }
        else {
            // test cards
            // visa, mc, amex, diners club, discover
            if($(cc_in).val() == '4111111111111111' || $(cc_in).val() == '5500000000000004' || $(cc_in).val() == '340000000000009' || $(cc_in).val() == '30000000000004' || $(cc_in).val() == '6011000000000004'){
                return "error";
            }
            return "pass";
        }
    }
    // maxLength
    // you can not have this value greater than arg
    function length_max(val_in, arg) {
        if( $(val_in).val().length > arg) { return "error"; }
        else { return "pass"; }
    }
    // minLength
    // you can not have this value less than arg
    function length_min(val_in, arg) {
        if($(val_in).val().length < arg) { return "error"; }
        else { return "pass"; }
    }
    // validat a selected item
    // check against <option value="empty" selected="selected">please choose</option>
    function make_selection(val_in) {
        if($(val_in).val() == 'empty') {return "error"; }
        else { return "pass"; }
    }
    // validate-is-checked
    // Insure a check box is checked: Perfect for a user who need to accept an agreement
    function is_checked(val_in) {
        err = "error";
        $(val_in).each(function(){ if(this.checked) { err = "pass"; } });
        return err;
    }
    // validate-is-equal_to
    // Check the value of one form field against the value of another form field: Both values must be the same or return error
    function is_equal_to(val_in, arg) {
        err = "error";
        if($(val_in).val() == eval(arg)) { err = "pass"; }
        return err;
    }
    // To do:
    // Test Ajax function for server query of validation methods
    // Posibilities - build validation methods in PHP.
    function jquery_ajax(endPoint, params) {
        $.ajax({
            url            : '/ajax/validate/' + endPoint,
            type        : 'POST',
            dataType    : 'json',
            data        : params,
            timeout        : 2000,
            error        : function(error) { alert('Error loading script /ajax/validate/' + endPoint); },
            success        : function(json) { return json; }
        });
    }
});