kendo.validator.js 21.6 KB
/** 
 * Kendo UI v2016.1.112 (http://www.telerik.com/kendo-ui)                                                                                                                                               
 * Copyright 2016 Telerik AD. All rights reserved.                                                                                                                                                      
 *                                                                                                                                                                                                      
 * Kendo UI commercial licenses may be obtained at                                                                                                                                                      
 * http://www.telerik.com/purchase/license-agreement/kendo-ui-complete                                                                                                                                  
 * If you do not own a commercial license, this file shall be governed by the trial license terms.                                                                                                      
                                                                                                                                                                                                       
                                                                                                                                                                                                       
                                                                                                                                                                                                       
                                                                                                                                                                                                       
                                                                                                                                                                                                       
                                                                                                                                                                                                       
                                                                                                                                                                                                       
                                                                                                                                                                                                       
                                                                                                                                                                                                       
                                                                                                                                                                                                       
                                                                                                                                                                                                       
                                                                                                                                                                                                       
                                                                                                                                                                                                       
                                                                                                                                                                                                       
                                                                                                                                                                                                       

*/
(function (f, define) {
    define('kendo.validator', ['kendo.core'], f);
}(function () {
    var __meta__ = {
        id: 'validator',
        name: 'Validator',
        category: 'web',
        description: 'The Validator offers an easy way to do a client-side form validation.',
        depends: ['core']
    };
    (function ($, undefined) {
        var kendo = window.kendo, Widget = kendo.ui.Widget, NS = '.kendoValidator', INVALIDMSG = 'k-invalid-msg', invalidMsgRegExp = new RegExp(INVALIDMSG, 'i'), INVALIDINPUT = 'k-invalid', VALIDINPUT = 'k-valid', emailRegExp = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([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])))$/i, urlRegExp = /^(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, INPUTSELECTOR = ':input:not(:button,[type=submit],[type=reset],[disabled],[readonly])', CHECKBOXSELECTOR = ':checkbox:not([disabled],[readonly])', NUMBERINPUTSELECTOR = '[type=number],[type=range]', BLUR = 'blur', NAME = 'name', FORM = 'form', NOVALIDATE = 'novalidate', proxy = $.proxy, patternMatcher = function (value, pattern) {
                if (typeof pattern === 'string') {
                    pattern = new RegExp('^(?:' + pattern + ')$');
                }
                return pattern.test(value);
            }, matcher = function (input, selector, pattern) {
                var value = input.val();
                if (input.filter(selector).length && value !== '') {
                    return patternMatcher(value, pattern);
                }
                return true;
            }, hasAttribute = function (input, name) {
                if (input.length) {
                    return input[0].attributes[name] != null;
                }
                return false;
            };
        if (!kendo.ui.validator) {
            kendo.ui.validator = {
                rules: {},
                messages: {}
            };
        }
        function resolveRules(element) {
            var resolvers = kendo.ui.validator.ruleResolvers || {}, rules = {}, name;
            for (name in resolvers) {
                $.extend(true, rules, resolvers[name].resolve(element));
            }
            return rules;
        }
        function decode(value) {
            return value.replace(/&amp/g, '&amp;').replace(/&quot;/g, '"').replace(/&#39;/g, '\'').replace(/&lt;/g, '<').replace(/&gt;/g, '>');
        }
        function numberOfDecimalDigits(value) {
            value = (value + '').split('.');
            if (value.length > 1) {
                return value[1].length;
            }
            return 0;
        }
        function parseHtml(text) {
            if ($.parseHTML) {
                return $($.parseHTML(text));
            }
            return $(text);
        }
        function searchForMessageContainer(elements, fieldName) {
            var containers = $(), element, attr;
            for (var idx = 0, length = elements.length; idx < length; idx++) {
                element = elements[idx];
                if (invalidMsgRegExp.test(element.className)) {
                    attr = element.getAttribute(kendo.attr('for'));
                    if (attr === fieldName) {
                        containers = containers.add(element);
                    }
                }
            }
            return containers;
        }
        var Validator = Widget.extend({
            init: function (element, options) {
                var that = this, resolved = resolveRules(element), validateAttributeSelector = '[' + kendo.attr('validate') + '!=false]';
                options = options || {};
                options.rules = $.extend({}, kendo.ui.validator.rules, resolved.rules, options.rules);
                options.messages = $.extend({}, kendo.ui.validator.messages, resolved.messages, options.messages);
                Widget.fn.init.call(that, element, options);
                that._errorTemplate = kendo.template(that.options.errorTemplate);
                if (that.element.is(FORM)) {
                    that.element.attr(NOVALIDATE, NOVALIDATE);
                }
                that._inputSelector = INPUTSELECTOR + validateAttributeSelector;
                that._checkboxSelector = CHECKBOXSELECTOR + validateAttributeSelector;
                that._errors = {};
                that._attachEvents();
                that._isValidated = false;
            },
            events: [
                'validate',
                'change'
            ],
            options: {
                name: 'Validator',
                errorTemplate: '<span class="k-widget k-tooltip k-tooltip-validation">' + '<span class="k-icon k-warning"> </span> #=message#</span>',
                messages: {
                    required: '{0} is required',
                    pattern: '{0} is not valid',
                    min: '{0} should be greater than or equal to {1}',
                    max: '{0} should be smaller than or equal to {1}',
                    step: '{0} is not valid',
                    email: '{0} is not valid email',
                    url: '{0} is not valid URL',
                    date: '{0} is not valid date',
                    dateCompare: 'End date should be greater than or equal to the start date'
                },
                rules: {
                    required: function (input) {
                        var checkbox = input.filter('[type=checkbox]').length && !input.is(':checked'), value = input.val();
                        return !(hasAttribute(input, 'required') && (value === '' || !value || checkbox));
                    },
                    pattern: function (input) {
                        if (input.filter('[type=text],[type=email],[type=url],[type=tel],[type=search],[type=password]').filter('[pattern]').length && input.val() !== '') {
                            return patternMatcher(input.val(), input.attr('pattern'));
                        }
                        return true;
                    },
                    min: function (input) {
                        if (input.filter(NUMBERINPUTSELECTOR + ',[' + kendo.attr('type') + '=number]').filter('[min]').length && input.val() !== '') {
                            var min = parseFloat(input.attr('min')) || 0, val = kendo.parseFloat(input.val());
                            return min <= val;
                        }
                        return true;
                    },
                    max: function (input) {
                        if (input.filter(NUMBERINPUTSELECTOR + ',[' + kendo.attr('type') + '=number]').filter('[max]').length && input.val() !== '') {
                            var max = parseFloat(input.attr('max')) || 0, val = kendo.parseFloat(input.val());
                            return max >= val;
                        }
                        return true;
                    },
                    step: function (input) {
                        if (input.filter(NUMBERINPUTSELECTOR + ',[' + kendo.attr('type') + '=number]').filter('[step]').length && input.val() !== '') {
                            var min = parseFloat(input.attr('min')) || 0, step = parseFloat(input.attr('step')) || 1, val = parseFloat(input.val()), decimals = numberOfDecimalDigits(step), raise;
                            if (decimals) {
                                raise = Math.pow(10, decimals);
                                return Math.floor((val - min) * raise) % (step * raise) / Math.pow(100, decimals) === 0;
                            }
                            return (val - min) % step === 0;
                        }
                        return true;
                    },
                    email: function (input) {
                        return matcher(input, '[type=email],[' + kendo.attr('type') + '=email]', emailRegExp);
                    },
                    url: function (input) {
                        return matcher(input, '[type=url],[' + kendo.attr('type') + '=url]', urlRegExp);
                    },
                    date: function (input) {
                        if (input.filter('[type^=date],[' + kendo.attr('type') + '=date]').length && input.val() !== '') {
                            return kendo.parseDate(input.val(), input.attr(kendo.attr('format'))) !== null;
                        }
                        return true;
                    }
                },
                validateOnBlur: true
            },
            destroy: function () {
                Widget.fn.destroy.call(this);
                this.element.off(NS);
            },
            value: function () {
                if (!this._isValidated) {
                    return false;
                }
                return this.errors().length === 0;
            },
            _submit: function (e) {
                if (!this.validate()) {
                    e.stopPropagation();
                    e.stopImmediatePropagation();
                    e.preventDefault();
                    return false;
                }
                return true;
            },
            _checkElement: function (element) {
                var state = this.value();
                this.validateInput(element);
                if (this.value() !== state) {
                    this.trigger('change');
                }
            },
            _attachEvents: function () {
                var that = this;
                if (that.element.is(FORM)) {
                    that.element.on('submit' + NS, proxy(that._submit, that));
                }
                if (that.options.validateOnBlur) {
                    if (!that.element.is(INPUTSELECTOR)) {
                        that.element.on(BLUR + NS, that._inputSelector, function () {
                            that._checkElement($(this));
                        });
                        that.element.on('click' + NS, that._checkboxSelector, function () {
                            that._checkElement($(this));
                        });
                    } else {
                        that.element.on(BLUR + NS, function () {
                            that._checkElement(that.element);
                        });
                        if (that.element.is(CHECKBOXSELECTOR)) {
                            that.element.on('click' + NS, function () {
                                that._checkElement(that.element);
                            });
                        }
                    }
                }
            },
            validate: function () {
                var inputs;
                var idx;
                var result = false;
                var length;
                var isValid = this.value();
                this._errors = {};
                if (!this.element.is(INPUTSELECTOR)) {
                    var invalid = false;
                    inputs = this.element.find(this._inputSelector);
                    for (idx = 0, length = inputs.length; idx < length; idx++) {
                        if (!this.validateInput(inputs.eq(idx))) {
                            invalid = true;
                        }
                    }
                    result = !invalid;
                } else {
                    result = this.validateInput(this.element);
                }
                this.trigger('validate', { valid: result });
                if (isValid !== result) {
                    this.trigger('change');
                }
                return result;
            },
            validateInput: function (input) {
                input = $(input);
                this._isValidated = true;
                var that = this, template = that._errorTemplate, result = that._checkValidity(input), valid = result.valid, className = '.' + INVALIDMSG, fieldName = input.attr(NAME) || '', lbl = that._findMessageContainer(fieldName).add(input.next(className).filter(function () {
                        var element = $(this);
                        if (element.filter('[' + kendo.attr('for') + ']').length) {
                            return element.attr(kendo.attr('for')) === fieldName;
                        }
                        return true;
                    })).hide(), messageText;
                input.removeAttr('aria-invalid');
                if (!valid) {
                    messageText = that._extractMessage(input, result.key);
                    that._errors[fieldName] = messageText;
                    var messageLabel = parseHtml(template({ message: decode(messageText) }));
                    var lblId = lbl.attr('id');
                    that._decorateMessageContainer(messageLabel, fieldName);
                    if (lblId) {
                        messageLabel.attr('id', lblId);
                    }
                    if (!lbl.replaceWith(messageLabel).length) {
                        messageLabel.insertAfter(input);
                    }
                    messageLabel.show();
                    input.attr('aria-invalid', true);
                } else {
                    delete that._errors[fieldName];
                }
                input.toggleClass(INVALIDINPUT, !valid);
                input.toggleClass(VALIDINPUT, valid);
                return valid;
            },
            hideMessages: function () {
                var that = this, className = '.' + INVALIDMSG, element = that.element;
                if (!element.is(INPUTSELECTOR)) {
                    element.find(className).hide();
                } else {
                    element.next(className).hide();
                }
            },
            _findMessageContainer: function (fieldName) {
                var locators = kendo.ui.validator.messageLocators, name, containers = $();
                for (var idx = 0, length = this.element.length; idx < length; idx++) {
                    containers = containers.add(searchForMessageContainer(this.element[idx].getElementsByTagName('*'), fieldName));
                }
                for (name in locators) {
                    containers = containers.add(locators[name].locate(this.element, fieldName));
                }
                return containers;
            },
            _decorateMessageContainer: function (container, fieldName) {
                var locators = kendo.ui.validator.messageLocators, name;
                container.addClass(INVALIDMSG).attr(kendo.attr('for'), fieldName || '');
                for (name in locators) {
                    locators[name].decorate(container, fieldName);
                }
                container.attr('role', 'alert');
            },
            _extractMessage: function (input, ruleKey) {
                var that = this, customMessage = that.options.messages[ruleKey], fieldName = input.attr(NAME);
                customMessage = kendo.isFunction(customMessage) ? customMessage(input) : customMessage;
                return kendo.format(input.attr(kendo.attr(ruleKey + '-msg')) || input.attr('validationMessage') || input.attr('title') || customMessage || '', fieldName, input.attr(ruleKey));
            },
            _checkValidity: function (input) {
                var rules = this.options.rules, rule;
                for (rule in rules) {
                    if (!rules[rule].call(this, input)) {
                        return {
                            valid: false,
                            key: rule
                        };
                    }
                }
                return { valid: true };
            },
            errors: function () {
                var results = [], errors = this._errors, error;
                for (error in errors) {
                    results.push(errors[error]);
                }
                return results;
            }
        });
        kendo.ui.plugin(Validator);
    }(window.kendo.jQuery));
    return window.kendo;
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
    (a3 || a2)();
}));