PageRenderTime 80ms CodeModel.GetById 25ms app.highlight 48ms RepoModel.GetById 1ms app.codeStats 1ms

/Scripts/jquery.validate.unobtrusive.js

https://bitbucket.org/parivedasolutions/attributeroutingtest
JavaScript | 367 lines | 355 code | 5 blank | 7 comment | 1 complexity | 5f820b3be143ca8a5fdaaaa6a464f8c9 MD5 | raw file
  1/*!
  2** Unobtrusive validation support library for jQuery and jQuery Validate
  3** Copyright (C) Microsoft Corporation. All rights reserved.
  4*/
  5
  6/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */
  7/*global document: false, jQuery: false */
  8
  9(function ($) {
 10    var $jQval = $.validator,
 11        adapters,
 12        data_validation = "unobtrusiveValidation";
 13
 14    function setValidationValues(options, ruleName, value) {
 15        options.rules[ruleName] = value;
 16        if (options.message) {
 17            options.messages[ruleName] = options.message;
 18        }
 19    }
 20
 21    function splitAndTrim(value) {
 22        return value.replace(/^\s+|\s+$/g, "").split(/\s*,\s*/g);
 23    }
 24
 25    function escapeAttributeValue(value) {
 26        // As mentioned on http://api.jquery.com/category/selectors/
 27        return value.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g, "\\$1");
 28    }
 29
 30    function getModelPrefix(fieldName) {
 31        return fieldName.substr(0, fieldName.lastIndexOf(".") + 1);
 32    }
 33
 34    function appendModelPrefix(value, prefix) {
 35        if (value.indexOf("*.") === 0) {
 36            value = value.replace("*.", prefix);
 37        }
 38        return value;
 39    }
 40
 41    function onError(error, inputElement) {  // 'this' is the form element
 42        var container = $(this).find("[data-valmsg-for='" + escapeAttributeValue(inputElement[0].name) + "']"),
 43            replaceAttrValue = container.attr("data-valmsg-replace"),
 44            replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null;
 45
 46        container.removeClass("field-validation-valid").addClass("field-validation-error");
 47        error.data("unobtrusiveContainer", container);
 48
 49        if (replace) {
 50            container.empty();
 51            error.removeClass("input-validation-error").appendTo(container);
 52        }
 53        else {
 54            error.hide();
 55        }
 56    }
 57
 58    function onErrors(event, validator) {  // 'this' is the form element
 59        var container = $(this).find("[data-valmsg-summary=true]"),
 60            list = container.find("ul");
 61
 62        if (list && list.length && validator.errorList.length) {
 63            list.empty();
 64            container.addClass("validation-summary-errors").removeClass("validation-summary-valid");
 65
 66            $.each(validator.errorList, function () {
 67                $("<li />").html(this.message).appendTo(list);
 68            });
 69        }
 70    }
 71
 72    function onSuccess(error) {  // 'this' is the form element
 73        var container = error.data("unobtrusiveContainer"),
 74            replaceAttrValue = container.attr("data-valmsg-replace"),
 75            replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) : null;
 76
 77        if (container) {
 78            container.addClass("field-validation-valid").removeClass("field-validation-error");
 79            error.removeData("unobtrusiveContainer");
 80
 81            if (replace) {
 82                container.empty();
 83            }
 84        }
 85    }
 86
 87    function onReset(event) {  // 'this' is the form element
 88        var $form = $(this);
 89        $form.data("validator").resetForm();
 90        $form.find(".validation-summary-errors")
 91            .addClass("validation-summary-valid")
 92            .removeClass("validation-summary-errors");
 93        $form.find(".field-validation-error")
 94            .addClass("field-validation-valid")
 95            .removeClass("field-validation-error")
 96            .removeData("unobtrusiveContainer")
 97            .find(">*")  // If we were using valmsg-replace, get the underlying error
 98                .removeData("unobtrusiveContainer");
 99    }
100
101    function validationInfo(form) {
102        var $form = $(form),
103            result = $form.data(data_validation),
104            onResetProxy = $.proxy(onReset, form);
105
106        if (!result) {
107            result = {
108                options: {  // options structure passed to jQuery Validate's validate() method
109                    errorClass: "input-validation-error",
110                    errorElement: "span",
111                    errorPlacement: $.proxy(onError, form),
112                    invalidHandler: $.proxy(onErrors, form),
113                    messages: {},
114                    rules: {},
115                    success: $.proxy(onSuccess, form)
116                },
117                attachValidation: function () {
118                    $form
119                        .unbind("reset." + data_validation, onResetProxy)
120                        .bind("reset." + data_validation, onResetProxy)
121                        .validate(this.options);
122                },
123                validate: function () {  // a validation function that is called by unobtrusive Ajax
124                    $form.validate();
125                    return $form.valid();
126                }
127            };
128            $form.data(data_validation, result);
129        }
130
131        return result;
132    }
133
134    $jQval.unobtrusive = {
135        adapters: [],
136
137        parseElement: function (element, skipAttach) {
138            /// <summary>
139            /// Parses a single HTML element for unobtrusive validation attributes.
140            /// </summary>
141            /// <param name="element" domElement="true">The HTML element to be parsed.</param>
142            /// <param name="skipAttach" type="Boolean">[Optional] true to skip attaching the
143            /// validation to the form. If parsing just this single element, you should specify true.
144            /// If parsing several elements, you should specify false, and manually attach the validation
145            /// to the form when you are finished. The default is false.</param>
146            var $element = $(element),
147                form = $element.parents("form")[0],
148                valInfo, rules, messages;
149
150            if (!form) {  // Cannot do client-side validation without a form
151                return;
152            }
153
154            valInfo = validationInfo(form);
155            valInfo.options.rules[element.name] = rules = {};
156            valInfo.options.messages[element.name] = messages = {};
157
158            $.each(this.adapters, function () {
159                var prefix = "data-val-" + this.name,
160                    message = $element.attr(prefix),
161                    paramValues = {};
162
163                if (message !== undefined) {  // Compare against undefined, because an empty message is legal (and falsy)
164                    prefix += "-";
165
166                    $.each(this.params, function () {
167                        paramValues[this] = $element.attr(prefix + this);
168                    });
169
170                    this.adapt({
171                        element: element,
172                        form: form,
173                        message: message,
174                        params: paramValues,
175                        rules: rules,
176                        messages: messages
177                    });
178                }
179            });
180
181            $.extend(rules, { "__dummy__": true });
182
183            if (!skipAttach) {
184                valInfo.attachValidation();
185            }
186        },
187
188        parse: function (selector) {
189            /// <summary>
190            /// Parses all the HTML elements in the specified selector. It looks for input elements decorated
191            /// with the [data-val=true] attribute value and enables validation according to the data-val-*
192            /// attribute values.
193            /// </summary>
194            /// <param name="selector" type="String">Any valid jQuery selector.</param>
195            var $forms = $(selector)
196                .parents("form")
197                .andSelf()
198                .add($(selector).find("form"))
199                .filter("form");
200
201            $(selector).find(":input[data-val=true]").each(function () {
202                $jQval.unobtrusive.parseElement(this, true);
203            });
204
205            $forms.each(function () {
206                var info = validationInfo(this);
207                if (info) {
208                    info.attachValidation();
209                }
210            });
211        }
212    };
213
214    adapters = $jQval.unobtrusive.adapters;
215
216    adapters.add = function (adapterName, params, fn) {
217        /// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation.</summary>
218        /// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
219        /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>
220        /// <param name="params" type="Array" optional="true">[Optional] An array of parameter names (strings) that will
221        /// be extracted from the data-val-nnnn-mmmm HTML attributes (where nnnn is the adapter name, and
222        /// mmmm is the parameter name).</param>
223        /// <param name="fn" type="Function">The function to call, which adapts the values from the HTML
224        /// attributes into jQuery Validate rules and/or messages.</param>
225        /// <returns type="jQuery.validator.unobtrusive.adapters" />
226        if (!fn) {  // Called with no params, just a function
227            fn = params;
228            params = [];
229        }
230        this.push({ name: adapterName, params: params, adapt: fn });
231        return this;
232    };
233
234    adapters.addBool = function (adapterName, ruleName) {
235        /// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where
236        /// the jQuery Validate validation rule has no parameter values.</summary>
237        /// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
238        /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>
239        /// <param name="ruleName" type="String" optional="true">[Optional] The name of the jQuery Validate rule. If not provided, the value
240        /// of adapterName will be used instead.</param>
241        /// <returns type="jQuery.validator.unobtrusive.adapters" />
242        return this.add(adapterName, function (options) {
243            setValidationValues(options, ruleName || adapterName, true);
244        });
245    };
246
247    adapters.addMinMax = function (adapterName, minRuleName, maxRuleName, minMaxRuleName, minAttribute, maxAttribute) {
248        /// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where
249        /// the jQuery Validate validation has three potential rules (one for min-only, one for max-only, and
250        /// one for min-and-max). The HTML parameters are expected to be named -min and -max.</summary>
251        /// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
252        /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>
253        /// <param name="minRuleName" type="String">The name of the jQuery Validate rule to be used when you only
254        /// have a minimum value.</param>
255        /// <param name="maxRuleName" type="String">The name of the jQuery Validate rule to be used when you only
256        /// have a maximum value.</param>
257        /// <param name="minMaxRuleName" type="String">The name of the jQuery Validate rule to be used when you
258        /// have both a minimum and maximum value.</param>
259        /// <param name="minAttribute" type="String" optional="true">[Optional] The name of the HTML attribute that
260        /// contains the minimum value. The default is "min".</param>
261        /// <param name="maxAttribute" type="String" optional="true">[Optional] The name of the HTML attribute that
262        /// contains the maximum value. The default is "max".</param>
263        /// <returns type="jQuery.validator.unobtrusive.adapters" />
264        return this.add(adapterName, [minAttribute || "min", maxAttribute || "max"], function (options) {
265            var min = options.params.min,
266                max = options.params.max;
267
268            if (min && max) {
269                setValidationValues(options, minMaxRuleName, [min, max]);
270            }
271            else if (min) {
272                setValidationValues(options, minRuleName, min);
273            }
274            else if (max) {
275                setValidationValues(options, maxRuleName, max);
276            }
277        });
278    };
279
280    adapters.addSingleVal = function (adapterName, attribute, ruleName) {
281        /// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where
282        /// the jQuery Validate validation rule has a single value.</summary>
283        /// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
284        /// in the data-val-nnnn HTML attribute(where nnnn is the adapter name).</param>
285        /// <param name="attribute" type="String">[Optional] The name of the HTML attribute that contains the value.
286        /// The default is "val".</param>
287        /// <param name="ruleName" type="String" optional="true">[Optional] The name of the jQuery Validate rule. If not provided, the value
288        /// of adapterName will be used instead.</param>
289        /// <returns type="jQuery.validator.unobtrusive.adapters" />
290        return this.add(adapterName, [attribute || "val"], function (options) {
291            setValidationValues(options, ruleName || adapterName, options.params[attribute]);
292        });
293    };
294
295    $jQval.addMethod("__dummy__", function (value, element, params) {
296        return true;
297    });
298
299    $jQval.addMethod("regex", function (value, element, params) {
300        var match;
301        if (this.optional(element)) {
302            return true;
303        }
304
305        match = new RegExp(params).exec(value);
306        return (match && (match.index === 0) && (match[0].length === value.length));
307    });
308
309    $jQval.addMethod("nonalphamin", function (value, element, nonalphamin) {
310        var match;
311        if (nonalphamin) {
312            match = value.match(/\W/g);
313            match = match && match.length >= nonalphamin;
314        }
315        return match;
316    });
317
318    adapters.addSingleVal("accept", "exts").addSingleVal("regex", "pattern");
319    adapters.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url");
320    adapters.addMinMax("length", "minlength", "maxlength", "rangelength").addMinMax("range", "min", "max", "range");
321    adapters.add("equalto", ["other"], function (options) {
322        var prefix = getModelPrefix(options.element.name),
323            other = options.params.other,
324            fullOtherName = appendModelPrefix(other, prefix),
325            element = $(options.form).find(":input[name='" + escapeAttributeValue(fullOtherName) + "']")[0];
326
327        setValidationValues(options, "equalTo", element);
328    });
329    adapters.add("required", function (options) {
330        // jQuery Validate equates "required" with "mandatory" for checkbox elements
331        if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") {
332            setValidationValues(options, "required", true);
333        }
334    });
335    adapters.add("remote", ["url", "type", "additionalfields"], function (options) {
336        var value = {
337            url: options.params.url,
338            type: options.params.type || "GET",
339            data: {}
340        },
341            prefix = getModelPrefix(options.element.name);
342
343        $.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) {
344            var paramName = appendModelPrefix(fieldName, prefix);
345            value.data[paramName] = function () {
346                return $(options.form).find(":input[name='" + escapeAttributeValue(paramName) + "']").val();
347            };
348        });
349
350        setValidationValues(options, "remote", value);
351    });
352    adapters.add("password", ["min", "nonalphamin", "regex"], function (options) {
353        if (options.params.min) {
354            setValidationValues(options, "minlength", options.params.min);
355        }
356        if (options.params.nonalphamin) {
357            setValidationValues(options, "nonalphamin", options.params.nonalphamin);
358        }
359        if (options.params.regex) {
360            setValidationValues(options, "regex", options.params.regex);
361        }
362    });
363
364    $(function () {
365        $jQval.unobtrusive.parse(document);
366    });
367} (jQuery));