Lightweight JavaScript form validation library inspired by CodeIgniter.
+No dependencies, just over 2kb gzipped, and customizable!
+ + + validate.js + (development - 16kb) + + + + validate.min.js + (minified - 2.1kb) + + + + + + + +Example
+ +Features
+ +-
+
- Validate form fields from over a dozen rules +
- No dependencies +
- Customizable messages +
- Supply your own validation callbacks for custom rules +
- Chainable customization methods for ease of declaration +
- Conditionally validate certain form fields +
- Works in all major browsers (even IE6!) +
- Modeled off the CodeIgniter form validation API +
Installation and Usage
+ +Include the JavaScript file in your source
+ +<script type="text/javascript" src="validate.min.js"></script>+ +
Create the validation object with your desired rules. This needs to be in a <script> tag located just before your closing </body> tag. The reason for this being that the DOM needs to have your form loaded before you can attach your rules.
+ +
+var validator = new FormValidator('example_form', [{
+ name: 'req',
+ display: 'required',
+ rules: 'required'
+}, {
+ name: 'alphanumeric',
+ rules: 'alpha_numeric'
+}, {
+ name: 'password',
+ rules: 'required'
+}, {
+ name: 'password_confirm',
+ display: 'password confirmation',
+ rules: 'required|matches[password]'
+}, {
+ name: 'email',
+ rules: 'valid_email',
+ depends: function() {
+ return Math.random() > .5;
+ }
+}, {
+ name: 'minlength',
+ display: 'min length',
+ rules: 'min_length[8]'
+}], function(errors, event) {
+ if (errors.length > 0) {
+ // Show the errors
+ }
+});
+
+ FormValidator
new FormValidator(formName, fields, callback) + +The FormValidator object is attached to the window upon loading validate.js. After creation, it will validate the fields on submission of the form named formName.
+ +The formName passed in to validate must be the exact value of the name attribute of the form
+ +<form name="example_form"></form>+ +
An array of fields will be used to perform validation on submission of the form. The array must contain objects containing these properties:
+ +-
+
-
+
name (required) - The name attribute of the element.
+<input name="email" />
+
+ -
+
display (optional) - The name of the field as it appears in error messages. If not present in the object, the name parameter will be used.
+Example: If your field name is user, you may choose to use a display of "Username."
+
+ -
+
rules (required) - One or more rules, which are piped together.
+Example - required|min_length[8]|matches[password_confirm]
+
+ -
+
depends (optional) - A function that runs before the field is validated. If the function returns false, the field is never validated against the declared rules.
+depends: function(field) { + return Math.random() > .5; +}+
+
A callback will always be executed after validation. Your callback should be ready to accept two parameters.
+ +-
+
-
+
errors - An array of errors from the validation object. If the length > 0, the form failed validation
+This array will contain javascript objects with up to four properties:
+
+ - id: The id attribute of the form element
+ - name: The name attribute of the form element
+ - message: The error message to display
+ - messages: The error message of every failed validation of the given field to display
+ - rule: The rule that prompted this error
+ - event - If the browser supports it, the onsubmit event is passed in. +
function(errors, event) {
+ if (errors.length > 0) {
+ var errorString = '';
+
+ for (var i = 0, errorLength = errors.length; i < errorLength; i++) {
+ errorString += errors[i].message + '<br />';
+ }
+
+ el.innerHTML = errorString;
+ }
+}
+ Custom Validation Rules
+ +validate.js supports the ability for you to include your own validation rules. This will allow you to extend validate.js to suit your needs. A common example of this would be checking the strength of a password.
+ +First, you need to add another rule to the field. It must always be prefaced with "callback_"
+ +rules: 'required|callback_check_password'+ +
Then you must call registerCallback on your instance of the FormValidator with the name of your custom rule and a function taking one parameter. This function will be called with one argument, the value of the field. If the value passes your custom validation, return true, otherwise return false. You can set a message for this rule using the setMessage method as described below.
+ +validator.registerCallback('check_password', function(value) {
+ if (passwordIsStrong(value)) {
+ return true;
+ }
+
+ return false;
+})
+.setMessage('check_password', 'Please choose a stronger password using at least 1 number.');
+
+ Callbacks behave according to the following rules:
+ #1. If the required rule is present, a callback will be fired once all other validation rules pass.
+ #2. If the field is not required and it is empty, the callback will not be called unless condition #3 is met.
+ #3. A callback will always be called if it is preceded by an '!' i.e. rules: '!callback_myCustomCallback'
+
Available Methods
+ +setMessage
validator.setMessage(rule, message) + +All of the default error messages are located at the top of validate.js in a defaults object. If you wish to change a message application wide, you should do so in the source code. If you would like to change a message for a form, use this method on your instance of the FormValidator object. When setting a new message, you should pass in %s, which will be replaced with the display parameter from the fields array
+ +validator.setMessage('required', 'You must fill out the %s field.');
+
+ registerCallback
validator.registerCallback(rule, callback) + +Used to pair a custom rule in the fields array with a callback to be executed upon validation.
+ +validator.registerCallback('check_email', function(value) {
+ if (emailIsUnique(value)) {
+ return true;
+ }
+
+ return false;
+});
+
+ registerConditional
validator.registerConditional(name, callback) + +An alternate syntax for declaring depends functions, which determine whether or not to validate a given field. + +
{
+ name: 'first_name',
+ rules: 'required',
+ depends: 'checkForRandomNumber'
+}
+
+ validator.registerConditional('checkForRandomNumber', function(field) {
+ return Math.random() > .5;
+});
+ Available Rules
+ +| Rule | +Description | +Parameter | +Example | +
|---|---|---|---|
| required | +returns false if the form element is empty. | +no | ++ |
| matches | +returns false if the form element value does not match the one in the parameter. | +yes | +matches[other_element] | +
| valid_email | +returns false if the form element value is not a valid email address. | +no | ++ |
| valid_emails | +returns false if any value provided in a comma separated list is not a valid email. | +no | ++ |
| min_length | +returns false if the form element value is shorter than the parameter. | +yes | +min_length[6] | +
| max_length | +returns false if the form element value is longer than the parameter. | +yes | +max_length[8] | +
| exact_length | +returns false if the form element value length is not exactly the parameter. | +yes | +exact_length[4] | +
| greater_than | +returns false if the form element value is less than the parameter after using parseFloat. | +yes | +greater_than[10] | +
| less_than | +returns false if the form element value is greater than the parameter after using parseFloat. | +yes | +less_than[2] | +
| alpha | +returns false if the form element contains anything other than alphabetical characters. | +no | ++ |
| alpha_numeric | +returns false if the form element contains anything other than alpha-numeric characters. | +no | ++ |
| alpha_dash | +returns false if the form element contains anything other than alphanumeric characters, underscores, or dashes. | +no | ++ |
| numeric | +returns false if the form element contains anything other than numeric characters. | +no | ++ |
| integer | +returns false if the form element contains anything other than an integer. | +no | ++ |
| decimal | +returns false if the form element contains anything other than a decimal. | +no | ++ |
| is_natural | +returns false if the form element contains anything other than a natural number: 0, 1, 2, 3, etc. | +no | ++ |
| is_natural_no_zero | +returns false if the form element contains anything other than a natural number, but not zero: 1, 2, 3, etc. | +no | ++ |
| valid_ip | +returns false if the supplied IP is not valid. | +no | ++ |
| valid_base64 | +returns false if the supplied string contains anything other than valid Base64 characters. | +no | ++ |
| valid_credit_card | +returns false if the supplied string is not a valid credit card | +no | ++ |
| valid_url | +returns false if the supplied string is not a valid url | +no | ++ |
| is_file_type | +returns false if the supplied file is not part of the comma separated list in the paramter | +yes | +is_file_type[gif,png,jpg] | +
Release Notes
+ +2.0.0 - 09/20/15
+ +-
+
- Adds messages property to errors. It contains all of the failed validations. +
1.4.1 - 08/20/14
+ +-
+
- Updated email regex to html 5 specificiation. +
1.4 - 04/13/14
+ +-
+
- Added `depends` for conditional validation. +
1.3 - 08/18/13
+ +-
+
- There has been an API-breaking change made to callback behavior:
+ #1. If the required rule is present, a callback will be fired once all other validation rules pass.
+ #2. If the field is not required and it is empty, the callback will not be called unless condition #3 is met.
+ #3. A callback will always be called if it is preceded by an '!' i.e. rules: '!callback_myCustomCallback'
+ - Fixed an issue where multiple checkboxes could not be validated. +
1.2.2 - 03/27/13
+ +-
+
- Callbacks will now be fired even if a field is not required - contributed by TimBenjamin +
- Fixed an issue in Internet Explorer where the event global variable was being overwritten. +
1.2.1 - 03/17/13
+ +-
+
- Added valid_url rule - contributed by tajo +
1.2 - 01/20/13
+ +-
+
- Added radio button support - contributed by ikr +
- Added is_file_type rule - contributed by tricinel +
- Updated email regex to html5 standard +
- Added the rule to the errors array in the callback - contributed by jhnns +
- Added valid_credit_card rule - contributed by grahamhayes +
1.1 - 05/17/12
+ +-
+
-
+
Changed the format of the error parameter in the validation callback.
+It now contains javascript objects containing up to three parameters:
+
+ - id: The id attribute of the form element
+ - name: The name attribute of the form element
+ - message: The error message to display
+
1.0.2 - 12/19/11
+ +-
+
- Added new rules: valid_emails, decimal, is_natural, is_natural_no_zero, valid_ip, valid_base64 +
- Bug fixes +
1.0.1 - 10/17/11
+ +-
+
- Minor bug fixes +
1.0.0 - 10/17/11
+ +-
+
- Initial release +
In Progress
+ +-
+
- node.js support +
Contact
+ +Questions? Need help? Feature request? Let me know on Twitter.
+Please file issues on GitHub.
+ +
+
+
+
+
+
diff --git a/styles/main.css b/styles/main.css
new file mode 100644
index 0000000..54deca0
--- /dev/null
+++ b/styles/main.css
@@ -0,0 +1,334 @@
+/* http://meyerweb.com/eric/tools/css/reset/
+ v2.0 | 20110126
+ License: none (public domain)
+*/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block;
+}
+body {
+ line-height: 1;
+}
+ol, ul {
+ list-style: none;
+}
+blockquote, q {
+ quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+b {
+ font-weight:bold;
+}
+
+/* Main CSS */
+
+body {
+ background:url('../assets/background.png');
+ color:#444;
+ font:15px 'Helvetica Neue', 'Helvetica', Arial, sans-serif;
+ margin:30px 100px;
+}
+
+#content {
+ margin:0 0 0 25px;
+ width:720px;
+}
+
+#content .bm {
+ margin-bottom:25px;
+}
+
+h1 {
+ color:#FF4200;
+ font:120px 'Andada', 'Helvetica', Arial, sans-serif;
+ margin:0 0 15px 0;
+}
+
+h2 {
+ font-size:22px;
+ font-weight:400;
+ text-shadow:0px 1px #f8f8f8;
+}
+
+h3 {
+ clear:both;
+ color:#FFF;
+ margin-bottom:20px;
+}
+
+h3 span {
+ background:#FF4200;
+ font-size:18px;
+ padding:5px 10px;
+}
+
+h4 {
+ float:left;
+ font-weight:700;
+ margin-bottom:15px;
+ margin-right:20px;
+}
+
+.button {
+ background-color:#0885c7;
+ background-repeat:repeat-x;
+ background-image:-khtml-gradient(linear, left top, left bottom, from(#4cbbf5), to(#0885c7));
+ background-image:-moz-linear-gradient(top, #4cbbf5, #0885c7);
+ background-image:-ms-linear-gradient(top, #4cbbf5, #0885c7);
+ background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #4cbbf5), color-stop(100%, #0885c7));
+ background-image:-webkit-linear-gradient(top, #4cbbf5, #0885c7);
+ background-image:-o-linear-gradient(top, #4cbbf5, #0885c7);
+ background-image:linear-gradient(top, #4cbbf5, #0885c7);
+
+ border:1px solid #00527d;
+ border-radius:5px;
+ -webkit-border-radius:5px;
+ -moz-border-radius:5px;
+
+ color:#ffffff;
+ cursor:pointer;
+ display:block;
+ float:left;
+ font-size:20px;
+ line-height:normal;
+ padding:10px 15px;
+ text-align:center;
+ text-decoration:none;
+ text-shadow:0 -1px #444444;
+
+ transition:0.2s linear all;
+ -webkit-transition:0.2s linear all;
+ -moz-transition:0.2s linear all;
+ -ms-transition:0.2s linear all;
+ -o-transition:0.2s linear all;
+}
+
+.button:hover {
+ background-position:0 -15px;
+ text-decoration:none;
+}
+
+.button:active {
+ box-shadow:inset 0 2px 2px rgba(0, 0, 0, 0.25);
+ -webkit-box-shadow:inset 0 2px 2px rgba(0, 0, 0, 0.25);
+ -moz-box-shadow:inset 0 2px 2px rgba(0, 0, 0, 0.25);
+}
+
+.button span {
+ display:block;
+ font-size:14px;
+ font-style:italic;
+}
+
+.button.download {
+ color:#fff;
+ margin:0 20px 30px 0;
+}
+
+.button.download:hover {
+ color:#fff;
+}
+
+.button.gray {
+ background-color: #dcdcdc;
+ background-repeat: no-repeat;
+ background-image:-khtml-gradient(linear, left top, left bottom, from(#f2f2f2), to(#dcdcdc));
+ background-image:-moz-linear-gradient(top, #f2f2f2, #dcdcdc);
+ background-image:-ms-linear-gradient(top, #f2f2f2, #dcdcdc);
+ background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #dcdcdc), color-stop(100%, #dcdcdc));
+ background-image:-webkit-linear-gradient(top, #f2f2f2, #dcdcdc);
+ background-image:-o-linear-gradient(top, #f2f2f2, #dcdcdc);
+ background-image:linear-gradient(top, #f2f2f2, #dcdcdc);
+ border:1px solid #444;
+ color:#444;
+ font-size:14px;
+ font-weight:bold;
+ text-shadow:0px -1px #FFF;
+}
+
+ul {
+ display:block;
+ list-style-position:outside;
+ list-style-type:disc;
+ margin-left:20px;
+ margin-bottom:40px;
+}
+
+ul.inline {
+ margin-bottom:15px;
+}
+
+li {
+ text-shadow:0px 1px #f8f8f8;
+}
+
+div.section {
+ margin-bottom:50px;
+}
+
+p {
+ margin-bottom:10px;
+ text-shadow:0px 1px #f8f8f8;
+}
+
+/* Form */
+
+label, input {
+ border:0;
+ display:block;
+ float:left;
+ padding:0;
+ margin:0 30px 5px 0;
+ width:330px;
+}
+
+input {
+ background:#f8f8f8;
+ border:1px solid #444;
+ border-radius:5px;
+ -webkit-border-radius:5px;
+ -moz-border-radius:5px;
+ margin-bottom:20px;
+ padding:10px 7px;
+ width:314px;
+}
+
+input:hover, textarea:hover {
+ box-shadow:0px 0px 6px #C0C0C0;
+ -moz-box-shadow:0px 0px 6px #C0C0C0
+ -webkit-box-shadow:0px 0px 6px #C0C0C0;
+}
+
+input:focus, textarea:focus {
+ border:1px solid #0885c7;
+ outline:none;
+ box-shadow:0px 0px 12px #C0C0C0;
+ -moz-box-shadow:0px 0px 12px #C0C0C0
+ -webkit-box-shadow:0px 0px 12px #C0C0C0;
+}
+
+button {
+ clear:both;
+ margin-bottom:50px;
+}
+
+.error_box {
+ background:#FAD3C4;
+ border:1px solid #A75B4E;
+ border-radius:5px;
+ -webkit-border-radius:5px;
+ -moz-border-radius:5px;
+ color:#444444;
+ display:none;
+ font-size:13px;
+ margin:0px 0px 15px 0px;
+ padding:8px 8px;
+ width:672px;
+}
+
+.success_box {
+ background:#E2F1BB;
+ border:1px solid #598800;
+ border-radius:5px;
+ -webkit-border-radius:5px;
+ -moz-border-radius:5px;
+ color:#000000;
+ display:none;
+ font-size:13px;
+ margin:0px 0px 15px 0px;
+ padding:8px 8px;
+ width:672px;
+}
+
+input#tos_checkbox {
+ clear:both;
+}
+
+/* links */
+
+a, a:active, a:visited {
+ color:#444;
+ text-decoration:underline;
+}
+
+a:hover {
+ color:#1F93D0;
+}
+
+/* Tables */
+
+table {
+ margin:35px 0 50px 0;
+}
+
+th {
+ text-align:left;
+}
+
+th span {
+ background:#1F93D0;
+ color:#FFFFFF;
+ display:block;
+ float:left;
+ padding:5px 10px;
+ margin:0 0 10px 0;
+}
+
+td {
+ font-size:15px;
+ padding:0 15px 13px 0;
+ text-shadow:0px 1px #f8f8f8;
+}
+
+/* Code Formatting */
+
+tt, pre {
+ color:#3c1001;
+ font:13px Monaco, Consolas, "Lucida Console", monospace;
+}
+
+pre {
+ border-left:5px solid #0885c7;
+ margin:0 0 40px 0;
+ padding:0 0 0 10px;
+}
+
+pre.inline {
+ margin:0 0 15px 0;
+}
+
+/* github buttons */
+
+.github {
+ margin-bottom:60px;
+}
diff --git a/traslation-spanish b/traslation-spanish
new file mode 100644
index 0000000..11c8ce6
--- /dev/null
+++ b/traslation-spanish
@@ -0,0 +1,30 @@
+var defaults = {
+ messages: {
+ required: 'El %s campo es requerido.',
+ matches: 'El %s campo no coincide con el %s campo.',
+ "default": 'El %s campo sigue por defecto, por favor cambielo.',
+ valid_email: 'El %s campo debe contener una dirreccion de correo valida.',
+ valid_emails: 'El %s campo debe contener todos los emails validos.',
+ min_length: 'El %s campo debe tener al menos %s caracteres.',
+ max_length: 'El %s no debe sobrepasar de %s caracteres.',
+ exact_length: 'El %s campo debe tener exactamente %s caracteres.',
+ greater_than: 'El %s campo debe tener un numero mayor a %s.',
+ less_than: 'El %s campo debe contener un numero menor a %s.',
+ alpha: 'El %s campo debe solo contener caracteres alfabeticos.',
+ alpha_numeric: 'El %s campo solo debe contener caracteres alfanumericos.',
+ alpha_dash: 'El %s campo must solo debe contener alfanumericos caracteres guiones y guiones bajos.',
+ numeric: 'El %s campo solo debe contener numeros enteros.',
+ integer: 'El %s campo debe contener numeros.',
+ decimal: 'El %s campo debe contener un numero decimal.',
+ is_natural: 'El %s campo debe contener solo numeros positivos.',
+ is_natural_no_zero: 'El %s campo debe contener numeros mayores que cero.',
+ valid_ip: 'El %s campo debe contener una dirección IP valida.',
+ valid_base64: 'El %s campo debe contener una cadena de texto en base64.',
+ valid_credit_card: 'El %s campo debe contener un numero de tarjeta de credito valida.',
+ is_file_type: 'El %s campo solo debe contener %s archivos.',
+ valid_url: 'El %s campo debe contener una URL valida.',
+ greater_than_date: 'El %s campo debe contener una fecha más reciente que %s.',
+ less_than_date: 'El %s campo debe contener una fecha más antigua que %s.',
+ greater_than_or_equal_date: 'El %s campo debe contener una fecha igual o más reciente que %s.',
+ less_than_or_equal_date: 'El %s campo debe contener una fecha igual o más antigua que %s.'
+ }
diff --git a/validate.js b/validate.js
index 32cc768..357aadf 100644
--- a/validate.js
+++ b/validate.js
@@ -1,6 +1,6 @@
/*
- * validate.js 1.3
- * Copyright (c) 2011 Rick Harrison, http://rickharrison.me
+ * validate.js 2.0.1
+ * Copyright (c) 2011 - 2015 Rick Harrison, http://rickharrison.me
* validate.js is open sourced under the MIT license.
* Portions of validate.js are inspired by CodeIgniter.
* http://rickharrison.github.com/validate.js
@@ -36,7 +36,11 @@
valid_base64: 'The %s field must contain a base64 string.',
valid_credit_card: 'The %s field must contain a valid credit card number.',
is_file_type: 'The %s field must contain only %s files.',
- valid_url: 'The %s field must contain a valid URL.'
+ valid_url: 'The %s field must contain a valid URL.',
+ greater_than_date: 'The %s field must contain a more recent date than %s.',
+ less_than_date: 'The %s field must contain an older date than %s.',
+ greater_than_or_equal_date: 'The %s field must contain a date that\'s at least as recent as %s.',
+ less_than_or_equal_date: 'The %s field must contain a date that\'s %s or older.'
},
callback: function(errors) {
@@ -51,7 +55,7 @@
numericRegex = /^[0-9]+$/,
integerRegex = /^\-?[0-9]+$/,
decimalRegex = /^\-?[0-9]*\.?[0-9]+$/,
- emailRegex = /^[a-zA-Z0-9.!#$%&'*+\-\/=?\^_`{|}~\-]+@[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*$/,
+ emailRegex = /^[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])?)*$/,
alphaRegex = /^[a-z]+$/i,
alphaNumericRegex = /^[a-z0-9]+$/i,
alphaDashRegex = /^[a-z0-9_\-]+$/i,
@@ -60,7 +64,8 @@
ipRegex = /^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})$/i,
base64Regex = /[^a-zA-Z0-9\/\+=]/i,
numericDashRegex = /^[\d\-\s]+$/,
- urlRegex = /^((http|https):\/\/(\w+:{0,1}\w*@)?(\S+)|)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
+ urlRegex = /^((http|https):\/\/(\w+:{0,1}\w*@)?(\S+)|)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,
+ dateRegex = /\d{4}-\d{1,2}-\d{1,2}/;
/*
* The exposed public object to validate a form:
@@ -83,12 +88,16 @@
this.form = this._formByNameOrNode(formNameOrNode) || {};
this.messages = {};
this.handlers = {};
+ this.conditionals = {};
for (var i = 0, fieldLength = fields.length; i < fieldLength; i++) {
var field = fields[i];
// If passed in incorrectly, we need to skip the field.
if ((!field.name && !field.names) || !field.rules) {
+ console.warn('validate.js: The following field is being skipped due to a misconfiguration:');
+ console.warn(field);
+ console.warn('Check to ensure you have properly configured a name and rules for this field');
continue;
}
@@ -97,7 +106,7 @@
*/
if (field.names) {
- for (var j = 0; j < field.names.length; j++) {
+ for (var j = 0, fieldNamesLength = field.names.length; j < fieldNamesLength; j++) {
this._addField(field, field.names[j]);
}
} else {
@@ -124,7 +133,7 @@
var i;
if ((element.length > 0) && (element[0].type === 'radio' || element[0].type === 'checkbox')) {
- for (i = 0; i < element.length; i++) {
+ for (i = 0, elementLength = element.length; i < elementLength; i++) {
if (element[i].checked) {
return element[i][attributeName];
}
@@ -162,6 +171,20 @@
return this;
};
+ /*
+ * @public
+ * Registers a conditional for a custom 'depends' rule
+ */
+
+ FormValidator.prototype.registerConditional = function(name, conditional) {
+ if (name && typeof name === 'string' && conditional && typeof conditional === 'function') {
+ this.conditionals[name] = conditional;
+ }
+
+ // return this for chaining
+ return this;
+ };
+
/*
* @private
* Determines if a form dom node was passed in or just a string representing the form name
@@ -181,7 +204,9 @@
name: nameValue,
display: field.display || nameValue,
rules: field.rules,
+ depends: field.depends,
id: null,
+ element: null,
type: null,
value: null,
checked: null
@@ -203,15 +228,28 @@
if (element && element !== undefined) {
field.id = attributeValue(element, 'id');
+ field.element = element;
field.type = (element.length > 0) ? element[0].type : element.type;
field.value = attributeValue(element, 'value');
field.checked = attributeValue(element, 'checked');
/*
* Run through the rules for each field.
+ * If the field has a depends conditional, only validate the field
+ * if it passes the custom function
*/
- this._validateField(field);
+ if (field.depends && typeof field.depends === "function") {
+ if (field.depends.call(this, field)) {
+ this._validateField(field);
+ }
+ } else if (field.depends && typeof field.depends === "string" && this.conditionals[field.depends]) {
+ if (this.conditionals[field.depends].call(this,field)) {
+ this._validateField(field);
+ }
+ } else {
+ this._validateField(field);
+ }
}
}
}
@@ -238,7 +276,8 @@
*/
FormValidator.prototype._validateField = function(field) {
- var rules = field.rules.split('|'),
+ var i, j,
+ rules = field.rules.split('|'),
indexOfRequired = field.rules.indexOf('required'),
isEmpty = (!field.value || field.value === '' || field.value === undefined);
@@ -246,7 +285,7 @@
* Run through the rules and execute the validation methods as needed
*/
- for (var i = 0, ruleLength = rules.length; i < ruleLength; i++) {
+ for (i = 0, ruleLength = rules.length; i < ruleLength; i++) {
var method = rules[i],
param = null,
failed = false,
@@ -269,7 +308,7 @@
method = parts[1];
param = parts[2];
}
-
+
if (method.charAt(0) === '!') {
method = method.substring(1, method.length);
}
@@ -287,7 +326,7 @@
method = method.substring(9, method.length);
if (typeof this.handlers[method] === 'function') {
- if (this.handlers[method].apply(this, [field.value, param]) === false) {
+ if (this.handlers[method].apply(this, [field.value, param, field]) === false) {
failed = true;
}
}
@@ -299,7 +338,7 @@
if (failed) {
// Make sure we have a message for this rule
- var source = this.messages[method] || defaults.messages[method],
+ var source = this.messages[field.name + '.' + method] || this.messages[method] || defaults.messages[method],
message = 'An error has occurred with the ' + field.display + ' field.';
if (source) {
@@ -310,19 +349,50 @@
}
}
- this.errors.push({
+ var existingError;
+ for (j = 0; j < this.errors.length; j += 1) {
+ if (field.id === this.errors[j].id) {
+ existingError = this.errors[j];
+ }
+ }
+
+ var errorObject = existingError || {
id: field.id,
+ display: field.display,
+ element: field.element,
name: field.name,
message: message,
+ messages: [],
rule: method
- });
-
- // Break out so as to not spam with validation errors (i.e. required and valid_email)
- break;
+ };
+ errorObject.messages.push(message);
+ if (!existingError) this.errors.push(errorObject);
}
}
};
+ /**
+ * private function _getValidDate: helper function to convert a string date to a Date object
+ * @param date (String) must be in format yyyy-mm-dd or use keyword: today
+ * @returns {Date} returns false if invalid
+ */
+ FormValidator.prototype._getValidDate = function(date) {
+ if (!date.match('today') && !date.match(dateRegex)) {
+ return false;
+ }
+
+ var validDate = new Date(),
+ validDateArray;
+
+ if (!date.match('today')) {
+ validDateArray = date.split('-');
+ validDate.setFullYear(validDateArray[0]);
+ validDate.setMonth(validDateArray[1] - 1);
+ validDate.setDate(validDateArray[2]);
+ }
+ return validDate;
+ };
+
/*
* @private
* Object containing all of the validation hooks
@@ -338,7 +408,7 @@
return (value !== null && value !== '');
},
-
+
"default": function(field, defaultName){
return field.value !== defaultName;
},
@@ -358,9 +428,9 @@
},
valid_emails: function(field) {
- var result = field.value.split(",");
+ var result = field.value.split(/\s*,\s*/g);
- for (var i = 0; i < result.length; i++) {
+ for (var i = 0, resultLength = result.length; i < resultLength; i++) {
if (!emailRegex.test(result[i])) {
return false;
}
@@ -488,13 +558,63 @@
len = typeArray.length;
for (i; i < len; i++) {
- if (ext == typeArray[i]) inArray = true;
+ if (ext.toUpperCase() == typeArray[i].toUpperCase()) inArray = true;
}
return inArray;
+ },
+
+ greater_than_date: function (field, date) {
+ var enteredDate = this._getValidDate(field.value),
+ validDate = this._getValidDate(date);
+
+ if (!validDate || !enteredDate) {
+ return false;
+ }
+
+ return enteredDate > validDate;
+ },
+
+ less_than_date: function (field, date) {
+ var enteredDate = this._getValidDate(field.value),
+ validDate = this._getValidDate(date);
+
+ if (!validDate || !enteredDate) {
+ return false;
+ }
+
+ return enteredDate < validDate;
+ },
+
+ greater_than_or_equal_date: function (field, date) {
+ var enteredDate = this._getValidDate(field.value),
+ validDate = this._getValidDate(date);
+
+ if (!validDate || !enteredDate) {
+ return false;
+ }
+
+ return enteredDate >= validDate;
+ },
+
+ less_than_or_equal_date: function (field, date) {
+ var enteredDate = this._getValidDate(field.value),
+ validDate = this._getValidDate(date);
+
+ if (!validDate || !enteredDate) {
+ return false;
+ }
+
+ return enteredDate <= validDate;
}
};
window.FormValidator = FormValidator;
-
})(window, document);
+
+/*
+ * Export as a CommonJS module
+ */
+if (typeof module !== 'undefined' && module.exports) {
+ module.exports = FormValidator;
+}
diff --git a/validate.min.js b/validate.min.js
new file mode 100644
index 0000000..2207ad2
--- /dev/null
+++ b/validate.min.js
@@ -0,0 +1,23 @@
+/*
+ * validate.js 2.0.1
+ * Copyright (c) 2011 - 2015 Rick Harrison, http://rickharrison.me
+ * validate.js is open sourced under the MIT license.
+ * Portions of validate.js are inspired by CodeIgniter.
+ * http://rickharrison.github.com/validate.js
+ */
+(function(r,t,l){var u={required:"The %s field is required.",matches:"The %s field does not match the %s field.","default":"The %s field is still set to default, please change.",valid_email:"The %s field must contain a valid email address.",valid_emails:"The %s field must contain all valid email addresses.",min_length:"The %s field must be at least %s characters in length.",max_length:"The %s field must not exceed %s characters in length.",exact_length:"The %s field must be exactly %s characters in length.",
+greater_than:"The %s field must contain a number greater than %s.",less_than:"The %s field must contain a number less than %s.",alpha:"The %s field must only contain alphabetical characters.",alpha_numeric:"The %s field must only contain alpha-numeric characters.",alpha_dash:"The %s field must only contain alpha-numeric characters, underscores, and dashes.",numeric:"The %s field must contain only numbers.",integer:"The %s field must contain an integer.",decimal:"The %s field must contain a decimal number.",
+is_natural:"The %s field must contain only positive numbers.",is_natural_no_zero:"The %s field must contain a number greater than zero.",valid_ip:"The %s field must contain a valid IP.",valid_base64:"The %s field must contain a base64 string.",valid_credit_card:"The %s field must contain a valid credit card number.",is_file_type:"The %s field must contain only %s files.",valid_url:"The %s field must contain a valid URL.",greater_than_date:"The %s field must contain a more recent date than %s.",less_than_date:"The %s field must contain an older date than %s.",
+greater_than_or_equal_date:"The %s field must contain a date that's at least as recent as %s.",less_than_or_equal_date:"The %s field must contain a date that's %s or older."},v=function(a){},w=/^(.+?)\[(.+)\]$/,g=/^[0-9]+$/,x=/^\-?[0-9]+$/,m=/^\-?[0-9]*\.?[0-9]+$/,q=/^[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])?)*$/,y=/^[a-z]+$/i,z=/^[a-z0-9]+$/i,A=/^[a-z0-9_\-]+$/i,B=/^[0-9]+$/i,C=/^[1-9][0-9]*$/i,D=/^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[0-9]{1,2})$/i,
+E=/[^a-zA-Z0-9\/\+=]/i,F=/^[\d\-\s]+$/,G=/^((http|https):\/\/(\w+:{0,1}\w*@)?(\S+)|)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,H=/\d{4}-\d{1,2}-\d{1,2}/,f=function(a,c,b){this.callback=b||v;this.errors=[];this.fields={};this.form=this._formByNameOrNode(a)||{};this.messages={};this.handlers={};this.conditionals={};a=0;for(b=c.length;a=parseInt(c,10):!1},max_length:function(a,c){return g.test(c)?a.value.length<=parseInt(c,10):!1},exact_length:function(a,c){return g.test(c)?a.value.length===parseInt(c,10):!1},greater_than:function(a,
+c){return m.test(a.value)?parseFloat(a.value)>parseFloat(c):!1},less_than:function(a,c){return m.test(a.value)?parseFloat(a.value)