(function ($) {
'use strict';
var $window = $(window),
_applyErrorStyle = function ($elem, conf) {
var $parent = $elem.parent();
$elem
.addClass(conf.errorElementClass)
.removeClass('valid');
if ($parent.hasClass("input-group"))
$parent = $parent.parent();
$parent.addClass('has-error').removeClass('has-success'); //twitter bs
if (conf.borderColorOnError !== '') {
$elem.css('border-color', conf.borderColorOnError);
}
},
_removeErrorStyle = function ($elem, conf) {
$elem.each(function () {
_setInlineErrorMessage($(this), '', conf, conf.errorMessagePosition);
var $parent = $(this).parent();
if ($parent.hasClass("input-group"))
$parent = $parent.parent();
$(this)
.removeClass('valid')
.removeClass(conf.errorElementClass)
.css('border-color', '');
$parent
.removeClass('has-error')
.removeClass('has-success')
.find('.' + conf.errorMessageClass) // remove inline error message
.remove();
});
},
_setInlineErrorMessage = function ($input, mess, conf, $messageContainer) {
var custom = _getInlineErrorElement($input);
var $parent = $input.parent();
if ($parent.hasClass("input-group"))
$parent = $parent.parent();
if (custom) {
custom.innerHTML = mess;
}
else if (typeof $messageContainer == 'object') {
var $found = false;
$messageContainer.find('.' + conf.errorMessageClass).each(function () {
if (this.inputReferer == $input[0]) {
$found = $(this);
return false;
}
});
if ($found) {
if (!mess) {
$found.remove();
} else {
$found.html(mess);
}
} else {
var $mess = $('
' + mess + '
');
$mess[0].inputReferer = $input[0];
$messageContainer.prepend($mess);
}
}
else {
var $mess = $parent.find('.' + conf.errorMessageClass + '.help-block');
if ($mess.length == 0) {
$mess = $('').addClass('help-block').addClass(conf.errorMessageClass);
$mess.appendTo($parent);
}
$mess.html(mess);
}
},
_getInlineErrorElement = function ($input, conf) {
return document.getElementById($input.attr('name') + '_err_msg');
},
_templateMessage = function ($form, title, errorMessages, conf) {
var messages = conf.errorMessageTemplate.messages.replace(/\{errorTitle\}/g, title);
var fields = [];
$.each(errorMessages, function (i, msg) {
fields.push(conf.errorMessageTemplate.field.replace(/\{msg\}/g, msg));
});
messages = messages.replace(/\{fields\}/g, fields.join(''));
var container = conf.errorMessageTemplate.container.replace(/\{errorMessageClass\}/g, conf.errorMessageClass);
container = container.replace(/\{messages\}/g, messages);
$form.children().eq(0).before(container);
};
/**
* Assigns validateInputOnBlur function to elements blur event
*
* @param {Object} language Optional, will override $.formUtils.LANG
* @param {Object} settings Optional, will override the default settings
* @return {jQuery}
*/
$.fn.validateOnBlur = function (language, settings) {
this.find('input[data-validation],textarea[data-validation],select[data-validation]')
.bind('blur.validation', function () {
$(this).validateInputOnBlur(language, settings, true, 'blur');
});
return this;
};
/*
* Assigns validateInputOnBlur function to elements custom event
* @param {Object} language Optional, will override $.formUtils.LANG
* @param {Object} settings Optional, will override the default settings
* * @return {jQuery}
*/
$.fn.validateOnEvent = function (language, settings) {
this.find('input[data-validation][data-validation-event],textarea[data-validation][data-validation-event],select[data-validation][data-validation-event]')
.each(function () {
var $el = $(this),
etype = $el.attr("data-validation-event");
if (etype) {
$el.bind(etype + ".validation", function () {
$(this).validateInputOnBlur(language, settings, true, etype);
});
}
});
return this;
};
/**
* fade in help message when input gains focus
* fade out when input loses focus
*
*
* @param {String} attrName - Optional, default is data-help
* @return {jQuery}
*/
$.fn.showHelpOnFocus = function (attrName) {
if (!attrName) {
attrName = 'data-validation-help';
}
// Remove previously added event listeners
this.find('.has-help-txt')
.valAttr('has-keyup-event', false)
.removeClass('has-help-txt');
// Add help text listeners
this.find('textarea,input').each(function () {
var $elem = $(this),
className = 'jquery_form_help_' + ($elem.attr('name') || '').replace(/(:|\.|\[|\])/g, ""),
help = $elem.attr(attrName);
if (help) {
$elem
.addClass('has-help-txt')
.unbind('focus.help')
.bind('focus.help', function () {
var $help = $elem.parent().find('.' + className);
if ($help.length == 0) {
$help = $('')
.addClass(className)
.addClass('help')
.addClass('help-block') // twitter bs
.text(help)
.hide();
$elem.after($help);
}
$help.fadeIn();
})
.unbind('blur.help')
.bind('blur.help', function () {
$(this)
.parent()
.find('.' + className)
.fadeOut('slow');
});
}
});
return this;
};
/**
* Validate single input when it loses focus
* shows error message in a span element
* that is appended to the parent element
*
* @param {Object} [language] Optional, will override $.formUtils.LANG
* @param {Object} [conf] Optional, will override the default settings
* @param {Boolean} attachKeyupEvent Optional
* @param {String} eventType
* @return {jQuery}
*/
$.fn.validateInputOnBlur = function (language, conf, attachKeyupEvent, eventType) {
$.formUtils.eventType = eventType;
if ((this.valAttr('suggestion-nr') || this.valAttr('postpone') || this.hasClass('hasDatepicker')) && !window.postponedValidation) {
// This validation has to be postponed
var _self = this,
postponeTime = this.valAttr('postpone') || 200;
window.postponedValidation = function () {
_self.validateInputOnBlur(language, conf, attachKeyupEvent, eventType);
window.postponedValidation = false;
};
setTimeout(function () {
if (window.postponedValidation) {
window.postponedValidation();
}
}, postponeTime);
return this;
}
language = $.extend({}, $.formUtils.LANG, language || {});
_removeErrorStyle(this, conf);
var $elem = this,
$form = $elem.closest("form"),
validationRule = $elem.attr(conf.validationRuleAttribute),
validation = $.formUtils.validateInput(
$elem,
language,
$.extend({}, conf, {errorMessagePosition: 'element'}),
$form,
eventType
);
if (validation === true) {
$elem
.addClass('valid')
.parent()
.addClass('has-success'); // twitter bs
} else if (validation !== null) {
_applyErrorStyle($elem, conf);
_setInlineErrorMessage($elem, validation, conf, conf.errorMessagePosition);
if (attachKeyupEvent) {
$elem
.unbind('keyup.validation')
.bind('keyup.validation', function () {
$(this).validateInputOnBlur(language, conf, false, 'keyup');
});
}
}
return this;
};
/**
* Short hand for fetching/adding/removing element attributes
* prefixed with 'data-validation-'
*
* @param {String} name
* @param {String|Boolean} [val]
* @return string|undefined
* @protected
*/
$.fn.valAttr = function (name, val) {
if (val === undefined) {
return this.attr('data-validation-' + name);
} else if (val === false || val === null) {
return this.removeAttr('data-validation-' + name);
} else {
if (name.length > 0)
name = '-' + name;
return this.attr('data-validation' + name, val);
}
};
/**
* Function that validate all inputs in given element
*
* @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;
$.formUtils.isValidatingEntireForm = true;
$.formUtils.haltValidation = false;
/**
* Adds message to error message stack if not already in the message stack
*
* @param {String} mess
* @para {jQuery} $elem
*/
var addErrorMessage = function (mess, $elem) {
// validate server side will return null as error message before the server is requested
if (mess !== null) {
if ($.inArray(mess, errorMessages) < 0) {
errorMessages.push(mess);
}
errorInputs.push($elem);
$elem.attr('current-error', mess);
if (displayError)
_applyErrorStyle($elem, conf);
}
},
/** 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) {
$form.find('.' + conf.errorMessageClass + '.alert').remove();
_removeErrorStyle($form.find('.' + conf.errorElementClass + ',.valid'), conf);
}
// Validate element values
$form.find('input,textarea,select').filter(':not([type="submit"],[type="button"])').each(function () {
var $elem = $(this);
var elementType = $elem.attr('type');
if (!ignoreInput($elem.attr('name'), elementType)) {
var validation = $.formUtils.validateInput(
$elem,
language,
conf,
$form,
'submit'
);
// Run element validation callback
if (typeof conf.onElementValidate == 'function') {
conf.onElementValidate((validation === true), $elem, $form, validation);
}
if (validation !== true) {
addErrorMessage(validation, $elem);
} else {
$elem
.valAttr('current-error', false)
.addClass('valid')
.parent()
.addClass('has-success');
}
}
});
// 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 (!$.formUtils.haltValidation && errorInputs.length > 0) {
if (displayError) {
// display all error messages in top of form
if (conf.errorMessagePosition === 'top') {
_templateMessage($form, language.errorTitle, errorMessages, conf);
}
// Customize display message
else if (conf.errorMessagePosition === 'custom') {
if (typeof conf.errorMessageCustom === 'function') {
conf.errorMessageCustom($form, language.errorTitle, errorMessages, conf);
}
}
// Display error message below input field or in defined container
else {
$.each(errorInputs, function (i, $input) {
_setInlineErrorMessage($input, $input.attr('current-error'), conf, conf.errorMessagePosition);
});
}
if (conf.scrollToTopOnError) {
$window.scrollTop($form.offset().top - 20);
}
}
return false;
}
return !$.formUtils.haltValidation;
};
/**
* @deprecated
* @param language
* @param conf
*/
$.fn.validateForm = function (language, conf) {
if (window.console && typeof window.console.warn == 'function') {
window.console.warn('Use of deprecated function $.validateForm, use $.isValid instead');
}
return this.isValid(language, conf, true);
}
/**
* Plugin for displaying input length restriction
*/
$.fn.restrictLength = function (maxLengthElement) {
new $.formUtils.lengthRestriction(this, maxLengthElement);
return this;
};
/**
* Add suggestion dropdown to inputs having data-suggestions with a comma
* separated string with suggestions
* @param {Array} [settings]
* @returns {jQuery}
*/
$.fn.addSuggestions = function (settings) {
var sugs = false;
this.find('input').each(function () {
var $field = $(this);
sugs = $.split($field.attr('data-suggestions'));
if (sugs.length > 0 && !$field.hasClass('has-suggestions')) {
$.formUtils.suggest($field, sugs, settings);
$field.addClass('has-suggestions');
}
});
return this;
};
/**
* A bit smarter split function
* @param {String} val
* @param {Function|String} [func]
* @param {String} [delim]
* @returns {Array|void}
*/
$.split = function (val, func, delim) {
if (typeof func != 'function') {
// return string
if (!val)
return [];
var values = [];
$.each(val.split(func ? func : ','), function (i, str) {
str = $.trim(str);
if (str.length)
values.push(str);
});
return values;
} else if (val) {
// use callback on each
if (!delim)
delim = ',';
$.each(val.split(delim), function (i, str) {
str = $.trim(str);
if (str.length)
return func(str, i);
});
}
};
/**
* Short hand function that makes the validation setup require less code
* @param conf
*/
$.validate = function (conf) {
var defaultConf = $.extend($.formUtils.defaultConfig(), {
form: 'form',
/*
* Enable custom event for validation
*/
validateOnEvent: true,
validateOnBlur: true,
showHelpOnFocus: true,
addSuggestions: true,
modules: '',
onModulesLoaded: null,
language: false,
onSuccess: false,
onError: false,
onElementValidate: false
});
conf = $.extend(defaultConf, conf || {});
// Add validation to forms
$(conf.form).each(function (i, form) {
var $form = $(form);
$window.trigger('formValidationSetup', [$form]);
// Remove all event listeners previously added
$form.find('.has-help-txt')
.unbind('focus.validation')
.unbind('blur.validation');
$form
.removeClass('has-validation-callback')
.unbind('submit.validation')
.unbind('reset.validation')
.find('input[data-validation],textarea[data-validation]')
.unbind('blur.validation');
// Validate when submitted
$form.bind('submit.validation', function () {
var $form = $(this);
if ($.formUtils.isLoadingModules) {
setTimeout(function () {
$form.trigger('submit.validation');
}, 200);
return false;
}
var valid = $form.isValid(conf.language, conf);
if (valid && typeof conf.onSuccess == 'function') {
var callbackResponse = conf.onSuccess($form);
if (callbackResponse === false)
return false;
} else if (!valid && typeof conf.onError == 'function') {
conf.onError($form);
return false;
} else {
return valid;
}
})
.bind('reset.validation', function () {
// remove messages
$(this).find('.' + conf.errorMessageClass + '.alert').remove();
_removeErrorStyle($(this).find('.' + conf.errorElementClass + ',.valid'), conf);
})
.addClass('has-validation-callback');
if (conf.showHelpOnFocus) {
$form.showHelpOnFocus();
}
if (conf.addSuggestions) {
$form.addSuggestions();
}
if (conf.validateOnBlur) {
$form.validateOnBlur(conf.language, conf);
$form.bind('html5ValidationAttrsFound', function () {
$form.validateOnBlur(conf.language, conf);
})
}
if (conf.validateOnEvent) {
$form.validateOnEvent(conf.language, conf);
}
});
if (conf.modules != '') {
if (typeof conf.onModulesLoaded == 'function') {
$window.one('validatorsLoaded', conf.onModulesLoaded);
}
$.formUtils.loadModules(conf.modules);
}
};
/**
* Object containing utility methods for this plugin
*/
$.formUtils = {
/**
* Default config for $(...).isValid();
*/
defaultConfig: function () {
return {
ignore: [], // Names of inputs not to be validated even though node attribute containing the validation rules tells us to
errorElementClass: 'error', // Class that will be put on elements which value is invalid
borderColorOnError: 'red', // 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: 'element', // Can be either "top" or "element" or "custom"
errorMessageTemplate: {
container: '{messages}
',
messages: '{errorTitle}',
field: '{msg}'
},
errorMessageCustom: _templateMessage,
scrollToTopOnError: true,
dateFormat: 'yyyy-mm-dd',
addValidClassOnAll: false, // whether or not to apply class="valid" even if the input wasn't validated
decimalSeparator: '.'
}
},
/**
* Available validators
*/
validators: {},
/**
* Events triggered by form validator
*/
_events: {load: [], valid: [], invalid: []},
/**
* Setting this property to true during validation will
* stop further validation from taking place and form will
* not be sent
*/
haltValidation: false,
/**
* This variable will be true $.fn.isValid() is called
* and false when $.fn.validateOnBlur is called
*/
isValidatingEntireForm: false,
/**
* Function for adding a validator
* @param {Object} validator
*/
addValidator: function (validator) {
// prefix with "validate_" for backward compatibility reasons
var name = validator.name.indexOf('validate_') === 0 ? validator.name : 'validate_' + validator.name;
if (validator.validateOnKeyUp === undefined)
validator.validateOnKeyUp = true;
this.validators[name] = validator;
},
/**
* @var {Boolean}
*/
isLoadingModules: false,
/**
* @var {Object}
*/
loadedModules: {},
/**
* @example
* $.formUtils.loadModules('date, security.dev');
*
* Will load the scripts date.js and security.dev.js from the
* directory where this script resides. If you want to load
* the modules from another directory you can use the
* path argument.
*
* The script will be cached by the browser unless the module
* name ends with .dev
*
* @param {String} modules - Comma separated string with module file names (no directory nor file extension)
* @param {String} [path] - Optional, path where the module files is located if their not in the same directory as the core modules
* @param {Boolean} [fireEvent] - Optional, whether or not to fire event 'load' when modules finished loading
*/
loadModules: function (modules, path, fireEvent) {
if (fireEvent === undefined)
fireEvent = true;
if ($.formUtils.isLoadingModules) {
setTimeout(function () {
$.formUtils.loadModules(modules, path, fireEvent);
});
return;
}
var hasLoadedAnyModule = false,
loadModuleScripts = function (modules, path) {
var moduleList = $.split(modules),
numModules = moduleList.length,
moduleLoadedCallback = function () {
numModules--;
if (numModules == 0) {
$.formUtils.isLoadingModules = false;
if (fireEvent && hasLoadedAnyModule) {
$window.trigger('validatorsLoaded');
}
}
};
if (numModules > 0) {
$.formUtils.isLoadingModules = true;
}
var cacheSuffix = '?__=' + (new Date().getTime()),
appendToElement = document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0];
$.each(moduleList, function (i, modName) {
modName = $.trim(modName);
if (modName.length == 0) {
moduleLoadedCallback();
}
else {
var scriptUrl = path + modName + (modName.substr(-3) == '.js' ? '' : '.js'),
script = document.createElement('SCRIPT');
if (scriptUrl in $.formUtils.loadedModules) {
// already 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.substr(-7) == '.dev.js' ? cacheSuffix : '');
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').each(function () {
if (this.src) {
var scriptName = this.src.substr(this.src.lastIndexOf('/') + 1, this.src.length);
if (scriptName.indexOf('jquery.form-validator.js') > -1 || scriptName.indexOf('jquery.form-validator.min.js') > -1) {
foundPath = this.src.substr(0, this.src.lastIndexOf('/')) + '/';
if (foundPath == '/')
foundPath = '';
return false;
}
}
});
if (foundPath !== false) {
loadModuleScripts(modules, foundPath);
return true;
}
return false;
};
if (!findScriptPathAndLoadModules()) {
$(findScriptPathAndLoadModules);
}
}
},
/**
* Validate the value of given element according to the validation rules
* found in the attribute data-validation. Will return null if no validation
* should take place, returns true if valid or error message if not valid
*
* @param {jQuery} $elem
* @param {Object} language ($.formUtils.LANG)
* @param {Object} conf
* @param {jQuery} $form
* @param {String} [eventContext]
* @return {String|Boolean}
*/
validateInput: function ($elem, language, conf, $form, eventContext) {
if ($elem.attr('disabled'))
return null; // returning null will prevent that the valid class gets applied to the element
$elem.trigger('beforeValidation');
var value = $elem.val() || '',
optional = $elem.valAttr('optional'),
// test if a checkbox forces this element to be validated
validationDependsOnCheckedInput = false,
validationDependentInputIsChecked = false,
validateIfCheckedElement = false,
// get value of this element's attribute "... if-checked"
validateIfCheckedElementName = $elem.valAttr("if-checked");
// make sure we can proceed
if (validateIfCheckedElementName != null) {
// Set the boolean telling us that the validation depends
// on another input being checked
validationDependsOnCheckedInput = true;
// select the checkbox type element in this form
validateIfCheckedElement = $form.find('input[name="' + validateIfCheckedElementName + '"]');
// test if it's property "checked" is checked
if (validateIfCheckedElement.prop('checked')) {
// set value for validation checkpoint
validationDependentInputIsChecked = true;
}
}
// validation checkpoint
// if empty AND optional attribute is present
// OR depending on a checkbox being checked AND checkbox is checked, return true
if ((!value && optional === 'true') || (validationDependsOnCheckedInput && !validationDependentInputIsChecked)) {
return conf.addValidClassOnAll ? true : null;
}
var validationRules = $elem.attr(conf.validationRuleAttribute),
// see if form element has inline err msg attribute
validationErrorMsg = true;
if (!validationRules) {
return conf.addValidClassOnAll ? true : null;
}
$.split(validationRules, function (rule) {
if (rule.indexOf('validate_') !== 0) {
rule = 'validate_' + rule;
}
var validator = $.formUtils.validators[rule];
if (validator && typeof validator['validatorFunction'] == 'function') {
// special change of element for checkbox_group rule
if (rule == 'validate_checkbox_group') {
// set element to first in group, so error msg is set only once
$elem = $("[name='" + $elem.attr('name') + "']:eq(0)");
}
var isValid = null;
if (eventContext != 'keyup' || validator.validateOnKeyUp) {
isValid = validator.validatorFunction(value, $elem, conf, language, $form);
}
if (!isValid) {
validationErrorMsg = null;
if (isValid !== null) {
validationErrorMsg = $elem.attr(conf.validationErrorMsgAttribute + '-' + rule.replace('validate_', ''));
if (!validationErrorMsg) {
validationErrorMsg = $elem.attr(conf.validationErrorMsgAttribute);
if (!validationErrorMsg) {
validationErrorMsg = language[validator.errorMessageKey];
if (!validationErrorMsg)
validationErrorMsg = validator.errorMessage;
}
}
}
return false; // breaks the iteration
}
} else {
throw new Error('Using undefined validator "' + rule + '"');
}
}, ' ');
if (typeof validationErrorMsg == 'string') {
$elem.trigger('validation', false);
return validationErrorMsg;
} else if (validationErrorMsg === null && !conf.addValidClassOnAll) {
return null;
} else {
$elem.trigger('validation', true);
return true;
}
},
/**
* Is it a correct date according to given dateFormat. Will return false if not, otherwise
* an array 0=>year 1=>month 2=>day
*
* @param {String} val
* @param {String} dateFormat
* @return {Array}|{Boolean}
*/
parseDate: function (val, dateFormat) {
var divider = dateFormat.replace(/[a-zA-Z]/gi, '').substring(0, 1),
regexp = '^',
formatParts = dateFormat.split(divider),
matches, day, month, year;
$.each(formatParts, function (i, part) {
regexp += (i > 0 ? '\\' + divider : '') + '(\\d{' + part.length + '})';
});
regexp += '$';
matches = val.match(new RegExp(regexp));
if (matches === null) {
return false;
}
var findDateUnit = function (unit, formatParts, matches) {
for (var i = 0; i < formatParts.length; i++) {
if (formatParts[i].substring(0, 1) === unit) {
return $.formUtils.parseDateInt(matches[i + 1]);
}
}
return -1;
};
month = findDateUnit('m', formatParts, matches);
day = findDateUnit('d', formatParts, matches);
year = findDateUnit('y', formatParts, matches);
if ((month === 2 && day > 28 && (year % 4 !== 0 || year % 100 === 0 && year % 400 !== 0))
|| (month === 2 && day > 29 && (year % 4 === 0 || year % 100 !== 0 && year % 400 === 0))
|| month > 12 || month === 0) {
return false;
}
if ((this.isShortMonth(month) && day > 30) || (!this.isShortMonth(month) && day > 31) || day === 0) {
return false;
}
return [year, month, day];
},
/**
* skum fix. är talet 05 eller lägre ger parseInt rätt int annars får man 0 när man kör parseInt?
*
* @param {String} val
* @param {Number}
*/
parseDateInt: function (val) {
if (val.indexOf('0') === 0) {
val = val.replace('0', '');
}
return parseInt(val, 10);
},
/**
* Has month only 30 days?
*
* @param {Number} m
* @return {Boolean}
*/
isShortMonth: function (m) {
return (m % 2 === 0 && m < 7) || (m % 2 !== 0 && m > 7);
},
/**
* Restrict input length
*
* @param {jQuery} $inputElement Jquery Html object
* @param {jQuery} $maxLengthElement jQuery Html Object
* @return void
*/
lengthRestriction: function ($inputElement, $maxLengthElement) {
// read maxChars from counter display initial text 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)
* @return array
*/
numericRangeCheck: function (value, rangeAllowed)
{
// split by dash
var range = $.split(rangeAllowed, '-');
// min or max
var minmax = parseInt(rangeAllowed.substr(3), 10)
// range ?
if (range.length == 2 && (value < parseInt(range[0], 10) || value > parseInt(range[1], 10)))
{
return ["out", range[0], range[1]];
} // value is out of 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
$window.bind('resize', function () {
$('.jquery-form-suggestions').each(function () {
var $container = $(this),
suggestID = $container.attr('data-suggest-container');
setSuggsetionPosition($container, $('.suggestions-' + suggestID).eq(0));
});
});
}
this._numSuggestionElements++;
var onSelectSuggestion = function ($el) {
var suggestionId = $el.valAttr('suggestion-nr');
$.formUtils._selectedSuggestion = null;
$.formUtils._previousTypedVal = null;
$('.jquery-form-suggestion-' + suggestionId).fadeOut('fast');
};
$elem
.data('suggestions', suggestions)
.valAttr('suggestion-nr', this._numSuggestionElements)
.unbind('focus.suggest')
.bind('focus.suggest', function () {
$(this).trigger('keyup');
$.formUtils._selectedSuggestion = null;
})
.unbind('keyup.suggest')
.bind('keyup.suggest', function () {
var $input = $(this),
foundSuggestions = [],
val = $.trim($input.val()).toLocaleLowerCase();
if (val == $.formUtils._previousTypedVal) {
return;
}
else {
$.formUtils._previousTypedVal = val;
}
var hasTypedSuggestion = false,
suggestionId = $input.valAttr('suggestion-nr'),
$suggestionContainer = $('.jquery-form-suggestion-' + suggestionId);
$suggestionContainer.scrollTop(0);
// Find the right 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());
onSelectSuggestion($input);
});
});
}
})
.unbind('keydown.validation')
.bind('keydown.validation', function (e) {
var code = (e.keyCode ? e.keyCode : e.which),
suggestionId,
$suggestionContainer,
$input = $(this);
if (code == 13 && $.formUtils._selectedSuggestion !== null) {
suggestionId = $input.valAttr('suggestion-nr');
$suggestionContainer = $('.jquery-form-suggestion-' + suggestionId);
if ($suggestionContainer.length > 0) {
var newText = $suggestionContainer.find('div').eq($.formUtils._selectedSuggestion).text();
$input.val(newText);
onSelectSuggestion($input);
e.preventDefault();
}
}
else {
suggestionId = $input.valAttr('suggestion-nr');
$suggestionContainer = $('.jquery-form-suggestion-' + suggestionId);
var $suggestions = $suggestionContainer.children();
if ($suggestions.length > 0 && $.inArray(code, [38, 40]) > -1) {
if (code == 38) { // key 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!',
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',
badInt: 'The input value was not a correct number',
badSecurityNumber: 'Your social security number was incorrect',
badUKVatAnswer: 'Incorrect UK VAT Number',
badStrength: 'The password isn\'t strong enough',
badNumberOfSelectedOptionsStart: 'You have to choose at least ',
badNumberOfSelectedOptionsEnd: ' answers',
badAlphaNumeric: 'The input value can only contain alphanumeric characters ',
badAlphaNumericExtra: ' and ',
wrongFileSize: 'The file you are trying to upload is too large',
wrongFileType: 'The file you are trying to upload is of the wrong type',
groupCheckedRangeStart: 'Please choose between ',
groupCheckedTooFewStart: 'Please choose at least ',
groupCheckedTooManyStart: 'Please choose a maximum of ',
groupCheckedEnd: ' item(s)',
badCreditCard: 'The credit card number is not correct',
badCVV: 'The CVV number was not correct'
}
};
/* * * * * * * * * * * * * * * * * * * * * *
CORE VALIDATORS
* * * * * * * * * * * * * * * * * * * * */
/*
* Validate email
*/
$.formUtils.addValidator({
name: 'email',
validatorFunction: function (email) {
var emailParts = email.toLowerCase().split('@');
if (emailParts.length == 2) {
var regex = /^[a-zA-Z0-9.'!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
return regex.test(email)
return $.formUtils.validators.validate_domain.validatorFunction(emailParts[1]) &&
!(/[^\w\+\.\-]/.test(emailParts[0]));
}
return false;
},
errorMessage: '',
errorMessageKey: 'badEmail'
});
/*
* Validate domain name
*/
$.formUtils.addValidator({
name: 'domain',
validatorFunction: function (val, $input) {
var topDomains = ['.ac', '.ad', '.ae', '.aero', '.af', '.ag', '.ai', '.al', '.am', '.an', '.ao',
'.aq', '.ar', '.arpa', '.as', '.asia', '.at', '.au', '.aw', '.ax', '.az', '.ba', '.bb',
'.bd', '.be', '.bf', '.bg', '.bh', '.bi', '.bike', '.biz', '.bj', '.bm', '.bn', '.bo',
'.br', '.bs', '.bt', '.bv', '.bw', '.by', '.bz', '.ca', '.camera', '.cat', '.cc', '.cd',
'.cf', '.cg', '.ch', '.ci', '.ck', '.cl', '.clothing', '.cm', '.cn', '.co', '.com',
'.construction', '.contractors', '.coop', '.cr', '.cu', '.cv', '.cw', '.cx', '.cy', '.cz',
'.de', '.diamonds', '.directory', '.dj', '.dk', '.dm', '.do', '.dz', '.ec', '.edu', '.ee',
'.eg', '.enterprises', '.equipment', '.er', '.es', '.estate', '.et', '.eu', '.fi', '.fj',
'.fk', '.fm', '.fo', '.fr', '.ga', '.gallery', '.gb', '.gd', '.ge', '.gf', '.gg', '.gh',
'.gi', '.gl', '.gm', '.gn', '.gov', '.gp', '.gq', '.gr', '.graphics', '.gs', '.gt', '.gu',
'.guru', '.gw', '.gy', '.hk', '.hm', '.hn', '.holdings', '.hr', '.ht', '.hu', '.id', '.ie',
'.il', '.im', '.in', '.info', '.int', '.io', '.iq', '.ir', '.is', '.it', '.je', '.jm', '.jo',
'.jobs', '.jp', '.ke', '.kg', '.kh', '.ki', '.kitchen', '.km', '.kn', '.kp', '.kr', '.kw',
'.ky', '.kz', '.la', '.land', '.lb', '.lc', '.li', '.lighting', '.lk', '.lr', '.ls', '.lt',
'.lu', '.lv', '.ly', '.ma', '.mc', '.md', '.me', '.menu', '.mg', '.mh', '.mil', '.mk', '.ml',
'.mm', '.mn', '.mo', '.mobi', '.mp', '.mq', '.mr', '.ms', '.mt', '.mu', '.museum', '.mv',
'.mw', '.mx', '.my', '.mz', '.na', '.name', '.nc', '.ne', '.net', '.nf', '.ng', '.ni',
'.nl', '.no', '.np', '.nr', '.nu', '.nz', '.om', '.org', '.pa', '.pe', '.pf', '.pg', '.ph',
'.photography', '.pk', '.pl', '.plumbing', '.pm', '.pn', '.post', '.pr', '.pro', '.ps', '.pt',
'.pw', '.py', '.qa', '.re', '.ro', '.rs', '.ru', '.rw', '.sa', '.sb', '.sc', '.sd', '.se',
'.sexy', '.sg', '.sh', '.si', '.singles', '.sj', '.sk', '.sl', '.sm', '.sn', '.so', '.sr',
'.st', '.su', '.sv', '.sx', '.sy', '.sz', '.tattoo', '.tc', '.td', '.technology', '.tel', '.tf',
'.tg', '.th', '.tips', '.tj', '.tk', '.tl', '.tm', '.tn', '.to', '.today', '.tp', '.tr', '.travel',
'.tt', '.tv', '.tw', '.tz', '.ua', '.ug', '.uk', '.uno', '.us', '.uy', '.uz', '.va', '.vc', '.ve',
'.ventures', '.vg', '.vi', '.vn', '.voyage', '.vu', '.wf', '.ws', '.xn--3e0b707e', '.xn--45brj9c',
'.xn--80ao21a', '.xn--80asehdb', '.xn--80aswg', '.xn--90a3ac', '.xn--clchc0ea0b2g2a9gcd', '.xn--fiqs8s',
'.xn--fiqz9s', '.xn--fpcrj9c3d', '.xn--fzc2c9e2c', '.xn--gecrj9c', '.xn--h2brj9c', '.xn--j1amh',
'.xn--j6w193g', '.xn--kprw13d', '.xn--kpry57d', '.xn--l1acc', '.xn--lgbbat1ad8j', '.xn--mgb9awbf',
'.xn--mgba3a4f16a', '.xn--mgbaam7a8h', '.xn--mgbayh7gpa', '.xn--mgbbh1a71e', '.xn--mgbc0a9azcg',
'.xn--mgberp4a5d4ar', '.xn--mgbx4cd0ab', '.xn--ngbc5azd', '.xn--o3cw4h', '.xn--ogbpf8fl', '.xn--p1ai',
'.xn--pgbs0dh', '.xn--q9jyb4c', '.xn--s9brj9c', '.xn--unup4y', '.xn--wgbh1c', '.xn--wgbl6a',
'.xn--xkc2al3hye2a', '.xn--xkc2dl3a5ee0h', '.xn--yfro4i67o', '.xn--ygbi2ammx', '.xxx', '.ye',
'.yt', '.za', '.zm', '.zw'],
ukTopDomains = ['co', 'me', 'ac', 'gov', 'judiciary', 'ltd', 'mod', 'net', 'nhs', 'nic',
'org', 'parliament', 'plc', 'police', 'sch', 'bl', 'british-library', 'jet', 'nls'],
dot = val.lastIndexOf('.'),
domain = val.substring(0, dot),
ext = val.substring(dot, val.length),
hasTopDomain = false;
for (var i = 0; i < topDomains.length; i++) {
if (topDomains[i] === ext) {
if (ext === '.uk') {
//Run Extra Checks for UK Domain Names
var domainParts = val.split('.');
var tld2 = domainParts[domainParts.length - 2];
for (var j = 0; j < ukTopDomains.length; j++) {
if (ukTopDomains[j] === tld2) {
hasTopDomain = true;
break;
}
}
if (hasTopDomain)
break;
} else {
hasTopDomain = true;
break;
}
}
}
if (!hasTopDomain) {
return false;
} else if (dot < 2 || dot > 57) {
return $.inArray(val, ['i.net', 'q.com', 'q.net', 'x.com', 'x.org', 'z.com', 'w.org']) > -1;
} else {
var firstChar = domain.substring(0, 1),
lastChar = domain.substring(domain.length - 1, domain.length);
if (firstChar === '-' || firstChar === '.' || lastChar === '-' || lastChar === '.') {
return false;
}
if (domain.split('..').length > 1) {
return false;
}
if (domain.replace(/[-\da-z\.]/g, '') !== '') {
return false;
}
}
if (typeof $input !== 'undefined') {
$input.val(val);
}
return true;
},
errorMessage: '',
errorMessageKey: 'badDomain'
});
/*
* Validate required
*/
$.formUtils.addValidator({
name: 'required',
validatorFunction: function (val, $el, config, language, $form) {
switch ($el.attr('type')) {
case 'checkbox':
return $el.is(':checked');
case 'radio':
return $form.find('input[name="' + $el.attr('name') + '"]').filter(':checked').length > 0;
default:
return $.trim(val) !== '';
}
},
errorMessage: '',
errorMessageKey: 'requiredFields'
});
/*
* Validate length range
*/
$.formUtils.addValidator({
name: 'length',
validatorFunction: function (val, $el, conf, lang) {
var lengthAllowed = $el.valAttr('length'),
type = $el.attr('type');
if (lengthAllowed == undefined) {
var elementType = $el.get(0).nodeName;
alert('Please add attribute "data-validation-length" to ' + elementType + ' 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];
var domainSlashPos = domain.indexOf('/');
if (domainSlashPos > -1)
domain = domain.substr(0, domainSlashPos);
return $.formUtils.validators.validate_domain.validatorFunction(domain); // todo: add support for IP-addresses
}
return false;
},
errorMessage: '',
errorMessageKey: 'badUrl'
});
/*
* Validate number (floating or integer)
*/
$.formUtils.addValidator({
name: 'number',
validatorFunction: function (val, $el, conf) {
if (val !== '') {
var allowing = $el.valAttr('allowing') || '',
decimalSeparator = $el.valAttr('decimal-separator') || conf.decimalSeparator,
allowsRange = false,
begin, end,
steps = $el.valAttr('step') || '',
allowsSteps = false;
if (allowing.indexOf('number') == -1)
allowing += ',number';
if (allowing.indexOf('negative') > -1 && val.indexOf('-') === 0) {
val = val.substr(1);
}
if (allowing.indexOf('range') > -1)
{
begin = parseFloat(allowing.substring(allowing.indexOf("[") + 1, allowing.indexOf(";")));
end = parseFloat(allowing.substring(allowing.indexOf(";") + 1, allowing.indexOf("]")));
allowsRange = true;
}
if (steps != "")
{
allowsSteps = true;
}
if (decimalSeparator == ',') {
if (val.indexOf('.') > -1) {
return false;
}
// Fix for checking range with floats using ,
val = val.replace(',', '.');
}
if (allowing.indexOf('number') > -1 && val.replace(/[0-9]/g, '') === '' && (!allowsRange || (val >= begin && val <= end)) && (!allowsSteps || (val % steps == 0))) {
return true;
}
if (allowing.indexOf('float') > -1 && val.match(new RegExp('^([0-9]+)\\.([0-9]+)$')) !== null && (!allowsRange || (val >= begin && val <= end)) && (!allowsSteps || (val % steps == 0))) {
return true;
}
}
return false;
},
errorMessage: '',
errorMessageKey: 'badInt'
});
/*
* Validate alpha numeric
*/
$.formUtils.addValidator({
name: 'alphanumeric',
validatorFunction: function (val, $el, conf, language) {
var patternStart = '^([a-zA-Z0-9',
patternEnd = ']+)$',
additionalChars = $el.attr('data-validation-allowing'),
pattern = '';
if (additionalChars) {
pattern = patternStart + additionalChars + patternEnd;
var extra = additionalChars.replace(/\\/g, '');
if (extra.indexOf(' ') > -1) {
extra = extra.replace(' ', '');
extra += ' and spaces ';
}
this.errorMessage = language.badAlphaNumeric + language.badAlphaNumericExtra + extra;
} else {
pattern = patternStart + patternEnd;
this.errorMessage = language.badAlphaNumeric;
}
return new RegExp(pattern).test(val);
},
errorMessage: '',
errorMessageKey: ''
});
/*
* Validate against regexp
*/
$.formUtils.addValidator({
name: 'custom',
validatorFunction: function (val, $el, conf) {
var regexp = new RegExp($el.valAttr('regexp'));
return regexp.test(val);
},
errorMessage: '',
errorMessageKey: 'badCustomVal'
});
/*
* Validate date
*/
$.formUtils.addValidator({
name: 'date',
validatorFunction: function (date, $el, conf) {
var dateFormat = 'yyyy-mm-dd';
if ($el.valAttr('format')) {
dateFormat = $el.valAttr('format');
}
else if (conf.dateFormat) {
dateFormat = conf.dateFormat;
}
return $.formUtils.parseDate(date, dateFormat) !== false;
},
errorMessage: '',
errorMessageKey: 'badDate'
});
/*
* Validate group of checkboxes, validate qty required is checked
* written by Steve Wasiura : http://stevewasiura.waztech.com
* element attrs
* data-validation="checkbox_group"
* data-validation-qty="1-2" // min 1 max 2
* data-validation-error-msg="chose min 1, max of 2 checkboxes"
*/
$.formUtils.addValidator({
name: 'checkbox_group',
validatorFunction: function (val, $el, conf, lang, $form)
{ // preset return var
var checkResult = true;
// get name of element. since it is a checkbox group, all checkboxes will have same name
var elname = $el.attr('name');
// get count of checked checkboxes with this name
var checkedCount = $("input[type=checkbox][name^='" + elname + "']:checked", $form).length;
// get el attr that specs qty required / allowed
var 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;
checkResult = false;
break;
// below min qty
case "min":
this.errorMessage = lang.groupCheckedTooFewStart + qtyCheckResults[1] + lang.groupCheckedEnd;
checkResult = false;
break;
// above max qty
case "max":
this.errorMessage = lang.groupCheckedTooManyStart + qtyCheckResults[1] + lang.groupCheckedEnd;
checkResult = false;
break;
// ok
default:
checkResult = true;
}
return checkResult;
}
// errorMessage : '', // set above in switch statement
// errorMessageKey: '' // not used
});
})(jQuery);