1/* 2 * Inline Form Validation Engine 2.6.1, 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 "use strict"; 16 17 var methods = { 18 19 /** 20 * Kind of the constructor, called before any action 21 * @param {Map} user options 22 */ 23 init: function(options) { 24 var form = this; 25 if (!form.data('jqv') || form.data('jqv') == null ) { 26 options = methods._saveOptions(form, options); 27 // bind all formError elements to close on click 28 $(".formError").on("click", function() { 29 $(this).fadeOut(150, function() { 30 // remove prompt once invisible 31 $(this).parent('.formErrorOuter').remove(); 32 $(this).remove(); 33 }); 34 }); 35 } 36 return this; 37 }, 38 /** 39 * Attachs jQuery.validationEngine to form.submit and field.blur events 40 * Takes an optional params: a list of options 41 * ie. jQuery("#formID1").validationEngine('attach', {promptPosition : "centerRight"}); 42 */ 43 attach: function(userOptions) { 44 45 if(!$(this).is("form")) { 46 alert("Sorry, jqv.attach() only applies to a form"); 47 return this; 48 } 49 50 var form = this; 51 var options; 52 53 if(userOptions) 54 options = methods._saveOptions(form, userOptions); 55 else 56 options = form.data('jqv'); 57 58 options.validateAttribute = (form.find("[data-validation-engine*=validate]").length) ? "data-validation-engine" : "class"; 59 if (options.binded) { 60 61 // bind fields 62 form.find("["+options.validateAttribute+"*=validate]").not("[type=checkbox]").not("[type=radio]").not(".datepicker").bind(options.validationEventTrigger, methods._onFieldEvent); 63 form.find("["+options.validateAttribute+"*=validate][type=checkbox],["+options.validateAttribute+"*=validate][type=radio]").bind("click", methods._onFieldEvent); 64 form.find("["+options.validateAttribute+"*=validate][class*=datepicker]").bind(options.validationEventTrigger,{"delay": 300}, methods._onFieldEvent); 65 } 66 if (options.autoPositionUpdate) { 67 $(window).bind("resize", { 68 "noAnimation": true, 69 "formElem": form 70 }, methods.updatePromptsPosition); 71 } 72 // bind form.submit 73 form.bind("submit", methods._onSubmitEvent); 74 return this; 75 }, 76 /** 77 * Unregisters any bindings that may point to jQuery.validaitonEngine 78 */ 79 detach: function() { 80 81 if(!$(this).is("form")) { 82 alert("Sorry, jqv.detach() only applies to a form"); 83 return this; 84 } 85 86 var form = this; 87 var options = form.data('jqv'); 88 89 // unbind fields 90 form.find("["+options.validateAttribute+"*=validate]").not("[type=checkbox]").unbind(options.validationEventTrigger, methods._onFieldEvent); 91 form.find("["+options.validateAttribute+"*=validate][type=checkbox],[class*=validate][type=radio]").unbind("click", methods._onFieldEvent); 92 93 // unbind form.submit 94 form.unbind("submit", methods.onAjaxFormComplete); 95 96 // unbind live fields (kill) 97 form.find("["+options.validateAttribute+"*=validate]").not("[type=checkbox]").die(options.validationEventTrigger, methods._onFieldEvent); 98 form.find("["+options.validateAttribute+"*=validate][type=checkbox]").die("click", methods._onFieldEvent); 99 100 // unbind form.submit 101 form.die("submit", methods.onAjaxFormComplete); 102 form.removeData('jqv'); 103 104 if (options.autoPositionUpdate) 105 $(window).unbind("resize", methods.updatePromptsPosition); 106 107 return this; 108 }, 109 /** 110 * Validates either a form or a list of fields, shows prompts accordingly. 111 * Note: There is no ajax form validation with this method, only field ajax validation are evaluated 112 * 113 * @return true if the form validates, false if it fails 114 */ 115 validate: function() { 116 var element = $(this); 117 var valid = null; 118 if(element.is("form") && !element.hasClass('validating')) { 119 element.addClass('validating'); 120 var options = element.data('jqv'); 121 valid = methods._validateFields(this); 122
123 // If the form doesn't validate, clear the 'validating' class before the user has a chance to submit again 124 setTimeout(function(){ 125 element.removeClass('validating'); 126 }, 100); 127 if (valid && options.onFormSuccess) { 128 options.onFormSuccess(); 129 } else if (!valid && options.onFormFailure) { 130 options.onFormFailure(); 131 } 132 } else if (element.is('form')) { 133 element.removeClass('validating'); 134 } else { 135 // field validation 136 var form = element.closest('form'); 137 var options = form.data('jqv'); 138 valid = methods._validateField(element, options); 139 140 if (valid && options.onFieldSuccess) 141 options.onFieldSuccess(); 142 else if (options.onFieldFailure && options.InvalidFields.length > 0) { 143 options.onFieldFailure(); 144 } 145 } 146 return valid; 147 }, 148 /** 149 * Redraw prompts position, useful when you change the DOM state when validating 150 */ 151 updatePromptsPosition: function(event) { 152 153 if (event && this == window) { 154 var form = event.data.formElem; 155 var noAnimation = event.data.noAnimation; 156 } 157 else 158 var form = $(this.closest('form')); 159 160 var options = form.data('jqv'); 161 // No option, take default one 162 form.find('['+options.validateAttribute+'*=validate]').not(":disabled").each(function(){ 163 var field = $(this); 164 if (options.prettySelect && field.is(":hidden")) 165 field = form.find("#" + options.usePrefix + field.attr('id') + options.useSuffix); 166 var prompt = methods._getPrompt(field); 167 var promptText = $(prompt).find(".formErrorContent").html(); 168 169 if(prompt) 170 methods._updatePrompt(field, $(prompt), promptText, undefined, false, options, noAnimation); 171 }); 172 return this; 173 }, 174 /** 175 * Displays a prompt on a element. 176 * Note that the element needs an id! 177 * 178 * @param {String} promptText html text to display type 179 * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red) 180 * @param {String} possible values topLeft, topRight, bottomLeft, centerRight, bottomRight 181 */ 182 showPrompt: function(promptText, type, promptPosition, showArrow) { 183 184 var form = this.closest('form'); 185 var options = form.data('jqv'); 186 // No option, take default one 187 if(!options) 188 options = methods._saveOptions(this, options); 189 if(promptPosition) 190 options.promptPosition=promptPosition; 191 options.showArrow = showArrow==true; 192 193 methods._showPrompt(this, promptText, type, false, options); 194 return this; 195 }, 196 /** 197 * Closes form error prompts, CAN be invidual 198 */ 199 hide: function() { 200 var form = $(this).closest('form'); 201 var options = form.data('jqv'); 202 var fadeDuration = (options && options.fadeDuration) ? options.fadeDuration : 0.3; 203 var closingtag; 204 205 if($(this).is("form")) { 206 closingtag = "parentForm"+methods._getClassName($(this).attr("id")); 207 } else { 208 closingtag = methods._getClassName($(this).attr("id")) +"formError"; 209 } 210 $('.'+closingtag).fadeTo(fadeDuration, 0.3, function() { 211 $(this).parent('.formErrorOuter').remove(); 212 $(this).remove(); 213 });
214 return this; 215 }, 216 /** 217 * Closes all error prompts on the page 218 */ 219 hideAll: function() { 220 221 var form = this; 222 var options = form.data('jqv'); 223 var duration = options ? options.fadeDuration:0.3; 224 $('.formError').fadeTo(duration, 0.3, function() { 225 $(this).parent('.formErrorOuter').remove(); 226 $(this).remove(); 227 }); 228 return this; 229 }, 230 /** 231 * Typically called when user exists a field using tab or a mouse click, triggers a field 232 * validation 233 */ 234 _onFieldEvent: function(event) { 235 var field = $(this); 236 var form = field.closest('form'); 237 var options = form.data('jqv'); 238 options.eventTrigger = "field"; 239 // validate the current field 240 window.setTimeout(function() { 241 methods._validateField(field, options); 242 if (options.InvalidFields.length == 0 && options.onFieldSuccess) { 243 options.onFieldSuccess(); 244 } else if (options.InvalidFields.length > 0 && options.onFieldFailure) { 245 options.onFieldFailure(); 246 } 247 }, (event.data) ? event.data.delay : 0); 248 249 }, 250 /** 251 * Called when the form is submited, shows prompts accordingly 252 * 253 * @param {jqObject} 254 * form 255 * @return false if form submission needs to be cancelled 256 */ 257 _onSubmitEvent: function() { 258 var form = $(this); 259 var options = form.data('jqv'); 260 options.eventTrigger = "submit"; 261 262 // validate each field 263 // (- skip field ajax validation, not necessary IF we will perform an ajax form validation) 264 var r=methods._validateFields(form); 265 266 if (r && options.ajaxFormValidation) { 267 methods._validateFormWithAjax(form, options); 268 // cancel form auto-submission - process with async call onAjaxFormComplete 269 return false; 270 } 271 272 if(options.onValidationComplete) { 273 // !! ensures that an undefined return is interpreted as return false but allows a onValidationComplete() to possibly return true and have form continue processing 274 return !!options.onValidationComplete(form, r); 275 } 276 return r; 277 }, 278 /** 279 * Return true if the ajax field validations passed so far 280 * @param {Object} options 281 * @return true, is all ajax validation passed so far (remember aja
281x is async) 282 */ 283 _checkAjaxStatus: function(options) { 284 var status = true; 285 $.each(options.ajaxValidCache, function(key, value) { 286 if (!value) { 287 status = false; 288 // break the each 289 return false; 290 } 291 }); 292 return status; 293 }, 294 295 /** 296 * Return true if the ajax field is validated 297 * @param {String} fieldid 298 * @param {Object} options 299 * @return true, if validation passed, false if false or doesn't exist 300 */ 301 _checkAjaxFieldStatus: function(fieldid, options) { 302 return options.ajaxValidCache[fieldid] == true; 303 }, 304 /** 305 * Validates form fields, shows prompts accordingly 306 * 307 * @param {jqObject} 308 * form 309 * @param {skipAjaxFieldValidation} 310 * boolean - when set to true, ajax field validation is skipped, typically used when the submit button is clicked 311 * 312 * @return true if form is valid, false if not, undefined if ajax form validation is done 313 */ 314 _validateFields: function(form) { 315 var options = form.data('jqv'); 316 317 // this variable is set to true if an error is found 318 var errorFound = false; 319 320 // Trigger hook, start validation 321 form.trigger("jqv.form.validating"); 322 // first, evaluate status of non ajax fields 323 var first_err=null; 324 form.find('['+options.validateAttribute+'*=validate]').not(":disabled").each( function() { 325 var field = $(this); 326 var names = []; 327 if ($.inArray(field.attr('name'), names) < 0) { 328 errorFound |= methods._validateField(field, options); 329 if (errorFound && first_err==null) 330 if (field.is(":hidden") && options.prettySelect) 331 first_err = field = form.find("#" + options.usePrefix + methods._jqSelector(field.attr('id')) + options.useSuffix); 332 else 333 first_err=field; 334 if (options.doNotShowAllErrosOnSubmit) 335 return false; 336 names.push(field.attr('name')); 337 338 //if option set, stop checking validation rules after one error is found 339 if(options.showOneMessage == true && errorFound){ 340 return false; 341 } 342 } 343 }); 344 345 // second, check to see if all ajax calls completed ok 346 // errorFound |= !methods._checkAjaxStatus(options); 347 348 // third, check status and scroll the container accordingly 349 form.trigger("jqv.form.result", [errorFound]); 350 351 if (errorFound) { 352 if (options.scroll) { 353 var destination=first_err.offset().top; 354 var fixleft = first_err.offset().left; 355 356 //prompt positioning adjustment support. Usage: positionType:Xshift,Yshift (for ex.: bottomLeft:+20 or bottomLeft:-20,+10) 357 var positionType=options.promptPosition; 358 if (typeof(positionType)=='string' && positionType.indexOf(":")!=-1) 359 positionType=positionType.substring(0,positionType.indexOf(":")); 360 361 if (positionType!="bottomRight" && positionType!="bottomLeft") { 362 var prompt_err= methods._getPrompt(first_err); 363 if (prompt_err) { 364 destination=prompt_err.offset().top; 365 } 366 } 367 368 // get the position of the first error, there should be at least one, no need to check this 369 //var destination = form.find(".formError:not('.greenPopup'):first").offset().top; 370 if (options.isOverflown) { 371 var overflowDIV = $(options.overflownDIV); 372 if(!overflowDIV.length) return false; 373 var scrollContainerScroll = overflowDIV.scrollTop(); 374 var scrollContainerPos = -parseInt(overflowDIV.offset().top); 375 376 destination += scrollContainerScroll + scrollContainerPos - 5; 377 var scrollContainer = $(options.overflownDIV + ":not(:animated)"); 378 379 scrollContainer.animate({ scrollTop: destination }, 1100, function(){ 380 if(options.focusFirstField) first_err.focus(); 381 }); 382 383 } else { 384 $("html, body").animate({ 385 scrollTop: destination 386 }, 1100, function(){ 387 if(options.focusFirstField) first_err.focus(); 388 }); 389 $("html, body").animate({scrollLeft: fixleft},1100) 390 } 391 392 } else if(options.focusFirstField) 393 first_err.focus(); 394 return false; 395 } 396 return true; 397 }, 398 /** 399 * This method is called to perform an ajax form validation. 400 * During this process all the (field, value) pairs are sent to the server which returns a list of invalid fields or true 401 * 402 * @param {jqObject} form 403 * @param {Map} options 404 */ 405 _validateFormWithAjax: function(form, options) { 406 407 var data = form.serialize(); 408 var type = (options.ajaxFormMethod) ? options.ajaxFormMethod : "GET"; 409 var url = (options.ajaxFormValidationURL) ? options.ajaxFormValidationURL : form.attr("action"); 410 var dataType = (options.dataType) ? options.dataType : "json"; 411 $.ajax({
412 type: type, 413 url: url, 414 cache: false, 415 dataType: dataType, 416 data: data, 417 form: form, 418 methods: methods, 419 options: options, 420 beforeSend: function() { 421 return options.onBeforeAjaxFormValidation(form, options); 422 }, 423 error: function(data, transport) { 424 methods._ajaxError(data, transport); 425 }, 426 success: function(json) { 427 if ((dataType == "json") && (json !== true)) { 428 // getting to this case doesn't necessary means that the form is invalid 429 // the server may return green or closing prompt actions 430 // this flag helps figuring it out 431 var errorInForm=false; 432 for (var i = 0; i < json.length; i++) { 433 var value = json[i]; 434 435 var errorFieldId = value[0]; 436 var errorField = $($("#" + errorFieldId)[0]); 437 438 // make sure we found the element 439 if (errorField.length == 1) { 440 441 // promptText or selector 442 var msg = value[2]; 443 // if the field is valid 444 if (value[1] == true) { 445 446 if (msg == "" || !msg){ 447 // if for some reason, status==true and error="", just close the prompt 448 methods._closePrompt(errorField); 449 } else { 450 // the field is valid, but we are displaying a green prompt 451 if (options.allrules[msg]) { 452 var txt = options.allrules[msg].alertTextOk; 453 if (txt) 454 msg = txt; 455 } 456 methods._showPrompt(errorField, msg, "pass", false, options, true); 457 } 458 } else { 459 // the field is invalid, show the red error prompt 460 errorInForm|=true; 461 if (options.allrules[msg]) { 462 var txt = options.allrules[msg].alertText; 463 if (txt) 464 msg = txt; 465 } 466 methods._showPrompt(errorField, msg, "", false, options, true); 467 } 468 } 469 } 470 options.onAjaxFormComplete(!errorInForm, form, json, options); 471 } else 472 options.onAjaxFormComplete(true, form, json, options); 473 474 } 475 }); 476 477 }, 478 /** 479 * Validates field, shows prompts accordingly 480 * 481 * @param {jqObject} 482 * field 483 * @param {Array[String]} 484 * field's validation rules 485 * @param {Map} 486 * user options 487 * @return false if field is valid (It is inversed for *fields*, it return false on validate and true on errors.) 488 */ 489 _validateField: function(field, options, skipAjaxValidation) { 490 if (!field.attr("id")) { 491 field.attr("id", "form-validation-field-" + $.validationEngine.fieldIdCounter); 492 ++$.validationEngine.fieldIdCounter; 493 } 494 495 if (field.is(":hidden") && !options.prettySelect || field.parent().is(":hidden")) 496 return false; 497 498 var rulesParsing = field.attr(options.validateAttribute); 499 var getRules = /validate\[(.*)\]/.exec(rulesParsing); 500 501 if (!getRules) 502 return false; 503 var str = getRules[1]; 504 var rules = str.split(/\[|,|\]/); 505 506 // true if we ran the ajax validation, tells the logic to stop messing with prompts 507 var isAjaxValidator = false; 508 var fieldName = field.attr("name"); 509 var promptText = ""; 510 var promptType = ""; 511 var required = false; 512 var limitErrors = false; 513 options.isError = false; 514 options.showArrow = true; 515 516 // If the programmer wants to limit the amount of error messages per field, 517 if (options.maxErrorsPerField > 0) { 518 limitErrors = true; 519 } 520 521 var form = $(field.closest("form")); 522 // Fix for adding spaces in the rules 523 for (var i = 0; i < rules.length; i++) { 524 rules[i] = rules[i].replace(" ", ""); 525 // Remove any parsing errors 526 if (rules[i] === '') { 527 delete rules[i]; 528 } 529 } 530 531 for (var i = 0, field_errors = 0; i < rules.length; i++) { 532 533 // If we are limiting errors, and have hit the max, break 534 if (limitErrors && field_errors >= options.maxErrorsPerField) { 535 // If we haven't hit a required yet, check to see if there is one in the validation rules for this 536 // field and that it's index is greater or equal to our current index 537 if (!required) { 538 var have_required = $.inArray('required', rules); 539 required = (have_required != -1 && have_required >= i); 540 } 541 break; 542 } 543 544 545 var errorMsg = undefined; 546 switch (rules[i]) { 547 548 case "required": 549 required = true; 550 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._required); 551 break;
552 case "custom": 553 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._custom); 554 break; 555 case "groupRequired": 556 // Check is its the first of group, if not, reload validation with first field 557 // AND continue normal validation on present field 558 var classGroup = "["+options.validateAttribute+"*=" +rules[i + 1] +"]"; 559 var firstOfGroup = form.find(classGroup).eq(0); 560 if(firstOfGroup[0] != field[0]){ 561 methods._validateField(firstOfGroup, options, skipAjaxValidation); 562 options.showArrow = true; 563 continue; 564 } 565 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._groupRequired); 566 if(errorMsg) required = true; 567 options.showArrow = false; 568 break; 569 case "ajax": 570 // AJAX defaults to returning it's loading message 571 errorMsg = methods._ajax(field, rules, i, options); 572 if (errorMsg) { 573 promptType = "load"; 574 } 575 break; 576 case "minSize": 577 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._minSize); 578 break; 579 case "maxSize": 580 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._maxSize); 581 break; 582 case "min": 583 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._min); 584 break; 585 case "max": 586 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._max); 587 break; 588 case "past": 589 errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._past); 590 break; 591 case "future": 592 errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._future); 593 break; 594 case "dateRange": 595 var classGroup = "["+options.validateAttribute+"*=" + rules[i + 1] + "]"; 596 options.firstOfGroup = form.find(classGroup).eq(0); 597 options.secondOfGroup = form.find(classGroup).eq(1); 598 599 //if one entry out of the pair has value then proceed to run through validation 600 if (options.firstOfGroup[0].value || options.secondOfGroup[0].value) { 601 errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._dateRange); 602 } 603 if (errorMsg) required = true; 604 options.showArrow = false; 605 break; 606 607 case "dateTimeRange": 608 var classGroup = "["+options.validateAttribute+"*=" + rules[i + 1] + "]"; 609 options.firstOfGroup = form.find(classGroup).eq(0); 610 options.secondOfGroup = form.find(classGroup).eq(1); 611 612 //if one entry out of the pair has value then proceed to run through validation 613 if (options.firstOfGroup[0].value || options.secondOfGroup[0].value) { 614 errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._dateTimeRange); 615 } 616 if (errorMsg) required = true; 617 options.showArrow = false; 618 break; 619 case "maxCheckbox": 620 field = $(form.find("input[name='" + fieldName + "']")); 621 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._maxCheckbox); 622 break; 623 case "minCheckbox": 624 field = $(form.find("input[name='" + fieldName + "']")); 625 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._minCheckbox); 626 break; 627 case "equals": 628 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._equals); 629 break; 630 case "funcCall": 631 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._funcCall); 632 break; 633 case "creditCard": 634 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._creditCard); 635 break; 636 case "condRequired": 637 errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._condRequired); 638 if (errorMsg !== undefined) { 639 required = true; 640 } 641 break; 642 643 default: 644 } 645 646 var end_validation = false; 647 648 // If we were passed back an message object, check what the status was to determine what to do 649 if (typeof errorMsg == "object") { 650 switch (errorMsg.status) { 651 case "_break": 652 end_validation = true; 653 break; 654 // If we have an error message, set errorMsg to the error message 655 case "_error": 656 errorMsg = errorMsg.message; 657 break; 658 // If we want to throw an error, but not show a prompt, return early with true 659 case "_error_no_prompt": 660 return true; 661 break; 662 // Anything else we continue on 663 default: 664 break; 665 } 666 } 667 668 // If it has been specified that validation should end now, break 669 if (end_validation) { 670 break; 671 } 672 673 // If we have a string, that means that we have an error, so add it to the error message. 674 if (typeof errorMsg == 'string') { 675 promptText += errorMsg + "<br/>"; 676 options.isError = true; 677 field_errors++; 678 } 679 } 680 // If the rules required is not added, an empty field is not val
680idated 681 if(!required && field.val().length < 1) options.isError = false; 682 683 // Hack for radio/checkbox group button, the validation go into the 684 // first radio/checkbox of the group 685 var fieldType = field.prop("type"); 686 687 if ((fieldType == "radio" || fieldType == "checkbox") && form.find("input[name='" + fieldName + "']").size() > 1) { 688 field = $(form.find("input[name='" + fieldName + "'][type!=hidden]:first")); 689 options.showArrow = false; 690 } 691 692 if(field.is(":hidden") && options.prettySelect) { 693 field = form.find("#" + options.usePrefix + methods._jqSelector(field.attr('id')) + options.useSuffix); 694 } 695 696 if (options.isError){ 697 methods._showPrompt(field, promptText, promptType, false, options); 698 }else{ 699 if (!isAjaxValidator) methods._closePrompt(field); 700 } 701 702 if (!isAjaxValidator) { 703 field.trigger("jqv.field.result", [field, options.isError, promptText]); 704 } 705 706 /* Record error */ 707 var errindex = $.inArray(field[0], options.InvalidFields); 708 if (errindex == -1) { 709 if (options.isError) 710 options.InvalidFields.push(field[0]); 711 } else if (!options.isError) { 712 options.InvalidFields.splice(errindex, 1); 713 } 714 715 methods._handleStatusCssClasses(field, options); 716 717 return options.isError; 718 }, 719 /** 720 * Handling css classes of fields indicating result of validation 721 * 722 * @param {jqObject} 723 * field 724 * @param {Array[String]} 725 * field's validation rules 726 * @private 727 */ 728 _handleStatusCssClasses: function(field, options) { 729 /* remove all classes */ 730 if(options.addSuccessCssClassToField) 731 field.removeClass(options.addSuccessCssClassToField); 732 733 if(options.addFailureCssClassToField) 734 field.removeClass(options.addFailureCssClassToField); 735 736 /* Add classes */ 737 if (options.addSuccessCssClassToField && !options.isError) 738 field.addClass(options.addSuccessCssClassToField); 739 740 if (options.addFailureCssClassToField && options.isError) 741 field.addClass(options.addFailureCssClassToField); 742 }, 743 744 /******************** 745 * _getErrorMessage 746 * 747 * @param form 748 * @param field 749 * @param rule 750 * @param rules 751 * @param i 752 * @param options 753 * @param originalValidationMethod 754 * @return {*} 755 * @private 756 */ 757 _getErrorMessage:function (form, field, rule, rules, i, options, originalValidationMethod) { 758 // If we are using the custon validation type, build the index for the rule. 759 // Otherwise if we are doing a function call, make the call and return the object 760 // that is passed back. 761 var beforeChangeRule = rule; 762 if (rule == "custom") { 763 var custom_validation_type_index = jQuery.inArray(rule, rules)+ 1; 764 var custom_validation_type = rules[custom_validation_type_index]; 765 rule = "custom[" + custom_validation_type + "]"; 766 } 767 var element_classes = (field.attr("data-validation-engine")) ? field.attr("data-validation-engine") : field.attr("class"); 768 var element_classes_array = element_classes.split(" "); 769 770 // Call the original validation method. If we are dealing with dates or checkboxes, also pass the form 771 var errorMsg; 772 if (rule == "future" || rule == "past" || rule == "maxCheckbox" || rule == "minCheckbox") { 773 errorMsg = originalValidationMethod(form, field, rules, i, options); 774 } else { 775 errorMsg = originalValidationMethod(field, rules, i, options); 776 } 777 778 // If the original validation method returned an error and we have a custom error message, 779 // return the custom message instead. Otherwise return the original error message. 780 if (errorMsg != undefined) { 781 var custom_message = methods._getCustomErrorMessage($(field), element_classes_array, beforeChangeRule, options); 782 if (custom_message) errorMsg = custom_message; 783 } 784 return errorMsg; 785 786 }, 787 _getCustomErrorMessage:function (field, classes, rule, options) { 788 var custom_message = false; 789 var validityProp = methods._validityProp[rule]; 790 // If there is a validityProp for this rule, check to see if the field has an attribute for it 791 if (validityProp != undefined) { 792 custom_message = field.attr("data-errormessage-"+validityProp); 793 // If there was an error message for it, return the message 794 if (custom_message != undefined) 795 return custom_message; 796 } 797 custom_message = field.attr("data-errormessage"); 798 // If there is an inline custom error message, return it 799 if (custom_message != undefined) 800 return custom_message; 801 var id = '#' + field.attr("id"); 802 // If we have custom messages for the element's id, get the message for the rule from the id. 803 // Otherwise, if we have custom messages for the element's classes, use the first class message we find instead. 804 if (typeof options.custom_error_messages[id] != "undefined" && 805 typeof options.custom_error_messages[id][rule] != "undefined" ) { 806 custom_message = options.custom_error_messages[id][rule]['message']; 807 } else if (classes.length > 0) { 808 for (var i = 0; i < classes.length && classes.length > 0; i++) { 809 var element_class = "." + classes[i]; 810 if (typeof options.custom_error_messages[element_class] != "undefined" && 811 typeof options.custom_error_messages[element_class][rule] != "undefined") { 812 custom_message = options.custom_error_messages[element_class][rule]['message']; 813 break; 814 } 815 } 816 } 817 if (!custom_message && 818 typeof options.custom_error_messages[rule] != "undefined" && 819 typeof options.custom_error_messages[rule]['message'] != "undefined"){ 820 custom_message = options.custom_error_messages[rule]['message']; 821 } 822 return custom_message; 823 }, 824 _validityProp: { 825 "required": "value-missing",
826 "custom": "custom-error", 827 "groupRequired": "value-missing", 828 "ajax": "custom-error", 829 "minSize": "range-underflow", 830 "maxSize": "range-overflow", 831 "min": "range-underflow", 832 "max": "range-overflow", 833 "past": "type-mismatch", 834 "future": "type-mismatch", 835 "dateRange": "type-mismatch", 836 "dateTimeRange": "type-mismatch", 837 "maxCheckbox": "range-overflow", 838 "minCheckbox": "range-underflow", 839 "equals": "pattern-mismatch", 840 "funcCall": "custom-error", 841 "creditCard": "pattern-mismatch", 842 "condRequired": "value-missing" 843 }, 844 /** 845 * Required validation 846 * 847 * @param {jqObject} field 848 * @param {Array[String]} rules 849 * @param {int} i rules index 850 * @param {Map} 851 * user options 852 * @param {bool} condRequired flag when method is used for internal purpose in condRequired check 853 * @return an error string if validation failed 854 */ 855 _required: function(field, rules, i, options, condRequired) { 856 switch (field.prop("type")) { 857 case "text": 858 case "password": 859 case "textarea": 860 case "file": 861 case "select-one": 862 case "select-multiple": 863 default: 864 865 if (! $.trim(field.val()) || field.val() == field.attr("data-validation-placeholder") || field.val() == field.attr("placeholder")) 866 return options.allrules[rules[i]].alertText; 867 break; 868 case "radio": 869 case "checkbox": 870 // new validation style to only check dependent field 871 if (condRequired) { 872 if (!field.attr('checked')) { 873 return options.allrules[rules[i]].alertTextCheckboxMultiple; 874 } 875 break; 876 } 877 // old validation style 878 var form = field.closest("form"); 879 var name = field.attr("name"); 880 if (form.find("input[name='" + name + "']:checked").size() == 0) { 881 if (form.find("input[name='" + name + "']:visible").size() == 1) 882 return options.allrules[rules[i]].alertTextCheckboxe; 883 else 884 return options.allrules[rules[i]].alertTextCheckboxMultiple; 885 } 886 break; 887 } 888 }, 889 /** 890 * Validate that 1 from the group field is required 891 * 892 * @param {jqObject} field 893 * @param {Array[String]} rules 894 * @param {int} i rules index 895 * @param {Map} 896 * user options 897 * @return an error string if validation failed 898 */ 899 _groupRequired: function(field, rules, i, options) { 900 var classGroup = "["+options.validateAttribute+"*=" +rules[i + 1] +"]"; 901 var isValid = false; 902 // Check all fields from the group 903 field.closest("form").find(classGroup).each(function(){ 904 if(!methods._required($(this), rules, i, options)){ 905 isValid = true; 906 return false; 907 } 908 }); 909 910 if(!isValid) { 911 return options.allrules[rules[i]].alertText; 912 } 913 }, 914 /** 915 * Validate rules 916 * 917 * @param {jqObject} field 918 * @param {Array[String]} rules 919 * @param {int} i rules index 920 * @param {Map} 921 * user options 922 * @return an error string if validation failed 923 */ 924 _custom: function(field, rules, i, options) { 925 var customRule = rules[i + 1]; 926 var rule = options.allrules[customRule]; 927 var fn; 928 if(!rule) { 929 alert("jqv:custom rule not found - "+customRule); 930 return; 931 } 932 933 if(rule["regex"]) { 934 var ex=rule.regex; 935 if(!ex) { 936 alert("jqv:custom regex not found - "+customRule); 937 return; 938 } 939 var pattern = new RegExp(ex); 940 941 if (!pattern.test(field.val())) return options.allrules[customRule].alertText; 942 943 } else if(rule["func"]) { 944 fn = rule["func"]; 945 946 if (typeof(fn) !== "function") { 947 alert("jqv:custom parameter 'function' is no function - "+customRule); 948 return; 949 } 950 951 if (!fn(field, rules, i, options)) 952 return options.allrules[customRule].alertText; 953 } else { 954 alert("jqv:custom type not allowed "+customRule); 955 return; 956 } 957 }, 958 /** 959 * Validate custom function outside of the engine scope 960 * 961 * @param {jqObject} field 962 * @param {Array[String]} rules 963 * @param {int} i rules index 964 * @param {Map} 965 * user options 966 * @return an error string if validation failed 967 */ 968 _funcCall: function(field, rules, i, options) { 969 var functionName = rules[i + 1];
970 var fn; 971 if(functionName.indexOf('.') >-1) 972 { 973 var namespaces = functionName.split('.'); 974 var scope = window; 975 while(namespaces.length) 976 { 977 scope = scope[namespaces.shift()]; 978 } 979 fn = scope; 980 } 981 else 982 fn = window[functionName] || options.customFunctions[functionName]; 983 if (typeof(fn) == 'function') 984 return fn(field, rules, i, options); 985 986 }, 987 /** 988 * Field match 989 * 990 * @param {jqObject} field 991 * @param {Array[String]} rules 992 * @param {int} i rules index 993 * @param {Map} 994 * user options 995 * @return an error string if validation failed 996 */ 997 _equals: function(field, rules, i, options) { 998 var equalsField = rules[i + 1]; 999 1000 if (field.val() != $("#" + equalsField).val()) 1001 return options.allrules.equals.alertText; 1002 }, 1003 /** 1004 * Check the maximum size (in characters) 1005 * 1006 * @param {jqObject} field 1007 * @param {Array[String]} rules 1008 * @param {int} i rules index 1009 * @param {Map} 1010 * user options 1011 * @return an error string if validation failed 1012 */ 1013 _maxSize: function(field, rules, i, options) { 1014 var max = rules[i + 1]; 1015 var len = field.val().length; 1016 1017 if (len > max) { 1018 var rule = options.allrules.maxSize; 1019 return rule.alertText + max + rule.alertText2; 1020 } 1021 }, 1022 /** 1023 * Check the minimum size (in characters) 1024 * 1025 * @param {jqObject} field 1026 * @param {Array[String]} rules 1027 * @param {int} i rules index 1028 * @param {Map} 1029 * user options 1030 * @return an error string if validation failed 1031 */ 1032 _minSize: function(field, rules, i, options) { 1033 var min = rules[i + 1]; 1034 var len = field.val().length; 1035 1036 if (len < min) { 1037 var rule = options.allrules.minSize; 1038 return rule.alertText + min + rule.alertText2; 1039 } 1040 }, 1041 /** 1042 * Check number minimum value 1043 * 1044 * @param {jqObject} field 1045 * @param {Array[String]} rules 1046 * @param {int} i rules index 1047 * @param {Map} 1048 * user options 1049 * @return an error string if validation failed 1050 */ 1051 _min: function(field, rules, i, options) { 1052 var min = parseFloat(rules[i + 1]); 1053 var len = parseFloat(field.val()); 1054 1055 if (len < min) { 1056 var rule = options.allrules.min; 1057 if (rule.alertText2) return rule.alertText + min + rule.alertText2; 1058 return rule.alertText + min; 1059 } 1060 }, 1061 /** 1062 * Check number maximum value 1063 * 1064 * @param {jqObject} field 1065 * @param {Array[String]} rules 1066 * @param {int} i rules index 1067 * @param {Map} 1068 * user options 1069 * @return an error string if validation failed 1070 */ 1071 _max: function(field, rules, i, options) { 1072 var max = parseFloat(rules[i + 1]); 1073 var len = parseFloat(field.val()); 1074 1075 if (len >max ) { 1076 var rule = options.allrules.max; 1077 if (rule.alertText2) return rule.alertText + max + rule.alertText2; 1078 //orefalo: to review, also do the translations 1079 return rule.alertText + max; 1080 } 1081 }, 1082 /** 1083 * Checks date is in the past 1084 * 1085 * @param {jqObject} field 1086 * @param {Array[String]} rules 1087 * @param {int} i rules index 1088 * @param {Map} 1089 * user options 1090 * @return an error string if validation failed 1091 */ 1092 _past: function(form, field, rules, i, options) { 1093 1094 var p=rules[i + 1]; 1095 var fieldAlt = $(form.find("input[name='" + p.replace(/^#+/, '') + "']")); 1096 var pdate; 1097 1098 if (p.toLowerCase() == "now") { 1099 pdate = new Date(); 1100 } else if (undefined != fieldAlt.val()) { 1101 if (fieldAlt.is(":disabled")) 1102 return; 1103 pdate = methods._parseDate(fieldAlt.val()); 1104 } else { 1105 pdate = methods._parseDate(p); 1106 } 1107 var vdate = methods._parseDate(field.val()); 1108 1109 if (vdate > pdate ) { 1110 var rule = options.allrules.past; 1111 if (rule.alertText2) return rule.alertText + methods._dateToString(pdate) + rule.alertText2; 1112 return rule.alertText + methods._dateToString(pdate); 1113 } 1114 }, 1115 /** 1116 * Checks date is in the future 1117 * 1118 * @param {jqObject} field 1119 * @param {Array[String]} rules 1120 * @param {int} i rules index 1121 * @param {Map} 1122 * user options 1123 * @return an error string if validation failed 1124 */ 1125 _future: function(form, field, rules, i, options) { 1126 1127 var p=rules[i + 1];
1128 var fieldAlt = $(form.find("input[name='" + p.replace(/^#+/, '') + "']")); 1129 var pdate; 1130 1131 if (p.toLowerCase() == "now") { 1132 pdate = new Date(); 1133 } else if (undefined != fieldAlt.val()) { 1134 if (fieldAlt.is(":disabled")) 1135 return; 1136 pdate = methods._parseDate(fieldAlt.val()); 1137 } else { 1138 pdate = methods._parseDate(p); 1139 } 1140 var vdate = methods._parseDate(field.val()); 1141 1142 if (vdate < pdate ) { 1143 var rule = options.allrules.future; 1144 if (rule.alertText2) 1145 return rule.alertText + methods._dateToString(pdate) + rule.alertText2; 1146 return rule.alertText + methods._dateToString(pdate); 1147 } 1148 }, 1149 /** 1150 * Checks if valid date 1151 * 1152 * @param {string} date string 1153 * @return a bool based on determination of valid date 1154 */ 1155 _isDate: function (value) { 1156 var dateRegEx = new RegExp(/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$|^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(?:(?:0?[1-9]|1[0-2])(\/|-)(?:0?[1-9]|1\d|2[0-8]))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(0?2(\/|-)29)(\/|-)(?:(?:0[48]00|[13579][26]00|[2468][048]00)|(?:\d\d)?(?:0[48]|[2468][048]|[13579][26]))$/); 1157 return dateRegEx.test(value); 1158 }, 1159 /** 1160 * Checks if valid date time 1161 * 1162 * @param {string} date string 1163 * @return a bool based on determination of valid date time 1164 */ 1165 _isDateTime: function (value){ 1166 var dateTimeRegEx = new RegExp(/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1}$|^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^((1[012]|0?[1-9]){1}\/(0?[1-9]|[12][0-9]|3[01]){1}\/\d{2,4}\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1})$/); 1167 return dateTimeRegEx.test(value); 1168 }, 1169 //Checks if the start date is before the end date 1170 //returns true if end is later than start 1171 _dateCompare: function (start, end) { 1172 return (new Date(start.toString()) < new Date(end.toString())); 1173 }, 1174 /** 1175 * Checks date range 1176 * 1177 * @param {jqObject} first field name 1178 * @param {jqObject} second field name 1179 * @return an error string if validation failed 1180 */ 1181 _dateRange: function (field, rules, i, options) { 1182 //are not both populated 1183 if ((!options.firstOfGroup[0].value && options.secondOfGroup[0].value) || (options.firstOfGroup[0].value && !options.secondOfGroup[0].value)) { 1184 return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2; 1185 } 1186 1187 //are not both dates 1188 if (!methods._isDate(options.firstOfGroup[0].value) || !methods._isDate(options.secondOfGroup[0].value)) { 1189 return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2; 1190 } 1191 1192 //are both dates but range is off 1193 if (!methods._dateCompare(options.firstOfGroup[0].value, options.secondOfGroup[0].value)) { 1194 return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2; 1195 } 1196 }, 1197 /** 1198 * Checks date time range 1199 * 1200 * @param {jqObject} first field name 1201 * @param {jqObject} second field name 1202 * @return an error string if validation failed 1203 */ 1204 _dateTimeRange: function (field, rules, i, options) { 1205 //are not both populated 1206 if ((!options.firstOfGroup[0].value && options.secondOfGroup[0].value) || (options.firstOfGroup[0].value && !options.secondOfGroup[0].value)) { 1207 return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2; 1208 } 1209 //are not both dates 1210 if (!methods._isDateTime(options.firstOfGroup[0].value) || !methods._isDateTime(options.secondOfGroup[0].value)) { 1211 return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2; 1212 } 1213 //are both dates but range is off 1214 if (!methods._dateCompare(options.firstOfGroup[0].value, options.secondOfGroup[0].value)) { 1215 return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2; 1216 } 1217 }, 1218 /** 1219 * Max number of checkbox selected 1220 * 1221 * @param {jqObject} field 1222 * @param {Array[String]} rules 1223 * @param {int} i rules index 1224 * @param {Map} 1225 * user options 1226 * @return an error string if validation failed 1227 */ 1228 _maxCheckbox: function(form, field, rules, i, options) { 1229 1230 var nbCheck = rules[i + 1];
1231 var groupname = field.attr("name"); 1232 var groupSize = form.find("input[name='" + groupname + "']:checked").size(); 1233 if (groupSize > nbCheck) { 1234 options.showArrow = false; 1235 if (options.allrules.maxCheckbox.alertText2) 1236 return options.allrules.maxCheckbox.alertText + " " + nbCheck + " " + options.allrules.maxCheckbox.alertText2; 1237 return options.allrules.maxCheckbox.alertText; 1238 } 1239 }, 1240 /** 1241 * Min number of checkbox selected 1242 * 1243 * @param {jqObject} field 1244 * @param {Array[String]} rules 1245 * @param {int} i rules index 1246 * @param {Map} 1247 * user options 1248 * @return an error string if validation failed 1249 */ 1250 _minCheckbox: function(form, field, rules, i, options) { 1251 1252 var nbCheck = rules[i + 1]; 1253 var groupname = field.attr("name"); 1254 var groupSize = form.find("input[name='" + groupname + "']:checked").size(); 1255 if (groupSize < nbCheck) { 1256 options.showArrow = false; 1257 return options.allrules.minCheckbox.alertText + " " + nbCheck + " " + options.allrules.minCheckbox.alertText2; 1258 } 1259 }, 1260 /** 1261 * Checks that it is a valid credit card number according to the 1262 * Luhn checksum algorithm. 1263 * 1264 * @param {jqObject} field 1265 * @param {Array[String]} rules 1266 * @param {int} i rules index 1267 * @param {Map} 1268 * user options 1269 * @return an error string if validation failed 1270 */ 1271 _creditCard: function(field, rules, i, options) { 1272 //spaces and dashes may be valid characters, but must be stripped to calculate the checksum. 1273 var valid = false, cardNumber = field.val().replace(/ +/g, '').replace(/-+/g, ''); 1274 1275 var numDigits = cardNumber.length; 1276 if (numDigits >= 14 && numDigits <= 16 && parseInt(cardNumber) > 0) { 1277 1278 var sum = 0, i = numDigits - 1, pos = 1, digit, luhn = new String(); 1279 do { 1280 digit = parseInt(cardNumber.charAt(i)); 1281 luhn += (pos++ % 2 == 0) ? digit * 2 : digit; 1282 } while (--i >= 0) 1283 1284 for (i = 0; i < luhn.length; i++) { 1285 sum += parseInt(luhn.charAt(i)); 1286 } 1287 valid = sum % 10 == 0; 1288 } 1289 if (!valid) return options.allrules.creditCard.alertText; 1290 }, 1291 /** 1292 * Ajax field validation 1293 * 1294 * @param {jqObject} field 1295 * @param {Array[String]} rules 1296 * @param {int} i rules index 1297 * @param {Map} 1298 * user options 1299 * @return nothing! the ajax validator handles the prompts itself 1300 */ 1301 _ajax: function(field, rules, i, options) { 1302 1303 var errorSelector = rules[i + 1]; 1304 var rule = options.allrules[errorSelector]; 1305 var extraData = rule.extraData; 1306 var extraDataDynamic = rule.extraDataDynamic; 1307 var data = { 1308 "fieldId" : field.attr("id"), 1309 "fieldValue" : field.val() 1310 }; 1311 1312 if (typeof extraData === "object") { 1313 $.extend(data, extraData); 1314 } else if (typeof extraData === "string") { 1315 var tempData = extraData.split("&"); 1316 for(var i = 0; i < tempData.length; i++) { 1317 var values = tempData[i].split("="); 1318 if (values[0] && values[0]) { 1319 data[values[0]] = values[1]; 1320 } 1321 } 1322 } 1323 1324 if (extraDataDynamic) { 1325 var tmpData = []; 1326 var domIds = String(extraDataDynamic).split(","); 1327 for (var i = 0; i < domIds.length; i++) { 1328 var id = domIds[i]; 1329 if ($(id).length) { 1330 var inputValue = field.closest("form").find(id).val(); 1331 var keyValue = id.replace('#', '') + '=' + escape(inputValue); 1332 data[id.replace('#', '')] = inputValue; 1333 } 1334 } 1335 } 1336 1337 // If a field change event triggered this we want to clear the cache for this ID 1338 if (options.eventTrigger == "field") { 1339 delete(options.ajaxValidCache[field.attr("id")]); 1340 } 1341 1342 // If there is an error or if the the field is already validated, do not re-execute AJAX 1343 if (!options.isError && !methods._checkAjaxFieldStatus(field.attr("id"), options)) { 1344 $.ajax({
1345 type: options.ajaxFormValidationMethod, 1346 url: rule.url, 1347 cache: false, 1348 dataType: "json", 1349 data: data, 1350 field: field, 1351 rule: rule, 1352 methods: methods, 1353 options: options, 1354 beforeSend: function() {}, 1355 error: function(data, transport) { 1356 methods._ajaxError(data, transport); 1357 }, 1358 success: function(json) { 1359 1360 // asynchronously called on success, data is the json answer from the server 1361 var errorFieldId = json[0]; 1362 //var errorField = $($("#" + errorFieldId)[0]); 1363 var errorField = $("#"+ errorFieldId).eq(0); 1364 1365 // make sure we found the element 1366 if (errorField.length == 1) { 1367 var status = json[1]; 1368 // read the optional msg from the server 1369 var msg = json[2]; 1370 if (!status) { 1371 // Houston we got a problem - display an red prompt 1372 options.ajaxValidCache[errorFieldId] = false; 1373 options.isError = true; 1374 1375 // resolve the msg prompt 1376 if(msg) { 1377 if (options.allrules[msg]) { 1378 var txt = options.allrules[msg].alertText; 1379 if (txt) { 1380 msg = txt; 1381 } 1382 } 1383 } 1384 else 1385 msg = rule.alertText; 1386 1387 methods._showPrompt(errorField, msg, "", true, options); 1388 } else { 1389 options.ajaxValidCache[errorFieldId] = true; 1390 1391 // resolves the msg prompt 1392 if(msg) { 1393 if (options.allrules[msg]) { 1394 var txt = options.allrules[msg].alertTextOk; 1395 if (txt) { 1396 msg = txt; 1397 } 1398 } 1399 } 1400 else 1401 msg = rule.alertTextOk; 1402 1403 // see if we should display a green prompt 1404 if (msg) 1405 methods._showPrompt(errorField, msg, "pass", true, options); 1406 else 1407 methods._closePrompt(errorField); 1408 1409 // If a submit form triggered this, we want to re-submit the form 1410 if (options.eventTrigger == "submit") 1411 field.closest("form").submit(); 1412 } 1413 } 1414 errorField.trigger("jqv.field.result", [errorField, options.isError, msg]); 1415 } 1416 }); 1417 1418 return rule.alertTextLoad; 1419 } 1420 }, 1421 /** 1422 * Common method to handle ajax errors 1423 * 1424 * @param {Object} data 1425 * @param {Object} transport 1426 */ 1427 _ajaxError: function(data, transport) { 1428 if(data.status == 0 && transport == null) 1429 alert("The page is not served from a server! ajax call failed"); 1430 else if(typeof console != "undefined") 1431 console.log("Ajax error: " + data.status + " " + transport); 1432 }, 1433 /** 1434 * date -> string 1435 * 1436 * @param {Object} date 1437 */ 1438 _dateToString: function(date) { 1439 return date.getFullYear()+"-"+(date.getMonth()+1)+"-"+date.getDate(); 1440 }, 1441 /** 1442 * Parses an ISO date 1443 * @param {String} d 1444 */ 1445 _parseDate: function(d) { 1446 1447 var dateParts = d.split("-"); 1448 if(dateParts==d) 1449 dateParts = d.split("/"); 1450 return new Date(dateParts[0], (dateParts[1] - 1) ,dateParts[2]); 1451 }, 1452 /** 1453 * Builds or updates a prompt with the given information 1454 * 1455 * @param {jqObject} field 1456 * @param {String} promptText html text to display type 1457 * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red) 1458 * @param {boolean} ajaxed - use to mark fields than being validated with ajax 1459 * @param {Map} options user options 1460 */ 1461 _showPrompt: function(field, promptText, type, ajaxed, options, ajaxform) { 1462 var prompt = methods._getPrompt(field); 1463 // The ajax submit errors are not see has an error in the form, 1464 // When the form errors are returned, the engine see 2 bubbles, but those are ebing closed by the engine at the same time 1465 // Because no error was found befor submitting 1466 if(ajaxform) prompt = false; 1467 if (prompt) 1468 methods._updatePrompt(field, prompt, promptText, type, ajaxed, options); 1469 else 1470 methods._buildPrompt(field, promptText, type, ajaxed, options); 1471 }, 1472 /** 1473 * Builds and shades a prompt for the given field. 1474 * 1475 * @param {jqObject} field 1476 * @param {String} promptText html text to display type 1477 * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red) 1478 * @param {boolean} ajaxed - use to mark fields than being validated with ajax 1479 * @param {Map} options user options 1480 */ 1481 _buildPrompt: function(field, promptText, type, ajaxed, options) { 1482 1483 // create the prompt 1484 var prompt = $('<div>'); 1485 prompt.addClass(methods._getClassName(field.attr("id")) + "formError"); 1486 // add a class name to identify the parent form of the prompt 1487 prompt.addClass("parentForm"+methods._getClassName(field.parents('form').attr("id"))); 1488 prompt.addClass("formError"); 1489 1490 switch (type) { 1491 case "pass": 1492 prompt.addClass("greenPopup"); 1493 break; 1494 case "load": 1495 prompt.addClass("blackPopup"); 1496 break; 1497 default: 1498 /* it has error */ 1499 //alert("unknown popup type:"+type); 1500 } 1501 if (ajaxed) 1502 prompt.addClass("ajaxed"); 1503 1504 // create the prompt content 1505 var promptContent = $('<div>').addClass("formErrorContent").html(promptText).appendTo(prompt); 1506 // create the css arrow pointing at the field 1507 // note that there is no triangle on max-checkbox and radio 1508 if (options.showArrow) { 1509 var arrow = $('<div>').addClass("formErrorArrow"); 1510 1511 //prompt positioning adjustment support. Usage: positionType:Xshift,Yshift (for ex.: bottomLeft:+20 or bottomLeft:-20,+10) 1512 var positionType=field.data("promptPosition") || options.promptPosition; 1513 if (typeof(positionType)=='string') 1514 { 1515 var pos=positionType.indexOf(":"); 1516 if(pos!=-1) 1517 positionType=positionType.substring(0,pos); 1518 } 1519 1520 switch (positionType) { 1521 case "bottomLeft": 1522 case "bottomRight": 1523 prompt.find(".formErrorContent").before(arrow); 1524 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>'); 1525 break; 1526 case "topLeft": 1527 case "topRight": 1528 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>'); 1529 prompt.append(arrow); 1530 break; 1531 } 1532 } 1533 // Modify z-indexes for jquery ui 1534 if (field.closest('.ui-dialog').length) 1535 prompt.addClass('formErrorInsideDialog'); 1536 1537 prompt.css({ 1538 "opacity": 0, 1539 'position':'absolute' 1540 }); 1541 field.before(prompt); 1542 1543 var pos = methods._calculatePosition(field, prompt, options); 1544 prompt.css({ 1545 "top": pos.callerTopPosition, 1546 "left": pos.callerleftPosition, 1547 "marginTop": pos.marginTopSize, 1548 "opacity": 0 1549 }).data("callerField", field); 1550 1551 if (options.autoHidePrompt) { 1552 setTimeout(function(){ 1553 prompt.animate({ 1554 "opacity": 0 1555 },function(){ 1556 prompt.closest('.formErrorOuter').remove(); 1557 prompt.remove(); 1558 });
1559 }, options.autoHideDelay); 1560 } 1561 return prompt.animate({ 1562 "opacity": 0.87 1563 }); 1564 }, 1565 /** 1566 * Updates the prompt text field - the field for which the prompt 1567 * @param {jqObject} field 1568 * @param {String} promptText html text to display type 1569 * @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red) 1570 * @param {boolean} ajaxed - use to mark fields than being validated with ajax 1571 * @param {Map} options user options 1572 */ 1573 _updatePrompt: function(field, prompt, promptText, type, ajaxed, options, noAnimation) { 1574 1575 if (prompt) { 1576 if (typeof type !== "undefined") { 1577 if (type == "pass") 1578 prompt.addClass("greenPopup"); 1579 else 1580 prompt.removeClass("greenPopup"); 1581 1582 if (type == "load") 1583 prompt.addClass("blackPopup"); 1584 else 1585 prompt.removeClass("blackPopup"); 1586 } 1587 if (ajaxed) 1588 prompt.addClass("ajaxed"); 1589 else 1590 prompt.removeClass("ajaxed"); 1591 1592 prompt.find(".formErrorContent").html(promptText); 1593 1594 var pos = methods._calculatePosition(field, prompt, options); 1595 var css = {"top": pos.callerTopPosition, 1596 "left": pos.callerleftPosition, 1597 "marginTop": pos.marginTopSize}; 1598 1599 if (noAnimation) 1600 prompt.css(css); 1601 else 1602 prompt.animate(css); 1603 } 1604 }, 1605 /** 1606 * Closes the prompt associated with the given field 1607 * 1608 * @param {jqObject} 1609 * field 1610 */ 1611 _closePrompt: function(field) { 1612 var prompt = methods._getPrompt(field); 1613 if (prompt) 1614 prompt.fadeTo("fast", 0, function() { 1615 prompt.parent('.formErrorOuter').remove(); 1616 prompt.remove(); 1617 }); 1618 }, 1619 closePrompt: function(field) { 1620 return methods._closePrompt(field); 1621 }, 1622 /** 1623 * Returns the error prompt matching the field if any 1624 * 1625 * @param {jqObject} 1626 * field 1627 * @return undefined or the error prompt (jqObject) 1628 */ 1629 _getPrompt: function(field) { 1630 var formId = $(field).closest('form').attr('id'); 1631 var className = methods._getClassName(field.attr("id")) + "formError"; 1632 var match = $("." + methods._escapeExpression(className) + '.parentForm' + formId)[0]; 1633 if (match) 1634 return $(match); 1635 }, 1636 /** 1637 * Returns the escapade classname 1638 * 1639 * @param {selector} 1640 * className 1641 */ 1642 _escapeExpression: function (selector) { 1643 return selector.replace(/([#;&,\.\+\*\~':"\!\^$\[\]\(\)=>\|])/g, "\\$1"); 1644 }, 1645 /** 1646 * returns true if we are in a RTLed document 1647 * 1648 * @param {jqObject} field 1649 */ 1650 isRTL: function(field) 1651 { 1652 var $document = $(document); 1653 var $body = $('body'); 1654 var rtl = 1655 (field && field.hasClass('rtl')) || 1656 (field && (field.attr('dir') || '').toLowerCase()==='rtl') || 1657 $document.hasClass('rtl') || 1658 ($document.attr('dir') || '').toLowerCase()==='rtl' || 1659 $body.hasClass('rtl') || 1660 ($body.attr('dir') || '').toLowerCase()==='rtl'; 1661 return Boolean(rtl); 1662 }, 1663 /** 1664 * Calculates prompt position 1665 * 1666 * @param {jqObject} 1667 * field 1668 * @param {jqObject} 1669 * the prompt 1670 * @param {Map} 1671 * options 1672 * @return positions 1673 */ 1674 _calculatePosition: function (field, promptElmt, options) { 1675 1676 var promptTopPosition, promptleftPosition, marginTopSize; 1677 var fieldWidth = field.width(); 1678 var fieldLeft = field.position().left; 1679 var fieldTop = field.position().top; 1680 var fieldHeight = field.height(); 1681 var promptHeight = promptElmt.height(); 1682 1683 1684 // is the form contained in an overflown container? 1685 promptTopPosition = promptleftPosition = 0; 1686 // compensation for the arrow 1687 marginTopSize = -promptHeight; 1688 1689 1690 //prompt positioning adjustment support 1691 //now you can adjust prompt position 1692 //usage: positionType:Xshift,Yshift 1693 //for example: 1694 // bottomLeft:+20 means bottomLeft position shifted by 20 pixels right horizontally 1695 // topRight:20, -15 means topRight position shifted by 20 pixels to right and 15 pixels to top 1696 //You can use +pixels, - pixels. If no sign is provided than + is default. 1697 var positionType=field.data("promptPosition") || options.promptPosition; 1698 var shift1=""; 1699 var shift2=""; 1700 var shiftX=0; 1701 var shiftY=0; 1702 if (typeof(positionType)=='string') { 1703 //do we have any position adjustments ? 1704 if (positionType.indexOf(":")!=-1) { 1705 shift1=positionType.substring(positionType.indexOf(":")+1); 1706 positionType=positionType.substring(0,positionType.indexOf(":")); 1707 1708 //if any advanced positioning will be needed (percents or something else) - parser should be added here 1709 //for now we use simple parseInt() 1710 1711 //do we have second parameter? 1712 if (shift1.indexOf(",") !=-1) { 1713 shift2=shift1.substring(shift1.indexOf(",") +1); 1714 shift1=shift1.substring(0,shift1.indexOf(",")); 1715 shiftY=parseInt(shift2); 1716 if (isNaN(shiftY)) shiftY=0; 1717 }; 1718 1719 shiftX=parseInt(shift1); 1720 if (isNaN(shift1)) shift1=0; 1721 1722 }; 1723 }; 1724 1725 1726 switch (positionType) { 1727 default: 1728 case "topRight": 1729 promptleftPosition += fieldLeft + fieldWidth - 30; 1730 promptTopPosition += fieldTop; 1731 break; 1732 1733 case "topLeft": 1734 promptTopPosition += fieldTop; 1735 promptleftPosition += fieldLeft; 1736 break; 1737 1738 case "centerRight": 1739 promptTopPosition = fieldTop+4; 1740 marginTopSize = 0; 1741 promptleftPosition= fieldLeft + field.outerWidth(true)+5; 1742 break; 1743 case "centerLeft": 1744 promptleftPosition = fieldLeft - (promptElmt.width() + 2); 1745 promptTopPosition = fieldTop+4; 1746 marginTopSize = 0; 1747 1748 break; 1749 1750 case "bottomLeft": 1751 promptTopPosition = fieldTop + field.height() + 5; 1752 marginTopSize = 0; 1753 promptleftPosition = fieldLeft; 1754 break; 1755 case "bottomRight": 1756 promptleftPosition = fieldLeft + fieldWidth - 30; 1757 promptTopPosition = fieldTop + field.height() + 5; 1758 marginTopSize = 0; 1759 }; 1760 1761 1762 1763 //apply adjusments if any 1764 promptleftPosition += shiftX; 1765 promptTopPosition += shiftY; 1766 1767 return { 1768 "callerTopPosition": promptTopPosition + "px", 1769 "callerleftPosition": promptleftPosition + "px", 1770 "marginTopSize": marginTopSize + "px" 1771 }; 1772 }, 1773 /** 1774 * Saves the user options and variables in the form.data 1775 * 1776 * @param {jqObject} 1777 * form - the form where the user option should be saved 1778 * @param {Map} 1779 * options - the user options 1780 * @return the user options (extended from the defaults) 1781 */ 1782 _saveOptions: function(form, options) { 1783 1784 // is there a language localisation ? 1785 if ($.validationEngineLanguage) 1786 var allRules = $.validationEngineLanguage.allRules; 1787 else 1788 $.error("jQuery.validationEngine rules are not loaded, plz add localization files to the page"); 1789 // --- Internals DO NOT TOUCH or OVERLOAD --- 1790 // validation rules and i18 1791 $.validationEngine.defaults.allrules = allRules; 1792 1793 var userOptions = $.extend(true,{},$.validationEngine.defaults,options); 1794 1795 form.data('jqv', userOptions); 1796 return userOptions; 1797 }, 1798 1799 /** 1800 * Removes forbidden characters from class name 1801 * @param {String} className 1802 */ 1803 _getClassName: function(className) { 1804 if(className) 1805 return className.replace(/:/g, "_").replace(/\./g, "_"); 1806 }, 1807 /** 1808 * Escape special character for jQuery selector 1809 * http://totaldev.com/content/escaping-characters-get-valid-jquery-id 1810 * @param {String} selector 1811 */ 1812 _jqSelector: function(str){ 1813 return str.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1'); 1814 }, 1815 /** 1816 * Conditionally required field 1817 * 1818 * @param {jqObject} field 1819 * @param {Array[String]} rules 1820 * @param {int} i rules index 1821 * @param {Map} 1822 * user options 1823 * @return an error string if validation failed 1824 */ 1825 _condRequired: function(field, rules, i, options) { 1826 var idx, dependingField; 1827 1828 for(idx = (i + 1); idx < rules.length; idx++) { 1829 dependingField = jQuery("#" + rules[idx]).first(); 1830 1831 /* Use _required for determining wether dependingField has a value. 1832 * There is logic there for handling all field types, and default value; so we won't replicate that here 1833 * Indicate this special use by setting the last parameter to true so we only validate the dependingField on chackboxes and radio buttons (#462) 1834 */ 1835 if (dependingField.length && methods._required(dependingField, ["required"], 0, options, true) == undefined) { 1836 /* We now know any of the depending fields has a value, 1837 * so we can validate this field as per normal required code 1838 */ 1839 return methods._required(field, ["required"], 0, options); 1840 } 1841 } 1842 } 1843 }; 1844 1845 /** 1846 * Plugin entry point. 1847 * You may pass an action as a parameter or a list of options. 1848 * if none, the init and attach methods are being called. 1849 * Remember: if you pass options, the attached method is NOT called automatically 1850 * 1851 * @param {String} 1852 * method (optional) action 1853 */ 1854 $.fn.validationEngine = function(method) { 1855 1856 var form = $(this); 1857 if(!form[0]) return form; // stop here if the form does not exist 1858 1859 if (typeof(method) == 'string' && method.charAt(0) != '_' && methods[method]) { 1860 1861 // make sure init is called once 1862 if(method != "showPrompt" && method != "hide" && method != "hideAll") 1863 methods.init.apply(form); 1864 1865 return methods[method].apply(form, Array.prototype.slice.call(arguments, 1)); 1866 } else if (typeof method == 'object' || !method) { 1867 1868 // default constructor with or without arguments 1869 methods.init.apply(form, arguments); 1870 return methods.attach.apply(form); 1871 } else { 1872 $.error('Method ' + method + ' does not exist in jQuery.validationEngine'); 1873 } 1874 }; 1875 1876 1877 1878 // LEAK GLOBAL OPTIONS 1879 $.validationEngine= {fieldIdCounter: 0,defaults:{ 1880 1881 // Name of the event triggering field validation 1882 validationEventTrigger: "blur", 1883 // Automatically scroll viewport to the first error 1884 scroll: true, 1885 // Focus on the first input 1886 focusFirstField:true, 1887 // Opening box position, possible locations are: topLeft, 1888 // topRight, bottomLeft, centerRight, bottomRight 1889 promptPosition: "topRight", 1890 bindMethod:"bind", 1891 // internal, automatically set to true when it parse a _ajax rule
1892 inlineAjax: false, 1893 // if set to true, the form data is sent asynchronously via ajax to the form.action url (get) 1894 ajaxFormValidation: false, 1895 // The url to send the submit ajax validation (default to action) 1896 ajaxFormValidationURL: false, 1897 // HTTP method used for ajax validation 1898 ajaxFormValidationMethod: 'get', 1899 // Ajax form validation callback method: boolean onComplete(form, status, errors, options) 1900 // retuns false if the form.submit event needs to be canceled. 1901 onAjaxFormComplete: $.noop, 1902 // called right before the ajax call, may return false to cancel 1903 onBeforeAjaxFormValidation: $.noop, 1904 // Stops form from submitting and execute function assiciated with it 1905 onValidationComplete: false, 1906 1907 // Used when you have a form fields too close and the errors messages are on top of other disturbing viewing messages 1908 doNotShowAllErrosOnSubmit: false, 1909 // Object where you store custom messages to override the default error messages 1910 custom_error_messages:{}, 1911 // true if you want to vind the input fields 1912 binded: true, 1913 // set to true, when the prompt arrow needs to be displayed 1914 showArrow: true, 1915 // did one of the validation fail ? kept global to stop further ajax validations 1916 isError: false, 1917 // Limit how many displayed errors a field can have 1918 maxErrorsPerField: false, 1919 1920 // Caches field validation status, typically only bad status are created. 1921 // the array is used during ajax form validation to detect issues early and prevent an expensive submit 1922 ajaxValidCache: {}, 1923 // Auto update prompt position after window resize 1924 autoPositionUpdate: false, 1925 1926 InvalidFields: [], 1927 onFieldSuccess: false, 1928 onFieldFailure: false, 1929 onFormSuccess: false, 1930 onFormFailure: false, 1931 addSuccessCssClassToField: false, 1932 addFailureCssClassToField: false, 1933 1934 // Auto-hide prompt 1935 autoHidePrompt: false, 1936 // Delay before auto-hide 1937 autoHideDelay: 10000, 1938 // Fade out duration while hiding the validations 1939 fadeDuration: 0.3, 1940 // Use Prettify select library 1941 prettySelect: false, 1942 // Custom ID uses prefix 1943 usePrefix: "", 1944 // Custom ID uses suffix 1945 useSuffix: "", 1946 // Only show one message per error prompt 1947 showOneMessage: false 1948 }}; 1949 $(function(){$.validationEngine.defaults.promptPosition = methods.isRTL()?'topLeft':"topRight"}); 1950})(jQuery); 1951 1952
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.