isValid - Ribeiro-Tiago/utilities GitHub Wiki

Home

/* Validates the received obj according the validation rules
* @param {array|object} obj - array or single object with obj (either DOM object or value) to be validated, validation rules and custom error messages (if the user wants)
* @param {boolean} errorHandle - tells us if we're handling validation errors or not
* @throws {Error} - throws this exception if arguments are empty
* @return {boolean} - returns true if every input is valid and false if not
*/ 
let isValid = function(obj, errorHandle){
    let handleErrors = ((typeof module !== 'undefined' && typeof module.exports !== 'undefined') || (typeof define === 'function' && define.amd)) ? false : (errorHandle || true);
    
    if (isEmpty(obj))
        throw new Error("Invalid arguments!");

    let errors = [];

    /**
        * Checks the validation rules for a certain input and validates it. If need be, adds new entry to errors array
        * @param {string} input - input to validate. Either DOM element or simple value
        * @param {string} rule - validation rule
        * @param {string} message - custom error message
        * @param {number} ruleValue - if the validation needs a value (e.g.: maxvalue, minvalue), this is that value
        * @throws {Error} - throws exception if rule is valid or one of the ruleValues isn't valid
        */
    let validate = function(input, rule, message, ruleValue, optional){
        let isinputDOM = (input.tagName);
        let inputValue = (isinputDOM) ? input.value : input;
        
        if (optional && isEmpty(inputValue))
            return true;

        if (rule === "required")
        {
            if (isEmpty(inputValue))
            {
                errors.push({
                    error: message || "Required field!",
                    input: input
                });
            }
        }

        else if (rule === "number")
        {
            if (!isNumber(inputValue))
            {
                errors.push({
                    error: message || "Numeric field!",
                    input: input
                });
            }
        }

        else if (rule === "even")
        {
            if (!isEven(inputValue))
            {
                errors.push({
                    error: message || "Value must be even!",
                    input: input
                });
            }
        }

        else if (rule === "maxvalue")
        {
            if (!isNumber(ruleValue))
                throw new Error ("Error validating maxvalue: value isn't number!");

            if (parseInt(inputValue) > parseInt(ruleValue))
            {
                errors.push({
                    error: message || "Value must be below " + ruleValue + "!",
                    input: input
                });
            }
        }

        else if (rule === "minvalue")
        {
            if (!isNumber(ruleValue))
                throw new Error ("Error validating maxvalue: value isn't number!");
            
            if (parseInt(inputValue) < parseInt(ruleValue))
            {
                errors.push({
                    error: message || "Value must be above " + ruleValue + "!",
                    input: input
                });
            }
        }

        else if (rule === "positive")
        {
            if (!isPositive(inputValue))
            {
                errors.push({
                    error: message || "Field must be positive!",
                    input: input
                });
            }
        }

        else if (rule === "value")
        {
            let incorrectValue = function(value){
                return (parseInt(inputValue) !== parseInt(value));
            }

            let different = (isObject(ruleValue)) ? ruleValue.every(incorrectValue) : incorrectValue(ruleValue);
            
            if (different)
            {
                errors.push({
                    error: message || "Value must be one of the following: " + ruleValue + "!",
                    input: input
                });
            }
        }

        else if (rule === "maxlen")
        {
            if (inputValue.length > ruleValue)
            {
                errors.push({
                    error: message || "Maximum value length: " + ruleValue + "!",
                    input: input
                });
            }
        }
        
        else if (rule === "minlen")
        {
            if (inputValue.length < ruleValue)
            {
                errors.push({
                    error: message || "Minimum value length: " + ruleValue + "!",
                    input: input
                });
            }
        }

        else if (rule === "email")
        {
            let regex = new RegExp(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/);
            
            if (!regex.test(inputValue))
            {
                errors.push({
                    error: message || "Invalid email!",
                    input: input
                });
            }
        }

        else if (rule === "equal")
        {
            let tmp = (ruleValue.tagName) ? ruleValue.value : ruleValue;
            
            if (inputValue !== tmp.value)
            {
                errors.push({
                    error: message || "Values don't match!",
                    input: [input, tmp]
                });
            }
        }

        else if (rule === "phone")
        {
            let err = false;

            for (let index in ruleValue){
                let format = ruleValue[index];
                if (err)
                    break;

                for (let i = 0; i < format.length; i++)
                {
                    if (format[i] !== inputValue[i])
                    {
                        err = true;
                        errors.push({
                            error: message || "Values don't match!",
                            input: input
                        });
                        break;
                    }
                }
            };
        }

        else 
            throw new Error("Invalid rule: !\n\tInput: " + input.name + "\n\tRule: " + rule);
    };

    /**
        * Goes through obj and rules and calls validate function
        * @param {object} item - object with values (field, rule, etc)
        */
    let parseObj = function(item){
        let rule = item.rule;
        let input = item.input;

        /**
            * Since the input can take up to 3 shapes, we must validate to see which one we're handling and 
            * act acording. If it's an array we've to go throught each one of it's elements, if it's an object
            * we've to access the object elements, if it's neither we simply send the respective values
            * @param {*} input - input we're preparing to validate 
            */
        let parseInput = function(input){
            if (isArray(rule))
            {
                Array.prototype.forEach.call(rule, function(current, index){
                    validate(input, current.rule, current.message, current.value, current.optional);
                });
            }
            else if (isObject(rule))
                validate(input, rule.rule, rule.message, rule.value, rule.optional);
            else
                validate(input, rule, item.message, item.optional);
        }
        
        // since it's now possible validate more than one input with the same rules we need 
        // check if we're hanlding a single input or several. If it's several we go throught 
        // each one of them and call the respective function. If not we simply call the function
        if (isArray(input))
        {
            Array.prototype.forEach.call(input, function(current){
                parseInput(current);
            })
        }
        else 
            parseInput(input);
    }
    
    
    // since it's now possible to send both an array with all objects to be validated or just 
    // a single object to validate, we gotta check which one we're validating. If we're 
    // validating several inputs, we go throught each of them invidually and validate them
    // else we'll simply validate what we recieved
    if (isArray(obj))
    {
        Array.prototype.forEach.call(obj, function(item){
            parseObj(item);
        });
    }
    else
        parseObj(obj);

    // It's also possible that we don't want to handle the errors here, and wanna 
    // do it some other way, if so, we also gotta validate for that
    if (handleErrors)
    {
        // if there are no errors remove any existing error displaying DOM and return true,
        // indicating the inputs are valid
        if (isEmpty(errors))
        {
            removeValdiationErrors();
            return true;
        }

        // if not, we handle those DOM those errors and return false,
        // indicating the inputs aren't valid
        handleValidationErrors(errors);

        return false;   
    }
    
    return isEmpty(errors);
};