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