1/* 2 * Inline Form Validation Engine 2.2, jQuery plugin 3 * 4 * Copyright(c) 2010, Cedric Dugas 5 * http://www.position-absolute.com 6 * 7 * 2.0 Rewrite by Olivier Refalo 8 * http://www.crionics.com 9 * 10 * Form validation engine allowing custom regex rules to be added. 11 * Licensed under the MIT License 12 */ 13(function($) { 14 15 var methods = { 16 17 /** 18 * Kind of the constructor, called before any action 19 * @param {Map} user options 20 */ 21 init: function(options) { 22 var form = this; 23 if (!form.data('jqv') || form.data('jqv') == null ) { 24 methods._saveOptions(form, options); 25 26 // bind all formError elements to close on click 27 //$(".formError").live("click", function() { 28 $(".formError").on("click", function () { 29 $(this).fadeOut(150, function() { 30 31 // remove prompt once invisible 32 $(this).remove(); 33 }); 34 }); 35 } 36 }, 37 /** 38 * Attachs jQuery.validationEngine to form.submit and field.blur events 39 * Takes an optional params: a list of options 40 * ie. jQuery("#formID1").validationEngine('attach', {promptPosition : "centerRight"}); 41 */ 42 attach: function(userOptions) { 43 var form = this; 44 var options; 45 46 if(userOptions) 47 options = methods._saveOptions(form, userOptions); 48 else 49 options = form.data('jqv'); 50 51 var validateAttribute = (form.find("[data-validation-engine*=validate]")) ? "data-validation-engine" : "class"; 52 53 if (!options.binded) { 54 if (options.bindMethod == "bind"){ 55 56 // bind fields 57 form.find("[class*=validate]:not([type=checkbox])").bind(options.validationEventTrigger, methods._onFieldEvent); 58 form.find("[class*=validate][type=checkbox]").bind("click", methods._onFieldEvent); 59 60 // bind form.submit 61 form.bind("submit", methods._onSubmitEvent); 62 } else if (options.bindMethod == "live") { 63 // bind fields with LIVE (for persistant state) 64 form.find("[class*=validate]:not([type=checkbox])").live(options.validationEventTrigger, methods._onFieldEvent); 65 form.find("[class*=validate][type=checkbox]").live("click", methods._onFieldEvent); 66 67 // bind form.submit 68 form.live("submit", methods._onSubmitEvent); 69 } 70 71 options.binded = true; 72 } 73 74 }, 75 /** 76 * Unregisters any bindings that may point to jQuery.validaitonEngine 77 */ 78 detach: function() { 79 var form = this; 80 var options = form.data('jqv'); 81 if (options.binded) { 82 83 // unbind fields 84 form.find("[class*=validate]").not("[type=checkbox]").unbind(options.validationEventTrigger, methods._onFieldEvent); 85 form.find("[class*=validate][type=checkbox]").unbind("click", methods._onFieldEvent); 86 // unbind form.submit 87 form.unbind("submit", methods.onAjaxFormComplete); 88 89 90 // unbind live fields (kill) 91 form.find("[class*=validate]").not("[type=checkbox]").die(options.validationEventTrigger, methods._onFieldEvent); 92 form.find("[class*=validate][type=checkbox]").die("click", methods._onFieldEvent); 93 // unbind form.submit 94 form.die("submit", methods.onAjaxFormComplete); 95 96 form.removeData('jqv'); 97 } 98 }, 99 /** 100 * Validates the form fields, shows prompts accordingly. 101 * Note: There is no ajax form validation with this method, only field ajax validation are evaluated 102 * 103 * @return true if the form validates, false if it fails 104 */ 105 validate: function() { 106 return methods._validateFields(this); 107 }, 108 /** 109 * Validates one field, shows prompt accordingly. 110 * Note: There is no ajax form validation with this method, only field ajax validation are evaluated 111 * 112 * @return true if the form validates, false if it fails 113 */ 114 validateField: function(el) { 115 var options = $(this).data('jqv'); 116 return methods._validateField($(el), options); 117 }, 118 /** 119 * Validates the form fields, shows prompts accordingly. 120 * Note: this methods performs fields and form ajax validations(if setup) 121 * 122 * @return true if the form validates, false if it fails, undefined if ajax is used for form validation 123 */ 124 validateform: function() { 125 return methods._onSubmitEvent.call(this); 126 }, 127 /**
128 * Displays a prompt on a element. 129 * Note that the element needs an id! 130 * 131 * @param {String} promptText html text to display type 132 * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red) 133 * @param {String} possible values topLeft, topRight, bottomLeft, centerRight, bottomRight 134 */ 135 showPrompt: function(promptText, type, promptPosition, showArrow) { 136 137 var form = this.closest('form'); 138 var options = form.data('jqv'); 139 // No option, take default one 140 if(!options) options = methods._saveOptions(this, options); 141 if(promptPosition) 142 options.promptPosition=promptPosition; 143 options.showArrow = showArrow==true; 144 145 methods._showPrompt(this, promptText, type, false, options); 146 }, 147 /** 148 * Closes all error prompts on the page 149 */ 150 hidePrompt: function() { 151 var promptClass = "."+ methods._getClassName($(this).attr("id")) + "formError"; 152 $(promptClass).fadeTo("fast", 0.3, function() { 153 $(this).remove(); 154 }); 155 }, 156 /** 157 * Closes form error prompts, CAN be invidual 158 */ 159 hide: function() { 160 var closingtag; 161 if($(this).is("form")){ 162 closingtag = "parentForm"+$(this).attr('id'); 163 }else{ 164 closingtag = $(this).attr('id') +"formError"; 165 } 166 $('.'+closingtag).fadeTo("fast", 0.3, function() { 167 $(this).remove(); 168 }); 169 }, 170 /** 171 * Closes all error prompts on the page 172 */ 173 hideAll: function() { 174 $('.formError').fadeTo("fast", 0.3, function() { 175 $(this).remove(); 176 }); 177 }, 178 /** 179 * Typically called when user exists a field using tab or a mouse click, triggers a field 180 * validation 181 */ 182 _onFieldEvent: function() { 183 var field = $(this); 184 var form = field.closest('form'); 185 var options = form.data('jqv'); 186 // validate the current field 187 methods._validateField(field, options); 188 }, 189 /** 190 * Called when the form is submited, shows prompts accordingly 191 * 192 * @param {jqObject} 193 * form 194 * @return false if form submission needs to be cancelled 195 */ 196 _onSubmitEvent: function() { 197 var form = $(this); 198 var options = form.data('jqv'); 199 200 // validate each field (- skip field ajax validation, no necessary since we will perform an ajax form validation) 201 var r=methods._validateFields(form, true); 202 203 if (r && options.ajaxFormValidation) { 204 methods._validateFormWithAjax(form, options); 205 return false; 206 } 207 208 if(options.onValidationComplete) { 209 options.onValidationComplete(form, r); 210 return false; 211 } 212 return r; 213 }, 214 215 /** 216 * Return true if the ajax field validations passed so far 217 * @param {Object} options 218 * @return true, is all ajax validation passed so far (remember aja
218x is async) 219 */ 220 _checkAjaxStatus: function(options) { 221 var status = true; 222 $.each(options.ajaxValidCache, function(key, value) { 223 if (!value) { 224 status = false; 225 // break the each 226 return false; 227 } 228 }); 229 return status; 230 }, 231 /** 232 * Validates form fields, shows prompts accordingly 233 * 234 * @param {jqObject} 235 * form 236 * @param {skipAjaxFieldValidation} 237 * boolean - when set to true, ajax field validation is skipped, typically used when the submit button is clicked 238 * 239 * @return true if form is valid, false if not, undefined if ajax form validation is done 240 */ 241 _validateFields: function(form, skipAjaxValidation) { 242 var options = form.data('jqv'); 243 244 // this variable is set to true if an error is found 245 var errorFound = false; 246 247 // Trigger hook, start validation 248 form.trigger("jqv.form.validating"); 249 // first, evaluate status of non ajax fields 250 form.find('[class*=validate]').not(':hidden').each( function() { 251 var field = $(this); 252 errorFound |= methods._validateField(field, options, skipAjaxValidation); 253 }); 254 // second, check to see if all ajax calls completed ok 255 // errorFound |= !methods._checkAjaxStatus(options); 256 257 // thrird, check status and scroll the container accordingly 258 form.trigger("jqv.form.result", [errorFound]); 259 260 if (errorFound) { 261 262 if (options.scroll) { 263 264 // get the position of the first error, there should be at least one, no need to check this 265 //var destination = form.find(".formError:not('.greenPopup'):first").offset().top; 266 267 // look for the visually top prompt 268 var destination = Number.MAX_VALUE; 269 var fixleft = 0; 270 var lst = $(".formError:not('.greenPopup')"); 271 272 for (var i = 0; i < lst.length; i++) { 273 var d = $(lst[i]).offset().top; 274 if (d < destination){ 275 destination = d; 276 fixleft = $(lst[i]).offset().left; 277 } 278 } 279 280 if (!options.isOverflown) 281 $("html:not(:animated),body:not(:animated)").animate({ 282 scrollTop: destination, 283 scrollLeft: fixleft 284 }, 1100); 285 else { 286 var overflowDIV = $(options.overflownDIV); 287 var scrollContainerScroll = overflowDIV.scrollTop(); 288 var scrollContainerPos = -parseInt(overflowDIV.offset().top); 289 290 destination += scrollContainerScroll + scrollContainerPos - 5; 291 var scrollContainer = $(options.overflownDIV + ":not(:animated)"); 292 293 scrollContainer.animate({ 294 scrollTop: destination 295 }, 1100); 296 297 $("html:not(:animated),body:not(:animated)").animate({ 298 scrollTop: overflowDIV.offset().top, 299 scrollLeft: fixleft 300 }, 1100); 301 } 302 } 303 return false; 304 } 305 return true; 306 }, 307 /** 308 * This method is called to perform an ajax form validation. 309 * During this process all the (field, value) pairs are sent to the server which returns a list of invalid fields or true 310 * 311 * @param {jqObject} form 312 * @param {Map} options 313 */ 314 _validateFormWithAjax: function(form, options) { 315 316 var data = form.serialize(); 317 var url = (options.ajaxFormValidationURL) ? options.ajaxFormValidationURL : form.attr("action"); 318 $.ajax({ 319 type: "GET", 320 url: url, 321 cache: false, 322 dataType: "json", 323 data: data, 324 form: form, 325 methods: methods, 326 options: options, 327 beforeSend: function() { 328 return options.onBeforeAjaxFormValidation(form, options); 329 }, 330 error: function(data, transport) { 331 methods._ajaxError(data, transport); 332 }, 333 success: function(json) { 334 335 if (json !== true) { 336 337 // getting to this case doesn't necessary means that the form is invali
337d 338 // the server may return green or closing prompt actions 339 // this flag helps figuring it out 340 var errorInForm=false; 341 for (var i = 0; i < json.length; i++) { 342 var value = json[i]; 343 344 var errorFieldId = value[0]; 345 var errorField = $($("#" + errorFieldId)[0]); 346 347 // make sure we found the element 348 if (errorField.length == 1) { 349 350 // promptText or selector 351 var msg = value[2]; 352 // if the field is valid 353 if (value[1] == true) { 354 355 if (msg == "" || !msg){ 356 // if for some reason, status==true and error="", just close the prompt 357 methods._closePrompt(errorField); 358 } else { 359 // the field is valid, but we are displaying a green prompt 360 if (options.allrules[msg]) { 361 var txt = options.allrules[msg].alertTextOk; 362 if (txt) 363 msg = txt; 364 } 365 methods._showPrompt(errorField, msg, "pass", false, options, true); 366 } 367 368 } else { 369 // the field is invalid, show the red error prompt 370 errorInForm|=true; 371 if (options.allrules[msg]) { 372 var txt = options.allrules[msg].alertText; 373 if (txt) 374 msg = txt; 375 } 376 methods._showPrompt(errorField, msg, "", false, options, true); 377 } 378 } 379 } 380 options.onAjaxFormComplete(!errorInForm, form, json, options); 381 } else 382 options.onAjaxFormComplete(true, form, "", options); 383 } 384 }); 385 386 }, 387 /** 388 * Validates field, shows prompts accordingly 389 * 390 * @param {jqObject} 391 * field 392 * @param {Array[String]} 393 * field's validation rules 394 * @param {Map} 395 * user options 396 * @return true if field is valid 397 */ 398 _validateField: function(field, options, skipAjaxValidation) { 399 if (!field.attr("id")) 400 $.error("jQueryValidate: an ID attribute is required for this field: " + field.attr("name") + " class:" + 401 field.attr("class")); 402 403 var rulesParsing = field.attr('class'); 404 var getRules = /validate\[(.*)\]/.exec(rulesParsing); 405 if (!getRules) 406 return false; 407 var str = getRules[1]; 408 var rules = str.split(/\[|,|\]/); 409 410 // true if we ran the ajax validation, tells the logic to stop messing with prompts 411 var isAjaxValidator = false; 412 var fieldName = field.attr("name"); 413 var promptText = ""; 414 var required = false; 415 options.isError = false; 416 options.showArrow = true; 417 418 for (var i = 0; i < rules.length; i++) { 419 420 var errorMsg = undefined; 421 switch (rules[i]) { 422 423 case "required": 424 required = true; 425 errorMsg = methods._required(field, rules, i, options); 426 break; 427 case "custom": 428 errorMsg = methods._customRegex(field, rules, i, options); 429 break; 430 case "ajax":
431 // ajax has its own prompts handling technique 432 if(!skipAjaxValidation){ 433 errorMsg = methods._ajax(field, rules, i, options); 434 isAjaxValidator = true; 435 436 } 437 break; 438 case "minSize": 439 errorMsg = methods._minSize(field, rules, i, options); 440 break; 441 case "maxSize": 442 errorMsg = methods._maxSize(field, rules, i, options); 443 break; 444 case "min": 445 errorMsg = methods._min(field, rules, i, options); 446 break; 447 case "max": 448 errorMsg = methods._max(field, rules, i, options); 449 break; 450 case "past": 451 errorMsg = methods._past(field, rules, i, options); 452 break; 453 case "future": 454 errorMsg = methods._future(field, rules, i, options); 455 break; 456 case "maxCheckbox": 457 errorMsg = methods._maxCheckbox(field, rules, i, options); 458 field = $($("input[name='" + fieldName + "']")); 459 break; 460 case "minCheckbox": 461 errorMsg = methods._minCheckbox(field, rules, i, options); 462 field = $($("input[name='" + fieldName + "']")); 463 break; 464 case "equals": 465 errorMsg = methods._equals(field, rules, i, options); 466 break; 467 case "funcCall": 468 errorMsg = methods._funcCall(field, rules, i, options); 469 break; 470 471 default: 472 //$.error("jQueryValidator rule not found"+rules[i]); 473 } 474 if (errorMsg !== undefined) { 475 promptText += errorMsg + "<br/>"; 476 options.isError = true; 477 478 } 479 480 } 481 // If the rules required is not added, an empty field is not validated 482 if(!required){ 483 if(field.val() == "") options.isError = false; 484 } 485 // Hack for radio/checkbox group button, the validation go into the 486 // first radio/checkbox of the group 487 var fieldType = field.attr("type"); 488 489 if ((fieldType == "radio" || fieldType == "checkbox") && $("input[name='" + fieldName + "']").size() > 1) { 490 field = $($("input[name='" + fieldName + "'][type!=hidden]:first")); 491 options.showArrow = false; 492 } 493 494 if (options.isError){ 495 496 methods._showPrompt(field, promptText, "", false, options); 497 }else{ 498 if (!isAjaxValidator) methods._closePrompt(field); 499 } 500 field.trigger("jqv.field.result", [field, options.isError, promptText]); 501 return options.isError; 502 }, 503 /** 504 * Required validation 505 * 506 * @param {jqObject} field 507 * @param {Array[String]} rules 508 * @param {int} i rules index 509 * @param {Map} 510 * user options 511 * @return an error string if validation failed 512 */ 513 _required: function(field, rules, i, options) { 514 switch (field.attr("type")) { 515 case "text": 516 case "password": 517 case "textarea": 518 case "file": 519 default: 520 if (!field.val()) 521 return options.allrules[rules[i]].alertText; 522 break; 523 case "radio": 524 case "checkbox": 525 var name = field.attr("name"); 526 if ($("input[name='" + name + "']:checked").size() == 0) { 527 528 if ($("input[name='" + name + "']").size() == 1) 529 return options.allrules[rules[i]].alertTextCheckboxe; 530 else 531 return options.allrules[rules[i]].alertTextCheckboxMultiple; 532 } 533 break; 534 // required for <select> 535 case "select-one": 536 // added by [email protected] for select boxes, Thank y
536ou 537 if (!field.val()) 538 return options.allrules[rules[i]].alertText; 539 break; 540 case "select-multiple": 541 // added by [email protected] for select boxes, Thank you 542 if (!field.find("option:selected").val()) 543 return options.allrules[rules[i]].alertText; 544 break; 545 } 546 }, 547 /** 548 * Validate Regex rules 549 * 550 * @param {jqObject} field 551 * @param {Array[String]} rules 552 * @param {int} i rules index 553 * @param {Map} 554 * user options 555 * @return an error string if validation failed 556 */ 557 _customRegex: function(field, rules, i, options) { 558 var customRule = rules[i + 1]; 559 var rule = options.allrules[customRule]; 560 if(!rule) { 561 alert("jqv:custom rule not found "+customRule); 562 return; 563 } 564 565 var ex=rule.regex; 566 if(!ex) { 567 alert("jqv:custom regex not found "+customRule); 568 return; 569 } 570 var pattern = new RegExp(ex); 571 572 if (!pattern.test(field.val())) 573 return options.allrules[customRule].alertText; 574 }, 575 /** 576 * Validate custom function outside of the engine scope 577 * 578 * @param {jqObject} field 579 * @param {Array[String]} rules 580 * @param {int} i rules index 581 * @param {Map} 582 * user options 583 * @return an error string if validation failed 584 */ 585 _funcCall: function(field, rules, i, options) { 586 var functionName = rules[i + 1]; 587 var fn = window[functionName]; 588 if (typeof(fn) == 'function') 589 return fn(field, rules, i, options); 590 591 }, 592 /** 593 * Field match 594 * 595 * @param {jqObject} field 596 * @param {Array[String]} rules 597 * @param {int} i rules index 598 * @param {Map} 599 * user options 600 * @return an error string if validation failed 601 */ 602 _equals: function(field, rules, i, options) { 603 var equalsField = rules[i + 1]; 604 605 if (field.val() != $("#" + equalsField).val()) 606 return options.allrules.equals.alertText; 607 }, 608 /** 609 * Check the maximum size (in characters) 610 * 611 * @param {jqObject} field 612 * @param {Array[String]} rules 613 * @param {int} i rules index 614 * @param {Map} 615 * user options 616 * @return an error string if validation failed 617 */ 618 _maxSize: function(field, rules, i, options) { 619 var max = rules[i + 1]; 620 var len = field.val().length; 621 622 if (len > max) { 623 var rule = options.allrules.maxSize; 624 return rule.alertText + max + rule.alertText2; 625 } 626 }, 627 /** 628 * Check the minimum size (in characters) 629 * 630 * @param {jqObject} field 631 * @param {Array[String]} rules 632 * @param {int} i rules index 633 * @param {Map} 634 * user options 635 * @return an error string if validation failed 636 */ 637 _minSize: function(field, rules, i, options) { 638 var min = rules[i + 1]; 639 var len = field.val().length; 640 641 if (len < min) { 642 var rule = options.allrules.minSize; 643 return rule.alertText + min + rule.alertText2; 644 } 645 }, 646 /** 647 * Check number minimum value 648 * 649 * @param {jqObject} field 650 * @param {Array[String]} rules 651 * @param {int} i rules index 652 * @param {Map} 653 * user options 654 * @return an error string if validation failed 655 */ 656 _min: function(field, rules, i, options) { 657 var min = parseFloat(rules[i + 1]); 658 var len = parseFloat(field.val()); 659 660 if (len < min) { 661 var rule = options.allrules.min; 662 if (rule.alertText2) return rule.alertText + min + rule.alertText2; 663 return rule.alertText + min; 664 } 665 }, 666 /** 667 * Check number maximum value 668 * 669 * @param {jqObject} field 670 * @param {Array[String]} rules 671 * @param {int} i rules index 672 * @param {Map} 673 * user options 674 * @return an error string if validation failed 675 */ 676 _max: function(field, rules, i, options) { 677 var max = parseFloat(rules[i + 1]); 678 var len = parseFloat(field.val()); 679 680 if (len >max ) { 681 var rule = options.allrules.max; 682 if (rule.alertText2) return rule.alertText + max + rule.alertText2; 683 //orefalo: to review, also do the translations 684 return rule.alertText + max; 685 } 686 }, 687 /** 688 * Checks date is in the past 689 * 690 * @param {jqObject} field 691 * @param {Array[String]} rules 692 * @param {int} i rules index 693 * @param {Map} 694 * user options 695 * @return an error string if validation failed 696 */ 697 _past: function(field, rules, i, options) { 698 699 var p=rules[i + 1]; 700 var pdate = (p.toLowerCase() == "now")? new Date():methods._parseDate(p); 701 var vdate = methods._parseDate(field.val()); 702 703 if (vdate < pdate ) { 704 var rule = options.allrules.past; 705 if (rule.alertText2) return rule.alertText + methods._dateToString(pdate) + rule.alertText2; 706 return rule.alertText + methods._dateToString(pdate); 707 } 708 }, 709 /** 710 * Checks date is in the future 711 * 712 * @param {jqObject} field 713 * @param {Array[String]} rules 714 * @param {int} i rules index 715 * @param {Map} 716 * user options 717 * @return an error string if validation failed 718 */ 719 _future: function(field, rules, i, options) { 720 721 var p=rules[i + 1]; 722 var pdate = (p.toLowerCase() == "now")? new Date():methods._parseDate(p); 723 var vdate = methods._parseDate(field.val()); 724 725 if (vdate > pdate ) { 726 var rule = options.allrules.future; 727 if (rule.alertText2) return rule.alertText + methods._dateToString(pdate) + rule.alertText2; 728 return rule.alertText + methods._dateToString(pdate); 729 } 730 }, 731 /** 732 * Max number of checkbox selected 733 * 734 * @param {jqObject} field 735 * @param {Array[String]} rules 736 * @param {int} i rules index 737 * @param {Map} 738 * user options 739 * @return an error string if validation failed 740 */ 741 _maxCheckbox: function(field, rules, i, options) { 742 743 var nbCheck = rules[i + 1]; 744 var groupname = field.attr("name"); 745 var groupSize = $("input[name='" + groupname + "']:checked").size(); 746 if (groupSize > nbCheck) { 747 options.showArrow = false;
748 if (options.allrules.maxCheckbox.alertText2) return options.allrules.maxCheckbox.alertText + " " + nbCheck + " " + options.allrules.maxCheckbox.alertText2; 749 return options.allrules.maxCheckbox.alertText; 750 } 751 }, 752 /** 753 * Min number of checkbox selected 754 * 755 * @param {jqObject} field 756 * @param {Array[String]} rules 757 * @param {int} i rules index 758 * @param {Map} 759 * user options 760 * @return an error string if validation failed 761 */ 762 _minCheckbox: function(field, rules, i, options) { 763 764 var nbCheck = rules[i + 1]; 765 var groupname = field.attr("name"); 766 var groupSize = $("input[name='" + groupname + "']:checked").size(); 767 if (groupSize < nbCheck) { 768 options.showArrow = false; 769 return options.allrules.minCheckbox.alertText + " " + nbCheck + " " + 770 options.allrules.minCheckbox.alertText2; 771 } 772 }, 773 /** 774 * Ajax field validation 775 * 776 * @param {jqObject} field 777 * @param {Array[String]} rules 778 * @param {int} i rules index 779 * @param {Map} 780 * user options 781 * @return nothing! the ajax validator handles the prompts itself 782 */ 783 _ajax: function(field, rules, i, options) { 784 785 786 var errorSelector = rules[i + 1]; 787 var rule = options.allrules[errorSelector]; 788 var extraData = rule.extraData; 789 var extraDataDynamic = rule.extraDataDynamic; 790 791 if (!extraData) 792 extraData = ""; 793 794 if (extraDataDynamic) { 795 var tmpData = []; 796 var domIds = String(extraDataDynamic).split(","); 797 for (var i = 0; i < domIds.length; i++) { 798 var id = domIds[i]; 799 if ($(id).length) { 800 var inputValue = field.closest("form").find(id).val(); 801 var keyValue = id.replace('#', '') + '=' + escape(inputValue); 802 tmpData.push(keyValue); 803 } 804 } 805 extraDataDynamic = tmpData.join("&"); 806 } else { 807 extraDataDynamic = ""; 808 } 809 810 if (!options.isError) { 811 $.ajax({ 812 type: "POST", 813 url: rule.url, 814 cache: false, 815 dataType: "json", 816 data: "fieldId=" + field.attr("id") + "&fieldValue=" + field.val() + "&extraData=" + extraData + "&" + extraDataDynamic, 817 field: field, 818 rule: rule, 819 methods: methods, 820 options: options, 821 beforeSend: function() { 822 // build the loading prompt 823 var loadingText = rule.alertTextLoad; 824 if (loadingText) 825 methods._showPrompt(field, loadingText, "load", true, options); 826 }, 827 error: function(data, transport) { 828 methods._ajaxError(data, transport); 829 }, 830 success: function(json) { 831 832 // asynchronously called on success, data is the json answer from the server 833 834 var errorFieldId = json[0]; 835 var errorField = $($("#" + errorFieldId)[0]); 836 // make sure we found the element 837 if (errorField.length == 1) { 838 var status = json[1]; 839 // read the optional msg from the server 840 var msg = json[2]; 841 if (!status) { 842 // Houston we got a problem - display an red prompt 843 options.ajaxValidCache[errorFieldId] = false; 844 options.isError = true; 845 846 // resolve the msg prompt 847 if(msg) { 848 if (options.allrules[msg]) { 849 var txt = options.allrules[msg].alertText; 850 if (txt) 851 msg = txt; 852 } 853 } 854 else 855 msg = rule.alertText; 856 857 methods._showPrompt(errorField, msg, "", false, options); 858 return msg; 859 } else { 860 if (options.ajaxValidCache[errorFieldId] !== undefined) 861 options.ajaxValidCache[errorFieldId] = true; 862 863 // resolves the msg prompt 864 if(msg) { 865 if (options.allrules[msg]) { 866 var txt = options.allrules[msg].alertTextOk; 867 if (txt) 868 msg = txt; 869 } 870 } 871 else 872 msg = rule.alertTextOk; 873 874 // see if we should display a green prompt 875 if (msg) 876 methods._showPrompt(errorField, msg, "pass", true, options); 877 else 878 methods._closePrompt(errorField); 879 880 return msg; 881 } 882 } 883 884 } 885 }); 886 } 887 }, 888 /** 889 * Common method to handle ajax errors 890 * 891 * @param {Object} data 892 * @param {Object} transport 893 */ 894 _ajaxError: function(data, transport) { 895 if(data.status == 0 && transport == null) 896 alert("The page is not served from a server! ajax call failed"); 897 else if(typeof console != "undefined") 898 console.log("Ajax error: " + data.status + " " + transport); 899 }, 900 /** 901 * date -> string 902 * 903 * @param {Object} date 904 */ 905 _dateToString: function(date) { 906 907 return date.getFullYear()+"-"+(date.getMonth()+1)+"-"+date.getDate(); 908 }, 909 /** 910 * Parses an ISO date 911 * @param {String} d 912 */ 913 _parseDate: function(d) { 914 915 var dateParts = d.split("-"); 916 if(dateParts==d) 917 dateParts = d.split("/"); 918 return new Date(dateParts[0], (dateParts[1] - 1) ,dateParts[2]); 919 }, 920 /** 921 * Builds or updates a prompt with the given information 922 * 923 * @param {jqObject} field 924 * @param {String} promptText html text to display type 925 * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red) 926 * @param {boolean} ajaxed - use to mark fields than being validated with ajax 927 * @param {Map} options user options 928 */ 929 _showPrompt: function(field, promptText, type, ajaxed, options, ajaxform) { 930 var prompt = methods._getPrompt(field); 931 // The ajax submit errors are not see has an error in the form, 932 // When the form errors are returned, the engine see 2 bubbles, but those are ebing closed by the engine at the same time 933 // Because no error was found befor submitting 934 if(ajaxform) prompt = false;
935 if (prompt) 936 methods._updatePrompt(field, prompt, promptText, type, ajaxed, options); 937 else 938 methods._buildPrompt(field, promptText, type, ajaxed, options); 939 }, 940 /** 941 * Builds and shades a prompt for the given field. 942 * 943 * @param {jqObject} field 944 * @param {String} promptText html text to display type 945 * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red) 946 * @param {boolean} ajaxed - use to mark fields than being validated with ajax 947 * @param {Map} options user options 948 */ 949 _buildPrompt: function(field, promptText, type, ajaxed, options) { 950 951 // create the prompt 952 var prompt = $('<div>'); 953 prompt.addClass(methods._getClassName(field.attr("id")) + "formError"); 954 // add a class name to identify the parent form of the prompt 955 if(field.is(":input")) prompt.addClass("parentForm"+methods._getClassName(field.parents('form').attr("id"))); 956 prompt.addClass("formError"); 957 958 switch (type) { 959 case "pass": 960 prompt.addClass("greenPopup"); 961 break; 962 case "load": 963 prompt.addClass("blackPopup"); 964 } 965 if (ajaxed) 966 prompt.addClass("ajaxed"); 967 968 // create the prompt content 969 var promptContent = $('<div>').addClass("formErrorContent").html(promptText).appendTo(prompt); 970 // create the css arrow pointing at the field 971 // note that there is no triangle on max-checkbox and radio 972 if (options.showArrow) { 973 var arrow = $('<div>').addClass("formErrorArrow"); 974 975 switch (options.promptPosition) { 976 case "bottomLeft": 977 case "bottomRight": 978 prompt.find(".formErrorContent").before(arrow); 979 arrow.addClass("formErrorArrowBottom").html('<div class="line1"><!-- --></div><div class="line2"><!-- --></div><div class="line3"><!-- --></div><div class="line4"><!-- --></div><div class="line5"><!-- --></div><div class="line6"><!-- --></div><div class="line7"><!-- --></div><div class="line8"><!-- --></div><div class="line9"><!-- --></div><div class="line10"><!-- --></div>'); 980 break; 981 case "topLeft": 982 case "topRight": 983 arrow.html('<div class="line10"><!-- --></div><div class="line9"><!-- --></div><div class="line8"><!-- --></div><div class="line7"><!-- --></div><div class="line6"><!-- --></div><div class="line5"><!-- --></div><div class="line4"><!-- --></div><div class="line3"><!-- --></div><div class="line2"><!-- --></div><div class="line1"><!-- --></div>'); 984 prompt.append(arrow); 985 break; 986 } 987 } 988 989 //Cedric: Needed if a container is in position:relative 990 // insert prompt in the form or in the overflown container? 991 if (options.isOverflown) 992 field.before(prompt); 993 else 994 $("body").append(prompt); 995 996 var pos = methods._calculatePosition(field, prompt, options); 997 prompt.css({ 998 "top": pos.callerTopPosition, 999 "left": pos.callerleftPosition, 1000 "marginTop": pos.marginTopSize, 1001 "opacity": 0 1002 }); 1003 1004 return prompt.animate({ 1005 "opacity": 0.87 1006 }); 1007 1008 }, 1009 /** 1010 * Updates the prompt text field - the field for which the prompt 1011 * @param {jqObject} field 1012 * @param {String} promptText html text to display type 1013 * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red) 1014 * @param {boolean} ajaxed - use to mark fields than being validated with ajax 1015 * @param {Map} options user options 1016 */ 1017 _updatePrompt: function(field, prompt, promptText, type, ajaxed, options) { 1018 1019 if (prompt) { 1020 if (type == "pass") 1021 prompt.addClass("greenPopup"); 1022 else 1023 prompt.removeClass("greenPopup"); 1024 1025 if (type == "load") 1026 prompt.addClass("blackPopup"); 1027 else 1028 prompt.removeClass("blackPopup"); 1029 1030 if (ajaxed) 1031 prompt.addClass("ajaxed"); 1032 else 1033 prompt.removeClass("ajaxed"); 1034 1035 prompt.find(".formErrorContent").html(promptText); 1036 1037 var pos = methods._calculatePosition(field, prompt, options); 1038 prompt.animate({ 1039 "top": pos.callerTopPosition, 1040 "marginTop": pos.marginTopSize 1041 }); 1042 } 1043 }, 1044 /** 1045 * Closes the prompt associated with the given field 1046 * 1047 * @param {jqObject} 1048 * field 1049 */ 1050 _closePrompt: function(field) { 1051 1052 var prompt = methods._getPrompt(field); 1053 if (prompt) 1054 prompt.fadeTo("fast", 0, function() { 1055 prompt.remove(); 1056 }); 1057 }, 1058 closePrompt: function(field) { 1059 return methods._closePrompt(field); 1060 }, 1061 /** 1062 * Returns the error prompt matching the field if any 1063 * 1064 * @param {jqObject} 1065 * field 1066 * @return undefined or the error prompt (jqObject) 1067 */ 1068 _getPrompt: function(field) { 1069 1070 var className = "." + methods._getClassName(field.attr("id")) + "formError"; 1071 var match = $(className)[0]; 1072 if (match) 1073 return $(match); 1074 }, 1075 /** 1076 * Calculates prompt position 1077 * 1078 * @param {jqObject} 1079 * field 1080 * @param {jqObject} 1081 * the prompt 1082 * @param {Map} 1083 * options 1084 * @return positions 1085 */ 1086 _calculatePosition: function(field, promptElmt, options) { 1087 1088 var promptTopPosition, promptleftPosition, marginTopSize; 1089 var fieldWidth = field.width(); 1090 var promptHeight = promptElmt.height(); 1091 1092 var overflow = options.isOverflown; 1093 if (overflow) { 1094 // is the form contained in an overflown container? 1095 promptTopPosition = promptleftPosition = 0; 1096 // compensation for the arrow 1097 marginTopSize = -promptHeight; 1098 } else { 1099 var offset = field.offset(); 1100 promptTopPosition = offset.top; 1101 promptleftPosition = offset.left; 1102 marginTopSize = 0; 1103 } 1104 1105 switch (options.promptPosition) { 1106 1107 default: 1108 case "topRight": 1109 if (overflow) 1110 // Is the form contained in an overflown container? 1111 promptleftPosition += fieldWidth - 30; 1112 else { 1113 promptleftPosition += fieldWidth - 30; 1114 promptTopPosition += -promptHeight; 1115 } 1116 break; 1117 case "topLeft": 1118 promptTopPosition += -promptHeight - 10; 1119 break; 1120 case "centerRight": 1121 promptleftPosition += fieldWidth + 13; 1122 break; 1123 case "bottomLeft": 1124 promptTopPosition = promptTopPosition + field.height() + 15; 1125 break; 1126 case "bottomRight": 1127 promptleftPosition += fieldWidth - 30; 1128 promptTopPosition += field.height() + 5; 1129 } 1130 1131 return { 1132 "callerTopPosition": promptTopPosition + "px", 1133 "callerleftPosition": promptleftPosition + "px", 1134 "marginTopSize": marginTopSize + "px" 1135 }; 1136 }, 1137 /** 1138 * Saves the user options and variables in the form.data 1139 * 1140 * @param {jqObject} 1141 * form - the form where the user option should be saved 1142 * @param {Map} 1143 * options - the user options 1144 * @return the user options (extended from the defaults) 1145 */ 1146 _saveOptions: function(form, options) { 1147 1148 // is there a language localisation ? 1149 if ($.validationEngineLanguage) 1150 var allRules = $.validationEngineLanguage.allRules; 1151 else 1152 $.error("jQuery.validationEngine rules are not loaded, plz add localization files to the page"); 1153 1154 var userOptions = $.extend({ 1155 1156 // Name of the event triggering field validation 1157 validationEventTrigger: "blur", 1158 // Automatically scroll viewport to the first error 1159 scroll: true, 1160 // Opening box position, possible locations are: topLeft, 1161 // topRight, bottomLeft, centerRight, bottomRight 1162 promptPosition: "topRight", 1163 bindMethod:"bind", 1164 // internal, automatically set to true when it parse a _ajax rule 1165 inlineAjax: false, 1166 // if set to true, the form data is sent asynchronously via ajax to the form.action url (get) 1167 ajaxFormValidation: false, 1168 // Ajax form validation callback method: boolean onComplete(form, status, errors, options) 1169 // retuns false if the form.submit event needs to be canceled. 1170 ajaxFormValidationURL: false, 1171 // The url to send the submit ajax validation (default to action) 1172 onAjaxFormComplete: $.noop, 1173 // called right before the ajax call, may return false to cancel 1174 onBeforeAjaxFormValidation: $.noop, 1175 // Stops form from submitting and execute function assiciated with it 1176 onValidationComplete: false, 1177 1178 // Used when the form is displayed within a scrolling DIV
1179 isOverflown: false, 1180 overflownDIV: "", 1181 1182 // --- Internals DO NOT TOUCH or OVERLOAD --- 1183 // validation rules and i18 1184 allrules: allRules, 1185 // true when form and fields are binded 1186 binded: false, 1187 // set to true, when the prompt arrow needs to be displayed 1188 showArrow: true, 1189 // did one of the validation fail ? kept global to stop further ajax validations 1190 isError: false, 1191 // Caches field validation status, typically only bad status are created. 1192 // the array is used during ajax form validation to detect issues early and prevent an expensive submit 1193 ajaxValidCache: {} 1194 1195 }, options); 1196 1197 form.data('jqv', userOptions); 1198 return userOptions; 1199 }, 1200 1201 /** 1202 * Removes forbidden characters from class name 1203 * @param {String} className 1204 */ 1205 _getClassName: function(className) { 1206 return className.replace(":","_").replace(".","_"); 1207 } 1208 }; 1209 1210 /** 1211 * Plugin entry point. 1212 * You may pass an action as a parameter or a list of options. 1213 * if none, the init and attach methods are being called. 1214 * Remember: if you pass options, the attached method is NOT called automatically 1215 * 1216 * @param {String} 1217 * method (optional) action 1218 */ 1219 $.fn.validationEngine = function(method) { 1220 1221 var form = $(this); 1222 if(!form[0]) return false; // stop here if the form does not exist 1223 1224 if (typeof(method) == 'string' && method.charAt(0) != '_' && methods[method]) { 1225 1226 // make sure init is called once 1227 if(method != "showPrompt" && method != "hidePrompt" && method != "hide" && method != "hideAll") 1228 methods.init.apply(form); 1229 1230 return methods[method].apply(form, Array.prototype.slice.call(arguments, 1)); 1231 } else if (typeof method == 'object' || !method) { 1232 // default constructor with or without arguments 1233 methods.init.apply(form, arguments); 1234 return methods.attach.apply(form); 1235 } else { 1236 $.error('Method ' + method + ' does not exist in jQuery.validationEngine'); 1237 } 1238 }; 1239})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.