jquery.validate.js 37 KB

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