/*! * Parsleyjs * Guillaume Potier - * Version 2.0.0 - built Sat Apr 19 2014 17:31:23 * MIT Licensed * */ !(function($) { var ParsleyUtils = { // Parsley DOM-API // returns object from dom attributes and values // if attr is given, returns bool if attr present in DOM or not attr: function ($element, namespace, checkAttr) { var attribute, obj = {}, regex = new RegExp('^' + namespace, 'i'); if ('undefined' === typeof $element || 'undefined' === typeof $element[0]) return {}; for (var i in $element[0].attributes) { attribute = $element[0].attributes[i]; if ('undefined' !== typeof attribute && null !== attribute && attribute.specified && regex.test(attribute.name)) { if ('undefined' !== typeof checkAttr && new RegExp(checkAttr + '$', 'i').test(attribute.name)) return true; obj[this.camelize(attribute.name.replace(namespace, ''))] = this.deserializeValue(attribute.value); } } return 'undefined' === typeof checkAttr ? obj : false; }, setAttr: function ($element, namespace, attr, value) { $element[0].setAttribute(this.dasherize(namespace + attr), String(value)); }, // Recursive object / array getter get: function (obj, path) { var i = 0, paths = (path || '').split('.'); while (this.isObject(obj) || this.isArray(obj)) { obj = obj[paths[i++]]; if (i === paths.length) return obj; } return undefined; }, hash: function (length) { return String(Math.random()).substring(2, length ? length + 2 : 9); }, /** Third party functions **/ // Underscore isArray isArray: function (mixed) { return Object.prototype.toString.call(mixed) === '[object Array]'; }, // Underscore isObject isObject: function (mixed) { return mixed === Object(mixed); }, // Zepto deserialize function deserializeValue: function (value) { var num; try { return value ? value == "true" || (value == "false" ? false : value == "null" ? null : !isNaN(num = Number(value)) ? num : /^[\[\{]/.test(value) ? $.parseJSON(value) : value) : value; } catch (e) { return value; } }, // Zepto camelize function camelize: function (str) { return str.replace(/-+(.)?/g, function(match, chr) { return chr ? chr.toUpperCase() : ''; }); }, // Zepto dasherize function dasherize: function (str) { return str.replace(/::/g, '/') .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') .replace(/([a-z\d])([A-Z])/g, '$1_$2') .replace(/_/g, '-') .toLowerCase(); } }; // All these options could be overriden and specified directly in DOM using // `data-parsley-` default DOM-API // eg: `inputs` can be set in DOM using `data-parsley-inputs="input, textarea"` // eg: `data-parsley-stop-on-first-failing-constraint="false"` var ParsleyDefaults = { // ### General // Default data-namespace for DOM API namespace: 'data-parsley-', // Supported inputs by default inputs: 'input, textarea, select', // Excluded inputs by default excluded: 'input[type=button], input[type=submit], input[type=reset], input[type=hidden]', // Stop validating field on highest priority failing constraint priorityEnabled: true, // ### UI // Enable\Disable error messages uiEnabled: true, // Key events threshold before validation validationThreshold: 3, // Focused field on form validation error. 'fist'|'last'|'none' focus: 'first', // `$.Event()` that will trigger validation. eg: `keyup`, `change`.. trigger: false, // Class that would be added on every failing validation Parsley field errorClass: 'parsley-error', // Same for success validation successClass: 'parsley-success', // Return the `$element` that will receive these above success or error classes // Could also be (and given directly from DOM) a valid selector like `'#div'` classHandler: function (ParsleyField) {}, // Return the `$element` where errors will be appended // Could also be (and given directly from DOM) a valid selector like `'#div'` errorsContainer: function (ParsleyField) {}, // ul elem that would receive errors' list errorsWrapper: '', // li elem that would receive error message errorTemplate: '
  • ' }; var ParsleyAbstract = function() {}; ParsleyAbstract.prototype = { asyncSupport: false, actualizeOptions: function () { this.options = this.OptionsFactory.get(this); return this; }, // ParsleyValidator validate proxy function . Could be replaced by third party scripts validateThroughValidator: function (value, constraints, priority) { return window.ParsleyValidator.validate.apply(window.ParsleyValidator, [value, constraints, priority]); }, // Subscribe an event and a handler for a specific field or a specific form // If on a ParsleyForm instance, it will be attached to form instance and also // To every field instance for this form subscribe: function (name, fn) { $.listenTo(this, name.toLowerCase(), fn); return this; }, // Same as subscribe above. Unsubscribe an event for field, or form + its fields unsubscribe: function (name) { $.unsubscribeTo(this, name.toLowerCase()); return this; }, // Reset UI reset: function () { // Field case: just emit a reset event for UI if ('ParsleyForm' !== this.__class__) return $.emit('parsley:field:reset', this); // Form case: emit a reset event for each field for (var i = 0; i < this.fields.length; i++) $.emit('parsley:field:reset', this.fields[i]); $.emit('parsley:form:reset', this); }, // Destroy Parsley instance (+ UI) destroy: function () { // Field case: emit destroy event to clean UI and then destroy stored instance if ('ParsleyForm' !== this.__class__) { this.$element.removeData('Parsley'); this.$element.removeData('ParsleyFieldMultiple'); $.emit('parsley:field:destroy', this); return; } // Form case: destroy all its fields and then destroy stored instance for (var i = 0; i < this.fields.length; i++) this.fields[i].destroy(); this.$element.removeData('Parsley'); $.emit('parsley:form:destroy', this); } }; /*! * validator.js * Guillaume Potier - * Version 0.5.8 - built Sun Mar 16 2014 17:18:21 * MIT Licensed * */ ( function ( exports ) { /** * Validator */ var Validator = function ( options ) { this.__class__ = 'Validator'; this.__version__ = '0.5.8'; this.options = options || {}; this.bindingKey = this.options.bindingKey || '_validatorjsConstraint'; return this; }; Validator.prototype = { constructor: Validator, /* * Validate string: validate( string, Assert, string ) || validate( string, [ Assert, Assert ], [ string, string ] ) * Validate object: validate( object, Constraint, string ) || validate( object, Constraint, [ string, string ] ) * Validate binded object: validate( object, string ) || validate( object, [ string, string ] ) */ validate: function ( objectOrString, AssertsOrConstraintOrGroup, group ) { if ( 'string' !== typeof objectOrString && 'object' !== typeof objectOrString ) throw new Error( 'You must validate an object or a string' ); // string / array validation if ( 'string' === typeof objectOrString || _isArray(objectOrString) ) return this._validateString( objectOrString, AssertsOrConstraintOrGroup, group ); // binded object validation if ( this.isBinded( objectOrString ) ) return this._validateBindedObject( objectOrString, AssertsOrConstraintOrGroup ); // regular object validation return this._validateObject( objectOrString, AssertsOrConstraintOrGroup, group ); }, bind: function ( object, constraint ) { if ( 'object' !== typeof object ) throw new Error( 'Must bind a Constraint to an object' ); object[ this.bindingKey ] = new Constraint( constraint ); return this; }, unbind: function ( object ) { if ( 'undefined' === typeof object._validatorjsConstraint ) return this; delete object[ this.bindingKey ]; return this; }, isBinded: function ( object ) { return 'undefined' !== typeof object[ this.bindingKey ]; }, getBinded: function ( object ) { return this.isBinded( object ) ? object[ this.bindingKey ] : null; }, _validateString: function ( string, assert, group ) { var result, failures = []; if ( !_isArray( assert ) ) assert = [ assert ]; for ( var i = 0; i < assert.length; i++ ) { if ( ! ( assert[ i ] instanceof Assert) ) throw new Error( 'You must give an Assert or an Asserts array to validate a string' ); result = assert[ i ].check( string, group ); if ( result instanceof Violation ) failures.push( result ); } return failures.length ? failures : true; }, _validateObject: function ( object, constraint, group ) { if ( 'object' !== typeof constraint ) throw new Error( 'You must give a constraint to validate an object' ); if ( constraint instanceof Constraint ) return constraint.check( object, group ); return new Constraint( constraint ).check( object, group ); }, _validateBindedObject: function ( object, group ) { return object[ this.bindingKey ].check( object, group ); } }; Validator.errorCode = { must_be_a_string: 'must_be_a_string', must_be_an_array: 'must_be_an_array', must_be_a_number: 'must_be_a_number', must_be_a_string_or_array: 'must_be_a_string_or_array' }; /** * Constraint */ var Constraint = function ( data, options ) { this.__class__ = 'Constraint'; this.options = options || {}; this.nodes = {}; if ( data ) { try { this._bootstrap( data ); } catch ( err ) { throw new Error( 'Should give a valid mapping object to Constraint', err, data ); } } return this; }; Constraint.prototype = { constructor: Constraint, check: function ( object, group ) { var result, failures = {}; // check all constraint nodes if strict validation enabled. Else, only object nodes that have a constraint for ( var property in this.options.strict ? this.nodes : object ) { if ( this.options.strict ? this.has( property, object ) : this.has( property ) ) { result = this._check( property, object[ property ], group ); // check returned an array of Violations or an object mapping Violations if ( ( _isArray( result ) && result.length > 0 ) || ( !_isArray( result ) && !_isEmptyObject( result ) ) ) failures[ property ] = result; // in strict mode, get a violation for each constraint node not in object } else if ( this.options.strict ) { try { // we trigger here a HaveProperty Assert violation to have uniform Violation object in the end new Assert().HaveProperty( property ).validate( object ); } catch ( violation ) { failures[ property ] = violation; } } } return _isEmptyObject(failures) ? true : failures; }, add: function ( node, object ) { if ( object instanceof Assert || ( _isArray( object ) && object[ 0 ] instanceof Assert ) ) { this.nodes[ node ] = object; return this; } if ( 'object' === typeof object && !_isArray( object ) ) { this.nodes[ node ] = object instanceof Constraint ? object : new Constraint( object ); return this; } throw new Error( 'Should give an Assert, an Asserts array, a Constraint', object ); }, has: function ( node, nodes ) { nodes = 'undefined' !== typeof nodes ? nodes : this.nodes; return 'undefined' !== typeof nodes[ node ]; }, get: function ( node, placeholder ) { return this.has( node ) ? this.nodes[ node ] : placeholder || null; }, remove: function ( node ) { var _nodes = []; for ( var i in this.nodes ) if ( i !== node ) _nodes[ i ] = this.nodes[ i ]; this.nodes = _nodes; return this; }, _bootstrap: function ( data ) { if ( data instanceof Constraint ) return this.nodes = data.nodes; for ( var node in data ) this.add( node, data[ node ] ); }, _check: function ( node, value, group ) { // Assert if ( this.nodes[ node ] instanceof Assert ) return this._checkAsserts( value, [ this.nodes[ node ] ], group ); // Asserts if ( _isArray( this.nodes[ node ] ) ) return this._checkAsserts( value, this.nodes[ node ], group ); // Constraint -> check api if ( this.nodes[ node ] instanceof Constraint ) return this.nodes[ node ].check( value, group ); throw new Error( 'Invalid node', this.nodes[ node ] ); }, _checkAsserts: function ( value, asserts, group ) { var result, failures = []; for ( var i = 0; i < asserts.length; i++ ) { result = asserts[ i ].check( value, group ); if ( 'undefined' !== typeof result && true !== result ) failures.push( result ); // Some asserts (Collection for example) could return an object // if ( result && ! ( result instanceof Violation ) ) // return result; // // // Vast assert majority return Violation // if ( result instanceof Violation ) // failures.push( result ); } return failures; } }; /** * Violation */ var Violation = function ( assert, value, violation ) { this.__class__ = 'Violation'; if ( ! ( assert instanceof Assert ) ) throw new Error( 'Should give an assertion implementing the Assert interface' ); this.assert = assert; this.value = value; if ( 'undefined' !== typeof violation ) this.violation = violation; }; Violation.prototype = { show: function () { var show = { assert: this.assert.__class__, value: this.value }; if ( this.violation ) show.violation = this.violation; return show; }, __toString: function () { if ( 'undefined' !== typeof this.violation ) this.violation = '", ' + this.getViolation().constraint + ' expected was ' + this.getViolation().expected; return this.assert.__class__ + ' assert failed for "' + this.value + this.violation || ''; }, getViolation: function () { var constraint, expected; for ( constraint in this.violation ) expected = this.violation[ constraint ]; return { constraint: constraint, expected: expected }; } }; /** * Assert */ var Assert = function ( group ) { this.__class__ = 'Assert'; this.__parentClass__ = this.__class__; this.groups = []; if ( 'undefined' !== typeof group ) this.addGroup( group ); return this; }; Assert.prototype = { construct: Assert, check: function ( value, group ) { if ( group && !this.hasGroup( group ) ) return; if ( !group && this.hasGroups() ) return; try { return this.validate( value, group ); } catch ( violation ) { return violation; } }, hasGroup: function ( group ) { if ( _isArray( group ) ) return this.hasOneOf( group ); // All Asserts respond to "Any" group if ( 'Any' === group ) return true; // Asserts with no group also respond to "Default" group. Else return false if ( !this.hasGroups() ) return 'Default' === group; return -1 !== this.groups.indexOf( group ); }, hasOneOf: function ( groups ) { for ( var i = 0; i < groups.length; i++ ) if ( this.hasGroup( groups[ i ] ) ) return true; return false; }, hasGroups: function () { return this.groups.length > 0; }, addGroup: function ( group ) { if ( _isArray( group ) ) return this.addGroups( group ); if ( !this.hasGroup( group ) ) this.groups.push( group ); return this; }, removeGroup: function ( group ) { var _groups = []; for ( var i = 0; i < this.groups.length; i++ ) if ( group !== this.groups[ i ] ) _groups.push( this.groups[ i ] ); this.groups = _groups; return this; }, addGroups: function ( groups ) { for ( var i = 0; i < groups.length; i++ ) this.addGroup( groups[ i ] ); return this; }, /** * Asserts definitions */ HaveProperty: function ( node ) { this.__class__ = 'HaveProperty'; this.node = node; this.validate = function ( object ) { if ( 'undefined' === typeof object[ this.node ] ) throw new Violation( this, object, { value: this.node } ); return true; }; return this; }, Blank: function () { this.__class__ = 'Blank'; this.validate = function ( value ) { if ( 'string' !== typeof value ) throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string } ); if ( '' !== value.replace( /^\s+/g, '' ).replace( /\s+$/g, '' ) ) throw new Violation( this, value ); return true; }; return this; }, Callback: function ( fn ) { this.__class__ = 'Callback'; this.arguments = Array.prototype.slice.call( arguments ); if ( 1 === this.arguments.length ) this.arguments = []; else this.arguments.splice( 0, 1 ); if ( 'function' !== typeof fn ) throw new Error( 'Callback must be instanciated with a function' ); this.fn = fn; this.validate = function ( value ) { var result = this.fn.apply( this, [ value ].concat( this.arguments ) ); if ( true !== result ) throw new Violation( this, value, { result: result } ); return true; }; return this; }, Choice: function ( list ) { this.__class__ = 'Choice'; if ( !_isArray( list ) && 'function' !== typeof list ) throw new Error( 'Choice must be instanciated with an array or a function' ); this.list = list; this.validate = function ( value ) { var list = 'function' === typeof this.list ? this.list() : this.list; for ( var i = 0; i < list.length; i++ ) if ( value === list[ i ] ) return true; throw new Violation( this, value, { choices: list } ); }; return this; }, Collection: function ( constraint ) { this.__class__ = 'Collection'; this.constraint = 'undefined' !== typeof constraint ? new Constraint( constraint ) : false; this.validate = function ( collection, group ) { var result, validator = new Validator(), count = 0, failures = {}, groups = this.groups.length ? this.groups : group; if ( !_isArray( collection ) ) throw new Violation( this, array, { value: Validator.errorCode.must_be_an_array } ); for ( var i = 0; i < collection.length; i++ ) { result = this.constraint ? validator.validate( collection[ i ], this.constraint, groups ) : validator.validate( collection[ i ], groups ); if ( !_isEmptyObject( result ) ) failures[ count ] = result; count++; } return !_isEmptyObject( failures ) ? failures : true; }; return this; }, Count: function ( count ) { this.__class__ = 'Count'; this.count = count; this.validate = function ( array ) { if ( !_isArray( array ) ) throw new Violation( this, array, { value: Validator.errorCode.must_be_an_array } ); var count = 'function' === typeof this.count ? this.count( array ) : this.count; if ( isNaN( Number( count ) ) ) throw new Error( 'Count must be a valid interger', count ); if ( count !== array.length ) throw new Violation( this, array, { count: count } ); return true; }; return this; }, Email: function () { this.__class__ = 'Email'; this.validate = function ( value ) { var 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; if ( 'string' !== typeof value ) throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string } ); if ( !regExp.test( value ) ) throw new Violation( this, value ); return true; }; return this; }, Eql: function ( eql ) { this.__class__ = 'Eql'; if ( 'undefined' === typeof eql ) throw new Error( 'Equal must be instanciated with an Array or an Object' ); this.eql = eql; this.validate = function ( value ) { var eql = 'function' === typeof this.eql ? this.eql( value ) : this.eql; if ( !expect.eql( eql, value ) ) throw new Violation( this, value, { eql: eql } ); return true; }; return this; }, EqualTo: function ( reference ) { this.__class__ = 'EqualTo'; if ( 'undefined' === typeof reference ) throw new Error( 'EqualTo must be instanciated with a value or a function' ); this.reference = reference; this.validate = function ( value ) { var reference = 'function' === typeof this.reference ? this.reference( value ) : this.reference; if ( reference !== value ) throw new Violation( this, value, { value: reference } ); return true; }; return this; }, GreaterThan: function ( threshold ) { this.__class__ = 'GreaterThan'; if ( 'undefined' === typeof threshold ) throw new Error( 'Should give a threshold value' ); this.threshold = threshold; this.validate = function ( value ) { if ( '' === value || isNaN( Number( value ) ) ) throw new Violation( this, value, { value: Validator.errorCode.must_be_a_number } ); if ( this.threshold >= value ) throw new Violation( this, value, { threshold: this.threshold } ); return true; }; return this; }, GreaterThanOrEqual: function ( threshold ) { this.__class__ = 'GreaterThanOrEqual'; if ( 'undefined' === typeof threshold ) throw new Error( 'Should give a threshold value' ); this.threshold = threshold; this.validate = function ( value ) { if ( '' === value || isNaN( Number( value ) ) ) throw new Violation( this, value, { value: Validator.errorCode.must_be_a_number } ); if ( this.threshold > value ) throw new Violation( this, value, { threshold: this.threshold } ); return true; }; return this; }, InstanceOf: function ( classRef ) { this.__class__ = 'InstanceOf'; if ( 'undefined' === typeof classRef ) throw new Error( 'InstanceOf must be instanciated with a value' ); this.classRef = classRef; this.validate = function ( value ) { if ( true !== (value instanceof this.classRef) ) throw new Violation( this, value, { classRef: this.classRef } ); return true; }; return this; }, IPv4: function () { this.__class__ = 'IPv4'; this.validate = function ( value ) { var regExp = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; if ( 'string' !== typeof value ) throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string } ); if ( !regExp.test( value ) ) throw new Violation( this, value ); return true; }; return this; }, Length: function ( boundaries ) { this.__class__ = 'Length'; if ( !boundaries.min && !boundaries.max ) throw new Error( 'Lenth assert must be instanciated with a { min: x, max: y } object' ); this.min = boundaries.min; this.max = boundaries.max; this.validate = function ( value ) { if ( 'string' !== typeof value && !_isArray( value ) ) throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string_or_array } ); if ( 'undefined' !== typeof this.min && this.min === this.max && value.length !== this.min ) throw new Violation( this, value, { min: this.min, max: this.max } ); if ( 'undefined' !== typeof this.max && value.length > this.max ) throw new Violation( this, value, { max: this.max } ); if ( 'undefined' !== typeof this.min && value.length < this.min ) throw new Violation( this, value, { min: this.min } ); return true; }; return this; }, LessThan: function ( threshold ) { this.__class__ = 'LessThan'; if ( 'undefined' === typeof threshold ) throw new Error( 'Should give a threshold value' ); this.threshold = threshold; this.validate = function ( value ) { if ( '' === value || isNaN( Number( value ) ) ) throw new Violation( this, value, { value: Validator.errorCode.must_be_a_number } ); if ( this.threshold <= value ) throw new Violation( this, value, { threshold: this.threshold } ); return true; }; return this; }, LessThanOrEqual: function ( threshold ) { this.__class__ = 'LessThanOrEqual'; if ( 'undefined' === typeof threshold ) throw new Error( 'Should give a threshold value' ); this.threshold = threshold; this.validate = function ( value ) { if ( '' === value || isNaN( Number( value ) ) ) throw new Violation( this, value, { value: Validator.errorCode.must_be_a_number } ); if ( this.threshold < value ) throw new Violation( this, value, { threshold: this.threshold } ); return true; }; return this; }, Mac: function () { this.__class__ = 'Mac'; this.validate = function ( value ) { var regExp = /^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$/i; if ( 'string' !== typeof value ) throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string } ); if ( !regExp.test( value ) ) throw new Violation( this, value ); return true; }; return this; }, NotNull: function () { this.__class__ = 'NotNull'; this.validate = function ( value ) { if ( null === value || 'undefined' === typeof value ) throw new Violation( this, value ); return true; }; return this; }, NotBlank: function () { this.__class__ = 'NotBlank'; this.validate = function ( value ) { if ( 'string' !== typeof value ) throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string } ); if ( '' === value.replace( /^\s+/g, '' ).replace( /\s+$/g, '' ) ) throw new Violation( this, value ); return true; }; return this; }, Null: function () { this.__class__ = 'Null'; this.validate = function ( value ) { if ( null !== value ) throw new Violation( this, value ); return true; }; return this; }, Range: function ( min, max ) { this.__class__ = 'Range'; if ( 'undefined' === typeof min || 'undefined' === typeof max ) throw new Error( 'Range assert expects min and max values' ); this.min = min; this.max = max; this.validate = function ( value ) { try { // validate strings and objects with their Length if ( ( 'string' === typeof value && isNaN( Number( value ) ) ) || _isArray( value ) ) new Assert().Length( { min: this.min, max: this.max } ).validate( value ); // validate numbers with their value else new Assert().GreaterThanOrEqual( this.min ).validate( value ) && new Assert().LessThanOrEqual( this.max ).validate( value ); return true; } catch ( violation ) { throw new Violation( this, value, violation.violation ); } return true; }; return this; }, Regexp: function ( regexp, flag ) { this.__class__ = 'Regexp'; if ( 'undefined' === typeof regexp ) throw new Error( 'You must give a regexp' ); this.regexp = regexp; this.flag = flag || ''; this.validate = function ( value ) { if ( 'string' !== typeof value ) throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string } ); if ( !new RegExp( this.regexp, this.flag ).test( value ) ) throw new Violation( this, value, { regexp: this.regexp, flag: this.flag } ); return true; }; return this; }, Required: function () { this.__class__ = 'Required'; this.validate = function ( value ) { if ( 'undefined' === typeof value ) throw new Violation( this, value ); try { if ( 'string' === typeof value ) new Assert().NotNull().validate( value ) && new Assert().NotBlank().validate( value ); else if ( true === _isArray( value ) ) new Assert().Length( { min: 1 } ).validate( value ); } catch ( violation ) { throw new Violation( this, value ); } return true; }; return this; }, // Unique() or Unique ( { key: foo } ) Unique: function ( object ) { this.__class__ = 'Unique'; if ( 'object' === typeof object ) this.key = object.key; this.validate = function ( array ) { var value, store = []; if ( !_isArray( array ) ) throw new Violation( this, array, { value: Validator.errorCode.must_be_an_array } ); for ( var i = 0; i < array.length; i++ ) { value = 'object' === typeof array[ i ] ? array[ i ][ this.key ] : array[ i ]; if ( 'undefined' === typeof value ) continue; if ( -1 !== store.indexOf( value ) ) throw new Violation( this, array, { value: value } ); store.push( value ); } return true; }; return this; } }; // expose to the world these awesome classes exports.Assert = Assert; exports.Validator = Validator; exports.Violation = Violation; exports.Constraint = Constraint; /** * Some useful object prototypes / functions here */ // IE8<= compatibility // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf if (!Array.prototype.indexOf) Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { if (this === null) { throw new TypeError(); } var t = Object(this); var len = t.length >>> 0; if (len === 0) { return -1; } var n = 0; if (arguments.length > 1) { n = Number(arguments[1]); if (n != n) { // shortcut for verifying if it's NaN n = 0; } else if (n !== 0 && n != Infinity && n != -Infinity) { n = (n > 0 || -1) * Math.floor(Math.abs(n)); } } if (n >= len) { return -1; } var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); for (; k < len; k++) { if (k in t && t[k] === searchElement) { return k; } } return -1; }; // Test if object is empty, useful for Constraint violations check var _isEmptyObject = function ( obj ) { for ( var property in obj ) return false; return true; }; var _isArray = function ( obj ) { return Object.prototype.toString.call( obj ) === '[object Array]'; }; // https://github.com/LearnBoost/expect.js/blob/master/expect.js var expect = { eql: function ( actual, expected ) { if ( actual === expected ) { return true; } else if ( 'undefined' !== typeof Buffer && Buffer.isBuffer( actual ) && Buffer.isBuffer( expected ) ) { if ( actual.length !== expected.length ) return false; for ( var i = 0; i < actual.length; i++ ) if ( actual[i] !== expected[i] ) return false; return true; } else if ( actual instanceof Date && expected instanceof Date ) { return actual.getTime() === expected.getTime(); } else if ( typeof actual !== 'object' && typeof expected !== 'object' ) { // loosy == return actual == expected; } else { return this.objEquiv(actual, expected); } }, isUndefinedOrNull: function ( value ) { return value === null || typeof value === 'undefined'; }, isArguments: function ( object ) { return Object.prototype.toString.call(object) == '[object Arguments]'; }, keys: function ( obj ) { if ( Object.keys ) return Object.keys( obj ); var keys = []; for ( var i in obj ) if ( Object.prototype.hasOwnProperty.call( obj, i ) ) keys.push(i); return keys; }, objEquiv: function ( a, b ) { if ( this.isUndefinedOrNull( a ) || this.isUndefinedOrNull( b ) ) return false; if ( a.prototype !== b.prototype ) return false; if ( this.isArguments( a ) ) { if ( !this.isArguments( b ) ) return false; return eql( pSlice.call( a ) , pSlice.call( b ) ); } try { var ka = this.keys( a ), kb = this.keys( b ), key, i; if ( ka.length !== kb.length ) return false; ka.sort(); kb.sort(); for ( i = ka.length - 1; i >= 0; i-- ) if ( ka[ i ] != kb[ i ] ) return false; for ( i = ka.length - 1; i >= 0; i-- ) { key = ka[i]; if ( !this.eql( a[ key ], b[ key ] ) ) return false; } return true; } catch ( e ) { return false; } } }; // AMD Compliance if ( "function" === typeof define && define.amd ) { define( 'validator', [],function() { return exports; } ); } } )( 'undefined' === typeof exports ? this[ 'undefined' !== typeof validatorjs_ns ? validatorjs_ns : 'Validator' ] = {} : exports ); var ParsleyValidator = function (validators, catalog) { this.__class__ = 'ParsleyValidator'; this.Validator = Validator; // Default Parsley locale is en this.locale = 'en'; this.init(validators || {}, catalog || {}); }; ParsleyValidator.prototype = { init: function (validators, catalog) { this.catalog = catalog; for (var name in validators) this.addValidator(name, validators[name].fn, validators[name].priority); $.emit('parsley:validator:init'); }, // Set new messages locale if we have dictionary loaded in ParsleyConfig.i18n setLocale: function (locale) { if ('undefined' === typeof this.catalog[locale]) throw new Error(locale + ' is not available in the catalog'); this.locale = locale; return this; }, // Add a new messages catalog for a given locale. Set locale for this catalog if set === `true` addCatalog: function (locale, messages, set) { if ('object' === typeof messages) this.catalog[locale] = messages; if (true === set) return this.setLocale(locale); return this; }, // Add a specific message for a given constraint in a given locale addMessage: function (locale, name, message) { if (undefined === typeof this.catalog[locale]) this.catalog[locale] = {}; this.catalog[locale][name.toLowerCase()] = message; return this; }, validate: function (value, constraints, priority) { return new this.Validator.Validator().validate.apply(new Validator.Validator(), arguments); }, // Add a new validator addValidator: function (name, fn, priority) { this.validators[name.toLowerCase()] = function (requirements) { return $.extend(new Validator.Assert().Callback(fn, requirements), { priority: priority }); }; return this; }, updateValidator: function (name, fn, priority) { return addValidator(name, fn, priority); }, removeValidator: function (name) { delete this.validators[name]; return this; }, getErrorMessage: function (constraint) { var message; // Type constraints are a bit different, we have to match their requirements too to find right error message if ('type' === constraint.name) message = this.catalog[this.locale][constraint.name][constraint.requirements]; else message = this.formatMessage(this.catalog[this.locale][constraint.name], constraint.requirements); return '' !== message ? message : this.catalog[this.locale].defaultMessage; }, // Kind of light `sprintf()` implementation formatMessage: function (string, parameters) { if ('object' === typeof parameters) { for (var i in parameters) string = this.formatMessage(string, parameters[i]); return string; } return 'string' === typeof string ? string.replace(new RegExp('%s', 'i'), parameters) : ''; }, // Here is the Parsley default validators list. // This is basically Validatorjs validators, with different API for some of them // and a Parsley priority set validators: { notblank: function () { return $.extend(new Validator.Assert().NotBlank(), { priority: 2 }); }, required: function () { return $.extend(new Validator.Assert().Required(), { priority: 512 }); }, type: function (type) { var assert; switch (type) { case 'email': assert = new Validator.Assert().Email(); break; case 'number': assert = new Validator.Assert().Regexp('^-?(?:\\d+|\\d{1,3}(?:,\\d{3})+)?(?:\\.\\d+)?$'); break; case 'integer': assert = new Validator.Assert().Regexp('^-?\\d+$'); break; case 'digits': assert = new Validator.Assert().Regexp('^\\d+$'); break; case 'alphanum': assert = new Validator.Assert().Regexp('^\\w+$', 'i'); break; case 'url': assert = new Validator.Assert().Regexp('(https?:\\/\\/)?(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,4}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)', 'i'); break; default: throw new Error('validator type `' + type + '` is not supported'); } return $.extend(assert, { priority: 256 }); }, pattern: function (regexp) { var flags = ''; // Test if RegExp is literal, if not, nothing to be done, otherwise, we need to isolate flags and pattern if (!!(/^\/.*\/(?:[gimy]*)$/.test(regexp))) { // Replace the regexp literal string with the first match group: ([gimy]*) // If no flag is present, this will be a blank string flags = regexp.replace(/.*\/([gimy]*)$/, '$1'); // Again, replace the regexp literal string with the first match group: // everything excluding the opening and closing slashes and the flags regexp = regexp.replace(new RegExp('^/(.*?)/' + flags + '$'), '$1'); } return $.extend(new Validator.Assert().Regexp(regexp, flags), { priority: 64 }); }, minlength: function (value) { return $.extend(new Validator.Assert().Length({ min: value }), { priority: 30, requirementsTransformer: function () { return 'string' === typeof value && !isNaN(value) ? parseInt(value, 10) : value; } }); }, maxlength: function (value) { return $.extend(new Validator.Assert().Length({ max: value }), { priority: 30, requirementsTransformer: function () { return 'string' === typeof value && !isNaN(value) ? parseInt(value, 10) : value; } }); }, length: function (array) { return $.extend(new Validator.Assert().Length({ min: array[0], max: array[1] }), { priority: 32 }); }, mincheck: function (length) { return this.minlength(length); }, maxcheck: function (length) { return this.maxlength(length); }, check: function (array) { return this.length(array); }, min: function (value) { return $.extend(new Validator.Assert().GreaterThanOrEqual(value), { priority: 30, requirementsTransformer: function () { return 'string' === typeof value && !isNaN(value) ? parseInt(value, 10) : value; } }); }, max: function (value) { return $.extend(new Validator.Assert().LessThanOrEqual(value), { priority: 30, requirementsTransformer: function () { return 'string' === typeof value && !isNaN(value) ? parseInt(value, 10) : value; } }); }, range: function (array) { return $.extend(new Validator.Assert().Range(array[0], array[1]), { priority: 32, requirementsTransformer: function () { for (var i = 0; i < array.length; i++) array[i] = 'string' === typeof array[i] && !isNaN(array[i]) ? parseInt(array[i], 10) : array[i]; return array; } }); }, equalto: function (value) { return $.extend(new Validator.Assert().EqualTo(value), { priority: 256, requirementsTransformer: function () { return $(value).length ? $(value).val() : value; } }); } } }; var ParsleyUI = function (options) { this.__class__ = 'ParsleyUI'; }; ParsleyUI.prototype = { listen: function () { $.listen('parsley:form:init', this, this.setupForm); $.listen('parsley:field:init', this, this.setupField); $.listen('parsley:field:validated', this, this.reflow); $.listen('parsley:form:validated', this, this.focus); $.listen('parsley:field:reset', this, this.reset); $.listen('parsley:form:destroy', this, this.destroy); $.listen('parsley:field:destroy', this, this.destroy); return this; }, reflow: function (fieldInstance) { // If this field has not an active UI (case for multiples) don't bother doing something if ('undefined' === typeof fieldInstance._ui || false === fieldInstance._ui.active) return; // Diff between two validation results var diff = this._diff(fieldInstance.validationResult, fieldInstance._ui.lastValidationResult); // Then store current validation result for next reflow fieldInstance._ui.lastValidationResult = fieldInstance.validationResult; // Field have been validated at least once if here. Useful for binded key events.. fieldInstance._ui.validatedOnce = true; // Handle valid / invalid / none field class this.manageStatusClass(fieldInstance); // Add, remove, updated errors messages this.manageErrorsMessages(fieldInstance, diff); // Triggers impl this.actualizeTriggers(fieldInstance); // If field is not valid for the first time, bind keyup trigger to ease UX and quickly inform user if ((diff.kept.length || diff.added.length) && 'undefined' === typeof fieldInstance._ui.failedOnce) this.manageFailingFieldTrigger(fieldInstance); }, // Returns an array of field's error message(s) getErrorsMessages: function (fieldInstance) { // No error message, field is valid if (true === fieldInstance.validationResult) return []; var messages = []; for (var i = 0; i < fieldInstance.validationResult.length; i++) messages.push(this._getErrorMessage(fieldInstance, fieldInstance.validationResult[i].assert)); return messages; }, manageStatusClass: function (fieldInstance) { if (true === fieldInstance.validationResult) this._successClass(fieldInstance); else if (fieldInstance.validationResult.length > 0) this._errorClass(fieldInstance); else this._resetClass(fieldInstance); }, manageErrorsMessages: function (fieldInstance, diff) { if ('undefined' !== typeof fieldInstance.options.errorsMessagesDisabled) return; // Case where we have errorMessage option that configure an unique field error message, regardless failing validators if ('undefined' !== typeof fieldInstance.options.errorMessage) { if ((diff.added.length || diff.kept.length)) { if (0 === fieldInstance._ui.$errorsWrapper.find('.parsley-custom-error-message').length) fieldInstance._ui.$errorsWrapper .append($(fieldInstance.options.errorTemplate) .addClass('parsley-custom-error-message')); return fieldInstance._ui.$errorsWrapper .addClass('filled') .find('.parsley-custom-error-message') .html(fieldInstance.options.errorMessage); } return fieldInstance._ui.$errorsWrapper .removeClass('filled') .find('.parsley-custom-error-message') .remove(); } // Show, hide, update failing constraints messages for (var i = 0; i < diff.removed.length; i++) this.removeError(fieldInstance, diff.removed[i].assert.name, true); for (i = 0; i < diff.added.length; i++) this.addError(fieldInstance, diff.added[i].assert.name, undefined, diff.added[i].assert, true); for (i = 0; i < diff.kept.length; i++) this.updateError(fieldInstance, diff.kept[i].assert.name, undefined, diff.kept[i].assert, true); }, // TODO: strange API here, intuitive for manual usage with addError(pslyInstance, 'foo', 'bar') // but a little bit complex for above internal usage, with forced undefined parametter.. addError: function (fieldInstance, name, message, assert, doNotUpdateClass) { fieldInstance._ui.$errorsWrapper .addClass('filled') .append($(fieldInstance.options.errorTemplate) .addClass('parsley-' + name) .html(message || this._getErrorMessage(fieldInstance, assert))); if (true !== doNotUpdateClass) this._errorClass(fieldInstance); }, // Same as above updateError: function (fieldInstance, name, message, assert, doNotUpdateClass) { fieldInstance._ui.$errorsWrapper .addClass('filled') .find('.parsley-' + name) .html(message || this._getErrorMessage(fieldInstance, assert)); if (true !== doNotUpdateClass) this._errorClass(fieldInstance); }, // Same as above twice removeError: function (fieldInstance, name, doNotUpdateClass) { fieldInstance._ui.$errorsWrapper .removeClass('filled') .find('.parsley-' + name) .remove(); // edge case possible here: remove a standard Parsley error that is still failing in fieldInstance.validationResult // but highly improbable cuz' manually removing a well Parsley handled error makes no sense. if (true !== doNotUpdateClass) this.manageStatusClass(fieldInstance); }, focus: function (formInstance) { if (true === formInstance.validationResult || 'none' === formInstance.options.focus) return formInstance._focusedField = null; formInstance._focusedField = null; for (var i = 0; i < formInstance.fields.length; i++) if (true !== formInstance.fields[i].validationResult && formInstance.fields[i].validationResult.length > 0 && 'undefined' === typeof formInstance.fields[i].options.noFocus) { if ('first' === formInstance.options.focus) { formInstance._focusedField = formInstance.fields[i].$element; return formInstance._focusedField.focus(); } formInstance._focusedField = formInstance.fields[i].$element; } if (null === formInstance._focusedField) return null; return formInstance._focusedField.focus(); }, _getErrorMessage: function (fieldInstance, constraint) { var customConstraintErrorMessage = constraint.name + 'Message'; if ('undefined' !== typeof fieldInstance.options[customConstraintErrorMessage]) return window.ParsleyValidator.formatMessage(fieldInstance.options[customConstraintErrorMessage], constraint.requirements); return window.ParsleyValidator.getErrorMessage(constraint); }, _diff: function (newResult, oldResult, deep) { var added = [], kept = []; for (var i = 0; i < newResult.length; i++) { var found = false; for (var j = 0; j < oldResult.length; j++) if (newResult[i].assert.name === oldResult[j].assert.name) { found = true; break; } if (found) kept.push(newResult[i]); else added.push(newResult[i]); } return { kept: kept, added: added, removed: !deep ? this._diff(oldResult, newResult, true).added : [] }; }, setupForm: function (formInstance) { formInstance.$element.on('submit.Parsley', false, $.proxy(formInstance.onSubmitValidate, formInstance)); // UI could be disabled if (false === formInstance.options.uiEnabled) return; formInstance.$element.attr('novalidate', ''); }, setupField: function (fieldInstance) { var _ui = { active: false }; // UI could be disabled if (false === fieldInstance.options.uiEnabled) return; _ui.active = true; // Give field its Parsley id in DOM fieldInstance.$element.attr(fieldInstance.options.namespace + 'id', fieldInstance.__id__); /** Generate important UI elements and store them in fieldInstance **/ // $errorClassHandler is the $element that woul have parsley-error and parsley-success classes _ui.$errorClassHandler = this._manageClassHandler(fieldInstance); // $errorsWrapper is a div that would contain the various field errors, it will be appended into $errorsContainer _ui.errorsWrapperId = 'parsley-id-' + ('undefined' !== typeof fieldInstance.options.multiple ? 'multiple-' + fieldInstance.options.multiple : fieldInstance.__id__); _ui.$errorsWrapper = $(fieldInstance.options.errorsWrapper).attr('id', _ui.errorsWrapperId); // ValidationResult UI storage to detect what have changed bwt two validations, and update DOM accordingly _ui.lastValidationResult = []; _ui.validatedOnce = false; _ui.validationInformationVisible = false; // Store it in fieldInstance for later fieldInstance._ui = _ui; /** Mess with DOM now **/ this._insertErrorWrapper(fieldInstance); // Bind triggers first time this.actualizeTriggers(fieldInstance); }, // Determine which element will have `parsley-error` and `parsley-success` classes _manageClassHandler: function (fieldInstance) { // An element selector could be passed through DOM with `data-parsley-class-handler=#foo` if ('string' === typeof fieldInstance.options.classHandler && $(fieldInstance.options.classHandler).length) return $(fieldInstance.options.classHandler); // Class handled could also be determined by function given in Parsley options var $handler = fieldInstance.options.classHandler(fieldInstance); // If this function returned a valid existing DOM element, go for it if ('undefined' !== typeof $handler && $handler.length) return $handler; // Otherwise, if simple element (input, texatrea, select..) it will perfectly host the classes if ('undefined' === typeof fieldInstance.options.multiple || fieldInstance.$element.is('select')) return fieldInstance.$element; // But if multiple element (radio, checkbox), that would be their parent return fieldInstance.$element.parent(); }, _insertErrorWrapper: function (fieldInstance) { var $errorsContainer; if ('string' === typeof fieldInstance.options.errorsContainer ) if ($(fieldInstance.options.errorsContainer + '').length) return $(fieldInstance.options.errorsContainer).append(fieldInstance._ui.$errorsWrapper); else if (window.console && window.console.warn) window.console.warn('The errors container `' + fieldInstance.options.errorsContainer + '` does not exist in DOM'); if ('function' === typeof fieldInstance.options.errorsContainer) $errorsContainer = fieldInstance.options.errorsContainer(fieldInstance); if ('undefined' !== typeof $errorsContainer && $errorsContainer.length) return $errorsContainer.append(fieldInstance._ui.$errorsWrapper); return 'undefined' === typeof fieldInstance.options.multiple ? fieldInstance.$element.after(fieldInstance._ui.$errorsWrapper) : fieldInstance.$element.parent().after(fieldInstance._ui.$errorsWrapper); }, actualizeTriggers: function (fieldInstance) { var that = this; // Remove Parsley events already binded on this field if (fieldInstance.options.multiple) $('[' + fieldInstance.options.namespace + 'multiple="' + fieldInstance.options.multiple + '"]').each(function () { $(this).off('.Parsley'); }); else fieldInstance.$element.off('.Parsley'); // If no trigger is set, all good if (false === fieldInstance.options.trigger) return; var triggers = fieldInstance.options.trigger.replace(/^\s+/g , '').replace(/\s+$/g , ''); if ('' === triggers) return; // Bind fieldInstance.eventValidate if exists (for parsley.ajax for example), ParsleyUI.eventValidate otherwise if (fieldInstance.options.multiple) $('[' + fieldInstance.options.namespace + 'multiple="' + fieldInstance.options.multiple + '"]').each(function () { $(this).on( triggers.split(' ').join('.Parsley ') + '.Parsley', false, $.proxy('function' === typeof fieldInstance.eventValidate ? fieldInstance.eventValidate : that.eventValidate, fieldInstance)); }); else fieldInstance.$element .on( triggers.split(' ').join('.Parsley ') + '.Parsley', false, $.proxy('function' === typeof fieldInstance.eventValidate ? fieldInstance.eventValidate : this.eventValidate, fieldInstance)); }, // Called through $.proxy with fieldInstance. `this` context is ParsleyField eventValidate: function(event) { // For keyup, keypress, keydown.. events that could be a little bit obstrusive // do not validate if val length < min threshold on first validation. Once field have been validated once and info // about success or failure have been displayed, always validate with this trigger to reflect every yalidation change. if (new RegExp('key').test(event.type)) if (!this._ui.validationInformationVisible && this.getValue().length <= this.options.validationThreshold) return; this._ui.validatedOnce = true; this.validate(); }, manageFailingFieldTrigger: function (fieldInstance) { fieldInstance._ui.failedOnce = true; // Radio and checkboxes fields must bind every field multiple if (fieldInstance.options.multiple) $('[' + fieldInstance.options.namespace + 'multiple="' + fieldInstance.options.multiple + '"]').each(function () { if (!new RegExp('change', 'i').test($(this).parsley().options.trigger || '')) return $(this).on('change.ParsleyFailedOnce', false, $.proxy(fieldInstance.validate, fieldInstance)); }); // Select case if (fieldInstance.$element.is('select')) if (!new RegExp('change', 'i').test(fieldInstance.options.trigger || '')) return fieldInstance.$element.on('change.ParsleyFailedOnce', false, $.proxy(fieldInstance.validate, fieldInstance)); // All other inputs fields if (!new RegExp('keyup', 'i').test(fieldInstance.options.trigger || '')) return fieldInstance.$element.on('keyup.ParsleyFailedOnce', false, $.proxy(fieldInstance.validate, fieldInstance)); }, reset: function (parsleyInstance) { // Reset all event listeners parsleyInstance.$element.off('.Parsley'); parsleyInstance.$element.off('.ParsleyFailedOnce'); // Nothing to do if UI never initialized for this field if ('undefined' === typeof parsleyInstance._ui) return; if ('ParsleyForm' === parsleyInstance.__class__) return; // Reset all errors' li parsleyInstance._ui.$errorsWrapper.children().each(function () { $(this).remove(); }); // Reset validation class this._resetClass(parsleyInstance); // Reset validation flags and last validation result parsleyInstance._ui.validatedOnce = false; parsleyInstance._ui.lastValidationResult = []; parsleyInstance._ui.validationInformationVisible = false; }, destroy: function (parsleyInstance) { this.reset(parsleyInstance); if ('ParsleyForm' === parsleyInstance.__class__) return; parsleyInstance._ui.$errorsWrapper.remove(); delete parsleyInstance._ui; }, _successClass: function (fieldInstance) { fieldInstance._ui.validationInformationVisible = true; fieldInstance._ui.$errorClassHandler.removeClass(fieldInstance.options.errorClass).addClass(fieldInstance.options.successClass); }, _errorClass: function (fieldInstance) { fieldInstance._ui.validationInformationVisible = true; fieldInstance._ui.$errorClassHandler.removeClass(fieldInstance.options.successClass).addClass(fieldInstance.options.errorClass); }, _resetClass: function (fieldInstance) { fieldInstance._ui.$errorClassHandler.removeClass(fieldInstance.options.successClass).removeClass(fieldInstance.options.errorClass); } }; var ParsleyOptionsFactory = function (defaultOptions, globalOptions, userOptions, namespace) { this.__class__ = 'OptionsFactory'; this.__id__ = ParsleyUtils.hash(4); this.formOptions = null; this.fieldOptions = null; this.staticOptions = $.extend(true, {}, defaultOptions, globalOptions, userOptions, { namespace: namespace }); }; ParsleyOptionsFactory.prototype = { get: function (parsleyInstance) { if ('undefined' === typeof parsleyInstance.__class__) throw new Error('Parsley Instance expected'); switch (parsleyInstance.__class__) { case 'Parsley': return this.staticOptions; case 'ParsleyForm': return this.getFormOptions(parsleyInstance); case 'ParsleyField': case 'ParsleyFieldMultiple': return this.getFieldOptions(parsleyInstance); default: throw new Error('Instance ' + parsleyInstance.__class__ + ' is not supported'); } }, getFormOptions: function (formInstance) { this.formOptions = ParsleyUtils.attr(formInstance.$element, this.staticOptions.namespace); // not deep extend, since formOptions is a 1 level deep object return $.extend({}, this.staticOptions, this.formOptions); }, getFieldOptions: function (fieldInstance) { this.fieldOptions = ParsleyUtils.attr(fieldInstance.$element, this.staticOptions.namespace); if (null === this.formOptions && 'undefined' !== typeof fieldInstance.parent) this.formOptions = this.getFormOptions(fieldInstance.parent); // not deep extend, since formOptions and fieldOptions is a 1 level deep object return $.extend({}, this.staticOptions, this.formOptions, this.fieldOptions); } }; var ParsleyForm = function (element, OptionsFactory) { this.__class__ = 'ParsleyForm'; this.__id__ = ParsleyUtils.hash(4); if ('OptionsFactory' !== ParsleyUtils.get(OptionsFactory, '__class__')) throw new Error('You must give an OptionsFactory instance'); this.OptionsFactory = OptionsFactory; this.$element = $(element); this.validationResult = null; this.options = this.OptionsFactory.get(this); }; ParsleyForm.prototype = { onSubmitValidate: function (event) { this.validate(undefined, undefined, event); // prevent form submission if validation fails if (false === this.validationResult && event instanceof $.Event) { event.stopImmediatePropagation(); event.preventDefault(); } return this; }, // @returns boolean validate: function (group, force, event) { this.submitEvent = event; this.validationResult = true; var fieldValidationResult = []; // Refresh form DOM options and form's fields that could have changed this._refreshFields(); $.emit('parsley:form:validate', this); // loop through fields to validate them one by one for (var i = 0; i < this.fields.length; i++) { // do not validate a field if not the same as given validation group if (group && group !== this.fields[i].options.group) continue; fieldValidationResult = this.fields[i].validate(force); if (true !== fieldValidationResult && fieldValidationResult.length > 0 && this.validationResult) this.validationResult = false; } $.emit('parsley:form:validated', this); return this.validationResult; }, // Iterate over refreshed fields, and stop on first failure isValid: function (group, force) { this._refreshFields(); for (var i = 0; i < this.fields.length; i++) { // do not validate a field if not the same as given validation group if (group && group !== this.fields[i].options.group) continue; if (false === this.fields[i].isValid(force)) return false; } return true; }, _refreshFields: function () { return this.actualizeOptions()._bindFields(); }, _bindFields: function () { var self = this; this.fields = []; this.fieldsMappedById = {}; this.$element.find(this.options.inputs).each(function () { var fieldInstance = new window.Parsley(this, {}, self); // Only add valid and not excluded `ParsleyField` and `ParsleyFieldMultiple` children if (('ParsleyField' === fieldInstance.__class__ || 'ParsleyFieldMultiple' === fieldInstance.__class__) && !fieldInstance.$element.is(fieldInstance.options.excluded)) if ('undefined' === typeof self.fieldsMappedById[fieldInstance.__class__ + '-' + fieldInstance.__id__]) { self.fieldsMappedById[fieldInstance.__class__ + '-' + fieldInstance.__id__] = fieldInstance; self.fields.push(fieldInstance); } }); return this; } }; var ConstraintFactory = function (parsleyField, name, requirements, priority, isDomConstraint) { if (!new RegExp('ParsleyField').test(ParsleyUtils.get(parsleyField, '__class__'))) throw new Error('ParsleyField or ParsleyFieldMultiple instance expected'); if ('function' !== typeof window.ParsleyValidator.validators[name] && 'Assert' !== window.ParsleyValidator.validators[name](requirements).__parentClass__) throw new Error('Valid validator expected'); var getPriority = function (parsleyField, name) { if ('undefined' !== typeof parsleyField.options[name + 'Priority']) return parsleyField.options[name + 'Priority']; return ParsleyUtils.get(window.ParsleyValidator.validators[name](requirements), 'priority') || 2; }; priority = priority || getPriority(parsleyField, name); // If validator have a requirementsTransformer, execute it if ('function' === typeof window.ParsleyValidator.validators[name](requirements).requirementsTransformer) requirements = window.ParsleyValidator.validators[name](requirements).requirementsTransformer(); return $.extend(window.ParsleyValidator.validators[name](requirements), { name: name, requirements: requirements, priority: priority, groups: [priority], isDomConstraint: isDomConstraint || ParsleyUtils.attr(parsleyField.$element, parsleyField.options.namespace, name) }); }; var ParsleyField = function (field, OptionsFactory, parsleyFormInstance) { this.__class__ = 'ParsleyField'; this.__id__ = ParsleyUtils.hash(4); this.$element = $(field); // If we have a parent `ParsleyForm` instance given, use its `OptionsFactory`, and save parent if ('undefined' !== typeof parsleyFormInstance) { this.parent = parsleyFormInstance; this.OptionsFactory = this.parent.OptionsFactory; this.options = this.OptionsFactory.get(this); // Else, take the `Parsley` one } else { this.OptionsFactory = OptionsFactory; this.options = this.OptionsFactory.get(this); } // Initialize some properties this.constraints = []; this.constraintsByName = {}; this.validationResult = []; // Bind constraints this._bindConstraints(); }; ParsleyField.prototype = { // # Public API // Validate field and $.emit some events for mainly `ParsleyUI` // @returns validationResult: // - `true` if all constraint passes // - `[]` if not required field and empty (not validated) // - `[Violation, [Violation..]]` if there were validation errors validate: function (force) { this.value = this.getValue(); // Field Validate event. `this.value` could be altered for custom needs $.emit('parsley:field:validate', this); $.emit('parsley:field:' + (this.isValid(force, this.value) ? 'success' : 'error'), this); // Field validated event. `this.validationResult` could be altered for custom needs too $.emit('parsley:field:validated', this); return this.validationResult; }, // Just validate field. Do not trigger any event // Same @return as `validate()` isValid: function (force, value) { // Recompute options and rebind constraints to have latest changes this.refreshConstraints(); // Sort priorities to validate more important first var priorities = this._getConstraintsSortedPriorities(); // Value could be passed as argument, needed to add more power to 'parsley:field:validate' value = value || this.getValue(); // If a field is empty and not required, leave it alone, it's just fine // Except if `data-parsley-validate-if-empty` explicitely added, useful for some custom validators if (0 === value.length && !this._isRequired() && 'undefined' === typeof this.options.validateIfEmpty && true !== force) return this.validationResult = []; // If we want to validate field against all constraints, just call Validator and let it do the job if (false === this.options.priorityEnabled) return true === (this.validationResult = this.validateThroughValidator(value, this.constraints, 'Any')); // Else, iterate over priorities one by one, and validate related asserts one by one for (var i = 0; i < priorities.length; i++) if (true !== (this.validationResult = this.validateThroughValidator(value, this.constraints, priorities[i]))) return false; return true; }, // @returns Parsley field computed value that could be overrided or configured in DOM getValue: function () { var value; // Value could be overriden in DOM if ('undefined' !== typeof this.options.value) value = this.options.value; else value = this.$element.val(); // Handle wrong DOM or configurations if ('undefined' === typeof value || null === value) return ''; // Use `data-parsley-trim-value="true"` to auto trim inputs entry if (true === this.options.trimValue) return value.replace(/^\s+|\s+$/g, ''); return value; }, // Actualize options that could have change since previous validation // Re-bind accordingly constraints (could be some new, removed or updated) refreshConstraints: function () { return this.actualizeOptions()._bindConstraints(); }, /** * Add a new constraint to a field * * @method addConstraint * @param {String} name * @param {Mixed} requirements optional * @param {Number} priority optional * @param {Boolean} isDomConstraint optional */ addConstraint: function (name, requirements, priority, isDomConstraint) { name = name.toLowerCase(); if ('function' === typeof window.ParsleyValidator.validators[name]) { var constraint = new ConstraintFactory(this, name, requirements, priority, isDomConstraint); // if constraint already exist, delete it and push new version if ('undefined' !== this.constraintsByName[constraint.name]) this.removeConstraint(constraint.name); this.constraints.push(constraint); this.constraintsByName[constraint.name] = constraint; } return this; }, // Remove a constraint removeConstraint: function (name) { for (var i = 0; i < this.constraints.length; i++) if (name === this.constraints[i].name) { this.constraints.splice(i, 1); break; } return this; }, // Update a constraint (Remove + re-add) updateConstraint: function (name, parameters, priority) { return this.removeConstraint(name) .addConstraint(name, parameters, priority); }, // # Internals // Internal only. // Bind constraints from config + options + DOM _bindConstraints: function () { var constraints = []; // clean all existing DOM constraints to only keep javascript user constraints for (var i = 0; i < this.constraints.length; i++) if (false === this.constraints[i].isDomConstraint) constraints.push(this.constraints[i]); this.constraints = constraints; // then re-add Parsley DOM-API constraints for (var name in this.options) this.addConstraint(name, this.options[name]); // finally, bind special HTML5 constraints return this._bindHtml5Constraints(); }, // Internal only. // Bind specific HTML5 constraints to be HTML5 compliant _bindHtml5Constraints: function () { // html5 required if (this.$element.hasClass('required') || this.$element.attr('required')) this.addConstraint('required', true, undefined, true); // html5 pattern if ('string' === typeof this.$element.attr('pattern')) this.addConstraint('pattern', this.$element.attr('pattern'), undefined, true); // range if ('undefined' !== typeof this.$element.attr('min') && 'undefined' !== typeof this.$element.attr('max')) this.addConstraint('range', [this.$element.attr('min'), this.$element.attr('max')], undefined, true); // HTML5 min else if ('undefined' !== typeof this.$element.attr('min')) this.addConstraint('min', this.$element.attr('min'), undefined, true); // HTML5 max else if ('undefined' !== typeof this.$element.attr('max')) this.addConstraint('max', this.$element.attr('max'), undefined, true); // html5 types var type = this.$element.attr('type'); if ('undefined' === typeof type) return this; // Small special case here for HTML5 number, that is in fact an integer validator if ('number' === type) return this.addConstraint('type', 'integer', undefined, true); // Regular other HTML5 supported types else if (new RegExp(type, 'i').test('email url range')) return this.addConstraint('type', type, undefined, true); return this; }, // Internal only. // Field is required if have required constraint without `false` value _isRequired: function () { if ('undefined' === typeof this.constraintsByName.required) return false; return false !== this.constraintsByName.required.requirements; }, // Internal only. // Sort constraints by priority DESC _getConstraintsSortedPriorities: function () { var priorities = []; // Create array unique of priorities for (var i = 0; i < this.constraints.length; i++) if (-1 === priorities.indexOf(this.constraints[i].priority)) priorities.push(this.constraints[i].priority); // Sort them by priority DESC priorities.sort(function (a, b) { return b - a; }); return priorities; } }; var ParsleyMultiple = function () { this.__class__ = 'ParsleyFieldMultiple'; }; ParsleyMultiple.prototype = { // Add new `$element` sibling for multiple field addElement: function ($element) { this.$elements.push($element); return this; }, // See `ParsleyField.refreshConstraints()` refreshConstraints: function () { var fieldConstraints; this.constraints = []; // Select multiple special treatment if (this.$element.is('select')) { this.actualizeOptions()._bindConstraints(); return this; } // Gather all constraints for each input in the multiple group for (var i = 0; i < this.$elements.length; i++) { fieldConstraints = this.$elements[i].data('ParsleyFieldMultiple').refreshConstraints().constraints; for (var j = 0; j < fieldConstraints.length; j++) this.addConstraint(fieldConstraints[j].name, fieldConstraints[j].requirements, fieldConstraints[j].priority, fieldConstraints[j].isDomConstraint); } return this; }, // See `ParsleyField.getValue()` getValue: function () { // Value could be overriden in DOM if ('undefined' !== typeof this.options.value) return this.options.value; // Radio input case if (this.$element.is('input[type=radio]')) return $('[' + this.options.namespace + 'multiple="' + this.options.multiple + '"]:checked').val() || ''; // checkbox input case if (this.$element.is('input[type=checkbox]')) { var values = []; $('[' + this.options.namespace + 'multiple="' + this.options.multiple + '"]:checked').each(function () { values.push($(this).val()); }); return values.length ? values : []; } // Select multiple case if (this.$element.is('select') && null === this.$element.val()) return []; // Default case that should never happen return this.$element.val(); }, _init: function (multiple) { this.$elements = [this.$element]; this.options.multiple = multiple; return this; } }; var o = $({}), subscribed = {}; // $.listen(name, callback); // $.listen(name, context, callback); $.listen = function (name) { if ('undefined' === typeof subscribed[name]) subscribed[name] = []; if ('function' === typeof arguments[1]) return subscribed[name].push({ fn: arguments[1] }); if ('object' === typeof arguments[1] && 'function' === typeof arguments[2]) return subscribed[name].push({ fn: arguments[2], ctxt: arguments[1] }); throw new Error('Wrong parameters'); }; $.listenTo = function (instance, name, fn) { if ('undefined' === typeof subscribed[name]) subscribed[name] = []; if (!(instance instanceof ParsleyField) && !(instance instanceof ParsleyForm)) throw new Error('Must give Parsley instance'); if ('string' !== typeof name || 'function' !== typeof fn) throw new Error('Wrong parameters'); subscribed[name].push({ instance: instance, fn: fn }); }; $.unsubscribe = function (name, fn) { if ('undefined' === typeof subscribed[name]) return; if ('string' !== typeof name || 'function' !== typeof fn) throw new Error('Wrong arguments'); for (var i = 0; i < subscribed[name].length; i++) if (subscribed[name][i].fn === fn) return subscribed[name].splice(i, 1); }; $.unsubscribeTo = function (instance, name) { if ('undefined' === typeof subscribed[name]) return; if (!(instance instanceof ParsleyField) && !(instance instanceof ParsleyForm)) throw new Error('Must give Parsley instance'); for (var i = 0; i < subscribed[name].length; i++) if ('undefined' !== typeof subscribed[name][i].instance && subscribed[name][i].instance.__id__ === instance.__id__) return subscribed[name].splice(i, 1); }; $.unsubscribeAll = function (name) { if ('undefined' === typeof subscribed[name]) return; delete subscribed[name]; }; // $.emit(name [, arguments...]); // $.emit(name, instance [, arguments..]); $.emit = function (name, instance) { if ('undefined' === typeof subscribed[name]) return; // loop through registered callbacks for this event for (var i = 0; i < subscribed[name].length; i++) { // if instance is not registered, simple emit if ('undefined' === typeof subscribed[name][i].instance) { subscribed[name][i].fn.apply('undefined' !== typeof subscribed[name][i].ctxt ? subscribed[name][i].ctxt : o, Array.prototype.slice.call(arguments, 1)); continue; } // if instance registered but no instance given for the emit, continue if (!(instance instanceof ParsleyField) && !(instance instanceof ParsleyForm)) continue; // if instance is registered and same id, emit if (subscribed[name][i].instance.__id__ === instance.__id__) { subscribed[name][i].fn.apply(o, Array.prototype.slice.call(arguments, 1)); continue; } // if registered instance is a Form and fired one is a Field, loop over all its fields and emit if field found if (subscribed[name][i].instance instanceof ParsleyForm && instance instanceof ParsleyField) for (var j = 0; j < subscribed[name][i].instance.fields.length; j++) if (subscribed[name][i].instance.fields[j].__id__ === instance.__id__) { subscribed[name][i].fn.apply(o, Array.prototype.slice.call(arguments, 1)); continue; } } }; $.subscribed = function () { return subscribed; }; // ParsleyConfig definition if not already set window.ParsleyConfig = window.ParsleyConfig || {}; window.ParsleyConfig.i18n = window.ParsleyConfig.i18n || {}; // Define then the messages window.ParsleyConfig.i18n.en = $.extend(window.ParsleyConfig.i18n.en || {}, { defaultMessage: "This value seems to be invalid.", type: { email: "This value should be a valid email.", url: "This value should be a valid url.", number: "This value should be a valid number.", integer: "This value should be a valid integer.", digits: "This value should be digits.", alphanum: "This value should be alphanumeric." }, notblank: "This value should not be blank.", required: "This value is required.", pattern: "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.", length: "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.", check: "You must select between %s and %s choices.", equalto: "This value should be the same." }); // If file is loaded after Parsley main file, auto-load locale if ('undefined' !== typeof window.ParsleyValidator) window.ParsleyValidator.addCatalog('en', window.ParsleyConfig.i18n.en, true); // Parsley.js 2.0.0 // http://parsleyjs.org // (c) 20012-2014 Guillaume Potier, Wisembly // Parsley may be freely distributed under the MIT license. // ### Parsley factory var Parsley = function (element, options, parsleyFormInstance) { this.__class__ = 'Parsley'; this.__version__ = '2.0.0'; this.__id__ = ParsleyUtils.hash(4); // Parsley must be instanciated with a DOM element or jQuery $element if ('undefined' === typeof element) throw new Error('You must give an element'); if ('undefined' !== typeof parsleyFormInstance && 'ParsleyForm' !== parsleyFormInstance.__class__) throw new Error('Parent instance must be a ParsleyForm instance'); return this.init($(element), options, parsleyFormInstance); }; Parsley.prototype = { init: function ($element, options, parsleyFormInstance) { if (!$element.length) throw new Error('You must bind Parsley on an existing element.'); this.$element = $element; // If element have already been binded, returns its saved Parsley instance if (this.$element.data('Parsley')) { var savedparsleyFormInstance = this.$element.data('Parsley'); // If saved instance have been binded without a ParsleyForm parent and there is one given in this call, add it if ('undefined' !== typeof parsleyFormInstance) savedparsleyFormInstance.parent = parsleyFormInstance; return savedparsleyFormInstance; } // Handle 'static' options this.OptionsFactory = new ParsleyOptionsFactory(ParsleyDefaults, ParsleyUtils.get(window, 'ParsleyConfig') || {}, options, this.getNamespace(options)); this.options = this.OptionsFactory.get(this); // A ParsleyForm instance is obviously a `
    ` elem but also every node that is not an input and have `data-parsley-validate` attribute if (this.$element.is('form') || (ParsleyUtils.attr(this.$element, this.options.namespace, 'validate') && !this.$element.is(this.options.inputs))) return this.bind('parsleyForm'); // Every other supported element and not excluded element is binded as a `ParsleyField` or `ParsleyFieldMultiple` else if (this.$element.is(this.options.inputs) && !this.$element.is(this.options.excluded)) return this.isMultiple() ? this.handleMultiple(parsleyFormInstance) : this.bind('parsleyField', parsleyFormInstance); return this; }, isMultiple: function () { return (this.$element.is('input[type=radio], input[type=checkbox]') && 'undefined' === typeof this.options.multiple) || (this.$element.is('select') && 'undefined' !== typeof this.$element.attr('multiple')); }, // Multiples fields are a real nightmare :( // Maybe some refacto would be appreciated here.. handleMultiple: function (parsleyFormInstance) { var that = this, name, multiple, parsleyMultipleInstance; // Get parsleyFormInstance options if exist, mixed with element attributes this.options = $.extend(this.options, parsleyFormInstance ? parsleyFormInstance.OptionsFactory.get(parsleyFormInstance) : {}, ParsleyUtils.attr(this.$element, this.options.namespace)); // Handle multiple name if (this.options.multiple) { multiple = this.options.multiple; } else if ('undefined' !== typeof this.$element.attr('name') && this.$element.attr('name').length) { multiple = name = this.$element.attr('name'); } else if ('undefined' !== typeof this.$element.attr('id') && this.$element.attr('id').length) { multiple = this.$element.attr('id'); } // Special select multiple input if (this.$element.is('select') && 'undefined' !== typeof this.$element.attr('multiple')) { return this.bind('parsleyFieldMultiple', parsleyFormInstance, multiple || this.__id__); // Else for radio / checkboxes, we need a `name` or `data-parsley-multiple` to properly bind it } else if ('undefined' === typeof multiple) { if (window.console && window.console.warn) window.console.warn('To be binded by Parsley, a radio, a checkbox and a multiple select input must have either a name or a multiple option.', this.$element); return this; } // Remove special chars multiple = multiple.replace(/(:|\.|\[|\]|\$)/g, ''); // Add proper `data-parsley-multiple` to siblings if we had a name if ('undefined' !== typeof name) $('input[name="' + name + '"]').each(function () { if ($(this).is('input[type=radio], input[type=checkbox]')) $(this).attr(that.options.namespace + 'multiple', multiple); }); // Check here if we don't already have a related multiple instance saved if ($('[' + this.options.namespace + 'multiple=' + multiple +']').length) for (var i = 0; i < $('[' + this.options.namespace + 'multiple=' + multiple +']').length; i++) if ('undefined' !== typeof $($('[' + this.options.namespace + 'multiple=' + multiple +']').get(i)).data('Parsley')) { parsleyMultipleInstance = $($('[' + this.options.namespace + 'multiple=' + multiple +']').get(i)).data('Parsley'); if (!this.$element.data('ParsleyFieldMultiple')) { parsleyMultipleInstance.addElement(this.$element); this.$element.attr(this.options.namespace + 'id', parsleyMultipleInstance.__id__); } break; } // Create a secret ParsleyField instance for every multiple field. It would be stored in `data('ParsleyFieldMultiple')` // And would be useful later to access classic `ParsleyField` stuff while being in a `ParsleyFieldMultiple` instance this.bind('parsleyField', parsleyFormInstance, multiple, true); return parsleyMultipleInstance || this.bind('parsleyFieldMultiple', parsleyFormInstance, multiple); }, // Retrieve namespace used for DOM-API getNamespace: function (options) { // `data-parsley-namespace=` if ('undefined' !== typeof this.$element.data('parsleyNamespace')) return this.$element.data('parsleyNamespace'); if ('undefined' !== typeof ParsleyUtils.get(options, 'namespace')) return options.namespace; if ('undefined' !== typeof ParsleyUtils.get(window, 'ParsleyConfig.namespace')) return window.ParsleyConfig.namespace; return ParsleyDefaults.namespace; }, // Return proper `ParsleyForm`, `ParsleyField` or `ParsleyFieldMultiple` bind: function (type, parentParsleyFormInstance, multiple, doNotStore) { var parsleyInstance; switch (type) { case 'parsleyForm': parsleyInstance = $.extend( new ParsleyForm(this.$element, this.OptionsFactory), new ParsleyAbstract(), window.ParsleyExtend )._bindFields(); break; case 'parsleyField': parsleyInstance = $.extend( new ParsleyField(this.$element, this.OptionsFactory, parentParsleyFormInstance), new ParsleyAbstract(), window.ParsleyExtend ); break; case 'parsleyFieldMultiple': parsleyInstance = $.extend( new ParsleyField(this.$element, this.OptionsFactory, parentParsleyFormInstance), new ParsleyAbstract(), new ParsleyMultiple(), window.ParsleyExtend )._init(multiple); break; default: throw new Error(type + 'is not a supported Parsley type'); } if ('undefined' !== typeof multiple) ParsleyUtils.setAttr(this.$element, this.options.namespace, 'multiple', multiple); if ('undefined' !== typeof doNotStore) { this.$element.data('ParsleyFieldMultiple', parsleyInstance); return parsleyInstance; } // Store instance if `ParsleyForm`, `ParsleyField` or `ParsleyFieldMultiple` if (new RegExp('ParsleyF', 'i').test(parsleyInstance.__class__)) { // Store for later access the freshly binded instance in DOM element itself using jQuery `data()` this.$element.data('Parsley', parsleyInstance); // Tell the world we got a new ParsleyForm or ParsleyField instance! $.emit('parsley:' + ('parsleyForm' === type ? 'form' : 'field') + ':init', parsleyInstance); } return parsleyInstance; } }; // ### jQuery API // `$('.elem').parsley(options)` or `$('.elem').psly(options)` $.fn.parsley = $.fn.psly = function (options) { if (this.length > 1) { var instances = []; this.each(function () { instances.push($(this).parsley(options)); }); return instances; } // Return undefined if applied to non existing DOM element if (!$(this).length) { if (window.console && window.console.warn) window.console.warn('You must bind Parsley on an existing element.'); return; } return new Parsley(this, options); }; // ### ParsleyUI // UI is a class apart that only listen to some events and them modify DOM accordingly // Could be overriden by defining a `window.ParsleyConfig.ParsleyUI` appropriate class (with `listen()` method basically) window.ParsleyUI = 'function' === typeof ParsleyUtils.get(window, 'ParsleyConfig.ParsleyUI') ? new window.ParsleyConfig.ParsleyUI().listen() : new ParsleyUI().listen(); // ### ParsleyField and ParsleyForm extension // Ensure that defined if not already the case if ('undefined' === typeof window.ParsleyExtend) window.ParsleyExtend = {}; // ### ParsleyConfig // Ensure that defined if not already the case if ('undefined' === typeof window.ParsleyConfig) window.ParsleyConfig = {}; // ### Globals window.Parsley = window.psly = Parsley; window.ParsleyUtils = ParsleyUtils; window.ParsleyValidator = new ParsleyValidator(window.ParsleyConfig.validators, window.ParsleyConfig.i18n); // ### PARSLEY auto-binding // Prevent it by setting `ParsleyConfig.autoBind` to `false` if (false !== ParsleyUtils.get(window, 'ParsleyConfig.autoBind')) $(document).ready(function () { // Works only on `data-parsley-validate`. if ($('[data-parsley-validate]').length) $('[data-parsley-validate]').parsley(); }); // AMD Compliance if ('function' === typeof define && define.amd) define('parsley', function() { return window.Parsley; } ); })(window.jQuery);