/////////////////////////////////////////////////////////////
// Javascript form validator
/////////////////////////////////////////////////////////////


//Validator class
function Validator() {
    this.rulelist = new Array();
    this.title = 'The form contains following errors:';
    
    //set the message that is displayed at the top of error list
    this.setTitle = function(newTitle) {
        this.title = newTitle;
    }
    
    //adding new rules (validator functions)
    //"field" must be string or array of strings (multiple fields)
    //"func" must be a function (that returns true/false and accepts form element or array of form elements as first argument)
    //"message" must be a string (will be displayed when function returns false)
    //if more than 3 arguments are given then these all will be added to function call (arguments 2, ...)
    this.addRule = function(field, func, message) {
        if(((typeof field)!='string' && (typeof field)!='object') || (typeof func)!='function' || (typeof message)!='string') {
            return;
        }
        
        var ruleObject = new Object();
        ruleObject.field = field;
        ruleObject.func = func;
        ruleObject.message = message;
        
        var allArgs = this.addRule.arguments;
        if(allArgs.length > 3) {
            ruleObject.args = new Array();
            for(var i=3;i<allArgs.length;i++) {
                ruleObject.args.push(allArgs[i]);
            }
        }
        this.rulelist.push(ruleObject);
    }
    
    //perform validation (execute all rules)
    this.validate = function(frm) {
        var isValid = true;
        var errorlist = '';
        for(var i=0;i<this.rulelist.length;i++) {
            var element;
            if((typeof this.rulelist[i].field)=='string') {
                element = frm.elements[this.rulelist[i].field];
            } else {
                element = new Array();
                for(var j=0;j<this.rulelist[i].field.length;j++) {
                    element[j] = frm.elements[this.rulelist[i].field[j]];
                }
            }
            if((typeof element)=='undefined') continue;
            
            //making an comma-separated argument list from array
            var argsstr = '';
            if((typeof this.rulelist[i].args)!='undefined') {
                for(var j=0;j<this.rulelist[i].args.length;j++) {
                    argsstr = argsstr + ', ' + 'this.rulelist[i].args[' + j + ']';
                }
            }
            
            //eval is used because the number of arguments is unknown
            if(!eval('this.rulelist[i].func(element' + argsstr + ')')) {
                if(isValid) {
                    isValid = false;
                    if(element.type=='text' || element.type=='textarea' || element.type=='select-one'
                                            || element.type=='select-multiple' || element.type=='password')
                    {
                        element.focus();
                    }
                }
                errorlist = errorlist + "\n" + this.rulelist[i].message;
            }
        }
        
        if(!isValid) {
            alert(this.title + "\n" + errorlist);
        }
        return isValid;
    }
}



//Some validator functions. custom functions can be added straight to html-files.
//validator functions must take an object as first argument followed by other arguments and
//must return true/false. Validator functions shouldn't alert anything.

//Function that checks if something is selected/typed.
//other functions will return true for empty values, so this rule must be registered
//for all data that is REQUIRED.
function isNonEmpty(element) {
    if(element.type=='text' || element.type=='textarea') {
        if(element.value.match(/^\s*$/)) return false;
        else return true;
        
    } else if(element.type=='hidden') {
        if(element.value.length==0) return false;
        else return true;
        
    } else if(element.type=='password') {
        if(element.value.length==0) return false;
        else return true;
        
    } else if(element.type=='select-one') {
        if(element.selectedIndex < 0 ||element.options[element.selectedIndex].value.match(/^(0|\s*)$/)) return false;
        else return true;

    } else if(element.type=='select-multiple') {
        for(var i=0;i<element.length;i++) {
            if(element.options[i].selected) return true;
        }
        return false;
        
    //single checkbox or radio
    } else if(element.type=='checkbox' || element.type=='radio') {
        if(element.checked) return true;
        else return false;
        
    //radio/checkbox group
    } else if((typeof element)=='object' && element.length != 'undefined') {
        for(var i=0;i<element.length;i++) {
            if(element[i].checked) return true;
        }
        return false;
    }
    return true; //other/unknown element.
}

//for element length check
function isElementLong(element, length){
    if (element.value.length > length)
        return false;
    else
        return true;
}

//main function for all kind of text/number validation.
function matchesPattern(element, pattern) {
    if(element.type=='text' || element.type=='textarea') {
        return element.value.length==0 || element.value.match(pattern);
    } else return true;
}

function isValidEmail(element) {
    return matchesPattern(element, /^[a-z0-9][a-z0-9\.\-\_]*@[a-z0-9][a-z0-9\.\-\_]*\.[a-z]{2,4}$/i);
}

function isValidPhone(element) {
    return matchesPattern(element, /^\+?(\(\+?[0-9\ ]+\))?[0-9\ ]{3,}$/);
}

//for password & confirmation
function areFieldsEqual(elements) {
    if(!elements.length) return true;
    var val = elements[0].value;
    for(var i=1;i<elements.length;i++) {
        if(elements[i].value!=val) return false;
    }
    return true;
}

//for numbers
function isGreaterThan(element,number) {
    if(element.type=='text' || element.type=='textarea' || element.type=='hidden' ) {
        if(element.value.length==0) return true;
        else if(!element.value.match(/^[0-9]|[.]+$/)) return false;
        else return parseFloat(element.value) > number;
    }
}

function isGreaterOrEqualThan(element,number) {
    if(element.type=='text' || element.type=='textarea' || element.type=='hidden' ) {
        if(element.value.length==0) return true;
        else if(!element.value.match(/^[0-9]|[.]+$/)) return false;
        else return parseFloat(element.value) >= number;
    }
}

function isLessThan(element,number) {
    if(element.type=='text' || element.type=='textarea') {
        if(element.value.length==0) return true;
        else if(!element.value.match(/^[0-9]|[.]+$/)) return false;
        else return parseFloat(element.value) < number;
    }
}

function isLessOrEqualThan(element,number) {
    if(element.type=='text' || element.type=='textarea') {
        if(element.value.length==0) return true;
        else if(!element.value.match(/^[0-9]|[.]+$/)) return false;
        else return parseFloat(element.value) <= number;
    }
}

//for multiselects & checkbox groups
function isMinSelected(element, count) {
    var selected = 0;
    for(var i=0;i<element.length;i++) {
        if(element.type=='select-multiple' ? element.options[i].selected : element[i].checked) {
            selected++;
        }
            
        if(selected>=count) return true;
    }
    return selected==0; //if 0, then no error message is needed
}

function isMaxSelected(element, count) {
    var selected = 0;
    for(var i=0;i<element.length;i++) {
        if(element.type=='select-multiple' ? element.options[i].selected : element[i].checked) {
            selected++;
        }
            
        if(selected>count) return false;
    }
    return true;
}

function isExactlySelected(element, count) {
    var selected = 0;
    for(var i=0;i<element.length;i++) {
        if(element.type=='select-multiple' ? element.options[i].selected : element[i].checked) {
            selected++;
        }
    }
    return selected==0 || selected==count;
}

