Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HiveStatistics/sources/HeuristicLab.Services.Hive.Statistics/3.3/Scripts/jquery.validate.js @ 11552

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