/** * MC Validation * * A flexible Javascript class for validating form data. * * @author Sean Murphy * @link http://mindcomet.com * @version 1.0 * @todo Write more validation rules. */ /** * MC_Validation constructor * * @param object formObj The form to be validated. * @param string msgType [optional] Set the message display type to 'alert' or 'inline'. * @return object Returns a MC_Validation object */ function MC_Validation(formObj, msgType) { this.form = formObj; this.rules = []; this.badFields = []; this.show_msgs = true; this.CSS_error = ''; this.msg_area = ''; if (msgType == 'inline') { this.msg_type = msgType; } else { this.msg_type = 'alert'; } } /** * setClass * * Sets the CSS class name to be applied to message
s and form fields. * * @param string error The CSS class name. * @return void */ MC_Validation.prototype.setClass = function(error) { this.CSS_error = error; } /** * setMsgArea * * Sets the HTML container element error messages will be placed in. * * By creating our own div inside the element that is passed, we are afforded * better styling control and won't disrupt any other messages which might be * there already from a server side script. * * @param string elm_id The id of the element. * @return void */ MC_Validation.prototype.setMsgArea = function(elm_id) { var wrapper = document.getElementById(elm_id); this.msg_area = document.createElement('div'); this.msg_area.setAttribute('class', 'msgs'); wrapper.appendChild(this.msg_area); } /** * setShowMsgs * * Sets whether or not we should display error messages when validation fails. * * @param bool value * @return void */ MC_Validation.prototype.setShowMsgs = function(value) { this.show_msgs = (value == 'false') ? 'false':'true'; } /** * addRules * * Adds a validation rule for a form element. * * @param string id The id of the element having a rule applied to it. * @param string rules Pipe (|) seperated list of rule function names. Optionally you can specify a custom callback function. * @param string msg [optional] A custom error message to be displayed when this rule fails. * If you pass 'false' as the third paramiter no error message will be displayed. * @return void */ MC_Validation.prototype.addRules = function(id, rules, msg) { if (document.getElementById(id) && rules) { this.rules.push(new Array(document.getElementById(id), rules, msg)); } } /** * _addClassName * * Adds a CSS class to an element without disrupting other classes set to it. * * @param string element The element having the class applied to it. * @param string class_name Name of the CSS class. * @return void */ MC_Validation.prototype._addClassName = function(element, class_name) { var regex = new RegExp(class_name); if (!element.className.match(regex)) { var classes = element.className.split(' '); classes.push(class_name); element.className = classes.join(' '); } } /** * _removeClassName * * Removes a CSS class from an element without disrupting other classes set to it. * * @param string element The element having the class removed from it. * @param string class_name Name of the CSS class. * @return void */ MC_Validation.prototype._removeClassName = function(element, class_name) { var regex = new RegExp(class_name); element.className = element.className.replace(regex, ''); } /** * clearErrors * * Clear previously highlighted fields and error messages * * @return void */ MC_Validation.prototype.clearErrors = function() { for(var i = 0; i < this.form.length; i++) { this._removeClassName(this.form.elements[i], this.CSS_error); } this.msg_area.innerHTML = ''; } /** * displayErrors * * Handles error message display and form field highlighting * * @note If you would like to prevent form fields from being highlighted * use setClasses('') * @return void */ MC_Validation.prototype.displayErrors = function() { var msgs = ''; for(var x = 0; x < this.badFields.length; x++) { // Highlight fields with errors this._addClassName(this.badFields[x][0], this.CSS_error); // Queue up our error messages if (this.badFields[x][1] != 'false') { if (this.msg_type == 'inline') { var onClick = 'onClick="document.getElementById(\'' + this.badFields[x][0].id + '\').focus();"'; msgs += '
' + this.badFields[x][1] + "
\n"; } else { msgs += this.badFields[x][1] + '\n'; } } } if (this.show_msgs) { switch(this.msg_type) { case 'inline': if (!this.msg_area) alert('Your message display type is set to inline, but you have not set a valid message area using setMsgArea().'); else this.msg_area.innerHTML = msgs; break; case 'alert': alert(msgs); break; } } // Set focus to first field with error this.badFields[0][0].focus(); } /** * validate * * Runs the current form data through all the validation rules that were set. * * @return bool True if all rules passed, false (and calls displayErrors()) if rules failed * @todo Could use more inline documentation. */ MC_Validation.prototype.validate = function() { // Reset our array of failed rules this.badFields = []; var element; var mrules; var error; var errorMsgs; var params; var rule_parts; for(var idx = 0; idx < this.rules.length; idx++) { // Reset array of error messages errorMsgs = []; element = this.rules[idx][0]; // For cases when multiple rules are passed at one time. mrules = this.rules[idx][1].split('|'); for(var rule = 0; rule < mrules.length; rule++) { rule_parts = mrules[rule].match(/^(\w+)(\[(\w+)\])?$/); if (!eval('this.' + rule_parts[1]) && eval(rule_parts[1])) { // Custom callback params = 'element'; } else { // Some rules will need access to attributes other than element.value; // we do that here. switch(mrules[rule]) { case 'optionSelected': params = 'element.selectedIndex'; break; case 'radioSelected': params = 'element.name'; break; case 'checkboxSelected': params = 'element.checked'; break; default: params = 'element.value'; } rule_parts[1] = 'this.' + rule_parts[1]; } if (rule_parts[3]) { params += ", '" + rule_parts[3] + "'"; } error = eval(rule_parts[1] + '(' + params + ')'); if (error) { errorMsgs.push(error); } } if(errorMsgs.length > 0) { // Check if a custom message was set for this field. if (this.rules[idx][2]) { errorMsgs[0] = this.rules[idx][2]; } this.badFields.push(new Array(element, errorMsgs[0])); } } this.clearErrors(); if(this.badFields.length > 0) { this.displayErrors(); return(false); } return(true); } /** * validEmail * * Validates email addresses according to RFC 2822 (and some common usage exceptions). * * @param string str The email address. * @return string Empty string if success, else an error message. */ MC_Validation.prototype.validEmail = function(str) { var error = "Invalid email address."; if (!str) return ''; // Check for invalid characters if (str.match(/[\x00-\x1F\x7F-\xFF]/)) return error; // Check that there's one @ symbol, and that the lengths are right if (!str.match(/^[^@]{1,64}@[^@]{1,255}$/)) return error; // Split it into sections to make life easier var email_array = str.split('@'); // Check local part var local_array = email_array[0].split('.'); for(var i = 0; i < local_array.length; i++) { if (!local_array[i].match(/^(([A-Za-z0-9!#$%&\'*+\/=?^_`{|}~-]+)|("[^"]+"))$/)) return error; } // Check domain part if (!email_array[1].match(/^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}$/) || !email_array[1].match(/^\[(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}\]$/)) { // If not an IP address var domain_array = email_array[1].split('.'); if (domain_array.length < 2) { // Not enough parts to be a valid domain return error; } for(var j = 0; j < domain_array.length; j++) { if (!domain_array[j].match(/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]))$/)) return error; } } return ''; } /** * validPhone * * Validates a phone number regardless of valid delimiters used. * Valid delimiters are spaces ( ), dashes (-), periods (.), and pluses (+). * Supports country codes. * * @param string str The phone number. * @return string Empty string if success, else an error message. */ MC_Validation.prototype.validPhone = function(str) { /* | country_code = "(\+?\d{1,3})?"; | area_code = "(\(\d{3}\)|\d{3})"; | exchange = "\d{3}"; | local = "\d{4}"; | delimiter = "(\.|\s|-)?"; | pattern = '/^'+country_code+delimiter+area_code+delimiter+exchange+delimiter+local+'$/'; */ if (str && !str.match(/^(\+?\d{1,3})?(\.|\s|-)?(\(\d{3}\)|\d{3})(\.|\s|-)?\d{3}(\.|\s|-)?\d{4}$/)) { return 'Invalid phone number.'; } return ''; } /** * formatPhone * * Formats phone number in a standard way. (Consistancy man, consistancy) * Returns phone number in the format of +123.123.123.1234 or 123.123.1234 * * @param string str The unformated phone number * @return string Formatted phone number. */ MC_Validation.prototype.formatPhone = function(str) { var clean = str.replace(/[^0-9]/, ''); if (clean.length > 10) { return clean.replace(/^(\d{1,3})(\d{3})(\d{3})(\d{4})$/, '+$1.$2.$3.$4'); } return clean.replace(/^(\d{3})(\d{3})(\d{4})$/, '$1.$2.$3'); } /** * validZip * * Validates zip codes. Accepts the forms 12345 and 12345-1234. * * @param string str The zip code. * @return string Empty string if success, else an error message. */ MC_Validation.prototype.validZip = function(str) { if (str && !str.match(/^\d{5}(-\d{4})?$/)) { return 'Invalid zip code.'; } return ''; } /** * isNotEmpty * * Checks if a string is NOT empty. * * @param string str The string. * @return string Empty string if success, else an error message. */ MC_Validation.prototype.isNotEmpty = function(str) { if (str.length === 0) { return 'Field has not been filled in.'; } return ''; } /** * isEmpty * * Checks if a string IS empty. * * @param string str The string. * @return string Empty string if success, else an error message. */ MC_Validation.prototype.isEmpty = function(str) { if (str.length !== 0) { return 'Field should not be filled in.'; } return ''; } /** * alnum * * Checks if a string alphanumeric (including underscore). * @param string str The string. * @return string Empty string if success, else an error message. */ MC_Validation.prototype.alnum = function(str) { if (str && !str.match(/^\w+$/)) { return 'Field should contain only alphanumeric characters.'; } return ''; } /** * alpha * * Checks if a string alphabetic. * * @param string str The string. * @return string Empty string if success, else an error message. */ MC_Validation.prototype.alpha = function(str) { if (str && !str.match(/^[a-zA-Z]+$/)) { return 'Field should contain only alphabetic characters.'; } return ''; } /** * digits * * Checks if string contains only digits. * * @param string str The string. * @return string Empty string if success, else an error message. */ MC_Validation.prototype.digits = function(str) { if (str && !str.match(/^\d+$/)) { return 'Field should contain only digits (0-9).'; } return ''; } /** * min_length * * Checks if string is of a minimum length. * * @param string str The string. * @param string len The minimum length. * @return string Empty string if success, else an error message. */ MC_Validation.prototype.min_length = function(str, len) { if (str.length < len) { return 'Field must be at least ' + len + ' characters long.'; } return ''; } /** * max_length * * Checks if string is of a maximum length. * * @param string str The string. * @param string len The maximum length. * @return string Empty string if success, else an error message. */ MC_Validation.prototype.max_length = function(str, len) { if (str.length > len) { return 'Field must be less than ' + len + ' characters long.'; } return ''; } /** * exact_length * * Checks if string is of an exact length. * * @param string str The string. * @param string len The exact length. * @return string Empty string if success, else an error message. */ MC_Validation.prototype.exact_length = function(str, len) { if (str.length != len) { return 'Field must be ' + len + ' characters long.'; } return ''; } /** * matches * * Checks if string matches another string. * * @param string str The first string. * @param string id The id of the element to match. * @return string Empty string if success, else an error message. */ MC_Validation.prototype.matches = function(str, id) { if (str != document.getElementById(id).value) { return 'Fields do not match.'; } return ''; } /** * optionSelected * * Checks if select box has option selected. * * @param string index The selectedIndex. * @return string Empty string if success, else an error message. */ MC_Validation.prototype.optionSelected = function(index) { if (!index) { return 'You must make a selection.'; } return ''; } /** * radioSelected * * Checks if one radio in a radio group has be selected. * * @param string grpName The name attribute radios share in common. * @return string Empty string if success, else an error message. */ MC_Validation.prototype.radioSelected = function(grpName) { var radios = eval('this.form.' + grpName); for(var n = 0; n < radios.length; n++) { if (radios[n].checked) return ''; } return 'You must select a radio button.'; } /** * checkboxSelected * * Checks if checkbox has been checked. * * @param string checked The checked attribute of a checkbox. * @return string Empty string if success, else an error message. */ MC_Validation.prototype.checkboxSelected = function(checked) { if (!checked) { return 'Checkbox must be checked.'; } return ''; }