jquery.validate.js 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231
  1. /*!
  2. * jQuery Validation Plugin 1.11.1
  3. *
  4. * http://bassistance.de/jquery-plugins/jquery-plugin-validation/
  5. * http://docs.jquery.com/Plugins/Validation
  6. *
  7. * Copyright 2013 Jörn Zaefferer
  8. * Released under the MIT license:
  9. * http://www.opensource.org/licenses/mit-license.php
  10. */
  11. (function($) {
  12. $.extend($.fn, {
  13. // http://docs.jquery.com/Plugins/Validation/validate
  14. validate: function( options ) {
  15. // if nothing is selected, return nothing; can't chain anyway
  16. if ( !this.length ) {
  17. if ( options && options.debug && window.console ) {
  18. console.warn( "Nothing selected, can't validate, returning nothing." );
  19. }
  20. return;
  21. }
  22. // check if a validator for this form was already created
  23. var validator = $.data( this[0], "validator" );
  24. if ( validator ) {
  25. return validator;
  26. }
  27. // Add novalidate tag if HTML5.
  28. this.attr( "novalidate", "novalidate" );
  29. validator = new $.validator( options, this[0] );
  30. $.data( this[0], "validator", validator );
  31. if ( validator.settings.onsubmit ) {
  32. this.validateDelegate( ":submit", "click", function( event ) {
  33. if ( validator.settings.submitHandler ) {
  34. validator.submitButton = event.target;
  35. }
  36. // allow suppressing validation by adding a cancel class to the submit button
  37. if ( $(event.target).hasClass("cancel") ) {
  38. validator.cancelSubmit = true;
  39. }
  40. // allow suppressing validation by adding the html5 formnovalidate attribute to the submit button
  41. if ( $(event.target).attr("formnovalidate") !== undefined ) {
  42. validator.cancelSubmit = true;
  43. }
  44. });
  45. // validate the form on submit
  46. this.submit( function( event ) {
  47. if ( validator.settings.debug ) {
  48. // prevent form submit to be able to see console output
  49. event.preventDefault();
  50. }
  51. function handle() {
  52. var hidden;
  53. if ( validator.settings.submitHandler ) {
  54. if ( validator.submitButton ) {
  55. // insert a hidden input as a replacement for the missing submit button
  56. hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val( $(validator.submitButton).val() ).appendTo(validator.currentForm);
  57. }
  58. validator.settings.submitHandler.call( validator, validator.currentForm, event );
  59. if ( validator.submitButton ) {
  60. // and clean up afterwards; thanks to no-block-scope, hidden can be referenced
  61. hidden.remove();
  62. }
  63. return false;
  64. }
  65. return true;
  66. }
  67. // prevent submit for invalid forms or custom submit handlers
  68. if ( validator.cancelSubmit ) {
  69. validator.cancelSubmit = false;
  70. return handle();
  71. }
  72. if ( validator.form() ) {
  73. if ( validator.pendingRequest ) {
  74. validator.formSubmitted = true;
  75. return false;
  76. }
  77. return handle();
  78. } else {
  79. validator.focusInvalid();
  80. return false;
  81. }
  82. });
  83. }
  84. return validator;
  85. },
  86. // http://docs.jquery.com/Plugins/Validation/valid
  87. valid: function() {
  88. if ( $(this[0]).is("form")) {
  89. return this.validate().form();
  90. } else {
  91. var valid = true;
  92. var validator = $(this[0].form).validate();
  93. this.each(function() {
  94. valid = valid && validator.element(this);
  95. });
  96. return valid;
  97. }
  98. },
  99. // attributes: space seperated list of attributes to retrieve and remove
  100. removeAttrs: function( attributes ) {
  101. var result = {},
  102. $element = this;
  103. $.each(attributes.split(/\s/), function( index, value ) {
  104. result[value] = $element.attr(value);
  105. $element.removeAttr(value);
  106. });
  107. return result;
  108. },
  109. // http://docs.jquery.com/Plugins/Validation/rules
  110. rules: function( command, argument ) {
  111. var element = this[0];
  112. if ( command ) {
  113. var settings = $.data(element.form, "validator").settings;
  114. var staticRules = settings.rules;
  115. var existingRules = $.validator.staticRules(element);
  116. switch(command) {
  117. case "add":
  118. $.extend(existingRules, $.validator.normalizeRule(argument));
  119. // remove messages from rules, but allow them to be set separetely
  120. delete existingRules.messages;
  121. staticRules[element.name] = existingRules;
  122. if ( argument.messages ) {
  123. settings.messages[element.name] = $.extend( settings.messages[element.name], argument.messages );
  124. }
  125. break;
  126. case "remove":
  127. if ( !argument ) {
  128. delete staticRules[element.name];
  129. return existingRules;
  130. }
  131. var filtered = {};
  132. $.each(argument.split(/\s/), function( index, method ) {
  133. filtered[method] = existingRules[method];
  134. delete existingRules[method];
  135. });
  136. return filtered;
  137. }
  138. }
  139. var data = $.validator.normalizeRules(
  140. $.extend(
  141. {},
  142. $.validator.classRules(element),
  143. $.validator.attributeRules(element),
  144. $.validator.dataRules(element),
  145. $.validator.staticRules(element)
  146. ), element);
  147. // make sure required is at front
  148. if ( data.required ) {
  149. var param = data.required;
  150. delete data.required;
  151. data = $.extend({required: param}, data);
  152. }
  153. return data;
  154. }
  155. });
  156. // Custom selectors
  157. $.extend($.expr[":"], {
  158. // http://docs.jquery.com/Plugins/Validation/blank
  159. blank: function( a ) { return !$.trim("" + $(a).val()); },
  160. // http://docs.jquery.com/Plugins/Validation/filled
  161. filled: function( a ) { return !!$.trim("" + $(a).val()); },
  162. // http://docs.jquery.com/Plugins/Validation/unchecked
  163. unchecked: function( a ) { return !$(a).prop("checked"); }
  164. });
  165. // constructor for validator
  166. $.validator = function( options, form ) {
  167. this.settings = $.extend( true, {}, $.validator.defaults, options );
  168. this.currentForm = form;
  169. this.init();
  170. };
  171. $.validator.format = function( source, params ) {
  172. if ( arguments.length === 1 ) {
  173. return function() {
  174. var args = $.makeArray(arguments);
  175. args.unshift(source);
  176. return $.validator.format.apply( this, args );
  177. };
  178. }
  179. if ( arguments.length > 2 && params.constructor !== Array ) {
  180. params = $.makeArray(arguments).slice(1);
  181. }
  182. if ( params.constructor !== Array ) {
  183. params = [ params ];
  184. }
  185. $.each(params, function( i, n ) {
  186. source = source.replace( new RegExp("\\{" + i + "\\}", "g"), function() {
  187. return n;
  188. });
  189. });
  190. return source;
  191. };
  192. $.extend($.validator, {
  193. defaults: {
  194. messages: {},
  195. groups: {},
  196. rules: {},
  197. errorClass: "error",
  198. validClass: "valid",
  199. errorElement: "label",
  200. focusInvalid: true,
  201. errorContainer: $([]),
  202. errorLabelContainer: $([]),
  203. onsubmit: true,
  204. ignore: ":hidden",
  205. ignoreTitle: false,
  206. onfocusin: function( element, event ) {
  207. this.lastActive = element;
  208. // hide error label and remove error class on focus if enabled
  209. if ( this.settings.focusCleanup && !this.blockFocusCleanup ) {
  210. if ( this.settings.unhighlight ) {
  211. this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );
  212. }
  213. this.addWrapper(this.errorsFor(element)).hide();
  214. }
  215. },
  216. onfocusout: function( element, event ) {
  217. if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) {
  218. this.element(element);
  219. }
  220. },
  221. onkeyup: function( element, event ) {
  222. if ( event.which === 9 && this.elementValue(element) === "" ) {
  223. return;
  224. } else if ( element.name in this.submitted || element === this.lastElement ) {
  225. this.element(element);
  226. }
  227. },
  228. onclick: function( element, event ) {
  229. // click on selects, radiobuttons and checkboxes
  230. if ( element.name in this.submitted ) {
  231. this.element(element);
  232. }
  233. // or option elements, check parent select in that case
  234. else if ( element.parentNode.name in this.submitted ) {
  235. this.element(element.parentNode);
  236. }
  237. },
  238. highlight: function( element, errorClass, validClass ) {
  239. if ( element.type === "radio" ) {
  240. this.findByName(element.name).addClass(errorClass).removeClass(validClass);
  241. } else {
  242. $(element).addClass(errorClass).removeClass(validClass);
  243. }
  244. },
  245. unhighlight: function( element, errorClass, validClass ) {
  246. if ( element.type === "radio" ) {
  247. this.findByName(element.name).removeClass(errorClass).addClass(validClass);
  248. } else {
  249. $(element).removeClass(errorClass).addClass(validClass);
  250. }
  251. }
  252. },
  253. // http://docs.jquery.com/Plugins/Validation/Validator/setDefaults
  254. setDefaults: function( settings ) {
  255. $.extend( $.validator.defaults, settings );
  256. },
  257. messages: {
  258. required: "This field is required.",
  259. remote: "Please fix this field.",
  260. email: "Please enter a valid email address.",
  261. url: "Please enter a valid URL.",
  262. date: "Please enter a valid date.",
  263. dateISO: "Please enter a valid date (ISO).",
  264. number: "Please enter a valid number.",
  265. digits: "Please enter only digits.",
  266. creditcard: "Please enter a valid credit card number.",
  267. equalTo: "Please enter the same value again.",
  268. maxlength: $.validator.format("Please enter no more than {0} characters."),
  269. minlength: $.validator.format("Please enter at least {0} characters."),
  270. rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."),
  271. range: $.validator.format("Please enter a value between {0} and {1}."),
  272. max: $.validator.format("Please enter a value less than or equal to {0}."),
  273. min: $.validator.format("Please enter a value greater than or equal to {0}.")
  274. },
  275. autoCreateRanges: false,
  276. prototype: {
  277. init: function() {
  278. this.labelContainer = $(this.settings.errorLabelContainer);
  279. this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm);
  280. this.containers = $(this.settings.errorContainer).add( this.settings.errorLabelContainer );
  281. this.submitted = {};
  282. this.valueCache = {};
  283. this.pendingRequest = 0;
  284. this.pending = {};
  285. this.invalid = {};
  286. this.reset();
  287. var groups = (this.groups = {});
  288. $.each(this.settings.groups, function( key, value ) {
  289. if ( typeof value === "string" ) {
  290. value = value.split(/\s/);
  291. }
  292. $.each(value, function( index, name ) {
  293. groups[name] = key;
  294. });
  295. });
  296. var rules = this.settings.rules;
  297. $.each(rules, function( key, value ) {
  298. rules[key] = $.validator.normalizeRule(value);
  299. });
  300. function delegate(event) {
  301. var validator = $.data(this[0].form, "validator"),
  302. eventType = "on" + event.type.replace(/^validate/, "");
  303. if ( validator.settings[eventType] ) {
  304. validator.settings[eventType].call(validator, this[0], event);
  305. }
  306. }
  307. $(this.currentForm)
  308. .validateDelegate(":text, [type='password'], [type='file'], select, textarea, " +
  309. "[type='number'], [type='search'] ,[type='tel'], [type='url'], " +
  310. "[type='email'], [type='datetime'], [type='date'], [type='month'], " +
  311. "[type='week'], [type='time'], [type='datetime-local'], " +
  312. "[type='range'], [type='color'] ",
  313. "focusin focusout keyup", delegate)
  314. .validateDelegate("[type='radio'], [type='checkbox'], select, option", "click", delegate);
  315. if ( this.settings.invalidHandler ) {
  316. $(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);
  317. }
  318. },
  319. // http://docs.jquery.com/Plugins/Validation/Validator/form
  320. form: function() {
  321. this.checkForm();
  322. $.extend(this.submitted, this.errorMap);
  323. this.invalid = $.extend({}, this.errorMap);
  324. if ( !this.valid() ) {
  325. $(this.currentForm).triggerHandler("invalid-form", [this]);
  326. }
  327. this.showErrors();
  328. return this.valid();
  329. },
  330. checkForm: function() {
  331. this.prepareForm();
  332. for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) {
  333. this.check( elements[i] );
  334. }
  335. return this.valid();
  336. },
  337. // http://docs.jquery.com/Plugins/Validation/Validator/element
  338. element: function( element ) {
  339. element = this.validationTargetFor( this.clean( element ) );
  340. this.lastElement = element;
  341. this.prepareElement( element );
  342. this.currentElements = $(element);
  343. var result = this.check( element ) !== false;
  344. if ( result ) {
  345. delete this.invalid[element.name];
  346. } else {
  347. this.invalid[element.name] = true;
  348. }
  349. if ( !this.numberOfInvalids() ) {
  350. // Hide error containers on last error
  351. this.toHide = this.toHide.add( this.containers );
  352. }
  353. this.showErrors();
  354. return result;
  355. },
  356. // http://docs.jquery.com/Plugins/Validation/Validator/showErrors
  357. showErrors: function( errors ) {
  358. if ( errors ) {
  359. // add items to error list and map
  360. $.extend( this.errorMap, errors );
  361. this.errorList = [];
  362. for ( var name in errors ) {
  363. this.errorList.push({
  364. message: errors[name],
  365. element: this.findByName(name)[0]
  366. });
  367. }
  368. // remove items from success list
  369. this.successList = $.grep( this.successList, function( element ) {
  370. return !(element.name in errors);
  371. });
  372. }
  373. if ( this.settings.showErrors ) {
  374. this.settings.showErrors.call( this, this.errorMap, this.errorList );
  375. } else {
  376. this.defaultShowErrors();
  377. }
  378. },
  379. // http://docs.jquery.com/Plugins/Validation/Validator/resetForm
  380. resetForm: function() {
  381. if ( $.fn.resetForm ) {
  382. $(this.currentForm).resetForm();
  383. }
  384. this.submitted = {};
  385. this.lastElement = null;
  386. this.prepareForm();
  387. this.hideErrors();
  388. this.elements().removeClass( this.settings.errorClass ).removeData( "previousValue" );
  389. },
  390. numberOfInvalids: function() {
  391. return this.objectLength(this.invalid);
  392. },
  393. objectLength: function( obj ) {
  394. var count = 0;
  395. for ( var i in obj ) {
  396. count++;
  397. }
  398. return count;
  399. },
  400. hideErrors: function() {
  401. this.addWrapper( this.toHide ).hide();
  402. },
  403. valid: function() {
  404. return this.size() === 0;
  405. },
  406. size: function() {
  407. return this.errorList.length;
  408. },
  409. focusInvalid: function() {
  410. if ( this.settings.focusInvalid ) {
  411. try {
  412. $(this.findLastActive() || this.errorList.length && this.errorList[0].element || [])
  413. .filter(":visible")
  414. .focus()
  415. // manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
  416. .trigger("focusin");
  417. } catch(e) {
  418. // ignore IE throwing errors when focusing hidden elements
  419. }
  420. }
  421. },
  422. findLastActive: function() {
  423. var lastActive = this.lastActive;
  424. return lastActive && $.grep(this.errorList, function( n ) {
  425. return n.element.name === lastActive.name;
  426. }).length === 1 && lastActive;
  427. },
  428. elements: function() {
  429. var validator = this,
  430. rulesCache = {};
  431. // select all valid inputs inside the form (no submit or reset buttons)
  432. return $(this.currentForm)
  433. .find("input, select, textarea")
  434. .not(":submit, :reset, :image, [disabled]")
  435. .not( this.settings.ignore )
  436. .filter(function() {
  437. if ( !this.name && validator.settings.debug && window.console ) {
  438. console.error( "%o has no name assigned", this);
  439. }
  440. // select only the first element for each name, and only those with rules specified
  441. if ( this.name in rulesCache || !validator.objectLength($(this).rules()) ) {
  442. return false;
  443. }
  444. rulesCache[this.name] = true;
  445. return true;
  446. });
  447. },
  448. clean: function( selector ) {
  449. return $(selector)[0];
  450. },
  451. errors: function() {
  452. var errorClass = this.settings.errorClass.replace(" ", ".");
  453. return $(this.settings.errorElement + "." + errorClass, this.errorContext);
  454. },
  455. reset: function() {
  456. this.successList = [];
  457. this.errorList = [];
  458. this.errorMap = {};
  459. this.toShow = $([]);
  460. this.toHide = $([]);
  461. this.currentElements = $([]);
  462. },
  463. prepareForm: function() {
  464. this.reset();
  465. this.toHide = this.errors().add( this.containers );
  466. },
  467. prepareElement: function( element ) {
  468. this.reset();
  469. this.toHide = this.errorsFor(element);
  470. },
  471. elementValue: function( element ) {
  472. var type = $(element).attr("type"),
  473. val = $(element).val();
  474. if ( type === "radio" || type === "checkbox" ) {
  475. return $("input[name='" + $(element).attr("name") + "']:checked").val();
  476. }
  477. if ( typeof val === "string" ) {
  478. return val.replace(/\r/g, "");
  479. }
  480. return val;
  481. },
  482. check: function( element ) {
  483. element = this.validationTargetFor( this.clean( element ) );
  484. var rules = $(element).rules();
  485. var dependencyMismatch = false;
  486. var val = this.elementValue(element);
  487. var result;
  488. for (var method in rules ) {
  489. var rule = { method: method, parameters: rules[method] };
  490. try {
  491. result = $.validator.methods[method].call( this, val, element, rule.parameters );
  492. // if a method indicates that the field is optional and therefore valid,
  493. // don't mark it as valid when there are no other rules
  494. if ( result === "dependency-mismatch" ) {
  495. dependencyMismatch = true;
  496. continue;
  497. }
  498. dependencyMismatch = false;
  499. if ( result === "pending" ) {
  500. this.toHide = this.toHide.not( this.errorsFor(element) );
  501. return;
  502. }
  503. if ( !result ) {
  504. this.formatAndAdd( element, rule );
  505. return false;
  506. }
  507. } catch(e) {
  508. if ( this.settings.debug && window.console ) {
  509. console.log( "Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e );
  510. }
  511. throw e;
  512. }
  513. }
  514. if ( dependencyMismatch ) {
  515. return;
  516. }
  517. if ( this.objectLength(rules) ) {
  518. this.successList.push(element);
  519. }
  520. return true;
  521. },
  522. // return the custom message for the given element and validation method
  523. // specified in the element's HTML5 data attribute
  524. customDataMessage: function( element, method ) {
  525. return $(element).data("msg-" + method.toLowerCase()) || (element.attributes && $(element).attr("data-msg-" + method.toLowerCase()));
  526. },
  527. // return the custom message for the given element name and validation method
  528. customMessage: function( name, method ) {
  529. var m = this.settings.messages[name];
  530. return m && (m.constructor === String ? m : m[method]);
  531. },
  532. // return the first defined argument, allowing empty strings
  533. findDefined: function() {
  534. for(var i = 0; i < arguments.length; i++) {
  535. if ( arguments[i] !== undefined ) {
  536. return arguments[i];
  537. }
  538. }
  539. return undefined;
  540. },
  541. defaultMessage: function( element, method ) {
  542. return this.findDefined(
  543. this.customMessage( element.name, method ),
  544. this.customDataMessage( element, method ),
  545. // title is never undefined, so handle empty string as undefined
  546. !this.settings.ignoreTitle && element.title || undefined,
  547. $.validator.messages[method],
  548. "<strong>Warning: No message defined for " + element.name + "</strong>"
  549. );
  550. },
  551. formatAndAdd: function( element, rule ) {
  552. var message = this.defaultMessage( element, rule.method ),
  553. theregex = /\$?\{(\d+)\}/g;
  554. if ( typeof message === "function" ) {
  555. message = message.call(this, rule.parameters, element);
  556. } else if (theregex.test(message)) {
  557. message = $.validator.format(message.replace(theregex, "{$1}"), rule.parameters);
  558. }
  559. this.errorList.push({
  560. message: message,
  561. element: element
  562. });
  563. this.errorMap[element.name] = message;
  564. this.submitted[element.name] = message;
  565. },
  566. addWrapper: function( toToggle ) {
  567. if ( this.settings.wrapper ) {
  568. toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );
  569. }
  570. return toToggle;
  571. },
  572. defaultShowErrors: function() {
  573. var i, elements;
  574. for ( i = 0; this.errorList[i]; i++ ) {
  575. var error = this.errorList[i];
  576. if ( this.settings.highlight ) {
  577. this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );
  578. }
  579. this.showLabel( error.element, error.message );
  580. }
  581. if ( this.errorList.length ) {
  582. this.toShow = this.toShow.add( this.containers );
  583. }
  584. if ( this.settings.success ) {
  585. for ( i = 0; this.successList[i]; i++ ) {
  586. this.showLabel( this.successList[i] );
  587. }
  588. }
  589. if ( this.settings.unhighlight ) {
  590. for ( i = 0, elements = this.validElements(); elements[i]; i++ ) {
  591. this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass );
  592. }
  593. }
  594. this.toHide = this.toHide.not( this.toShow );
  595. this.hideErrors();
  596. this.addWrapper( this.toShow ).show();
  597. },
  598. validElements: function() {
  599. return this.currentElements.not(this.invalidElements());
  600. },
  601. invalidElements: function() {
  602. return $(this.errorList).map(function() {
  603. return this.element;
  604. });
  605. },
  606. showLabel: function( element, message ) {
  607. var label = this.errorsFor( element );
  608. if ( label.length ) {
  609. // refresh error/success class
  610. label.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );
  611. // replace message on existing label
  612. label.html(message);
  613. } else {
  614. // create label
  615. label = $("<" + this.settings.errorElement + ">")
  616. .attr("for", this.idOrName(element))
  617. .addClass(this.settings.errorClass)
  618. .html(message || "");
  619. if ( this.settings.wrapper ) {
  620. // make sure the element is visible, even in IE
  621. // actually showing the wrapped element is handled elsewhere
  622. label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
  623. }
  624. if ( !this.labelContainer.append(label).length ) {
  625. if ( this.settings.errorPlacement ) {
  626. this.settings.errorPlacement(label, $(element) );
  627. } else {
  628. label.insertAfter(element);
  629. }
  630. }
  631. }
  632. if ( !message && this.settings.success ) {
  633. label.text("");
  634. if ( typeof this.settings.success === "string" ) {
  635. label.addClass( this.settings.success );
  636. } else {
  637. this.settings.success( label, element );
  638. }
  639. }
  640. this.toShow = this.toShow.add(label);
  641. },
  642. errorsFor: function( element ) {
  643. var name = this.idOrName(element);
  644. return this.errors().filter(function() {
  645. return $(this).attr("for") === name;
  646. });
  647. },
  648. idOrName: function( element ) {
  649. return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name);
  650. },
  651. validationTargetFor: function( element ) {
  652. // if radio/checkbox, validate first element in group instead
  653. if ( this.checkable(element) ) {
  654. element = this.findByName( element.name ).not(this.settings.ignore)[0];
  655. }
  656. return element;
  657. },
  658. checkable: function( element ) {
  659. return (/radio|checkbox/i).test(element.type);
  660. },
  661. findByName: function( name ) {
  662. return $(this.currentForm).find("[name='" + name + "']");
  663. },
  664. getLength: function( value, element ) {
  665. switch( element.nodeName.toLowerCase() ) {
  666. case "select":
  667. return $("option:selected", element).length;
  668. case "input":
  669. if ( this.checkable( element) ) {
  670. return this.findByName(element.name).filter(":checked").length;
  671. }
  672. }
  673. return value.length;
  674. },
  675. depend: function( param, element ) {
  676. return this.dependTypes[typeof param] ? this.dependTypes[typeof param](param, element) : true;
  677. },
  678. dependTypes: {
  679. "boolean": function( param, element ) {
  680. return param;
  681. },
  682. "string": function( param, element ) {
  683. return !!$(param, element.form).length;
  684. },
  685. "function": function( param, element ) {
  686. return param(element);
  687. }
  688. },
  689. optional: function( element ) {
  690. var val = this.elementValue(element);
  691. return !$.validator.methods.required.call(this, val, element) && "dependency-mismatch";
  692. },
  693. startRequest: function( element ) {
  694. if ( !this.pending[element.name] ) {
  695. this.pendingRequest++;
  696. this.pending[element.name] = true;
  697. }
  698. },
  699. stopRequest: function( element, valid ) {
  700. this.pendingRequest--;
  701. // sometimes synchronization fails, make sure pendingRequest is never < 0
  702. if ( this.pendingRequest < 0 ) {
  703. this.pendingRequest = 0;
  704. }
  705. delete this.pending[element.name];
  706. if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() ) {
  707. $(this.currentForm).submit();
  708. this.formSubmitted = false;
  709. } else if (!valid && this.pendingRequest === 0 && this.formSubmitted) {
  710. $(this.currentForm).triggerHandler("invalid-form", [this]);
  711. this.formSubmitted = false;
  712. }
  713. },
  714. previousValue: function( element ) {
  715. return $.data(element, "previousValue") || $.data(element, "previousValue", {
  716. old: null,
  717. valid: true,
  718. message: this.defaultMessage( element, "remote" )
  719. });
  720. }
  721. },
  722. classRuleSettings: {
  723. required: {required: true},
  724. email: {email: true},
  725. url: {url: true},
  726. date: {date: true},
  727. dateISO: {dateISO: true},
  728. number: {number: true},
  729. digits: {digits: true},
  730. creditcard: {creditcard: true}
  731. },
  732. addClassRules: function( className, rules ) {
  733. if ( className.constructor === String ) {
  734. this.classRuleSettings[className] = rules;
  735. } else {
  736. $.extend(this.classRuleSettings, className);
  737. }
  738. },
  739. classRules: function( element ) {
  740. var rules = {};
  741. var classes = $(element).attr("class");
  742. if ( classes ) {
  743. $.each(classes.split(" "), function() {
  744. if ( this in $.validator.classRuleSettings ) {
  745. $.extend(rules, $.validator.classRuleSettings[this]);
  746. }
  747. });
  748. }
  749. return rules;
  750. },
  751. attributeRules: function( element ) {
  752. var rules = {};
  753. var $element = $(element);
  754. var type = $element[0].getAttribute("type");
  755. for (var method in $.validator.methods) {
  756. var value;
  757. // support for <input required> in both html5 and older browsers
  758. if ( method === "required" ) {
  759. value = $element.get(0).getAttribute(method);
  760. // Some browsers return an empty string for the required attribute
  761. // and non-HTML5 browsers might have required="" markup
  762. if ( value === "" ) {
  763. value = true;
  764. }
  765. // force non-HTML5 browsers to return bool
  766. value = !!value;
  767. } else {
  768. value = $element.attr(method);
  769. }
  770. // convert the value to a number for number inputs, and for text for backwards compability
  771. // allows type="date" and others to be compared as strings
  772. if ( /min|max/.test( method ) && ( type === null || /number|range|text/.test( type ) ) ) {
  773. value = Number(value);
  774. }
  775. if ( value ) {
  776. rules[method] = value;
  777. } else if ( type === method && type !== 'range' ) {
  778. // exception: the jquery validate 'range' method
  779. // does not test for the html5 'range' type
  780. rules[method] = true;
  781. }
  782. }
  783. // maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs
  784. if ( rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength) ) {
  785. delete rules.maxlength;
  786. }
  787. return rules;
  788. },
  789. dataRules: function( element ) {
  790. var method, value,
  791. rules = {}, $element = $(element);
  792. for (method in $.validator.methods) {
  793. value = $element.data("rule-" + method.toLowerCase());
  794. if ( value !== undefined ) {
  795. rules[method] = value;
  796. }
  797. }
  798. return rules;
  799. },
  800. staticRules: function( element ) {
  801. var rules = {};
  802. var validator = $.data(element.form, "validator");
  803. if ( validator.settings.rules ) {
  804. rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {};
  805. }
  806. return rules;
  807. },
  808. normalizeRules: function( rules, element ) {
  809. // handle dependency check
  810. $.each(rules, function( prop, val ) {
  811. // ignore rule when param is explicitly false, eg. required:false
  812. if ( val === false ) {
  813. delete rules[prop];
  814. return;
  815. }
  816. if ( val.param || val.depends ) {
  817. var keepRule = true;
  818. switch (typeof val.depends) {
  819. case "string":
  820. keepRule = !!$(val.depends, element.form).length;
  821. break;
  822. case "function":
  823. keepRule = val.depends.call(element, element);
  824. break;
  825. }
  826. if ( keepRule ) {
  827. rules[prop] = val.param !== undefined ? val.param : true;
  828. } else {
  829. delete rules[prop];
  830. }
  831. }
  832. });
  833. // evaluate parameters
  834. $.each(rules, function( rule, parameter ) {
  835. rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter;
  836. });
  837. // clean number parameters
  838. $.each(['minlength', 'maxlength'], function() {
  839. if ( rules[this] ) {
  840. rules[this] = Number(rules[this]);
  841. }
  842. });
  843. $.each(['rangelength', 'range'], function() {
  844. var parts;
  845. if ( rules[this] ) {
  846. if ( $.isArray(rules[this]) ) {
  847. rules[this] = [Number(rules[this][0]), Number(rules[this][1])];
  848. } else if ( typeof rules[this] === "string" ) {
  849. parts = rules[this].split(/[\s,]+/);
  850. rules[this] = [Number(parts[0]), Number(parts[1])];
  851. }
  852. }
  853. });
  854. if ( $.validator.autoCreateRanges ) {
  855. // auto-create ranges
  856. if ( rules.min && rules.max ) {
  857. rules.range = [rules.min, rules.max];
  858. delete rules.min;
  859. delete rules.max;
  860. }
  861. if ( rules.minlength && rules.maxlength ) {
  862. rules.rangelength = [rules.minlength, rules.maxlength];
  863. delete rules.minlength;
  864. delete rules.maxlength;
  865. }
  866. }
  867. return rules;
  868. },
  869. // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
  870. normalizeRule: function( data ) {
  871. if ( typeof data === "string" ) {
  872. var transformed = {};
  873. $.each(data.split(/\s/), function() {
  874. transformed[this] = true;
  875. });
  876. data = transformed;
  877. }
  878. return data;
  879. },
  880. // http://docs.jquery.com/Plugins/Validation/Validator/addMethod
  881. addMethod: function( name, method, message ) {
  882. $.validator.methods[name] = method;
  883. $.validator.messages[name] = message !== undefined ? message : $.validator.messages[name];
  884. if ( method.length < 3 ) {
  885. $.validator.addClassRules(name, $.validator.normalizeRule(name));
  886. }
  887. },
  888. methods: {
  889. // http://docs.jquery.com/Plugins/Validation/Methods/required
  890. required: function( value, element, param ) {
  891. // check if dependency is met
  892. if ( !this.depend(param, element) ) {
  893. return "dependency-mismatch";
  894. }
  895. if ( element.nodeName.toLowerCase() === "select" ) {
  896. // could be an array for select-multiple or a string, both are fine this way
  897. var val = $(element).val();
  898. return val && val.length > 0;
  899. }
  900. if ( this.checkable(element) ) {
  901. return this.getLength(value, element) > 0;
  902. }
  903. return $.trim(value).length > 0;
  904. },
  905. // http://docs.jquery.com/Plugins/Validation/Methods/email
  906. email: function( value, element ) {
  907. // contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
  908. return this.optional(element) || /^((([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.test(value);
  909. },
  910. // http://docs.jquery.com/Plugins/Validation/Methods/url
  911. url: function( value, element ) {
  912. // contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/
  913. return this.optional(element) || /^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
  914. },
  915. // http://docs.jquery.com/Plugins/Validation/Methods/date
  916. date: function( value, element ) {
  917. return this.optional(element) || !/Invalid|NaN/.test(new Date(value).toString());
  918. },
  919. // http://docs.jquery.com/Plugins/Validation/Methods/dateISO
  920. dateISO: function( value, element ) {
  921. return this.optional(element) || /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/.test(value);
  922. },
  923. // http://docs.jquery.com/Plugins/Validation/Methods/number
  924. number: function( value, element ) {
  925. return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value);
  926. },
  927. // http://docs.jquery.com/Plugins/Validation/Methods/digits
  928. digits: function( value, element ) {
  929. return this.optional(element) || /^\d+$/.test(value);
  930. },
  931. // http://docs.jquery.com/Plugins/Validation/Methods/creditcard
  932. // based on http://en.wikipedia.org/wiki/Luhn
  933. creditcard: function( value, element ) {
  934. if ( this.optional(element) ) {
  935. return "dependency-mismatch";
  936. }
  937. // accept only spaces, digits and dashes
  938. if ( /[^0-9 \-]+/.test(value) ) {
  939. return false;
  940. }
  941. var nCheck = 0,
  942. nDigit = 0,
  943. bEven = false;
  944. value = value.replace(/\D/g, "");
  945. for (var n = value.length - 1; n >= 0; n--) {
  946. var cDigit = value.charAt(n);
  947. nDigit = parseInt(cDigit, 10);
  948. if ( bEven ) {
  949. if ( (nDigit *= 2) > 9 ) {
  950. nDigit -= 9;
  951. }
  952. }
  953. nCheck += nDigit;
  954. bEven = !bEven;
  955. }
  956. return (nCheck % 10) === 0;
  957. },
  958. // http://docs.jquery.com/Plugins/Validation/Methods/minlength
  959. minlength: function( value, element, param ) {
  960. var length = $.isArray( value ) ? value.length : this.getLength($.trim(value), element);
  961. return this.optional(element) || length >= param;
  962. },
  963. // http://docs.jquery.com/Plugins/Validation/Methods/maxlength
  964. maxlength: function( value, element, param ) {
  965. var length = $.isArray( value ) ? value.length : this.getLength($.trim(value), element);
  966. return this.optional(element) || length <= param;
  967. },
  968. // http://docs.jquery.com/Plugins/Validation/Methods/rangelength
  969. rangelength: function( value, element, param ) {
  970. var length = $.isArray( value ) ? value.length : this.getLength($.trim(value), element);
  971. return this.optional(element) || ( length >= param[0] && length <= param[1] );
  972. },
  973. // http://docs.jquery.com/Plugins/Validation/Methods/min
  974. min: function( value, element, param ) {
  975. return this.optional(element) || value >= param;
  976. },
  977. // http://docs.jquery.com/Plugins/Validation/Methods/max
  978. max: function( value, element, param ) {
  979. return this.optional(element) || value <= param;
  980. },
  981. // http://docs.jquery.com/Plugins/Validation/Methods/range
  982. range: function( value, element, param ) {
  983. return this.optional(element) || ( value >= param[0] && value <= param[1] );
  984. },
  985. // http://docs.jquery.com/Plugins/Validation/Methods/equalTo
  986. equalTo: function( value, element, param ) {
  987. // bind to the blur event of the target in order to revalidate whenever the target field is updated
  988. // TODO find a way to bind the event just once, avoiding the unbind-rebind overhead
  989. var target = $(param);
  990. if ( this.settings.onfocusout ) {
  991. target.unbind(".validate-equalTo").bind("blur.validate-equalTo", function() {
  992. $(element).valid();
  993. });
  994. }
  995. return value === target.val();
  996. },
  997. // http://docs.jquery.com/Plugins/Validation/Methods/remote
  998. remote: function( value, element, param ) {
  999. if ( this.optional(element) ) {
  1000. return "dependency-mismatch";
  1001. }
  1002. var previous = this.previousValue(element);
  1003. if (!this.settings.messages[element.name] ) {
  1004. this.settings.messages[element.name] = {};
  1005. }
  1006. previous.originalMessage = this.settings.messages[element.name].remote;
  1007. this.settings.messages[element.name].remote = previous.message;
  1008. param = typeof param === "string" && {url:param} || param;
  1009. if ( previous.old === value ) {
  1010. return previous.valid;
  1011. }
  1012. previous.old = value;
  1013. var validator = this;
  1014. this.startRequest(element);
  1015. var data = {};
  1016. data[element.name] = value;
  1017. $.ajax($.extend(true, {
  1018. url: param,
  1019. mode: "abort",
  1020. port: "validate" + element.name,
  1021. dataType: "json",
  1022. data: data,
  1023. success: function( response ) {
  1024. validator.settings.messages[element.name].remote = previous.originalMessage;
  1025. var valid = response === true || response === "true";
  1026. if ( valid ) {
  1027. var submitted = validator.formSubmitted;
  1028. validator.prepareElement(element);
  1029. validator.formSubmitted = submitted;
  1030. validator.successList.push(element);
  1031. delete validator.invalid[element.name];
  1032. validator.showErrors();
  1033. } else {
  1034. var errors = {};
  1035. var message = response || validator.defaultMessage( element, "remote" );
  1036. errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message;
  1037. validator.invalid[element.name] = true;
  1038. validator.showErrors(errors);
  1039. }
  1040. previous.valid = valid;
  1041. validator.stopRequest(element, valid);
  1042. }
  1043. }, param));
  1044. return "pending";
  1045. }
  1046. }
  1047. });
  1048. // deprecated, use $.validator.format instead
  1049. $.format = $.validator.format;
  1050. }(jQuery));
  1051. // ajax mode: abort
  1052. // usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
  1053. // if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
  1054. (function($) {
  1055. var pendingRequests = {};
  1056. // Use a prefilter if available (1.5+)
  1057. if ( $.ajaxPrefilter ) {
  1058. $.ajaxPrefilter(function( settings, _, xhr ) {
  1059. var port = settings.port;
  1060. if ( settings.mode === "abort" ) {
  1061. if ( pendingRequests[port] ) {
  1062. pendingRequests[port].abort();
  1063. }
  1064. pendingRequests[port] = xhr;
  1065. }
  1066. });
  1067. } else {
  1068. // Proxy ajax
  1069. var ajax = $.ajax;
  1070. $.ajax = function( settings ) {
  1071. var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode,
  1072. port = ( "port" in settings ? settings : $.ajaxSettings ).port;
  1073. if ( mode === "abort" ) {
  1074. if ( pendingRequests[port] ) {
  1075. pendingRequests[port].abort();
  1076. }
  1077. pendingRequests[port] = ajax.apply(this, arguments);
  1078. return pendingRequests[port];
  1079. }
  1080. return ajax.apply(this, arguments);
  1081. };
  1082. }
  1083. }(jQuery));
  1084. // provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation
  1085. // handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target
  1086. (function($) {
  1087. $.extend($.fn, {
  1088. validateDelegate: function( delegate, type, handler ) {
  1089. return this.bind(type, function( event ) {
  1090. var target = $(event.target);
  1091. if ( target.is(delegate) ) {
  1092. return handler.apply(target, arguments);
  1093. }
  1094. });
  1095. }
  1096. });
  1097. }(jQuery));