1/*! 2* Parsleyjs 3* Guillaume Potier - <[email protected]> 4* Version 2.0.6 - built Sat Jan 24 2015 14:44:37 5* MIT Licensed 6* 7*/ 8!(function (factory) { 9 if (typeof define === 'function' && define.amd) { 10 // AMD. Register as an anonymous module depending on jQuery. 11 define(['jquery'], factory); 12 } else { 13 // No AMD. Register plugin with global jQuery object. 14 factory(jQuery); 15 } 16}(function ($) { 17 // small hack for requirejs if jquery is loaded through map and not path 18 // see http://requirejs.org/docs/jquery.html 19 if ('undefined' === typeof $ && 'undefined' !== typeof window.jQuery) 20 $ = window.jQuery; 21 var ParsleyUtils = { 22 // Parsley DOM-API 23 // returns object from dom attributes and values 24 // if attr is given, returns bool if attr present in DOM or not 25 attr: function ($element, namespace, checkAttr) { 26 var 27 attribute, 28 obj = {}, 29 msie = this.msieversion(), 30 regex = new RegExp('^' + namespace, 'i'); 31 if ('undefined' === typeof $element || 'undefined' === typeof $element[0]) 32 return {}; 33 for (var i in $element[0].attributes) { 34 attribute = $element[0].attributes[i]; 35 if ('undefined' !== typeof attribute && null !== attribute && (!msie || msie >= 8 || attribute.specified) && regex.test(attribute.name)) { 36 if ('undefined' !== typeof checkAttr && new RegExp(checkAttr + '$', 'i').test(attribute.name)) 37 return true; 38 obj[this.camelize(attribute.name.replace(namespace, ''))] = this.deserializeValue(attribute.value); 39 } 40 } 41 return 'undefined' === typeof checkAttr ? obj : false; 42 }, 43 setAttr: function ($element, namespace, attr, value) { 44 $element[0].setAttribute(this.dasherize(namespace + attr), String(value)); 45 }, 46 // Recursive object / array getter 47 get: function (obj, path) { 48 var 49 i = 0, 50 paths = (path || '').split('.'); 51 while (this.isObject(obj) || this.isArray(obj)) { 52 obj = obj[paths[i++]]; 53 if (i === paths.length) 54 return obj; 55 } 56 return undefined; 57 }, 58 hash: function (length) { 59 return String(Math.random()).substring(2, length ? length + 2 : 9); 60 }, 61 /** Third party functions **/ 62 // Underscore isArray 63 isArray: function (mixed) { 64 return Object.prototype.toString.call(mixed) === '[object Array]'; 65 }, 66 // Underscore isObject 67 isObject: function (mixed) { 68 return mixed === Object(mixed); 69 }, 70 // Zepto deserialize function 71 deserializeValue: function (value) { 72 var num; 73 try { 74 return value ? 75 value == "true" || 76 (value == "false" ? false : 77 value == "null" ? null : 78 !isNaN(num = Number(value)) ? num : 79 /^[\[\{]/.test(value) ? $.parseJSON(value) : 80 value) 81 : value; 82 } catch (e) { return value; } 83 }, 84 // Zepto camelize function 85 camelize: function (str) { 86 return str.replace(/-+(.)?/g, function(match, chr) { 87 return chr ? chr.toUpperCase() : ''; 88 }); 89 }, 90 // Zepto dasherize function 91 dasherize: function (str) { 92 return str.replace(/::/g, '/') 93 .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') 94 .replace(/([a-z\d])([A-Z])/g, '$1_$2') 95 .replace(/_/g, '-') 96 .toLowerCase(); 97 }, 98 // http://support.microsoft.com/kb/167820 99 // http://stackoverflow.com/questions/19999388/jquery-check-if-user-is-using-ie 100 msieversion: function () { 101 var 102 ua = window.navigator.userAgent, 103 msie = ua.indexOf('MSIE '); 104 if (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./)) 105 return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10); 106 return 0; 107 } 108 }; 109// All these options could be overriden and specified directly in DOM using 110// `data-parsley-` default DOM-API 111// eg: `inputs` can be set in DOM using `data-parsley-inputs="input, textarea"` 112// eg: `data-parsley-stop-on-first-failing-constraint="false"` 113 var ParsleyDefaults = { 114 // ### General 115 // Default data-namespace for DOM API
116 namespace: 'data-parsley-', 117 // Supported inputs by default 118 inputs: 'input, textarea, select', 119 // Excluded inputs by default 120 excluded: 'input[type=button], input[type=submit], input[type=reset], input[type=hidden]', 121 // Stop validating field on highest priority failing constraint 122 priorityEnabled: true, 123 // ### UI 124 // Enable\Disable error messages 125 uiEnabled: true, 126 // Key events threshold before validation 127 validationThreshold: 3, 128 // Focused field on form validation error. 'fist'|'last'|'none' 129 focus: 'first', 130 // `$.Event()` that will trigger validation. eg: `keyup`, `change`... 131 trigger: false, 132 // Class that would be added on every failing validation Parsley field 133 errorClass: 'parsley-error', 134 // Same for success validation 135 successClass: 'parsley-success', 136 // Return the `$element` that will receive these above success or error classes 137 // Could also be (and given directly from DOM) a valid selector like `'#div'` 138 classHandler: function (ParsleyField) {}, 139 // Return the `$element` where errors will be appended 140 // Could also be (and given directly from DOM) a valid selector like `'#div'` 141 errorsContainer: function (ParsleyField) {}, 142 // ul elem that would receive errors' list 143 errorsWrapper: '<ul class="parsley-errors-list"></ul>', 144 // li elem that would receive error message 145 errorTemplate: '<li></li>' 146 }; 147 148 var ParsleyAbstract = function() {}; 149 ParsleyAbstract.prototype = { 150 asyncSupport: false, 151 actualizeOptions: function () { 152 this.options = this.OptionsFactory.get(this); 153 return this; 154 }, 155 // ParsleyValidator validate proxy function . Could be replaced by third party scripts 156 validateThroughValidator: function (value, constraints, priority) { 157 return window.ParsleyValidator.validate(value, constraints, priority); 158 }, 159 // Subscribe an event and a handler for a specific field or a specific form 160 // If on a ParsleyForm instance, it will be attached to form instance and also 161 // To every field instance for this form 162 subscribe: function (name, fn) { 163 $.listenTo(this, name.toLowerCase(), fn); 164 return this; 165 }, 166 // Same as subscribe above. Unsubscribe an event for field, or form + its fields 167 unsubscribe: function (name) { 168 $.unsubscribeTo(this, name.toLowerCase()); 169 return this; 170 }, 171 // Reset UI 172 reset: function () { 173 // Field case: just emit a reset event for UI 174 if ('ParsleyForm' !== this.__class__) 175 return $.emit('parsley:field:reset', this); 176 // Form case: emit a reset event for each field 177 for (var i = 0; i < this.fields.length; i++) 178 $.emit('parsley:field:reset', this.fields[i]); 179 $.emit('parsley:form:reset', this); 180 }, 181 // Destroy Parsley instance (+ UI) 182 destroy: function () { 183 // Field case: emit destroy event to clean UI and then destroy stored instance 184 if ('ParsleyForm' !== this.__class__) { 185 this.$element.removeData('Parsley'); 186 this.$element.removeData('ParsleyFieldMultiple'); 187 $.emit('parsley:field:destroy', this); 188 return; 189 } 190 // Form case: destroy all its fields and then destroy stored instance 191 for (var i = 0; i < this.fields.length; i++) 192 this.fields[i].destroy(); 193 this.$element.removeData('Parsley'); 194 $.emit('parsley:form:destroy', this); 195 } 196 }; 197/*! 198* validator.js 199* Guillaume Potier - <[email protected]> 200* Version 1.0.1 - built Mon Aug 25 2014 16:10:10 201* MIT Licensed 202* 203*/ 204var Validator = ( function ( ) { 205 var exports = {}; 206 /** 207 * Validator 208 */ 209 var Validator = function ( options ) { 210 this.__class__ = 'Validator'; 211 this.__version__ = '1.0.1'; 212 this.options = options || {}; 213 this.bindingKey = this.options.bindingKey || '_validatorjsConstraint'; 214 }; 215 Validator.prototype = { 216 constructor: Validator, 217 /* 218 * Validate string: validate( string, Assert, string ) || validate( string, [ Assert, Assert ], [ string, string ] ) 219 * Validate object: validate( object, Constraint, string ) || validate( object, Constraint, [ string, string ] ) 220 * Validate binded object: validate( object, string ) || validate( object, [ string, string ] ) 221 */ 222 validate: function ( objectOrString, AssertsOrConstraintOrGroup, group ) { 223 if ( 'string' !== typeof objectOrString && 'object' !== typeof objectOrString ) 224 throw new Error( 'You must validate an object or a string' ); 225 // string / array validation 226 if ( 'string' === typeof objectOrString || _isArray(objectOrString) )
227 return this._validateString( objectOrString, AssertsOrConstraintOrGroup, group ); 228 // binded object validation 229 if ( this.isBinded( objectOrString ) ) 230 return this._validateBindedObject( objectOrString, AssertsOrConstraintOrGroup ); 231 // regular object validation 232 return this._validateObject( objectOrString, AssertsOrConstraintOrGroup, group ); 233 }, 234 bind: function ( object, constraint ) { 235 if ( 'object' !== typeof object ) 236 throw new Error( 'Must bind a Constraint to an object' ); 237 object[ this.bindingKey ] = new Constraint( constraint ); 238 return this; 239 }, 240 unbind: function ( object ) { 241 if ( 'undefined' === typeof object._validatorjsConstraint ) 242 return this; 243 delete object[ this.bindingKey ]; 244 return this; 245 }, 246 isBinded: function ( object ) { 247 return 'undefined' !== typeof object[ this.bindingKey ]; 248 }, 249 getBinded: function ( object ) { 250 return this.isBinded( object ) ? object[ this.bindingKey ] : null; 251 }, 252 _validateString: function ( string, assert, group ) { 253 var result, failures = []; 254 if ( !_isArray( assert ) ) 255 assert = [ assert ]; 256 for ( var i = 0; i < assert.length; i++ ) { 257 if ( ! ( assert[ i ] instanceof Assert) ) 258 throw new Error( 'You must give an Assert or an Asserts array to validate a string' ); 259 result = assert[ i ].check( string, group ); 260 if ( result instanceof Violation ) 261 failures.push( result ); 262 } 263 return failures.length ? failures : true; 264 }, 265 _validateObject: function ( object, constraint, group ) { 266 if ( 'object' !== typeof constraint ) 267 throw new Error( 'You must give a constraint to validate an object' ); 268 if ( constraint instanceof Constraint ) 269 return constraint.check( object, group ); 270 return new Constraint( constraint ).check( object, group ); 271 }, 272 _validateBindedObject: function ( object, group ) { 273 return object[ this.bindingKey ].check( object, group ); 274 } 275 }; 276 Validator.errorCode = { 277 must_be_a_string: 'must_be_a_string', 278 must_be_an_array: 'must_be_an_array', 279 must_be_a_number: 'must_be_a_number', 280 must_be_a_string_or_array: 'must_be_a_string_or_array' 281 }; 282 /** 283 * Constraint 284 */ 285 var Constraint = function ( data, options ) { 286 this.__class__ = 'Constraint'; 287 this.options = options || {}; 288 this.nodes = {}; 289 if ( data ) { 290 try { 291 this._bootstrap( data ); 292 } catch ( err ) { 293 throw new Error( 'Should give a valid mapping object to Constraint', err, data ); 294 } 295 } 296 }; 297 Constraint.prototype = { 298 constructor: Constraint, 299 check: function ( object, group ) { 300 var result, failures = {}; 301 // check all constraint nodes. 302 for ( var property in this.nodes ) { 303 var isRequired = false; 304 var constraint = this.get(property); 305 var constraints = _isArray( constraint ) ? constraint : [constraint]; 306 for (var i = constraints.length - 1; i >= 0; i--) { 307 if ( 'Required' === constraints[i].__class__ ) { 308 isRequired = constraints[i].requiresValidation( group ); 309 continue; 310 } 311 } 312 if ( ! this.has( property, object ) && ! this.options.strict && ! isRequired ) { 313 continue; 314 } 315 try { 316 if (! this.has( property, this.options.strict || isRequired ? object : undefined ) ) { 317 // we trigger here a HaveProperty Assert violation to have uniform Violation object in the end 318 new Assert().HaveProperty( property ).validate( object ); 319 } 320 result = this._check( property, object[ property ], group ); 321 // check returned an array of Violations or an object mapping Violations 322 if ( ( _isArray( result ) && result.length > 0 ) || ( !_isArray( result ) && !_isEmptyObject( result ) ) ) { 323 failures[ property ] = result; 324 } 325 } catch ( violation ) { 326 failures[ property ] = violation; 327 } 328 } 329 return _isEmptyObject(failures) ? true : failures; 330 }, 331 add: function ( node, object ) { 332 if ( object instanceof Assert || ( _isArray( object ) && object[ 0 ] instanceof Assert ) ) { 333 this.nodes[ node ] = object; 334 return this; 335 } 336 if ( 'object' === typeof object && !_isArray( object ) ) { 337 this.nodes[ node ] = object instanceof Constraint ? object : new Constraint( object ); 338 return this; 339 } 340 throw new Error( 'Should give an Assert, an Asserts array, a Constraint', object ); 341 }, 342 has: function ( node, nodes ) { 343 nodes = 'undefined' !== typeof nodes ? nodes : this.nodes; 344 return 'undefined' !== typeof nodes[ node ]; 345 }, 346 get: function ( node, placeholder ) { 347 return this.has( node ) ? this.nodes[ node ] : placeholder || null; 348 }, 349 remove: function ( node ) { 350 var _nodes = []; 351 for ( var i in this.nodes ) 352 if ( i !== node ) 353 _nodes[ i ] = this.nodes[ i ]; 354 this.nodes = _nodes; 355 return this; 356 }, 357 _bootstrap: function ( data ) { 358 if ( data instanceof Constraint ) 359 return this.nodes = data.nodes; 360 for ( var node in data ) 361 this.add( node, data[ node ] ); 362 }, 363 _check: function ( node, value, group ) { 364 // Assert 365 if ( this.nodes[ node ] instanceof Assert ) 366 return this._checkAsserts( value, [ this.nodes[ node ] ], group ); 367 // Asserts 368 if ( _isArray( this.nodes[ node ] ) ) 369 return this._checkAsserts( value, this.nodes[ node ], group ); 370 // Constraint -> check api 371 if ( this.nodes[ node ] instanceof Constraint ) 372 return this.nodes[ node ].check( value, group ); 373 throw new Error( 'Invalid node', this.nodes[ node ] ); 374 }, 375 _checkAsserts: function ( value, asserts, group ) { 376 var result, failures = []; 377 for ( var i = 0; i < asserts.length; i++ ) { 378 result = asserts[ i ].check( value, group ); 379 if ( 'undefined' !== typeof result && true !== result ) 380 failures.push( result ); 381 // Some asserts (Collection for example) could return an object 382 // if ( result && ! ( result instanceof Violation ) ) 383 // return result; 384 // 385 // // Vast assert majority return Violation 386 // if ( result instanceof Violation ) 387 // failures.push( result ); 388 } 389 return failures; 390 } 391 }; 392 /** 393 * Violation 394 */ 395 var Violation = function ( assert, value, violation ) { 396 this.__class__ = 'Violation'; 397 if ( ! ( assert instanceof Assert ) ) 398 throw new Error( 'Should give an assertion implementing the Assert interface' ); 399 this.assert = assert; 400 this.value = value; 401 if ( 'undefined' !== typeof violation ) 402 this.violation = violation; 403 }; 404 Violation.prototype = { 405 show: function () { 406 var show = { 407 assert: this.assert.__class__, 408 value: this.value 409 }; 410 if ( this.violation ) 411 show.violation = this.violation; 412 return show; 413 }, 414 __toString: function () { 415 if ( 'undefined' !== typeof this.violation ) 416 this.violation = '", ' + this.getViolation().constraint + ' expected was ' + this.getViolation().expected; 417 return this.assert.__class__ + ' assert failed for "' + this.value + this.violation || ''; 418 }, 419 getViolation: function () { 420 var constraint, expected; 421 for ( constraint in this.violation ) 422 expected = this.violation[ constraint ]; 423 return { constraint: constraint, expected: expected }; 424 } 425 }; 426 /** 427 * Assert 428 */ 429 var Assert = function ( group ) { 430 this.__class__ = 'Assert'; 431 this.__parentClass__ = this.__class__; 432 this.groups = []; 433 if ( 'undefined' !== typeof group ) 434 this.addGroup( group ); 435 }; 436 Assert.prototype = { 437 construct: Assert, 438 requiresValidation: function ( group ) { 439 if ( group && !this.hasGroup( group ) ) 440 return false; 441 if ( !group && this.hasGroups() ) 442 return false; 443 return true; 444 }, 445 check: function ( value, group ) { 446 if ( !this.requiresValidation( group ) ) 447 return;
448 try { 449 return this.validate( value, group ); 450 } catch ( violation ) { 451 return violation; 452 } 453 }, 454 hasGroup: function ( group ) { 455 if ( _isArray( group ) ) 456 return this.hasOneOf( group ); 457 // All Asserts respond to "Any" group 458 if ( 'Any' === group ) 459 return true; 460 // Asserts with no group also respond to "Default" group. Else return false 461 if ( !this.hasGroups() ) 462 return 'Default' === group; 463 return -1 !== this.groups.indexOf( group ); 464 }, 465 hasOneOf: function ( groups ) { 466 for ( var i = 0; i < groups.length; i++ ) 467 if ( this.hasGroup( groups[ i ] ) ) 468 return true; 469 return false; 470 }, 471 hasGroups: function () { 472 return this.groups.length > 0; 473 }, 474 addGroup: function ( group ) { 475 if ( _isArray( group ) ) 476 return this.addGroups( group ); 477 if ( !this.hasGroup( group ) ) 478 this.groups.push( group ); 479 return this; 480 }, 481 removeGroup: function ( group ) { 482 var _groups = []; 483 for ( var i = 0; i < this.groups.length; i++ ) 484 if ( group !== this.groups[ i ] ) 485 _groups.push( this.groups[ i ] ); 486 this.groups = _groups; 487 return this; 488 }, 489 addGroups: function ( groups ) { 490 for ( var i = 0; i < groups.length; i++ ) 491 this.addGroup( groups[ i ] ); 492 return this; 493 }, 494 /** 495 * Asserts definitions 496 */ 497 HaveProperty: function ( node ) { 498 this.__class__ = 'HaveProperty'; 499 this.node = node; 500 this.validate = function ( object ) { 501 if ( 'undefined' === typeof object[ this.node ] ) 502 throw new Violation( this, object, { value: this.node } ); 503 return true; 504 }; 505 return this; 506 }, 507 Blank: function () { 508 this.__class__ = 'Blank'; 509 this.validate = function ( value ) { 510 if ( 'string' !== typeof value ) 511 throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string } ); 512 if ( '' !== value.replace( /^\s+/g, '' ).replace( /\s+$/g, '' ) ) 513 throw new Violation( this, value ); 514 return true; 515 }; 516 return this; 517 }, 518 Callback: function ( fn ) { 519 this.__class__ = 'Callback'; 520 this.arguments = Array.prototype.slice.call( arguments ); 521 if ( 1 === this.arguments.length ) 522 this.arguments = []; 523 else 524 this.arguments.splice( 0, 1 ); 525 if ( 'function' !== typeof fn ) 526 throw new Error( 'Callback must be instanciated with a function' ); 527 this.fn = fn; 528 this.validate = function ( value ) { 529 var result = this.fn.apply( this, [ value ].concat( this.arguments ) ); 530 if ( true !== result ) 531 throw new Violation( this, value, { result: result } ); 532 return true; 533 }; 534 return this; 535 }, 536 Choice: function ( list ) { 537 this.__class__ = 'Choice'; 538 if ( !_isArray( list ) && 'function' !== typeof list ) 539 throw new Error( 'Choice must be instanciated with an array or a function' ); 540 this.list = list; 541 this.validate = function ( value ) { 542 var list = 'function' === typeof this.list ? this.list() : this.list; 543 for ( var i = 0; i < list.length; i++ ) 544 if ( value === list[ i ] ) 545 return true; 546 throw new Violation( this, value, { choices: list } ); 547 }; 548 return this; 549 }, 550 Collection: function ( assertOrConstraint ) { 551 this.__class__ = 'Collection'; 552 this.constraint = 'undefined' !== typeof assertOrConstraint ? (assertOrConstraint instanceof Assert ? assertOrConstraint : new Constraint( assertOrConstraint )) : false; 553 this.validate = function ( collection, group ) { 554 var result, validator = new Validator(), count = 0, failures = {}, groups = this.groups.length ? this.groups : group; 555 if ( !_isArray( collection ) ) 556 throw new Violation( this, collection, { value: Validator.errorCode.must_be_an_array } ); 557 for ( var i = 0; i < collection.length; i++ ) { 558 result = this.constraint ? 559 validator.validate( collection[ i ], this.constraint, groups ) : 560 validator.validate( collection[ i ], groups ); 561 if ( !_isEmptyObject( result ) ) 562 failures[ count ] = result; 563 count++; 564 } 565 return !_isEmptyObject( failures ) ? failures : true; 566 }; 567 return this; 568 }, 569 Count: function ( count ) { 570 this.__class__ = 'Count'; 571 this.count = count; 572 this.validate = function ( array ) { 573 if ( !_isArray( array ) ) 574 throw new Violation( this, array, { value: Validator.errorCode.must_be_an_array } ); 575 var count = 'function' === typeof this.count ? this.count( array ) : this.count; 576 if ( isNaN( Number( count ) ) )
577 throw new Error( 'Count must be a valid interger', count ); 578 if ( count !== array.length ) 579 throw new Violation( this, array, { count: count } ); 580 return true; 581 }; 582 return this; 583 }, 584 Email: function () { 585 this.__class__ = 'Email'; 586 this.validate = function ( value ) { 587 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; 588 if ( 'string' !== typeof value ) 589 throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string } ); 590 if ( !regExp.test( value ) ) 591 throw new Violation( this, value ); 592 return true; 593 }; 594 return this; 595 }, 596 EqualTo: function ( reference ) { 597 this.__class__ = 'EqualTo'; 598 if ( 'undefined' === typeof reference ) 599 throw new Error( 'EqualTo must be instanciated with a value or a function' ); 600 this.reference = reference; 601 this.validate = function ( value ) { 602 var reference = 'function' === typeof this.reference ? this.reference( value ) : this.reference; 603 if ( reference !== value ) 604 throw new Violation( this, value, { value: reference } ); 605 return true; 606 }; 607 return this; 608 }, 609 GreaterThan: function ( threshold ) { 610 this.__class__ = 'GreaterThan'; 611 if ( 'undefined' === typeof threshold ) 612 throw new Error( 'Should give a threshold value' ); 613 this.threshold = threshold; 614 this.validate = function ( value ) { 615 if ( '' === value || isNaN( Number( value ) ) ) 616 throw new Violation( this, value, { value: Validator.errorCode.must_be_a_number } ); 617 if ( this.threshold >= value ) 618 throw new Violation( this, value, { threshold: this.threshold } ); 619 return true; 620 }; 621 return this; 622 }, 623 GreaterThanOrEqual: function ( threshold ) { 624 this.__class__ = 'GreaterThanOrEqual'; 625 if ( 'undefined' === typeof threshold ) 626 throw new Error( 'Should give a threshold value' ); 627 this.threshold = threshold; 628 this.validate = function ( value ) { 629 if ( '' === value || isNaN( Number( value ) ) ) 630 throw new Violation( this, value, { value: Validator.errorCode.must_be_a_number } ); 631 if ( this.threshold > value ) 632 throw new Violation( this, value, { threshold: this.threshold } ); 633 return true; 634 }; 635 return this; 636 }, 637 InstanceOf: function ( classRef ) { 638 this.__class__ = 'InstanceOf'; 639 if ( 'undefined' === typeof classRef ) 640 throw new Error( 'InstanceOf must be instanciated with a value' ); 641 this.classRef = classRef; 642 this.validate = function ( value ) { 643 if ( true !== (value instanceof this.classRef) ) 644 throw new Violation( this, value, { classRef: this.classRef } ); 645 return true; 646 }; 647 return this; 648 }, 649 Length: function ( boundaries ) { 650 this.__class__ = 'Length'; 651 if ( !boundaries.min && !boundaries.max ) 652 throw new Error( 'Lenth assert must be instanciated with a { min: x, max: y } object' ); 653 this.min = boundaries.min; 654 this.max = boundaries.max; 655 this.validate = function ( value ) { 656 if ( 'string' !== typeof value && !_isArray( value ) ) 657 throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string_or_array } ); 658 if ( 'undefined' !== typeof this.min && this.min === this.max && value.length !== this.min ) 659 throw new Violation( this, value, { min: this.min, max: this.max } ); 660 if ( 'undefined' !== typeof this.max && value.length > this.max ) 661 throw new Violation( this, value, { max: this.max } ); 662 if ( 'undefined' !== typeof this.min && value.length < this.min ) 663 throw new Violation( this, value, { min: this.min } ); 664 return true; 665 }; 666 return this; 667 }, 668 LessThan: function ( threshold ) { 669 this.__class__ = 'LessThan'; 670 if ( 'undefined' === typeof threshold ) 671 throw new Error( 'Should give a threshold value' ); 672 this.threshold = threshold; 673 this.validate = function ( value ) { 674 if ( '' === value || isNaN( Number( value ) ) ) 675 throw new Violation( this, value, { value: Validator.errorCode.must_be_a_number } ); 676 if ( this.threshold <= value ) 677 throw new Violation( this, value, { threshold: this.threshold } ); 678 return true; 679 }; 680 return this; 681 }, 682 LessThanOrEqual: function ( threshold ) { 683 this.__class__ = 'LessThanOrEqual'; 684 if ( 'undefined' === typeof threshold ) 685 throw new Error( 'Should give a threshold value' ); 686 this.threshold = threshold; 687 this.validate = function ( value ) { 688 if ( '' === value || isNaN( Number( value ) ) ) 689 throw new Violation( this, value, { value: Validator.errorCode.must_be_a_number } ); 690 if ( this.threshold < value ) 691 throw new Violation( this, value, { threshold: this.threshold } ); 692 return true; 693 }; 694 return this; 695 }, 696 NotNull: function () { 697 this.__class__ = 'NotNull'; 698 this.validate = function ( value ) { 699 if ( null === value || 'undefined' === typeof value ) 700 throw new Violation( this, value ); 701 return true; 702 }; 703 return this; 704 }, 705 NotBlank: function () { 706 this.__class__ = 'NotBlank'; 707 this.validate = function ( value ) { 708 if ( 'string' !== typeof value ) 709 throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string } ); 710 if ( '' === value.replace( /^\s+/g, '' ).replace( /\s+$/g, '' ) ) 711 throw new Violation( this, value ); 712 return true; 713 }; 714 return this; 715 }, 716 Null: function () { 717 this.__class__ = 'Null'; 718 this.validate = function ( value ) { 719 if ( null !== value ) 720 throw new Violation( this, value ); 721 return true; 722 }; 723 return this; 724 }, 725 Range: function ( min, max ) { 726 this.__class__ = 'Range'; 727 if ( 'undefined' === typeof min || 'undefined' === typeof max ) 728 throw new Error( 'Range assert expects min and max values' ); 729 this.min = min; 730 this.max = max; 731 this.validate = function ( value ) { 732 try { 733 // validate strings and objects with their Length 734 if ( ( 'string' === typeof value && isNaN( Number( value ) ) ) || _isArray( value ) ) 735 new Assert().Length( { min: this.min, max: this.max } ).validate( value ); 736 // validate numbers with their value 737 else 738 new Assert().GreaterThanOrEqual( this.min ).validate( value ) && new Assert().LessThanOrEqual( this.max ).validate( value ); 739 return true; 740 } catch ( violation ) { 741 throw new Violation( this, value, violation.violation ); 742 } 743 return true; 744 }; 745 return this; 746 }, 747 Regexp: function ( regexp, flag ) { 748 this.__class__ = 'Regexp'; 749 if ( 'undefined' === typeof regexp ) 750 throw new Error( 'You must give a regexp' ); 751 this.regexp = regexp; 752 this.flag = flag || ''; 753 this.validate = function ( value ) { 754 if ( 'string' !== typeof value ) 755 throw new Violation( this, value, { value: Validator.errorCode.must_be_a_string } ); 756 if ( !new RegExp( this.regexp, this.flag ).test( value ) ) 757 throw new Violation( this, value, { regexp: this.regexp, flag: this.flag } ); 758 return true; 759 }; 760 return this; 761 }, 762 Required: function () { 763 this.__class__ = 'Required'; 764 this.validate = function ( value ) { 765 if ( 'undefined' === typeof value ) 766 throw new Violation( this, value ); 767 try { 768 if ( 'string' === typeof value ) 769 new Assert().NotNull().validate( value ) && new Assert().NotBlank().validate( value ); 770 else if ( true === _isArray( value ) ) 771 new Assert().Length( { min: 1 } ).validate( value ); 772 } catch ( violation ) { 773 throw new Violation( this, value ); 774 } 775 return true; 776 }; 777 return this; 778 }, 779 // Unique() or Unique ( { key: foo } ) 780 Unique: function ( object ) { 781 this.__class__ = 'Unique'; 782 if ( 'object' === typeof object ) 783 this.key = object.key; 784 this.validate = function ( array ) { 785 var value, store = []; 786 if ( !_isArray( array ) ) 787 throw new Violation( this, array, { value: Validator.errorCode.must_be_an_array } ); 788 for ( var i = 0; i < array.length; i++ ) { 789 value = 'object' === typeof array[ i ] ? array[ i ][ this.key ] : array[ i ]; 790 if ( 'undefined' === typeof value ) 791 continue; 792 if ( -1 !== store.indexOf( value ) ) 793 throw new Violation( this, array, { value: value } ); 794 store.push( value ); 795 } 796 return true; 797 }; 798 return this; 799 } 800 }; 801 // expose to the world these awesome classes 802 exports.Assert = Assert; 803 exports.Validator = Validator; 804 exports.Violation = Violation; 805 exports.Constraint = Constraint; 806 /** 807 * Some useful object prototypes / functions here 808 */ 809 // IE8<= compatibility 810 // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf 811 if (!Array.prototype.indexOf) 812 Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { 813 814 if (this === null) { 815 throw new TypeError(); 816 } 817 var t = Object(this); 818 var len = t.length >>> 0; 819 if (len === 0) { 820 return -1; 821 } 822 var n = 0; 823 if (arguments.length > 1) { 824 n = Number(arguments[1]);
825 if (n != n) { // shortcut for verifying if it's NaN 826 n = 0; 827 } else if (n !== 0 && n != Infinity && n != -Infinity) { 828 n = (n > 0 || -1) * Math.floor(Math.abs(n)); 829 } 830 } 831 if (n >= len) { 832 return -1; 833 } 834 var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); 835 for (; k < len; k++) { 836 if (k in t && t[k] === searchElement) { 837 return k; 838 } 839 } 840 return -1; 841 }; 842 // Test if object is empty, useful for Constraint violations check 843 var _isEmptyObject = function ( obj ) { 844 for ( var property in obj ) 845 return false; 846 return true; 847 }; 848 var _isArray = function ( obj ) { 849 return Object.prototype.toString.call( obj ) === '[object Array]'; 850 }; 851 // AMD export 852 if ( typeof define === 'function' && define.amd ) { 853 define( 'vendors/validator.js/dist/validator',[],function() { 854 return exports; 855 } ); 856 // commonjs export 857 } else if ( typeof module !== 'undefined' && module.exports ) { 858 module.exports = exports; 859 // browser 860 } else { 861 window[ 'undefined' !== typeof validatorjs_ns ? validatorjs_ns : 'Validator' ] = exports; 862 } 863 864 return exports; 865} )( ); 866 867 // This is needed for Browserify usage that requires Validator.js through module.exports 868 Validator = 'undefined' !== typeof Validator ? Validator : ('undefined' !== typeof module ? module.exports : null); 869 var ParsleyValidator = function (validators, catalog) { 870 this.__class__ = 'ParsleyValidator'; 871 this.Validator = Validator; 872 // Default Parsley locale is en 873 this.locale = 'en'; 874 this.init(validators || {}, catalog || {}); 875 }; 876 ParsleyValidator.prototype = { 877 init: function (validators, catalog) { 878 this.catalog = catalog; 879 for (var name in validators) 880 this.addValidator(name, validators[name].fn, validators[name].priority, validators[name].requirementsTransformer); 881 $.emit('parsley:validator:init'); 882 }, 883 // Set new messages locale if we have dictionary loaded in ParsleyConfig.i18n 884 setLocale: function (locale) { 885 if ('undefined' === typeof this.catalog[locale]) 886 throw new Error(locale + ' is not available in the catalog'); 887 this.locale = locale; 888 return this; 889 }, 890 // Add a new messages catalog for a given locale. Set locale for this catalog if set === `true` 891 addCatalog: function (locale, messages, set) { 892 if ('object' === typeof messages) 893 this.catalog[locale] = messages; 894 if (true === set) 895 return this.setLocale(locale); 896 return this; 897 }, 898 // Add a specific message for a given constraint in a given locale 899 addMessage: function (locale, name, message) { 900 if ('undefined' === typeof this.catalog[locale]) 901 this.catalog[locale] = {}; 902 this.catalog[locale][name.toLowerCase()] = message; 903 return this; 904 }, 905 validate: function (value, constraints, priority) { 906 return new this.Validator.Validator().validate.apply(new Validator.Validator(), arguments); 907 }, 908 // Add a new validator 909 addValidator: function (name, fn, priority, requirementsTransformer) { 910 this.validators[name.toLowerCase()] = function (requirements) { 911 return $.extend(new Validator.Assert().Callback(fn, requirements), { 912 priority: priority, 913 requirementsTransformer: requirementsTransformer 914 }); 915 }; 916 return this; 917 }, 918 updateValidator: function (name, fn, priority, requirementsTransformer) { 919 return this.addValidator(name, fn, priority, requirementsTransformer); 920 }, 921 removeValidator: function (name) { 922 delete this.validators[name]; 923 return this; 924 }, 925 getErrorMessage: function (constraint) { 926 var message; 927 // Type constraints are a bit different, we have to match their requirements too to find right error message 928 if ('type' === constraint.name)
929 message = this.catalog[this.locale][constraint.name][constraint.requirements]; 930 else 931 message = this.formatMessage(this.catalog[this.locale][constraint.name], constraint.requirements); 932 return '' !== message ? message : this.catalog[this.locale].defaultMessage; 933 }, 934 // Kind of light `sprintf()` implementation 935 formatMessage: function (string, parameters) { 936 if ('object' === typeof parameters) { 937 for (var i in parameters) 938 string = this.formatMessage(string, parameters[i]); 939 return string; 940 } 941 return 'string' === typeof string ? string.replace(new RegExp('%s', 'i'), parameters) : ''; 942 }, 943 // Here is the Parsley default validators list. 944 // This is basically Validatorjs validators, with different API for some of them 945 // and a Parsley priority set 946 validators: { 947 notblank: function () { 948 return $.extend(new Validator.Assert().NotBlank(), { priority: 2 }); 949 }, 950 required: function () { 951 return $.extend(new Validator.Assert().Required(), { priority: 512 }); 952 }, 953 type: function (type) { 954 var assert; 955 switch (type) { 956 case 'email': 957 assert = new Validator.Assert().Email(); 958 break; 959 // range type just ensure we have a number here 960 case 'range': 961 case 'number': 962 assert = new Validator.Assert().Regexp('^-?(?:\\d+|\\d{1,3}(?:,\\d{3})+)?(?:\\.\\d+)?$'); 963 break; 964 case 'integer': 965 assert = new Validator.Assert().Regexp('^-?\\d+$'); 966 break; 967 case 'digits': 968 assert = new Validator.Assert().Regexp('^\\d+$'); 969 break; 970 case 'alphanum': 971 assert = new Validator.Assert().Regexp('^\\w+$', 'i'); 972 break; 973 case 'url': 974 assert = new Validator.Assert().Regexp('(https?:\\/\\/)?(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,24}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)', 'i'); 975 break; 976 default: 977 throw new Error('validator type `' + type + '` is not supported'); 978 } 979 return $.extend(assert, { priority: 256 }); 980 }, 981 pattern: function (regexp) { 982 var flags = ''; 983 // Test if RegExp is literal, if not, nothing to be done, otherwise, we need to isolate flags and pattern 984 if (!!(/^\/.*\/(?:[gimy]*)$/.test(regexp))) { 985 // Replace the regexp literal string with the first match group: ([gimy]*) 986 // If no flag is present, this will be a blank string 987 flags = regexp.replace(/.*\/([gimy]*)$/, '$1'); 988 // Again, replace the regexp literal string with the first match group: 989 // everything excluding the opening and closing slashes and the flags 990 regexp = regexp.replace(new RegExp('^/(.*?)/' + flags + '$'), '$1'); 991 } 992 return $.extend(new Validator.Assert().Regexp(regexp, flags), { priority: 64 });
993 }, 994 minlength: function (value) { 995 return $.extend(new Validator.Assert().Length({ min: value }), { 996 priority: 30, 997 requirementsTransformer: function () { 998 return 'string' === typeof value && !isNaN(value) ? parseInt(value, 10) : value; 999 } 1000 }); 1001 }, 1002 maxlength: function (value) { 1003 return $.extend(new Validator.Assert().Length({ max: value }), { 1004 priority: 30, 1005 requirementsTransformer: function () { 1006 return 'string' === typeof value && !isNaN(value) ? parseInt(value, 10) : value; 1007 } 1008 }); 1009 }, 1010 length: function (array) { 1011 return $.extend(new Validator.Assert().Length({ min: array[0], max: array[1] }), { priority: 32 }); 1012 }, 1013 mincheck: function (length) { 1014 return this.minlength(length); 1015 }, 1016 maxcheck: function (length) { 1017 return this.maxlength(length); 1018 }, 1019 check: function (array) { 1020 return this.length(array); 1021 }, 1022 min: function (value) { 1023 return $.extend(new Validator.Assert().GreaterThanOrEqual(value), { 1024 priority: 30, 1025 requirementsTransformer: function () { 1026 return 'string' === typeof value && !isNaN(value) ? parseInt(value, 10) : value; 1027 } 1028 }); 1029 }, 1030 max: function (value) { 1031 return $.extend(new Validator.Assert().LessThanOrEqual(value), { 1032 priority: 30, 1033 requirementsTransformer: function () { 1034 return 'string' === typeof value && !isNaN(value) ? parseInt(value, 10) : value; 1035 } 1036 }); 1037 }, 1038 range: function (array) { 1039 return $.extend(new Validator.Assert().Range(array[0], array[1]), { 1040 priority: 32, 1041 requirementsTransformer: function () { 1042 for (var i = 0; i < array.length; i++) 1043 array[i] = 'string' === typeof array[i] && !isNaN(array[i]) ? parseInt(array[i], 10) : array[i]; 1044 return array; 1045 } 1046 }); 1047 }, 1048 equalto: function (value) { 1049 return $.extend(new Validator.Assert().EqualTo(value), { 1050 priority: 256, 1051 requirementsTransformer: function () { 1052 return $(value).length ? $(value).val() : value; 1053 } 1054 }); 1055 } 1056 } 1057 }; 1058 1059 var ParsleyUI = function (options) { 1060 this.__class__ = 'ParsleyUI'; 1061 }; 1062 ParsleyUI.prototype = { 1063 listen: function () { 1064 $.listen('parsley:form:init', this, this.setupForm); 1065 $.listen('parsley:field:init', this, this.setupField); 1066 $.listen('parsley:field:validated', this, this.reflow); 1067 $.listen('parsley:form:validated', this, this.focus); 1068 $.listen('parsley:field:reset', this, this.reset); 1069 $.listen('parsley:form:destroy', this, this.destroy); 1070 $.listen('parsley:field:destroy', this, this.destroy); 1071 return this; 1072 }, 1073 reflow: function (fieldInstance) { 1074 // If this field has not an active UI (case for multiples) don't bother doing something 1075 if ('undefined' === typeof fieldInstance._ui || false === fieldInstance._ui.active) 1076 return; 1077 // Diff between two validation results 1078 var diff = this._diff(fieldInstance.validationResult, fieldInstance._ui.lastValidationResult); 1079 // Then store current validation result for next reflow 1080 fieldInstance._ui.lastValidationResult = fieldInstance.validationResult; 1081 // Field have been validated at least once if here. Useful for binded key events... 1082 fieldInstance._ui.validatedOnce = true; 1083 // Handle valid / invalid / none field class 1084 this.manageStatusClass(fieldInstance); 1085 // Add, remove, updated errors messages 1086 this.manageErrorsMessages(fieldInstance, diff); 1087 // Triggers impl 1088 this.actualizeTriggers(fieldInstance); 1089 // If field is not valid for the first time, bind keyup trigger to ease UX and quickly inform user 1090 if ((diff.kept.length || diff.added.length) && 'undefined' === typeof fieldInstance._ui.failedOnce) 1091 this.manageFailingFieldTrigger(fieldInstance); 1092 }, 1093 // Returns an array of field's error message(s) 1094 getErrorsMessages: function (fieldInstance) { 1095 // No error message, field is valid 1096 if (true === fieldInstance.validationResult) 1097 return []; 1098 var messages = []; 1099 for (var i = 0; i < fieldInstance.validationResult.length; i++) 1100 messages.push(this._getErrorMessage(fieldInstance, fieldInstance.validationResult[i].assert)); 1101 return messages; 1102 }, 1103 manageStatusClass: function (fieldInstance) { 1104 if (true === fieldInstance.validationResult) 1105 this._successClass(fieldInstance); 1106 else if (fieldInstance.validationResult.length > 0) 1107 this._errorClass(fieldInstance); 1108 else 1109 this._resetClass(fieldInstance); 1110 }, 1111 manageErrorsMessages: function (fieldInstance, diff) { 1112 if ('undefined' !== typeof fieldInstance.options.errorsMessagesDisabled) 1113 return; 1114 // Case where we have errorMessage option that configure an unique field error message, regardless failing validators 1115 if ('undefined' !== typeof fieldInstance.options.errorMessage) { 1116 if ((diff.added.length || diff.kept.length)) { 1117 if (0 === fieldInstance._ui.$errorsWrapper.find('.parsley-custom-error-message').length) 1118 fieldInstance._ui.$errorsWrapper 1119 .append( 1120 $(fieldInstance.options.errorTemplate) 1121 .addClass('parsley-custom-error-message') 1122 ); 1123 return fieldInstance._ui.$errorsWrapper 1124 .addClass('filled') 1125 .find('.parsley-custom-error-message') 1126 .html(fieldInstance.options.errorMessage); 1127 } 1128 return fieldInstance._ui.$errorsWrapper 1129 .removeClass('filled') 1130 .find('.parsley-custom-error-message') 1131 .remove(); 1132 } 1133 // Show, hide, update failing constraints messages 1134 for (var i = 0; i < diff.removed.length; i++) 1135 this.removeError(fieldInstance, diff.removed[i].assert.name, true); 1136 for (i = 0; i < diff.added.length; i++) 1137 this.addError(fieldInstance, diff.added[i].assert.name, undefined, diff.added[i].assert, true); 1138 for (i = 0; i < diff.kept.length; i++) 1139 this.updateError(fieldInstance, diff.kept[i].assert.name, undefined, diff.kept[i].assert, true); 1140 }, 1141 // TODO: strange API here, intuitive for manual usage with addError(pslyInstance, 'foo', 'bar') 1142 // but a little bit complex for above internal usage, with forced undefined parameter... 1143 addError: function (fieldInstance, name, message, assert, doNotUpdate
1143Class) { 1144 fieldInstance._ui.$errorsWrapper 1145 .addClass('filled') 1146 .append( 1147 $(fieldInstance.options.errorTemplate) 1148 .addClass('parsley-' + name) 1149 .html(message || this._getErrorMessage(fieldInstance, assert)) 1150 ); 1151 if (true !== doNotUpdateClass) 1152 this._errorClass(fieldInstance); 1153 }, 1154 // Same as above 1155 updateError: function (fieldInstance, name, message, assert, doNotUpdateClass) { 1156 fieldInstance._ui.$errorsWrapper 1157 .addClass('filled') 1158 .find('.parsley-' + name) 1159 .html(message || this._getErrorMessage(fieldInstance, assert)); 1160 if (true !== doNotUpdateClass) 1161 this._errorClass(fieldInstance); 1162 }, 1163 // Same as above twice 1164 removeError: function (fieldInstance, name, doNotUpdateClass) { 1165 fieldInstance._ui.$errorsWrapper 1166 .removeClass('filled') 1167 .find('.parsley-' + name) 1168 .remove(); 1169 // edge case possible here: remove a standard Parsley error that is still failing in fieldInstance.validationResult 1170 // but highly improbable cuz' manually removing a well Parsley handled error makes no sense. 1171 if (true !== doNotUpdateClass) 1172 this.manageStatusClass(fieldInstance); 1173 }, 1174 focus: function (formInstance) { 1175 if (true === formInstance.validationResult || 'none' === formInstance.options.focus) 1176 return formInstance._focusedField = null; 1177 formInstance._focusedField = null; 1178 for (var i = 0; i < formInstance.fields.length; i++) 1179 if (true !== formInstance.fields[i].validationResult && formInstance.fields[i].validationResult.length > 0 && 'undefined' === typeof formInstance.fields[i].options.noFocus) { 1180 if ('first' === formInstance.options.focus) { 1181 formInstance._focusedField = formInstance.fields[i].$element; 1182 return formInstance._focusedField.focus(); 1183 } 1184 formInstance._focusedField = formInstance.fields[i].$element; 1185 } 1186 if (null === formInstance._focusedField) 1187 return null; 1188 return formInstance._focusedField.focus(); 1189 }, 1190 _getErrorMessage: function (fieldInstance, constraint) { 1191 var customConstraintErrorMessage = constraint.name + 'Message'; 1192 if ('undefined' !== typeof fieldInstance.options[customConstraintErrorMessage]) 1193 return window.ParsleyValidator.formatMessage(fieldInstance.options[customConstraintErrorMessage], constraint.requirements); 1194 return window.ParsleyValidator.getErrorMessage(constraint); 1195 }, 1196 _diff: function (newResult, oldResult, deep) { 1197 var 1198 added = [], 1199 kept = []; 1200 for (var i = 0; i < newResult.length; i++) { 1201 var found = false; 1202 for (var j = 0; j < oldResult.length; j++) 1203 if (newResult[i].assert.name === oldResult[j].assert.name) { 1204 found = true; 1205 break; 1206 } 1207 if (found) 1208 kept.push(newResult[i]); 1209 else
1210 added.push(newResult[i]); 1211 } 1212 return { 1213 kept: kept, 1214 added: added, 1215 removed: !deep ? this._diff(oldResult, newResult, true).added : [] 1216 }; 1217 }, 1218 setupForm: function (formInstance) { 1219 formInstance.$element.on('submit.Parsley', false, $.proxy(formInstance.onSubmitValidate, formInstance)); 1220 // UI could be disabled 1221 if (false === formInstance.options.uiEnabled) 1222 return; 1223 formInstance.$element.attr('novalidate', ''); 1224 }, 1225 setupField: function (fieldInstance) { 1226 var _ui = { active: false }; 1227 // UI could be disabled 1228 if (false === fieldInstance.options.uiEnabled) 1229 return; 1230 _ui.active = true; 1231 // Give field its Parsley id in DOM 1232 fieldInstance.$element.attr(fieldInstance.options.namespace + 'id', fieldInstance.__id__); 1233 /** Generate important UI elements and store them in fieldInstance **/ 1234 // $errorClassHandler is the $element that woul have parsley-error and parsley-success classes 1235 _ui.$errorClassHandler = this._manageClassHandler(fieldInstance); 1236 // $errorsWrapper is a div that would contain the various field errors, it will be appended into $errorsContainer 1237 _ui.errorsWrapperId = 'parsley-id-' + ('undefined' !== typeof fieldInstance.options.multiple ? 'multiple-' + fieldInstance.options.multiple : fieldInstance.__id__); 1238 _ui.$errorsWrapper = $(fieldInstance.options.errorsWrapper).attr('id', _ui.errorsWrapperId); 1239 // ValidationResult UI storage to detect what have changed bwt two validations, and update DOM accordingly 1240 _ui.lastValidationResult = []; 1241 _ui.validatedOnce = false; 1242 _ui.validationInformationVisible = false; 1243 // Store it in fieldInstance for later 1244 fieldInstance._ui = _ui; 1245 // Stops excluded inputs from getting errorContainer added 1246 if( !fieldInstance.$element.is(fieldInstance.options.excluded) ) { 1247 /** Mess with DOM now **/ 1248 this._insertErrorWrapper(fieldInstance); 1249 } 1250 // Bind triggers first time 1251 this.actualizeTriggers(fieldInstance); 1252 }, 1253 // Determine which element will have `parsley-error` and `parsley-success` classes 1254 _manageClassHandler: function (fieldInstance) { 1255 // An element selector could be passed through DOM with `data-parsley-class-handler=#foo` 1256 if ('string' === typeof fieldInstance.options.classHandler && $(fieldInstance.options.classHandler).length) 1257 return $(fieldInstance.options.classHandler); 1258 // Class handled could also be determined by function given in Parsley options 1259 var $handler = fieldInstance.options.classHandler(fieldInstance); 1260 // If this function returned a valid existing DOM element, go for it 1261 if ('undefined' !== typeof $handler && $handler.length) 1262 return $handler; 1263 // Otherwise, if simple element (input, texatrea, select...) it will perfectly host the classes 1264 if ('undefined' === typeof fieldInstance.options.multiple || fieldInstance.$element.is('select')) 1265 return fieldInstance.$element; 1266 // But if multiple element (radio, checkbox), that would be their parent 1267 return fieldInstance.$element.parent(); 1268 }, 1269 _insertErrorWrapper: function (fieldInstance) { 1270 var $errorsContainer; 1271 if ('string' === typeof fieldInstance.options.errorsContainer) { 1272 if ($(fieldInstance.options.errorsContainer).length) 1273 return $(fieldInstance.options.errorsContainer).append(fieldInstance._ui.$errorsWrapper); 1274 else if (window.console && window.console.warn) 1275 window.console.warn('The errors container `' + fieldInstance.options.errorsContainer + '` does not exist in DOM'); 1276 } 1277 else if ('function' === typeof fieldInstance.options.errorsContainer) 1278 $errorsContainer = fieldInstance.options.errorsContainer(fieldInstance); 1279 if ('undefined' !== typeof $errorsContainer && $errorsContainer.length) 1280 return $errorsContainer.append(fieldInstance._ui.$errorsWrapper); 1281 return 'undefined' === typeof fieldInstance.options.multiple ? fieldInstance.$element.after(fieldInstance._ui.$errorsWrapper) : fieldInstance.$element.parent().after(fieldInstance._ui.$errorsWrapper); 1282 }, 1283 actualizeTriggers: function (fieldInstance) { 1284 var $toBind = fieldInstance.$element; 1285 if (fieldInstance.options.multiple) 1286 $toBind = $('[' + fieldInstance.options.namespace + 'multiple="' + fieldInstance.options.multiple + '"]') 1287 // Remove Parsley events already binded on this field 1288 $toBind.off('.Parsley'); 1289 // If no trigger is set, all good 1290 if (false === fieldInstance.options.trigger) 1291 return; 1292 var triggers = fieldInstance.options.trigger.replace(/^\s+/g , '').replace(/\s+$/g , ''); 1293 if ('' === triggers) 1294 return; 1295 // Bind fieldInstance.eventValidate if exists (for parsley.ajax for example), ParsleyUI.eventValidate otherwise 1296 $toBind.on( 1297 triggers.split(' ').join('.Parsley ') + '.Parsley', 1298 $.proxy('function' === typeof fieldInstance.eventValidate ? fieldInstance.eventValidate : this.eventValidate, fieldInstance)); 1299 }, 1300 // Called through $.proxy with fieldInstance. `this` context is ParsleyField 1301 eventValidate: function(event) {
1302 // For keyup, keypress, keydown... events that could be a little bit obstrusive 1303 // do not validate if val length < min threshold on first validation. Once field have been validated once and info 1304 // about success or failure have been displayed, always validate with this trigger to reflect every yalidation change. 1305 if (new RegExp('key').test(event.type)) 1306 if (!this._ui.validationInformationVisible && this.getValue().length <= this.options.validationThreshold) 1307 return; 1308 this._ui.validatedOnce = true; 1309 this.validate(); 1310 }, 1311 manageFailingFieldTrigger: function (fieldInstance) { 1312 fieldInstance._ui.failedOnce = true; 1313 // Radio and checkboxes fields must bind every field multiple 1314 if (fieldInstance.options.multiple) 1315 $('[' + fieldInstance.options.namespace + 'multiple="' + fieldInstance.options.multiple + '"]').each(function () { 1316 if (!new RegExp('change', 'i').test($(this).parsley().options.trigger || '')) 1317 return $(this).on('change.ParsleyFailedOnce', false, $.proxy(fieldInstance.validate, fieldInstance)); 1318 }); 1319 // Select case 1320 if (fieldInstance.$element.is('select')) 1321 if (!new RegExp('change', 'i').test(fieldInstance.options.trigger || '')) 1322 return fieldInstance.$element.on('change.ParsleyFailedOnce', false, $.proxy(fieldInstance.validate, fieldInstance)); 1323 // All other inputs fields 1324 if (!new RegExp('keyup', 'i').test(fieldInstance.options.trigger || '')) 1325 return fieldInstance.$element.on('keyup.ParsleyFailedOnce', false, $.proxy(fieldInstance.validate, fieldInstance)); 1326 }, 1327 reset: function (parsleyInstance) { 1328 // Reset all event listeners 1329 parsleyInstance.$element.off('.Parsley'); 1330 parsleyInstance.$element.off('.ParsleyFailedOnce'); 1331 // Nothing to do if UI never initialized for this field 1332 if ('undefined' === typeof parsleyInstance._ui) 1333 return; 1334 if ('ParsleyForm' === parsleyInstance.__class__) 1335 return; 1336 // Reset all errors' li 1337 parsleyInstance._ui.$errorsWrapper 1338 .removeClass('filled') 1339 .children() 1340 .remove(); 1341 // Reset validation class 1342 this._resetClass(parsleyInstance); 1343 // Reset validation flags and last validation result 1344 parsleyInstance._ui.validatedOnce = false; 1345 parsleyInstance._ui.lastValidationResult = []; 1346 parsleyInstance._ui.validationInformationVisible = false; 1347 }, 1348 destroy: function (parsleyInstance) { 1349 this.reset(parsleyInstance); 1350 if ('ParsleyForm' === parsleyInstance.__class__) 1351 return; 1352 if ('undefined' !== typeof parsleyInstance._ui) 1353 parsleyInstance._ui.$errorsWrapper.remove(); 1354 delete parsleyInstance._ui; 1355 }, 1356 _successClass: function (fieldInstance) { 1357 fieldInstance._ui.validationInformationVisible = true; 1358 fieldInstance._ui.$errorClassHandler.removeClass(fieldInstance.options.errorClass).addClass(fieldInstance.options.successClass); 1359 }, 1360 _errorClass: function (fieldInstance) { 1361 fieldInstance._ui.validationInformationVisible = true; 1362 fieldInstance._ui.$errorClassHandler.removeClass(fieldInstance.options.successClass).addClass(fieldInstance.options.errorClass); 1363 }, 1364 _resetClass: function (fieldInstance) { 1365 fieldInstance._ui.$errorClassHandler.removeClass(fieldInstance.options.successClass).removeClass(fieldInstance.options.errorClass); 1366 } 1367 }; 1368 1369 var ParsleyOptionsFactory = function (defaultOptions, globalOptions, userOptions, namespace) { 1370 this.__class__ = 'OptionsFactory'; 1371 this.__id__ = ParsleyUtils.hash(4); 1372 this.formOptions = null; 1373 this.fieldOptions = null; 1374 this.staticOptions = $.extend(true, {}, defaultOptions, globalOptions, userOptions, { namespace: namespace }); 1375 }; 1376 ParsleyOptionsFactory.prototype = { 1377 get: function (parsleyInstance) { 1378 if ('undefined' === typeof parsleyInstance.__class__) 1379 throw new Error('Parsley Instance expected'); 1380 switch (parsleyInstance.__class__) { 1381 case 'Parsley': 1382 return this.staticOptions; 1383 case 'ParsleyForm': 1384 return this.getFormOptions(parsleyInstance); 1385 case 'ParsleyField': 1386 case 'ParsleyFieldMultiple': 1387 return this.getFieldOptions(parsleyInstance); 1388 default: 1389 throw new Error('Instance ' + parsleyInstance.__class__ + ' is not supported'); 1390 } 1391 }, 1392 getFormOptions: function (formInstance) { 1393 this.formOptions = ParsleyUtils.attr(formInstance.$element, this.staticOptions.namespace); 1394 // not deep extend, since formOptions is a 1 level deep object 1395 return $.extend({}, this.staticOptions, this.formOptions); 1396 }, 1397 getFieldOptions: function (fieldInstance) { 1398 this.fieldOptions = ParsleyUtils.attr(fieldInstance.$element, this.staticOptions.namespace); 1399 if (null === this.formOptions && 'undefined' !== typeof fieldInstance.parent) 1400 this.formOptions = this.getFormOptions(fieldInstance.parent); 1401 // not deep extend, since formOptions and fieldOptions is a 1 level deep object 1402 return $.extend({}, this.staticOptions, this.formOptions, this.fieldOptions); 1403 } 1404 }; 1405 1406 var ParsleyForm = function (element, OptionsFactory) { 1407 this.__class__ = 'ParsleyForm'; 1408 this.__id__ = ParsleyUtils.hash(4); 1409 if ('OptionsFactory' !== ParsleyUtils.get(OptionsFactory, '__class__')) 1410 throw new Error('You must give an OptionsFactory instance'); 1411 this.OptionsFactory = OptionsFactory;
1412 this.$element = $(element); 1413 this.validationResult = null; 1414 this.options = this.OptionsFactory.get(this); 1415 }; 1416 ParsleyForm.prototype = { 1417 onSubmitValidate: function (event) { 1418 this.validate(undefined, undefined, event); 1419 // prevent form submission if validation fails 1420 if (false === this.validationResult && event instanceof $.Event) { 1421 event.stopImmediatePropagation(); 1422 event.preventDefault(); 1423 } 1424 return this; 1425 }, 1426 // @returns boolean 1427 validate: function (group, force, event) { 1428 this.submitEvent = event; 1429 this.validationResult = true; 1430 var fieldValidationResult = []; 1431 $.emit('parsley:form:validate', this); 1432 // Refresh form DOM options and form's fields that could have changed 1433 this._refreshFields(); 1434 // loop through fields to validate them one by one 1435 for (var i = 0; i < this.fields.length; i++) { 1436 // do not validate a field if not the same as given validation group 1437 if (group && !this._isFieldInGroup(this.fields[i], group)) 1438 continue; 1439 fieldValidationResult = this.fields[i].validate(force); 1440 if (true !== fieldValidationResult && fieldValidationResult.length > 0 && this.validationResult) 1441 this.validationResult = false; 1442 } 1443 $.emit('parsley:form:' + (this.validationResult ? 'success' : 'error'), this); 1444 $.emit('parsley:form:validated', this); 1445 return this.validationResult; 1446 }, 1447 // Iterate over refreshed fields, and stop on first failure 1448 isValid: function (group, force) { 1449 this._refreshFields(); 1450 for (var i = 0; i < this.fields.length; i++) { 1451 // do not validate a field if not the same as given validation group 1452 if (group && !this._isFieldInGroup(this.fields[i], group)) 1453 continue; 1454 if (false === this.fields[i].isValid(force)) 1455 return false; 1456 } 1457 return true; 1458 }, 1459 _isFieldInGroup: function (field, group) { 1460 if(ParsleyUtils.isArray(field.options.group)) 1461 return -1 !== $.inArray(group, field.options.group); 1462 return field.options.group === group; 1463 }, 1464 _refreshFields: function () { 1465 return this.actualizeOptions()._bindFields(); 1466 }, 1467 _bindFields: function () { 1468 var self = this; 1469 this.fields = []; 1470 this.fieldsMappedById = {}; 1471 this.$element.find(this.options.inputs).each(function () { 1472 var fieldInstance = new window.Parsley(this, {}, self); 1473 // Only add valid and not excluded `ParsleyField` and `ParsleyFieldMultiple` children 1474 if (('ParsleyField' === fieldInstance.__class__ || 'ParsleyFieldMultiple' === fieldInstance.__class__) && !fieldInstance.$element.is(fieldInstance.options.excluded)) 1475 if ('undefined' === typeof self.fieldsMappedById[fieldInstance.__class__ + '-' + fieldInstance.__id__]) { 1476 self.fieldsMappedById[fieldInstance.__class__ + '-' + fieldInstance.__id__] = fieldInstance; 1477 self.fields.push(fieldInstance); 1478 } 1479 }); 1480 return this; 1481 } 1482 }; 1483 1484 var ConstraintFactory = function (parsleyField, name, requirements, priority, isDomConstraint) { 1485 var assert = {}; 1486 if (!new RegExp('ParsleyField').test(ParsleyUtils.get(parsleyField, '__class__'))) 1487 throw new Error('ParsleyField or ParsleyFieldMultiple instance expected'); 1488 if ('function' === typeof window.ParsleyValidator.validators[name]) 1489 assert = window.ParsleyValidator.validators[name](requirements); 1490 if ('Assert' !== assert.__parentClass__) 1491 throw new Error('Valid validator expected'); 1492 var getPriority = function () { 1493 if ('undefined' !== typeof parsleyField.options[name + 'Priority']) 1494 return parsleyField.options[name + 'Priority']; 1495 return ParsleyUtils.get(assert, 'priority') || 2; 1496 }; 1497 priority = priority || getPriority(); 1498 // If validator have a requirementsTransformer, execute it 1499 if ('function' === typeof assert.requirementsTransformer) { 1500 requirements = assert.requirementsTransformer(); 1501 // rebuild assert with new requirements 1502 assert = window.ParsleyValidator.validators[name](requirements); 1503 } 1504 return $.extend(assert, { 1505 name: name, 1506 requirements: requirements, 1507 priority: priority, 1508 groups: [priority], 1509 isDomConstraint: isDomConstraint || ParsleyUtils.attr(parsleyField.$element, parsleyField.options.namespace, name) 1510 });
1511 }; 1512 1513 var ParsleyField = function (field, OptionsFactory, parsleyFormInstance) { 1514 this.__class__ = 'ParsleyField'; 1515 this.__id__ = ParsleyUtils.hash(4); 1516 this.$element = $(field); 1517 // If we have a parent `ParsleyForm` instance given, use its `OptionsFactory`, and save parent 1518 if ('undefined' !== typeof parsleyFormInstance) { 1519 this.parent = parsleyFormInstance; 1520 this.OptionsFactory = this.parent.OptionsFactory; 1521 this.options = this.OptionsFactory.get(this); 1522 // Else, take the `Parsley` one 1523 } else { 1524 this.OptionsFactory = OptionsFactory; 1525 this.options = this.OptionsFactory.get(this); 1526 } 1527 // Initialize some properties 1528 this.constraints = []; 1529 this.constraintsByName = {}; 1530 this.validationResult = []; 1531 // Bind constraints 1532 this._bindConstraints(); 1533 }; 1534 ParsleyField.prototype = { 1535 // # Public API 1536 // Validate field and $.emit some events for mainly `ParsleyUI` 1537 // @returns validationResult: 1538 // - `true` if all constraints pass 1539 // - `[]` if not required field and empty (not validated) 1540 // - `[Violation, [Violation...]]` if there were validation errors 1541 validate: function (force) { 1542 this.value = this.getValue(); 1543 // Field Validate event. `this.value` could be altered for custom needs 1544 $.emit('parsley:field:validate', this); 1545 $.emit('parsley:field:' + (this.isValid(force, this.value) ? 'success' : 'error'), this); 1546 // Field validated event. `this.validationResult` could be altered for custom needs too 1547 $.emit('parsley:field:validated', this); 1548 return this.validationResult; 1549 }, 1550 // Just validate field. Do not trigger any event 1551 // Same @return as `validate()` 1552 isValid: function (force, value) { 1553 // Recompute options and rebind constraints to have latest changes 1554 this.refreshConstraints(); 1555 // Sort priorities to validate more important first 1556 var priorities = this._getConstraintsSortedPriorities(); 1557 if (0 === priorities.length) 1558 return this.validationResult = []; 1559 // Value could be passed as argument, needed to add more power to 'parsley:field:validate' 1560 if ('undefined' === typeof value || null === value) 1561 value = this.getValue(); 1562 // If a field is empty and not required, leave it alone, it's just fine 1563 // Except if `data-parsley-validate-if-empty` explicitely added, useful for some custom validators 1564 if (!value.length && !this._isRequired() && 'undefined' === typeof this.options.validateIfEmpty && true !== force) 1565 return this.validationResult = []; 1566 // If we want to validate field against all constraints, just call Validator and let it do the job 1567 if (false === this.options.priorityEnabled) 1568 return true === (this.validationResult = this.validateThroughValidator(value, this.constraints, 'Any')); 1569 // Else, iterate over priorities one by one, and validate related asserts one by one 1570 for (var i = 0; i < priorities.length; i++) 1571 if (true !== (this.validationResult = this.validateThroughValidator(value, this.constraints, priorities[i]))) 1572 return false; 1573 return true; 1574 }, 1575 // @returns Parsley field computed value that could be overrided or configured in DOM 1576 getValue: function () { 1577 var value; 1578 // Value could be overriden in DOM 1579 if ('undefined' !== typeof this.options.value) 1580 value = this.options.value; 1581 else 1582 value = this.$element.val(); 1583 // Handle wrong DOM or configurations 1584 if ('undefined' === typeof value || null === value) 1585 return ''; 1586 // Use `data-parsley-trim-value="true"` to auto trim inputs entry 1587 if (true === this.options.trimValue) 1588 return value.replace(/^\s+|\s+$/g, ''); 1589 return value; 1590 }, 1591 // Actualize options that could have change since previous validation 1592 // Re-bind accordingly constraints (could be some new, removed or updated) 1593 refreshConstraints: function () { 1594 return this.actualizeOptions()._bindConstraints();
1595 }, 1596 /** 1597 * Add a new constraint to a field 1598 * 1599 * @method addConstraint 1600 * @param {String} name 1601 * @param {Mixed} requirements optional 1602 * @param {Number} priority optional 1603 * @param {Boolean} isDomConstraint optional 1604 */ 1605 addConstraint: function (name, requirements, priority, isDomConstraint) { 1606 name = name.toLowerCase(); 1607 if ('function' === typeof window.ParsleyValidator.validators[name]) { 1608 var constraint = new ConstraintFactory(this, name, requirements, priority, isDomConstraint); 1609 // if constraint already exist, delete it and push new version 1610 if ('undefined' !== this.constraintsByName[constraint.name]) 1611 this.removeConstraint(constraint.name); 1612 this.constraints.push(constraint); 1613 this.constraintsByName[constraint.name] = constraint; 1614 } 1615 return this; 1616 }, 1617 // Remove a constraint 1618 removeConstraint: function (name) { 1619 for (var i = 0; i < this.constraints.length; i++) 1620 if (name === this.constraints[i].name) { 1621 this.constraints.splice(i, 1); 1622 break; 1623 } 1624 delete this.constraintsByName[name]; 1625 return this; 1626 }, 1627 // Update a constraint (Remove + re-add) 1628 updateConstraint: function (name, parameters, priority) { 1629 return this.removeConstraint(name) 1630 .addConstraint(name, parameters, priority); 1631 }, 1632 // # Internals 1633 // Internal only. 1634 // Bind constraints from config + options + DOM 1635 _bindConstraints: function () { 1636 var constraints = [], constraintsByName = {}; 1637 // clean all existing DOM constraints to only keep javascript user constraints 1638 for (var i = 0; i < this.constraints.length; i++) 1639 if (false === this.constraints[i].isDomConstraint) { 1640 constraints.push(this.constraints[i]); 1641 constraintsByName[this.constraints[i].name] = this.constraints[i]; 1642 } 1643 this.constraints = constraints; 1644 this.constraintsByName = constraintsByName; 1645 // then re-add Parsley DOM-API constraints 1646 for (var name in this.options) 1647 this.addConstraint(name, this.options[name]); 1648 // finally, bind special HTML5 constraints 1649 return this._bindHtml5Constraints(); 1650 }, 1651 // Internal only. 1652 // Bind specific HTML5 constraints to be HTML5 compliant 1653 _bindHtml5Constraints: function () { 1654 // html5 required 1655 if (this.$element.hasClass('required') || this.$element.attr('required')) 1656 this.addConstraint('required', true, undefined, true); 1657 // html5 pattern 1658 if ('string' === typeof this.$element.attr('pattern')) 1659 this.addConstraint('pattern', this.$element.attr('pattern'), undefined, true); 1660 // range 1661 if ('undefined' !== typeof this.$element.attr('min') && 'undefined' !== typeof this.$element.attr('max')) 1662 this.addConstraint('range', [this.$element.attr('min'), this.$element.attr('max')], undefined, true); 1663 // HTML5 min 1664 else if ('undefined' !== typeof this.$element.attr('min')) 1665 this.addConstraint('min', this.$element.attr('min'), undefined, true); 1666 // HTML5 max 1667 else if ('undefined' !== typeof this.$element.attr('max')) 1668 this.addConstraint('max', this.$element.attr('max'), undefined, true); 1669 1670 // length 1671 if ('undefined' !== typeof this.$element.attr('minlength') && 'undefined' !== typeof this.$element.attr('maxlength')) 1672 this.addConstraint('length', [this.$element.attr('minlength'), this.$element.attr('maxlength')], undefined, true); 1673 // HTML5 minlength 1674 else if ('undefined' !== typeof this.$element.attr('minlength')) 1675 this.addConstraint('minlength', this.$element.attr('minlength'), undefined, true); 1676 // HTML5 maxlength 1677 else if ('undefined' !== typeof this.$element.attr('maxlength')) 1678 this.addConstraint('maxlength', this.$element.attr('maxlength'), undefined, true); 1679 1680 // html5 types 1681 var type = this.$element.attr('type'); 1682 if ('undefined' === typeof type) 1683 return this; 1684 // Small special case here for HTML5 number: integer validator if step attribute is undefined or an integer value, number otherwise 1685 if ('number' === type) { 1686 if (('undefined' === typeof this.$element.attr('step')) || (0 === parseFloat(this.$element.attr('step')) % 1)) { 1687 return this.addConstraint('type', 'integer', undefined, true); 1688 } else { 1689 return this.addConstraint('type', 'number', undefined, true); 1690 } 1691 // Regular other HTML5 supported types 1692 } else if (new RegExp(type, 'i').test('email url range')) { 1693 return this.addConstraint('type', type, undefined, true); 1694 } 1695 return this; 1696 }, 1697 // Internal only. 1698 // Field is required if have required constraint without `false` value 1699 _isRequired: function () { 1700 if ('undefined' === typeof this.constraintsByName.required) 1701 return false; 1702 return false !== this.constraintsByName.required.requirements; 1703 }, 1704 // Internal only. 1705 // Sort constraints by priority DESC 1706 _getConstraintsSortedPriorities: function () { 1707 var priorities = []; 1708 // Create array unique of priorities 1709 for (var i = 0; i < this.constraints.length; i++) 1710 if (-1 === priorities.indexOf(this.constraints[i].priority)) 1711 priorities.push(this.constraints[i].priority); 1712 // Sort them by priority DESC 1713 priorities.sort(function (a, b) { return b - a; }); 1714 return priorities; 1715 } 1716 }; 1717 1718 var ParsleyMultiple = function () { 1719 this.__class__ = 'ParsleyFieldMultiple'; 1720 }; 1721 ParsleyMultiple.prototype = { 1722 // Add new `$element` sibling for multiple field 1723 addElement: function ($element) { 1724 this.$elements.push($element); 1725 return this; 1726 }, 1727 // See `ParsleyField.refreshConstraints()` 1728 refreshConstraints: function () { 1729 var fieldConstraints; 1730 this.constraints = []; 1731 // Select multiple special treatment 1732 if (this.$element.is('select')) { 1733 this.actualizeOptions()._bindConstraints();
1734 return this; 1735 } 1736 // Gather all constraints for each input in the multiple group 1737 for (var i = 0; i < this.$elements.length; i++) { 1738 // Check if element have not been dynamically removed since last binding 1739 if (!$('html').has(this.$elements[i]).length) { 1740 this.$elements.splice(i, 1); 1741 continue; 1742 } 1743 fieldConstraints = this.$elements[i].data('ParsleyFieldMultiple').refreshConstraints().constraints; 1744 for (var j = 0; j < fieldConstraints.length; j++) 1745 this.addConstraint(fieldConstraints[j].name, fieldConstraints[j].requirements, fieldConstraints[j].priority, fieldConstraints[j].isDomConstraint); 1746 } 1747 return this; 1748 }, 1749 // See `ParsleyField.getValue()` 1750 getValue: function () { 1751 // Value could be overriden in DOM 1752 if ('undefined' !== typeof this.options.value) 1753 return this.options.value; 1754 // Radio input case 1755 if (this.$element.is('input[type=radio]')) 1756 return $('[' + this.options.namespace + 'multiple="' + this.options.multiple + '"]:checked').val() || ''; 1757 // checkbox input case 1758 if (this.$element.is('input[type=checkbox]')) { 1759 var values = []; 1760 $('[' + this.options.namespace + 'multiple="' + this.options.multiple + '"]:checked').each(function () { 1761 values.push($(this).val()); 1762 }); 1763 return values.length ? values : []; 1764 } 1765 // Select multiple case 1766 if (this.$element.is('select') && null === this.$element.val()) 1767 return []; 1768 // Default case that should never happen 1769 return this.$element.val(); 1770 }, 1771 _init: function (multiple) { 1772 this.$elements = [this.$element]; 1773 this.options.multiple = multiple; 1774 return this; 1775 } 1776 }; 1777 1778 var 1779 o = $({}), 1780 subscribed = {}; 1781 // $.listen(name, callback); 1782 // $.listen(name, context, callback); 1783 $.listen = function (name) { 1784 if ('undefined' === typeof subscribed[name]) 1785 subscribed[name] = []; 1786 if ('function' === typeof arguments[1]) 1787 return subscribed[name].push({ fn: arguments[1] }); 1788 if ('object' === typeof arguments[1] && 'function' === typeof arguments[2]) 1789 return subscribed[name].push({ fn: arguments[2], ctxt: arguments[1] }); 1790 throw new Error('Wrong parameters'); 1791 }; 1792 $.listenTo = function (instance, name, fn) { 1793 if ('undefined' === typeof subscribed[name]) 1794 subscribed[name] = []; 1795 if (!(instance instanceof ParsleyField) && !(instance instanceof ParsleyForm)) 1796 throw new Error('Must give Parsley instance'); 1797 if ('string' !== typeof name || 'function' !== typeof fn) 1798 throw new Error('Wrong parameters'); 1799 subscribed[name].push({ instance: instance, fn: fn }); 1800 }; 1801 $.unsubscribe = function (name, fn) { 1802 if ('undefined' === typeof subscribed[name]) 1803 return; 1804 if ('string' !== typeof name || 'function' !== typeof fn) 1805 throw new Error('Wrong arguments'); 1806 for (var i = 0; i < subscribed[name].length; i++) 1807 if (subscribed[name][i].fn === fn) 1808 return subscribed[name].splice(i, 1); 1809 }; 1810 $.unsubscribeTo = function (instance, name) { 1811 if ('undefined' === typeof subscribed[name]) 1812 return; 1813 if (!(instance instanceof ParsleyField) && !(instance instanceof ParsleyForm)) 1814 throw new Error('Must give Parsley instance'); 1815 for (var i = 0; i < subscribed[name].length; i++) 1816 if ('undefined' !== typeof subscribed[name][i].instance && subscribed[name][i].instance.__id__ === instance.__id__) 1817 return subscribed[name].splice(i, 1); 1818 }; 1819 $.unsubscribeAll = function (name) { 1820 if ('undefined' === typeof subscribed[name]) 1821 return; 1822 delete subscribed[name]; 1823 }; 1824 // $.emit(name [, arguments...]); 1825 // $.emit(name, instance [, arguments...]); 1826 $.emit = function (name, instance) { 1827 if ('undefined' === typeof subscribed[name]) 1828 return; 1829 // loop through registered callbacks for this event 1830 for (var i = 0; i < subscribed[name].length; i++) { 1831 // if instance is not registered, simple emit 1832 if ('undefined' === typeof subscribed[name][i].instance) { 1833 subscribed[name][i].fn.apply('undefined' !== typeof subscribed[name][i].ctxt ? subscribed[name][i].ctxt : o, Array.prototype.slice.call(arguments, 1)); 1834 continue; 1835 } 1836 // if instance registered but no instance given for the emit, continue 1837 if (!(instance instanceof ParsleyField) && !(instance instanceof ParsleyForm)) 1838 continue; 1839 // if instance is registered and same id, emit 1840 if (subscribed[name][i].instance.__id__ === instance.__id__) { 1841 subscribed[name][i].fn.apply(o, Array.prototype.slice.call(arguments, 1)); 1842 continue; 1843 } 1844 // if registered instance is a Form and fired one is a Field, loop over all its fields and emit if field found 1845 if (subscribed[name][i].instance instanceof ParsleyForm && instance instanceof ParsleyField) 1846 for (var j = 0; j < subscribed[name][i].instance.fields.length; j++) 1847 if (subscribed[name][i].instance.fields[j].__id__ === instance.__id__) { 1848 subscribed[name][i].fn.apply(o, Array.prototype.slice.call(arguments, 1)); 1849 continue; 1850 } 1851 } 1852 }; 1853 $.subscribed = function () { return subscribed; }; 1854 1855// ParsleyConfig definition if not already set 1856window.ParsleyConfig = window.ParsleyConfig || {}; 1857window.ParsleyConfig.i18n = window.ParsleyConfig.i18n || {}; 1858// Define then the messages 1859window.ParsleyConfig.i18n.en = $.extend(window.ParsleyConfig.i18n.en || {}, { 1860 defaultMessage: "This value seems to be invalid.", 1861 type: { 1862 email: "This value should be a valid email.", 1863 url: "This value should be a valid url.", 1864 number: "This value should be a valid number.", 1865 integer: "This value should be a valid integer.", 1866 digits: "This value should be digits.",
1867 alphanum: "This value should be alphanumeric." 1868 }, 1869 notblank: "This value should not be blank.", 1870 required: "This value is required.", 1871 pattern: "This value seems to be invalid.", 1872 min: "This value should be greater than or equal to %s.", 1873 max: "This value should be lower than or equal to %s.", 1874 range: "This value should be between %s and %s.", 1875 minlength: "This value is too short. It should have %s characters or more.", 1876 maxlength: "This value is too long. It should have %s characters or fewer.", 1877 length: "This value length is invalid. It should be between %s and %s characters long.", 1878 mincheck: "You must select at least %s choices.", 1879 maxcheck: "You must select %s choices or fewer.", 1880 check: "You must select between %s and %s choices.", 1881 equalto: "This value should be the same." 1882}); 1883// If file is loaded after Parsley main file, auto-load locale 1884if ('undefined' !== typeof window.ParsleyValidator) 1885 window.ParsleyValidator.addCatalog('en', window.ParsleyConfig.i18n.en, true); 1886 1887// Parsley.js 2.0.6 1888// http://parsleyjs.org 1889// (c) 20012-2014 Guillaume Potier, Wisembly 1890// Parsley may be freely distributed under the MIT license. 1891 1892 // ### Parsley factory 1893 var Parsley = function (element, options, parsleyFormInstance) { 1894 this.__class__ = 'Parsley'; 1895 this.__version__ = '2.0.6'; 1896 this.__id__ = ParsleyUtils.hash(4); 1897 // Parsley must be instantiated with a DOM element or jQuery $element 1898 if ('undefined' === typeof element) 1899 throw new Error('You must give an element'); 1900 if ('undefined' !== typeof parsleyFormInstance && 'ParsleyForm' !== parsleyFormInstance.__class__) 1901 throw new Error('Parent instance must be a ParsleyForm instance'); 1902 return this.init($(element), options, parsleyFormInstance); 1903 }; 1904 Parsley.prototype = { 1905 init: function ($element, options, parsleyFormInstance) { 1906 if (!$element.length) 1907 throw new Error('You must bind Parsley on an existing element.'); 1908 this.$element = $element; 1909 // If element have already been binded, returns its saved Parsley instance 1910 if (this.$element.data('Parsley')) { 1911 var savedparsleyFormInstance = this.$element.data('Parsley'); 1912 // If saved instance have been binded without a ParsleyForm parent and there is one given in this call, add it 1913 if ('undefined' !== typeof parsleyFormInstance) 1914 savedparsleyFormInstance.parent = parsleyFormInstance; 1915 return savedparsleyFormInstance; 1916 } 1917 // Handle 'static' options 1918 this.OptionsFactory = new ParsleyOptionsFactory(ParsleyDefaults, ParsleyUtils.get(window, 'ParsleyConfig') || {}, options, this.getNamespace(options)); 1919 this.options = this.OptionsFactory.get(this); 1920 // A ParsleyForm instance is obviously a `<form>` elem but also every node that is not an input and have `data-parsley-validate` attribute 1921 if (this.$element.is('form') || (ParsleyUtils.attr(this.$element, this.options.namespace, 'validate') && !this.$element.is(this.options.inputs))) 1922 return this.bind('parsleyForm'); 1923 // Every other supported element and not excluded element is binded as a `ParsleyField` or `ParsleyFieldMultiple` 1924 else if (this.$element.is(this.options.inputs) && !this.$element.is(this.options.excluded)) 1925 return this.isMultiple() ? this.handleMultiple(parsleyFormInstance) : this.bind('parsleyField', parsleyFormInstance); 1926 return this; 1927 }, 1928 isMultiple: function () { 1929 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')); 1930 }, 1931 // Multiples fields are a real nightmare :( 1932 // Maybe some refacto would be appreciated here... 1933 handleMultiple: function (parsleyFormInstance) { 1934 var 1935 that = this, 1936 name, 1937 multiple, 1938 parsleyMultipleInstance; 1939 // Get parsleyFormInstance options if exist, mixed with element attributes 1940 this.options = $.extend(this.options, parsleyFormInstance ? parsleyFormInstance.OptionsFactory.get(parsleyFormInstance) : {}, ParsleyUtils.attr(this.$element, this.options.namespace)); 1941 // Handle multiple name 1942 if (this.options.multiple) 1943 multiple = this.options.multiple; 1944 else if ('undefined' !== typeof this.$element.attr('name') && this.$element.attr('name').length) 1945 multiple = name = this.$element.attr('name'); 1946 else if ('undefined' !== typeof this.$element.attr('id') && this.$element.attr('id').length) 1947 multiple = this.$element.attr('id'); 1948 // Special select multiple input 1949 if (this.$element.is('select') && 'undefined' !== typeof this.$element.attr('multiple')) { 1950 return this.bind('parsleyFieldMultiple', parsleyFormInstance, multiple || this.__id__); 1951 // Else for radio / checkboxes, we need a `name` or `data-parsley-multiple` to properly bind it 1952 } else if ('undefined' === typeof multiple) { 1953 if (window.console && window.console.warn) 1954 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); 1955 return this; 1956 } 1957 // Remove special chars 1958 multiple = multiple.replace(/(:|\.|\[|\]|\{|\}|\$)/g, ''); 1959 // Add proper `data-parsley-multiple` to siblings if we have a valid multiple name 1960 if ('undefined' !== typeof name) { 1961 $('input[name="' + name + '"]').each(function () { 1962 if ($(this).is('input[type=radio], input[type=checkbox]')) 1963 $(this).attr(that.options.namespace + 'multiple', multiple); 1964 }); 1965 } 1966 // Check here if we don't already have a related multiple instance saved 1967 if ($('[' + this.options.namespace + 'multiple=' + multiple +']').length) { 1968 for (var i = 0; i < $('[' + this.options.namespace + 'multiple=' + multiple +']').length; i++) { 1969 if ('undefined' !== typeof $($('[' + this.options.namespace + 'multiple=' + multiple +']').get(i)).data('Parsley')) { 1970 parsleyMultipleInstance = $($('[' + this.options.namespace + 'multiple=' + multiple +']').get(i)).data('Parsley'); 1971 if (!this.$element.data('ParsleyFieldMultiple')) { 1972 parsleyMultipleInstance.addElement(this.$element); 1973 this.$element.attr(this.options.namespace + 'id', parsleyMultipleInstance.__id__); 1974 } 1975 break; 1976 } 1977 } 1978 } 1979 // Create a secret ParsleyField instance for every multiple field. It would be stored in `data('ParsleyFieldMultiple')` 1980 // And would be useful later to access classic `ParsleyField` stuff while being in a `ParsleyFieldMultiple` instance 1981 this.bind('parsleyField', parsleyFormInstance, multiple, true); 1982 return parsleyMultipleInstance || this.bind('parsleyFieldMultiple', parsleyFormInstance, multiple); 1983 }, 1984 // Retrieve namespace used for DOM-API 1985 getNamespace: function (options) { 1986 // `data-parsley-namespace=<namespace>` 1987 if ('undefined' !== typeof this.$element.data('parsleyNamespace')) 1988 return this.$element.data('parsleyNamespace'); 1989 if ('undefined' !== typeof ParsleyUtils.get(options, 'namespace')) 1990 return options.namespace; 1991 if ('undefined' !== typeof ParsleyUtils.get(window, 'ParsleyConfig.namespace')) 1992 return window.ParsleyConfig.namespace; 1993 return ParsleyDefaults.namespace; 1994 }, 1995 // Return proper `ParsleyForm`, `ParsleyField` or `ParsleyFieldMultiple` 1996 bind: function (type, parentParsleyFormInstance, multiple, doNotStore) { 1997 var parsleyInstance; 1998 switch (type) { 1999 case 'parsleyForm': 2000 parsleyInstance = $.extend( 2001 new ParsleyForm(this.$element, this.OptionsFactory), 2002 new ParsleyAbstract(), 2003 window.ParsleyExtend 2004 )._bindFields(); 2005 break; 2006 case 'parsleyField': 2007 parsleyInstance = $.extend( 2008 new ParsleyField(this.$element, this.OptionsFactory, parentParsleyFormInstance), 2009 new ParsleyAbstract(), 2010 window.ParsleyExtend 2011 ); 2012 break; 2013 case 'parsleyFieldMultiple': 2014 parsleyInstance = $.extend( 2015 new ParsleyField(this.$element, this.OptionsFactory, parentParsleyFormInstance), 2016 new ParsleyAbstract(), 2017 new ParsleyMultiple(), 2018 window.ParsleyExtend 2019 )._init(multiple); 2020 break; 2021 default: 2022 throw new Error(type + 'is not a supported Parsley type'); 2023 } 2024 if ('undefined' !== typeof multiple) 2025 ParsleyUtils.setAttr(this.$element, this.options.namespace, 'multiple', multiple); 2026 if ('undefined' !== typeof doNotStore) { 2027 this.$element.data('ParsleyFieldMultiple', parsleyInstance); 2028 return parsleyInstance; 2029 } 2030 // Store instance if `ParsleyForm`, `ParsleyField` or `ParsleyFieldMultiple` 2031 if (new RegExp('ParsleyF', 'i').test(parsleyInstance.__class__)) { 2032 // Store for later access the freshly binded instance in DOM element itself using jQuery `data()`
2033 this.$element.data('Parsley', parsleyInstance); 2034 // Tell the world we got a new ParsleyForm or ParsleyField instance! 2035 $.emit('parsley:' + ('parsleyForm' === type ? 'form' : 'field') + ':init', parsleyInstance); 2036 } 2037 return parsleyInstance; 2038 } 2039 }; 2040 // ### jQuery API 2041 // `$('.elem').parsley(options)` or `$('.elem').psly(options)` 2042 $.fn.parsley = $.fn.psly = function (options) { 2043 if (this.length > 1) { 2044 var instances = []; 2045 this.each(function () { 2046 instances.push($(this).parsley(options)); 2047 }); 2048 return instances; 2049 } 2050 // Return undefined if applied to non existing DOM element 2051 if (!$(this).length) { 2052 if (window.console && window.console.warn) 2053 window.console.warn('You must bind Parsley on an existing element.'); 2054 return; 2055 } 2056 return new Parsley(this, options); 2057 }; 2058 // ### ParsleyUI 2059 // UI is a class apart that only listen to some events and them modify DOM accordingly 2060 // Could be overri
2060den by defining a `window.ParsleyConfig.ParsleyUI` appropriate class (with `listen()` method basically) 2061 window.ParsleyUI = 'function' === typeof ParsleyUtils.get(window, 'ParsleyConfig.ParsleyUI') ? 2062 new window.ParsleyConfig.ParsleyUI().listen() : new ParsleyUI().listen(); 2063 // ### ParsleyField and ParsleyForm extension 2064 // Ensure that defined if not already the case 2065 if ('undefined' === typeof window.ParsleyExtend) 2066 window.ParsleyExtend = {}; 2067 // ### ParsleyConfig 2068 // Ensure that defined if not already the case 2069 if ('undefined' === typeof window.ParsleyConfig) 2070 window.ParsleyConfig = {}; 2071 // ### Globals 2072 window.Parsley = window.psly = Parsley; 2073 window.ParsleyUtils = ParsleyUtils; 2074 window.ParsleyValidator = new ParsleyValidator(window.ParsleyConfig.validators, window.ParsleyConfig.i18n); 2075 // ### PARSLEY auto-binding 2076 // Prevent it by setting `ParsleyConfig.autoBind` to `false` 2077 if (false !== ParsleyUtils.get(window, 'ParsleyConfig.autoBind')) 2078 $(function () { 2079 // Works only on `data-parsley-validate`. 2080 if ($('[data-parsley-validate]').length) 2081 $('[data-parsley-validate]').parsley(); 2082 }); 2083}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.