mirror of
https://github.com/Oreolek/ifhub.club.git
synced 2024-07-08 01:14:24 +03:00
42321428f8
* Перенесены либы из фреймворка
1339 lines
44 KiB
JavaScript
1339 lines
44 KiB
JavaScript
/*
|
|
* Parsley.js allows you to verify your form inputs frontend side, without writing a line of javascript. Or so..
|
|
*
|
|
* Author: Guillaume Potier - @guillaumepotier
|
|
*/
|
|
|
|
!function ($) {
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* Validator class stores all constraints functions and associated messages.
|
|
* Provides public interface to add, remove or modify them
|
|
*
|
|
* @class Validator
|
|
* @constructor
|
|
*/
|
|
var Validator = function ( options ) {
|
|
/**
|
|
* Error messages
|
|
*
|
|
* @property messages
|
|
* @type {Object}
|
|
*/
|
|
this.messages = {
|
|
defaultMessage: "This value seems to be invalid."
|
|
, type: {
|
|
email: "This value should be a valid email."
|
|
, url: "This value should be a valid url."
|
|
, urlstrict: "This value should be a valid url."
|
|
, number: "This value should be a valid number."
|
|
, digits: "This value should be digits."
|
|
, dateIso: "This value should be a valid date (YYYY-MM-DD)."
|
|
, alphanum: "This value should be alphanumeric."
|
|
, phone: "This value should be a valid phone number."
|
|
}
|
|
, notnull: "This value should not be null."
|
|
, notblank: "This value should not be blank."
|
|
, required: "This value is required."
|
|
, regexp: "This value seems to be invalid."
|
|
, min: "This value should be greater than or equal to %s."
|
|
, max: "This value should be lower than or equal to %s."
|
|
, range: "This value should be between %s and %s."
|
|
, minlength: "This value is too short. It should have %s characters or more."
|
|
, maxlength: "This value is too long. It should have %s characters or less."
|
|
, rangelength: "This value length is invalid. It should be between %s and %s characters long."
|
|
, mincheck: "You must select at least %s choices."
|
|
, maxcheck: "You must select %s choices or less."
|
|
, rangecheck: "You must select between %s and %s choices."
|
|
, equalto: "This value should be the same."
|
|
},
|
|
|
|
this.init( options );
|
|
};
|
|
|
|
Validator.prototype = {
|
|
|
|
constructor: Validator
|
|
|
|
/**
|
|
* Validator list. Built-in validators functions
|
|
*
|
|
* @property validators
|
|
* @type {Object}
|
|
*/
|
|
, validators: {
|
|
notnull: function ( val ) {
|
|
return val.length > 0;
|
|
}
|
|
|
|
, notblank: function ( val ) {
|
|
return 'string' === typeof val && '' !== val.replace( /^\s+/g, '' ).replace( /\s+$/g, '' );
|
|
}
|
|
|
|
// Works on all inputs. val is object for checkboxes
|
|
, required: function ( val ) {
|
|
|
|
// for checkboxes and select multiples. Check there is at least one required value
|
|
if ( 'object' === typeof val ) {
|
|
for ( var i in val ) {
|
|
if ( this.required( val[ i ] ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return this.notnull( val ) && this.notblank( val );
|
|
}
|
|
|
|
, type: function ( val, type ) {
|
|
var regExp;
|
|
|
|
switch ( type ) {
|
|
case 'number':
|
|
regExp = /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/;
|
|
break;
|
|
case 'digits':
|
|
regExp = /^\d+$/;
|
|
break;
|
|
case 'alphanum':
|
|
regExp = /^\w+$/;
|
|
break;
|
|
case 'email':
|
|
regExp = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\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])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i;
|
|
break;
|
|
case 'url':
|
|
val = new RegExp( '(https?|s?ftp|git)', 'i' ).test( val ) ? val : 'http://' + val;
|
|
/* falls through */
|
|
case 'urlstrict':
|
|
regExp = /^(https?|s?ftp|git):\/\/(((([a-z]|\d|-|\.|_|~|[\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])([a-z]|\d|-|\.|_|~|[\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])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i;
|
|
break;
|
|
case 'dateIso':
|
|
regExp = /^(\d{4})\D?(0[1-9]|1[0-2])\D?([12]\d|0[1-9]|3[01])$/;
|
|
break;
|
|
case 'phone':
|
|
regExp = /^((\+\d{1,3}(-| )?\(?\d\)?(-| )?\d{1,5})|(\(?\d{2,6}\)?))(-| )?(\d{3,4})(-| )?(\d{4})(( x| ext)\d{1,5}){0,1}$/;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
// test regExp if not null
|
|
return '' !== val ? regExp.test( val ) : false;
|
|
}
|
|
|
|
, regexp: function ( val, regExp, self ) {
|
|
return new RegExp( regExp, self.options.regexpFlag || '' ).test( val );
|
|
}
|
|
|
|
, minlength: function ( val, min ) {
|
|
return val.length >= min;
|
|
}
|
|
|
|
, maxlength: function ( val, max ) {
|
|
return val.length <= max;
|
|
}
|
|
|
|
, rangelength: function ( val, arrayRange ) {
|
|
return this.minlength( val, arrayRange[ 0 ] ) && this.maxlength( val, arrayRange[ 1 ] );
|
|
}
|
|
|
|
, min: function ( val, min ) {
|
|
return Number( val ) >= min;
|
|
}
|
|
|
|
, max: function ( val, max ) {
|
|
return Number( val ) <= max;
|
|
}
|
|
|
|
, range: function ( val, arrayRange ) {
|
|
return val >= arrayRange[ 0 ] && val <= arrayRange[ 1 ];
|
|
}
|
|
|
|
, equalto: function ( val, elem, self ) {
|
|
self.options.validateIfUnchanged = true;
|
|
|
|
return val === $( elem ).val();
|
|
}
|
|
|
|
, remote: function ( val, url, self ) {
|
|
var result = null
|
|
, data = {}
|
|
, dataType = {};
|
|
|
|
/* @livestreet begin */
|
|
data.fields = {
|
|
0: {
|
|
field: self.$element.attr( 'name' ),
|
|
value: val
|
|
}
|
|
};
|
|
data.params=ls.utils.getDataOptions(self.$element, 'remoteParam') || {};
|
|
|
|
data.security_ls_key = LIVESTREET_SECURITY_KEY;
|
|
/* @livestreet end */
|
|
|
|
if ( 'undefined' !== typeof self.options.remoteDatatype ) {
|
|
dataType = { dataType: self.options.remoteDatatype };
|
|
}
|
|
|
|
var manage = function ( isConstraintValid, message ) {
|
|
// remove error message if we got a server message, different from previous message
|
|
if ( 'undefined' !== typeof message && 'undefined' !== typeof self.Validator.messages.remote && message !== self.Validator.messages.remote ) {
|
|
$( self.ulError + ' .remote' ).remove();
|
|
}
|
|
|
|
self.updtConstraint( { name: 'remote', valid: isConstraintValid }, message );
|
|
self.manageValidationResult();
|
|
};
|
|
|
|
// transform string response into object
|
|
var handleResponse = function ( response ) {
|
|
if ( 'object' === typeof response ) {
|
|
return response;
|
|
}
|
|
|
|
try {
|
|
response = $.parseJSON( response );
|
|
} catch ( err ) {}
|
|
|
|
return response;
|
|
}
|
|
|
|
/* @livestreet begin */
|
|
var manageErrorMessage = function ( response ) {
|
|
return 'object' === typeof response && null !== response ? ( 'undefined' !== typeof response.aErrors ? response.aErrors[data.fields[0].field].join("<br>") : ( 'undefined' !== typeof response.message ? response.message : null ) ) : null;
|
|
}
|
|
/* @livestreet end */
|
|
|
|
$.ajax( $.extend( {}, {
|
|
url: url
|
|
, data: data
|
|
, type: self.options.remoteMethod || 'GET'
|
|
, success: function ( response ) {
|
|
response = handleResponse( response );
|
|
/* @livestreet begin */
|
|
manage( 1 === response || true === response || ( 'object' === typeof response && null !== response && 'undefined' !== typeof response.success ) || ( 'object' === typeof response && null !== response && 'undefined' === typeof response.aErrors ), manageErrorMessage( response )
|
|
/* @livestreet end */
|
|
);
|
|
}
|
|
, error: function ( response ) {
|
|
response = handleResponse( response );
|
|
manage( false, manageErrorMessage( response ) );
|
|
}
|
|
}, dataType ) );
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Aliases for checkboxes constraints
|
|
*/
|
|
, mincheck: function ( obj, val ) {
|
|
return this.minlength( obj, val );
|
|
}
|
|
|
|
, maxcheck: function ( obj, val ) {
|
|
return this.maxlength( obj, val);
|
|
}
|
|
|
|
, rangecheck: function ( obj, arrayRange ) {
|
|
return this.rangelength( obj, arrayRange );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Register custom validators and messages
|
|
*/
|
|
, init: function ( options ) {
|
|
var customValidators = options.validators
|
|
, customMessages = options.messages;
|
|
|
|
var key;
|
|
for ( key in customValidators ) {
|
|
this.addValidator(key, customValidators[ key ]);
|
|
}
|
|
|
|
for ( key in customMessages ) {
|
|
this.addMessage(key, customMessages[ key ]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Replace %s placeholders by values
|
|
*
|
|
* @method formatMesssage
|
|
* @param {String} message Message key
|
|
* @param {Mixed} args Args passed by validators functions. Could be string, number or object
|
|
* @return {String} Formatted string
|
|
*/
|
|
, formatMesssage: function ( message, args ) {
|
|
|
|
if ( 'object' === typeof args ) {
|
|
for ( var i in args ) {
|
|
message = this.formatMesssage( message, args[ i ] );
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
return 'string' === typeof message ? message.replace( new RegExp( '%s', 'i' ), args ) : '';
|
|
}
|
|
|
|
/**
|
|
* Add / override a validator in validators list
|
|
*
|
|
* @method addValidator
|
|
* @param {String} name Validator name. Will automatically bindable through data-name=''
|
|
* @param {Function} fn Validator function. Must return {Boolean}
|
|
*/
|
|
, addValidator: function ( name, fn ) {
|
|
this.validators[ name ] = fn;
|
|
}
|
|
|
|
/**
|
|
* Add / override error message
|
|
*
|
|
* @method addMessage
|
|
* @param {String} name Message name. Will automatically be binded to validator with same name
|
|
* @param {String} message Message
|
|
*/
|
|
, addMessage: function ( key, message, type ) {
|
|
|
|
if ( 'undefined' !== typeof type && true === type ) {
|
|
this.messages.type[ key ] = message;
|
|
return;
|
|
}
|
|
|
|
// custom types messages are a bit tricky cuz' nested ;)
|
|
if ( 'type' === key ) {
|
|
for ( var i in message ) {
|
|
this.messages.type[ i ] = message[ i ];
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
this.messages[ key ] = message;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* ParsleyField class manage each form field inside a validated Parsley form.
|
|
* Returns if field valid or not depending on its value and constraints
|
|
* Manage field error display and behavior, event triggers and more
|
|
*
|
|
* @class ParsleyField
|
|
* @constructor
|
|
*/
|
|
var ParsleyField = function ( element, options, type ) {
|
|
this.options = options;
|
|
this.Validator = new Validator( options );
|
|
|
|
// if type is ParsleyFieldMultiple, just return this. used for clone
|
|
if ( type === 'ParsleyFieldMultiple' ) {
|
|
return this;
|
|
}
|
|
|
|
this.init( element, type || 'ParsleyField' );
|
|
};
|
|
|
|
ParsleyField.prototype = {
|
|
|
|
constructor: ParsleyField
|
|
|
|
/**
|
|
* Set some properties, bind constraint validators and validation events
|
|
*
|
|
* @method init
|
|
* @param {Object} element
|
|
* @param {Object} options
|
|
*/
|
|
, init: function ( element, type ) {
|
|
this.type = type;
|
|
this.valid = true;
|
|
this.element = element;
|
|
this.validatedOnce = false;
|
|
this.$element = $( element );
|
|
this.val = this.$element.val();
|
|
this.isRequired = false;
|
|
this.constraints = {};
|
|
|
|
// overriden by ParsleyItemMultiple if radio or checkbox input
|
|
if ( 'undefined' === typeof this.isRadioOrCheckbox ) {
|
|
this.isRadioOrCheckbox = false;
|
|
this.hash = this.generateHash();
|
|
this.errorClassHandler = this.options.errors.classHandler( element, this.isRadioOrCheckbox ) || this.$element;
|
|
}
|
|
|
|
// error ul dom management done only once at init
|
|
this.ulErrorManagement();
|
|
|
|
// bind some html5 properties
|
|
this.bindHtml5Constraints();
|
|
|
|
// bind validators to field
|
|
this.addConstraints();
|
|
|
|
// bind parsley events if validators have been registered
|
|
if ( this.hasConstraints() ) {
|
|
this.bindValidationEvents();
|
|
}
|
|
}
|
|
|
|
, setParent: function ( elem ) {
|
|
this.$parent = $( elem );
|
|
}
|
|
|
|
, getParent: function () {
|
|
return this.$parent;
|
|
}
|
|
|
|
/**
|
|
* Bind some extra html5 types / validators
|
|
*
|
|
* @private
|
|
* @method bindHtml5Constraints
|
|
*/
|
|
, bindHtml5Constraints: function () {
|
|
// add html5 required support + class required support
|
|
if ( this.$element.hasClass( 'required' ) || this.$element.prop( 'required' ) ) {
|
|
this.options.required = true;
|
|
}
|
|
|
|
// add html5 supported types & options
|
|
if ( 'undefined' !== typeof this.$element.attr( 'type' ) && new RegExp( this.$element.attr( 'type' ), 'i' ).test( 'email url number range' ) ) {
|
|
this.options.type = this.$element.attr( 'type' );
|
|
|
|
// number and range types could have min and/or max values
|
|
if ( new RegExp( this.options.type, 'i' ).test( 'number range' ) ) {
|
|
this.options.type = 'number';
|
|
|
|
// double condition to support jQuery and Zepto.. :(
|
|
if ( 'undefined' !== typeof this.$element.attr( 'min' ) && this.$element.attr( 'min' ).length ) {
|
|
this.options.min = this.$element.attr( 'min' );
|
|
}
|
|
|
|
if ( 'undefined' !== typeof this.$element.attr( 'max' ) && this.$element.attr( 'max' ).length ) {
|
|
this.options.max = this.$element.attr( 'max' );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( 'string' === typeof this.$element.attr( 'pattern' ) && this.$element.attr( 'pattern' ).length ) {
|
|
this.options.regexp = this.$element.attr( 'pattern' );
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Attach field validators functions passed through data-api
|
|
*
|
|
* @private
|
|
* @method addConstraints
|
|
*/
|
|
, addConstraints: function () {
|
|
for ( var constraint in this.options ) {
|
|
var addConstraint = {};
|
|
addConstraint[ constraint ] = this.options[ constraint ];
|
|
this.addConstraint( addConstraint, true );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dynamically add a new constraint to a field
|
|
*
|
|
* @method addConstraint
|
|
* @param {Object} constraint { name: requirements }
|
|
*/
|
|
, addConstraint: function ( constraint, doNotUpdateValidationEvents ) {
|
|
for ( var name in constraint ) {
|
|
name = name.toLowerCase();
|
|
|
|
if ( 'function' === typeof this.Validator.validators[ name ] ) {
|
|
this.constraints[ name ] = {
|
|
name: name
|
|
, requirements: constraint[ name ]
|
|
, valid: null
|
|
}
|
|
|
|
if ( name === 'required' ) {
|
|
this.isRequired = true;
|
|
}
|
|
|
|
this.addCustomConstraintMessage( name );
|
|
}
|
|
}
|
|
|
|
// force field validation next check and reset validation events
|
|
if ( 'undefined' === typeof doNotUpdateValidationEvents ) {
|
|
this.bindValidationEvents();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dynamically update an existing constraint to a field.
|
|
* Simple API: { name: requirements }
|
|
*
|
|
* @method updtConstraint
|
|
* @param {Object} constraint
|
|
*/
|
|
, updateConstraint: function ( constraint, message ) {
|
|
for ( var name in constraint ) {
|
|
this.updtConstraint( { name: name, requirements: constraint[ name ], valid: null }, message );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dynamically update an existing constraint to a field.
|
|
* Complex API: { name: name, requirements: requirements, valid: boolean }
|
|
*
|
|
* @method updtConstraint
|
|
* @param {Object} constraint
|
|
*/
|
|
, updtConstraint: function ( constraint, message ) {
|
|
this.constraints[ constraint.name ] = $.extend( true, this.constraints[ constraint.name ], constraint );
|
|
|
|
if ( 'string' === typeof message ) {
|
|
this.Validator.messages[ constraint.name ] = message ;
|
|
}
|
|
|
|
// force field validation next check and reset validation events
|
|
this.bindValidationEvents();
|
|
}
|
|
|
|
/**
|
|
* Dynamically remove an existing constraint to a field.
|
|
*
|
|
* @method removeConstraint
|
|
* @param {String} constraintName
|
|
*/
|
|
, removeConstraint: function ( constraintName ) {
|
|
var constraintName = constraintName.toLowerCase();
|
|
|
|
delete this.constraints[ constraintName ];
|
|
|
|
if ( constraintName === 'required' ) {
|
|
this.isRequired = false;
|
|
}
|
|
|
|
// if there are no more constraint, destroy parsley instance for this field
|
|
if ( !this.hasConstraints() ) {
|
|
// in a form context, remove item from parent
|
|
if ( 'ParsleyForm' === typeof this.getParent() ) {
|
|
this.getParent().removeItem( this.$element );
|
|
return;
|
|
}
|
|
|
|
this.destroy();
|
|
return;
|
|
}
|
|
|
|
this.bindValidationEvents();
|
|
}
|
|
|
|
/**
|
|
* Add custom constraint message, passed through data-API
|
|
*
|
|
* @private
|
|
* @method addCustomConstraintMessage
|
|
* @param constraint
|
|
*/
|
|
, addCustomConstraintMessage: function ( constraint ) {
|
|
// custom message type data-type-email-message -> typeEmailMessage | data-minlength-error => minlengthMessage
|
|
var customMessage = constraint
|
|
+ ( 'type' === constraint && 'undefined' !== typeof this.options[ constraint ] ? this.options[ constraint ].charAt( 0 ).toUpperCase() + this.options[ constraint ].substr( 1 ) : '' )
|
|
+ 'Message';
|
|
|
|
if ( 'undefined' !== typeof this.options[ customMessage ] ) {
|
|
this.Validator.addMessage( 'type' === constraint ? this.options[ constraint ] : constraint, this.options[ customMessage ], 'type' === constraint );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Bind validation events on a field
|
|
*
|
|
* @private
|
|
* @method bindValidationEvents
|
|
*/
|
|
, bindValidationEvents: function () {
|
|
// this field has validation events, that means it has to be validated
|
|
this.valid = null;
|
|
this.$element.addClass( 'parsley-validated' );
|
|
|
|
// remove eventually already binded events
|
|
this.$element.off( '.' + this.type );
|
|
|
|
// force add 'change' event if async remote validator here to have result before form submitting
|
|
if ( this.options.remote && !new RegExp( 'change', 'i' ).test( this.options.trigger ) ) {
|
|
this.options.trigger = !this.options.trigger ? 'change' : ' change';
|
|
}
|
|
|
|
// alaways bind keyup event, for better UX when a field is invalid
|
|
var triggers = ( !this.options.trigger ? '' : this.options.trigger )
|
|
+ ( new RegExp( 'key', 'i' ).test( this.options.trigger ) ? '' : ' keyup' );
|
|
|
|
// alaways bind change event, for better UX when a select is invalid
|
|
if ( this.$element.is( 'select' ) ) {
|
|
triggers += new RegExp( 'change', 'i' ).test( triggers ) ? '' : ' change';
|
|
}
|
|
|
|
// trim triggers to bind them correctly with .on()
|
|
triggers = triggers.replace( /^\s+/g , '' ).replace( /\s+$/g , '' );
|
|
|
|
this.$element.on( ( triggers + ' ' ).split( ' ' ).join( '.' + this.type + ' ' ), false, $.proxy( this.eventValidation, this ) );
|
|
}
|
|
|
|
/**
|
|
* Hash management. Used for ul error
|
|
*
|
|
* @method generateHash
|
|
* @returns {String} 5 letters unique hash
|
|
*/
|
|
, generateHash: function () {
|
|
return 'parsley-' + ( Math.random() + '' ).substring( 2 );
|
|
}
|
|
|
|
/**
|
|
* Public getHash accessor
|
|
*
|
|
* @method getHash
|
|
* @returns {String} hash
|
|
*/
|
|
, getHash: function () {
|
|
return this.hash;
|
|
}
|
|
|
|
/**
|
|
* Returns field val needed for validation
|
|
* Special treatment for radio & checkboxes
|
|
*
|
|
* @method getVal
|
|
* @returns {String} val
|
|
*/
|
|
, getVal: function () {
|
|
return this.$element.data('value') || this.$element.val();
|
|
}
|
|
|
|
/**
|
|
* Called when validation is triggered by an event
|
|
* Do nothing if val.length < this.options.validationMinlength
|
|
*
|
|
* @method eventValidation
|
|
* @param {Object} event jQuery event
|
|
*/
|
|
, eventValidation: function ( event ) {
|
|
var val = this.getVal();
|
|
|
|
// do nothing on keypress event if not explicitely passed as data-trigger and if field has not already been validated once
|
|
if ( event.type === 'keyup' && !/keyup/i.test( this.options.trigger ) && !this.validatedOnce ) {
|
|
return true;
|
|
}
|
|
|
|
// do nothing on change event if not explicitely passed as data-trigger and if field has not already been validated once
|
|
if ( event.type === 'change' && !/change/i.test( this.options.trigger ) && !this.validatedOnce ) {
|
|
return true;
|
|
}
|
|
|
|
// start validation process only if field has enough chars and validation never started
|
|
if ( !this.isRadioOrCheckbox && val.length < this.options.validationMinlength && !this.validatedOnce ) {
|
|
return true;
|
|
}
|
|
|
|
this.validate();
|
|
}
|
|
|
|
/**
|
|
* Return if field verify its constraints
|
|
*
|
|
* @method isValid
|
|
* @return {Boolean} Is field valid or not
|
|
*/
|
|
, isValid: function () {
|
|
return this.validate( false );
|
|
}
|
|
|
|
/**
|
|
* Return if field has constraints
|
|
*
|
|
* @method hasConstraints
|
|
* @return {Boolean} Is field has constraints or not
|
|
*/
|
|
, hasConstraints: function () {
|
|
for ( var constraint in this.constraints ) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Validate a field & display errors
|
|
*
|
|
* @method validate
|
|
* @param {Boolean} errorBubbling set to false if you just want valid boolean without error bubbling next to fields
|
|
* @return {Boolean} Is field valid or not
|
|
*/
|
|
, validate: function ( errorBubbling ) {
|
|
var val = this.getVal()
|
|
, valid = null;
|
|
|
|
// do not even bother trying validating a field w/o constraints
|
|
if ( !this.hasConstraints() ) {
|
|
return null;
|
|
}
|
|
|
|
// reset Parsley validation if onFieldValidate returns true, or if field is empty and not required
|
|
if ( this.options.listeners.onFieldValidate( this.element, this ) || ( '' === val && !this.isRequired ) ) {
|
|
this.reset();
|
|
return null;
|
|
}
|
|
|
|
// do not validate a field already validated and unchanged !
|
|
if ( !this.needsValidation( val ) ) {
|
|
return this.valid;
|
|
}
|
|
|
|
valid = this.applyValidators();
|
|
|
|
if ( 'undefined' !== typeof errorBubbling ? errorBubbling : this.options.showErrors ) {
|
|
this.manageValidationResult();
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
/**
|
|
* Check if value has changed since previous validation
|
|
*
|
|
* @method needsValidation
|
|
* @param value
|
|
* @return {Boolean}
|
|
*/
|
|
, needsValidation: function ( val ) {
|
|
if ( !this.options.validateIfUnchanged && this.valid !== null && this.val === val && this.validatedOnce ) {
|
|
return false;
|
|
}
|
|
|
|
this.val = val;
|
|
return this.validatedOnce = true;
|
|
}
|
|
|
|
/**
|
|
* Loop through every fields validators
|
|
* Adds errors after unvalid fields
|
|
*
|
|
* @method applyValidators
|
|
* @return {Mixed} {Boolean} If field valid or not, null if not validated
|
|
*/
|
|
, applyValidators: function () {
|
|
var valid = null;
|
|
|
|
for ( var constraint in this.constraints ) {
|
|
var result = this.Validator.validators[ this.constraints[ constraint ].name ]( this.val, this.constraints[ constraint ].requirements, this );
|
|
|
|
if ( false === result ) {
|
|
valid = false;
|
|
this.constraints[ constraint ].valid = valid;
|
|
this.options.listeners.onFieldError( this.element, this.constraints, this );
|
|
} else if ( true === result ) {
|
|
this.constraints[ constraint ].valid = true;
|
|
valid = false !== valid;
|
|
this.options.listeners.onFieldSuccess( this.element, this.constraints, this );
|
|
}
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
/**
|
|
* Fired when all validators have be executed
|
|
* Returns true or false if field is valid or not
|
|
* Display errors messages below failed fields
|
|
* Adds parsley-success or parsley-error class on fields
|
|
*
|
|
* @method manageValidationResult
|
|
* @return {Boolean} Is field valid or not
|
|
*/
|
|
, manageValidationResult: function () {
|
|
var valid = null;
|
|
|
|
for ( var constraint in this.constraints ) {
|
|
if ( false === this.constraints[ constraint ].valid ) {
|
|
this.manageError( this.constraints[ constraint ] );
|
|
valid = false;
|
|
} else if ( true === this.constraints[ constraint ].valid ) {
|
|
this.removeError( this.constraints[ constraint ].name );
|
|
valid = false !== valid;
|
|
}
|
|
}
|
|
|
|
this.valid = valid;
|
|
|
|
if ( true === this.valid ) {
|
|
this.removeErrors();
|
|
this.errorClassHandler.removeClass( this.options.errorClass ).addClass( this.options.successClass );
|
|
return true;
|
|
} else if ( false === this.valid ) {
|
|
this.errorClassHandler.removeClass( this.options.successClass ).addClass( this.options.errorClass );
|
|
return false;
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
/**
|
|
* Manage ul error Container
|
|
*
|
|
* @private
|
|
* @method ulErrorManagement
|
|
*/
|
|
, ulErrorManagement: function () {
|
|
this.ulError = '#' + this.hash;
|
|
this.ulTemplate = $( this.options.errors.errorsWrapper ).attr( 'id', this.hash ).addClass( 'parsley-error-list' );
|
|
}
|
|
|
|
/**
|
|
* Remove li / ul error
|
|
*
|
|
* @method removeError
|
|
* @param {String} constraintName Method Name
|
|
*/
|
|
, removeError: function ( constraintName ) {
|
|
var liError = this.ulError + ' .' + constraintName
|
|
, that = this;
|
|
|
|
this.options.animate ? $( liError ).fadeOut( this.options.animateDuration, function () {
|
|
$( this ).remove();
|
|
|
|
if ( that.ulError && $( that.ulError ).children().length === 0 ) {
|
|
that.removeErrors();
|
|
} } ) : $( liError ).remove();
|
|
|
|
// remove li error, and ul error if no more li inside
|
|
if ( this.ulError && $( this.ulError ).children().length === 0 ) {
|
|
this.removeErrors();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add li error
|
|
*
|
|
* @method addError
|
|
* @param {Object} { minlength: "error message for minlength constraint" }
|
|
*/
|
|
, addError: function ( error ) {
|
|
for ( var constraint in error ) {
|
|
var liTemplate = $( this.options.errors.errorElem ).addClass( constraint );
|
|
|
|
$( this.ulError ).append( this.options.animate ? $( liTemplate ).html( error[ constraint ] ).hide().fadeIn( this.options.animateDuration ) : $( liTemplate ).html( error[ constraint ] ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove all ul / li errors
|
|
*
|
|
* @method removeErrors
|
|
*/
|
|
, removeErrors: function () {
|
|
this.options.animate ? $( this.ulError ).fadeOut( this.options.animateDuration, function () { $( this ).remove(); } ) : $( this.ulError ).remove();
|
|
}
|
|
|
|
/**
|
|
* Remove ul errors and parsley error or success classes
|
|
*
|
|
* @method reset
|
|
*/
|
|
, reset: function () {
|
|
this.valid = null;
|
|
this.removeErrors();
|
|
this.validatedOnce = false;
|
|
this.errorClassHandler.removeClass( this.options.successClass ).removeClass( this.options.errorClass );
|
|
|
|
for ( var constraint in this.constraints ) {
|
|
this.constraints[ constraint ].valid = null;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Add li / ul errors messages
|
|
*
|
|
* @method manageError
|
|
* @param {Object} constraint
|
|
*/
|
|
, manageError: function ( constraint ) {
|
|
// display ulError container if it has been removed previously (or never shown)
|
|
if ( !$( this.ulError ).length ) {
|
|
this.manageErrorContainer();
|
|
}
|
|
|
|
// TODO: refacto properly
|
|
// if required constraint but field is not null, do not display
|
|
if ( 'required' === constraint.name && null !== this.getVal() && this.getVal().length > 0 ) {
|
|
return;
|
|
// if empty required field and non required constraint fails, do not display
|
|
} else if ( this.isRequired && 'required' !== constraint.name && ( null === this.getVal() || 0 === this.getVal().length ) ) {
|
|
return;
|
|
}
|
|
|
|
// TODO: refacto error name w/ proper & readable function
|
|
var constraintName = constraint.name
|
|
, liClass = false !== this.options.errorMessage ? 'custom-error-message' : constraintName
|
|
, liError = {}
|
|
, message = false !== this.options.errorMessage ? this.options.errorMessage : ( constraint.name === 'type' ?
|
|
this.Validator.messages[ constraintName ][ constraint.requirements ] : ( 'undefined' === typeof this.Validator.messages[ constraintName ] ?
|
|
this.Validator.messages.defaultMessage : this.Validator.formatMesssage( this.Validator.messages[ constraintName ], constraint.requirements ) ) );
|
|
|
|
// add liError if not shown. Do not add more than once custom errorMessage if exist
|
|
if ( !$( this.ulError + ' .' + liClass ).length ) {
|
|
liError[ liClass ] = message;
|
|
this.addError( liError );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create ul error container
|
|
*
|
|
* @method manageErrorContainer
|
|
*/
|
|
, manageErrorContainer: function () {
|
|
var errorContainer = this.options.errorContainer || this.options.errors.container( this.element, this.isRadioOrCheckbox )
|
|
, ulTemplate = this.options.animate ? this.ulTemplate.show() : this.ulTemplate;
|
|
|
|
if ( 'undefined' !== typeof errorContainer ) {
|
|
$( errorContainer ).append( ulTemplate );
|
|
return;
|
|
}
|
|
|
|
!this.isRadioOrCheckbox ? this.$element.after( ulTemplate ) : this.$element.parent().after( ulTemplate );
|
|
}
|
|
|
|
/**
|
|
* Add custom listeners
|
|
*
|
|
* @param {Object} { listener: function () {} }, eg { onFormSubmit: function ( valid, event, focus ) { ... } }
|
|
*/
|
|
, addListener: function ( object ) {
|
|
for ( var listener in object ) {
|
|
this.options.listeners[ listener ] = object[ listener ];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Destroy parsley field instance
|
|
*
|
|
* @private
|
|
* @method destroy
|
|
*/
|
|
, destroy: function () {
|
|
this.$element.removeClass( 'parsley-validated' );
|
|
this.reset().$element.off( '.' + this.type ).removeData( this.type );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* ParsleyFieldMultiple override ParsleyField for checkbox and radio inputs
|
|
* Pseudo-heritance to manage divergent behavior from ParsleyItem in dedicated methods
|
|
*
|
|
* @class ParsleyFieldMultiple
|
|
* @constructor
|
|
*/
|
|
var ParsleyFieldMultiple = function ( element, options, type ) {
|
|
this.initMultiple( element, options );
|
|
this.inherit( element, options );
|
|
this.Validator = new Validator( options );
|
|
|
|
// call ParsleyField constructor
|
|
this.init( element, type || 'ParsleyFieldMultiple' );
|
|
};
|
|
|
|
ParsleyFieldMultiple.prototype = {
|
|
|
|
constructor: ParsleyFieldMultiple
|
|
|
|
/**
|
|
* Set some specific properties, call some extra methods to manage radio / checkbox
|
|
*
|
|
* @method init
|
|
* @param {Object} element
|
|
* @param {Object} options
|
|
*/
|
|
, initMultiple: function ( element, options ) {
|
|
this.element = element;
|
|
this.$element = $( element );
|
|
this.group = options.group || false;
|
|
this.hash = this.getName();
|
|
this.siblings = this.group ? '[data-group="' + this.group + '"]' : 'input[name="' + this.$element.attr( 'name' ) + '"]';
|
|
this.isRadioOrCheckbox = true;
|
|
this.isRadio = this.$element.is( 'input[type=radio]' );
|
|
this.isCheckbox = this.$element.is( 'input[type=checkbox]' );
|
|
this.errorClassHandler = options.errors.classHandler( element, this.isRadioOrCheckbox ) || this.$element.parent();
|
|
}
|
|
|
|
/**
|
|
* Set specific constraints messages, do pseudo-heritance
|
|
*
|
|
* @private
|
|
* @method inherit
|
|
* @param {Object} element
|
|
* @param {Object} options
|
|
*/
|
|
, inherit: function ( element, options ) {
|
|
var clone = new ParsleyField( element, options, 'ParsleyFieldMultiple' );
|
|
|
|
for ( var property in clone ) {
|
|
if ( 'undefined' === typeof this[ property ] ) {
|
|
this[ property ] = clone [ property ];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set specific constraints messages, do pseudo-heritance
|
|
*
|
|
* @method getName
|
|
* @returns {String} radio / checkbox hash is cleaned 'name' or data-group property
|
|
*/
|
|
, getName: function () {
|
|
if ( this.group ) {
|
|
return 'parsley-' + this.group;
|
|
}
|
|
|
|
if ( 'undefined' === typeof this.$element.attr( 'name' ) ) {
|
|
throw "A radio / checkbox input must have a data-group attribute or a name to be Parsley validated !";
|
|
}
|
|
|
|
return 'parsley-' + this.$element.attr( 'name' ).replace( /(:|\.|\[|\])/g, '' );
|
|
}
|
|
|
|
/**
|
|
* Special treatment for radio & checkboxes
|
|
* Returns checked radio or checkboxes values
|
|
*
|
|
* @method getVal
|
|
* @returns {String} val
|
|
*/
|
|
, getVal: function () {
|
|
if ( this.isRadio ) {
|
|
return $( this.siblings + ':checked' ).val() || '';
|
|
}
|
|
|
|
if ( this.isCheckbox ) {
|
|
var values = [];
|
|
|
|
$( this.siblings + ':checked' ).each( function () {
|
|
values.push( $( this ).val() );
|
|
} );
|
|
|
|
return values;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Bind validation events on a field
|
|
*
|
|
* @private
|
|
* @method bindValidationEvents
|
|
*/
|
|
, bindValidationEvents: function () {
|
|
// this field has validation events, that means it has to be validated
|
|
this.valid = null;
|
|
this.$element.addClass( 'parsley-validated' );
|
|
|
|
// remove eventually already binded events
|
|
this.$element.off( '.' + this.type );
|
|
|
|
// alaways bind keyup event, for better UX when a field is invalid
|
|
var self = this
|
|
, triggers = ( !this.options.trigger ? '' : this.options.trigger )
|
|
+ ( new RegExp( 'change', 'i' ).test( this.options.trigger ) ? '' : ' change' );
|
|
|
|
// trim triggers to bind them correctly with .on()
|
|
triggers = triggers.replace( /^\s+/g , '' ).replace( /\s+$/g ,'' );
|
|
|
|
// bind trigger event on every siblings
|
|
$( this.siblings ).each(function () {
|
|
$( this ).on( triggers.split( ' ' ).join( '.' + self.type + ' ' ) , false, $.proxy( self.eventValidation, self ) );
|
|
} )
|
|
}
|
|
};
|
|
|
|
/**
|
|
* ParsleyForm class manage Parsley validated form.
|
|
* Manage its fields and global validation
|
|
*
|
|
* @class ParsleyForm
|
|
* @constructor
|
|
*/
|
|
var ParsleyForm = function ( element, options, type ) {
|
|
this.init( element, options, type || 'parsleyForm' );
|
|
};
|
|
|
|
ParsleyForm.prototype = {
|
|
|
|
constructor: ParsleyForm
|
|
|
|
/* init data, bind jQuery on() actions */
|
|
, init: function ( element, options, type ) {
|
|
this.type = type;
|
|
this.items = [];
|
|
this.$element = $( element );
|
|
this.options = options;
|
|
var self = this;
|
|
|
|
this.$element.find( options.inputs ).each( function () {
|
|
self.addItem( this );
|
|
});
|
|
|
|
this.$element.on( 'submit.' + this.type , false, $.proxy( this.validate, this ) );
|
|
}
|
|
|
|
/**
|
|
* Add custom listeners
|
|
*
|
|
* @param {Object} { listener: function () {} }, eg { onFormSubmit: function ( valid, event, focus ) { ... } }
|
|
*/
|
|
, addListener: function ( object ) {
|
|
for ( var listener in object ) {
|
|
if ( new RegExp( 'Field' ).test( listener ) ) {
|
|
for ( var item = 0; item < this.items.length; item++ ) {
|
|
this.items[ item ].addListener( object );
|
|
}
|
|
} else {
|
|
this.options.listeners[ listener ] = object[ listener ];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a new parsleyItem child to ParsleyForm
|
|
*
|
|
* @method addItem
|
|
* @param elem
|
|
*/
|
|
, addItem: function ( elem ) {
|
|
if ( $( elem ).is( this.options.excluded ) ) {
|
|
return false;
|
|
}
|
|
|
|
var ParsleyField = $( elem ).parsley( this.options );
|
|
ParsleyField.setParent( this );
|
|
|
|
this.items.push( ParsleyField );
|
|
}
|
|
|
|
/**
|
|
* Removes a parsleyItem child from ParsleyForm
|
|
*
|
|
* @method removeItem
|
|
* @param elem
|
|
* @return {Boolean}
|
|
*/
|
|
, removeItem: function ( elem ) {
|
|
var parsleyItem = $( elem ).parsley();
|
|
|
|
// identify & remove item if same Parsley hash
|
|
for ( var i = 0; i < this.items.length; i++ ) {
|
|
if ( this.items[ i ].hash === parsleyItem.hash ) {
|
|
this.items[ i ].destroy();
|
|
this.items.splice( i, 1 );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Process each form field validation
|
|
* Display errors, call custom onFormSubmit() function
|
|
*
|
|
* @method validate
|
|
* @param {Object} event jQuery Event
|
|
* @return {Boolean} Is form valid or not
|
|
*/
|
|
, validate: function ( event ) {
|
|
var valid = true;
|
|
this.focusedField = false;
|
|
|
|
for ( var item = 0; item < this.items.length; item++ ) {
|
|
if ( 'undefined' !== typeof this.items[ item ] && false === this.items[ item ].validate() ) {
|
|
valid = false;
|
|
|
|
if ( !this.focusedField && 'first' === this.options.focus || 'last' === this.options.focus ) {
|
|
this.focusedField = this.items[ item ].$element;
|
|
}
|
|
}
|
|
}
|
|
|
|
// form is invalid, focus an error field depending on focus policy
|
|
if ( this.focusedField && !valid ) {
|
|
this.focusedField.focus();
|
|
}
|
|
|
|
this.options.listeners.onFormSubmit( valid, event, this );
|
|
|
|
return valid;
|
|
}
|
|
|
|
, isValid: function () {
|
|
for ( var item = 0; item < this.items.length; item++ ) {
|
|
if ( false === this.items[ item ].isValid() ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Remove all errors ul under invalid fields
|
|
*
|
|
* @method removeErrors
|
|
*/
|
|
, removeErrors: function () {
|
|
for ( var item = 0; item < this.items.length; item++ ) {
|
|
this.items[ item ].parsley( 'reset' );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* destroy Parsley binded on the form and its fields
|
|
*
|
|
* @method destroy
|
|
*/
|
|
, destroy: function () {
|
|
for ( var item = 0; item < this.items.length; item++ ) {
|
|
this.items[ item ].destroy();
|
|
}
|
|
|
|
this.$element.off( '.' + this.type ).removeData( this.type );
|
|
}
|
|
|
|
/**
|
|
* reset Parsley binded on the form and its fields
|
|
*
|
|
* @method reset
|
|
*/
|
|
, reset: function () {
|
|
for ( var item = 0; item < this.items.length; item++ ) {
|
|
this.items[ item ].reset();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Parsley plugin definition
|
|
* Provides an interface to access public Validator, ParsleyForm and ParsleyField functions
|
|
*
|
|
* @class Parsley
|
|
* @constructor
|
|
* @param {Mixed} Options. {Object} to configure Parsley or {String} method name to call a public class method
|
|
* @param {Function} Callback function
|
|
* @return {Mixed} public class method return
|
|
*/
|
|
$.fn.parsley = function ( option, fn ) {
|
|
var options = $.extend( true, {}, $.fn.parsley.defaults, 'undefined' !== typeof window.ParsleyConfig ? window.ParsleyConfig : {}, option, this.data() )
|
|
, newInstance = null;
|
|
|
|
function bind ( self, type ) {
|
|
var parsleyInstance = $( self ).data( type );
|
|
|
|
// if data never binded or we want to clone a build (for radio & checkboxes), bind it right now!
|
|
if ( !parsleyInstance ) {
|
|
switch ( type ) {
|
|
case 'parsleyForm':
|
|
parsleyInstance = new ParsleyForm( self, options, 'parsleyForm' );
|
|
break;
|
|
case 'parsleyField':
|
|
parsleyInstance = new ParsleyField( self, options, 'parsleyField' );
|
|
break;
|
|
case 'parsleyFieldMultiple':
|
|
parsleyInstance = new ParsleyFieldMultiple( self, options, 'parsleyFieldMultiple' );
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
$( self ).data( type, parsleyInstance );
|
|
}
|
|
|
|
// here is our parsley public function accessor
|
|
if ( 'string' === typeof option && 'function' === typeof parsleyInstance[ option ] ) {
|
|
var response = parsleyInstance[ option ]( fn );
|
|
|
|
return 'undefined' !== typeof response ? response : $( self );
|
|
}
|
|
|
|
return parsleyInstance;
|
|
}
|
|
|
|
// if a form elem is given, bind all its input children
|
|
if ( $( this ).is( 'form' ) ) {
|
|
newInstance = bind ( $( this ), 'parsleyForm' );
|
|
|
|
// if it is a Parsley supported single element, bind it too, except inputs type hidden
|
|
// add here a return instance, cuz' we could call public methods on single elems with data[ option ]() above
|
|
} else if ( $( this ).is( options.inputs ) && !$( this ).is( options.excluded ) ) {
|
|
newInstance = bind( $( this ), !$( this ).is( 'input[type=radio], input[type=checkbox]' ) ? 'parsleyField' : 'parsleyFieldMultiple' );
|
|
}
|
|
|
|
return 'function' === typeof fn ? fn() : newInstance;
|
|
};
|
|
|
|
$.fn.parsley.Constructor = ParsleyForm;
|
|
|
|
/**
|
|
* Parsley plugin configuration
|
|
*
|
|
* @property $.fn.parsley.defaults
|
|
* @type {Object}
|
|
*/
|
|
$.fn.parsley.defaults = {
|
|
// basic data-api overridable properties here..
|
|
inputs: 'input, textarea, select' // Default supported inputs.
|
|
, excluded: 'input[type=hidden], :disabled' // Do not validate input[type=hidden] & :disabled.
|
|
, trigger: false // $.Event() that will trigger validation. eg: keyup, change..
|
|
, animate: true // fade in / fade out error messages
|
|
, animateDuration: 300 // fadein/fadout ms time
|
|
, focus: 'first' // 'fist'|'last'|'none' which error field would have focus first on form validation
|
|
, validationMinlength: 3 // If trigger validation specified, only if value.length > validationMinlength
|
|
, successClass: 'parsley-success' // Class name on each valid input
|
|
, errorClass: 'parsley-error' // Class name on each invalid input
|
|
, errorMessage: false // Customize an unique error message showed if one constraint fails
|
|
, validators: {} // Add your custom validators functions
|
|
, showErrors: true // Set to false if you don't want Parsley to display error messages
|
|
, messages: {} // Add your own error messages here
|
|
|
|
//some quite advanced configuration here..
|
|
, validateIfUnchanged: false // false: validate once by field value change
|
|
, errors: {
|
|
classHandler: function ( elem, isRadioOrCheckbox ) {} // specify where parsley error-success classes are set
|
|
, container: function ( elem, isRadioOrCheckbox ) {} // specify an elem where errors will be **apened**
|
|
, errorsWrapper: '<ul></ul>' // do not set an id for this elem, it would have an auto-generated id
|
|
, errorElem: '<li></li>' // each field constraint fail in an li
|
|
}
|
|
, listeners: {
|
|
onFieldValidate: function ( elem, ParsleyForm ) { return false; } // Executed on validation. Return true to ignore field validation
|
|
, onFormSubmit: function ( isFormValid, event, ParsleyForm ) {} // Executed once on form validation
|
|
, onFieldError: function ( elem, constraints, ParsleyField ) {} // Executed when a field is detected as invalid
|
|
, onFieldSuccess: function ( elem, constraints, ParsleyField ) {} // Executed when a field passes validation
|
|
}
|
|
};
|
|
|
|
/* PARSLEY auto-bind DATA-API + Global config retrieving
|
|
* =================================================== */
|
|
$( window ).on( 'load', function () {
|
|
$( '[data-validate="parsley"]' ).each( function () {
|
|
$( this ).parsley();
|
|
} );
|
|
} );
|
|
|
|
// This plugin works with jQuery or Zepto (with data extension built for Zepto.)
|
|
}(window.jQuery || window.Zepto);
|