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