parsley.js 106 KB

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