/*
* 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("