1/** 2* jQuery Form Validator 3* ------------------------------------------ 4* Created by Victor Jonsson <http://www.victorjonsson.se> 5* 6* @website http://formvalidator.net/ 7* @license Dual licensed under the MIT or GPL Version 2 licenses 8* @version 2.1.27 9*/ 10(function($) { 11 12 'use strict'; 13 14 /** 15 * Assigns validateInputOnBlur function to elements blur event 16 * 17 * @param {Object} language Optional, will override $.formUtils.LANG 18 * @param {Object} settings Optional, will override the default settings 19 * @return {jQuery} 20 */ 21 $.fn.validateOnBlur = function(language, settings) { 22 23 var blurValidationCallback = function() { 24 $(this).validateInputOnBlur(language, settings); 25 }; 26 27 this.find('input[data-validation], textarea[data-validation]') 28 .unbind('blur', blurValidationCallback) 29 .bind('blur', blurValidationCallback); 30 31 return this; 32 }; 33 34 /** 35 * fade in help message when input gains focus 36 * fade out when input loses focus 37 * <input data-help="The info that I want to display for the user when input is focused" ... /> 38 * 39 * @param {String} attrName - Optional, default is data-help 40 * @return {jQuery} 41 */ 42 $.fn.showHelpOnFocus = function(attrName) { 43 if(!attrName) { 44 attrName = 'data-validation-help'; 45 } 46 47 // Remove previously added event listeners 48 this.find('.has-help-txt') 49 .valAttr('has-keyup-event', false) 50 .valAttr('backend-valid', false) 51 .valAttr('backend-invalid', false) 52 .unbind('focus') 53 .unbind('blur') 54 .removeClass('has-help-txt'); 55 56 // Add help text listeners 57 this.find('textarea,input').each(function() { 58 var $element = $(this), 59 className = 'jquery_form_help_' + $element.attr('name'), 60 help = $element.attr(attrName); 61 62 if(help) { 63 $element 64 .addClass('has-help-txt') 65 .focus(function() { 66 var $help = $element.parent().find('.'+className); 67 if($help.length == 0) { 68 $help = $('<span />') 69 .addClass(className) 70 .addClass('help-block') // twitter bs 71 .text(help) 72 .hide(); 73 74 $element.after($help); 75 76 } 77 $help.fadeIn(); 78 }) 79 .blur(function() { 80 $(this) 81 .parent() 82 .find('.'+className) 83 .fadeOut('slow'); 84 }); 85 } 86 }); 87 88 return this; 89 }; 90 91 /** 92 * Validate single input when it loses focus 93 * shows error message in a span element 94 * that is appended to the parent element 95 * 96 * @param {Object} [language] Optional, will override $.formUtils.LANG 97 * @param {Object} [config] Optional, will override the default settings 98 * @param {Boolean} [attachKeyupEvent] Optional 99 * @param {String} [eventContext] 100 * @return {jQuery} 101 */ 102 $.fn.validateInputOnBlur = function(language, config, attachKeyupEvent, eventContext) { 103 if(attachKeyupEvent === undefined) 104 attachKeyupEvent = true; 105 if(!eventContext) 106 eventContext = 'blur'; 107 108 language = $.extend($.formUtils.LANG, language || {}); 109 config = $.extend($.formUtils.defaultConfig(), config || {}); 110 config.errorMessagePosition = 'element'; 111 112 var $element = this, 113 114 // test if there is custom obj to hold element error msg (id = element name + err_msg) 115 elementErrMsgObj = document.getElementById($element.attr('name')+'_err_msg'), 116 117 $form = $element.closest("form"), 118 119 validationRule = $element.attr(config.validationRuleAttribute); 120 121 // Remove possible error style applied by previous validation 122 $element 123 .removeClass(config.errorElementClass) 124 .css('border-color', '') 125 .parent() 126 .find('.'+config.errorMessageClass).remove(); 127 128 // Twitter bs 129 $form.find('.has-error').removeClass('has-error'); 130 $element.removeClass('valid').parent().removeClass('has-success'); 131 132 // if element has custom err msg container, clear it 133 if( elementErrMsgObj != null) { 134 elementErrMsgObj.innerHTML = ''; 135 } 136
137 var validation = $.formUtils.validateInput($element, language, config, $form, eventContext); 138 139 if(validation === true) { 140 $element 141 .addClass('valid') 142 .parent() 143 .addClass('has-success'); // twitter bs 144 } else if(validation === null) { 145 $element 146 .removeClass('valid') 147 .parent() 148 .removeClass('has-error') 149 .removeClass('has-success'); 150 } else { 151 $element 152 .addClass(config.errorElementClass) 153 .removeClass('valid') 154 .parent() 155 .addClass('has-error') 156 .removeClass('has-success'); // twitter bs 157 158 // if element has custom err msg container, use it 159 if( elementErrMsgObj != null) { 160 elementErrMsgObj.innerHTML = validation; 161 } else { // use regular span append 162 var $parent = $element.parent(); 163 $parent.append('<span class="'+config.errorMessageClass+' help-block">'+validation+'</span>'); 164 $parent.addClass('has-error'); // twitter bs 165 } 166 167 if(config.borderColorOnError !== '') { 168 $element.css('border-color', config.borderColorOnError); 169 } 170 171 if(attachKeyupEvent) { 172 $element.bind('keyup', function() { 173 $(this).validateInputOnBlur(language, config, false, 'keyup'); 174 }); 175 } 176 } 177 178 return this; 179 }; 180 181 /** 182 * Short hand for fetching/adding/removing element attributes 183 * prefixed with 'data-validation-' 184 * 185 * @param {String} name 186 * @param {String|Boolean} [val] 187 * @return string|undefined 188 * @protected 189 */ 190 $.fn.valAttr = function(name, val) { 191 if( val === undefined ) { 192 return this.attr('data-validation-'+name); 193 } else if( val === false || val === null ) { 194 return this.removeAttr('data-validation-'+name); 195 } else { 196 if(name.length > 0) name='-'+name; 197 return this.attr('data-validation'+name, val); 198 } 199 }; 200 201 /** 202 * Function that validate all inputs in a form 203 * 204 * @param [language] 205 * @param [config] 206 */ 207 $.fn.validateForm = function(language, config) { 208 209 language = $.extend($.formUtils.LANG, language || {}); 210 config = $.extend($.formUtils.defaultConfig(), config || {}); 211 212 $.formUtils.isValidatingEntireForm = true; 213 $.formUtils.haltValidation = false; 214 215 /** 216 * Adds message to error message stack if not already in the message stack 217 * 218 * @param {String} mess 219 * @para {jQuery} $element 220 */ 221 var addErrorMessage = function(mess, $element) { 222 // validate server side will return null as error message before the server is requested 223 if(mess !== null) { 224 if ($.inArray(mess, errorMessages) < 0) { 225 errorMessages.push(mess); 226 } 227 errorInputs.push($element); 228 $element 229 .valAttr('current-error', mess) 230 .removeClass('valid') 231 .parent() 232 .removeClass('has-success'); 233 } 234 }, 235 236 /** Error messages for this validation */ 237 errorMessages = [], 238 239 /** Input elements which value was not valid */ 240 errorInputs = [], 241 242 /** Form instance */ 243 $form = this, 244 245 /** 246 * Tells whether or not to validate element with this name and of this type 247 * 248 * @param {String} name 249 * @param {String} type 250 * @return {Boolean} 251 */ 252 ignoreInput = function(name, type) { 253 if (type === 'submit' || type === 'button') { 254 return true; 255 } 256 return $.inArray(name, config.ignore || []) > -1; 257 }; 258 259 // 260 // Validate element values 261 // 262 $form.find('input,textarea,select').filter(':not([type="submit"],[type="button"])').each(function() { 263 var $element = $(this); 264 var elementType = $element.attr('type'); 265 if (!ignoreInput($element.attr('name'), elementType)) { 266
267 var validation = $.formUtils.validateInput( 268 $element, 269 language, 270 config, 271 $form, 272 'submit' 273 ); 274 275 if(validation !== true) { 276 addErrorMessage(validation, $element); 277 } else { 278 $element 279 .valAttr('current-error', false) 280 .addClass('valid') 281 .parent() 282 .addClass('has-success'); 283 } 284 } 285 286 }); 287 288 // 289 // Reset style and remove error class 290 // 291 $form.find('.has-error').removeClass('has-error'); 292 $form.find('input,textarea,select') 293 .css('border-color', '') 294 .removeClass(config.errorElementClass); 295 296 // 297 // Remove possible error messages from last validation 298 // 299 $('.' + $.split(config.errorMessageClass, ' ').join('.')).remove(); 300 $('.'+config.errorMessageClass).remove(); 301 302 // 303 // Run validation callback 304 // 305 if( typeof config.onValidate == 'function' ) { 306 var resp = config.onValidate($form); 307 if( resp && resp.element && resp.message ) { 308 addErrorMessage(resp.message, resp.element); 309 } 310 } 311 312 // 313 // Validation failed 314 // 315 if (!$.formUtils.haltValidation && errorInputs.length > 0) { 316 317 // Reset form validation flag 318 $.formUtils.isValidatingEntireForm = false; 319 320 // Apply error style to invalid inputs 321 $.each(errorInputs, function(i, $input) { 322 if (config.borderColorOnError !== '') { 323 $input.css('border-color', config.borderColorOnError); 324 } 325 $input 326 .addClass(config.errorElementClass) 327 .parent() 328 .addClass('has-error'); 329 }); 330 331 // display all error messages in top of form 332 if (config.errorMessagePosition === 'top') { 333 var messages = '<strong>' + language.errorTitle + '</strong>'; 334 $.each(errorMessages, function(i, mess) { 335 messages += '<br />* ' + mess; 336 }); 337 338 // using div instead of P gives better control of css display properties 339 $form.children().eq(0).before('<div class="' + config.errorMessageClass + ' alert alert-danger">' + messages + '</div>'); 340 if(config.scrollToTopOnError) { 341 $(window).scrollTop($form.offset().top - 20); 342 } 343 } 344 345 // Display error message below input field 346 else { 347 $.each(errorInputs, function(i, $input) { 348 var $parent = $input.parent(), 349 $errorSpan = $parent.find('span[class='+config.errorMessageClass+']'); 350 if ($errorSpan.length > 0) { 351 $errorSpan.text(', '+$input.valAttr('current-error')); 352 } else { 353 $parent.append('<span class="'+config.errorMessageClass+' help-block">' + $input.valAttr('current-error') + '</span>'); 354 } 355 }); 356 } 357 return false; 358 } 359 360 // Reset form validation flag 361 $.formUtils.isValidatingEntireForm = false; 362 363 return !$.formUtils.haltValidation; 364 }; 365 366 /** 367 * Plugin for displaying input length restriction 368 */ 369 $.fn.restrictLength = function(maxLengthElement) { 370 new $.formUtils.lengthRestriction(this, maxLengthElement); 371 return this; 372 }; 373 374 /** 375 * Add suggestion dropdown to inputs having data-suggestions with a comma 376 * separated string with suggestions 377 * @param {Array} [settings] 378 * @returns {jQuery} 379 */ 380 $.fn.addSuggestions = function(settings) { 381 var sugs = false; 382 this.find('input').each(function() { 383 var $field = $(this); 384 385 sugs = $.split($field.attr('data-suggestions')); 386 387 if( sugs.length > 0 && !$field.hasClass('has-suggestions') ) { 388 $.formUtils.suggest($field, sugs, settings); 389 $field.addClass('has-suggestions'); 390 } 391 }); 392 return this; 393 }; 394 395 /** 396 * A bit smarter split function 397 * @param {String} val 398 * @param {Function|String} [func] 399 * @param {String} [delim] 400 * @returns {Array|void} 401 */ 402 $.split = function(val, func, delim) { 403 if( typeof func != 'function' ) { 404 // return string 405 if( !val ) 406 return []; 407 var values = []; 408 $.each(val.split(func ? func:','), function(i,str) { 409 str = $.trim(str); 410 if( str.length ) 411 values.push(str); 412 }); 413 return values; 414 } else if( val ) { 415 // use callback on each 416 if( !delim ) 417 delim = ','; 418 $.each(val.split(delim), function(i, str) { 419 str = $.trim(str); 420 if( str.length ) 421 return func(str, i); 422 }); 423 } 424 }; 425 426 /** 427 * Short hand function that makes the validation setup require less code 428 * @param config 429 */ 430 $.validate = function(config) { 431 config = $.extend({ 432 form : 'form', 433 validateOnBlur : true, 434 showHelpOnFocus : true, 435 addSuggestions : true, 436 modules : '', 437 onModulesLoaded : null, 438 language : false, 439 onSuccess : false, 440 onError : false 441 }, config || {}); 442 443 var formSubmitCallback = function() { 444 var $form = $(this); 445 if($.formUtils.isLoadingModules) { 446 setTimeout(function() { 447 $form.trigger('submit'); 448 }, 200); 449 return false; 450 }
451 var valid = $(this).validateForm(config.language, config); 452 if( valid && typeof config.onSuccess == 'function') { 453 var callbackResponse = config.onSuccess($form); 454 if( callbackResponse === false ) 455 return false; 456 } else if ( !valid && typeof config.onError == 'function' ) { 457 config.onError($form); 458 return false; 459 } else { 460 return valid; 461 } 462 }; 463 464 // Remove all event listeners previously added 465 $('form').unbind('submit', formSubmitCallback); 466 467 // Add validation to forms 468 $.split(config.form, function(formQuery) { 469 470 var $form = $(formQuery); 471 472 // Validate when submitted 473 $form.bind('submit', formSubmitCallback); 474 475 if( config.showHelpOnFocus ) { 476 $form.showHelpOnFocus(); 477 } 478 if( config.addSuggestions ) { 479 $form.addSuggestions(); 480 } 481 if( config.validateOnBlur ) { 482 $form.validateOnBlur(config.language, config); 483 } 484 }); 485 486 if( config.modules != '' ) { 487 if( typeof config.onModulesLoaded == 'function' ) { 488 $.formUtils.on('load', function() { 489 config.onModulesLoaded(); 490 }); 491 }
492 $.formUtils.loadModules(config.modules); 493 } 494 }; 495 496 /** 497 * @deprecated 498 * @param {Object} config 499 */ 500 $.validationSetup = function(config) { 501 if( typeof console != 'undefined' && console.warn ) { 502 window.console.warn('Using deprecated function $.validationSetup, pls use $.validate instead'); 503 } 504 $.validate(config); 505 }; 506 507 /** 508 * Object containing utility methods for this plugin 509 */ 510 $.formUtils = { 511 512 /** 513 * Default config for $(...).validateForm(); 514 */ 515 defaultConfig : function() { 516 return { 517 ignore : [], // Names of inputs not to be validated even though node attribute containing the validation rules tells us to 518 errorElementClass : 'error', // Class that will be put on elements which value is invalid 519 borderColorOnError : 'red', // Border color of elements which value is invalid, empty string to not change border color 520 errorMessageClass : 'form-error', // class name of div containing error messages when validation fails 521 validationRuleAttribute : 'data-validation', // name of the attribute holding the validation rules 522 validationErrorMsgAttribute : 'data-validation-error-msg', // define custom err msg inline with element 523 errorMessagePosition : 'element', // Can be either "top" or "element" 524 scrollToTopOnError : true, 525 dateFormat : 'yyyy-mm-dd', 526 addValidClassOnAll : false, // whether or not to apply class="valid" even if the input wasn't validated 527 decimalSeparator : '.' 528 } 529 }, 530 531 /** 532 * Available validators 533 */ 534 validators : {}, 535 536 /** 537 * Events triggered by form validator 538 */ 539 _events : {load : [], valid: [], invalid:[]}, 540 541 /** 542 * Setting this property to true during validation will 543 * stop further validation from taking place and form will 544 * not be sent 545 */ 546 haltValidation : false, 547 548 /** 549 * This variable will be true $.fn.validateForm() is called 550 * and false when $.fn.validateOnBlur is called 551 */ 552 isValidatingEntireForm : false, 553 554 /** 555 * Function for adding a validator 556 * @param {Object} validator 557 */ 558 addValidator : function(validator) { 559 // prefix with "validate_" for backward compatibility reasons 560 var name = validator.name.indexOf('validate_') === 0 ? validator.name : 'validate_'+validator.name; 561 if( validator.validateOnKeyUp === undefined ) 562 validator.validateOnKeyUp = true; 563 this.validators[name] = validator; 564 }, 565 566 /** 567 * @param {String} evt 568 * @param {Function} callback 569 */ 570 on : function(evt, callback) { 571 // Why not use $(document).bind('validators.loaded', func); 572 if( this._events[evt] === undefined ) 573 this._events[evt] = []; 574 this._events[evt].push(callback); 575 }, 576 577 /** 578 * @param {String} evt 579 * @param [argA] 580 * @param [argB] 581 */ 582 trigger : function(evt, argA, argB) { 583 $.each(this._events[evt] || [], function(i, func) { 584 func(argA, argB); 585 }); 586 }, 587 588 /** 589 * @ {Boolean} 590 */ 591 isLoadingModules : false, 592 593 loadedModules : {}, 594 595 /** 596 * @example
597 * $.formUtils.loadModules('date, security.dev'); 598 * 599 * Will load the scripts date.js and security.dev.js from the 600 * directory where this script resides. If you want to load 601 * the modules from another directory you can use the 602 * path argument. 603 * 604 * The script will be cached by the browser unless the module 605 * name ends with .dev 606 * 607 * @param {String} modules - Comma separated string with module file names (no directory nor file extension) 608 * @param {String} [path] - Optional, path where the module files is located if their not in the same directory as the core modules 609 * @param {Boolean} [fireEvent] - Optional, whether or not to fire event 'load' when modules finished loading 610 */ 611 loadModules : function(modules, path, fireEvent) { 612 613 if( fireEvent === undefined ) 614 fireEvent = true; 615 616 if( $.formUtils.isLoadingModules ) { 617 setTimeout(function() {
618 $.formUtils.loadModules(modules, path, fireEvent); 619 }); 620 return; 621 } 622 623 var hasLoadedAnyModule = false; 624 625 var loadModuleScripts = function(modules, path) { 626 var moduleList = $.split(modules), 627 numModules = moduleList.length, 628 moduleLoadedCallback = function() { 629 numModules--; 630 if( numModules == 0 ) { 631 $.formUtils.isLoadingModules = false; 632 if( fireEvent && hasLoadedAnyModule ) { 633 $.formUtils.trigger('load', path); 634 } 635 } 636 }; 637 638 if( numModules > 0 ) { 639 $.formUtils.isLoadingModules = true; 640 } 641 642 var cacheSuffix = '?__='+( new Date().getTime() ), 643 appendToElement = document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]; 644 645 $.each(moduleList, function(i, modName) { 646 modName = $.trim(modName); 647 if( modName.length == 0 ) { 648 moduleLoadedCallback(); 649 } 650 else { 651 var scriptUrl = path + modName + (modName.substr(-3) == '.js' ? '':'.js'), 652 script = document.createElement('SCRIPT'); 653 654 if( scriptUrl in $.formUtils.loadedModules ) { 655 // already loaded 656 moduleLoadedCallback(); 657 } 658 else { 659 660 // Remember that this script is loaded 661 $.formUtils.loadedModules[scriptUrl] = 1; 662 hasLoadedAnyModule = true; 663 664 // Load the script 665 script.type = 'text/javascript'; 666 script.onload = moduleLoadedCallback; 667 script.src = scriptUrl + ( scriptUrl.substr(-7) == '.dev.js' ? cacheSuffix:'' ); 668 script.onreadystatechange = function() { 669 // IE 7 fix 670 if( this.readyState == 'complete' ) { 671 moduleLoadedCallback(); 672 } 673 }; 674 appendToElement.appendChild( script ); 675 } 676 } 677 }); 678 }; 679 680 if( path ) { 681 loadModuleScripts(modules, path); 682 } else { 683 var findScriptPathAndLoadModules = function() { 684 var foundPath = false; 685 $('script').each(function() { 686 if( this.src ) { 687 var scriptName = this.src.substr(this.src.lastIndexOf('/')+1, this.src.length); 688 if(scriptName.indexOf('jquery.form-validator.js') > -1 || scriptName.indexOf('jquery.form-validator.min.js') > -1) { 689 foundPath = this.src.substr(0, this.src.lastIndexOf('/')) + '/'; 690 if( foundPath == '/' ) 691 foundPath = ''; 692 return false; 693 } 694 } 695 }); 696 697 if( foundPath !== false) { 698 loadModuleScripts(modules, foundPath); 699 return true; 700 } 701 return false; 702 }; 703 704 if( !findScriptPathAndLoadModules() ) { 705 $(findScriptPathAndLoadModules); 706 } 707 } 708 }, 709 710 /** 711 * Validate the value of given element according to the validation rules 712 * found in the attribute data-validation. Will return true if valid, 713 * error message otherwise 714 * 715 * @param {jQuery} $element 716 * @param {Object} language ($.formUtils.LANG) 717 * @param {Object} config 718 * @param {jQuery} $form 719 * @param {String} [eventContext] 720 * @return {String|Boolean} 721 */ 722 validateInput : function($element, language, config, $form, eventContext) { 723 724 var value = $.trim( $element.val() || ''), 725 optional = $element.valAttr('optional'), 726 727 // test if a checkbox forces this element to be validated 728 validationDependsOnCheckedInput = false, 729 validationDependentInputIsChecked = false, 730 validateIfCheckedElement = false, 731 732 // get value of this element's attribute "... if-checked" 733 validateIfCheckedElementName = $element.valAttr("if-checked"); 734 735 // make sure we can proceed 736 if (validateIfCheckedElementName != null) { 737 738 // Set the boolean telling us that the validation depends 739 // on another input being checked 740 validationDependsOnCheckedInput = true; 741 742 // select the checkbox type element in this form 743 validateIfCheckedElement = $form.find('input[name="' + validateIfCheckedElementName + '"]'); 744 745 // test if it's property "checked" is checked 746 if ( validateIfCheckedElement.prop('checked') ) { 747 // set value for validation checkpoint 748 validationDependentInputIsChecked = true; 749 } 750 } 751 752 // validation checkpoint 753 // if empty AND optional attribute is present 754 // OR depending on a checkbox being checked AND checkbox is checked, return true 755 if ((!value && optional === 'true') || (validationDependsOnCheckedInput && !validationDependentInputIsChecked)) { 756 return config.addValidClassOnAll ? true:null; 757 } 758
759 var validationRules = $element.attr(config.validationRuleAttribute), 760 761 // see if form element has inline err msg attribute 762 validationErrorMsg = true; 763 764 if( !validationRules ) { 765 return config.addValidClassOnAll ? true:null; 766 } 767 768 $.split(validationRules, function(rule) { 769 if( rule.indexOf('validate_') !== 0 ) { 770 rule = 'validate_' + rule; 771 } 772 773 var validator = $.formUtils.validators[rule]; 774 775 if( validator && typeof validator['validatorFunction'] == 'function' ) { 776 // special change of element for checkbox_group rule 777 if ( rule == 'validate_checkbox_group' ) { 778 // set element to first in group, so error msg is set only once 779 $element = $("[name='"+$element.attr('name')+"']:eq(0)"); 780 } 781 782 var isValid = true; 783 if( eventContext != 'keyup' || validator.validateOnKeyUp ) { 784 isValid = validator.validatorFunction(value, $element, config, language, $form); 785 } 786 787 if(!isValid) { 788 validationErrorMsg = $element.attr(config.validationErrorMsgAttribute); 789 if( !validationErrorMsg ) { 790 validationErrorMsg = language[validator.errorMessageKey]; 791 if( !validationErrorMsg ) 792 validationErrorMsg = validator.errorMessage; 793 } 794 return false; // breaks the iteration 795 } 796 797 } else { 798 console.warn('Using undefined validator "'+rule+'"'); 799 } 800 801 }, ' '); 802 803 if( typeof validationErrorMsg == 'string' ) { 804 return validationErrorMsg; 805 } else { 806 return true; 807 } 808 }, 809 810 /** 811 * Is it a correct date according to given dateFormat. Will return false if not, otherwise 812 * an array 0=>year 1=>month 2=>day 813 * 814 * @param {String} val 815 * @param {String} dateFormat 816 * @return {Array}|{Boolean} 817 */ 818 parseDate : function(val, dateFormat) { 819 var divider = dateFormat.replace(/[a-zA-Z]/gi, '').substring(0,1), 820 regexp = '^', 821 formatParts = dateFormat.split(divider), 822 matches, day, month, year; 823 824 $.each(formatParts, function(i, part) { 825 regexp += (i > 0 ? '\\'+divider:'') + '(\\d{'+part.length+'})'; 826 }); 827 828 regexp += '$'; 829 830 matches = val.match(new RegExp(regexp)); 831 if (matches === null) { 832 return false; 833 } 834 835 var findDateUnit = function(unit, formatParts, matches) { 836 for(var i=0; i < formatParts.length; i++) { 837 if(formatParts[i].substring(0,1) === unit) { 838 return $.formUtils.parseDateInt(matches[i+1]); 839 } 840 } 841 return -1; 842 }; 843 844 month = findDateUnit('m', formatParts, matches); 845 day = findDateUnit('d', formatParts, matches); 846 year = findDateUnit('y', formatParts, matches); 847 848 if ((month === 2 && day > 28 && (year % 4 !== 0 || year % 100 === 0 && year % 400 !== 0)) 849 || (month === 2 && day > 29 && (year % 4 === 0 || year % 100 !== 0 && year % 400 === 0)) 850 || month > 12 || month === 0) { 851 return false; 852 } 853 if ((this.isShortMonth(month) && day > 30) || (!this.isShortMonth(month) && day > 31) || day === 0) { 854 return false; 855 } 856 857 return [year, month, day]; 858 }, 859 860 /** 861 * skum fix. är talet 05 eller lägre ger parseInt rätt int annars får man 0 när man kör parseInt? 862 * 863 * @param {String} val 864 * @param {Number} 865 */ 866 parseDateInt : function(val) { 867 if (val.indexOf('0') === 0) { 868 val = val.replace('0', ''); 869 } 870 return parseInt(val,10); 871 }, 872 873 /** 874 * Has month only 30 days? 875 * 876 * @param {Number} m 877 * @return {Boolean} 878 */ 879 isShortMonth : function(m) { 880 return (m % 2 === 0 && m < 7) || (m % 2 !== 0 && m > 7); 881 }, 882 883 /** 884 * Restrict input length 885 * 886 * @param {jQuery} $inputElement Jquery Html object 887 * @param {jQuery} $maxLengthElement jQuery Html Object 888 * @return void 889 */ 890 lengthRestriction : function($inputElement, $maxLengthElement) { 891 // read maxChars from counter display initial text value 892 var maxChars = parseInt($maxLengthElement.text(),10), 893 894 // internal function does the counting and sets display value 895 countCharacters = function() { 896 var numChars = $inputElement.val().length; 897 if(numChars > maxChars) { 898 // get current scroll bar position 899 var currScrollTopPos = $inputElement.scrollTop(); 900 // trim value to max length 901 $inputElement.val($inputElement.val().substring(0, maxChars)); 902 $inputElement.scrollTop(currScrollTopPos); 903 } 904 // set counter text 905 $maxLengthElement.text(maxChars - numChars); 906 }; 907 908 // bind events to this element 909 // setTimeout is needed, cut or paste fires before val is available 910 $($inputElement).bind('keydown keyup keypress focus blur', countCharacters ) 911 .bind('cut paste', function(){ setTimeout(countCharacters, 100); } ) ; 912
913 // count chars on pageload, if there are prefilled input-values 914 $(document).bind("ready", countCharacters); 915 }, 916 917 /** 918 * Test numeric against allowed range 919 * 920 * @param $value int 921 * @param $rangeAllowed str; (1-2, min1, max2) 922 * @return array 923 */ 924 numericRangeCheck : function(value, rangeAllowed) 925 { 926 // split by dash 927 var range = $.split(rangeAllowed, '-'); 928 // min or max 929 var minmax = parseInt(rangeAllowed.substr(3),10) 930 // range ? 931 if (range.length == 2 && (value < parseInt(range[0],10) || value > parseInt(range[1],10) ) ) 932 { return [ "out", range[0], range[1] ] ; } // value is out of range 933 else if (rangeAllowed.indexOf('min') === 0 && (value < minmax ) ) // min 934 { return ["min", minmax]; } // value is below min 935 else if (rangeAllowed.indexOf('max') === 0 && (value > minmax ) ) // max 936 { return ["max", minmax]; } // value is above max 937 else { return [ "ok" ] ; } // value is in allowed range 938 }, 939 940 941 _numSuggestionElements : 0, 942 _selectedSuggestion : null, 943 _previousTypedVal : null, 944 945 /** 946 * Utility function that can be used to create plugins that gives
947 * suggestions when inputs is typed into 948 * @param {jQuery} $element 949 * @param {Array} suggestions 950 * @param {Object} settings - Optional 951 * @return {jQuery} 952 */ 953 suggest : function($element, suggestions, settings) { 954 var config = { 955 css : { 956 maxHeight: '150px', 957 background: '#FFF', 958 lineHeight:'150%', 959 textDecoration : 'underline', 960 overflowX : 'hidden', 961 overflowY : 'auto', 962 border : '#CCC solid 1px', 963 borderTop : 'none', 964 cursor: 'pointer' 965 }, 966 activeSuggestionCSS : { 967 background : '#E9E9E9' 968 } 969 }; 970 971 if(settings) 972 $.extend(config, settings); 973 974 config.css['position'] = 'absolute'; 975 config.css['z-index'] = 9999; 976 $element.attr('autocomplete', 'off'); 977 978 this._numSuggestionElements++; 979 980 var onSelectSuggestion = function($el) { 981 var suggestionId = $el.valAttr('suggestion-nr'); 982 $.formUtils._selectedSuggestion = null; 983 $.formUtils._previousTypedVal = null; 984 $('.jquery-form-suggestion-'+suggestionId).fadeOut('fast'); 985 }; 986 987 $element 988 .data('suggestions', suggestions) 989 .valAttr('suggestion-nr', this._numSuggestionElements) 990 .bind('focus', function() { 991 $(this).trigger('keyup'); 992 $.formUtils._selectedSuggestion = null; 993 }) 994 .bind('keyup', function() { 995 var $input = $(this), 996 foundSuggestions = [], 997 val = $.trim($input.val()).toLocaleLowerCase(); 998 999 if(val == $.formUtils._previousTypedVal) { 1000 return; 1001 } 1002 else { 1003 $.formUtils._previousTypedVal = val; 1004 } 1005 1006 var hasTypedSuggestion = false, 1007 suggestionId = $input.valAttr('suggestion-nr'), 1008 $suggestionContainer = $('.jquery-form-suggestion-'+suggestionId); 1009 1010 $suggestionContainer.scrollTop(0); 1011 1012 // Find the right suggestions 1013 if(val != '') { 1014 var findPartial = val.length > 2; 1015 $.each($input.data('suggestions'), function(i, suggestion) { 1016 var lowerCaseVal = suggestion.toLocaleLowerCase(); 1017 if( lowerCaseVal == val ) { 1018 foundSuggestions.push('<strong>'+suggestion+'</strong>'); 1019 hasTypedSuggestion = true; 1020 return false; 1021 } else if(lowerCaseVal.indexOf(val) === 0 || (findPartial && lowerCaseVal.indexOf(val) > -1)) { 1022 foundSuggestions.push(suggestion.replace(new RegExp(val, 'gi'), '<strong>$&</strong>')); 1023 } 1024 }); 1025 } 1026 1027 // Hide suggestion container 1028 if(hasTypedSuggestion || (foundSuggestions.length == 0 && $suggestionContainer.length > 0)) { 1029 $suggestionContainer.hide(); 1030 } 1031 1032 // Create suggestion container if not already exists 1033 else if(foundSuggestions.length > 0 && $suggestionContainer.length == 0) {
1034 $suggestionContainer = $('<div></div>').css(config.css).appendTo('body'); 1035 $suggestionContainer.addClass('jquery-form-suggestions'); 1036 $suggestionContainer.addClass('jquery-form-suggestion-'+suggestionId); 1037 } 1038 1039 // Show hidden container 1040 else if(foundSuggestions.length > 0 && !$suggestionContainer.is(':visible')) { 1041 $suggestionContainer.show(); 1042 } 1043 1044 // add suggestions 1045 if(foundSuggestions.length > 0 && val.length != foundSuggestions[0].length) { 1046 1047 // put container in place every time, just in case 1048 var offset = $input.offset(); 1049 $suggestionContainer.css({ 1050 width : $input.outerWidth(), 1051 left : offset.left + 'px', 1052 top : (offset.top + $input.outerHeight()) +'px' 1053 }); 1054 1055 // Add suggestions HTML to container 1056 $suggestionContainer.html(''); 1057 $.each(foundSuggestions, function(i, text) { 1058 $('<div></div>') 1059 .append(text) 1060 .css({ 1061 overflow: 'hidden', 1062 textOverflow : 'ellipsis', 1063 whiteSpace : 'nowrap', 1064 padding: '5px' 1065 }) 1066 .addClass('form-suggest-element') 1067 .appendTo($suggestionContainer) 1068 .click(function() { 1069 $input.focus(); 1070 $input.val( $(this).text() ); 1071 onSelectSuggestion($input); 1072 }); 1073 }); 1074 } 1075 }) 1076 .bind('keydown', function(e) { 1077 var code = (e.keyCode ? e.keyCode : e.which), 1078 suggestionId, 1079 $suggestionContainer, 1080 $input = $(this); 1081 1082 if(code == 13 && $.formUtils._selectedSuggestion !== null) { 1083 suggestionId = $input.valAttr('suggestion-nr'); 1084 $suggestionContainer = $('.jquery-form-suggestion-'+suggestionId); 1085 if($suggestionContainer.length > 0) { 1086 var newText = $suggestionContainer.find('div').eq($.formUtils._selectedSuggestion).text(); 1087 $input.val(newText); 1088 onSelectSuggestion($input); 1089 e.preventDefault(); 1090 } 1091 } 1092 else { 1093 suggestionId = $input.valAttr('suggestion-nr'); 1094 $suggestionContainer = $('.jquery-form-suggestion-'+suggestionId); 1095 var $suggestions = $suggestionContainer.children(); 1096 if($suggestions.length > 0 && $.inArray(code, [38,40]) > -1) { 1097 if(code == 38) { // key up 1098 if($.formUtils._selectedSuggestion === null) 1099 $.formUtils._selectedSuggestion = $suggestions.length-1; 1100 else 1101 $.formUtils._selectedSuggestion--; 1102 if($.formUtils._selectedSuggestion < 0) 1103 $.formUtils._selectedSuggestion = $suggestions.length-1; 1104 } 1105 else if(code == 40) { // key down 1106 if($.formUtils._selectedSuggestion === null) 1107 $.formUtils._selectedSuggestion = 0; 1108 else 1109 $.formUtils._selectedSuggestion++; 1110 if($.formUtils._selectedSuggestion > ($suggestions.length-1)) 1111 $.formUtils._selectedSuggestion = 0; 1112 1113 } 1114 1115 // Scroll in suggestion window 1116 var containerInnerHeight = $suggestionContainer.innerHe
1116ight(), 1117 containerScrollTop = $suggestionContainer.scrollTop(), 1118 suggestionHeight = $suggestionContainer.children().eq(0).outerHeight(), 1119 activeSuggestionPosY = suggestionHeight * ($.formUtils._selectedSuggestion); 1120 1121 if( activeSuggestionPosY < containerScrollTop || activeSuggestionPosY > (containerScrollTop+containerInnerHeight)) { 1122 $suggestionContainer.scrollTop( activeSuggestionPosY ); 1123 } 1124 1125 $suggestions 1126 .removeClass('active-suggestion') 1127 .css('background', 'none') 1128 .eq($.formUtils._selectedSuggestion) 1129 .addClass('active-suggestion') 1130 .css(config.activeSuggestionCSS); 1131 1132 e.preventDefault(); 1133 return false; 1134 } 1135 } 1136 }) 1137 .bind('blur', function() { 1138 onSelectSuggestion($(this)); 1139 }); 1140 1141 return $element; 1142 }, 1143 1144 /** 1145 * Error dialogs 1146 * 1147 * @var {Object} 1148 */ 1149 LANG : { 1150 errorTitle : 'Form submission failed!', 1151 requiredFields : 'Bitte dieses Feld ausfüllen', 1152 badTime : 'You have not given a correct time', 1153 badEmail : 'Bitte eine E-Mail-Adresse eintragen (in Kleinbuchstaben)', 1154 badTelephone : 'You have not given a correct phone number', 1155 badSecurityAnswer : 'You have not given a correct answer to the security question', 1156 badDate : 'You have not given a correct date', 1157 lengthBadStart : 'Bitte eine Eingabe zwischen ', 1158 lengthBadEnd : ' Zeichen', 1159 lengthTooLongStart : 'Diese Eingabe ist länger als ', 1160 lengthTooShortStart : 'Diese Eingabe ist kürzer als ', 1161 notConfirmed : 'Diese Eingabe stimmt nicht überein', 1162 badDomain : 'Incorrect domain value', 1163 badUrl : 'The answer you gave was not a correct URL', 1164 badCustomVal : 'You gave an incorrect answer', 1165 badInt : 'Die Eingabe muss eine Zahl sein', 1166 badSecurityNumber : 'Your social security number was incorrect', 1167 badUKVatAnswer : 'Incorrect UK VAT Number', 1168 badStrength : 'The password isn\'t strong enough', 1169 badNumberOfSelectedOptionsStart : 'You have to choose at least ', 1170 badNumberOfSelectedOptionsEnd : ' answers', 1171 badAlphaNumeric : 'Die Eingabe darf nur Buchstaben und Zahlen enthalten, keine Sonderzeichen wie ä, ö, ü, ß', 1172 badAlphaNumericExtra: ' and ', 1173 wrongFileSize : 'The file you are trying to upload is too large', 1174 wrongFileType : 'The file you are trying to upload is of wrong type', 1175 groupCheckedRangeStart : 'Please choose between ', 1176 groupCheckedTooFewStart : 'Bitte wähle mindestens ', 1177 groupCheckedTooManyStart : 'Please choose a maximum of ', 1178 groupCheckedEnd : ' Möglichkeit' 1179 } 1180 }; 1181 1182 1183 /* * * * * * * * * * * * * * * * * * * * * * 1184 CORE VALIDATORS 1185 * * * * * * * * * * * * * * * * * * * * */ 1186 1187 1188 /* 1189 * Validate email 1190 */ 1191 $.formUtils.addValidator({ 1192 name : 'email', 1193 validatorFunction : function(email) { 1194 1195 var emailParts = email.split('@'); 1196 if( emailParts.length == 2 ) { 1197 return $.formUtils.validators.validate_domain.validatorFunction(emailParts[1]) && 1198 !(/[^a-zA-Z0-9_\+\.\-]/.test(emailParts[0])); 1199 } 1200 1201 return false; 1202 }, 1203 errorMessage : '', 1204 errorMessageKey : 'badEmail' 1205 }); 1206 1207 /* 1208 * Validate domain name 1209 */ 1210 $.formUtils.addValidator({ 1211 name: 'domain', 1212 validatorFunction: function(val, $input) { 1213 if (typeof val !== 'string') return false; 1214 1215 val = val.trim().toLowerCase(); 1216 1217 // Regex to match modern domain names 1218 const domainRegex = /^(?!-)[a-z0-9-]{1,63}(?<!-)(\.(?!-)[a-z0-9-]{1,63}(?<!-))*\.[a-z]{2,}$/; 1219 1220 const isValid = domainRegex.test(val); 1221 1222 if (isValid && typeof $input !== 'undefined') { 1223 $input.val(val); // Optional: normalize input 1224 } 1225 1226 return isValid; 1227 }, 1228 errorMessage: 'Bitte trage einen korrekten Domain Namen ein.', 1229 errorMessageKey: 'badDomain' 1230}); 1231 1232 /* 1233 * Validate required 1234 */ 1235 $.formUtils.addValidator({ 1236 name : 'required', 1237 validatorFunction : function(val, $el) { 1238 return $el.attr('type') == 'checkbox' ? $el.is(':checked') : $.trim(val) !== ''; 1239 }, 1240 errorMessage : '', 1241 errorMessageKey: 'requiredFields' 1242 });
1243 1244 /* 1245 * Validate length range 1246 */ 1247 $.formUtils.addValidator({ 1248 name : 'length', 1249 validatorFunction : function(val, $el, config, lang) { 1250 var lengthAllowed = $el.valAttr('length'), 1251 type = $el.attr('type'); 1252 1253 if(lengthAllowed == undefined) { 1254 var elementType = $el.get(0).nodeName; 1255 alert('Please add attribute "data-validation-length" to '+elementType+' named '+$el.attr('name')); 1256 return true; 1257 } 1258 1259 // check if length is above min, below max or within range. 1260 var len = type == 'file' && $el.get(0).files !== undefined ? $el.get(0).files.length : val.length, 1261 lengthCheckResults = $.formUtils.numericRangeCheck(len, lengthAllowed), 1262 checkResult; 1263 1264 switch(lengthCheckResults[0] ) 1265 { // outside of allowed range 1266 case "out": 1267 this.errorMessage = lang.lengthBadStart + lengthAllowed + lang.lengthBadEnd; 1268 checkResult = false; 1269 break; 1270 // too short 1271 case "min": 1272 this.errorMessage = lang.lengthTooShortStart + lengthCheckResults[1] + lang.lengthBadEnd; 1273 checkResult = false; 1274 break; 1275 // too long 1276 case "max": 1277 this.errorMessage = lang.lengthTooLongStart + lengthCheckResults[1] + lang.lengthBadEnd; 1278 checkResult = false; 1279 break; 1280 // ok 1281 default: 1282 checkResult = true; 1283 } 1284 1285 return checkResult; 1286 }, 1287 errorMessage : '', 1288 errorMessageKey: '' 1289 }); 1290 1291 /* 1292 * Validate url 1293 */ 1294 $.formUtils.addValidator({ 1295 name : 'url', 1296 validatorFunction : function(url) { 1297 // written by Scott Gonzalez: http://projects.scottsplayground.com/iri/ but added support for arrays in the url ?arg[]=sdfsdf 1298 var urlFilter = /^(https|http|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|\[|\]|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i; 1299 if( urlFilter.test(url) ) { 1300 var domain = url.split('://')[1]; 1301 var domainSlashPos = domain.indexOf('/'); 1302 if(domainSlashPos > -1) 1303 domain = domain.substr(0, domainSlashPos); 1304 1305 return $.formUtils.validators.validate_domain.validatorFunction(domain); // todo: add support for IP-addresses 1306 } 1307 return false; 1308 }, 1309 errorMessage : '', 1310 errorMessageKey: 'badUrl' 1311 }); 1312 1313 /* 1314 * Validate number (floating or integer) 1315 */ 1316 $.formUtils.addValidator({ 1317 name : 'number', 1318 validatorFunction : function(val, $el, config) { 1319 if(val !== '') { 1320 var allowing = $el.valAttr('allowing') || '', 1321 decimalSeparator = $el.valAttr('decimal-separator') || config.decimalSeparator, 1322 allowsRange = false, 1323 begin, end; 1324 1325 if(allowing.indexOf('number') == -1) 1326 allowing += ',number'; 1327 1328 if(allowing.indexOf('negative') > -1 && val.indexOf('-') === 0) { 1329 val = val.substr(1); 1330 } 1331 1332 if (allowing.indexOf('range') > -1) 1333 { 1334 begin = parseFloat(allowing.substring(allowing.indexOf("[")+1, allowing.indexOf(";"))); 1335 end = parseFloat(allowing.substring(allowing.indexOf(";")+1,allowing.indexOf("]"))); 1336 allowsRange = true; 1337 } 1338 1339 if(allowing.indexOf('number') > -1 && val.replace(/[0-9]/g, '') === '' && (!allowsRange || (val >= begin && val <= end)) ) { 1340 return true; 1341 } 1342 if(allowing.indexOf('float') >
1342 -1 && val.match(new RegExp('^([0-9]+)\\'+decimalSeparator+'([0-9]+)$')) !== null && (!allowsRange || (val >= begin && val <= end)) ) { 1343 return true; 1344 } 1345 } 1346 return false; 1347 }, 1348 errorMessage : '', 1349 errorMessageKey: 'badInt' 1350 }); 1351 1352 /* 1353 * Validate alpha numeric 1354 */ 1355 $.formUtils.addValidator({ 1356 name : 'alphanumeric', 1357 validatorFunction : function(val, $el, config, language) { 1358 var patternStart = '^([a-zA-Z0-9', 1359 patternEnd = ']+)$', 1360 additionalChars = $el.attr('data-validation-allowing'), 1361 pattern = ''; 1362 1363 if( additionalChars ) { 1364 pattern = patternStart + additionalChars + patternEnd; 1365 var extra = additionalChars.replace(/\\/g, ''); 1366 if( extra.indexOf(' ') > -1 ) { 1367 extra = extra.replace(' ', ''); 1368 extra += ' and spaces '; 1369 } 1370 this.errorMessage = language.badAlphaNumeric + language.badAlphaNumericExtra + extra; 1371 } else { 1372 pattern = patternStart + patternEnd; 1373 this.errorMessage = language.badAlphaNumeric; 1374 } 1375 1376 return new RegExp(pattern).test(val); 1377 }, 1378 errorMessage : '', 1379 errorMessageKey: '' 1380 }); 1381 1382 /* 1383 * Validate against regexp 1384 */ 1385 $.formUtils.addValidator({ 1386 name : 'custom', 1387 validatorFunction : function(val, $el, config) { 1388 var regexp = new RegExp($el.valAttr('regexp')); 1389 return regexp.test(val); 1390 }, 1391 errorMessage : '', 1392 errorMessageKey: 'badCustomVal' 1393 }); 1394 1395 /* 1396 * Validate date 1397 */ 1398 $.formUtils.addValidator({ 1399 name : 'date', 1400 validatorFunction : function(date, $el, conf) { 1401 var dateFormat = 'yyyy-mm-dd'; 1402 if($el.valAttr('format')) { 1403 dateFormat = $el.valAttr('format'); 1404 } 1405 else if( conf.dateFormat ) { 1406 dateFormat = conf.dateFormat; 1407 } 1408 1409 return $.formUtils.parseDate(date, dateFormat) !== false; 1410 }, 1411 errorMessage : '', 1412 errorMessageKey: 'badDate' 1413 }); 1414 1415 1416 /* 1417 * Validate group of checkboxes, validate qty required is checked 1418 * written by Steve Wasiura : http://stevewasiura.waztech.com 1419 * element attrs 1420 * data-validation="checkbox_group" 1421 * data-validation-qty="1-2" // min 1 max 2 1422 * data-validation-error-msg="chose min 1, max of 2 checkboxes" 1423 */ 1424 $.formUtils.addValidator({ 1425 name : 'checkbox_group', 1426 validatorFunction : function(val, $el, config, lang, $form) 1427 { // preset return var 1428 var checkResult = true; 1429 // get name of element. since it is a checkbox group, all checkboxes will have same name 1430 var elname = $el.attr('name'); 1431 // get count of checked checkboxes with this name 1432 var checkedCount = $("input[type=checkbox][name^='"+elname+"']:checked", $form).length; 1433 // get el attr that specs qty required / allowed 1434 var qtyAllowed = $el.valAttr('qty'); 1435 if (qtyAllowed == undefined) { 1436 var elementType = $el.get(0).nodeName; 1437 alert('Attribute "data-validation-qty" is missing from '+elementType+' named '+$el.attr('name')); 1438 } 1439 // call Utility function to check if count is above min, below max, within range etc. 1440 var qtyCheckResults = $.formUtils.numericRangeCheck(checkedCount, qtyAllowed) ; 1441 // results will be array, [0]=result str, [1]=qty int 1442 switch(qtyCheckResults[0] ) { 1443 // outside allowed range 1444 case "out": 1445 this.errorMessage = lang.groupCheckedRangeStart + qtyAllowed + lang.groupCheckedEnd; 1446 checkResult = false; 1447 break; 1448 // below min qty 1449 case "min": 1450 this.errorMessage = lang.groupCheckedTooFewStart + qtyCheckResults[1] + lang.groupCheckedEnd; 1451 checkResult = false; 1452 break; 1453 // above max qty 1454 case "max": 1455 this.errorMessage = lang.groupCheckedTooManyStart + qtyCheckResults[1] + lang.groupCheckedEnd; 1456 checkResult = false; 1457 break; 1458 // ok 1459 default: 1460 checkResult = true; 1461 } 1462 1463 return checkResult; 1464 1465 } // remove comma 1466 // errorMessage : '', // set above in switch statement 1467 // errorMessageKey: '' // not used 1468 });
1469 1470})(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.