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