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