﻿// plugin inspirovan jednak popisem staveni pluginu: http://www.learningjquery.com/2007/10/a-plugin-development-pattern
// dale pak pokrocilym validatorem: http://www.position-absolute.com/articles/jquery-form-validator-because-form-validation-is-a-mess/
// tady via download link a pak jsou vybrany casti kodu z jquery.validationEngine-en.js

// samotny plugin:
// create closure
(function ($) {
    //
    // plugin definition
    //

    $.fn.validate = function (options) {

        // debug(this);
        // build main options before element iteration
        options = $.extend({}, $.fn.validate.defaults, options);

        // this variable is set to true if an error is found
        var errorFound = false;

        var errorMessages = new Array();

        var firstForm = $($(this)[0]);

        var errorBox = $(options.errorBoxId);
        if (errorBox.length == 0 && options.showError) {
            options.errorBoxId = '_' + firstForm.attr('id') + '_validate_errorBox';
            errorBox = $('#' + options.errorBoxId);
            if (errorBox.length == 0) {
                errorBox = $('<div id="' + options.errorBoxId + '" class="validate-errorBox"></div>').insertBefore(firstForm);
            }
        }

        // iterate and reformat each matched element
        this.each(function () {
            form = $(this);



            // first, evaluate status of non ajax fields
            form.find('[class*=validate]').not(':hidden').each(function () {
                var field = $(this);

                if (_validateField(field, options)) {
                    // pokud jde o prvni chybu, posilame na ni focus...
                    if (!errorFound && options.setFocus) field.focus();

                    errorFound = true;

                    errorMessages.push(field.attr('title'));

                }
            });

        });

        if (errorFound) {
            if (options.showError) {
                var errorHtml = "<ul>";

                var uniqueErrrorMesages = errorMessages.unique();
                for (var a = 0, b = uniqueErrrorMesages.length; a < b; a++) {
                    errorHtml += "<li>" + uniqueErrrorMesages[a] + "</li>";
                }

                errorHtml += "</ul>";
                errorBox.html(errorHtml).show();
            }
            return false;
        }
        else {
            errorBox.hide();
        }

        if (options.hideErrorBox) errorBox.hide();
        return true;

    };

    function _validateField(field, options) {
        // rozparsujeme sadu pravidel - trida se sklada z validation[pravidlo] - pokud neni v [] nic specifikovano, preskakujeme
        var rulesParsing = field.attr('class');
        var getRules = /\[(.*)\]/.exec(rulesParsing);
        if (getRules === null)
            return false;
        var str = getRules[1];
        var rules = str.split(/\[|,|\]/);

        var fieldName = field.attr('name');
        var isError = false;

        for (var i = 0; i < rules.length; i++) {
            var errorMsg = undefined;
            switch (rules[i]) {

                case "required":
                    //options.isError |= _required(field, rules, i, options);
                    if (_required(field, rules, i, options)) isError = true;
                    break;
                case "custom":
                    //errorMsg = methods._customRegex(field, rules, i, options);
                    if (_customRegex(field, rules, i, options)) isError = true;
                    break;
                case "minSize":
                    //isError |= _minSize(field, rules, i, options);
                    if (_minSize(field, rules, i, options)) isError = true;
                    break;
                case "maxSize":
                    isError |= _maxSize(field, rules, i, options);
                    break;
                case "min":
                    isError |= _min(field, rules, i, options);
                    break;
                case "max":
                    isError |= _max(field, rules, i, options);
                    break;
                case "past":
                    isError |= _past(field, rules, i, options);
                    break;
                case "future":
                    isError |= _future(field, rules, i, options);
                    break;
                case "maxCheckbox":
                    isError |= _maxCheckbox(field, rules, i, options);
                    field = $($("input[name='" + fieldName + "']"));
                    break;
                case "minCheckbox":
                    isError |= _minCheckbox(field, rules, i, options);
                    field = $($("input[name='" + fieldName + "']"));
                    break;
                case "equals":
                    isError |= _equals(field, rules, i, options);
                    break;
                case "funcCall":
                    isError |= _funcCall(field, rules, i, options);
                    break;
                default:
                    //$.error("jQueryValidator rule not found"+rules[i]);
            }

        }
        // Hack for radio/checkbox group button, the validation go into the
        // first radio/checkbox of the group
        var fieldType = field.attr("type");

        if ((fieldType == "radio" || fieldType == "checkbox") && $("input[name='" + fieldName + "']").size() > 1) {
            //field = $($("input[name='" + fieldName + "'][type!=hidden]:first"));
            //options.showArrow = false;

            if (isError) $($("input[name='" + fieldName + "'][type!=hidden]")).addClass('validation-error');
            else $($("input[name='" + fieldName + "'][type!=hidden]")).removeClass('validation-error');
        }
        else {
            if (isError) field.addClass('validation-error');
            else field.removeClass('validation-error');
        }


        return isError;

    }


    // vracime "is error?"
    function _required(field, rules, i, options) {

        switch (field.attr("type")) {
            case "text":
            case "password":
            case "textarea":
                if (!field.val())
                    return true;
                break;
            case "radio":
            case "checkbox":
                var name = field.attr("name");
                if ($("input[name='" + name + "']:checked").size() === 0) {
                    if ($("input[name='" + name + "']").size() === 1)
                        return true;
                    else
                        return true;
                }
                break;
            // required for <select>                                                                             
            case "select-one":
                // added by paul@kinetek.net for select boxes, Thank you
                if (!field.val())
                    return true;
                break;
            case "select-multiple":
                // added by paul@kinetek.net for select boxes, Thank you
                if (!field.find("option:selected").val())
                    return true;
                break;
        }
        return false;
    };


    /**
    * Validate Regex rules
    *
    * @param {jqObject} field
    * @param {Array[String]} rules
    * @param {int} i rules index
    * @param {Map}
    * user options
    * @return an error string if validation failed
    */
    function _customRegex(field, rules, i, options) {
        var customRule = rules[i + 1];
        var pattern = new RegExp(options.allRules[customRule].regex);

        if (!pattern.test(field.attr('value'))) return true;
        return false;
    }

    /**
    * Validate custom function outside of the engine scope
    *
    * @param {jqObject} field
    * @param {Array[String]} rules
    * @param {int} i rules index
    * @param {Map}
    * user options
    * @return an error string if validation failed
    */
    function _funcCall(field, rules, i, options) {
        var functionName = rules[i + 1];
        var fn = window[functionName];
        if (typeof (fn) === 'function')
            return fn(field, rules, i, options);

    }
    /**
    * Field match
    *
    * @param {jqObject} field
    * @param {Array[String]} rules
    * @param {int} i rules index
    * @param {Map}
    * user options
    * @return an error string if validation failed
    */
    function _equals(field, rules, i, options) {
        var equalsField = rules[i + 1];

        if (field.attr('value') != $("#" + equalsField).attr('value')) return true;
        else return false;
    }

    /**
    * Check the maximum size (in characters)
    *
    * @param {jqObject} field
    * @param {Array[String]} rules
    * @param {int} i rules index
    * @param {Map}
    * user options
    * @return an error string if validation failed
    */
    function _maxSize(field, rules, i, options) {
        var max = rules[i + 1];
        var len = field.attr('value').length;

        if (len > max && len > 0) return true;
        return false;
    }
    /**
    * Check the minimum size (in characters)
    *
    * @param {jqObject} field
    * @param {Array[String]} rules
    * @param {int} i rules index
    * @param {Map}
    * user options
    * @return an error string if validation failed
    */
    function _minSize(field, rules, i, options) {
        var min = rules[i + 1];
        var len = field.attr('value').length;

        if (len < min && len > 0) return true;
        return false;
    }
    /**
    * Check number minimum value
    *
    * @param {jqObject} field
    * @param {Array[String]} rules
    * @param {int} i rules index
    * @param {Map}
    * user options
    * @return an error string if validation failed
    */
    function _min(field, rules, i, options) {
        var min = parseFloat(rules[i + 1]);
        var len = parseFloat(field.attr('value'));

        if (len < min) return true;
        return false;
    }
    /**
    * Check number maximum value
    *
    * @param {jqObject} field
    * @param {Array[String]} rules
    * @param {int} i rules index
    * @param {Map}
    * user options
    * @return an error string if validation failed
    */
    function _max(field, rules, i, options) {
        var max = parseFloat(rules[i + 1]);
        var len = parseFloat(field.attr('value'));

        if (len > max) return true;
        return false;
    }
    /**
    * Checks date is in the past
    *
    * @param {jqObject} field
    * @param {Array[String]} rules
    * @param {int} i rules index
    * @param {Map}
    * user options
    * @return an error string if validation failed
    */
    function _past(field, rules, i, options) {

        var p = rules[i + 1];
        var pdate = (p.toLowerCase() == "now") ? new Date() : _parseDate(p);
        var vdate = _parseDate(field.attr('value'));

        if (vdate > pdate) return true;
        return false;
    }
    /**
    * Checks date is in the past
    *
    * @param {jqObject} field
    * @param {Array[String]} rules
    * @param {int} i rules index
    * @param {Map}
    * user options
    * @return an error string if validation failed
    */
    function _future(field, rules, i, options) {

        var p = rules[i + 1];
        var pdate = (p.toLowerCase() == "now") ? new Date() : _parseDate(p);
        var vdate = _parseDate(field.attr('value'));

        if (vdate < pdate) return true;
        return false;
    }
    /**
    * Max number of checkbox selected
    *
    * @param {jqObject} field
    * @param {Array[String]} rules
    * @param {int} i rules index
    * @param {Map}
    * user options
    * @return an error string if validation failed
    */
    function _maxCheckbox(field, rules, i, options) {

        var nbCheck = rules[i + 1];
        var groupname = field.attr("name");
        var groupSize = $("input[name='" + groupname + "']:checked").size();
        if (groupSize > nbCheck) return true;
        return false;
    }
    /**
    * Min number of checkbox selected
    *
    * @param {jqObject} field
    * @param {Array[String]} rules
    * @param {int} i rules index
    * @param {Map}
    * user options
    * @return an error string if validation failed
    */
    function _minCheckbox(field, rules, i, options) {

        var nbCheck = rules[i + 1];
        var groupname = field.attr("name");
        var groupSize = $("input[name='" + groupname + "']:checked").size();
        if (groupSize < nbCheck) return true;
        return false;
    }
    /**
    * date -> string
    *
    * @param {Object} date
    */
    function _dateToString(date) {
        return date.getDate() + '.' + (date.getMonth() + 1) + '.' + date.getFullYear();
    }
    /**
    * Parses an ISO date
    * @param {String} d
    */
    function _parseDate(d) {
        var dateParts = d.split(".");
        return new Date(dateParts[2], (dateParts[1] - 1), dateParts[0]);
    }

    /*
    //
    // private function for debugging
    //
    function debug($obj) {
    if (window.console && window.console.log)
    window.console.log('hilight selection count: ' + $obj.size());
    };
    //
    // define and expose our format function
    //
    $.fn.hilight.format = function (txt) {
    return '<strong>' + txt + '</strong>';
    };

    //
    // plugin defaults
    //
    $.fn.hilight.defaults = {
    foreground: 'red',
    background: 'yellow'
    };
    */

    $.fn.validate.defaults = {
        errorBoxId: null,
        showError: true,
        setFocus: true,
        hideErrorBox: false,
        allRules: {
            "required": { // Add your regex rules here, you can take telephone as an example
                "regex": "none",
                "alertText": "* This field is required",
                "alertTextCheckboxMultiple": "* Please select an option",
                "alertTextCheckboxe": "* This checkbox is required"
            },
            "minSize": {
                "regex": "none",
                "alertText": "* Minimum ",
                "alertText2": " characters allowed"
            },
            "maxSize": {
                "regex": "none",
                "alertText": "* Maximum ",
                "alertText2": " characters allowed"
            },
            "min": {
                "regex": "none",
                "alertText": "* Minimum value is "
            },
            "max": {
                "regex": "none",
                "alertText": "* Maximum value is "
            },
            "past": {
                "regex": "none",
                "alertText": "* Date prior to "
            },
            "future": {
                "regex": "none",
                "alertText": "* Date past "
            },
            "maxCheckbox": {
                "regex": "none",
                "alertText": "* Checks allowed Exceeded"
            },
            "minCheckbox": {
                "regex": "none",
                "alertText": "* Please select ",
                "alertText2": " options"
            },
            "equals": {
                "regex": "none",
                "alertText": "* Fields do not match"
            },
            "phone": {
                // credit: jquery.h5validate.js / orefalo
                "regex": /^(([\+][0-9]{3})?[0-9]{9})$/,
                "alertText": "* Invalid phone number"
            },
            "prefixphone": {
                // credit: jquery.h5validate.js / orefalo
                "regex": /^([\+][0-9]{12})$/,
                "alertText": "* Invalid phone number"
            },
            "email": {
                // Simplified, was not working in the Iphone browser
                "regex": /^(([A-Za-z0-9_\-\.\'])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,6}))?$/,
                "alertText": "* Invalid email address"
            },
            "integer": {
                "regex": /^[\-\+]?\d+$/,
                "alertText": "* Not a valid integer"
            },
            "number": {
                // Number, including positive, negative, and floating decimal. credit: orefalo
                "regex": /^[\-\+]?(([0-9]+)([\.,]([0-9]+))?|([\.,]([0-9]+))?)$/,
                "alertText": "* Invalid floating decimal number"
            },
            "psc": {
                "regex": /^[0-9]{3}\ ?[0-9]{2}$/,
                "alertText": "* Špatně zadané PSČ"
            },
            "date": {
                // Date in ISO format. Credit: bassistance
                //"regex": /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/,
                "regex": /^(\d{1,2}\.\s?\d{1,2}\.\s?\d{2,4})?$/,
                "alertText": "* Invalid date, must be in YYYY-MM-DD format"
            },
            "datetime": {
                // Date in ISO format. Credit: bassistance
                //"regex": /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/,
                "regex": /^(\d{1,2}\.\s?\d{1,2}\.\s?\d{2,4}(\s\d{1,2}:\d{1,2}(:\d{1,2})?)?)?$/,
                "alertText": "* Invalid date, must be in YYYY-MM-DD format"
            },
            "ipv4": {
                "regex": /^([1-9][0-9]{0,2})+\.([1-9][0-9]{0,2})+\.([1-9][0-9]{0,2})+\.([1-9][0-9]{0,2})+$/,
                "alertText": "* Invalid IP address"
            },
            "url": {
                "regex": /^((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})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?)?$/,
                "alertText": "* Invalid URL"
            },
            "onlyNumberSp": {
                "regex": /^[0-9\ ]*$/,
                "alertText": "* Numbers only"
            },
            "onlyNumber": {
                "regex": /^[0-9]*$/,
                "alertText": "* Numbers only"
            },
            "onlyLetterSp": {
                "regex": /^[a-zA-Z\ \']*$/,
                "alertText": "* Letters only"
            },
            "onlyLetterNumber": {
                "regex": /^[0-9a-zA-Z]*$/,
                "alertText": "* No special characters allowed"
            },
            // --- CUSTOM RULES -- Those are specific to the demos, they can be removed or changed to your likings
            "ajaxUserCall": {
                "url": "ajaxValidateFieldUser",
                // you may want to pass extra data on the ajax call
                "extraData": "name=eric",
                "alertText": "* This user is already taken",
                "alertTextLoad": "* Validating, please wait"
            },
            "ajaxNameCall": {
                // remote json service location
                "url": "ajaxValidateFieldName",
                // error
                "alertText": "* This name is already taken",
                // if you provide an "alertTextOk", it will show as a green prompt when the field validates
                "alertTextOk": "* This name is available",
                // speaks by itself
                "alertTextLoad": "* Validating, please wait"
            },
            "validate2fields": {
                "alertText": "* Please input HELLO"
            }
        }

    }



    // end of closure
})(jQuery);

// Array.indexOf( value, begin, strict ) - Return index of the first element that matches value
Array.prototype.indexOf = function (v, b, s) {
    for (var i = +b || 0, l = this.length; i < l; i++) {
        if (this[i] === v || s && this[i] == v) { return i; }
    }
    return -1;
};

Array.prototype.unique = function (b) {
    var a = new Array(), i, l = this.length;
    for (i = 0; i < l; i++) {
        if (a.indexOf(this[i], 0, b) < 0) { a.push(this[i]); }
    }
    return a;
}


