/*
* validate.js 1.1
* Copyright (c) 2011 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(window, document, undefined) {
/*
* If you would like an application-wide config, change these defaults.
* Otherwise, use the setMessage() function to configure form specific messages.
*/
var defaults = {
messages: {
required: 'The %s field is required.',
matches: 'The %s field does not match the %s field.',
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.'
},
callback: function(errors) {
}
};
/*
* Define the regular expressions that will be used
*/
var ruleRegex = /^(.+)\[(.+)\]$/,
numericRegex = /^[0-9]+$/,
integerRegex = /^\-?[0-9]+$/,
decimalRegex = /^\-?[0-9]*\.?[0-9]+$/,
emailRegex = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,6}$/i,
alphaRegex = /^[a-z]+$/i,
alphaNumericRegex = /^[a-z0-9]+$/i,
alphaDashRegex = /^[a-z0-9_-]+$/i,
naturalRegex = /^[0-9]+$/i,
naturalNoZeroRegex = /^[1-9][0-9]*$/i,
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;
/*
* The exposed public object to validate a form:
*
* @param formName - String - The name attribute of the form (i.e. <form name="myForm"></form>)
* @param fields - Array - [{
* name: The name of the element (i.e. <input name="myField" />)
* display: 'Field Name'
* rules: required|matches[password_confirm]
* }]
* @param callback - Function - The callback after validation has been performed.
* @argument errors - An array of validation errors
* @argument event - The javascript event
*/
var FormValidator = function(formName, fields, callback) {
this.callback = callback || defaults.callback;
this.errors = [];
this.fields = {};
this.form = document.forms[formName] || {};
this.messages = {};
this.handlers = {};
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.rules) {
continue;
}
/*
* Build the master fields array that has all the information needed to validate
*/
this.fields[field.name] = {
name: field.name,
display: field.display || field.name,
rules: field.rules,
id: null,
type: null,
value: null,
checked: null
};
}
/*
* Attach an event callback for the form submission
*/
this.form.onsubmit = (function(that) {
return function(event) {
try {
return that._validateForm(event);
} catch(e) {}
}
})(this);
};
/*
* @public
* Sets a custom message for one of the rules
*/
FormValidator.prototype.setMessage = function(rule, message) {
this.messages[rule] = message;
// return this for chaining
return this;
};
/*
* @public
* Registers a callback for a custom rule (i.e. callback_username_check)
*/
FormValidator.prototype.registerCallback = function(name, handler) {
if (name && typeof name === 'string' && handler && typeof handler === 'function') {
this.handlers[name] = handler;
}
// return this for chaining
return this;
};
/*
* @private
* Runs the validation when the form is submitted.
*/
FormValidator.prototype._validateForm = function(event) {
this.errors = [];
for (var key in this.fields) {
if (this.fields.hasOwnProperty(key)) {
var field = this.fields[key] || {},
element = this.form[field.name];
if (element && element !== undefined) {
field.id = element.id;
field.type = element.type;
field.value = element.value;
field.checked = element.checked;
}
/*
* Run through the rules for each field.
*/
this._validateField(field);
}
}
if (typeof this.callback === 'function') {
this.callback(this.errors, event);
}
if (this.errors.length > 0) {
if (event && event.preventDefault) {
event.preventDefault();
} else {
// IE6 doesn't pass in an event parameter so return false
return false;
}
}
return true;
};
/*
* @private
* Looks at the fields value and evaluates it against the given rules
*/
FormValidator.prototype._validateField = function(field) {
var rules = field.rules.split('|');
/*
* If the value is null and not required, we don't need to run through validation
*/
if (field.rules.indexOf('required') === -1 && (!field.value || field.value === '' || field.value === undefined)) {
return;
}
/*
* Run through the rules and execute the validation methods as needed
*/
for (var i = 0, ruleLength = rules.length; i < ruleLength; i++) {
var method = rules[i],
param = null,
failed = false;
/*
* If the rule has a parameter (i.e. matches[param]) split it out
*/
if (parts = ruleRegex.exec(method)) {
method = parts[1];
param = parts[2];
}
/*
* If the hook is defined, run it to find any validation errors
*/
if (typeof this._hooks[method] === 'function') {
if (!this._hooks[method].apply(this, [field, param])) {
failed = true;
}
} else if (method.substring(0, 9) === 'callback_') {
// Custom method. Execute the handler if it was registered
method = method.substring(9, method.length);
if (typeof this.handlers[method] === 'function') {
if (this.handlers[method].apply(this, [field.value]) === false) {
failed = true;
}
}
}
/*
* If the hook failed, add a message to the errors array
*/
if (failed) {
// Make sure we have a message for this rule
var source = this.messages[method] || defaults.messages[method],
message = 'An error has occurred with the ' + field.display + ' field.';
if (source) {
message = source.replace('%s', field.display);
if (param) {
message = message.replace('%s', (this.fields[param]) ? this.fields[param].display : param);
}
}
this.errors.push({
id: field.id,
name: field.name,
message: message
});
// Break out so as to not spam with validation errors (i.e. required and valid_email)
break;
}
}
};
/*
* @private
* Object containing all of the validation hooks
*/
FormValidator.prototype._hooks = {
required: function(field) {
var value = field.value;
if (field.type === 'checkbox') {
return (field.checked === true);
}
return (value !== null && value !== '');
},
matches: function(field, matchName) {
if (el = this.form[matchName]) {
return field.value === el.value;
}
return false;
},
valid_email: function(field) {
return emailRegex.test(field.value);
},
valid_emails: function(field) {
var result = field.value.split(",");
for (var i = 0; i < result.length; i++) {
if (!emailRegex.test(result[i])) {
return false;
}
}
return true;
},
min_length: function(field, length) {
if (!numericRegex.test(length)) {
return false;
}
return (field.value.length >= parseInt(length, 10));
},
max_length: function(field, length) {
if (!numericRegex.test(length)) {
return false;
}
return (field.value.length <= parseInt(length, 10));
},
exact_length: function(field, length) {
if (!numericRegex.test(length)) {
return false;
}
return (field.value.length === parseInt(length, 10));
},
greater_than: function(field, param) {
if (!decimalRegex.test(field.value)) {
return false;
}
return (parseFloat(field.value) > parseFloat(param));
},
less_than: function(field, param) {
if (!decimalRegex.test(field.value)) {
return false;
}
return (parseFloat(field.value) < parseFloat(param));
},
alpha: function(field) {
return (alphaRegex.test(field.value));
},
alpha_numeric: function(field) {
return (alphaNumericRegex.test(field.value));
},
alpha_dash: function(field) {
return (alphaDashRegex.test(field.value));
},
numeric: function(field) {
return (decimalRegex.test(field.value));
},
integer: function(field) {
return (integerRegex.test(field.value));
},
decimal: function(field) {
return (decimalRegex.test(field.value));
},
is_natural: function(field) {
return (naturalRegex.test(field.value));
},
is_natural_no_zero: function(field) {
return (naturalNoZeroRegex.test(field.value));
},
valid_ip: function(field) {
return (ipRegex.test(field.value));
},
valid_base64: function(field) {
return (base64Regex.test(field.value));
}
};
window.FormValidator = FormValidator;
})(window, document);