PluginProbe
Virtue/Ascend/Pinnacle Toolkit / 4.9.12.2
Virtue/Ascend/Pinnacle Toolkit v4.9.12.2
4.9.12.2 trunk 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.0 3.1 3.2 3.3 3.4 3.7 All 48 releases
virtue-toolkit / assets / jquery.validate.js

jquery.validate.js in Virtue/Ascend/Pinnacle Toolkit 4.9.12.2, at assets/jquery.validate.js

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