jquery.validate.js 38 KB

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