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