2288 lines
74 KiB
JavaScript
2288 lines
74 KiB
JavaScript
(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.49
|
||
* @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 = $('<div class="' + conf.errorMessageClass + ' alert"></div>');
|
||
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 = $('<span></span>').addClass('help-block').addClass(conf.errorMessageClass);
|
||
$message.appendTo($parent);
|
||
}
|
||
setErrorMessage($message);
|
||
}
|
||
},
|
||
setMessageInTopOfForm: function ($form, errorMessages, conf, lang) {
|
||
var view = '<div class="{errorMessageClass} alert alert-danger">'+
|
||
'<strong>{errorTitle}</strong>'+
|
||
'<ul>{fields}</ul>'+
|
||
'</div>',
|
||
$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 += '<li>'+msg+'</li>';
|
||
});
|
||
|
||
$.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
|
||
* <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';
|
||
}
|
||
|
||
// 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 = $('<span />')
|
||
.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),
|
||
formDefaultConfig = $elem.closest('form').get(0).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.attr('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.attr('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;
|
||
|
||
// 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: '<div class="{errorMessageClass} alert alert-danger">{messages}</div>',
|
||
messages: '<strong>{errorTitle}</strong><ul>{fields}</ul>',
|
||
field: '<li>{msg}</li>'
|
||
},
|
||
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, conf, language]);
|
||
|
||
|
||
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('<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 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 = $('<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 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) {
|
||
$('<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());
|
||
$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: 'Form submission failed!',
|
||
requiredField: 'This is a required field',
|
||
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',
|
||
badUKNin: 'Incorrect UK NIN',
|
||
badUKUtr: 'Incorrect UK UTR 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',
|
||
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];
|
||
|
||
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 <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,
|
||
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);
|
||
|
||
|
||
}));
|