/* * Form Validation: jQuery form validation plug-in v1.0 beta 2 * * Copyright (c) 2006 Jörn Zaefferer * * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html */ /** * Validates either a single form on submit or a list of elements on a user-defined event. * * The normal behaviour is to validate a form when a submit button is clicked or * the user presses enter when an input of that form is focused. * * It is also possible to validate each individual element of that form, eg. on blur or keyup. * * @example $("#myform").validate(); * @before
* *
* @desc Validates a form on submit. Rules are read from metadata. * * @example $("input").validate({ * focusInvalid: false, * event: "blur" * }); * @desc Validates all input elements on blur event (when the element looses focus). * Deactivates focus of invalid elements. * * @example $("#myform").validate({ * submitHandler: function(form) { * $(form).ajaxSubmit(); * } * }); * @desc Uses form plugin's ajaxSubmit method to handle the form submit, while preventing * the default submit. * * @example $("#myform").validate({ * event: "keyup" * rules: { * firstname: { required: true }, * age: { * required: "#firstname:blank", * number: true, * minValue: 3 * }, * password: { * required: function() { * return $("#age").val() < 18; * }, * minLength: 5, * maxLength: 32 * } * }, * messages { * password: { * required: "Your password is required because you are not yet 18 years or older." * minLength: "Please enter a password at least 5 characters long.", * maxLength: "Please enter a password no longer then 32 characters long." * }, * age: "Please specify your age as a number (at least 3)." * } * }); * @desc Validate a form on submit and each element on keyup. Rules are specified * for three elements, and a message is customized for the "password" and the * "age" elements. Inline rules are ignored. The password is only required when the age is lower * then 18. The age is only required when the firstname is blank. * * @example $("#myform").validate({ * errorClass: "invalid", * errorLabelContainer: $("#messageBox"), * wrapper: "li" * }); * @before *
* * * * *
* @result *
* * * * *
* @desc Validates a form on submit. The class used to search, create and display * error labels is changed to "invalid". This is also added to invalid elements. * * All error labels are displayed inside an unordered list with the ID "messageBox", as * specified by the jQuery object passed as errorContainer option. All error elements * are wrapped inside an li element, to create a list of messages. * * To ease the setup of the form, debug option is set to true, preventing a submit * of the form no matter of being valid or not. * * * @example $("#myform").validate({ * errorPlacement: function(error, element) { * error.appendTo( element.parent("td").next("td") ); * } * }); * @before
* * * * * * * * * * *
*
*
* @result
* * * * * * * * * * *
*
*
* @desc Validates a form on submit. Customizes the placement of the generated labels * by appending them to the next table cell. * * @example $("#myform").validate({ * errorContainer: $("#messageBox1, #messageBox2"), * errorLabelContainer: $("#messageBox1 ul"), * wrapper: "li", * }); * @before
*

The are errors in your form!

* *
*
* * * * *
*
*

The are errors in your form, see details above!

*
* @result *
* * * * *
* @desc Validates a form on submit. Similar to the above example, but with an additional * container for error messages. The elements given as the errorContainer are all shown * and hidden when errors occur. But the error labels themselve are added to the element(s) * given as errorLabelContainer, here an unordered list. Therefore the error labels are * also wrapped into li elements (wrapper option). * * @param Map options Optional settings to configure validation * @option String errorClass Use this class to look for existing error labels and add it to * invalid elements. Default: "error" * @option String wrapper Wrap error labels with the specified element, eg "li". Default: none * @option Boolean debug If true, the form is not submitted and certain errors are display on the console (requires Firebug or Firebug lite). Default: none * @option Boolean focusInvalid Focus the last active or first invalid element. Disable for blur-validation, crashes IE otherwise. Default: true * @option Function submitHandler Callback for handling the actual * submit when the form is valid. Gets the form as the only argmument. Default: normal form submit * @option Map messages Key/value pairs defining custom messages. * Key is the ID or name (for radio/checkbox inputs) of an element, * value the message to display for that element. Instead of a plain message * another map with specific messages for each rule can be used. * Can be specified for one or more elements. Can be overriden by * specifying the title attribute on the element. * Default: none, the default message for the method is used. * @option Map rules Key/value pairs defining custom rules. * Key is the ID or name (for radio/checkbox inputs) of an element, * value is an object consisting of rule/parameter pairs, eg. {required: true, min: 3} * Default: none, rules are read from metadata via metadata plugin * @option String event The event on which to validate. If anything is specified, like * blur or keyup, each element is validated on that event. Default: none * @option Boolean onsubmit Validate the form on submit. Set to false to use only other * events for validation (option event). Default: true * @option String meta In case you use metadata for other plugins, too, you * want to wrap your validation rules * into their own object that can be specified via this option. Default: none * @option jQuery errorContainer Hide and show this container when validating. Default: none * @option jQuery errorLabelContainer Search and append error labels to this container, and show and hide it accordingly. Default: none * @option Function showErrors A custom message display handler. Gets the map of errors as the * first argument and a refernce to the validator object as the second. * You can trigger (in addition to your own messages) the default behaviour by calling * the defaultShowErrors() method of the validator. * Default: none, uses built-in message disply. * @option Function errorPlacement Used to customize placement of created error labels. * First argument: jQuery object containing the created error label * Second argument: jQuery object containing the invalid element * Default: Places the error label after the invalid element * * @name validate * @type $.validator * @cat Plugins/Validate */ jQuery.extend(jQuery.fn, { validate: function( options ) { var validator = new jQuery.validator( options, this ); // select all valid inputs inside the form (no submit or reset buttons) // and listen for focus events to save reference to last focused element validator.elements = this.find(":input:not(:submit):not(:reset)").focus(function() { validator.lastActive = this; }); if ( validator.settings.onsubmit ) { // validate the form on submit this.submit( function( event ) { if ( validator.settings.debug ) // prevent form submit to be able to see console output event.preventDefault(); return validator.form(); }); } if ( validator.settings.event ) { // validate all elements on some other event like blur or keypress validator.elements.bind( validator.settings.event, function() { validator.element(this); } ); } return validator; }, // destructive add push: function( t ) { return this.setArray( jQuery.merge( this.get(), t ) ); }, forId: function( id ) { return this.filter( "[@for=" + id + "]" ); } }); /** * Custom expression to filter for blank fields. * * @example jQuery("input:blank").length * @before * @result 2 * * @property * @type String * @name :blank * @cat Plugins/Validate */ /** * Custom expression to filter for filled fields. * * @example jQuery("input:filled").length * @before * @result 1 * * @property * @type String * @name :filled * @cat Plugins/Validate */ jQuery.extend(jQuery.expr[":"], { blank: "!jQuery.trim(a.value)", filled: "!!jQuery.trim(a.value)" }); // constructor for validator jQuery.validator = function( options, form ) { this.settings = jQuery.extend( {}, jQuery.validator.defaults, options ); this.currentForm = form[0]; this.labelContainer = this.settings.errorLabelContainer; this.errorContext = this.labelContainer.length && this.labelContainer || form; this.containers = this.settings.errorContainer.add( this.settings.errorLabelContainer ); this.reset(); }; jQuery.extend(jQuery.validator, { defaults: { messages: {}, errorClass: "error", focusInvalid: true, errorContainer: jQuery( [] ), errorLabelContainer: jQuery( [] ), onsubmit: true }, /** * Modify default settings for validation. * * @example jQuery.validator.setDefaults({ * debug: true * ); * @desc Sets the debug setting for all validation calls following. * * @param Object settings * @name jQuery.validator.setDefaults * @type undefined * @cat Plugins/Validate */ setDefaults: function(settings) { jQuery.extend( jQuery.validator.defaults, settings ); }, /** * Default messages for all default methods. * * User addMethod() to add methods with messages. * * Replace these messages for localization. * * @property * @type String * @name jQuery.validator.messages * @cat Plugins/Validate */ messages: { required: "This field is required.", maxLength: "Please enter a value no longer then {0} characters.", minLength: "Please enter a value of at least {0} characters.", rangeLength: "Please enter a value between {0} and {1} characters long.", email: "Please enter a valid email address.", url: "Please enter a valid URL.", date: "Please enter a valid date.", dateISO: "Please enter a valid date (ISO).", dateDE: "Bitte geben Sie ein g�ltiges Datum ein.", number: "Please enter a valid number.", numberDE: "Bitte geben Sie eine Nummer ein.", digits: "Please enter only digits", equalTo: "Please enter the same value again.", rangeValue: "Please enter a value between {0} and {1}.", maxValue: "Please enter a value less than or equal to {0}.", minValue: "Please enter a value greater than or equal to {0}." }, prototype: { /** * Validate on instant the entire form. * * @example $("#myform").validate().form(); * @desc Triggers form validation programmatcitally. * * @name jQuery.validator.protoype.form * @type Boolean True when the form is valid, otherwise false * @cat Plugins/Validate */ form: function() { this.prepareForm(); for ( var i = 0, element; element = this.elements[i++]; ) { this.check( element ); } return this.valid(); }, /** * Validate on instant a single element. * * @example $("#myform").validate().element( "#myselect" ); * @desc Triggers validation on a single element programmatically. * * @param String|Element element A selector or an element to validate * * @name jQuery.validator.protoype.element * @type Boolean True when the form is valid, otherwise false * @cat Plugins/Validate */ element: function( element ) { this.prepareElement( element ); this.check( element ); this.showErrors(); }, /** * Show the specified messages. * * @example var validator = $("#myform").validate(); * validator.showErrors({"firstname": "I know that your firstname is Pete, Pete!"}); * @desc Adds and shows error message programmatically. * * @param Map errors One or more key/value pairs of identifiers (IDs or names) and messages * * @name jQuery.validator.protoype.showErrors * @cat Plugins/Validate */ showErrors: function(errors) { if(errors) jQuery.extend(this.errorList, errors); this.settings.showErrors ? this.settings.showErrors( this.errorList, this ) : this.defaultShowErrors(); }, /** * Resets the controlled form, including resetting input fields * to their original value (requires form plugin), removing classes * indicating invalid elements and hiding error messages. * * @example var validator = $("#myform").validate(); * validator.resetForm(); * @desc Reset the form controlled by this validator. * * @name jQuery.validator.protoype.resetForm * @cat Plugins/Validate */ resetForm: function() { if( jQuery.fn.resetForm ) jQuery( this.currentForm ).resetForm(); this.prepareForm(); this.hideErrors(); this.elements.removeClass( this.settings.errorClass ); }, clean: function( selector ) { return jQuery( selector )[0]; }, errors: function() { return jQuery( "label." + this.settings.errorClass, this.errorContext ); }, reset: function( element ) { this.errorList = {}; this.toShow = $( [] ); this.toHide = $( [] ); }, prepareForm: function() { this.reset(); this.toHide = this.errors().push( this.containers ); this.toShow.push( this.containers ); }, prepareElement: function( element ) { this.reset(); this.toHide = this.errors().forId( this.findId( this.clean( element ) ) ); }, check: function( element ) { element = this.clean( element ); jQuery( element ).removeClass( this.settings.errorClass ); var rules = this.rules( element ); for( var i = 0, rule; rule = rules[i++]; ) { try { var result = jQuery.validator.methods[rule.method]( jQuery.trim(element.value), element, rule.parameters ); if( result === -1 ) break; if( !result ) { jQuery(element).addClass( this.settings.errorClass ); this.formatAndAdd( rule, element); break; } } catch(e) { this.settings.debug && window.console && console.error("exception occured when checking element " + element.id + ", check the '" + rule.method + "' method"); throw e; } } }, message: function( id, rule ) { var m = this.settings.messages[id]; return m && (m.constructor == String ? m : m[rule.method]); }, formatAndAdd: function( rule, element) { var id = this.findId( element ), param = rule.parameters; this.errorList[id] = ( element.title || this.message(id, rule) || jQuery.validator.messages[rule.method] || "Warning: No message defined for " + id + "" ) .replace( "{0}", (param.constructor == Array ? param[0] : param) || "" ) .replace( "{1}", param[1] || "" ); }, valid: function() { if ( this.countErrors() ) { this.showErrors(); return false; } else { this.hideErrors(); if ( this.settings.submitHandler ) { this.settings.submitHandler( this.currentForm ); return false; } return true; } }, countErrors: function() { var count = 0; jQuery.each( this.errorList, function() { count++; } ); return count; }, hideErrors: function() { this.toggle( "Hide" ); }, toggle: function(that) { var self = this; function which() { return self["to" + that]; } if ( this.settings.wrapper ) { which().push( which().parents( this.settings.wrapper ) ); } which()[that.toLowerCase()](); return this; }, defaultShowErrors: function() { var first = true; for ( var elementID in this.errorList ) { if( first && this.settings.focusInvalid ) { // check if the last focused element is invalid if( this.lastActive && this.errorList[this.lastActive.id]) // focus it this.lastActive.focus(); // otherwise, find the firt invalid lement else { // IE throws an exception when focusing hidden element try { // focus the first invalid element var element = jQuery("#"+elementID); // radio/checkbox doesn't have an ID if(element.length) element[0].focus(); } catch(e) { this.settings.debug && window.console && console.error(e); } } first = false; } // display the error label for the first failed method this.showError( elementID, this.errorList[elementID] ); } this.toHide = this.toHide.not( this.toShow ); this.toggle( "Hide" ).toggle( "Show" ); }, showError: function(id, message) { var error = this.errors().forId(id); if ( error.length ) { // check if we have a generated label, replace the message then if( error.attr("generated") ) { error.html(message); } } else { // create label error = jQuery("