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