jquery.validate.js 40 KB

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