blob: ef398db2d2d184f410aadfffbec1d1dd5b5f3e45 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/**
2 * jQuery Validation Plugin 1.9.0
3 *
4 * http://bassistance.de/jquery-plugins/jquery-plugin-validation/
5 * http://docs.jquery.com/Plugins/Validation
6 *
7 * Copyright (c) 2006 - 2011 Jörn Zaefferer
8 *
9 * Dual licensed under the MIT and GPL licenses:
10 * http://www.opensource.org/licenses/mit-license.php
11 * http://www.gnu.org/licenses/gpl.html
12 */
13
14(function($) {
15
16$.extend($.fn, {
17 // http://docs.jquery.com/Plugins/Validation/validate
18 validate: function( options ) {
19
20 // if nothing is selected, return nothing; can't chain anyway
21 if (!this.length) {
22 options && options.debug && window.console && console.warn( "nothing selected, can't validate, returning nothing" );
23 return;
24 }
25
26 // check if a validator for this form was already created
27 var validator = $.data(this[0], 'validator');
28 if ( validator ) {
29 return validator;
30 }
31
32 // Add novalidate tag if HTML5.
33 this.attr('novalidate', 'novalidate');
34
35 validator = new $.validator( options, this[0] );
36 $.data(this[0], 'validator', validator);
37
38 if ( validator.settings.onsubmit ) {
39
40 var inputsAndButtons = this.find("input, button");
41
42 // allow suppresing validation by adding a cancel class to the submit button
43 inputsAndButtons.filter(".cancel").click(function () {
44 validator.cancelSubmit = true;
45 });
46
47 // when a submitHandler is used, capture the submitting button
48 if (validator.settings.submitHandler) {
49 inputsAndButtons.filter(":submit").click(function () {
50 validator.submitButton = this;
51 });
52 }
53
54 // validate the form on submit
55 this.submit( function( event ) {
56 if ( validator.settings.debug )
57 // prevent form submit to be able to see console output
58 event.preventDefault();
59
60 function handle() {
61 if ( validator.settings.submitHandler ) {
62 if (validator.submitButton) {
63 // insert a hidden input as a replacement for the missing submit button
64 var hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val(validator.submitButton.value).appendTo(validator.currentForm);
65 }
66 validator.settings.submitHandler.call( validator, validator.currentForm );
67 if (validator.submitButton) {
68 // and clean up afterwards; thanks to no-block-scope, hidden can be referenced
69 hidden.remove();
70 }
71 return false;
72 }
73 return true;
74 }
75
76 // prevent submit for invalid forms or custom submit handlers
77 if ( validator.cancelSubmit ) {
78 validator.cancelSubmit = false;
79 return handle();
80 }
81 if ( validator.form() ) {
82 if ( validator.pendingRequest ) {
83 validator.formSubmitted = true;
84 return false;
85 }
86 return handle();
87 } else {
88 validator.focusInvalid();
89 return false;
90 }
91 });
92 }
93
94 return validator;
95 },
96 // http://docs.jquery.com/Plugins/Validation/valid
97 valid: function() {
98 if ( $(this[0]).is('form')) {
99 return this.validate().form();
100 } else {
101 var valid = true;
102 var validator = $(this[0].form).validate();
103 this.each(function() {
104 valid &= validator.element(this);
105 });
106 return valid;
107 }
108 },
109 // attributes: space seperated list of attributes to retrieve and remove
110 removeAttrs: function(attributes) {
111 var result = {},
112 $element = this;
113 $.each(attributes.split(/\s/), function(index, value) {
114 result[value] = $element.attr(value);
115 $element.removeAttr(value);
116 });
117 return result;
118 },
119 // http://docs.jquery.com/Plugins/Validation/rules
120 rules: function(command, argument) {
121 var element = this[0];
122
123 if (command) {
124 var settings = $.data(element.form, 'validator').settings;
125 var staticRules = settings.rules;
126 var existingRules = $.validator.staticRules(element);
127 switch(command) {
128 case "add":
129 $.extend(existingRules, $.validator.normalizeRule(argument));
130 staticRules[element.name] = existingRules;
131 if (argument.messages)
132 settings.messages[element.name] = $.extend( settings.messages[element.name], argument.messages );
133 break;
134 case "remove":
135 if (!argument) {
136 delete staticRules[element.name];
137 return existingRules;
138 }
139 var filtered = {};
140 $.each(argument.split(/\s/), function(index, method) {
141 filtered[method] = existingRules[method];
142 delete existingRules[method];
143 });
144 return filtered;
145 }
146 }
147
148 var data = $.validator.normalizeRules(
149 $.extend(
150 {},
151 $.validator.metadataRules(element),
152 $.validator.classRules(element),
153 $.validator.attributeRules(element),
154 $.validator.staticRules(element)
155 ), element);
156
157 // make sure required is at front
158 if (data.required) {
159 var param = data.required;
160 delete data.required;
161 data = $.extend({required: param}, data);
162 }
163
164 return data;
165 }
166});
167
168// Custom selectors
169$.extend($.expr[":"], {
170 // http://docs.jquery.com/Plugins/Validation/blank
171 blank: function(a) {return !$.trim("" + a.value);},
172 // http://docs.jquery.com/Plugins/Validation/filled
173 filled: function(a) {return !!$.trim("" + a.value);},
174 // http://docs.jquery.com/Plugins/Validation/unchecked
175 unchecked: function(a) {return !a.checked;}
176});
177
178// constructor for validator
179$.validator = function( options, form ) {
180 this.settings = $.extend( true, {}, $.validator.defaults, options );
181 this.currentForm = form;
182 this.init();
183};
184
185$.validator.format = function(source, params) {
186 if ( arguments.length == 1 )
187 return function() {
188 var args = $.makeArray(arguments);
189 args.unshift(source);
190 return $.validator.format.apply( this, args );
191 };
192 if ( arguments.length > 2 && params.constructor != Array ) {
193 params = $.makeArray(arguments).slice(1);
194 }
195 if ( params.constructor != Array ) {
196 params = [ params ];
197 }
198 $.each(params, function(i, n) {
199 //if params of {0}, {1} have translate the translate
200 if($.validator.messages[n]) {
201 n = $.i18n.prop(n);
202 }
203 source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
204 });
205 return source;
206};
207
208$.extend($.validator, {
209
210 defaults: {
211 messages: {},
212 groups: {},
213 rules: {},
214 errorClass: "error",
215 validClass: "valid",
216 errorElement: "label",
217 focusInvalid: true,
218 errorContainer: $( [] ),
219 errorLabelContainer: $( [] ),
220 onsubmit: true,
221 ignore: ":hidden",
222 ignoreTitle: false,
223 onfocusin: function(element, event) {
224 this.lastActive = element;
225
226 // hide error label and remove error class on focus if enabled
227 if ( this.settings.focusCleanup && !this.blockFocusCleanup ) {
228 this.settings.unhighlight && this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );
229 this.addWrapper(this.errorsFor(element)).hide();
230 }
231 },
232 onfocusout: function(element, event) {
233 if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) {
234 this.element(element);
235 }
236 },
237 onkeyup: function(element, event) {
238 if ( element.name in this.submitted || element == this.lastElement ) {
239 this.element(element);
240 }
241 },
242 onclick: function(element, event) {
243 // click on selects, radiobuttons and checkboxes
244 if ( element.name in this.submitted )
245 this.element(element);
246 // or option elements, check parent select in that case
247 else if (element.parentNode.name in this.submitted)
248 this.element(element.parentNode);
249 },
250 highlight: function(element, errorClass, validClass) {
251 if (element.type === 'radio') {
252 this.findByName(element.name).addClass(errorClass).removeClass(validClass);
253 } else {
254 $(element).addClass(errorClass).removeClass(validClass);
255 }
256 },
257 unhighlight: function(element, errorClass, validClass) {
258 if (element.type === 'radio') {
259 this.findByName(element.name).removeClass(errorClass).addClass(validClass);
260 } else {
261 $(element).removeClass(errorClass).addClass(validClass);
262 }
263 }
264 },
265
266 // http://docs.jquery.com/Plugins/Validation/Validator/setDefaults
267 setDefaults: function(settings) {
268 $.extend( $.validator.defaults, settings );
269 },
270
271 messages: {
272 required: "This field is required.",
273 remote: "Please fix this field.",
274 email: "Please enter a valid email address.",
275 url: "Please enter a valid URL.",
276 date: "Please enter a valid date.",
277 dateISO: "Please enter a valid date (ISO).",
278 number: "Please enter a valid number.",
279 digits: "Please enter only digits.",
280 creditcard: "Please enter a valid credit card number.",
281 equalTo: "Please enter the same value again.",
282 accept: "Please enter a value with a valid extension.",
283 maxlength: $.validator.format("Please enter no more than {0} characters."),
284 minlength: $.validator.format("Please enter at least {0} characters."),
285 rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."),
286 range: $.validator.format("Please enter a value between {0} and {1}."),
287 max: $.validator.format("Please enter a value less than or equal to {0}."),
288 min: $.validator.format("Please enter a value greater than or equal to {0}.")
289 },
290
291 autoCreateRanges: false,
292
293 prototype: {
294
295 init: function() {
296 this.labelContainer = $(this.settings.errorLabelContainer);
297 this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm);
298 this.containers = $(this.settings.errorContainer).add( this.settings.errorLabelContainer );
299 this.submitted = {};
300 this.valueCache = {};
301 this.pendingRequest = 0;
302 this.pending = {};
303 this.invalid = {};
304 this.reset();
305
306 var groups = (this.groups = {});
307 $.each(this.settings.groups, function(key, value) {
308 $.each(value.split(/\s/), function(index, name) {
309 groups[name] = key;
310 });
311 });
312 var rules = this.settings.rules;
313 $.each(rules, function(key, value) {
314 rules[key] = $.validator.normalizeRule(value);
315 });
316
317 function delegate(event) {
318 var validator = $.data(this[0].form, "validator"),
319 eventType = "on" + event.type.replace(/^validate/, "");
320 validator.settings[eventType] && validator.settings[eventType].call(validator, this[0], event);
321 }
322 $(this.currentForm)
323 .validateDelegate("[type='text'], [type='password'], [type='file'], select, textarea, " +
324 "[type='number'], [type='search'] ,[type='tel'], [type='url'], " +
325 "[type='email'], [type='datetime'], [type='date'], [type='month'], " +
326 "[type='week'], [type='time'], [type='datetime-local'], " +
327 "[type='range'], [type='color'] ",
328 "focusin focusout keyup", delegate)
329 .validateDelegate("[type='radio'], [type='checkbox'], select, option", "click", delegate);
330
331 if (this.settings.invalidHandler)
332 $(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);
333 },
334
335 // http://docs.jquery.com/Plugins/Validation/Validator/form
336 form: function() {
337 this.checkForm();
338 $.extend(this.submitted, this.errorMap);
339 this.invalid = $.extend({}, this.errorMap);
340 if (!this.valid())
341 $(this.currentForm).triggerHandler("invalid-form", [this]);
342 this.showErrors();
343 return this.valid();
344 },
345
346 checkForm: function() {
347 this.prepareForm();
348 for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) {
349 this.check( elements[i] );
350 }
351 return this.valid();
352 },
353
354 // http://docs.jquery.com/Plugins/Validation/Validator/element
355 element: function( element ) {
356 element = this.validationTargetFor( this.clean( element ) );
357 this.lastElement = element;
358 this.prepareElement( element );
359 this.currentElements = $(element);
360 var result = this.check( element );
361 if ( result ) {
362 delete this.invalid[element.name];
363 } else {
364 this.invalid[element.name] = true;
365 }
366 if ( !this.numberOfInvalids() ) {
367 // Hide error containers on last error
368 this.toHide = this.toHide.add( this.containers );
369 }
370 this.showErrors();
371 return result;
372 },
373
374 // http://docs.jquery.com/Plugins/Validation/Validator/showErrors
375 showErrors: function(errors) {
376 if(errors) {
377 // add items to error list and map
378 $.extend( this.errorMap, errors );
379 this.errorList = [];
380 for ( var name in errors ) {
381 this.errorList.push({
382 message: errors[name],
383 element: this.findByName(name)[0]
384 });
385 }
386 // remove items from success list
387 this.successList = $.grep( this.successList, function(element) {
388 return !(element.name in errors);
389 });
390 }
391 this.settings.showErrors
392 ? this.settings.showErrors.call( this, this.errorMap, this.errorList )
393 : this.defaultShowErrors();
394 },
395
396 // http://docs.jquery.com/Plugins/Validation/Validator/resetForm
397 resetForm: function() {
398 if ( $.fn.resetForm )
399 $( this.currentForm ).resetForm();
400 this.submitted = {};
401 this.lastElement = null;
402 this.prepareForm();
403 this.hideErrors();
404 this.elements().removeClass( this.settings.errorClass );
405 },
406
407 numberOfInvalids: function() {
408 return this.objectLength(this.invalid);
409 },
410
411 objectLength: function( obj ) {
412 var count = 0;
413 for ( var i in obj )
414 count++;
415 return count;
416 },
417
418 hideErrors: function() {
419 this.addWrapper( this.toHide ).hide();
420 },
421
422 valid: function() {
423 return this.size() == 0;
424 },
425
426 size: function() {
427 return this.errorList.length;
428 },
429
430 focusInvalid: function() {
431 if( this.settings.focusInvalid ) {
432 try {
433 $(this.findLastActive() || this.errorList.length && this.errorList[0].element || [])
434 .filter(":visible")
435 .focus()
436 // manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
437 .trigger("focusin");
438 } catch(e) {
439 // ignore IE throwing errors when focusing hidden elements
440 }
441 }
442 },
443
444 findLastActive: function() {
445 var lastActive = this.lastActive;
446 return lastActive && $.grep(this.errorList, function(n) {
447 return n.element.name == lastActive.name;
448 }).length == 1 && lastActive;
449 },
450
451 elements: function() {
452 var validator = this,
453 rulesCache = {};
454
455 // select all valid inputs inside the form (no submit or reset buttons)
456 return $(this.currentForm)
457 .find("input, select, textarea")
458 .not(":submit, :reset, :image, [disabled]")
459 .not( this.settings.ignore )
460 .filter(function() {
461 !this.name && validator.settings.debug && window.console && console.error( "%o has no name assigned", this);
462
463 // select only the first element for each name, and only those with rules specified
464 if ( this.name in rulesCache || !validator.objectLength($(this).rules()) )
465 return false;
466
467 rulesCache[this.name] = true;
468 return true;
469 });
470 },
471
472 clean: function( selector ) {
473 return $( selector )[0];
474 },
475
476 errors: function() {
477 return $( this.settings.errorElement + "." + this.settings.errorClass, this.errorContext );
478 },
479
480 reset: function() {
481 this.successList = [];
482 this.errorList = [];
483 this.errorMap = {};
484 this.toShow = $([]);
485 this.toHide = $([]);
486 this.currentElements = $([]);
487 },
488
489 prepareForm: function() {
490 this.reset();
491 this.toHide = this.errors().add( this.containers );
492 },
493
494 prepareElement: function( element ) {
495 this.reset();
496 this.toHide = this.errorsFor(element);
497 },
498
499 check: function( element ) {
500 element = this.validationTargetFor( this.clean( element ) );
501
502 var rules = $(element).rules();
503 var dependencyMismatch = false;
504 for (var method in rules ) {
505 var rule = { method: method, parameters: rules[method] };
506 try {
507 var result = $.validator.methods[method].call( this, element.value.replace(/\r/g, ""), element, rule.parameters );
508
509 // if a method indicates that the field is optional and therefore valid,
510 // don't mark it as valid when there are no other rules
511 if ( result == "dependency-mismatch" ) {
512 dependencyMismatch = true;
513 continue;
514 }
515 dependencyMismatch = false;
516
517 if ( result == "pending" ) {
518 this.toHide = this.toHide.not( this.errorsFor(element) );
519 return;
520 }
521
522 if( !result ) {
523 this.formatAndAdd( element, rule );
524 return false;
525 }
526 } catch(e) {
527 this.settings.debug && window.console && console.log("exception occured when checking element " + element.id
528 + ", check the '" + rule.method + "' method", e);
529 throw e;
530 }
531 }
532 if (dependencyMismatch)
533 return;
534 if ( this.objectLength(rules) )
535 this.successList.push(element);
536 return true;
537 },
538
539 // return the custom message for the given element and validation method
540 // specified in the element's "messages" metadata
541 customMetaMessage: function(element, method) {
542 if (!$.metadata)
543 return;
544
545 var meta = this.settings.meta
546 ? $(element).metadata()[this.settings.meta]
547 : $(element).metadata();
548
549 return meta && meta.messages && meta.messages[method];
550 },
551
552 // return the custom message for the given element name and validation method
553 customMessage: function( name, method ) {
554 var m = this.settings.messages[name];
555 return m && (m.constructor == String
556 ? m
557 : m[method]);
558 },
559
560 // return the first defined argument, allowing empty strings
561 findDefined: function() {
562 for(var i = 0; i < arguments.length; i++) {
563 if (arguments[i] !== undefined)
564 return arguments[i];
565 }
566 return undefined;
567 },
568
569 defaultMessage: function( element, method) {
570 return this.findDefined(
571 this.customMessage( element.name, method ),
572 this.customMetaMessage( element, method ),
573 // title is never undefined, so handle empty string as undefined
574 !this.settings.ignoreTitle && element.title || undefined,
575 $.validator.messages[method],
576 "<strong>Warning: No message defined for " + element.name + "</strong>"
577 );
578 },
579
580 formatAndAdd: function( element, rule ) {
581 var message = this.defaultMessage( element, rule.method ),
582 theregex = /\$?\{(\d+)\}/g;
583 if ( typeof message == "function" ) {
584 message = message.call(this, rule.parameters, element);
585 } else if (theregex.test(message)) {
586 message = jQuery.format(message.replace(theregex, '{$1}'), rule.parameters);
587 }
588 this.errorList.push({
589 message: message,
590 element: element
591 });
592
593 this.errorMap[element.name] = message;
594 this.submitted[element.name] = message;
595 },
596
597 addWrapper: function(toToggle) {
598 if ( this.settings.wrapper )
599 toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );
600 return toToggle;
601 },
602
603 defaultShowErrors: function() {
604 for ( var i = 0; this.errorList[i]; i++ ) {
605 var error = this.errorList[i];
606 this.settings.highlight && this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );
607 this.showLabel( error.element, error.message );
608 }
609 if( this.errorList.length ) {
610 this.toShow = this.toShow.add( this.containers );
611 }
612 if (this.settings.success) {
613 for ( var i = 0; this.successList[i]; i++ ) {
614 this.showLabel( this.successList[i] );
615 }
616 }
617 if (this.settings.unhighlight) {
618 for ( var i = 0, elements = this.validElements(); elements[i]; i++ ) {
619 this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass );
620 }
621 }
622 this.toHide = this.toHide.not( this.toShow );
623 this.hideErrors();
624 this.addWrapper( this.toShow ).show();
625 },
626
627 validElements: function() {
628 return this.currentElements.not(this.invalidElements());
629 },
630
631 invalidElements: function() {
632 return $(this.errorList).map(function() {
633 return this.element;
634 });
635 },
636
637 showLabel: function(element, message) {
638 var label = this.errorsFor( element );
639 if ( label.length ) {
640 // refresh error/success class
641 label.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );
642
643 // check if we have a generated label, replace the message then
644 label.attr("generated") && label.html(message);
645 } else {
646 // create label
647 label = $("<" + this.settings.errorElement + "/>")
648 .attr({"for": this.idOrName(element), generated: true})
649 .addClass(this.settings.errorClass)
650 .html(message || "");
651 if ( this.settings.wrapper ) {
652 // make sure the element is visible, even in IE
653 // actually showing the wrapped element is handled elsewhere
654 label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
655 }
656 if ( !this.labelContainer.append(label).length )
657 this.settings.errorPlacement
658 ? this.settings.errorPlacement(label, $(element) )
659 : label.insertAfter(element);
660 }
661 if ( !message && this.settings.success ) {
662 label.text("");
663 typeof this.settings.success == "string"
664 ? label.addClass( this.settings.success )
665 : this.settings.success( label );
666 }
667 this.toShow = this.toShow.add(label);
668 },
669
670 errorsFor: function(element) {
671 var name = this.idOrName(element);
672 return this.errors().filter(function() {
673 return $(this).attr('for') == name;
674 });
675 },
676
677 idOrName: function(element) {
678 return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name);
679 },
680
681 validationTargetFor: function(element) {
682 // if radio/checkbox, validate first element in group instead
683 if (this.checkable(element)) {
684 element = this.findByName( element.name ).not(this.settings.ignore)[0];
685 }
686 return element;
687 },
688
689 checkable: function( element ) {
690 return /radio|checkbox/i.test(element.type);
691 },
692
693 findByName: function( name ) {
694 // select by name and filter by form for performance over form.find("[name=...]")
695 var form = this.currentForm;
696 return $(document.getElementsByName(name)).map(function(index, element) {
697 return element.form == form && element.name == name && element || null;
698 });
699 },
700
701 getLength: function(value, element) {
702 switch( element.nodeName.toLowerCase() ) {
703 case 'select':
704 return $("option:selected", element).length;
705 case 'input':
706 if( this.checkable( element) )
707 return this.findByName(element.name).filter(':checked').length;
708 }
709 return value.length;
710 },
711
712 depend: function(param, element) {
713 return this.dependTypes[typeof param]
714 ? this.dependTypes[typeof param](param, element)
715 : true;
716 },
717
718 dependTypes: {
719 "boolean": function(param, element) {
720 return param;
721 },
722 "string": function(param, element) {
723 return !!$(param, element.form).length;
724 },
725 "function": function(param, element) {
726 return param(element);
727 }
728 },
729
730 optional: function(element) {
731 return !$.validator.methods.required.call(this, $.trim(element.value), element) && "dependency-mismatch";
732 },
733
734 startRequest: function(element) {
735 if (!this.pending[element.name]) {
736 this.pendingRequest++;
737 this.pending[element.name] = true;
738 }
739 },
740
741 stopRequest: function(element, valid) {
742 this.pendingRequest--;
743 // sometimes synchronization fails, make sure pendingRequest is never < 0
744 if (this.pendingRequest < 0)
745 this.pendingRequest = 0;
746 delete this.pending[element.name];
747 if ( valid && this.pendingRequest == 0 && this.formSubmitted && this.form() ) {
748 $(this.currentForm).submit();
749 this.formSubmitted = false;
750 } else if (!valid && this.pendingRequest == 0 && this.formSubmitted) {
751 $(this.currentForm).triggerHandler("invalid-form", [this]);
752 this.formSubmitted = false;
753 }
754 },
755
756 previousValue: function(element) {
757 return $.data(element, "previousValue") || $.data(element, "previousValue", {
758 old: null,
759 valid: true,
760 message: this.defaultMessage( element, "remote" )
761 });
762 }
763
764 },
765
766 classRuleSettings: {
767 required: {required: true},
768 email: {email: true},
769 url: {url: true},
770 date: {date: true},
771 dateISO: {dateISO: true},
772 dateDE: {dateDE: true},
773 number: {number: true},
774 numberDE: {numberDE: true},
775 digits: {digits: true},
776 creditcard: {creditcard: true}
777 },
778
779 addClassRules: function(className, rules) {
780 className.constructor == String ?
781 this.classRuleSettings[className] = rules :
782 $.extend(this.classRuleSettings, className);
783 },
784
785 classRules: function(element) {
786 var rules = {};
787 var classes = $(element).attr('class');
788 classes && $.each(classes.split(' '), function() {
789 if (this in $.validator.classRuleSettings) {
790 $.extend(rules, $.validator.classRuleSettings[this]);
791 }
792 });
793 return rules;
794 },
795
796 attributeRules: function(element) {
797 var rules = {};
798 var $element = $(element);
799
800 for (var method in $.validator.methods) {
801 var value;
802 // If .prop exists (jQuery >= 1.6), use it to get true/false for required
803 if (method === 'required' && typeof $.fn.prop === 'function') {
804 value = $element.prop(method);
805 } else {
806 value = $element.attr(method);
807 }
808 if (value) {
809 rules[method] = value;
810 } else if ($element[0].getAttribute("type") === method) {
811 rules[method] = true;
812 }
813 }
814
815 // maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs
816 if (rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength)) {
817 delete rules.maxlength;
818 }
819
820 return rules;
821 },
822
823 metadataRules: function(element) {
824 if (!$.metadata) return {};
825
826 var meta = $.data(element.form, 'validator').settings.meta;
827 return meta ?
828 $(element).metadata()[meta] :
829 $(element).metadata();
830 },
831
832 staticRules: function(element) {
833 var rules = {};
834 var validator = $.data(element.form, 'validator');
835 if (validator.settings.rules) {
836 rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {};
837 }
838 return rules;
839 },
840
841 normalizeRules: function(rules, element) {
842 // handle dependency check
843 $.each(rules, function(prop, val) {
844 // ignore rule when param is explicitly false, eg. required:false
845 if (val === false) {
846 delete rules[prop];
847 return;
848 }
849 if (val.param || val.depends) {
850 var keepRule = true;
851 switch (typeof val.depends) {
852 case "string":
853 keepRule = !!$(val.depends, element.form).length;
854 break;
855 case "function":
856 keepRule = val.depends.call(element, element);
857 break;
858 }
859 if (keepRule) {
860 rules[prop] = val.param !== undefined ? val.param : true;
861 } else {
862 delete rules[prop];
863 }
864 }
865 });
866
867 // evaluate parameters
868 $.each(rules, function(rule, parameter) {
869 rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter;
870 });
871
872 // clean number parameters
873 $.each(['minlength', 'maxlength', 'min', 'max'], function() {
874 if (rules[this]) {
875 rules[this] = Number(rules[this]);
876 }
877 });
878 $.each(['rangelength', 'range'], function() {
879 if (rules[this]) {
880 rules[this] = [Number(rules[this][0]), Number(rules[this][1])];
881 }
882 });
883
884 if ($.validator.autoCreateRanges) {
885 // auto-create ranges
886 if (rules.min && rules.max) {
887 rules.range = [rules.min, rules.max];
888 delete rules.min;
889 delete rules.max;
890 }
891 if (rules.minlength && rules.maxlength) {
892 rules.rangelength = [rules.minlength, rules.maxlength];
893 delete rules.minlength;
894 delete rules.maxlength;
895 }
896 }
897
898 // To support custom messages in metadata ignore rule methods titled "messages"
899 if (rules.messages) {
900 delete rules.messages;
901 }
902
903 return rules;
904 },
905
906 // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
907 normalizeRule: function(data) {
908 if( typeof data == "string" ) {
909 var transformed = {};
910 $.each(data.split(/\s/), function() {
911 transformed[this] = true;
912 });
913 data = transformed;
914 }
915 return data;
916 },
917
918 // http://docs.jquery.com/Plugins/Validation/Validator/addMethod
919 addMethod: function(name, method, message) {
920 $.validator.methods[name] = method;
921 $.validator.messages[name] = message != undefined ? message : $.validator.messages[name];
922 if (method.length < 3) {
923 $.validator.addClassRules(name, $.validator.normalizeRule(name));
924 }
925 },
926
927 methods: {
928
929 // http://docs.jquery.com/Plugins/Validation/Methods/required
930 required: function(value, element, param) {
931 // check if dependency is met
932 if ( !this.depend(param, element) )
933 return "dependency-mismatch";
934 switch( element.nodeName.toLowerCase() ) {
935 case 'select':
936 // could be an array for select-multiple or a string, both are fine this way
937 var val = $(element).val();
938 return val && val.length > 0;
939 case 'input':
940 if ( this.checkable(element) )
941 return this.getLength(value, element) > 0;
942 default:
943 return $.trim(value).length > 0;
944 }
945 },
946
947 // http://docs.jquery.com/Plugins/Validation/Methods/remote
948 remote: function(value, element, param) {
949 if ( this.optional(element) )
950 return "dependency-mismatch";
951
952 var previous = this.previousValue(element);
953 if (!this.settings.messages[element.name] )
954 this.settings.messages[element.name] = {};
955 previous.originalMessage = this.settings.messages[element.name].remote;
956 this.settings.messages[element.name].remote = previous.message;
957
958 param = typeof param == "string" && {url:param} || param;
959
960 if ( this.pending[element.name] ) {
961 return "pending";
962 }
963 if ( previous.old === value ) {
964 return previous.valid;
965 }
966
967 previous.old = value;
968 var validator = this;
969 this.startRequest(element);
970 var data = {};
971 data[element.name] = value;
972 $.ajax($.extend(true, {
973 url: param,
974 mode: "abort",
975 port: "validate" + element.name,
976 dataType: "json",
977 data: data,
978 success: function(response) {
979 validator.settings.messages[element.name].remote = previous.originalMessage;
980 var valid = response === true;
981 if ( valid ) {
982 var submitted = validator.formSubmitted;
983 validator.prepareElement(element);
984 validator.formSubmitted = submitted;
985 validator.successList.push(element);
986 validator.showErrors();
987 } else {
988 var errors = {};
989 var message = response || validator.defaultMessage( element, "remote" );
990 errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message;
991 validator.showErrors(errors);
992 }
993 previous.valid = valid;
994 validator.stopRequest(element, valid);
995 }
996 }, param));
997 return "pending";
998 },
999
1000 // http://docs.jquery.com/Plugins/Validation/Methods/minlength
1001 minlength: function(value, element, param) {
1002 return this.optional(element) || this.getLength($.trim(value), element) >= param;
1003 },
1004
1005 // http://docs.jquery.com/Plugins/Validation/Methods/maxlength
1006 maxlength: function(value, element, param) {
1007 return this.optional(element) || this.getLength($.trim(value), element) <= param;
1008 },
1009
1010 // http://docs.jquery.com/Plugins/Validation/Methods/rangelength
1011 rangelength: function(value, element, param) {
1012 var length = this.getLength($.trim(value), element);
1013 return this.optional(element) || ( length >= param[0] && length <= param[1] );
1014 },
1015
1016 // http://docs.jquery.com/Plugins/Validation/Methods/min
1017 min: function( value, element, param ) {
1018 return this.optional(element) || value >= param;
1019 },
1020
1021 // http://docs.jquery.com/Plugins/Validation/Methods/max
1022 max: function( value, element, param ) {
1023 return this.optional(element) || value <= param;
1024 },
1025
1026 // http://docs.jquery.com/Plugins/Validation/Methods/range
1027 range: function( value, element, param ) {
1028 return this.optional(element) || ( value >= param[0] && value <= param[1] );
1029 },
1030
1031 // http://docs.jquery.com/Plugins/Validation/Methods/email
1032 email: function(value, element) {
1033 // contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
1034 return this.optional(element) || /^((([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.test(value);
1035 },
1036
1037 // http://docs.jquery.com/Plugins/Validation/Methods/url
1038 url: function(value, element) {
1039 // contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/
1040 return this.optional(element) || /^(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.test(value);
1041 },
1042
1043 // http://docs.jquery.com/Plugins/Validation/Methods/date
1044 date: function(value, element) {
1045 return this.optional(element) || !/Invalid|NaN/.test(new Date(value));
1046 },
1047
1048 // http://docs.jquery.com/Plugins/Validation/Methods/dateISO
1049 dateISO: function(value, element) {
1050 return this.optional(element) || /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(value);
1051 },
1052
1053 // http://docs.jquery.com/Plugins/Validation/Methods/number
1054 number: function(value, element) {
1055 return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(value);
1056 },
1057
1058 // http://docs.jquery.com/Plugins/Validation/Methods/digits
1059 digits: function(value, element) {
1060 return this.optional(element) || value == '0' || /^[1-9]\d*$/.test(value);
1061 },
1062
1063 // http://docs.jquery.com/Plugins/Validation/Methods/creditcard
1064 // based on http://en.wikipedia.org/wiki/Luhn
1065 creditcard: function(value, element) {
1066 if ( this.optional(element) )
1067 return "dependency-mismatch";
1068 // accept only spaces, digits and dashes
1069 if (/[^0-9 -]+/.test(value))
1070 return false;
1071 var nCheck = 0,
1072 nDigit = 0,
1073 bEven = false;
1074
1075 value = value.replace(/\D/g, "");
1076
1077 for (var n = value.length - 1; n >= 0; n--) {
1078 var cDigit = value.charAt(n);
1079 var nDigit = parseInt(cDigit, 10);
1080 if (bEven) {
1081 if ((nDigit *= 2) > 9)
1082 nDigit -= 9;
1083 }
1084 nCheck += nDigit;
1085 bEven = !bEven;
1086 }
1087
1088 return (nCheck % 10) == 0;
1089 },
1090
1091 // http://docs.jquery.com/Plugins/Validation/Methods/accept
1092 accept: function(value, element, param) {
1093 param = typeof param == "string" ? param.replace(/,/g, '|') : "png|jpe?g|gif";
1094 return this.optional(element) || value.match(new RegExp(".(" + param + ")$", "i"));
1095 },
1096
1097 // http://docs.jquery.com/Plugins/Validation/Methods/equalTo
1098 equalTo: function(value, element, param) {
1099 // bind to the blur event of the target in order to revalidate whenever the target field is updated
1100 // TODO find a way to bind the event just once, avoiding the unbind-rebind overhead
1101 var target = $(param).unbind(".validate-equalTo").bind("blur.validate-equalTo", function() {
1102 $(element).valid();
1103 });
1104 return value == target.val();
1105 }
1106
1107 }
1108
1109});
1110
1111// deprecated, use $.validator.format instead
1112$.format = $.validator.format;
1113
1114})(jQuery);
1115
1116// ajax mode: abort
1117// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
1118// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
1119;(function($) {
1120 var pendingRequests = {};
1121 // Use a prefilter if available (1.5+)
1122 if ( $.ajaxPrefilter ) {
1123 $.ajaxPrefilter(function(settings, _, xhr) {
1124 var port = settings.port;
1125 if (settings.mode == "abort") {
1126 if ( pendingRequests[port] ) {
1127 pendingRequests[port].abort();
1128 }
1129 pendingRequests[port] = xhr;
1130 }
1131 });
1132 } else {
1133 // Proxy ajax
1134 var ajax = $.ajax;
1135 $.ajax = function(settings) {
1136 var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode,
1137 port = ( "port" in settings ? settings : $.ajaxSettings ).port;
1138 if (mode == "abort") {
1139 if ( pendingRequests[port] ) {
1140 pendingRequests[port].abort();
1141 }
1142 return (pendingRequests[port] = ajax.apply(this, arguments));
1143 }
1144 return ajax.apply(this, arguments);
1145 };
1146 }
1147})(jQuery);
1148
1149// provides cross-browser focusin and focusout events
1150// IE has native support, in other browsers, use event caputuring (neither bubbles)
1151
1152// provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation
1153// handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target
1154;(function($) {
1155 // only implement if not provided by jQuery core (since 1.4)
1156 // TODO verify if jQuery 1.4's implementation is compatible with older jQuery special-event APIs
1157 if (!jQuery.event.special.focusin && !jQuery.event.special.focusout && document.addEventListener) {
1158 $.each({
1159 focus: 'focusin',
1160 blur: 'focusout'
1161 }, function( original, fix ){
1162 $.event.special[fix] = {
1163 setup:function() {
1164 this.addEventListener( original, handler, true );
1165 },
1166 teardown:function() {
1167 this.removeEventListener( original, handler, true );
1168 },
1169 handler: function(e) {
1170 arguments[0] = $.event.fix(e);
1171 arguments[0].type = fix;
1172 return $.event.handle.apply(this, arguments);
1173 }
1174 };
1175 function handler(e) {
1176 e = $.event.fix(e);
1177 e.type = fix;
1178 return $.event.handle.call(this, e);
1179 }
1180 });
1181 };
1182 $.extend($.fn, {
1183 validateDelegate: function(delegate, type, handler) {
1184 return this.bind(type, function(event) {
1185 var target = $(event.target);
1186 if (target.is(delegate)) {
1187 return handler.apply(target, arguments);
1188 }
1189 });
1190 }
1191 });
1192})(jQuery);