(function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(jQuery); } }(this, function (jQuery) { /** File generated by Grunt -- do not modify * JQUERY-FORM-VALIDATOR * * @version 2.3.54 * @website http://formvalidator.net/ * @author Victor Jonsson, http://victorjonsson.se * @license MIT */ /** */ (function ($, undefined) { var disableFormSubmit = function () { return false; }, HaltManager = { numHalted: 0, haltValidation: function($form) { this.numHalted++; $.formUtils.haltValidation = true; $form .unbind('submit', disableFormSubmit) .bind('submit', disableFormSubmit) .find('*[type="submit"]') .addClass('disabled') .attr('disabled', 'disabled'); }, unHaltValidation: function($form) { this.numHalted--; if (this.numHalted === 0) { $.formUtils.haltValidation = false; $form .unbind('submit', disableFormSubmit) .find('*[type="submit"]') .removeClass('disabled') .removeAttr('disabled', 'disabled'); } } }; function AsyncValidation($form, $input) { this.$form = $form; this.$input = $input; this.reset(); $input.on('change paste', this.reset.bind(this)); } AsyncValidation.prototype.reset = function() { this.haltedFormValidation = false; this.hasRun = false; this.isRunning = false; this.result = undefined; }; AsyncValidation.prototype.run = function(eventContext, callback) { if (eventContext === 'keyup') { return null; } else if (this.isRunning) { if (!this.haltedFormValidation && eventContext === 'submit') { HaltManager.haltValidation(); this.haltedFormValidation = true; } return null; // Waiting for result } else if(this.hasRun) { //this.$input.one('keyup change paste', this.reset.bind(this)); return this.result; } else { if (eventContext === 'submit') { HaltManager.haltValidation(this.$form); this.haltedFormValidation = true; } this.isRunning = true; this.$input .attr('disabled', 'disabled') .addClass('async-validation'); this.$form.addClass('async-validation'); callback(function(result) { this.done(result); }.bind(this)); return null; } }; AsyncValidation.prototype.done = function(result) { this.result = result; this.hasRun = true; this.isRunning = false; this.$input .removeAttr('disabled') .removeClass('async-validation'); this.$form.removeClass('async-validation'); if (this.haltedFormValidation) { HaltManager.unHaltValidation(this.$form); this.$form.trigger('submit'); } else { this.$input.trigger('validation.revalidate'); } }; $.formUtils = $.extend($.formUtils || {}, { asyncValidation: function(validatorName, $input, $form) { // Return async validator attached to this input element // or create a new async validator and attach it to the input var asyncValidation, input = $input.get(0); if (!input.asyncValidators) { input.asyncValidators = {}; } if (input.asyncValidators[validatorName]) { asyncValidation = input.asyncValidators[validatorName]; } else { asyncValidation = new AsyncValidation($form, $input); input.asyncValidators[validatorName] = asyncValidation; } return asyncValidation; } }); })(jQuery); /** * Deprecated functions and attributes * @todo: Remove in release of 3.0 */ (function ($, undefined) { 'use strict'; /** * @deprecated * @param language * @param conf */ $.fn.validateForm = function (language, conf) { $.formUtils.warn('Use of deprecated function $.validateForm, use $.isValid instead'); return this.isValid(language, conf, true); }; $(window) .on('formValidationPluginInit', function(evt, config) { convertDeprecatedLangCodeToISO6391(config); addSupportForCustomErrorMessageCallback(config); addSupportForElementReferenceInPositionParam(config); }) .on('validatorsLoaded formValidationSetup', function(evt, $form) { if( !$form ) { $form = $('form'); } addSupportForValidationDependingOnCheckedInput($form); }); function addSupportForCustomErrorMessageCallback(config) { if (config && config.errorMessagePosition === 'custom' && typeof config.errorMessageCustom === 'function') { $.formUtils.warn('Use of deprecated function errorMessageCustom, use config.submitErrorMessageCallback instead'); config.submitErrorMessageCallback = function($form, errorMessages) { config.errorMessageCustom( $form, config.language.errorTitle, errorMessages, config ); }; } } function addSupportForElementReferenceInPositionParam(config) { if (config.errorMessagePosition && typeof config.errorMessagePosition === 'object') { $.formUtils.warn('Deprecated use of config parameter errorMessagePosition, use config.submitErrorMessageCallback instead'); var $errorMessageContainer = config.errorMessagePosition; config.errorMessagePosition = 'top'; config.submitErrorMessageCallback = function() { return $errorMessageContainer; }; } } function addSupportForValidationDependingOnCheckedInput($form) { var $inputsDependingOnCheckedInputs = $form.find('[data-validation-if-checked]'); if ($inputsDependingOnCheckedInputs.length) { $.formUtils.warn( 'Detected use of attribute "data-validation-if-checked" which is '+ 'deprecated. Use "data-validation-depends-on" provided by module "logic"' ); } $inputsDependingOnCheckedInputs .on('beforeValidation', function() { var $elem = $(this), nameOfDependingInput = $elem.valAttr('if-checked'); // Set the boolean telling us that the validation depends // on another input being checked var $dependingInput = $('input[name="' + nameOfDependingInput + '"]', $form), dependingInputIsChecked = $dependingInput.is(':checked'), valueOfDependingInput = ($.formUtils.getValue($dependingInput) || '').toString(), requiredValueOfDependingInput = $elem.valAttr('if-checked-value'); if (!dependingInputIsChecked || !( !requiredValueOfDependingInput || requiredValueOfDependingInput === valueOfDependingInput )) { $elem.valAttr('skipped', true); } }); } function convertDeprecatedLangCodeToISO6391(config) { var deprecatedLangCodes = { se: 'sv', cz: 'cs', dk: 'da' }; if (config.lang in deprecatedLangCodes) { var newLangCode = deprecatedLangCodes[config.lang]; $.formUtils.warn( 'Deprecated use of lang code "'+config.lang+'" use "'+newLangCode+'" instead' ); config.lang = newLangCode; } } })(jQuery); /** * Utility methods used for displaying error messages (attached to $.formUtils) */ (function ($) { 'use strict'; var dialogs = { resolveErrorMessage: function($elem, validator, validatorName, conf, language) { var errorMsgAttr = conf.validationErrorMsgAttribute + '-' + validatorName.replace('validate_', ''), validationErrorMsg = $elem.attr(errorMsgAttr); if (!validationErrorMsg) { validationErrorMsg = $elem.attr(conf.validationErrorMsgAttribute); if (!validationErrorMsg) { if (typeof validator.errorMessageKey !== 'function') { validationErrorMsg = language[validator.errorMessageKey]; } else { validationErrorMsg = language[validator.errorMessageKey(conf)]; } if (!validationErrorMsg) { validationErrorMsg = validator.errorMessage; } } } return validationErrorMsg; }, getParentContainer: function ($elem) { if ($elem.valAttr('error-msg-container')) { return $($elem.valAttr('error-msg-container')); } else { var $parent = $elem.parent(); if (!$parent.hasClass('form-group') && !$parent.closest('form').hasClass('form-horizontal')) { var $formGroup = $parent.closest('.form-group'); if ($formGroup.length) { return $formGroup.eq(0); } } return $parent; } }, applyInputErrorStyling: function ($input, conf) { $input .addClass(conf.errorElementClass) .removeClass('valid'); this.getParentContainer($input) .addClass(conf.inputParentClassOnError) .removeClass(conf.inputParentClassOnSuccess); if (conf.borderColorOnError !== '') { $input.css('border-color', conf.borderColorOnError); } }, applyInputSuccessStyling: function($input, conf) { $input.addClass('valid'); this.getParentContainer($input) .addClass(conf.inputParentClassOnSuccess); }, removeInputStylingAndMessage: function($input, conf) { // Reset input css $input .removeClass('valid') .removeClass(conf.errorElementClass) .css('border-color', ''); var $parentContainer = dialogs.getParentContainer($input); // Reset parent css $parentContainer .removeClass(conf.inputParentClassOnError) .removeClass(conf.inputParentClassOnSuccess); // Remove possible error message if (typeof conf.inlineErrorMessageCallback === 'function') { var $errorMessage = conf.inlineErrorMessageCallback($input, false, conf); if ($errorMessage) { $errorMessage.html(''); } } else { $parentContainer .find('.' + conf.errorMessageClass) .remove(); } }, removeAllMessagesAndStyling: function($form, conf) { // Remove error messages in top of form if (typeof conf.submitErrorMessageCallback === 'function') { var $errorMessagesInTopOfForm = conf.submitErrorMessageCallback($form, false, conf); if ($errorMessagesInTopOfForm) { $errorMessagesInTopOfForm.html(''); } } else { $form.find('.' + conf.errorMessageClass + '.alert').remove(); } // Remove input css/messages $form.find('.' + conf.errorElementClass + ',.valid').each(function() { dialogs.removeInputStylingAndMessage($(this), conf); }); }, setInlineMessage: function ($input, errorMsg, conf) { this.applyInputErrorStyling($input, conf); var custom = document.getElementById($input.attr('name') + '_err_msg'), $messageContainer = false, setErrorMessage = function ($elem) { $.formUtils.$win.trigger('validationErrorDisplay', [$input, $elem]); $elem.html(errorMsg); }, addErrorToMessageContainer = function() { var $found = false; $messageContainer.find('.' + conf.errorMessageClass).each(function () { if (this.inputReferer === $input[0]) { $found = $(this); return false; } }); if ($found) { if (!errorMsg) { $found.remove(); } else { setErrorMessage($found); } } else if(errorMsg !== '') { $message = $('
'); setErrorMessage($message); $message[0].inputReferer = $input[0]; $messageContainer.prepend($message); } }, $message; if (custom) { // Todo: remove in 3.0 $.formUtils.warn('Using deprecated element reference ' + custom.id); $messageContainer = $(custom); addErrorToMessageContainer(); } else if (typeof conf.inlineErrorMessageCallback === 'function') { $messageContainer = conf.inlineErrorMessageCallback($input, errorMsg, conf); if (!$messageContainer) { // Error display taken care of by inlineErrorMessageCallback return; } addErrorToMessageContainer(); } else { var $parent = this.getParentContainer($input); $message = $parent.find('.' + conf.errorMessageClass + '.help-block'); if ($message.length === 0) { $message = $('').addClass('help-block').addClass(conf.errorMessageClass); $message.appendTo($parent); } setErrorMessage($message); } }, setMessageInTopOfForm: function ($form, errorMessages, conf, lang) { var view = '
'+ '{errorTitle}'+ ''+ '
', $container = false; if (typeof conf.submitErrorMessageCallback === 'function') { $container = conf.submitErrorMessageCallback($form, errorMessages, conf); if (!$container) { // message display taken care of by callback return; } } var viewParams = { errorTitle: lang.errorTitle, fields: '', errorMessageClass: conf.errorMessageClass }; $.each(errorMessages, function (i, msg) { viewParams.fields += '
  • '+msg+'
  • '; }); $.each(viewParams, function(param, value) { view = view.replace('{'+param+'}', value); }); if ($container) { $container.html(view); } else { $form.children().eq(0).before($(view)); } } }; $.formUtils = $.extend($.formUtils || {}, { dialogs: dialogs }); })(jQuery); /** * File declaring all methods if this plugin which is applied to $.fn. */ (function($, window, undefined) { 'use strict'; var _helpers = 0; /** * Assigns validateInputOnBlur function to elements blur event * * @param {Object} language Optional, will override $.formUtils.LANG * @param {Object} conf Optional, will override the default settings * @return {jQuery} */ $.fn.validateOnBlur = function (language, conf) { var $form = this, $elems = this.find('*[data-validation]'); $elems.each(function(){ var $this = $(this); if ($this.is('[type=radio]')){ var $additionals = $form.find('[type=radio][name="' + $this.attr('name') + '"]'); $additionals.bind('blur.validation', function(){ $this.validateInputOnBlur(language, conf, true, 'blur'); }); if (conf.validateCheckboxRadioOnClick) { $additionals.bind('click.validation', function () { $this.validateInputOnBlur(language, conf, true, 'click'); }); } } }); $elems.bind('blur.validation', function () { $(this).validateInputOnBlur(language, conf, true, 'blur'); }); if (conf.validateCheckboxRadioOnClick) { // bind click event to validate on click for radio & checkboxes for nice UX this.find('input[type=checkbox][data-validation],input[type=radio][data-validation]') .bind('click.validation', function () { $(this).validateInputOnBlur(language, conf, 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, config) { var $elements = this[0].nodeName === 'FORM' ? this.find('*[data-validation-event]') : this; $elements .each(function () { var $el = $(this), etype = $el.valAttr('event'); if (etype) { $el .unbind(etype + '.validation') .bind(etype + '.validation', function (evt) { if( (evt || {}).keyCode !== 9 ) { $(this).validateInputOnBlur(language, config, true, etype); } }); } }); return this; }; /** * fade in help message when input gains focus * fade out when input loses focus * * * @param {String} attrName - Optional, default is data-help * @return {jQuery} */ $.fn.showHelpOnFocus = function (attrName) { if (!attrName) { attrName = 'data-validation-help'; } // Add help text listeners this.find('textarea,input').each(function () { var $elem = $(this), className = 'jquery_form_help_' + (++_helpers), help = $elem.attr(attrName); // Reset $elem .removeClass('has-help-text') .unbind('focus.help') .unbind('blur.help'); if (help) { $elem .addClass('has-help-txt') .bind('focus.help', function () { var $help = $elem.parent().find('.' + className); if ($help.length === 0) { $help = $('') .addClass(className) .addClass('help') .addClass('help-block') // twitter bs .text(help) .hide(); $elem.after($help); } $help.fadeIn(); }) .bind('blur.help', function () { $(this) .parent() .find('.' + className) .fadeOut('slow'); }); } }); return this; }; /** * @param {Function} cb * @param {Object} [conf] * @param {Object} [lang] */ $.fn.validate = function(cb, conf, lang) { var language = $.extend({}, $.formUtils.LANG, lang || {}); this.each(function() { var $elem = $(this), $closestForm = $elem.closest('form').get(0) || {}, formDefaultConfig = $closestForm.validationConfig || {}; $elem.one('validation', function(evt, isValid) { if ( typeof cb === 'function' ) { cb(isValid, this, evt); } }); $elem.validateInputOnBlur( language, $.extend({}, formDefaultConfig, conf || {}), true ); }); }; /** * Tells whether or not validation of this input will have to postpone the form submit () * @returns {Boolean} */ $.fn.willPostponeValidation = function() { return (this.valAttr('suggestion-nr') || this.valAttr('postpone') || this.hasClass('hasDatepicker')) && !window.postponedValidation; }; /** * 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} eventContext * @return {jQuery} */ $.fn.validateInputOnBlur = function (language, conf, attachKeyupEvent, eventContext) { $.formUtils.eventType = eventContext; if ( this.willPostponeValidation() ) { // This validation has to be postponed var _self = this, postponeTime = this.valAttr('postpone') || 200; window.postponedValidation = function () { _self.validateInputOnBlur(language, conf, attachKeyupEvent, eventContext); window.postponedValidation = false; }; setTimeout(function () { if (window.postponedValidation) { window.postponedValidation(); } }, postponeTime); return this; } language = $.extend({}, $.formUtils.LANG, language || {}); $.formUtils.dialogs.removeInputStylingAndMessage(this, conf); var $elem = this, $form = $elem.closest('form'), result = $.formUtils.validateInput( $elem, language, conf, $form, eventContext ); var reValidate = function() { $elem.validateInputOnBlur(language, conf, false, 'blur.revalidated'); }; if (eventContext === 'blur') { $elem .unbind('validation.revalidate', reValidate) .one('validation.revalidate', reValidate); } if (attachKeyupEvent) { $elem.removeKeyUpValidation(); } if (result.shouldChangeDisplay) { if (result.isValid) { $.formUtils.dialogs.applyInputSuccessStyling($elem, conf); } else { $.formUtils.dialogs.setInlineMessage($elem, result.errorMsg, conf); } } if (!result.isValid && attachKeyupEvent) { $elem.validateOnKeyUp(language, conf); } return this; }; /** * Validate element on keyup-event */ $.fn.validateOnKeyUp = function(language, conf) { this.each(function() { var $input = $(this); if (!$input.valAttr('has-keyup-event')) { $input .valAttr('has-keyup-event', 'true') .bind('keyup.validation', function (evt) { if( evt.keyCode !== 9 ) { $input.validateInputOnBlur(language, conf, false, 'keyup'); } }); } }); return this; }; /** * Remove validation on keyup */ $.fn.removeKeyUpValidation = function() { this.each(function() { $(this) .valAttr('has-keyup-event', false) .unbind('keyup.validation'); }); return this; }; /** * Short hand for fetching/adding/removing element attributes * prefixed with 'data-validation-' * * @param {String} name * @param {String|Boolean} [val] * @return {String|undefined|jQuery} * @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 { name = ((name.length > 0) ? '-' + 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 validators delete $.formUtils.errorDisplayPreventedWhenHalted; displayError = 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.valAttr('current-error', mess); if (displayError) { $.formUtils.dialogs.applyInputErrorStyling($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 class if (displayError) { $.formUtils.dialogs.removeAllMessagesAndStyling($form, 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 result = $.formUtils.validateInput( $elem, language, conf, $form, 'submit' ); if (!result.isValid) { addErrorMessage(result.errorMsg, $elem); } else if (result.isValid && result.shouldChangeDisplay) { $elem.valAttr('current-error', false); $.formUtils.dialogs.applyInputSuccessStyling($elem, conf); } } }); // Run validation callback if (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 failed if (errorInputs.length > 0) { if (displayError) { if (conf.errorMessagePosition === 'top') { $.formUtils.dialogs.setMessageInTopOfForm($form, errorMessages, conf, language); } else { $.each(errorInputs, function (i, $input) { $.formUtils.dialogs.setInlineMessage($input, $input.valAttr('current-error'), conf); }); } if (conf.scrollToTopOnError) { $.formUtils.$win.scrollTop($form.offset().top - 20); } } } if (!displayError && $.formUtils.haltValidation) { $.formUtils.errorDisplayPreventedWhenHalted = true; } return errorInputs.length === 0 && !$.formUtils.haltValidation; }; /** * 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; }; })(jQuery, window); /** * Utility methods used for handling loading of modules (attached to $.formUtils) */ (function($) { 'use strict'; $.formUtils = $.extend($.formUtils || {}, { /** * @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 {function} [callback] - Optional, whether or not to fire event 'load' when modules finished loading */ loadModules: function (modules, path, callback) { if ($.formUtils.isLoadingModules) { setTimeout(function () { $.formUtils.loadModules(modules, path, callback); }, 10); 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 (callback && hasLoadedAnyModule) { if( typeof callback === 'function' ) { callback(); } } } }; 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.slice(-3) === '.js' ? '' : '.js'), script = document.createElement('SCRIPT'); if (scriptUrl in $.formUtils.loadedModules) { // already loaded moduleLoadedCallback(); } else { // Remember that this script is loaded $.formUtils.loadedModules[scriptUrl] = 1; hasLoadedAnyModule = true; if (typeof define === 'function' && define.amd) { require([scriptUrl + ( scriptUrl.slice(-7) === '.dev.js' ? cacheSuffix : '' )], moduleLoadedCallback); } else { // Load the script script.type = 'text/javascript'; script.onload = moduleLoadedCallback; script.src = scriptUrl + ( scriptUrl.slice(-7) === '.dev.js' ? cacheSuffix : '' ); script.onerror = function() { $.formUtils.warn('Unable to load form validation module '+scriptUrl); }; script.onreadystatechange = function () { // IE 7 fix if (this.readyState === 'complete' || this.readyState === 'loaded') { moduleLoadedCallback(); // Handle memory leak in IE this.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 () { var isScriptFromPluginNodeModulesDirectory = this.src.split('form-validator')[1].split('node_modules').length > 1; if (!isScriptFromPluginNodeModulesDirectory) { 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); } } } }); })(jQuery); /** * Setup function for the plugin */ (function ($) { 'use strict'; /** * A bit smarter split function * delimiter can be space, comma, dash or pipe * @param {String} val * @param {Function|String} [callback] * @param {Boolean} [allowSpaceAsDelimiter] * @returns {Array|void} */ $.split = function (val, callback, allowSpaceAsDelimiter) { // default to true allowSpaceAsDelimiter = allowSpaceAsDelimiter === undefined || allowSpaceAsDelimiter === true; var pattern = '[,|'+(allowSpaceAsDelimiter ? '\\s':'')+'-]\\s*', regex = new RegExp(pattern, 'g'); if (typeof callback !== 'function') { // return array if (!val) { return []; } var values = []; $.each(val.split(callback ? callback : regex), 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(regex), function (i, str) { str = $.trim(str); if (str.length) { return callback(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', validateOnEvent: false, validateOnBlur: true, validateCheckboxRadioOnClick: true, showHelpOnFocus: true, addSuggestions: true, modules: '', onModulesLoaded: null, language: false, onSuccess: false, onError: false, onElementValidate: false }); conf = $.extend(defaultConf, conf || {}); $(window).trigger('formValidationPluginInit', [conf]); if( conf.lang && conf.lang !== 'en' ) { var langModule = 'lang/'+conf.lang+'.js'; conf.modules += conf.modules.length ? ','+langModule : langModule; } // Add validation to forms $(conf.form).each(function (i, form) { // Make a reference to the config for this form form.validationConfig = conf; // Trigger jQuery event that we're about to setup validation var $form = $(form); $form.trigger('formValidationSetup', [$form, conf]); // Remove classes and event handlers that might have been // added by a previous call to $.validate $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 (evt) { var $form = $(this), stop = function() { evt.stopImmediatePropagation(); return false; }; if ($.formUtils.haltValidation) { // pressing several times on submit button while validation is halted return stop(); } if ($.formUtils.isLoadingModules) { setTimeout(function () { $form.trigger('submit.validation'); }, 200); return stop(); } var valid = $form.isValid(conf.language, conf); if ($.formUtils.haltValidation) { // Validation got halted by one of the validators return stop(); } else { if (valid && typeof conf.onSuccess === 'function') { var callbackResponse = conf.onSuccess($form); if (callbackResponse === false) { return stop(); } } else if (!valid && typeof conf.onError === 'function') { conf.onError($form); return stop(); } else { return valid ? true : stop(); } } }) .bind('reset.validation', function () { $.formUtils.dialogs.removeAllMessagesAndStyling($form, 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 !== '') { $.formUtils.loadModules(conf.modules, false, function() { if (typeof conf.onModulesLoaded === 'function') { conf.onModulesLoaded(); } var $form = typeof conf.form === 'string' ? $(conf.form) : conf.form; $.formUtils.$win.trigger('validatorsLoaded', [$form, conf]); }); } }; })(jQuery); /** * Utility methods and properties attached to $.formUtils */ (function($, window) { 'use strict'; var $win = $(window); $.formUtils = $.extend($.formUtils || {}, { $win: $win, /** * Default config for $(...).isValid(); */ defaultConfig: function () { return { ignore: [], // Names of inputs not to be validated even though `validationRuleAttribute` containing the validation rules tells us to errorElementClass: 'error', // Class that will be put on elements which value is invalid borderColorOnError: '#b94a48', // Border color of elements which value is invalid, empty string to not change border color errorMessageClass: 'form-error', // class name of div containing error messages when validation fails validationRuleAttribute: 'data-validation', // name of the attribute holding the validation rules validationErrorMsgAttribute: 'data-validation-error-msg', // define custom err msg inline with element errorMessagePosition: 'inline', // Can be either "top" or "inline" errorMessageTemplate: { container: '
    {messages}
    ', messages: '{errorTitle}', field: '
  • {msg}
  • ' }, scrollToTopOnError: true, dateFormat: 'yyyy-mm-dd', addValidClassOnAll: false, // whether or not to apply class="valid" even if the input wasn't validated decimalSeparator: '.', inputParentClassOnError: 'has-error', // twitter-bootstrap default class name inputParentClassOnSuccess: 'has-success', // twitter-bootstrap default class name validateHiddenInputs: false, // whether or not hidden inputs should be validated inlineErrorMessageCallback: false, submitErrorMessageCallback: false }; }, /** * 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, /** * Function for adding a validator * @param {Object} validator */ addValidator: function (validator) { // prefix with "validate_" for backward compatibility reasons var name = validator.name.indexOf('validate_') === 0 ? validator.name : 'validate_' + validator.name; if (validator.validateOnKeyUp === undefined) { validator.validateOnKeyUp = true; } this.validators[name] = validator; }, /** * Warn user via the console if available */ warn: function(msg) { if( 'console' in window ) { if( typeof window.console.warn === 'function' ) { window.console.warn(msg); } else if( typeof window.console.log === 'function' ) { window.console.log(msg); } } else { alert(msg); } }, /** * Same as input $.fn.val() but also supporting input of typ radio or checkbox * @example * * $.formUtils.getValue('.myRadioButtons', $('#some-form')); * $.formUtils.getValue($('#some-form').find('.check-boxes')); * * @param query * @param $parent * @returns {String|Boolean} */ getValue: function(query, $parent) { var $inputs = $parent ? $parent.find(query) : query; if ($inputs.length > 0 ) { var type = $inputs.eq(0).attr('type'); if (type === 'radio' || type === 'checkbox') { return $inputs.filter(':checked').val() || ''; } else { return $inputs.val() || ''; } } return false; }, /** * Validate the value of given element according to the validation rules * found in the attribute data-validation. Will return an object representing * a validation result, having the props shouldChangeDisplay, isValid and errorMsg * @param {jQuery} $elem * @param {Object} language ($.formUtils.LANG) * @param {Object} conf * @param {jQuery} $form * @param {String} [eventContext] * @return {Object} */ validateInput: function ($elem, language, conf, $form, eventContext) { conf = conf || $.formUtils.defaultConfig(); language = language || $.formUtils.LANG; var value = this.getValue($elem); $elem .valAttr('skipped', false) .one('beforeValidation', function() { // Skip input because its hidden or disabled // Doing this in a callback makes it possible for others to prevent the default // behaviour by binding to the same event and call evt.stopImmediatePropagation() if ($elem.attr('disabled') || (!$elem.is(':visible') && !conf.validateHiddenInputs)) { $elem.valAttr('skipped', 1); } }) .trigger('beforeValidation', [value, language, conf]); var inputIsOptional = $elem.valAttr('optional') === 'true', skipBecauseItsEmpty = !value && inputIsOptional, validationRules = $elem.attr(conf.validationRuleAttribute), isValid = true, errorMsg = '', result = {isValid: true, shouldChangeDisplay:true, errorMsg:''}; // For input type="number", browsers attempt to parse the entered value into a number. // If the input is not numeric, browsers handle the situation differently: // Chrome 48 simply disallows non-numeric input; FF 44 clears out the input box on blur; // Safari 5 parses the entered string to find a leading number. // If the input fails browser validation, the browser sets the input value equal to an empty string. // Therefore, we cannot distinguish (apart from hacks) between an empty input type="text" and one with a // value that can't be parsed by the browser. if (!validationRules || skipBecauseItsEmpty || $elem.valAttr('skipped')) { result.shouldChangeDisplay = conf.addValidClassOnAll; return result; } // Filter out specified characters var ignore = $elem.valAttr('ignore'); if (ignore) { $.each(ignore.split(''), function(i, character) { value = value.replace(new RegExp('\\'+character, 'g'), ''); }); } $.split(validationRules, function (rule) { if (rule.indexOf('validate_') !== 0) { rule = 'validate_' + rule; } var validator = $.formUtils.validators[rule]; if (validator) { // special change of element for checkbox_group rule if (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 = $form.find('[name="' + $elem.attr('name') + '"]:eq(0)'); } if (eventContext !== 'keyup' || validator.validateOnKeyUp) { // A validator can prevent itself from getting triggered on keyup isValid = validator.validatorFunction(value, $elem, conf, language, $form, eventContext); } if (!isValid) { if (conf.validateOnBlur) { $elem.validateOnKeyUp(language, conf); } errorMsg = $.formUtils.dialogs.resolveErrorMessage($elem, validator, rule, conf, language); return false; // break iteration } } else { // todo: Add some validator lookup function and tell immediately which module is missing throw new Error('Using undefined validator "' + rule + '". Maybe you have forgotten to load the module that "' + rule +'" belongs to?'); } }); if (isValid === false) { $elem.trigger('validation', false); result.errorMsg = errorMsg; result.isValid = false; result.shouldChangeDisplay = true; } else if (isValid === null) { // A validatorFunction returning null means that it's not able to validate // the input at this time. Most probably some async stuff need to gets finished // first and then the validator will re-trigger the validation. result.shouldChangeDisplay = false; } else { $elem.trigger('validation', true); result.shouldChangeDisplay = true; } // Run element validation callback if (typeof conf.onElementValidate === 'function' && errorMsg !== null) { conf.onElementValidate(result.isValid, $elem, $form, errorMsg); } $elem.trigger('afterValidation', [result, eventContext]); 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 * @param {Boolean} [addMissingLeadingZeros] * @return {Array}|{Boolean} */ parseDate: function (val, dateFormat, addMissingLeadingZeros) { 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 += '$'; if (addMissingLeadingZeros) { var newValueParts = []; $.each(val.split(divider), function(i, part) { if(part.length === 1) { part = '0'+part; } newValueParts.push(part); }); val = newValueParts.join(divider); } 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 * @return {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 value var maxChars = parseInt($maxLengthElement.text(), 10), charsLeft = 0, // internal function does the counting and sets display value countCharacters = function () { var numChars = $inputElement.val().length; if (numChars > maxChars) { // get current scroll bar position var 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, 10) * @return array */ numericRangeCheck: function (value, rangeAllowed) { // split by dash var range = $.split(rangeAllowed), // min or max minmax = parseInt(rangeAllowed.substr(3), 10); if( range.length === 1 && rangeAllowed.indexOf('min') === -1 && rangeAllowed.indexOf('max') === -1 ) { range = [rangeAllowed, rangeAllowed]; // only a number, checking agains an exact number of characters } // 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 range else if (rangeAllowed.indexOf('min') === 0 && (value < minmax )) // min { return ['min', minmax]; } // value is below min else if (rangeAllowed.indexOf('max') === 0 && (value > minmax )) // max { return ['max', minmax]; } // value is above max // since no other returns executed, value is in allowed range return [ '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 $win.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 suggestions if (val !== '') { var findPartial = val.length > 2; $.each($input.data('suggestions'), function (i, suggestion) { var lowerCaseVal = suggestion.toLocaleLowerCase(); if (lowerCaseVal === val) { foundSuggestions.push('' + suggestion + ''); hasTypedSuggestion = true; return false; } else if (lowerCaseVal.indexOf(val) === 0 || (findPartial && lowerCaseVal.indexOf(val) > -1)) { foundSuggestions.push(suggestion.replace(new RegExp(val, 'gi'), '$&')); } }); } // Hide suggestion container if (hasTypedSuggestion || (foundSuggestions.length === 0 && $suggestionContainer.length > 0)) { $suggestionContainer.hide(); } // Create suggestion container if not already exists else if (foundSuggestions.length > 0 && $suggestionContainer.length === 0) { $suggestionContainer = $('
    ').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 container else if (foundSuggestions.length > 0 && !$suggestionContainer.is(':visible')) { $suggestionContainer.show(); } // add suggestions if (foundSuggestions.length > 0 && val.length !== foundSuggestions[0].length) { // put container in place every time, just in case setSuggsetionPosition($suggestionContainer, $input); // Add suggestions HTML to container $suggestionContainer.html(''); $.each(foundSuggestions, function (i, text) { $('
    ') .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()); $input.trigger('change'); 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); $input.trigger('change'); 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 up if ($.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 down if ($.formUtils._selectedSuggestion === null) { $.formUtils._selectedSuggestion = 0; } else { $.formUtils._selectedSuggestion++; } if ($.formUtils._selectedSuggestion > ($suggestions.length - 1)) { $.formUtils._selectedSuggestion = 0; } } // Scroll in suggestion window var 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: 'Fehler beim Absenden!', requiredField: 'Dies ist ein Pflichtfeld', requiredFields: 'Es wurden nicht alle Pflichtfelder ausgefüllt', badTime: 'Es wurde keine richtige Zeit eingefügt', badEmail: 'Bitte eine korrekte E-Mail Adresse eintragen', badTelephone: 'Bitte eine korrekte Telefonnummer eintragen', badSecurityAnswer: 'Sie haben die Sicherheitsfrage nicht richtig beantwortet', badDate: 'Bitte eine korrektes Datum eintragen', lengthBadStart: 'Die Eingabe soll in dem Bereich liegen', lengthBadEnd: ' characters', lengthTooLongStart: 'Die Eingabe ist länger als ', lengthTooShortStart: 'Die Eingabe ist kürzer als ', notConfirmed: 'Die Eingabe konnte nicht bestätigt werden', badDomain: 'Domain Fehler', badUrl: 'Die Eingabe ist keine korrekte URL', badCustomVal: 'Die Eingabe ist nicht korrekt', andSpaces: ' und Leerraum ', badInt: 'Die Eingabe ist keine korrekte Nummer', badSecurityNumber: 'Fehler bei der Sicherheitsnummer', badUKVatAnswer: 'Falsche UK VAT Nummer', badUKNin: 'Falsche UK NIN', badUKUtr: 'Falssche UK UTR Number', badStrength: 'Das Passwort ist nicht stark genung', badNumberOfSelectedOptionsStart: 'Wählen Sie mindestens ', badNumberOfSelectedOptionsEnd: ' Antworten', badAlphaNumeric: 'Bitte nur Buchstaben und Zahlen eintragen ', badAlphaNumericExtra: ' und ', wrongFileSize: 'Die Datei ist zu groß (max %s)', wrongFileType: 'Nur Dateien des Typ %s sind erlaubt', groupCheckedRangeStart: 'Wählen Sie zwischen ', groupCheckedTooFewStart: 'Wählen Sie mindestens ', groupCheckedTooManyStart: 'Please choose a maximum of ', groupCheckedEnd: ' item(s)', badCreditCard: 'The credit card number is not correct', badCVV: 'The CVV number was not correct', wrongFileDim : 'Incorrect image dimensions,', imageTooTall : 'the image can not be taller than', imageTooWide : 'the image can not be wider than', imageTooSmall : 'the image was too small', min : 'min', max : 'max', imageRatioNotAccepted : 'Image ratio is not be accepted', badBrazilTelephoneAnswer: 'The phone number entered is invalid', badBrazilCEPAnswer: 'The CEP entered is invalid', badBrazilCPFAnswer: 'The CPF entered is invalid', badPlPesel: 'The PESEL entered is invalid', badPlNip: 'The NIP entered is invalid', badPlRegon: 'The REGON entered is invalid', badreCaptcha: 'Please confirm that you are not a bot', passwordComplexityStart: 'Password must contain at least ', passwordComplexitySeparator: ', ', passwordComplexityUppercaseInfo: ' uppercase letter(s)', passwordComplexityLowercaseInfo: ' lowercase letter(s)', passwordComplexitySpecialCharsInfo: ' special character(s)', passwordComplexityNumericCharsInfo: ' numeric character(s)', passwordComplexityEnd: '.' } }); })(jQuery, window); /** * File declaring all default validators. */ (function($) { /* * Validate email */ $.formUtils.addValidator({ name: 'email', validatorFunction: function (email) { var emailParts = email.toLowerCase().split('@'), localPart = emailParts[0], domain = emailParts[1]; function istZeichenDoppeltVorhanden(text, zeichen) { var zaehlung = 0; for (var i = 0; i < text.length; i++) { if (text[i] === zeichen) { zaehlung++; } if (zaehlung > 1) { return true; // Das Zeichen wurde mindestens zweimal gefunden } } return false; // Das Zeichen wurde nicht mindestens zweimal gefunden } var istDoppelt = istZeichenDoppeltVorhanden(email.toLowerCase(), '@'); if (istDoppelt) { return false; } if(emailParts[2]) { return false; } if (localPart && domain) { if( localPart.indexOf('"') === 0 ) { var len = localPart.length; localPart = localPart.replace(/\"/g, ''); if( localPart.length !== (len-2) ) { return false; // It was not allowed to have more than two apostrophes } } return $.formUtils.validators.validate_domain.validatorFunction(emailParts[1]) && localPart.indexOf('.') !== 0 && localPart.substring(localPart.length-1, localPart.length) !== '.' && localPart.indexOf('..') === -1 && !(/[^\w\+\.\-\#\-\_\~\!\$\&\'\(\)\*\+\,\;\=\:]/.test(localPart)); } 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.slice(-2))) && !(/[^a-zA-Z0-9]/.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: function(config) { if (config.errorMessagePosition === 'top' || typeof config.errorMessagePosition === 'function') { return 'requiredFields'; } else { return 'requiredField'; } } }); /* * 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 range case 'out': this.errorMessage = lang.lengthBadStart + lengthAllowed + lang.lengthBadEnd; checkResult = false; break; // too short case 'min': this.errorMessage = lang.lengthTooShortStart + lengthCheckResults[1] + lang.lengthBadEnd; checkResult = false; break; // too long case 'max': this.errorMessage = lang.lengthTooLongStart + lengthCheckResults[1] + lang.lengthBadEnd; checkResult = false; break; // ok default: 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 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, sanitize = $el.attr('data-sanitize') || '', isFormattedWithNumeral = sanitize.match(/(^|[\s])numberFormat([\s]|$)/i); if (isFormattedWithNumeral) { if (!window.numeral) { throw new ReferenceError('The data-sanitize value numberFormat cannot be used without the numeral' + ' library. Please see Data Validation in http://www.formvalidator.net for more information.'); } //Unformat input first, then convert back to String if (val.length) { val = String(numeral().unformat(val)); } } 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 (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) { 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', addMissingLeadingZeros = $el.valAttr('require-leading-zero') === 'false'; return $.formUtils.parseDate(date, dateFormat, addMissingLeadingZeros) !== 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 var var isValid = true, // get name of element. since it is a checkbox group, all checkboxes will have same name elname = $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 / allowed qtyAllowed = $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 int switch (qtyCheckResults[0]) { // outside allowed range case 'out': this.errorMessage = lang.groupCheckedRangeStart + qtyAllowed + lang.groupCheckedEnd; isValid = false; break; // below min qty case 'min': this.errorMessage = lang.groupCheckedTooFewStart + qtyCheckResults[1] + lang.groupCheckedEnd; isValid = false; break; // above max qty case 'max': this.errorMessage = lang.groupCheckedTooManyStart + qtyCheckResults[1] + lang.groupCheckedEnd; isValid = false; break; // ok default: isValid = true; } if( !isValid ) { var _triggerOnBlur = function() { $checkBoxes.unbind('click', _triggerOnBlur); $checkBoxes.filter('*[data-validation]').validateInputOnBlur(lang, conf, false, 'blur'); }; $checkBoxes.bind('click', _triggerOnBlur); } return isValid; } // errorMessage : '', // set above in switch statement // errorMessageKey: '' // not used }); })(jQuery); }));