UNPKG

nos-forms-jquery

Version:

Build and validate DRY html forms in minutes with JSON, jQuery and Bootstrap

940 lines (795 loc) 72.2 kB
/* * nos-forms-jquery - v2.1.0 * Build and validate DRY html forms in minutes with JSON, jQuery and Bootstrap * http://ibidata.github.io/nos-forms-jquery/ * * Made by IBI Data * Under MIT License */ /* global jQuery */ ; (function (factory) { if (typeof module === 'object' && typeof module.exports === 'object') { factory(require('jquery'), window, document); } else { factory(jQuery, window, document); } } (function ($, window, document, undefined) { "use strict"; // Create the defaults var defaults = { fields: null, animationSpeed: 100, validate: true, htmlValidation: false, ajax: true, honeypot: false, messages: { required: 'Please fill out all required fields', invalid: 'Please correct errors' }, messageLocation: { top: false, bottom: true }, onlySubmitWithValue: false, init: null, submit: null }; // The plugin constructor function Nos(element, options) { // need access to this later var self = this; // form id var $form = '#' + element.id; // form element this.form = $(element); // final plugin settings this.settings = $.extend(true, {}, defaults, options); // Store arrays of element types // These are used to determine which function will be called from the '_getElements' object below var _elements = { text: ['text', 'email', 'tel', 'password', 'number', 'hidden', 'zip', 'date', 'week', 'time', 'month', 'datetime-local', 'search', 'url'], textarea: ['textarea'], buttons: ['submit', 'reset', 'button'], select: ['select'], file: ['file'], check: ['checkbox', 'radio'], state: ['state'], clone: ['clone'], html: ['html'], other: ['range', 'color', 'image'], lbl: ['label'], buttonGroup: ['buttonGroup'] }; // takes user form object and converts to string fragments for creating html var _getAttrs = function (input) { return $.extend({}, input, { type: input.type ? ' type="' + input.type + '"' : '', name: input.name ? ' name="' + input.name + '"' : '', id: (input.id || input.name) ? ' id="' + (input.id || input.name) + '"' : '', minlength: (input.minlength || typeof input.minlength === 'number') ? ' minlength="' + input.minlength + '"' : '', maxlength: (input.maxlength || typeof input.maxlength === 'number') ? ' maxlength="' + input.maxlength + '"' : '', required: input.required ? ' required' : '', value: (input.value || typeof input.value === 'number') ? ' value="' + input.value + '"' : '', placeholder: input.placeholder ? ' placeholder="' + input.placeholder + '"' : '', formGroup: (input.formGroup || input.formGroup === undefined) ? _getElements.formGroup() : { start: '', end: '' }, label: input.label ? _getElements.label(input) : '', helpBlock: input.helpBlock ? '<span id="' + (input.id || input.name) + '-help-block" class="help-block nos-help-block">' + input.helpBlock + '</span>' : '', classname: input.classname ? ' class="' + input.classname + '"' : '', multiple: input.multiple ? ' multiple' : '', autofocus: input.autofocus ? ' autofocus' : '', disabled: input.disabled ? ' disabled' : '', readonly: input.readonly ? ' readonly' : '', title: input.title ? ' title="' + input.title + '"' : '', size: input.size ? ' size="' + input.size + '"' : '', data: input.data ? _getElements.data(input.data) : '', message: self.settings.validate ? _getUserErrorMessages(input) : { required: '', minlength: '', maxlength: '', min: '', max: '', valid: '' }, // returns object that stores user error messages tabindex: input.tabindex ? ' tabindex="' + input.tabindex + '"' : '' }); }; // assigns an object to the 'message' property above // this will contain the user error messages that will display on the form var _getUserErrorMessages = function (element) { return $.extend({}, { required: element.required ? _userErrorMessage.required(element) : '', valid: (element.type === 'email' || element.type === 'zip' || element.type === 'tel' || element.pattern || element.match) ? _userErrorMessage.valid(element) : '', minlength: (element.minlength && element.minlength > 1) ? _userErrorMessage.minlength(element) : '', maxlength: (element.maxlength && element.maxlength > 1) ? _userErrorMessage.maxlength(element) : '', min: (element.min && element.min > 1) ? _userErrorMessage.min(element) : '', max: element.max ? _userErrorMessage.max(element) : '' }); }; // Group of functions used to return a form element var _getElements = { self: this, // returns bootstrap form-group div formGroup: function () { return { start: '<div class="form-group nos-group">', end: '</div>' }; }, // returns honeypot fields honeypot: function () { return '<div class="nos-div-hp-css"><label for="nos-text-css[]">Please leave this field blank</label><input type="text" class="nos-text-css" name="nos-text-css[]" value=""></div><div class="nos-div-hp-js"><label for="nos-email-js[]">Please leave this field unchanged</label><input type="email" class="nos-email-js" name="nos-email-js[]" value="validemail@email.com"></div>'; }, // returns a div with a class div: function (classname) { return { start: '<div class="' + classname + '">', end: '</div>' }; }, data: function (input) { var dataAttr = ''; $.each(input, function (k, v) { dataAttr += ' data-' + k + '="' + v + '"'; }); return dataAttr; }, // returns bootstrap input group inputGroup: function (input) { var leftkeys = input.left ? Object.keys(input.left) : '', rightkeys = input.right ? Object.keys(input.right) : '', leftchoice = input.left ? leftkeys[$.inArray('text', leftkeys)] || leftkeys[$.inArray('button', leftkeys)] : '', rightchoice = input.right ? rightkeys[$.inArray('text', rightkeys)] || rightkeys[$.inArray('button', rightkeys)] : '', options = { left: input.left ? { text: '<span class="input-group-addon ' + (input.left.classname || '') + '">' + (input.left.text || '') + '</span>', button: '<span class="input-group-btn"><button class="' + (input.left.classname || '') + '" type="button">' + (input.left.button || '') + '</button></span>' } : '', right: input.right ? { text: '<span class="input-group-addon ' + (input.right.classname || '') + '">' + (input.right.text || '') + '</span>', button: '<span class="input-group-btn"><button class="' + (input.right.classname || '') + '" type="button">' + (input.right.button || '') + '</button></span>' } : '' }, sizes = { large: 'input-group-lg', lg: 'input-group-lg', small: 'input-group-sm', sm: 'input-group-sm' }; return { start: input && '<div class="input-group ' + (input.size && sizes[input.size] || '') + '">', left: input.left ? options.left[leftchoice] : '', right: input.right ? options.right[rightchoice] : '', end: input && '</div>' }; }, // fieldsets are used with checkboxes/radios fieldset: function (id, submitType) { return { start: '<fieldset id="' + id + '" class="nos-fieldset nos-submit-' + (submitType || "object") + '">', end: '</fieldset>' }; }, // returns a label label: function (input) { return '<label for="' + (input.id || input.name) + '" class="nos-label' + (input.required && ' required-field' || '') + '">' + input.label + '</label>'; }, // returns a label element lbl: function (input) { return '<label class="nos-label label-' + (input.name || input.id) + ' ' + (input.classname || '') + '">' + input.value + '</label>'; }, // returns text-based elements text: function (input) { var el = $.extend(_getAttrs(input), { pattern: input.pattern ? ' pattern="' + input.pattern + '"' : '', autocomplete: input.autocomplete ? ' autocomplete="' + input.autocomplete + '"' : '', step: input.step ? ' step="' + input.step + '"' : '', min: input.min ? ' min="' + input.min + '"' : '', max: input.max ? ' max="' + input.max + '"' : '', classname: input.classname ? ' class="form-control ' + input.classname + '"' : ' class="form-control"', inputGroup: input.inputGroup ? this.inputGroup(input.inputGroup) : { start: '', left: '', right: '', end: '' } }); var element = el.formGroup.start + el.label + el.inputGroup.start + el.inputGroup.left + '<input data-nos' + el.type + el.name + el.id + el.data + el.minlength + el.maxlength + el.placeholder + el.classname + el.tabindex + el.value + el.title + el.min + el.max + el.step + el.size + el.pattern + el.autocomplete + el.multiple + el.readonly + el.disabled + el.autofocus + el.required + '>' + el.inputGroup.right + el.inputGroup.end + el.helpBlock + el.message.required + el.message.minlength + el.message.maxlength + el.message.valid + el.message.min + el.message.max + el.formGroup.end; return element; }, // returns buttons buttons: function (input) { var nosgroup = input.formGroup ? 'nos-form-group nos-group ' : '', inline = input.inline ? 'nos-inline ' : '', align = input.align ? 'pull-' + input.align : '', prespace = input.align === 'right' ? '&nbsp;' : '', postspace = input.align === 'left' || input.align === undefined ? '&nbsp;' : ''; var el = $.extend(_getAttrs(input), { formaction: input.formaction ? ' formaction="' + input.formaction + '"' : '', formenctype: input.formenctype ? ' formenctype="' + input.formenctype + '"' : '', formmethod: input.formmethod ? ' formmethod="' + input.formmethod + '"' : '', formnovalidate: input.formnovalidate ? ' formnovalidate' : '', formtarget: input.formtarget ? ' formtarget="' + input.formtarget + '"' : '', value: input.value ? input.value : '', formGroup: (input.formGroup || input.inline) ? this.div(nosgroup + inline + align) : { start: '', end: '' } }); var element = el.formGroup.start + prespace + '<button data-nos' + el.type + el.id + el.data + el.classname + el.title + el.formtarget + el.formmethod + el.formaction + el.tabindex + el.formenctype + el.formnovalidate + el.disabled + '>' + el.value + '</button>' + postspace + el.formGroup.end; return element; }, // returns a button group buttonGroup: function (input) { var nosgroup = input.formGroup ? 'nos-form-group nos-group ' : '', align = input.align ? 'pull-' + input.align : '', vert = input.vertical ? '-vertical ' : ' '; var i, buttons = '', button = input.buttons; var el = $.extend(_getAttrs(input), { classname: input.classname ? ' class="btn-group' + vert + input.classname + '"' : ' class="btn-group' + vert + '"', formGroup: (input.formGroup || input.align) ? this.div(nosgroup + align) : { start: '', end: '' } }); for (i = 0; i < button.length; i++) { buttons += '<button data-nos type="' + button[i].type + '" class="' + button[i].classname + '" tabindex="' + button[i].tabindex + '">' + button[i].value + '</button>'; } var element = el.formGroup.start + el.label + '<br>' + '<div ' + el.classname + ' role="group">' + buttons + el.formGroup.end; return element; }, // returns textarea elements textarea: function (input) { var el = $.extend(_getAttrs(input), { rows: input.rows ? ' rows="' + input.rows + '"' : '', cols: input.cols ? ' cols="' + input.cols + '"' : '', wrap: input.wrap ? ' wrap="' + input.wrap + '"' : '', classname: input.classname ? ' class="form-control ' + input.classname + '"' : ' class="form-control"', value: (input.value || typeof input.value === 'number') ? input.value : '' }); var element = el.formGroup.start + el.label + '<textarea data-nos' + el.name + el.id + el.title + el.data + el.minlength + el.maxlength + el.placeholder + el.classname + el.tabindex + el.rows + el.cols + el.wrap + el.readonly + el.disabled + el.autofocus + el.required + '>' + el.value + '</textarea>' + el.helpBlock + el.message.required + el.message.minlength + el.message.maxlength + el.formGroup.end; return element; }, // returns select elements select: function (input) { var selOptions = {}, temp = {}; if (input.options.length) { $.each(input.options, function () { if (typeof this === 'object') { $.extend(selOptions, this); } else { temp[this] = this; $.extend(selOptions, temp); } }); } else { selOptions = input.options; } var options = ''; var el = $.extend(_getAttrs(input), { classname: input.classname ? ' class="form-control ' + input.classname + '"' : ' class="form-control"', selected: input.selected ? input.selected.toString().toLowerCase() : '', inputGroup: input.inputGroup ? this.inputGroup(input.inputGroup) : { start: '', left: '', right: '', end: '' } }); $.each(selOptions, function (k, v) { options += '<option value="' + k + '" ' + ((el.selected === k.toString().toLowerCase() || el.selected === v.toString().toLowerCase()) ? ' selected' : '') + '>' + v + '</option>'; }); var element = el.formGroup.start + el.label + el.inputGroup.start + el.inputGroup.left + '<select data-nos' + el.name + el.id + el.data + el.classname + el.multiple + el.title + el.size + el.readonly + el.tabindex + el.disabled + el.autofocus + el.required + '>' + options + '</select>' + el.inputGroup.right + el.inputGroup.end + el.helpBlock + el.message.required + el.formGroup.end; return element; }, // returns checkbox and radio elements check: function (input) { var inputOptions = {}, temp = {}; if (input.options.length) { $.each(input.options, function () { if (typeof this === 'object') { $.extend(inputOptions, this); } else { temp[this] = this; $.extend(inputOptions, temp); } }); } else { inputOptions = input.options; } var checked = ''; var el = $.extend(_getAttrs(input), { inline: input.inline ? ' class="' + input.type + '-inline"' : '', name: (input.name && input.type === 'checkbox') ? ' name="' + input.name + '[]' + '"' : ' name="' + input.name + '"', id: ' id="' + input.name + '-', div: !input.inline ? this.div(input.type) : { start: '', end: '' }, fieldset: this.fieldset(input.name, input.submitType) || { start: '', end: '' } }); var element = el.formGroup.start + el.label + el.fieldset.start; $.each(inputOptions, function (k, v) { if (typeof input.checked === 'object' || input.checked === undefined) { $.inArray(k, input.checked) > -1 ? checked = ' checked' : checked = ''; } else if (typeof input.checked === 'string') { k === input.checked ? checked = ' checked' : checked = ''; } else { console.warn('Your checkbox/radio "checked" property must be an object or string'); } element += el.div.start + '<label' + el.inline + '><input data-nos' + el.type + el.name + el.data + el.title + el.id + k + '" ' + el.classname + ' value="' + k + '"' + checked + el.disabled + el.autofocus + el.required + '>' + v + '</label>' + el.helpBlock + el.div.end; }); element += el.fieldset.end + el.message.required + el.formGroup.end; return element; }, // returns file elements file: function (input) { var el = $.extend(_getAttrs(input), { accept: input.accept && ' accept="' + input.accept + '"' || '', div: input.classname && this.div(input.classname) || { start: '', end: '' } }); var element = el.formGroup.start + el.label + el.div.start + '<input data-nos' + el.type + el.name + el.id + el.data + el.title + el.accept + el.multiple + el.tabindex + el.disabled + el.autofocus + el.required + '>' + el.helpBlock + el.message.required + el.div.end + el.formGroup.end; return element; }, // custom html element html: function (input) { return input.element; }, // returns input type range & color other: function (input) { var el = $.extend(_getAttrs(input), { step: input.step ? ' step="' + input.step + '"' : '', min: (input.min || typeof input.min === 'number') ? ' min="' + input.min + '"' : '', max: (input.max || typeof input.max === 'number') ? ' max="' + input.max + '"' : '', height: (input.height || typeof input.height === 'number') ? ' height="' + input.height + '"' : '', width: (input.width || typeof input.width === 'number') ? ' width="' + input.width + '"' : '', src: input.src ? ' src="' + input.src + '"' : '', alt: input.alt ? ' alt="' + input.alt + '"' : '' }); var element = el.formGroup.start + el.label + '<input data-nos' + el.type + el.name + el.id + el.data + el.classname + el.value + el.title + el.height + el.width + el.src + el.alt + el.tabindex + el.min + el.max + el.step + el.readonly + el.disabled + el.autofocus + el.required + '>' + el.helpBlock + el.message.required + el.message.min + el.message.max + el.formGroup.end; return element; }, // build clone fields clone: function (input) { var maxFields = (input.maxFields || 10) + 1, startFields = (input.start || 1), hideFields, element = '', i; // assign attributes var el = $.extend(_getAttrs(input), { placeholder: input.placeholder ? input.placeholder : '', classname: input.classname ? input.classname : 'form-control', addValue: input.addButtonValue ? input.addButtonValue : 'Add Field', removeValue: input.removeButtonValue ? input.removeButtonValue : 'Remove Field', addButtonClass: input.addButtonClass ? input.addButtonClass : 'btn btn-primary', removeButtonClass: input.removeButtonClass ? input.removeButtonClass : 'btn btn-danger', name: input.name ? ' name="' + input.name : '', message: { required: '<div style="display: none;" class="alert alert-danger nos-help nos-required msg-required-' + input.name + '">' + (input.label || 'This') + ' is a required field</div>' } }); element += el.formGroup.start; element += el.label; // loop to build clone input fields for (i = 1; i < maxFields; i++) { var addon = (input.addon || i); i <= startFields ? hideFields = '' : hideFields = ' hidden'; var div = this.div('input-group nos-input-group' + hideFields); element += el.formGroup.start + div.start + '<span class="input-group-addon nos-input-group-addon">' + (el.placeholder || '') + ' ' + addon + '</span>' + '<input data-nos type="text" class="nos-clone ' + el.classname + '"' + el.data + el.name + i + '[]" ' + el.required + '>' + div.end + el.formGroup.end; } element += el.formGroup.end; element += el.helpBlock; element += '<input type="button" data-nos-add-button class="' + el.addButtonClass + ' nos-form-group" value="' + el.addValue + '">&nbsp;<input type="button" data-nos-remove-button value="' + el.removeValue + '" class="' + el.removeButtonClass + ' nos-form-group">'; element += el.message.required; return element; }, // returns select box with 50 states/territories/Canadian Provinces // the user specifies what to include, and this function will combine the appropriate objects to create the select element state: function (input) { var defaultToUs = input.us || input.us === undefined; var territories = { "American Samoa": "AS", "Federated States Of Micronesia": "FM", "Guam": "GU", "Marshall Islands": "MH", "Northern Mariana Islands": "MP", "Palau": "PW", "Puerto Rico": "PR", "Virgin Islands": "VI" }; var states = { "Alabama": "AL", "Alaska": "AK", "Arizona": "AZ", "Arkansas": "AR", "California": "CA", "Colorado": "CO", "Connecticut": "CT", "Delaware": "DE", "District Of Columbia": "DC", "Florida": "FL", "Georgia": "GA", "Hawaii": "HI", "Idaho": "ID", "Illinois": "IL", "Indiana": "IN", "Iowa": "IA", "Kansas": "KS", "Kentucky": "KY", "Louisiana": "LA", "Maine": "ME", "Maryland": "MD", "Massachusetts": "MA", "Michigan": "MI", "Minnesota": "MN", "Mississippi": "MS", "Missouri": "MO", "Montana": "MT", "Nebraska": "NE", "Nevada": "NV", "New Hampshire": "NH", "New Jersey": "NJ", "New Mexico": "NM", "New York": "NY", "North Carolina": "NC", "North Dakota": "ND", "Ohio": "OH", "Oklahoma": "OK", "Oregon": "OR", "Pennsylvania": "PA", "Rhode Island": "RI", "South Carolina": "SC", "South Dakota": "SD", "Tennessee": "TN", "Texas": "TX", "Utah": "UT", "Vermont": "VT", "Virginia": "VA", "Washington": "WA", "West Virginia": "WV", "Wisconsin": "WI", "Wyoming": "WY" }; var provinces = { "Alberta": "AB", "British Columbia": "BC", "Manitoba": "MB", "New Brunswick": "NB", "Newfoundland and Labrador": "NL", "Nova Scotia": "NS", "Northwest Territories": "NT", "Nunavut": "NU", "Ontario": "ON", "Prince Edward Island": "PE", "Quebec": "QC", "Saskatchewan": "SK", "Yukon": "YT" }; var mexico = { "Aguascalientes": "AG", "Baja California": "BC", "Baja California Sur": "BS", "Campeche": "CM", "Chiapas": "CS", "Chihuahua": "CH", "Coahuila": "MX", "Colima": "CL", "Federal District": "DF", "Durango": "DG", "Guanajuato": "GT", "Guerrero": "GR", "Hidalgo": "HG", "Jalisco": "JA", "Mexico": "ME", "Michoacán": "MI", "Morelos": "MO", "Nayarit": "NA", "Nuevo León": "NL", "Oaxaca": "OA", "Puebla": "PU", "Querétaro": "QE", "Quintana Roo": "QR", "San Luis Potosí": "SL", "Sinaloa": "SI", "Sonora": "SO", "Tabasco": "TB", "Tamaulipas": "TM", "Tlaxcala": "TL", "Veracruz": "VE", "Yucatán": "YU", "Zacatecas": "ZA" }; // this function orders our state object alphabetically by key function sortObject(obj, order) { var key, i, tempArray = [], tempObj = {}; for (key in obj) { tempArray.push(key); } tempArray.sort(function (a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }); if (order === 'desc') { for (i = tempArray.length - 1; i >= 0; i--) { tempObj[tempArray[i]] = obj[tempArray[i]]; } } else { for (i = 0; i < tempArray.length; i++) { tempObj[tempArray[i]] = obj[tempArray[i]]; } } return tempObj; } // function to combine the objects and call the sort function once that is done // after object is complete, we create the html var stateObj = sortObject($.extend((defaultToUs && states), (input.usTerritory && territories), (input.canada && provinces), (input.mexico && mexico))), options = '<option value="">' + (input.defaultSelected || "Select One...") + '</option>'; var el = $.extend(_getAttrs(input), { classname: input.classname ? ' class="form-control ' + input.classname + '"' : ' class="form-control"', selected: input.selected ? input.selected.toString().toLowerCase() : '' }); $.each(stateObj, function (k, v) { options += '<option value="' + v + '" ' + ((el.selected === v.toString().toLowerCase() || el.selected === k.toString().toLowerCase()) ? ' selected' : '') + '>' + k + '</option>'; }); var element = el.formGroup.start + el.label + '<select data-nos ' + el.name + el.id + el.data + el.classname + el.size + el.multiple + el.readonly + el.tabindex + el.disabled + el.autofocus + el.required + '>' + options + '</select>' + el.helpBlock + el.message.required + el.formGroup.end; return element; } }; // field validation messages var _userErrorMessage = { required: function (el) { var message; el.messages ? message = el.messages.required : message = null; return '<div style="display: none;" class="alert alert-danger nos-help nos-required msg-required-' + el.name + '">' + (message || (el.label || el.placeholder || 'This') + ' is a required field') + '</div>'; }, valid: function (el) { var message; el.messages ? message = el.messages.invalid : message = null; return '<div style="display: none;" class="alert alert-warning nos-help nos-invalid msg-invalid-' + el.name + '">' + (message || (el.label || el.placeholder || 'This field') + ' must be valid') + '</div>'; }, minlength: function (el) { var message; el.messages ? message = el.messages.minlength : message = null; return '<div style="display: none;" class="alert alert-warning nos-help nos-invalid msg-minlength-' + el.name + '">' + (message || (el.label || el.placeholder || 'This field') + ' must have a minimum of ' + el.minlength + ' characters') + '</div>'; }, maxlength: function (el) { var message; el.messages ? message = el.messages.maxlength : message = null; return '<div style="display: none;" class="alert alert-warning nos-help nos-invalid msg-maxlength-' + el.name + '">' + (message || (el.label || el.placeholder || 'This field') + ' must have a maximum of ' + el.maxlength + ' characters') + '</div>'; }, min: function (el) { var message; el.messages ? message = el.messages.min : message = null; return '<div style="display: none;" class="alert alert-warning nos-help nos-invalid msg-min-' + el.name + '">' + (message || (el.label || el.placeholder || 'This field') + ' must have a minimum value of ' + el.min) + '</div>'; }, max: function (el) { var message; el.messages ? message = el.messages.max : message = null; return '<div style="display: none;" class="alert alert-warning nos-help nos-invalid msg-max-' + el.name + '">' + (message || (el.label || el.placeholder || 'This field') + ' must have a maximum value of ' + el.max) + '</div>'; }, form: { required: function (form) { return '<div style="display: none;" class="alert alert-danger nos-help nos-form-required msg-required-' + (form.name || form.id) + '">' + self.settings.messages.required + '</div>'; }, invalid: function (form) { return '<div style="display: none;" class="alert alert-warning nos-help nos-form-invalid msg-invalid-' + (form.name || form.id) + '">' + self.settings.messages.invalid + '</div>'; } } }; // field validation functions var _validator = { email: function (email) { return /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(email); }, phone: function (tel) { return /^[(]{0,1}[0-9]{3}[)]{0,1}[-\s\.]{0,1}[0-9]{3}[-\s\.]{0,1}[0-9]{4}$/.test(tel); }, zipcode: function (zip) { var usZip = /^\d{5}(-\d{4})?$/.test(zip); var mexicoZip = /^\\d{5}$/.test(zip); var canadaZip = /^[A-Za-z]\d[A-Za-z][ -]?\d[A-Za-z]\d$/.test(zip); return usZip || mexicoZip || canadaZip; }, // sanitizes text-based form inputs sanitize: function (input) { var output = input.replace(/<script[^>]*?>.*?<\/script>/gi, ''). replace(/<[\/\!]*?[^<>]*?>/gi, ''). replace(/<style[^>]*?>.*?<\/style>/gi, ''). replace(/<![\s\S]*?--[ \t\n\r]*>/gi, ''); return output; } }; // this function handles real time error messages while user is typing this._validate = function (fields) { // manage touched/untouched classes function manageTouchedFields() { var allFields = $($form + ' [data-nos]:not(:submit, :reset, :button, :image, :checkbox, :radio, input[type=color], input[type=range])'); allFields .addClass('nos-untouched') .on('focus change', function () { $(this).alterClass('nos-untouched', 'nos-touched'); }); } // resets form function reset() { $($form + ' :reset[data-nos]').off('click').on('click', function () { $(this).closest('form').find(':input:not(:submit, :reset, :button, :image)').val('').alterClass('nos-*', '').addClass('nos-untouched'); $($form + ' .nos-help').nosSlideUp(); }); } // checks maxlength on fields if browser doesn't catch/support it function maxLength(v) { var maxLengthId = (v.id || v.name); var msg = '.msg-maxlength-' + v.name; $('#' + maxLengthId) .on('keydown', function (e) { if ($(this).val().length > v.maxlength) { $(this).val($(this).val().substring(0, v.maxlength)); e.preventDefault(); } }) .on('blur keydown', function () { $(this).val().length > v.maxlength ? $(msg).nosSlideDown() : $(msg).nosSlideUp(); }); } // validates that the minlength has been met and displays/hides message to user function minLength(v) { var msg = '.msg-minlength-' + v.name; var id = '#' + (v.id || v.name); $(id).on('keyup change blur focus paste', function () { var minval = $(this).val().length; if (minval > 0) { minval < v.minlength ? ($(msg).nosSlideDown(), $(this).alterClass('nos-valid-minlength', 'nos-invalid-minlength')) : ($(msg).nosSlideUp(), $(this).alterClass('nos-invalid-minlength', 'nos-valid-minlength')); } else { $(msg).nosSlideUp(); $(this).removeClass('nos-invalid-minlength nos-valid-minlength'); } }).on('blur change', function () { $(this).removeClass('nos-valid-minlength'); }); } // validates the min and max attributes on number fields function minMax(v) { var minMsg = '.msg-min-' + v.name; var maxMsg = '.msg-max-' + v.name; var id = '#' + (v.id || v.name); $(id).on('keyup change blur focus paste submit', function () { var numVal = $(this).val(); if (numVal > 0) { numVal < v.min ? ($(minMsg).nosSlideDown(), $(this).alterClass('nos-valid-min', 'nos-invalid-min')) : ($(minMsg).nosSlideUp(), $(this).alterClass('nos-invalid-min', 'nos-valid-min')); numVal > v.max ? ($(maxMsg).nosSlideDown(), $(this).alterClass('nos-valid-max', 'nos-invalid-max')) : ($(maxMsg).nosSlideUp(), $(this).alterClass('nos-invalid-max', 'nos-valid-max')); } else { $(minMsg).nosSlideUp(); $(this).removeClass('nos-invalid-min nos-invalid-max'); } }).on('blur change', function () { $(this).removeClass('nos-valid-min nos-valid-max'); }); } // validates regex patterns function validatePattern(v) { var nm = v.name, msg = '.msg-invalid-' + nm, id = '#' + (v.id || nm), regex = new RegExp(v.pattern); $(id).on('keyup change blur focus paste', function () { if ($(this).val().length > 0) { regex.test($(this).val()) ? ($(msg).nosSlideUp(), $(this).alterClass('nos-invalid-pattern', 'nos-valid-pattern')) : ($(msg).nosSlideDown(), $(this).alterClass('nos-valid-pattern', 'nos-invalid-pattern')); } else { $(msg).nosSlideUp(); $(this).removeClass('nos-invalid-pattern nos-valid-pattern'); } }).on('blur change', function () { $(this).removeClass('nos-valid-pattern'); }); } function passwordMatch(v) { var id = '#' + v.match; var pwd = '#' + (v.id || v.name); var msg = '.msg-invalid-' + v.name; $(pwd + ',' + id).on('keyup change blur paste focus', function () { if ($(pwd).val().length > 0) { $(id).val() !== $(pwd).val() ? ($(msg).nosSlideDown(), $(pwd + ',' + id).alterClass('nos-valid-match', 'nos-invalid-match')) : ($(msg).nosSlideUp(), $(pwd + ',' + id).alterClass('nos-invalid-match', 'nos-valid-match')); } else { $(msg).nosSlideUp(); $(pwd + ',' + id).removeClass('nos-invalid-match nos-valid-match'); } }).on('blur change', function () { $(this).removeClass('nos-valid-match'); }); } // calls email/zip/phone validation functions and hides/displays messages to user function validateFields(v) { var nm = v.name, msg = $form + ' .msg-invalid-' + nm, id = '#' + (v.id || nm), emval; $(id).on('keyup input change blur paste focus', function () { switch (v.type) { // once the user starts to type, email/zip/tel will be sent through a validator and a // message will be displayed to warn user about invalid input // message will disappear once input is valid case 'email': emval = $(this).val(); if (emval.length > 0) { _validator.email(emval) ? ($(msg).nosSlideUp(), $(this).alterClass('nos-invalid-email', 'nos-valid-email')) : ($(msg).nosSlideDown(), $(this).alterClass('nos-valid-email', 'nos-invalid-email')); } emval === '' && ($(msg).nosSlideUp(), $(this).removeClass('nos-invalid-email nos-valid-email')); break; case 'zip': emval = $(this).val(); if (emval.length > 0) { _validator.zipcode(emval) ? ($(msg).nosSlideUp(), $(this).alterClass('nos-invalid-zip', 'nos-valid-zip')) : ($(msg).nosSlideDown(), $(this).alterClass('nos-valid-zip', 'nos-invalid-zip')); } emval === '' && ($(msg).nosSlideUp(), $(this).removeClass('nos-invalid-zip nos-valid-zip')); break; case 'tel': emval = $(this).val(); if (emval.length > 0) { _validator.phone(emval) ? ($(msg).nosSlideUp(), $(this).alterClass('nos-invalid-tel', 'nos-valid-tel')) : ($(msg).nosSlideDown(), $(this).alterClass('nos-valid-tel', 'nos-invalid-tel')); } emval === '' && ($(msg).nosSlideUp(), $(this).removeClass('nos-invalid-tel nos-valid-tel')); break; } }).on('blur change', function () { $(this).removeClass('nos-valid-email nos-valid-zip nos-valid-tel'); }); } function addMask(v) { if ($.maskWatchers) { $('#' + (v.id || v.name)).mask(v.mask); $('#' + (v.id || v.name)).attr('data-mask', true); } else { console.warn('You must include jQUery-Mask-Plugin to use "mask". Go here: https://igorescobar.github.io/jQuery-Mask-Plugin/'); } } function cloneButtons() { // clone field add button functionality $($form + ' [data-nos-add-button]').off('click').on('click', function () { $($form + ' .nos-input-group.hidden').length > 0 && $('.nos-input-group.hidden').eq(0).removeClass('hidden'); $($form + ' .nos-input-group.hidden').length === 0 && $(this).addClass('disabled'); $($form + ' .nos-input-group:not(.hidden)').length > 1 && $('[data-nos-remove-button]').removeClass('disabled'); }); // clone field remove button functionality $($form + ' [data-nos-remove-button]').off('click').on('click', function () { $($form + ' .nos-input-group:not(.hidden)').length > 1 && $('.nos-input-group:not(.hidden)').eq(-1).addClass('hidden'); $($form + ' .nos-input-group:not(.hidden)').length === 1 && $(this).addClass('disabled'); $($form + ' .nos-input-group.hidden').length > 0 && $('[data-nos-add-button]').removeClass('disabled'); }); } // calls the validation functions function callValidation(k, v) { // clone add/remove button functionality if (v.type === 'clone') cloneButtons(); // set mask if (v.mask) addMask(v); // call reset function if (v.type === 'reset') reset(); // call email/zip/phone validation function if ((v.type === 'email' || v.type === 'zip' || v.type === 'tel') && (v.validate || v.validate === undefined)) { validateFields(v); } // call pattern validation if (v.pattern) validatePattern(v); // call maxLength function if (v.maxlength) maxLength(v); // call minLength function if (v.minlength) minLength(v); // call min / max function if (v.min || v.max) minMax(v); // call password match function if (v.match) passwordMatch(v); } function findElements(column) { $.each(column, function (key, value) { callValidation(key, value); if (this.column) { findElements(this.column); } }); } // find out if the form is one column or multi-column // multi-column forms send multiple objects $.each(fields, function (k, v) { if (this.column) { callValidation(k, v); findElements(this.column); } else { callValidation(k, v); } }); // add touched/untouched classes manageTouchedFields(); }; // validation that runs on form submit this._submitValidation = function (data, evt) { // initializing form submit object var form = $(data).serializeArray(), formdata = {}, // selectors for required fields reqInput = $($form + ' [data-nos]:not(:radio, :checkbox, :button, :submit, :reset, :file, :image, select, .nos-clone)').filter('[required]:visible'), reqSR = $($form + ' select[data-nos]').filter('[required]:visible'), fileField = $($form + ' :file[data-nos]'), cbgroup = $($form + ' :checkbox[data-nos]').parents('fieldset'), cb = $($form + ' :checkbox[data-nos]').filter('[required]:visible').parents('fieldset'), radio = $($form + ' :radio[data-nos]').filter('[required]:visible').parents('fieldset'), requiredFields = $($form + ' [data-nos]:not(:file, input[type=range], input[type=color])').filter('[required]:visible'), clone = $($form + ' .nos-clone'); // assign serialized form object properties to new form submit object, unless it is a checkbox field function init() { $.each(form, function (key, value) { if (value.name.indexOf('[]') === -1 && (self.settings.onlySubmitWithValue ? (value.value && value.value !== "") : value)) { if (value.name.indexOf('.') > -1) { var notation = value.name.split('.'); if (!formdata[notation[0]]) formdata[notation[0]] = {}; formdata[notation[0]][notation[1]] = value.value; } else { formdata[value.name] = value.value; } } }); submitForm(); } function checkRequiredFields() { requiredFields.each(function () { ($(this).val().length < 1) ? $(this).alterClass('nos-valid-required', 'nos-invalid-required') : $(this).alterClass('nos-invalid-required', 'nos-valid-required'); $(this).on('change keyup keydown blur paste input', function () { ($(this).val().length < 1) ? $(this).alterClass('nos-valid-required', 'nos-invalid-required') : $(this).alterClass('nos-invalid-required', 'nos-valid-required'); }); }); } // build submit object for clone types function buildClone() { var cloneName = $(clone).parents().siblings('.nos-label').attr('for'); formdata[cloneName] = {}; $.each(clone, function () { var cloneFieldName = $(this).attr('name').split('[]'); if ($(this).val() !== "") { formdata[cloneName][cloneFieldName[0]] = $(this).val(); } }); } // create checkbox object for form submit response function cbSubmitObject() { var obj = {}; // checks if object is full of false values function allFalse(obj) { for (var i in obj) { if (obj[i] === true) return false; } return true; } cbgroup.each(function (i) { var str = $(this).attr('id'), fcb = $(this).find(':checkbox'), notation = str.split('.'), isStrObj = str.indexOf('.') > -1; if ($(this).hasClass('nos-submit-string')) { var arr = []; fcb.each(fu