Blame | Last modification | View Log | RSS feed
/*** jQuery Form Validator* ------------------------------------------* Created by Victor Jonsson <http://www.victorjonsson.se>** @website http://formvalidator.net/* @license Dual licensed under the MIT or GPL Version 2 licenses* @version 2.2.beta.69*/(function ($) {'use strict';var $window = $(window),_getInputParentContainer = function ($elem) {if ($elem.valAttr('error-msg-container')) {return $($elem.valAttr('error-msg-container'));} else {var $parent = $elem.parent();if (!$parent.hasClass('form-group')) {var $formGroup = $parent.closest('.form-group');if ($formGroup.length) {return $formGroup.eq(0);}}return $parent;}},_applyErrorStyle = function ($elem, conf) {$elem.addClass(conf.errorElementClass).removeClass('valid');_getInputParentContainer($elem).addClass(conf.inputParentClassOnError).removeClass(conf.inputParentClassOnSuccess);if (conf.borderColorOnError !== '') {$elem.css('border-color', conf.borderColorOnError);}},_removeErrorStyle = function ($elem, conf) {$elem.each(function () {var $this = $(this);_setInlineErrorMessage($this, '', conf, conf.errorMessagePosition);$this.removeClass('valid').removeClass(conf.errorElementClass).css('border-color', '');_getInputParentContainer($this).removeClass(conf.inputParentClassOnError).removeClass(conf.inputParentClassOnSuccess).find('.' + conf.errorMessageClass) // remove inline span holding error message.remove();});},_setInlineErrorMessage = function ($input, mess, conf, $messageContainer) {var custom = document.getElementById($input.attr('name') + '_err_msg');if (custom) {custom.innerHTML = mess;}else if (typeof $messageContainer == 'object') {var $found = false;$messageContainer.find('.' + conf.errorMessageClass).each(function () {if (this.inputReferer == $input[0]) {$found = $(this);return false;}});if ($found) {if (!mess) {$found.remove();} else {$found.html(mess);}} else {var $mess = $('<div class="' + conf.errorMessageClass + '">' + mess + '</div>');$mess[0].inputReferer = $input[0];$messageContainer.prepend($mess);}}else {var $parent = _getInputParentContainer($input),$mess = $parent.find('.' + conf.errorMessageClass + '.help-block');if ($mess.length == 0) {$mess = $('<span></span>').addClass('help-block').addClass(conf.errorMessageClass);$mess.appendTo($parent);}$mess.html(mess);}},_templateMessage = function ($form, title, errorMessages, conf) {var messages = conf.errorMessageTemplate.messages.replace(/\{errorTitle\}/g, title),fields = [],container;$.each(errorMessages, function (i, msg) {fields.push(conf.errorMessageTemplate.field.replace(/\{msg\}/g, msg));});messages = messages.replace(/\{fields\}/g, fields.join(''));container = conf.errorMessageTemplate.container.replace(/\{errorMessageClass\}/g, conf.errorMessageClass);container = container.replace(/\{messages\}/g, messages);$form.children().eq(0).before(container);};/*** Assigns validateInputOnBlur function to elements blur event** @param {Object} language Optional, will override $.formUtils.LANG* @param {Object} settings Optional, will override the default settings* @return {jQuery}*/$.fn.validateOnBlur = function (language, settings) {this.find('input[data-validation],textarea[data-validation],select[data-validation]').bind('blur.validation', function () {$(this).validateInputOnBlur(language, settings, true, 'blur');});if (settings.validateCheckboxRadioOnClick) {// bind click event to validate on click for radio & checkboxes for nice UXthis.find('input[type=checkbox][data-validation],input[type=radio][data-validation]').bind('click.validation', function () {$(this).validateInputOnBlur(language, settings, true, 'click');});}return this;};/** Assigns validateInputOnBlur function to elements custom event* @param {Object} language Optional, will override $.formUtils.LANG* @param {Object} settings Optional, will override the default settings* * @return {jQuery}*/$.fn.validateOnEvent = function (language, settings) {this.find('input[data-validation][data-validation-event],textarea[data-validation][data-validation-event],select[data-validation][data-validation-event]').each(function () {var $el = $(this),etype = $el.valAttr("event");if (etype) {$el.bind(etype + ".validation", function () {$(this).validateInputOnBlur(language, settings, true, etype);});}});return this;};/*** fade in help message when input gains focus* fade out when input loses focus* <input data-help="The info that I want to display for the user when input is focused" ... />** @param {String} attrName - Optional, default is data-help* @return {jQuery}*/$.fn.showHelpOnFocus = function (attrName) {if (!attrName) {attrName = 'data-validation-help';}// Remove previously added event listenersthis.find('.has-help-txt').valAttr('has-keyup-event', false).removeClass('has-help-txt');// Add help text listenersthis.find('textarea,input').each(function () {var $elem = $(this),className = 'jquery_form_help_' + ($elem.attr('name') || '').replace(/(:|\.|\[|\])/g, ""),help = $elem.attr(attrName);if (help) {$elem.addClass('has-help-txt').unbind('focus.help').bind('focus.help', function () {var $help = $elem.parent().find('.' + className);if ($help.length == 0) {$help = $('<span />').addClass(className).addClass('help').addClass('help-block') // twitter bs.text(help).hide();$elem.after($help);}$help.fadeIn();}).unbind('blur.help').bind('blur.help', function () {$(this).parent().find('.' + className).fadeOut('slow');});}});return this;};/*** Validate single input when it loses focus* shows error message in a span element* that is appended to the parent element** @param {Object} [language] Optional, will override $.formUtils.LANG* @param {Object} [conf] Optional, will override the default settings* @param {Boolean} attachKeyupEvent Optional* @param {String} eventType* @return {jQuery}*/$.fn.validateInputOnBlur = function (language, conf, attachKeyupEvent, eventType) {$.formUtils.eventType = eventType;if ((this.valAttr('suggestion-nr') || this.valAttr('postpone') || this.hasClass('hasDatepicker')) && !window.postponedValidation) {// This validation has to be postponedvar _self = this,postponeTime = this.valAttr('postpone') || 200;window.postponedValidation = function () {_self.validateInputOnBlur(language, conf, attachKeyupEvent, eventType);window.postponedValidation = false;};setTimeout(function () {if (window.postponedValidation) {window.postponedValidation();}}, postponeTime);return this;}language = $.extend({}, $.formUtils.LANG, language || {});_removeErrorStyle(this, conf);var $elem = this,$form = $elem.closest("form"),validationRule = $elem.attr(conf.validationRuleAttribute),validation = $.formUtils.validateInput($elem,language,$.extend({}, conf, {errorMessagePosition: 'element'}),$form,eventType);if (validation === true) {$elem.addClass('valid');_getInputParentContainer($elem).addClass(conf.inputParentClassOnSuccess);}else if (validation !== null) {_applyErrorStyle($elem, conf);_setInlineErrorMessage($elem, validation, conf, conf.errorMessagePosition);if (attachKeyupEvent) {$elem.unbind('keyup.validation').bind('keyup.validation', function () {$(this).validateInputOnBlur(language, conf, false, 'keyup');});}}return this;};/*** Short hand for fetching/adding/removing element attributes* prefixed with 'data-validation-'** @param {String} name* @param {String|Boolean} [val]* @return string|undefined* @protected*/$.fn.valAttr = function (name, val) {if (val === undefined) {return this.attr('data-validation-' + name);} else if (val === false || val === null) {return this.removeAttr('data-validation-' + name);} else {if (name.length > 0) name = '-' + name;return this.attr('data-validation' + name, val);}};/*** Function that validates all inputs in active form** @param {Object} [language]* @param {Object} [conf]* @param {Boolean} [displayError] Defaults to true*/$.fn.isValid = function (language, conf, displayError) {if ($.formUtils.isLoadingModules) {var $self = this;setTimeout(function () {$self.isValid(language, conf, displayError);}, 200);return null;}conf = $.extend({}, $.formUtils.defaultConfig(), conf || {});language = $.extend({}, $.formUtils.LANG, language || {});displayError = displayError !== false;if ($.formUtils.errorDisplayPreventedWhenHalted) {// isValid() was called programmatically with argument displayError set// to false when the validation was halted by any of the validatorsdelete $.formUtils.errorDisplayPreventedWhenHalteddisplayError = false;}$.formUtils.isValidatingEntireForm = true;$.formUtils.haltValidation = false;/*** Adds message to error message stack if not already in the message stack** @param {String} mess* @para {jQuery} $elem*/var addErrorMessage = function (mess, $elem) {if ($.inArray(mess, errorMessages) < 0) {errorMessages.push(mess);}errorInputs.push($elem);$elem.attr('current-error', mess);if (displayError)_applyErrorStyle($elem, conf);},/** Holds inputs (of type checkox or radio) already validated, to prevent recheck of mulitple checkboxes & radios */checkedInputs = [],/** Error messages for this validation */errorMessages = [],/** Input elements which value was not valid */errorInputs = [],/** Form instance */$form = this,/*** Tells whether or not to validate element with this name and of this type** @param {String} name* @param {String} type* @return {Boolean}*/ignoreInput = function (name, type) {if (type === 'submit' || type === 'button' || type == 'reset') {return true;}return $.inArray(name, conf.ignore || []) > -1;};// Reset style and remove error classif (displayError) {$form.find('.' + conf.errorMessageClass + '.alert').remove();_removeErrorStyle($form.find('.' + conf.errorElementClass + ',.valid'), conf);}// Validate element values$form.find('input,textarea,select').filter(':not([type="submit"],[type="button"])').each(function () {var $elem = $(this),elementType = $elem.attr('type'),isCheckboxOrRadioBtn = elementType == 'radio' || elementType == 'checkbox',elementName = $elem.attr('name');if (!ignoreInput(elementName, elementType) && (!isCheckboxOrRadioBtn || $.inArray(elementName, checkedInputs) < 0)) {if (isCheckboxOrRadioBtn)checkedInputs.push(elementName);var validation = $.formUtils.validateInput($elem,language,conf,$form,'submit');if (validation != null) {if (validation !== true) {addErrorMessage(validation, $elem);} else {$elem.valAttr('current-error', false).addClass('valid');_getInputParentContainer($elem).addClass(conf.inputParentClassOnSuccess);}}}});// Run validation callbackif (typeof conf.onValidate == 'function') {var errors = conf.onValidate($form);if ($.isArray(errors)) {$.each(errors, function (i, err) {addErrorMessage(err.message, err.element);});}else if (errors && errors.element && errors.message) {addErrorMessage(errors.message, errors.element);}}// Reset form validation flag$.formUtils.isValidatingEntireForm = false;// Validation failedif (!$.formUtils.haltValidation && errorInputs.length > 0) {if (displayError) {// display all error messages in top of formif (conf.errorMessagePosition === 'top') {_templateMessage($form, language.errorTitle, errorMessages, conf);}// Customize display messageelse if (conf.errorMessagePosition === 'custom') {if (typeof conf.errorMessageCustom === 'function') {conf.errorMessageCustom($form, language.errorTitle, errorMessages, conf);}}// Display error message below input field or in defined containerelse {$.each(errorInputs, function (i, $input) {_setInlineErrorMessage($input, $input.attr('current-error'), conf, conf.errorMessagePosition);});}if (conf.scrollToTopOnError) {$window.scrollTop($form.offset().top - 20);}}return false;}if (!displayError && $.formUtils.haltValidation) {$.formUtils.errorDisplayPreventedWhenHalted = true;}return !$.formUtils.haltValidation;};/*** @deprecated* @param language* @param conf*/$.fn.validateForm = function (language, conf) {if (window.console && typeof window.console.warn == 'function') {window.console.warn('Use of deprecated function $.validateForm, use $.isValid instead');}return this.isValid(language, conf, true);}/*** Plugin for displaying input length restriction*/$.fn.restrictLength = function (maxLengthElement) {new $.formUtils.lengthRestriction(this, maxLengthElement);return this;};/*** Add suggestion dropdown to inputs having data-suggestions with a comma* separated string with suggestions* @param {Array} [settings]* @returns {jQuery}*/$.fn.addSuggestions = function (settings) {var sugs = false;this.find('input').each(function () {var $field = $(this);sugs = $.split($field.attr('data-suggestions'));if (sugs.length > 0 && !$field.hasClass('has-suggestions')) {$.formUtils.suggest($field, sugs, settings);$field.addClass('has-suggestions');}});return this;};/*** A bit smarter split function* delimiter can be space, comma, dash or pipe* @param {String} val* @param {Function|String} [func]* @returns {Array|void}*/$.split = function (val, func) {if (typeof func != 'function') {// return arrayif (!val)return [];var values = [];$.each(val.split(func ? func : /[,|\-\s]\s*/g),function (i, str) {str = $.trim(str);if (str.length)values.push(str);});return values;} else if (val) {// exec callback func on each$.each(val.split(/[,|\-\s]\s*/g),function (i, str) {str = $.trim(str);if (str.length)return func(str, i);});}};/*** Short hand function that makes the validation setup require less code* @param conf*/$.validate = function (conf) {var defaultConf = $.extend($.formUtils.defaultConfig(), {form: 'form',/** Enable custom event for validation*/validateOnEvent: true,validateOnBlur: true,validateCheckboxRadioOnClick: true,showHelpOnFocus: true,addSuggestions: true,modules: '',onModulesLoaded: null,language: false,onSuccess: false,onError: false,onElementValidate: false,});conf = $.extend(defaultConf, conf || {});// Add validation to forms$(conf.form).each(function (i, form) {var $form = $(form);$window.trigger('formValidationSetup', [$form]);// Remove all event listeners previously added$form.find('.has-help-txt').unbind('focus.validation').unbind('blur.validation');$form.removeClass('has-validation-callback').unbind('submit.validation').unbind('reset.validation').find('input[data-validation],textarea[data-validation]').unbind('blur.validation');// Validate when submitted$form.bind('submit.validation', function () {var $form = $(this);if ($.formUtils.haltValidation) {// pressing several times on submit button while validation is haltedreturn false;}if ($.formUtils.isLoadingModules) {setTimeout(function () {$form.trigger('submit.validation');}, 200);return false;}var valid = $form.isValid(conf.language, conf);if ($.formUtils.haltValidation) {// Validation got halted by one of the validatorsreturn false;} else {if (valid && typeof conf.onSuccess == 'function') {var callbackResponse = conf.onSuccess($form);if (callbackResponse === false)return false;} else if (!valid && typeof conf.onError == 'function') {conf.onError($form);return false;} else {return valid;}}}).bind('reset.validation', function () {// remove messages$(this).find('.' + conf.errorMessageClass + '.alert').remove();_removeErrorStyle($(this).find('.' + conf.errorElementClass + ',.valid'), conf);}).addClass('has-validation-callback');if (conf.showHelpOnFocus) {$form.showHelpOnFocus();}if (conf.addSuggestions) {$form.addSuggestions();}if (conf.validateOnBlur) {$form.validateOnBlur(conf.language, conf);$form.bind('html5ValidationAttrsFound', function () {$form.validateOnBlur(conf.language, conf);})}if (conf.validateOnEvent) {$form.validateOnEvent(conf.language, conf);}});if (conf.modules != '') {if (typeof conf.onModulesLoaded == 'function') {$window.one('validatorsLoaded', conf.onModulesLoaded);}$.formUtils.loadModules(conf.modules);}};/*** Object containing utility methods for this plugin*/$.formUtils = {/*** Default config for $(...).isValid();*/defaultConfig: function () {return {ignore: [], // Names of inputs not to be validated even though node attribute containing the validation rules tells us toerrorElementClass: 'error', // Class that will be put on elements which value is invalidborderColorOnError: 'red', // Border color of elements which value is invalid, empty string to not change border colorerrorMessageClass: 'form-error', // class name of div containing error messages when validation failsvalidationRuleAttribute: 'data-validation', // name of the attribute holding the validation rulesvalidationErrorMsgAttribute: 'data-validation-error-msg', // define custom err msg inline with elementerrorMessagePosition: 'element', // Can be either "top" or "element" or "custom"errorMessageTemplate: {container: '<div class="{errorMessageClass} alert alert-danger">{messages}</div>',messages: '<strong>{errorTitle}</strong><ul>{fields}</ul>',field: '<li>{msg}</li>'},errorMessageCustom: _templateMessage,scrollToTopOnError: true,dateFormat: 'yyyy-mm-dd',addValidClassOnAll: false, // whether or not to apply class="valid" even if the input wasn't validateddecimalSeparator: '.',inputParentClassOnError: 'has-error', // twitter-bootstrap default class nameinputParentClassOnSuccess: 'has-success' // twitter-bootstrap default class name}},/*** Available validators*/validators: {},/*** Events triggered by form validator*/_events: {load: [], valid: [], invalid: []},/*** Setting this property to true during validation will* stop further validation from taking place and form will* not be sent*/haltValidation: false,/*** This variable will be true $.fn.isValid() is called* and false when $.fn.validateOnBlur is called*/isValidatingEntireForm: false,/*** Function for adding a validator* @param {Object} validator*/addValidator: function (validator) {// prefix with "validate_" for backward compatibility reasonsvar name = validator.name.indexOf('validate_') === 0 ? validator.name : 'validate_' + validator.name;if (validator.validateOnKeyUp === undefined)validator.validateOnKeyUp = true;this.validators[name] = validator;},/*** @var {Boolean}*/isLoadingModules: false,/*** @var {Object}*/loadedModules: {},/*** @example* $.formUtils.loadModules('date, security.dev');** Will load the scripts date.js and security.dev.js from the* directory where this script resides. If you want to load* the modules from another directory you can use the* path argument.** The script will be cached by the browser unless the module* name ends with .dev** @param {String} modules - Comma separated string with module file names (no directory nor file extension)* @param {String} [path] - Optional, path where the module files is located if their not in the same directory as the core modules* @param {Boolean} [fireEvent] - Optional, whether or not to fire event 'load' when modules finished loading*/loadModules: function (modules, path, fireEvent) {if (fireEvent === undefined)fireEvent = true;if ($.formUtils.isLoadingModules) {setTimeout(function () {$.formUtils.loadModules(modules, path, fireEvent);});return;}var hasLoadedAnyModule = false,loadModuleScripts = function (modules, path) {var moduleList = $.split(modules),numModules = moduleList.length,moduleLoadedCallback = function () {numModules--;if (numModules == 0) {$.formUtils.isLoadingModules = false;if (fireEvent && hasLoadedAnyModule) {$window.trigger('validatorsLoaded');}}};if (numModules > 0) {$.formUtils.isLoadingModules = true;}var cacheSuffix = '?__=' + ( new Date().getTime() ),appendToElement = document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0];$.each(moduleList, function (i, modName) {modName = $.trim(modName);if (modName.length == 0) {moduleLoadedCallback();}else {var scriptUrl = path + modName + (modName.substr(-3) == '.js' ? '' : '.js'),script = document.createElement('SCRIPT');if (scriptUrl in $.formUtils.loadedModules) {// already loadedmoduleLoadedCallback();}else {// Remember that this script is loaded$.formUtils.loadedModules[scriptUrl] = 1;hasLoadedAnyModule = true;// Load the scriptscript.type = 'text/javascript';script.onload = moduleLoadedCallback;script.src = scriptUrl + ( scriptUrl.substr(-7) == '.dev.js' ? cacheSuffix : '' );script.onreadystatechange = function () {// IE 7 fixif (this.readyState == 'complete' || this.readyState == 'loaded') {moduleLoadedCallback();// Handle memory leak in IEthis.onload = null;this.onreadystatechange = null;}};appendToElement.appendChild(script);}}});};if (path) {loadModuleScripts(modules, path);} else {var findScriptPathAndLoadModules = function () {var foundPath = false;$('script[src*="form-validator"]').each(function () {foundPath = this.src.substr(0, this.src.lastIndexOf('/')) + '/';if (foundPath == '/')foundPath = '';return false;});if (foundPath !== false) {loadModuleScripts(modules, foundPath);return true;}return false;};if (!findScriptPathAndLoadModules()) {$(findScriptPathAndLoadModules);}}},/*** Validate the value of given element according to the validation rules* found in the attribute data-validation. Will return null if no validation* should take place, returns true if valid or error message if not valid** @param {jQuery} $elem* @param {Object} language ($.formUtils.LANG)* @param {Object} conf* @param {jQuery} $form* @param {String} [eventContext]* @return {String|Boolean}*/validateInput: function ($elem, language, conf, $form, eventContext) {if ($elem.attr('disabled'))return null; // returning null will prevent that the valid class gets applied to the element$elem.trigger('beforeValidation');var value = $elem.val() || '',optional = $elem.valAttr('optional'),// test if a checkbox forces this element to be validatedvalidationDependsOnCheckedInput = false,validationDependentInputIsChecked = false,validateIfCheckedElement = false,// get value of this element's attribute "... if-checked"validateIfCheckedElementName = $elem.valAttr("if-checked");// make sure we can proceedif (validateIfCheckedElementName != null) {// Set the boolean telling us that the validation depends// on another input being checkedvalidationDependsOnCheckedInput = true;// select the checkbox type element in this formvalidateIfCheckedElement = $form.find('input[name="' + validateIfCheckedElementName + '"]');// test if it's property "checked" is checkedif (validateIfCheckedElement.prop('checked')) {// set value for validation checkpointvalidationDependentInputIsChecked = true;}}// validation checkpoint// if empty AND optional attribute is present// OR depending on a checkbox being checked AND checkbox is checked, return trueif ((!value && optional === 'true') || (validationDependsOnCheckedInput && !validationDependentInputIsChecked)) {return conf.addValidClassOnAll ? true : null;}var validationRules = $elem.attr(conf.validationRuleAttribute),// see if form element has inline err msg attributevalidationErrorMsg = true;if (!validationRules) {return conf.addValidClassOnAll ? true : null;}$.split(validationRules, function (rule) {if (rule.indexOf('validate_') !== 0) {rule = 'validate_' + rule;}var validator = $.formUtils.validators[rule];if (validator && typeof validator['validatorFunction'] == 'function') {// special change of element for checkbox_group ruleif (rule == 'validate_checkbox_group') {// set element to first in group, so error msg attr doesn't need to be set on all elements in group$elem = $("[name='" + $elem.attr('name') + "']:eq(0)");}var isValid = null;if (eventContext != 'keyup' || validator.validateOnKeyUp) {isValid = validator.validatorFunction(value, $elem, conf, language, $form);}if (!isValid) {validationErrorMsg = null;if (isValid !== null) {validationErrorMsg = $elem.attr(conf.validationErrorMsgAttribute + '-' + rule.replace('validate_', ''));if (!validationErrorMsg) {validationErrorMsg = $elem.attr(conf.validationErrorMsgAttribute);if (!validationErrorMsg) {validationErrorMsg = language[validator.errorMessageKey];if (!validationErrorMsg)validationErrorMsg = validator.errorMessage;}}}return false; // breaks the iteration}} else {throw new Error('Using undefined validator "' + rule + '"');}}, ' ');var result;if (typeof validationErrorMsg == 'string') {$elem.trigger('validation', false);result = validationErrorMsg;} else if (validationErrorMsg === null && !conf.addValidClassOnAll) {result = null;} else {$elem.trigger('validation', true);result = true;}// Run element validation callbackif (typeof conf.onElementValidate == 'function' && result !== null) {conf.onElementValidate((result === true), $elem, $form, validationErrorMsg);}return result;},/*** Is it a correct date according to given dateFormat. Will return false if not, otherwise* an array 0=>year 1=>month 2=>day** @param {String} val* @param {String} dateFormat* @return {Array}|{Boolean}*/parseDate: function (val, dateFormat) {var divider = dateFormat.replace(/[a-zA-Z]/gi, '').substring(0, 1),regexp = '^',formatParts = dateFormat.split(divider || null),matches, day, month, year;$.each(formatParts, function (i, part) {regexp += (i > 0 ? '\\' + divider : '') + '(\\d{' + part.length + '})';});regexp += '$';matches = val.match(new RegExp(regexp));if (matches === null) {return false;}var findDateUnit = function (unit, formatParts, matches) {for (var i = 0; i < formatParts.length; i++) {if (formatParts[i].substring(0, 1) === unit) {return $.formUtils.parseDateInt(matches[i + 1]);}}return -1;};month = findDateUnit('m', formatParts, matches);day = findDateUnit('d', formatParts, matches);year = findDateUnit('y', formatParts, matches);if ((month === 2 && day > 28 && (year % 4 !== 0 || year % 100 === 0 && year % 400 !== 0))|| (month === 2 && day > 29 && (year % 4 === 0 || year % 100 !== 0 && year % 400 === 0))|| month > 12 || month === 0) {return false;}if ((this.isShortMonth(month) && day > 30) || (!this.isShortMonth(month) && day > 31) || day === 0) {return false;}return [year, month, day];},/*** skum fix. är talet 05 eller lägre ger parseInt rätt int annars får man 0 när man kör parseInt?** @param {String} val* @param {Number}*/parseDateInt: function (val) {if (val.indexOf('0') === 0) {val = val.replace('0', '');}return parseInt(val, 10);},/*** Has month only 30 days?** @param {Number} m* @return {Boolean}*/isShortMonth: function (m) {return (m % 2 === 0 && m < 7) || (m % 2 !== 0 && m > 7);},/*** Restrict input length** @param {jQuery} $inputElement Jquery Html object* @param {jQuery} $maxLengthElement jQuery Html Object* @return void*/lengthRestriction: function ($inputElement, $maxLengthElement) {// read maxChars from counter display initial text valuevar maxChars = parseInt($maxLengthElement.text(), 10),charsLeft = 0,// internal function does the counting and sets display valuecountCharacters = function () {var numChars = $inputElement.val().length;if (numChars > maxChars) {// get current scroll bar positionvar currScrollTopPos = $inputElement.scrollTop();// trim value to max length$inputElement.val($inputElement.val().substring(0, maxChars));$inputElement.scrollTop(currScrollTopPos);}charsLeft = maxChars - numChars;if (charsLeft < 0)charsLeft = 0;// set counter text$maxLengthElement.text(charsLeft);};// bind events to this element// setTimeout is needed, cut or paste fires before val is available$($inputElement).bind('keydown keyup keypress focus blur', countCharacters).bind('cut paste', function () {setTimeout(countCharacters, 100);});// count chars on pageload, if there are prefilled input-values$(document).bind("ready", countCharacters);},/*** Test numeric against allowed range** @param $value int* @param $rangeAllowed str; (1-2, min1, max2)* @return array*/numericRangeCheck: function (value, rangeAllowed) {// split by dashvar range = $.split(rangeAllowed);// min or maxvar minmax = parseInt(rangeAllowed.substr(3), 10)// range ?if (range.length == 2 && (value < parseInt(range[0], 10) || value > parseInt(range[1], 10) )) {return [ "out", range[0], range[1] ];} // value is out of rangeelse if (rangeAllowed.indexOf('min') === 0 && (value < minmax )) // min{return ["min", minmax];} // value is below minelse if (rangeAllowed.indexOf('max') === 0 && (value > minmax )) // max{return ["max", minmax];} // value is above max// since no other returns executed, value is in allowed rangereturn [ "ok" ];},_numSuggestionElements: 0,_selectedSuggestion: null,_previousTypedVal: null,/*** Utility function that can be used to create plugins that gives* suggestions when inputs is typed into* @param {jQuery} $elem* @param {Array} suggestions* @param {Object} settings - Optional* @return {jQuery}*/suggest: function ($elem, suggestions, settings) {var conf = {css: {maxHeight: '150px',background: '#FFF',lineHeight: '150%',textDecoration: 'underline',overflowX: 'hidden',overflowY: 'auto',border: '#CCC solid 1px',borderTop: 'none',cursor: 'pointer'},activeSuggestionCSS: {background: '#E9E9E9'}},setSuggsetionPosition = function ($suggestionContainer, $input) {var offset = $input.offset();$suggestionContainer.css({width: $input.outerWidth(),left: offset.left + 'px',top: (offset.top + $input.outerHeight()) + 'px'});};if (settings)$.extend(conf, settings);conf.css['position'] = 'absolute';conf.css['z-index'] = 9999;$elem.attr('autocomplete', 'off');if (this._numSuggestionElements === 0) {// Re-position suggestion container if window size changes$window.bind('resize', function () {$('.jquery-form-suggestions').each(function () {var $container = $(this),suggestID = $container.attr('data-suggest-container');setSuggsetionPosition($container, $('.suggestions-' + suggestID).eq(0));});});}this._numSuggestionElements++;var onSelectSuggestion = function ($el) {var suggestionId = $el.valAttr('suggestion-nr');$.formUtils._selectedSuggestion = null;$.formUtils._previousTypedVal = null;$('.jquery-form-suggestion-' + suggestionId).fadeOut('fast');};$elem.data('suggestions', suggestions).valAttr('suggestion-nr', this._numSuggestionElements).unbind('focus.suggest').bind('focus.suggest', function () {$(this).trigger('keyup');$.formUtils._selectedSuggestion = null;}).unbind('keyup.suggest').bind('keyup.suggest', function () {var $input = $(this),foundSuggestions = [],val = $.trim($input.val()).toLocaleLowerCase();if (val == $.formUtils._previousTypedVal) {return;}else {$.formUtils._previousTypedVal = val;}var hasTypedSuggestion = false,suggestionId = $input.valAttr('suggestion-nr'),$suggestionContainer = $('.jquery-form-suggestion-' + suggestionId);$suggestionContainer.scrollTop(0);// Find the right suggestionsif (val != '') {var findPartial = val.length > 2;$.each($input.data('suggestions'), function (i, suggestion) {var lowerCaseVal = suggestion.toLocaleLowerCase();if (lowerCaseVal == val) {foundSuggestions.push('<strong>' + suggestion + '</strong>');hasTypedSuggestion = true;return false;} else if (lowerCaseVal.indexOf(val) === 0 || (findPartial && lowerCaseVal.indexOf(val) > -1)) {foundSuggestions.push(suggestion.replace(new RegExp(val, 'gi'), '<strong>$&</strong>'));}});}// Hide suggestion containerif (hasTypedSuggestion || (foundSuggestions.length == 0 && $suggestionContainer.length > 0)) {$suggestionContainer.hide();}// Create suggestion container if not already existselse if (foundSuggestions.length > 0 && $suggestionContainer.length == 0) {$suggestionContainer = $('<div></div>').css(conf.css).appendTo('body');$elem.addClass('suggestions-' + suggestionId);$suggestionContainer.attr('data-suggest-container', suggestionId).addClass('jquery-form-suggestions').addClass('jquery-form-suggestion-' + suggestionId);}// Show hidden containerelse if (foundSuggestions.length > 0 && !$suggestionContainer.is(':visible')) {$suggestionContainer.show();}// add suggestionsif (foundSuggestions.length > 0 && val.length != foundSuggestions[0].length) {// put container in place every time, just in casesetSuggsetionPosition($suggestionContainer, $input);// Add suggestions HTML to container$suggestionContainer.html('');$.each(foundSuggestions, function (i, text) {$('<div></div>').append(text).css({overflow: 'hidden',textOverflow: 'ellipsis',whiteSpace: 'nowrap',padding: '5px'}).addClass('form-suggest-element').appendTo($suggestionContainer).click(function () {$input.focus();$input.val($(this).text());onSelectSuggestion($input);});});}}).unbind('keydown.validation').bind('keydown.validation', function (e) {var code = (e.keyCode ? e.keyCode : e.which),suggestionId,$suggestionContainer,$input = $(this);if (code == 13 && $.formUtils._selectedSuggestion !== null) {suggestionId = $input.valAttr('suggestion-nr');$suggestionContainer = $('.jquery-form-suggestion-' + suggestionId);if ($suggestionContainer.length > 0) {var newText = $suggestionContainer.find('div').eq($.formUtils._selectedSuggestion).text();$input.val(newText);onSelectSuggestion($input);e.preventDefault();}}else {suggestionId = $input.valAttr('suggestion-nr');$suggestionContainer = $('.jquery-form-suggestion-' + suggestionId);var $suggestions = $suggestionContainer.children();if ($suggestions.length > 0 && $.inArray(code, [38, 40]) > -1) {if (code == 38) { // key upif ($.formUtils._selectedSuggestion === null)$.formUtils._selectedSuggestion = $suggestions.length - 1;else$.formUtils._selectedSuggestion--;if ($.formUtils._selectedSuggestion < 0)$.formUtils._selectedSuggestion = $suggestions.length - 1;}else if (code == 40) { // key downif ($.formUtils._selectedSuggestion === null)$.formUtils._selectedSuggestion = 0;else$.formUtils._selectedSuggestion++;if ($.formUtils._selectedSuggestion > ($suggestions.length - 1))$.formUtils._selectedSuggestion = 0;}// Scroll in suggestion windowvar containerInnerHeight = $suggestionContainer.innerHeight(),containerScrollTop = $suggestionContainer.scrollTop(),suggestionHeight = $suggestionContainer.children().eq(0).outerHeight(),activeSuggestionPosY = suggestionHeight * ($.formUtils._selectedSuggestion);if (activeSuggestionPosY < containerScrollTop || activeSuggestionPosY > (containerScrollTop + containerInnerHeight)) {$suggestionContainer.scrollTop(activeSuggestionPosY);}$suggestions.removeClass('active-suggestion').css('background', 'none').eq($.formUtils._selectedSuggestion).addClass('active-suggestion').css(conf.activeSuggestionCSS);e.preventDefault();return false;}}}).unbind('blur.suggest').bind('blur.suggest', function () {onSelectSuggestion($(this));});return $elem;},/*** Error dialogs** @var {Object}*/LANG: {errorTitle: 'Form submission failed!',requiredFields: 'You have not answered all required fields',badTime: 'You have not given a correct time',badEmail: 'You have not given a correct e-mail address',badTelephone: 'You have not given a correct phone number',badSecurityAnswer: 'You have not given a correct answer to the security question',badDate: 'You have not given a correct date',lengthBadStart: 'The input value must be between ',lengthBadEnd: ' characters',lengthTooLongStart: 'The input value is longer than ',lengthTooShortStart: 'The input value is shorter than ',notConfirmed: 'Input values could not be confirmed',badDomain: 'Incorrect domain value',badUrl: 'The input value is not a correct URL',badCustomVal: 'The input value is incorrect',andSpaces: ' and spaces ',badInt: 'The input value was not a correct number',badSecurityNumber: 'Your social security number was incorrect',badUKVatAnswer: 'Incorrect UK VAT Number',badStrength: 'The password isn\'t strong enough',badNumberOfSelectedOptionsStart: 'You have to choose at least ',badNumberOfSelectedOptionsEnd: ' answers',badAlphaNumeric: 'The input value can only contain alphanumeric characters ',badAlphaNumericExtra: ' and ',wrongFileSize: 'The file you are trying to upload is too large (max %s)',wrongFileType: 'Only files of type %s is allowed',groupCheckedRangeStart: 'Please choose between ',groupCheckedTooFewStart: 'Please choose at least ',groupCheckedTooManyStart: 'Please choose a maximum of ',groupCheckedEnd: ' item(s)',badCreditCard: 'The credit card number is not correct',badCVV: 'The CVV number was not correct'}};/* * * * * * * * * * * * * * * * * * * * * *CORE VALIDATORS* * * * * * * * * * * * * * * * * * * * *//** Validate email*/$.formUtils.addValidator({name: 'email',validatorFunction: function (email) {var emailParts = email.toLowerCase().split('@');if (emailParts.length == 2) {return $.formUtils.validators.validate_domain.validatorFunction(emailParts[1]) && !(/[^\w\+\.\-]/.test(emailParts[0])) && emailParts[0].length > 0;}return false;},errorMessage: '',errorMessageKey: 'badEmail'});/** Validate domain name*/$.formUtils.addValidator({name: 'domain',validatorFunction: function (val) {return val.length > 0 &&val.length <= 253 && // Including sub domains!(/[^a-zA-Z0-9]/.test(val.substr(-2))) && !(/[^a-zA-Z]/.test(val.substr(0, 1))) && !(/[^a-zA-Z0-9\.\-]/.test(val)) &&val.split('..').length == 1 &&val.split('.').length > 1;},errorMessage: '',errorMessageKey: 'badDomain'});/** Validate required*/$.formUtils.addValidator({name: 'required',validatorFunction: function (val, $el, config, language, $form) {switch ($el.attr('type')) {case 'checkbox':return $el.is(':checked');case 'radio':return $form.find('input[name="' + $el.attr('name') + '"]').filter(':checked').length > 0;default:return $.trim(val) !== '';}},errorMessage: '',errorMessageKey: 'requiredFields'});/** Validate length range*/$.formUtils.addValidator({name: 'length',validatorFunction: function (val, $el, conf, lang) {var lengthAllowed = $el.valAttr('length'),type = $el.attr('type');if (lengthAllowed == undefined) {alert('Please add attribute "data-validation-length" to ' + $el[0].nodeName + ' named ' + $el.attr('name'));return true;}// check if length is above min, below max or within range.var len = type == 'file' && $el.get(0).files !== undefined ? $el.get(0).files.length : val.length,lengthCheckResults = $.formUtils.numericRangeCheck(len, lengthAllowed),checkResult;switch (lengthCheckResults[0]) { // outside of allowed rangecase "out":this.errorMessage = lang.lengthBadStart + lengthAllowed + lang.lengthBadEnd;checkResult = false;break;// too shortcase "min":this.errorMessage = lang.lengthTooShortStart + lengthCheckResults[1] + lang.lengthBadEnd;checkResult = false;break;// too longcase "max":this.errorMessage = lang.lengthTooLongStart + lengthCheckResults[1] + lang.lengthBadEnd;checkResult = false;break;// okdefault:checkResult = true;}return checkResult;},errorMessage: '',errorMessageKey: ''});/** Validate url*/$.formUtils.addValidator({name: 'url',validatorFunction: function (url) {// written by Scott Gonzalez: http://projects.scottsplayground.com/iri/// - Victor Jonsson added support for arrays in the url ?arg[]=sdfsdf// - General improvements made by Stéphane Moureau <https://github.com/TraderStf>var urlFilter = /^(https?|ftp):\/\/((((\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])(\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])(\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/(((\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/((\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|\[|\]|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#(((\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i;if (urlFilter.test(url)) {var domain = url.split('://')[1],domainSlashPos = domain.indexOf('/');if (domainSlashPos > -1)domain = domain.substr(0, domainSlashPos);return $.formUtils.validators.validate_domain.validatorFunction(domain); // todo: add support for IP-addresses}return false;},errorMessage: '',errorMessageKey: 'badUrl'});/** Validate number (floating or integer)*/$.formUtils.addValidator({name: 'number',validatorFunction: function (val, $el, conf) {if (val !== '') {var allowing = $el.valAttr('allowing') || '',decimalSeparator = $el.valAttr('decimal-separator') || conf.decimalSeparator,allowsRange = false,begin, end,steps = $el.valAttr('step') || '',allowsSteps = false;if (allowing.indexOf('number') == -1)allowing += ',number';if (allowing.indexOf('negative') == -1 && val.indexOf('-') === 0) {return false;}if (allowing.indexOf('range') > -1) {begin = parseFloat(allowing.substring(allowing.indexOf("[") + 1, allowing.indexOf(";")));end = parseFloat(allowing.substring(allowing.indexOf(";") + 1, allowing.indexOf("]")));allowsRange = true;}if (steps != "")allowsSteps = true;if (decimalSeparator == ',') {if (val.indexOf('.') > -1) {return false;}// Fix for checking range with floats using ,val = val.replace(',', '.');}if (allowing.indexOf('number') > -1 && val.replace(/[0-9-]/g, '') === '' && (!allowsRange || (val >= begin && val <= end)) && (!allowsSteps || (val % steps == 0))) {return true;}if (allowing.indexOf('float') > -1 && val.match(new RegExp('^([0-9-]+)\\.([0-9]+)$')) !== null && (!allowsRange || (val >= begin && val <= end)) && (!allowsSteps || (val % steps == 0))) {return true;}}return false;},errorMessage: '',errorMessageKey: 'badInt'});/** Validate alpha numeric*/$.formUtils.addValidator({name: 'alphanumeric',validatorFunction: function (val, $el, conf, language) {var patternStart = '^([a-zA-Z0-9',patternEnd = ']+)$',additionalChars = $el.valAttr('allowing'),pattern = '';if (additionalChars) {pattern = patternStart + additionalChars + patternEnd;var extra = additionalChars.replace(/\\/g, '');if (extra.indexOf(' ') > -1) {extra = extra.replace(' ', '');extra += language.andSpaces || $.formUtils.LANG.andSpaces;}this.errorMessage = language.badAlphaNumeric + language.badAlphaNumericExtra + extra;} else {pattern = patternStart + patternEnd;this.errorMessage = language.badAlphaNumeric;}return new RegExp(pattern).test(val);},errorMessage: '',errorMessageKey: ''});/** Validate against regexp*/$.formUtils.addValidator({name: 'custom',validatorFunction: function (val, $el, conf) {var regexp = new RegExp($el.valAttr('regexp'));return regexp.test(val);},errorMessage: '',errorMessageKey: 'badCustomVal'});/** Validate date*/$.formUtils.addValidator({name: 'date',validatorFunction: function (date, $el, conf) {var dateFormat = $el.valAttr('format') || conf.dateFormat || 'yyyy-mm-dd';return $.formUtils.parseDate(date, dateFormat) !== false;},errorMessage: '',errorMessageKey: 'badDate'});/** Validate group of checkboxes, validate qty required is checked* written by Steve Wasiura : http://stevewasiura.waztech.com* element attrs* data-validation="checkbox_group"* data-validation-qty="1-2" // min 1 max 2* data-validation-error-msg="chose min 1, max of 2 checkboxes"*/$.formUtils.addValidator({name: 'checkbox_group',validatorFunction: function (val, $el, conf, lang, $form) {// preset return varvar checkResult = true,// get name of element. since it is a checkbox group, all checkboxes will have same nameelname = $el.attr('name'),// get checkboxes and count the checked ones$checkBoxes = $("input[type=checkbox][name^='" + elname + "']", $form),checkedCount = $checkBoxes.filter(':checked').length,// get el attr that specs qty required / allowedqtyAllowed = $el.valAttr('qty');if (qtyAllowed == undefined) {var elementType = $el.get(0).nodeName;alert('Attribute "data-validation-qty" is missing from ' + elementType + ' named ' + $el.attr('name'));}// call Utility function to check if count is above min, below max, within range etc.var qtyCheckResults = $.formUtils.numericRangeCheck(checkedCount, qtyAllowed);// results will be array, [0]=result str, [1]=qty intswitch (qtyCheckResults[0]) {// outside allowed rangecase "out":this.errorMessage = lang.groupCheckedRangeStart + qtyAllowed + lang.groupCheckedEnd;checkResult = false;break;// below min qtycase "min":this.errorMessage = lang.groupCheckedTooFewStart + qtyCheckResults[1] + lang.groupCheckedEnd;checkResult = false;break;// above max qtycase "max":this.errorMessage = lang.groupCheckedTooManyStart + qtyCheckResults[1] + lang.groupCheckedEnd;checkResult = false;break;// okdefault:checkResult = true;}if( !checkResult ) {var _triggerOnBlur = function() {$checkBoxes.unbind('click', _triggerOnBlur);$checkBoxes.filter('*[data-validation]').trigger('blur');};$checkBoxes.bind('click', _triggerOnBlur);}return checkResult;}// errorMessage : '', // set above in switch statement// errorMessageKey: '' // not used});})(jQuery);