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