nos-forms-jquery
Version:
Build and validate DRY html forms in minutes with JSON, jQuery and Bootstrap
940 lines (795 loc) • 72.2 kB
JavaScript
/*
* 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' ? ' ' : '',
postspace = input.align === 'left' || input.align === undefined ? ' ' : '';
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 + '"> <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