jquery.validate.js 37 KB

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