1(function ($) { 2 'use strict'; 3 var $window = $(window), 4 _applyErrorStyle = function ($elem, conf) { 5 var $parent = $elem.parent(); 6 $elem 7 .addClass(conf.errorElementClass) 8 .removeClass('valid'); 9 if ($parent.hasClass("input-group")) 10 $parent = $parent.parent(); 11 12 $parent.addClass('has-error').removeClass('has-success'); //twitter bs 13 14 15 if (conf.borderColorOnError !== '') { 16 $elem.css('border-color', conf.borderColorOnError); 17 } 18 }, 19 _removeErrorStyle = function ($elem, conf) { 20 $elem.each(function () { 21 _setInlineErrorMessage($(this), '', conf, conf.errorMessagePosition); 22 var $parent = $(this).parent(); 23 24 if ($parent.hasClass("input-group")) 25 $parent = $parent.parent(); 26 $(this) 27 .removeClass('valid') 28 .removeClass(conf.errorElementClass) 29 .css('border-color', ''); 30 $parent 31 .removeClass('has-error') 32 .removeClass('has-success') 33 .find('.' + conf.errorMessageClass) // remove inline error message 34 .remove(); 35 }); 36 }, 37 _setInlineErrorMessage = function ($input, mess, conf, $messageContainer) { 38 var custom = _getInlineErrorElement($input); 39 var $parent = $input.parent(); 40 if ($parent.hasClass("input-group")) 41 $parent = $parent.parent(); 42 if (custom) { 43 custom.innerHTML = mess; 44 } 45 else if (typeof $messageContainer == 'object') { 46 var $found = false; 47 $messageContainer.find('.' + conf.errorMessageClass).each(function () { 48 if (this.inputReferer == $input[0]) { 49 $found = $(this); 50 return false; 51 } 52 }); 53 if ($found) { 54 if (!mess) { 55 $found.remove(); 56 } else { 57 $found.html(mess); 58 } 59 } else { 60 var $mess = $('<div class="' + conf.errorMessageClass + '">' + mess + '</div>'); 61 $mess[0].inputReferer = $input[0]; 62 $messageContainer.prepend($mess); 63 } 64 } 65 else { 66 var $mess = $parent.find('.' + conf.errorMessageClass + '.jsst-help-block'); 67 if ($mess.length == 0) { 68 $mess = $('<span></span>').addClass('jsst-help-block').addClass(conf.errorMessageClass); 69 $mess.appendTo($parent); 70 } 71 $mess.html(mess); 72 } 73 }, 74 _getInlineErrorElement = function ($input, conf) { 75 return document.getElementById($input.attr('name') + '_err_msg'); 76 }, 77 _templateMessage = function ($form, title, errorMessages, conf) { 78 var messages = conf.errorMessageTemplate.messages.replace(/\{errorTitle\}/g, title); 79 var fields = []; 80 $.each(errorMessages, function (i, msg) { 81 fields.push(conf.errorMessageTemplate.field.replace(/\{msg\}/g, msg)); 82 }); 83 messages = messages.replace(/\{fields\}/g, fields.join('')); 84 var container = conf.errorMessageTemplate.container.replace(/\{errorMessageClass\}/g, conf.errorMessageClass); 85 container = container.replace(/\{messages\}/g, messages); 86 $form.children().eq(0).before(container); 87 }; 88 89 /** 90 * Assigns validateInputOnBlur function to elements blur event 91 * 92 * @param {Object} language Optional, will override $.formUtils.LANG 93 * @param {Object} settings Optional, will override the default settings 94 * @return {jQuery} 95 */ 96 $.fn.validateOnBlur = function (language, settings) { 97 this.find('input[data-validation],textarea[data-validation],select[data-validation]') 98 .bind('blur.validation', function () {
99 $(this).validateInputOnBlur(language, settings, true, 'blur'); 100 }); 101 102 return this; 103 }; 104 105 /* 106 * Assigns validateInputOnBlur function to elements custom event 107 * @param {Object} language Optional, will override $.formUtils.LANG 108 * @param {Object} settings Optional, will override the default settings 109 * * @return {jQuery} 110 */ 111 $.fn.validateOnEvent = function (language, settings) { 112 this.find('input[data-validation][data-validation-event],textarea[data-validation][data-validation-event],select[data-validation][data-validation-event]') 113 .each(function () { 114 var $el = $(this), 115 etype = $el.attr("data-validation-event"); 116 if (etype) { 117 $el.bind(etype + ".validation", function () { 118 $(this).validateInputOnBlur(language, settings, true, etype); 119 }); 120 } 121 }); 122 return this; 123 }; 124 125 /** 126 * fade in help message when input gains focus 127 * fade out when input loses focus 128 * <input data-help="The info that I want to display for the user when input is focused" ... /> 129 * 130 * @param {String} attrName - Optional, default is data-help 131 * @return {jQuery} 132 */ 133 $.fn.showHelpOnFocus = function (attrName) { 134 if (!attrName) { 135 attrName = 'data-validation-help'; 136 } 137 138 // Remove previously added event listeners 139 this.find('.has-help-txt') 140 .valAttr('has-keyup-event', false) 141 .removeClass('has-help-txt'); 142 143 // Add help text listeners 144 this.find('textarea,input').each(function () { 145 var $elem = $(this), 146 className = 'jquery_form_help_' + ($elem.attr('name') || '').replace(/(:|\.|\[|\])/g, ""), 147 help = $elem.attr(attrName); 148 149 if (help) { 150 $elem 151 .addClass('has-help-txt') 152 .unbind('focus.help') 153 .bind('focus.help', function () { 154 var $help = $elem.parent().find('.' + className); 155 if ($help.length == 0) { 156 $help = $('<span />') 157 .addClass(className) 158 .addClass('help') 159 .addClass('jsst-help-block') // twitter bs 160 .text(help) 161 .hide(); 162 163 $elem.after($help); 164 165 } 166 $help.fadeIn(); 167 }) 168 .unbind('blur.help') 169 .bind('blur.help', function () { 170 $(this) 171 .parent() 172 .find('.' + className) 173 .fadeOut('slow'); 174 }); 175 } 176 }); 177 178 return this; 179 }; 180 181 /** 182 * Validate single input when it loses focus 183 * shows error message in a span element 184 * that is appended to the parent element 185 * 186 * @param {Object} [language] Optional, will override $.formUtils.LANG 187 * @param {Object} [conf] Optional, will override the default settings 188 * @param {Boolean} attachKeyupEvent Optional 189 * @param {String} eventType 190 * @return {jQuery} 191 */ 192 $.fn.validateInputOnBlur = function (language, conf, attachKeyupEvent, eventType) { 193 $.formUtils.eventType = eventType; 194 195 if ((this.valAttr('suggestion-nr') || this.valAttr('postpone') || this.hasClass('hasDatepicker')) && !window.postponedValidation) { 196 // This validation has to be postponed 197 var _self = this, 198 postponeTime = this.valAttr('postpone') || 200; 199 200 window.postponedValidation = function () { 201 _self.validateInputOnBlur(language, conf, attachKeyupEvent, eventType); 202 window.postponedValidation = false;
203 }; 204 setTimeout(function () { 205 if (window.postponedValidation) { 206 window.postponedValidation(); 207 } 208 }, postponeTime); 209 210 return this; 211 } 212 213 language = $.extend({}, $.formUtils.LANG, language || {}); 214 _removeErrorStyle(this, conf); 215 var $elem = this, 216 $form = $elem.closest("form"), 217 validationRule = $elem.attr(conf.validationRuleAttribute), 218 validation = $.formUtils.validateInput( 219 $elem, 220 language, 221 $.extend({}, conf, {errorMessagePosition: 'element'}), 222 $form, 223 eventType 224 ); 225 226 if (validation === true) { 227 $elem 228 .addClass('valid') 229 .parent() 230 .addClass('has-success'); // twitter bs 231 } else if (validation !== null) { 232 233 _applyErrorStyle($elem, conf); 234 _setInlineErrorMessage($elem, validation, conf, conf.errorMessagePosition); 235 236 if (attachKeyupEvent) { 237 $elem 238 .unbind('keyup.validation') 239 .bind('keyup.validation', function () { 240 $(this).validateInputOnBlur(language, conf, false, 'keyup'); 241 }); 242 } 243 } 244 245 return this; 246 }; 247 248 /** 249 * Short hand for fetching/adding/removing element attributes 250 * prefixed with 'data-validation-' 251 * 252 * @param {String} name 253 * @param {String|Boolean} [val] 254 * @return string|undefined 255 * @protected 256 */ 257 $.fn.valAttr = function (name, val) { 258 if (val === undefined) { 259 return this.attr('data-validation-' + name); 260 } else if (val === false || val === null) { 261 return this.removeAttr('data-validation-' + name); 262 } else { 263 if (name.length > 0) 264 name = '-' + name; 265 return this.attr('data-validation' + name, val); 266 } 267 }; 268 269 /** 270 * Function that validate all inputs in given element 271 * 272 * @param {Object} [language] 273 * @param {Object} [conf] 274 * @param {Boolean} [displayError] Defaults to true 275 */ 276 $.fn.isValid = function (language, conf, displayError) { 277 278 if ($.formUtils.isLoadingModules) { 279 var $self = this; 280 setTimeout(function () { 281 $self.isValid(language, conf, displayError); 282 }, 200); 283 return null; 284 } 285 286 conf = $.extend({}, $.formUtils.defaultConfig(), conf || {}); 287 language = $.extend({}, $.formUtils.LANG, language || {}); 288 displayError = displayError !== false; 289 290 $.formUtils.isValidatingEntireForm = true; 291 $.formUtils.haltValidation = false; 292 293 /** 294 * Adds message to error message stack if not already in the message stack 295 * 296 * @param {String} mess 297 * @para {jQuery} $elem 298 */ 299 var addErrorMessage = function (mess, $elem) { 300 // validate server side will return null as error message before the server is requested 301 if (mess !== null) { 302 if ($.inArray(mess, errorMessages) < 0) { 303 errorMessages.push(mess); 304 } 305 errorInputs.push($elem); 306 $elem.attr('current-error', mess); 307 if (displayError) 308 _applyErrorStyle($elem, conf); 309 } 310 }, 311 /** Error messages for this validation */ 312 errorMessages = [], 313 /** Input elements which value was not valid */ 314 errorInputs = [], 315 /** Form instance */ 316 $form = this, 317 /** 318 * Tells whether or not to validate element with this name and of this type 319 * 320 * @param {String} name 321 * @param {String} type 322 * @return {Boolean} 323 */ 324 ignoreInput = function (name, type) { 325 if (type === 'submit' || type === 'button' || type == 'reset') { 326 return true; 327 } 328 return $.inArray(name, conf.ignore || []) > -1; 329 }; 330 331 // Reset style and remove error class 332 if (displayError) { 333 $form.find('.' + conf.errorMessageClass + '.alert').remove(); 334 _removeErrorStyle($form.find('.' + conf.errorElementClass + ',.valid'), conf); 335 } 336 337 // Validate element values 338 $form.find('input,textarea,select').filter(':not([type="submit"],[type="button"])').each(function () { 339 var $elem = $(this); 340 var elementType = $elem.attr('type'); 341 if (!ignoreInput($elem.attr('name'), elementType)) { 342
343 var validation = $.formUtils.validateInput( 344 $elem, 345 language, 346 conf, 347 $form, 348 'submit' 349 ); 350 351 // Run element validation callback 352 if (typeof conf.onElementValidate == 'function') { 353 conf.onElementValidate((validation === true), $elem, $form, validation); 354 } 355 356 if (validation !== true) { 357 addErrorMessage(validation, $elem); 358 } else { 359 $elem 360 .valAttr('current-error', false) 361 .addClass('valid') 362 .parent() 363 .addClass('has-success'); 364 } 365 } 366 367 }); 368 369 // Run validation callback 370 if (typeof conf.onValidate == 'function') { 371 var errors = conf.onValidate($form); 372 if ($.isArray(errors)) { 373 $.each(errors, function (i, err) { 374 addErrorMessage(err.message, err.element); 375 }); 376 } 377 else if (errors && errors.element && errors.message) { 378 addErrorMessage(errors.message, errors.element); 379 } 380 } 381 382 // Reset form validation flag 383 $.formUtils.isValidatingEntireForm = false; 384 385 // Validation failed 386 if (!$.formUtils.haltValidation && errorInputs.length > 0) { 387 388 if (displayError) { 389 // display all error messages in top of form 390 if (conf.errorMessagePosition === 'top') { 391 _templateMessage($form, language.errorTitle, errorMessages, conf); 392 } 393 // Customize display message 394 else if (conf.errorMessagePosition === 'custom') { 395 if (typeof conf.errorMessageCustom === 'function') { 396 conf.errorMessageCustom($form, language.errorTitle, errorMessages, conf); 397 } 398 } 399 // Display error message below input field or in defined container 400 else { 401 $.each(errorInputs, function (i, $input) { 402 _setInlineErrorMessage($input, $input.attr('current-error'), conf, conf.errorMessagePosition); 403 }); 404 } 405 406 if (conf.scrollToTopOnError) { 407 $window.scrollTop($form.offset().top - 20); 408 } 409 } 410 411 return false; 412 } 413 414 return !$.formUtils.haltValidation; 415 }; 416 417 /** 418 * @deprecated 419 * @param language 420 * @param conf 421 */ 422 $.fn.validateForm = function (language, conf) { 423 if (window.console && typeof window.console.warn == 'function') { 424 window.console.warn('Use of deprecated function $.validateForm, use $.isValid instead'); 425 } 426 return this.isValid(language, conf, true); 427 } 428 429 /** 430 * Plugin for displaying input length restriction 431 */ 432 $.fn.restrictLength = function (maxLengthElement) { 433 new $.formUtils.lengthRestriction(this, maxLengthElement); 434 return this; 435 }; 436 437 /** 438 * Add suggestion dropdown to inputs having data-suggestions with a comma 439 * separated string with suggestions 440 * @param {Array} [settings] 441 * @returns {jQuery} 442 */ 443 $.fn.addSuggestions = function (settings) { 444 var sugs = false; 445 this.find('input').each(function () { 446 var $field = $(this); 447 448 sugs = $.split($field.attr('data-suggestions')); 449 450 if (sugs.length > 0 && !$field.hasClass('has-suggestions')) { 451 $.formUtils.suggest($field, sugs, settings); 452 $field.addClass('has-suggestions'); 453 } 454 }); 455 return this; 456 }; 457 458 /** 459 * A bit smarter split function 460 * @param {String} val 461 * @param {Function|String} [func] 462 * @param {String} [delim] 463 * @returns {Array|void} 464 */ 465 $.split = function (val, func, delim) { 466 if (typeof func != 'function') { 467 // return string 468 if (!val) 469 return []; 470 var values = []; 471 $.each(val.split(func ? func : ','), function (i, str) { 472 str = $.trim(str); 473 if (str.length) 474 values.push(str); 475 }); 476 return values; 477 } else if (val) { 478 // use callback on each 479 if (!delim) 480 delim = ','; 481 $.each(val.split(delim), function (i, str) { 482 str = $.trim(str); 483 if (str.length) 484 return func(str, i); 485 }); 486 } 487 }; 488 489 /** 490 * Short hand function that makes the validation setup require less code 491 * @param conf 492 */ 493 $.validate = function (conf) { 494 495 var defaultConf = $.extend($.formUtils.defaultConfig(), { 496 form: 'form', 497 /* 498 * Enable custom event for validation 499 */ 500 validateOnEvent: true, 501 validateOnBlur: true, 502 showHelpOnFocus: true, 503 addSuggestions: true, 504 modules: '', 505 onModulesLoaded: null, 506 language: false, 507 onSuccess: false, 508 onError: false, 509 onElementValidate: false 510 }); 511 512 conf = $.extend(defaultConf, conf || {}); 513 514 // Add validation to forms 515 $(conf.form).each(function (i, form) { 516 517 var $form = $(form); 518 $window.trigger('formValidationSetup', [$form]); 519 520 // Remove all event listeners previously added 521 $form.find('.has-help-txt') 522 .unbind('focus.validation') 523 .unbind('blur.validation'); 524 $form 525 .removeClass('has-validation-callback') 526 .unbind('submit.validation') 527 .unbind('reset.validation') 528 .find('input[data-validation],textarea[data-validation]') 529 .unbind('blur.validation'); 530 531 // Validate when submitted 532 $form.bind('submit.validation', function () { 533 var $form = $(this); 534 535 if ($.formUtils.isLoadingModules) { 536 setTimeout(function () { 537 $form.trigger('submit.validation'); 538 }, 200); 539 return false;
540 } 541 var valid = $form.isValid(conf.language, conf); 542 if (valid && typeof conf.onSuccess == 'function') { 543 var callbackResponse = conf.onSuccess($form); 544 if (callbackResponse === false) 545 return false; 546 } else if (!valid && typeof conf.onError == 'function') { 547 conf.onError($form); 548 return false; 549 } else { 550 return valid; 551 } 552 }) 553 .bind('reset.validation', function () { 554 // remove messages 555 $(this).find('.' + conf.errorMessageClass + '.alert').remove(); 556 _removeErrorStyle($(this).find('.' + conf.errorElementClass + ',.valid'), conf); 557 }) 558 .addClass('has-validation-callback'); 559 560 if (conf.showHelpOnFocus) { 561 $form.showHelpOnFocus(); 562 } 563 if (conf.addSuggestions) { 564 $form.addSuggestions(); 565 } 566 if (conf.validateOnBlur) { 567 $form.validateOnBlur(conf.language, conf); 568 $form.bind('html5ValidationAttrsFound', function () { 569 $form.validateOnBlur(conf.language, conf); 570 }) 571 } 572 if (conf.validateOnEvent) { 573 $form.validateOnEvent(conf.language, conf); 574 } 575 576 }); 577 578 if (conf.modules != '') { 579 if (typeof conf.onModulesLoaded == 'function') { 580 $window.one('validatorsLoaded', conf.onModulesLoaded); 581 }
582 $.formUtils.loadModules(conf.modules); 583 } 584 }; 585 586 /** 587 * Object containing utility methods for this plugin 588 */ 589 $.formUtils = { 590 /** 591 * Default config for $(...).isValid(); 592 */ 593 defaultConfig: function () { 594 return { 595 ignore: [], // Names of inputs not to be validated even though node attribute containing the validation rules tells us to 596 errorElementClass: 'error', // Class that will be put on elements which value is invalid 597 borderColorOnError: 'red', // Border color of elements which value is invalid, empty string to not change border color 598 errorMessageClass: 'form-error', // class name of div containing error messages when validation fails 599 validationRuleAttribute: 'data-validation', // name of the attribute holding the validation rules 600 validationErrorMsgAttribute: 'data-validation-error-msg', // define custom err msg inline with element 601 errorMessagePosition: 'element', // Can be either "top" or "element" or "custom" 602 errorMessageTemplate: { 603 container: '<div class="{errorMessageClass} alert alert-danger">{messages}</div>', 604 messages: '<strong>{errorTitle}</strong><ul>{fields}</ul>', 605 field: '<li>{msg}</li>' 606 }, 607 errorMessageCustom: _templateMessage, 608 scrollToTopOnError: true, 609 dateFormat: 'yyyy-mm-dd', 610 addValidClassOnAll: false, // whether or not to apply class="valid" even if the input wasn't validated 611 decimalSeparator: '.' 612 } 613 }, 614 /** 615 * Available validators 616 */ 617 validators: {}, 618 /** 619 * Events triggered by form validator 620 */ 621 _events: {load: [], valid: [], invalid: []}, 622 /** 623 * Setting this property to true during validation will 624 * stop further validation from taking place and form will 625 * not be sent 626 */ 627 haltValidation: false, 628 /** 629 * This variable will be true $.fn.isValid() is called 630 * and false when $.fn.validateOnBlur is called 631 */ 632 isValidatingEntireForm: false, 633 /** 634 * Function for adding a validator 635 * @param {Object} validator 636 */ 637 addValidator: function (validator) { 638 // prefix with "validate_" for backward compatibility reasons 639 var name = validator.name.indexOf('validate_') === 0 ? validator.name : 'validate_' + validator.name; 640 if (validator.validateOnKeyUp === undefined) 641 validator.validateOnKeyUp = true; 642 this.validators[name] = validator; 643 }, 644 /** 645 * @var {Boolean} 646 */ 647 isLoadingModules: false, 648 /** 649 * @var {Object} 650 */ 651 loadedModules: {}, 652 /** 653 * @example
654 * $.formUtils.loadModules('date, security.dev'); 655 * 656 * Will load the scripts date.js and security.dev.js from the 657 * directory where this script resides. If you want to load 658 * the modules from another directory you can use the 659 * path argument. 660 * 661 * The script will be cached by the browser unless the module 662 * name ends with .dev 663 * 664 * @param {String} modules - Comma separated string with module file names (no directory nor file extension) 665 * @param {String} [path] - Optional, path where the module files is located if their not in the same directory as the core modules 666 * @param {Boolean} [fireEvent] - Optional, whether or not to fire event 'load' when modules finished loading 667 */ 668 loadModules: function (modules, path, fireEvent) { 669 670 if (fireEvent === undefined) 671 fireEvent = true; 672 673 if ($.formUtils.isLoadingModules) { 674 setTimeout(function () {
675 $.formUtils.loadModules(modules, path, fireEvent); 676 }); 677 return; 678 } 679 680 var hasLoadedAnyModule = false, 681 loadModuleScripts = function (modules, path) { 682 683 var moduleList = $.split(modules), 684 numModules = moduleList.length, 685 moduleLoadedCallback = function () { 686 numModules--; 687 if (numModules == 0) { 688 $.formUtils.isLoadingModules = false; 689 if (fireEvent && hasLoadedAnyModule) { 690 $window.trigger('validatorsLoaded'); 691 } 692 } 693 }; 694 695 if (numModules > 0) { 696 $.formUtils.isLoadingModules = true; 697 } 698 699 var cacheSuffix = '?__=' + (new Date().getTime()), 700 appendToElement = document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]; 701 702 $.each(moduleList, function (i, modName) { 703 modName = $.trim(modName); 704 if (modName.length == 0) { 705 moduleLoadedCallback(); 706 } 707 else { 708 var scriptUrl = path + modName + (modName.substr(-3) == '.js' ? '' : '.js'), 709 script = document.createElement('SCRIPT'); 710 711 if (scriptUrl in $.formUtils.loadedModules) { 712 // already loaded 713 moduleLoadedCallback(); 714 } 715 else { 716 717 // Remember that this script is loaded 718 $.formUtils.loadedModules[scriptUrl] = 1; 719 hasLoadedAnyModule = true; 720 721 // Load the script 722 script.type = 'text/javascript'; 723 script.onload = moduleLoadedCallback; 724 script.src = scriptUrl + (scriptUrl.substr(-7) == '.dev.js' ? cacheSuffix : ''); 725 script.onreadystatechange = function () { 726 // IE 7 fix 727 if (this.readyState == 'complete' || this.readyState == 'loaded') { 728 moduleLoadedCallback(); 729 // Handle memory leak in IE 730 this.onload = null; 731 this.onreadystatechange = null; 732 } 733 }; 734 appendToElement.appendChild(script); 735 } 736 } 737 }); 738 }; 739 740 if (path) { 741 loadModuleScripts(modules, path); 742 } else { 743 var findScriptPathAndLoadModules = function () { 744 var foundPath = false; 745 $('script').each(function () { 746 if (this.src) { 747 var scriptName = this.src.substr(this.src.lastIndexOf('/') + 1, this.src.length); 748 if (scriptName.indexOf('jquery.form-validator.js') > -1 || scriptName.indexOf('jquery.form-validator.min.js') > -1) { 749 foundPath = this.src.substr(0, this.src.lastIndexOf('/')) + '/'; 750 if (foundPath == '/') 751 foundPath = ''; 752 return false;
753 } 754 } 755 }); 756 757 if (foundPath !== false) { 758 loadModuleScripts(modules, foundPath); 759 return true; 760 } 761 return false; 762 }; 763 764 if (!findScriptPathAndLoadModules()) { 765 $(findScriptPathAndLoadModules); 766 } 767 } 768 }, 769 /** 770 * Validate the value of given element according to the validation rules 771 * found in the attribute data-validation. Will return null if no validation 772 * should take place, returns true if valid or error message if not valid 773 * 774 * @param {jQuery} $elem 775 * @param {Object} language ($.formUtils.LANG) 776 * @param {Object} conf 777 * @param {jQuery} $form 778 * @param {String} [eventContext] 779 * @return {String|Boolean} 780 */ 781 validateInput: function ($elem, language, conf, $form, eventContext) { 782 783 if ($elem.attr('disabled')) 784 return null; // returning null will prevent that the valid class gets applied to the element 785 786 $elem.trigger('beforeValidation'); 787 788 var value = $elem.val() || '', 789 optional = $elem.valAttr('optional'), 790 // test if a checkbox forces this element to be validated 791 validationDependsOnCheckedInput = false, 792 validationDependentInputIsChecked = false, 793 validateIfCheckedElement = false, 794 // get value of this element's attribute "... if-checked" 795 validateIfCheckedElementName = $elem.valAttr("if-checked"); 796 797 // make sure we can proceed 798 if (validateIfCheckedElementName != null) { 799 800 // Set the boolean telling us that the validation depends 801 // on another input being checked 802 validationDependsOnCheckedInput = true; 803 804 // select the checkbox type element in this form 805 validateIfCheckedElement = $form.find('input[name="' + validateIfCheckedElementName + '"]'); 806 807 // test if it's property "checked" is checked 808 if (validateIfCheckedElement.prop('checked')) { 809 // set value for validation checkpoint 810 validationDependentInputIsChecked = true; 811 } 812 } 813 814 // validation checkpoint 815 // if empty AND optional attribute is present 816 // OR depending on a checkbox being checked AND checkbox is checked, return true 817 if ((!value && optional === 'true') || (validationDependsOnCheckedInput && !validationDependentInputIsChecked)) { 818 return conf.addValidClassOnAll ? true : null; 819 } 820 821 var validationRules = $elem.attr(conf.validationRuleAttribute), 822 // see if form element has inline err msg attribute 823 validationErrorMsg = true; 824 825 if (!validationRules) { 826 return conf.addValidClassOnAll ? true : null; 827 } 828 829 $.split(validationRules, function (rule) { 830 if (rule.indexOf('validate_') !== 0) { 831 rule = 'validate_' + rule; 832 } 833 834 var validator = $.formUtils.validators[rule]; 835 836 if (validator && typeof validator['validatorFunction'] == 'function') { 837 // special change of element for checkbox_group rule 838 if (rule == 'validate_checkbox_group') { 839 // set element to first in group, so error msg is set only once 840 $elem = $("[name='" + $elem.attr('name') + "']:eq(0)"); 841 } 842 843 var isValid = null; 844 if (eventContext != 'keyup' || validator.validateOnKeyUp) { 845 isValid = validator.validatorFunction(value, $elem, conf, language, $form); 846 } 847 848 if (!isValid) { 849 validationErrorMsg = null; 850 if (isValid !== null) { 851 validationErrorMsg = $elem.attr(conf.validationErrorMsgAttribute + '-' + rule.replace('validate_', '')); 852 if (!validationErrorMsg) { 853 validationErrorMsg = $elem.attr(conf.validationErrorMsgAttribute); 854 if (!validationErrorMsg) { 855 validationErrorMsg = language[validator.errorMessageKey]; 856 if (!validationErrorMsg) 857 validationErrorMsg = validator.errorMessage; 858 } 859 } 860 } 861 return false; // breaks the iteration 862 } 863 864 } else { 865 throw new Error('Using undefined validator "' + rule + '"'); 866 } 867 868 }, ' '); 869 870 if (typeof validationErrorMsg == 'string') { 871 $elem.trigger('validation', false); 872 return validationErrorMsg; 873 } else if (validationErrorMsg === null && !conf.addValidClassOnAll) { 874 return null; 875 } else { 876 $elem.trigger('validation', true); 877 return true; 878 } 879 }, 880 /** 881 * Is it a correct date according to given dateFormat. Will return false if not, otherwise 882 * an array 0=>year 1=>month 2=>day 883 * 884 * @param {String} val 885 * @param {String} dateFormat 886 * @return {Array}|{Boolean} 887 */ 888 parseDate: function (val, dateFormat) { 889 var divider = dateFormat.replace(/[a-zA-Z]/gi, '').substring(0, 1), 890 regexp = '^', 891 formatParts = dateFormat.split(divider), 892 matches, day, month, year; 893 894 $.each(formatParts, function (i, part) { 895 regexp += (i > 0 ? '\\' + divider : '') + '(\\d{' + part.length + '})'; 896 }); 897 898 regexp += '$'; 899 900 matches = val.match(new RegExp(regexp)); 901 if (matches === null) { 902 return false;
903 } 904 905 var findDateUnit = function (unit, formatParts, matches) { 906 for (var i = 0; i < formatParts.length; i++) { 907 if (formatParts[i].substring(0, 1) === unit) { 908 return $.formUtils.parseDateInt(matches[i + 1]); 909 } 910 } 911 return -1; 912 }; 913 914 month = findDateUnit('m', formatParts, matches); 915 day = findDateUnit('d', formatParts, matches); 916 year = findDateUnit('y', formatParts, matches); 917 918 if ((month === 2 && day > 28 && (year % 4 !== 0 || year % 100 === 0 && year % 400 !== 0)) 919 || (month === 2 && day > 29 && (year % 4 === 0 || year % 100 !== 0 && year % 400 === 0)) 920 || month > 12 || month === 0) { 921 return false; 922 } 923 if ((this.isShortMonth(month) && day > 30) || (!this.isShortMonth(month) && day > 31) || day === 0) { 924 return false; 925 } 926 927 return [year, month, day]; 928 }, 929 /** 930 * skum fix. är talet 05 eller lägre ger parseInt rätt int annars får man 0 när man kör parseInt? 931 * 932 * @param {String} val 933 * @param {Number} 934 */ 935 parseDateInt: function (val) { 936 if (val.indexOf('0') === 0) { 937 val = val.replace('0', ''); 938 } 939 return parseInt(val, 10); 940 }, 941 /** 942 * Has month only 30 days? 943 * 944 * @param {Number} m 945 * @return {Boolean} 946 */ 947 isShortMonth: function (m) { 948 return (m % 2 === 0 && m < 7) || (m % 2 !== 0 && m > 7); 949 }, 950 /** 951 * Restrict input length 952 * 953 * @param {jQuery} $inputElement Jquery Html object 954 * @param {jQuery} $maxLengthElement jQuery Html Object 955 * @return void 956 */ 957 lengthRestriction: function ($inputElement, $maxLengthElement) { 958 // read maxChars from counter display initial text value 959 var maxChars = parseInt($maxLengthElement.text(), 10), 960 charsLeft = 0, 961 // internal function does the counting and sets display value 962 countCharacters = function () { 963 var numChars = $inputElement.val().length; 964 if (numChars > maxChars) { 965 // get current scroll bar position 966 var currScrollTopPos = $inputElement.scrollTop(); 967 // trim value to max length 968 $inputElement.val($inputElement.val().substring(0, maxChars)); 969 $inputElement.scrollTop(currScrollTopPos); 970 } 971 charsLeft = maxChars - numChars; 972 if (charsLeft < 0) 973 charsLeft = 0; 974 975 // set counter text 976 $maxLengthElement.text(charsLeft); 977 }; 978 979 // bind events to this element 980 // setTimeout is needed, cut or paste fires before val is available 981 $($inputElement).bind('keydown keyup keypress focus blur', countCharacters) 982 .bind('cut paste', function () { 983 setTimeout(countCharacters, 100); 984 }); 985 986 // count chars on pageload, if there are prefilled input-values 987 $(document).bind("ready", countCharacters); 988 }, 989 /** 990 * Test numeric against allowed range 991 * 992 * @param $value int 993 * @param $rangeAllowed str; (1-2, min1, max2) 994 * @return array 995 */ 996 numericRangeCheck: function (value, rangeAllowed) 997 { 998 // split by dash 999 var range = $.split(rangeAllowed, '-'); 1000 // min or max 1001 var minmax = parseInt(rangeAllowed.substr(3), 10) 1002 // range ? 1003 if (range.length == 2 && (value < parseInt(range[0], 10) || value > parseInt(range[1], 10))) 1004 { 1005 return ["out", range[0], range[1]]; 1006 } // value is out of range 1007 else if (rangeAllowed.indexOf('min') === 0 && (value < minmax)) // min 1008 { 1009 return ["min", minmax]; 1010 } // value is below min 1011 else if (rangeAllowed.indexOf('max') === 0 && (value > minmax)) // max 1012 { 1013 return ["max", minmax]; 1014 } // value is above max 1015 // since no other returns executed, value is in allowed range 1016 return ["ok"]; 1017 }, 1018 _numSuggestionElements: 0, 1019 _selectedSuggestion: null, 1020 _previousTypedVal: null, 1021 /** 1022 * Utility function that can be used to create plugins that gives
1023 * suggestions when inputs is typed into 1024 * @param {jQuery} $elem 1025 * @param {Array} suggestions 1026 * @param {Object} settings - Optional 1027 * @return {jQuery} 1028 */ 1029 suggest: function ($elem, suggestions, settings) { 1030 var conf = { 1031 css: { 1032 maxHeight: '150px', 1033 background: '#FFF', 1034 lineHeight: '150%', 1035 textDecoration: 'underline', 1036 overflowX: 'hidden', 1037 overflowY: 'auto', 1038 border: '#CCC solid 1px', 1039 borderTop: 'none', 1040 cursor: 'pointer' 1041 }, 1042 activeSuggestionCSS: { 1043 background: '#E9E9E9' 1044 } 1045 }, 1046 setSuggsetionPosition = function ($suggestionContainer, $input) { 1047 var offset = $input.offset(); 1048 $suggestionContainer.css({ 1049 width: $input.outerWidth(), 1050 left: offset.left + 'px', 1051 top: (offset.top + $input.outerHeight()) + 'px' 1052 }); 1053 }; 1054 1055 if (settings) 1056 $.extend(conf, settings); 1057 1058 conf.css['position'] = 'absolute'; 1059 conf.css['z-index'] = 9999; 1060 $elem.attr('autocomplete', 'off'); 1061 1062 if (this._numSuggestionElements === 0) { 1063 // Re-position suggestion container if window size changes 1064 $window.bind('resize', function () { 1065 $('.jquery-form-suggestions').each(function () { 1066 var $container = $(this), 1067 suggestID = $container.attr('data-suggest-container'); 1068 setSuggsetionPosition($container, $('.suggestions-' + suggestID).eq(0)); 1069 }); 1070 }); 1071 } 1072 1073 this._numSuggestionElements++; 1074 1075 var onSelectSuggestion = function ($el) { 1076 var suggestionId = $el.valAttr('suggestion-nr'); 1077 $.formUtils._selectedSuggestion = null; 1078 $.formUtils._previousTypedVal = null; 1079 $('.jquery-form-suggestion-' + suggestionId).fadeOut('fast'); 1080 }; 1081 1082 $elem 1083 .data('suggestions', suggestions) 1084 .valAttr('suggestion-nr', this._numSuggestionElements) 1085 .unbind('focus.suggest') 1086 .bind('focus.suggest', function () { 1087 $(this).trigger('keyup'); 1088 $.formUtils._selectedSuggestion = null; 1089 }) 1090 .unbind('keyup.suggest') 1091 .bind('keyup.suggest', function () { 1092 var $input = $(this), 1093 foundSuggestions = [], 1094 val = $.trim($input.val()).toLocaleLowerCase(); 1095 1096 if (val == $.formUtils._previousTypedVal) { 1097 return; 1098 } 1099 else { 1100 $.formUtils._previousTypedVal = val; 1101 } 1102 1103 var hasTypedSuggestion = false, 1104 suggestionId = $input.valAttr('suggestion-nr'), 1105 $suggestionContainer = $('.jquery-form-suggestion-' + suggestionId); 1106 1107 $suggestionContainer.scrollTop(0); 1108 1109 // Find the right suggestions 1110 if (val != '') { 1111 var findPartial = val.length > 2; 1112 $.each($input.data('suggestions'), function (i, suggestion) { 1113 var lowerCaseVal = suggestion.toLocaleLowerCase(); 1114 if (lowerCaseVal == val) { 1115 foundSuggestions.push('<strong>' + suggestion + '</strong>'); 1116 hasTypedSuggestion = true; 1117 return false;
1118 } else if (lowerCaseVal.indexOf(val) === 0 || (findPartial && lowerCaseVal.indexOf(val) > -1)) { 1119 foundSuggestions.push(suggestion.replace(new RegExp(val, 'gi'), '<strong>$&</strong>')); 1120 } 1121 }); 1122 } 1123 1124 // Hide suggestion container 1125 if (hasTypedSuggestion || (foundSuggestions.length == 0 && $suggestionContainer.length > 0)) { 1126 $suggestionContainer.hide(); 1127 } 1128 1129 // Create suggestion container if not already exists 1130 else if (foundSuggestions.length > 0 && $suggestionContainer.length == 0) { 1131 $suggestionContainer = $('<div></div>').css(conf.css).appendTo('body'); 1132 $elem.addClass('suggestions-' + suggestionId); 1133 $suggestionContainer 1134 .attr('data-suggest-container', suggestionId) 1135 .addClass('jquery-form-suggestions') 1136 .addClass('jquery-form-suggestion-' + suggestionId); 1137 } 1138 1139 // Show hidden container 1140 else if (foundSuggestions.length > 0 && !$suggestionContainer.is(':visible')) { 1141 $suggestionContainer.show(); 1142 } 1143 1144 // add suggestions 1145 if (foundSuggestions.length > 0 && val.length != foundSuggestions[0].length) { 1146 1147 // put container in place every time, just in case 1148 setSuggsetionPosition($suggestionContainer, $input); 1149 1150 // Add suggestions HTML to container 1151 $suggestionContainer.html(''); 1152 $.each(foundSuggestions, function (i, text) { 1153 $('<div></div>') 1154 .append(text) 1155 .css({ 1156 overflow: 'hidden', 1157 textOverflow: 'ellipsis', 1158 whiteSpace: 'nowrap', 1159 padding: '5px' 1160 }) 1161 .addClass('form-suggest-element') 1162 .appendTo($suggestionContainer) 1163 .click(function () { 1164 $input.focus(); 1165 $input.val($(this).text()); 1166 onSelectSuggestion($input); 1167 }); 1168 }); 1169 } 1170 }) 1171 .unbind('keydown.validation') 1172 .bind('keydown.validation', function (e) { 1173 var code = (e.keyCode ? e.keyCode : e.which), 1174 suggestionId, 1175 $suggestionContainer, 1176 $input = $(this); 1177 1178 if (code == 13 && $.formUtils._selectedSuggestion !== null) { 1179 suggestionId = $input.valAttr('suggestion-nr'); 1180 $suggestionContainer = $('.jquery-form-suggestion-' + suggestionId); 1181 if ($suggestionContainer.length > 0) { 1182 var newText = $suggestionContainer.find('div').eq($.formUtils._selectedSuggestion).text(); 1183 $input.val(newText); 1184 onSelectSuggestion($input); 1185 e.preventDefault(); 1186 } 1187 } 1188 else { 1189 suggestionId = $input.valAttr('suggestion-nr'); 1190 $suggestionContainer = $('.jquery-form-suggestion-' + suggestionId); 1191 var $suggestions = $suggestionContainer.children(); 1192 if ($suggestions.length > 0 && $.inArray(code, [38, 40]) > -1) { 1193 if (code == 38) { // key up 1194 if ($.formUtils._selectedSuggestion === null) 1195 $.formUtils._selectedSuggestion = $suggestions.length - 1; 1196 else 1197 $.formUtils._selectedSuggestion--; 1198 if ($.formUtils._selectedSuggestion < 0) 1199 $.formUtils._selectedSuggestion = $suggestions.length - 1; 1200 } 1201 else if (code == 40) { // key down 1202 if ($.formUtils._selectedSuggestion === null) 1203 $.formUtils._selectedSuggestion = 0; 1204 else 1205 $.formUtils._selectedSuggestion++; 1206 if ($.formUtils._selectedSuggestion > ($suggestions.length - 1)) 1207 $.formUtils._selectedSuggestion = 0; 1208 1209 } 1210 1211 // Scroll in suggestion window 1212 var containerInnerHeight = $suggestionContainer.innerHe
1212ight(), 1213 containerScrollTop = $suggestionContainer.scrollTop(), 1214 suggestionHeight = $suggestionContainer.children().eq(0).outerHeight(), 1215 activeSuggestionPosY = suggestionHeight * ($.formUtils._selectedSuggestion); 1216 1217 if (activeSuggestionPosY < containerScrollTop || activeSuggestionPosY > (containerScrollTop + containerInnerHeight)) { 1218 $suggestionContainer.scrollTop(activeSuggestionPosY); 1219 } 1220 1221 $suggestions 1222 .removeClass('active-suggestion') 1223 .css('background', 'none') 1224 .eq($.formUtils._selectedSuggestion) 1225 .addClass('active-suggestion') 1226 .css(conf.activeSuggestionCSS); 1227 1228 e.preventDefault(); 1229 return false; 1230 } 1231 } 1232 }) 1233 .unbind('blur.suggest') 1234 .bind('blur.suggest', function () { 1235 onSelectSuggestion($(this)); 1236 }); 1237 1238 return $elem; 1239 }, 1240 /** 1241 * Error dialogs 1242 * 1243 * @var {Object} 1244 */ 1245 LANG: { 1246 errorTitle: 'Form submission failed!', 1247 requiredFields: 'You have not answered all required fields', 1248 badTime: 'You have not given a correct time', 1249 badEmail: 'You have not given a correct e-mail address', 1250 badTelephone: 'You have not given a correct phone number', 1251 badSecurityAnswer: 'You have not given a correct answer to the security question', 1252 badDate: 'You have not given a correct date', 1253 lengthBadStart: 'The input value must be between ', 1254 lengthBadEnd: ' characters', 1255 lengthTooLongStart: 'The input value is longer than ', 1256 lengthTooShortStart: 'The input value is shorter than ', 1257 notConfirmed: 'Input values could not be confirmed', 1258 badDomain: 'Incorrect domain value', 1259 badUrl: 'The input value is not a correct URL', 1260 badCustomVal: 'The input value is incorrect', 1261 badInt: 'The input value was not a correct number', 1262 badSecurityNumber: 'Your social security number was incorrect', 1263 badUKVatAnswer: 'Incorrect UK VAT Number', 1264 badStrength: 'The password isn\'t strong enough', 1265 badNumberOfSelectedOptionsStart: 'You have to choose at least ', 1266 badNumberOfSelectedOptionsEnd: ' answers', 1267 badAlphaNumeric: 'The input value can only contain alphanumeric characters ', 1268 badAlphaNumericExtra: ' and ', 1269 wrongFileSize: 'The file you are trying to upload is too large', 1270 wrongFileType: 'The file you are trying to upload is of the wrong type', 1271 groupCheckedRangeStart: 'Please choose between ', 1272 groupCheckedTooFewStart: 'Please choose at least ', 1273 groupCheckedTooManyStart: 'Please choose a maximum of ', 1274 groupCheckedEnd: ' item(s)', 1275 badCreditCard: 'The credit card number is not correct', 1276 badCVV: 'The CVV number was not correct' 1277 } 1278 }; 1279 1280 1281 /* * * * * * * * * * * * * * * * * * * * * * 1282 CORE VALIDATORS 1283 * * * * * * * * * * * * * * * * * * * * */ 1284 1285 1286 /* 1287 * Validate email 1288 */ 1289 $.formUtils.addValidator({ 1290 name: 'email', 1291 validatorFunction: function (email) { 1292 1293 var emailParts = email.toLowerCase().split('@'); 1294 if (emailParts.length == 2) { 1295 var regex = /^[a-zA-Z0-9.'!#$%&âÃô*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/; 1296 return regex.test(email) 1297 1298 return $.formUtils.validators.validate_domain.validatorFunction(emailParts[1]) && 1299 !(/[^\w\+\.\-]/.test(emailParts[0])); 1300 } 1301 1302 return false;
1303 }, 1304 errorMessage: '', 1305 errorMessageKey: 'badEmail' 1306 }); 1307 1308 /* 1309 * Validate domain name 1310 */ 1311 $.formUtils.addValidator({ 1312 name: 'domain', 1313 validatorFunction: function (val, $input) { 1314 1315 var topDomains = ['.ac', '.ad', '.ae', '.aero', '.af', '.ag', '.ai', '.al', '.am', '.an', '.ao', 1316 '.aq', '.ar', '.arpa', '.as', '.asia', '.at', '.au', '.aw', '.ax', '.az', '.ba', '.bb', 1317 '.bd', '.be', '.bf', '.bg', '.bh', '.bi', '.bike', '.biz', '.bj', '.bm', '.bn', '.bo', 1318 '.br', '.bs', '.bt', '.bv', '.bw', '.by', '.bz', '.ca', '.camera', '.cat', '.cc', '.cd', 1319 '.cf', '.cg', '.ch', '.ci', '.ck', '.cl', '.clothing', '.cm', '.cn', '.co', '.com', 1320 '.construction', '.contractors', '.coop', '.cr', '.cu', '.cv', '.cw', '.cx', '.cy', '.cz', 1321 '.de', '.diamonds', '.directory', '.dj', '.dk', '.dm', '.do', '.dz', '.ec', '.edu', '.ee', 1322 '.eg', '.enterprises', '.equipment', '.er', '.es', '.estate', '.et', '.eu', '.fi', '.fj', 1323 '.fk', '.fm', '.fo', '.fr', '.ga', '.gallery', '.gb', '.gd', '.ge', '.gf', '.gg', '.gh', 1324 '.gi', '.gl', '.gm', '.gn', '.gov', '.gp', '.gq', '.gr', '.graphics', '.gs', '.gt', '.gu', 1325 '.guru', '.gw', '.gy', '.hk', '.hm', '.hn', '.holdings', '.hr', '.ht', '.hu', '.id', '.ie', 1326 '.il', '.im', '.in', '.info', '.int', '.io', '.iq', '.ir', '.is', '.it', '.je', '.jm', '.jo', 1327 '.jobs', '.jp', '.ke', '.kg', '.kh', '.ki', '.kitchen', '.km', '.kn', '.kp', '.kr', '.kw', 1328 '.ky', '.kz', '.la', '.land', '.lb', '.lc', '.li', '.lighting', '.lk', '.lr', '.ls', '.lt', 1329 '.lu', '.lv', '.ly', '.ma', '.mc', '.md', '.me', '.menu', '.mg', '.mh', '.mil', '.mk', '.ml', 1330 '.mm', '.mn', '.mo', '.mobi', '.mp', '.mq', '.mr', '.ms', '.mt', '.mu', '.museum', '.mv', 1331 '.mw', '.mx', '.my', '.mz', '.na', '.name', '.nc', '.ne', '.net', '.nf', '.ng', '.ni', 1332 '.nl', '.no', '.np', '.nr', '.nu', '.nz', '.om', '.org', '.pa', '.pe', '.pf', '.pg', '.ph', 1333 '.photography', '.pk', '.pl', '.plumbing', '.pm', '.pn', '.post', '.pr', '.pro', '.ps', '.pt', 1334 '.pw', '.py', '.qa', '.re', '.ro', '.rs', '.ru', '.rw', '.sa', '.sb', '.sc', '.sd', '.se', 1335 '.sexy', '.sg', '.sh', '.si', '.singles', '.sj', '.sk', '.sl', '.sm', '.sn', '.so', '.sr', 1336 '.st', '.su', '.sv', '.sx', '.sy', '.sz', '.tattoo', '.tc', '.td', '.technology', '.tel', '.tf', 1337 '.tg', '.th', '.tips', '.tj', '.tk', '.tl', '.tm', '.tn', '.to', '.today', '.tp', '.tr', '.travel', 1338 '.tt', '.tv', '.tw', '.tz', '.ua', '.ug', '.uk', '.uno', '.us', '.uy', '.uz', '.va', '.vc', '.ve', 1339 '.ventures', '.vg', '.vi', '.vn', '.voyage', '.vu', '.wf', '.ws', '.xn--3e0b707e', '.xn--45brj9c', 1340 '.xn--80ao21a', '.xn--80asehdb', '.xn--80aswg', '.xn--90a3ac', '.xn--clchc0ea0b2g2a9gcd', '.xn--fiqs8s', 1341 '.xn--fiqz9s', '.xn--fpcrj9c3d', '.xn--fzc2c9e2c', '.xn--gecrj9c', '.xn--h2brj9c', '.xn--j1amh', 1342 '.xn--j6w193g', '.xn--kprw13d', '.xn--kpry57d', '.xn--l1acc', '.xn--lgbbat1ad8j', '.xn--mgb9awbf', 1343 '.xn--mgba3a4f16a', '.xn--mgbaam7a8h', '.xn--mgbayh7gpa', '.xn--mgbbh1a71e', '.xn--mgbc0a9azcg', 1344 '.xn--mgberp4a5d4ar', '.xn--mgbx4cd0ab', '.xn--ngbc5azd', '.xn--o3cw4h', '.xn--ogbpf8fl', '.xn--p1ai', 1345 '.xn--pgbs0dh', '.xn--q9jyb4c', '.xn--s9brj9c', '.xn--unup4y', '.xn--wgbh1c', '.xn--wgbl6a', 1346 '.xn--xkc2al3hye2a', '.xn--xkc2dl3a5ee0h', '.xn--yfro4i67o', '.xn--ygbi2ammx', '.xxx', '.ye', 1347 '.yt', '.za', '.zm', '.zw'], 1348 ukTopDomains = ['co', 'me', 'ac', 'gov', 'judiciary', 'ltd', 'mod', 'net', 'nhs', 'nic', 1349 'org', 'parliament', 'plc', 'police', 'sch', 'bl', 'british-library', 'jet', 'nls'], 1350 dot = val.lastIndexOf('.'), 1351 domain = val.substring(0, dot), 1352 ext = val.substring(dot, val.length), 1353 hasTopDomain = false;
1354 1355 for (var i = 0; i < topDomains.length; i++) { 1356 if (topDomains[i] === ext) { 1357 if (ext === '.uk') { 1358 //Run Extra Checks for UK Domain Names 1359 var domainParts = val.split('.'); 1360 var tld2 = domainParts[domainParts.length - 2]; 1361 for (var j = 0; j < ukTopDomains.length; j++) { 1362 if (ukTopDomains[j] === tld2) { 1363 hasTopDomain = true; 1364 break; 1365 } 1366 } 1367 1368 if (hasTopDomain) 1369 break; 1370 1371 } else { 1372 hasTopDomain = true; 1373 break; 1374 } 1375 } 1376 } 1377 1378 if (!hasTopDomain) { 1379 return false; 1380 } else if (dot < 2 || dot > 57) { 1381 return $.inArray(val, ['i.net', 'q.com', 'q.net', 'x.com', 'x.org', 'z.com', 'w.org']) > -1; 1382 } else { 1383 var firstChar = domain.substring(0, 1), 1384 lastChar = domain.substring(domain.length - 1, domain.length); 1385 1386 if (firstChar === '-' || firstChar === '.' || lastChar === '-' || lastChar === '.') { 1387 return false; 1388 } 1389 if (domain.split('..').length > 1) { 1390 return false; 1391 } 1392 if (domain.replace(/[-\da-z\.]/g, '') !== '') { 1393 return false; 1394 } 1395 } 1396 1397 if (typeof $input !== 'undefined') { 1398 $input.val(val); 1399 } 1400 1401 return true; 1402 }, 1403 errorMessage: '', 1404 errorMessageKey: 'badDomain' 1405 }); 1406 1407 /* 1408 * Validate required 1409 */ 1410 $.formUtils.addValidator({ 1411 name: 'required', 1412 validatorFunction: function (val, $el, config, language, $form) { 1413 switch ($el.attr('type')) { 1414 case 'checkbox': 1415 return $el.is(':checked'); 1416 case 'radio': 1417 return $form.find('input[name="' + $el.attr('name') + '"]').filter(':checked').length > 0; 1418 default: 1419 return $.trim(val) !== ''; 1420 } 1421 }, 1422 errorMessage: '', 1423 errorMessageKey: 'requiredFields' 1424 }); 1425 1426 /* 1427 * Validate length range 1428 */ 1429 $.formUtils.addValidator({ 1430 name: 'length', 1431 validatorFunction: function (val, $el, conf, lang) { 1432 var lengthAllowed = $el.valAttr('length'), 1433 type = $el.attr('type'); 1434 1435 if (lengthAllowed == undefined) { 1436 var elementType = $el.get(0).nodeName; 1437 alert('Please add attribute "data-validation-length" to ' + elementType + ' named ' + $el.attr('name')); 1438 return true; 1439 } 1440 1441 // check if length is above min, below max or within range. 1442 var len = type == 'file' && $el.get(0).files !== undefined ? $el.get(0).files.length : val.length, 1443 lengthCheckResults = $.formUtils.numericRangeCheck(len, lengthAllowed), 1444 checkResult; 1445 1446 switch (lengthCheckResults[0]) 1447 { // outside of allowed range 1448 case "out": 1449 this.errorMessage = lang.lengthBadStart + lengthAllowed + lang.lengthBadEnd; 1450 checkResult = false;
1451 break; 1452 // too short 1453 case "min": 1454 this.errorMessage = lang.lengthTooShortStart + lengthCheckResults[1] + lang.lengthBadEnd; 1455 checkResult = false; 1456 break; 1457 // too long 1458 case "max": 1459 this.errorMessage = lang.lengthTooLongStart + lengthCheckResults[1] + lang.lengthBadEnd; 1460 checkResult = false; 1461 break; 1462 // ok 1463 default: 1464 checkResult = true; 1465 } 1466 1467 return checkResult; 1468 }, 1469 errorMessage: '', 1470 errorMessageKey: '' 1471 }); 1472 1473 /* 1474 * Validate url 1475 */ 1476 $.formUtils.addValidator({ 1477 name: 'url', 1478 validatorFunction: function (url) { 1479 // written by Scott Gonzalez: http://projects.scottsplayground.com/iri/ 1480 // - Victor Jonsson added support for arrays in the url ?arg[]=sdfsdf 1481 // - General improvements made by Stéphane Moureau <https://github.com/TraderStf> 1482 var urlFilter = /^(https?|ftp):\/\/((((\w|-|\.|~|[\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])(\w|-|\.|~|[\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])(\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/(((\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/((\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|\[|\]|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#(((\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i; 1483 if (urlFilter.test(url)) { 1484 var domain = url.split('://')[1]; 1485 var domainSlashPos = domain.indexOf('/'); 1486 if (domainSlashPos > -1) 1487 domain = domain.substr(0, domainSlashPos); 1488 1489 return $.formUtils.validators.validate_domain.validatorFunction(domain); // todo: add support for IP-addresses 1490 } 1491 return false; 1492 }, 1493 errorMessage: '', 1494 errorMessageKey: 'badUrl' 1495 }); 1496 1497 /* 1498 * Validate number (floating or integer) 1499 */ 1500 $.formUtils.addValidator({ 1501 name: 'number', 1502 validatorFunction: function (val, $el, conf) { 1503 if (val !== '') { 1504 var allowing = $el.valAttr('allowing') || '', 1505 decimalSeparator = $el.valAttr('decimal-separator') || conf.decimalSeparator, 1506 allowsRange = false, 1507 begin, end, 1508 steps = $el.valAttr('step') || '', 1509 allowsSteps = false; 1510 1511 if (allowing.indexOf('number') == -1) 1512 allowing += ',number'; 1513 1514 if (allowing.indexOf('negative') > -1 && val.indexOf('-') === 0) { 1515 val = val.substr(1); 1516 } 1517 1518 if (allowing.indexOf('range') > -1) 1519 { 1520 begin = parseFloat(allowing.substring(allowing.indexOf("[") + 1, allowing.indexOf(";"))); 1521 end = parseFloat(allowing.substring(allowing.indexOf(";") + 1, allowing.indexOf("]"))); 1522 allowsRange = true; 1523 } 1524 1525 if (steps != "") 1526 { 1527 allowsSteps = true; 1528 } 1529 1530 if (decimalSeparator == ',') { 1531 if (val.indexOf('.') > -1) { 1532 return false;
1533 } 1534 // Fix for checking range with floats using , 1535 val = val.replace(',', '.'); 1536 } 1537 1538 if (allowing.indexOf('number') > -1 && val.replace(/[0-9]/g, '') === '' && (!allowsRange || (val >= begin && val <= end)) && (!allowsSteps || (val % steps == 0))) { 1539 return true; 1540 } 1541 if (allowing.indexOf('float') > -1 && val.match(new RegExp('^([0-9]+)\\.([0-9]+)$')) !== null && (!allowsRange || (val >= begin && val <= end)) && (!allowsSteps || (val % steps == 0))) { 1542 return true; 1543 } 1544 } 1545 return false; 1546 }, 1547 errorMessage: '', 1548 errorMessageKey: 'badInt' 1549 }); 1550 1551 /* 1552 * Validate alpha numeric 1553 */ 1554 $.formUtils.addValidator({ 1555 name: 'alphanumeric', 1556 validatorFunction: function (val, $el, conf, language) { 1557 var patternStart = '^([a-zA-Z0-9', 1558 patternEnd = ']+)$', 1559 additionalChars = $el.attr('data-validation-allowing'), 1560 pattern = ''; 1561 1562 if (additionalChars) { 1563 pattern = patternStart + additionalChars + patternEnd; 1564 var extra = additionalChars.replace(/\\/g, ''); 1565 if (extra.indexOf(' ') > -1) { 1566 extra = extra.replace(' ', ''); 1567 extra += ' and spaces '; 1568 } 1569 this.errorMessage = language.badAlphaNumeric + language.badAlphaNumericExtra + extra; 1570 } else { 1571 pattern = patternStart + patternEnd; 1572 this.errorMessage = language.badAlphaNumeric; 1573 } 1574 1575 return new RegExp(pattern).test(val); 1576 }, 1577 errorMessage: '', 1578 errorMessageKey: '' 1579 }); 1580 1581 /* 1582 * Validate against regexp 1583 */ 1584 $.formUtils.addValidator({ 1585 name: 'custom', 1586 validatorFunction: function (val, $el, conf) { 1587 var regexp = new RegExp($el.valAttr('regexp')); 1588 return regexp.test(val); 1589 }, 1590 errorMessage: '', 1591 errorMessageKey: 'badCustomVal' 1592 }); 1593 1594 /* 1595 * Validate date 1596 */ 1597 $.formUtils.addValidator({ 1598 name: 'date', 1599 validatorFunction: function (date, $el, conf) { 1600 var dateFormat = 'yyyy-mm-dd'; 1601 if ($el.valAttr('format')) { 1602 dateFormat = $el.valAttr('format'); 1603 } 1604 else if (conf.dateFormat) { 1605 dateFormat = conf.dateFormat; 1606 } 1607 1608 return $.formUtils.parseDate(date, dateFormat) !== false; 1609 }, 1610 errorMessage: '', 1611 errorMessageKey: 'badDate' 1612 }); 1613 1614 1615 /* 1616 * Validate group of checkboxes, validate qty required is checked 1617 * written by Steve Wasiura : http://stevewasiura.waztech.com 1618 * element attrs 1619 * data-validation="checkbox_group" 1620 * data-validation-qty="1-2" // min 1 max 2 1621 * data-validation-error-msg="chose min 1, max of 2 checkboxes" 1622 */ 1623 $.formUtils.addValidator({ 1624 name: 'checkbox_group', 1625 validatorFunction: function (val, $el, conf, lang, $form) 1626 { // preset return var 1627 var checkResult = true; 1628 // get name of element. since it is a checkbox group, all checkboxes will have same name 1629 var elname = $el.attr('name'); 1630 // get count of checked checkboxes with this name 1631 var checkedCount = $("input[type=checkbox][name^='" + elname + "']:checked", $form).length; 1632 // get el attr that specs qty required / allowed 1633 var qtyAllowed = $el.valAttr('qty'); 1634 if (qtyAllowed == undefined) { 1635 var elementType = $el.get(0).nodeName; 1636 alert('Attribute "data-validation-qty" is missing from ' + elementType + ' named ' + $el.attr('name')); 1637 } 1638 // call Utility function to check if count is above min, below max, within range etc. 1639 var qtyCheckResults = $.formUtils.numericRangeCheck(checkedCount, qtyAllowed); 1640 // results will be array, [0]=result str, [1]=qty int 1641 switch (qtyCheckResults[0]) { 1642 // outside allowed range 1643 case "out": 1644 this.errorMessage = lang.groupCheckedRangeStart + qtyAllowed + lang.groupCheckedEnd; 1645 checkResult = false;
1646 break; 1647 // below min qty 1648 case "min": 1649 this.errorMessage = lang.groupCheckedTooFewStart + qtyCheckResults[1] + lang.groupCheckedEnd; 1650 checkResult = false; 1651 break; 1652 // above max qty 1653 case "max": 1654 this.errorMessage = lang.groupCheckedTooManyStart + qtyCheckResults[1] + lang.groupCheckedEnd; 1655 checkResult = false; 1656 break; 1657 // ok 1658 default: 1659 checkResult = true; 1660 } 1661 1662 return checkResult; 1663 1664 } 1665 // errorMessage : '', // set above in switch statement 1666 // errorMessageKey: '' // not used 1667 }); 1668 1669})(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.