You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1262 lines
39 KiB

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