1/** 2* Ajax Autocomplete for jQuery, version 1.3.0 3* (c) 2017 Tomas Kirda 4* 5* Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license. 6* For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete 7*/ 8 9/*jslint browser: true, white: true, single: true, this: true, multivar: true */ 10/*global define, window, document, jQuery, exports, require */ 11 12// Expose plugin as an AMD module if AMD loader is present: 13(function (factory) { 14 "use strict"; 15 if (typeof define === 'function' && define.amd) { 16 // AMD. Register as an anonymous module. 17 define(['jquery'], factory); 18 } else if (typeof exports === 'object' && typeof require === 'function') { 19 // Browserify 20 factory(require('jquery')); 21 } else { 22 // Browser globals 23 factory(jQuery); 24 } 25}(function ($) { 26 'use strict'; 27 28 var 29 utils = (function () { 30 return { 31 escapeRegExChars: function (value) { 32 return value.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&"); 33 }, 34 createNode: function (containerClass) { 35 var div = document.createElement('div'); 36 div.className = containerClass; 37 div.style.position = 'absolute'; 38 div.style.display = 'none'; 39 return div; 40 } 41 }; 42 }()), 43 44 keys = { 45 ESC: 27, 46 TAB: 9, 47 RETURN: 13, 48 LEFT: 37, 49 UP: 38, 50 RIGHT: 39, 51 DOWN: 40 52 }; 53 54 function Autocomplete(el, options) { 55 var noop = $.noop, 56 that = this, 57 defaults = { 58 ajaxSettings: {}, 59 autoSelectFirst: false, 60 appendTo: document.body, 61 serviceUrl: null, 62 lookup: null, 63 onSelect: null, 64 width: 'auto', 65 minChars: 1, 66 maxHeight: 300, 67 deferRequestBy: 0, 68 params: {}, 69 formatResult: Autocomplete.formatResult, 70 formatGroup: Autocomplete.formatGroup, 71 delimiter: null, 72 zIndex: 9999, 73 type: 'GET', 74 noCache: false, 75 onSearchStart: noop, 76 onSearchComplete: noop, 77 onSearchError: noop, 78 preserveInput: false, 79 containerClass: 'autocomplete-suggestions', 80 tabDisabled: false, 81 dataType: 'text', 82 currentRequest: null, 83 triggerSelectOnValidInput: true, 84 preventBadQueries: true, 85 lookupFilter: function (suggestion, originalQuery, queryLowerCase) { 86 return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1; 87 }, 88 paramName: 'query', 89 transformResult: function (response) { 90 return typeof response === 'string' ? $.parseJSON(response) : response; 91 }, 92 showNoSuggestionNotice: false, 93 noSuggestionNotice: 'No results', 94 orientation: 'bottom', 95 forceFixPosition: false 96 }; 97 98 // Shared variables: 99 that.element = el; 100 that.el = $(el); 101 that.suggestions = []; 102 that.badQueries = []; 103 that.selectedIndex = -1; 104 that.currentValue = that.element.value; 105 that.intervalId = 0; 106 that.cachedResponse = {}; 107 that.onChangeInterval = null; 108 that.onChange = null; 109 that.isLocal = false; 110 that.suggestionsContainer = null; 111 that.noSuggestionsContainer = null; 112 that.options = $.extend({}, defaults, options); 113 that.classes = { 114 selected: 'autocomplete-selected', 115 suggestion: 'autocomplete-suggestion' 116 }; 117 that.hint = null; 118 that.hintValue = ''; 119 that.selection = null; 120 121 // Initialize and set options: 122 that.initialize(); 123 that.setOptions(options); 124 } 125 126 Autocomplete.utils = utils; 127 128 $.Autocomplete = Autocomplete; 129 130 Autocomplete.formatResult = function (suggestion, currentValue) {
131 // Do not replace anything if there current value is empty 132 if (!currentValue) { 133 return suggestion.value; 134 } 135 136 var pattern = '(' + utils.escapeRegExChars(currentValue) + ')'; 137 138 return suggestion.value 139 .replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>') 140 .replace(/&/g, '&') 141 .replace(/</g, '<') 142 .replace(/>/g, '>') 143 .replace(/"/g, '"') 144 .replace(/<(\/?strong)>/g, '<$1>'); 145 }; 146 147 Autocomplete.formatGroup = function (suggestion, category) { 148 return '<div class="autocomplete-group"><strong>' + category + '</strong></div>'; 149 }; 150 151 Autocomplete.prototype = { 152 153 killerFn: null, 154 155 initialize: function () { 156 var that = this, 157 suggestionSelector = '.' + that.classes.suggestion, 158 selected = that.classes.selected, 159 options = that.options, 160 container; 161 162 // Remove autocomplete attribute to prevent native suggestions: 163 that.element.setAttribute('autocomplete', 'off'); 164 165 that.killerFn = function (e) { 166 if (!$(e.target).closest('.' + that.options.containerClass).length) { 167 that.killSuggestions(); 168 that.disableKillerFn(); 169 } 170 }; 171 172 // html() deals with many types: htmlString or Element or Array or jQuery
173 that.noSuggestionsContainer = $('<div class="autocomplete-no-suggestion"></div>') 174 .html(this.options.noSuggestionNotice).get(0); 175 176 that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass); 177 178 container = $(that.suggestionsContainer); 179 180 container.appendTo(options.appendTo); 181 182 // Only set width if it was provided: 183 if (options.width !== 'auto') { 184 container.css('width', options.width); 185 } 186 187 // Listen for mouse over event on suggestions list: 188 container.on('mouseover.autocomplete', suggestionSelector, function () { 189 that.activate($(this).data('index')); 190 }); 191 192 // Deselect active element when mouse leaves suggestions container: 193 container.on('mouseout.autocomplete', function () { 194 that.selectedIndex = -1; 195 container.children('.' + selected).removeClass(selected); 196 }); 197 198 // Listen for click event on suggestions list: 199 container.on('click.autocomplete', suggestionSelector, function () { 200 that.select($(this).data('index')); 201 return false; 202 }); 203 204 that.fixPositionCapture = function () { 205 if (that.visible) { 206 that.fixPosition(); 207 } 208 }; 209 210 $(window).on('resize.autocomplete', that.fixPositionCapture); 211 212 that.el.on('keydown.autocomplete', function (e) { that.onKeyPress(e); }); 213 that.el.on('keyup.autocomplete', function (e) { that.onKeyUp(e); }); 214 that.el.on('blur.autocomplete', function () { that.onBlur(); }); 215 that.el.on('focus.autocomplete', function () { that.onFocus(); }); 216 that.el.on('change.autocomplete', function (e) { that.onKeyUp(e); }); 217 that.el.on('input.autocomplete', function (e) { that.onKeyUp(e); }); 218 }, 219 220 onFocus: function () { 221 var that = this; 222 223 that.fixPosition(); 224 225 if (that.el.val().length >= that.options.minChars) { 226 that.onValueChange(); 227 } 228 }, 229 230 onBlur: function () { 231 this.enableKillerFn(); 232 }, 233 234 abortAjax: function () { 235 var that = this; 236 if (that.currentRequest) { 237 that.currentRequest.abort(); 238 that.currentRequest = null; 239 } 240 }, 241 242 setOptions: function (suppliedOptions) { 243 var that = this, 244 options = that.options; 245 246 $.extend(options, suppliedOptions); 247 248 that.isLocal = $.isArray(options.lookup); 249 250 if (that.isLocal) { 251 options.lookup = that.verifySuggestionsFormat(options.lookup); 252 } 253 254 options.orientation = that.validateOrientation(options.orientation, 'bottom'); 255 256 // Adjust height, width and z-index: 257 $(that.suggestionsContainer).css({ 258 'max-height': options.maxHeight + 'px', 259 'width': options.width + 'px', 260 'z-index': options.zIndex 261 }); 262 }, 263 264 265 clearCache: function () { 266 this.cachedResponse = {}; 267 this.badQueries = []; 268 }, 269 270 clear: function () { 271 this.clearCache(); 272 this.currentValue = ''; 273 this.suggestions = []; 274 }, 275 276 disable: function () { 277 var that = this; 278 that.disabled = true; 279 clearInterval(that.onChangeInterval); 280 that.abortAjax(); 281 }, 282 283 enable: function () { 284 this.disabled = false; 285 }, 286 287 fixPosition: function () { 288 // Use only when container has already its content 289 290 var that = this, 291 $container = $(that.suggestionsContainer), 292 containerParent = $container.parent().get(0); 293 // Fix position automatically when appended to body. 294 // In other cases force parameter must be given. 295 if (containerParent !== document.body && !that.options.forceFixPosition) { 296 return; 297 } 298 299 // Choose orientation 300 var orientation = that.options.orientation, 301 containerHeight = $container.outerHeight(), 302 height = that.el.outerHeight(), 303 offset = that.el.offset(), 304 styles = { 'top': offset.top, 'left': offset.left }; 305 306 if (orientation === 'auto') { 307 var viewPortHeight = $(window).height(), 308 scrollTop = $(window).scrollTop(), 309 topOverflow = -scrollTop + offset.top - containerHeight, 310 bottomOverflow = scrollTop + viewPortHeight - (offset.top + height + containerHeight); 311 312 orientation = (Math.max(topOverflow, bottomOverflow) === topOverflow) ? 'top' : 'bottom'; 313 } 314 315 if (orientation === 'top') { 316 styles.top += -containerHeight; 317 } else { 318 styles.top += height; 319 } 320 321 // If container is not positioned to body, 322 // correct its position using offset parent offset 323 if(containerParent !== document.body) { 324 var opacity = $container.css('opacity'), 325 parentOffsetDiff; 326 327 if (!that.visible){ 328 $container.css('opacity', 0).show(); 329 } 330 331 parentOffsetDiff = $container.offsetParent().offset(); 332 styles.top -= parentOffsetDiff.top; 333 styles.left -= parentOffsetDiff.left; 334 335 if (!that.visible){ 336 $container.css('opacity', opacity).hide(); 337 } 338 } 339 340 if (that.options.width === 'auto') { 341 styles.width = that.el.outerWidth() + 'px'; 342 } 343 344 $container.css(styles); 345 }, 346 347 enableKillerFn: function () { 348 var that = this; 349 $(document).on('click.autocomplete', that.killerFn); 350 }, 351 352 disableKillerFn: function () { 353 var that = this; 354 $(document).off('click.autocomplete', that.killerFn); 355 }, 356 357 killSuggestions: function () { 358 var that = this; 359 that.stopKillSuggestions(); 360 that.intervalId = window.setInterval(function () { 361 if (that.visible) { 362 // No need to restore value when 363 // preserveInput === true, 364 // because we did not change it 365 if (!that.options.preserveInput) { 366 that.el.val(that.currentValue); 367 } 368 369 that.hide(); 370 } 371 372 that.stopKillSuggestions(); 373 }, 50); 374 }, 375 376 stopKillSuggestions: function () { 377 window.clearInterval(this.intervalId); 378 }, 379 380 isCursorAtEnd: function () { 381 var that = this, 382 valLength = that.el.val().length, 383 selectionStart = that.element.selectionStart, 384 range; 385 386 if (typeof selectionStart === 'number') { 387 return selectionStart === valLength; 388 } 389 if (document.selection) { 390 range = document.selection.createRange(); 391 range.moveStart('character', -valLength); 392 return valLength === range.text.length; 393 } 394 return true; 395 }, 396 397 onKeyPress: function (e) { 398 var that = this; 399 400 // If suggestions are hidden and user presses arrow down, display suggestions: 401 if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) { 402 that.suggest(); 403 return; 404 } 405 406 if (that.disabled || !that.visible) { 407 return; 408 } 409 410 switch (e.which) { 411 case keys.ESC: 412 that.el.val(that.currentValue); 413 that.hide(); 414 break; 415 case keys.RIGHT: 416 if (that.hint && that.options.onHint && that.isCursorAtEnd()) { 417 that.selectHint(); 418 break; 419 } 420 return; 421 case keys.TAB: 422 if (that.hint && that.options.onHint) { 423 that.selectHint(); 424 return; 425 } 426 if (that.selectedIndex === -1) { 427 that.hide(); 428 return; 429 } 430 that.select(that.selectedIndex); 431 if (that.options.tabDisabled === false) { 432 return; 433 } 434 break; 435 case keys.RETURN: 436 if (that.selectedIndex === -1) { 437 that.hide(); 438 return; 439 } 440 that.select(that.selectedIndex); 441 break; 442 case keys.UP: 443 that.moveUp(); 444 break; 445 case keys.DOWN: 446 that.moveDown(); 447 break; 448 default: 449 return; 450 } 451 452 // Cancel event if function did not return: 453 e.stopImmediatePropagation(); 454 e.preventDefault(); 455 }, 456 457 onKeyUp: function (e) { 458 var that = this; 459 460 if (that.disabled) { 461 return; 462 } 463 464 switch (e.which) { 465 case keys.UP: 466 case keys.DOWN: 467 return; 468 } 469 470 clearInterval(that.onChangeInterval); 471 472 if (that.currentValue !== that.el.val()) { 473 that.findBestHint(); 474 if (that.options.deferRequestBy > 0) { 475 // Defer lookup in case when value changes very quickly: 476 that.onChangeInterval = setInterval(function () { 477 that.onValueChange(); 478 }, that.options.deferRequestBy); 479 } else { 480 that.onValueChange(); 481 } 482 } 483 }, 484 485 onValueChange: function () { 486 var that = this, 487 options = that.options, 488 value = that.el.val(), 489 query = that.getQuery(value); 490 491 if (that.selection && that.currentValue !== query) { 492 that.selection = null; 493 (options.onInvalidateSelection || $.noop).call(that.element); 494 } 495 496 clearInterval(that.onChangeInterval); 497 that.currentValue = value; 498 that.selectedIndex = -1; 499 500 // Check existing suggestion for the match before proceeding: 501 if (options.triggerSelectOnValidInput && that.isExactMatch(query)) { 502 that.select(0); 503 return; 504 } 505 506 if (query.length < options.minChars) { 507 that.hide(); 508 } else { 509 that.getSuggestions(query); 510 } 511 }, 512 513 isExactMatch: function (query) { 514 var suggestions = this.suggestions; 515 516 return (suggestions.length === 1 && suggestions[0].value.toLowerCase() === query.toLowerCase()); 517 }, 518 519 getQuery: function (value) { 520 var delimiter = this.options.delimiter, 521 parts; 522 523 if (!delimiter) { 524 return value; 525 } 526 parts = value.split(delimiter); 527 return $.trim(parts[parts.length - 1]); 528 }, 529 530 getSuggestionsLocal: function (query) { 531 var that = this, 532 options = that.options, 533 queryLowerCase = query.toLowerCase(), 534 filter = options.lookupFilter, 535 limit = parseInt(options.lookupLimit, 10), 536 data; 537 538 data = { 539 suggestions: $.grep(options.lookup, function (suggestion) { 540 return filter(suggestion, query, queryLowerCase); 541 }) 542 }; 543 544 if (limit && data.suggestions.length > limit) { 545 data.suggestions = data.suggestions.slice(0, limit); 546 } 547 548 return data; 549 }, 550 551 getSuggestions: function (q) { 552 var response, 553 that = this, 554 options = that.options, 555 serviceUrl = options.serviceUrl, 556 params, 557 cacheKey, 558 ajaxSettings; 559 560 options.params[options.paramName] = q; 561 params = options.ignoreParams ? null : options.params; 562 563 if (options.onSearchStart.call(that.element, options.params) === false) { 564 return; 565 } 566 567 if ($.isFunction(options.lookup)){ 568 options.lookup(q, function (data) { 569 that.suggestions = data.suggestions; 570 that.suggest(); 571 options.onSearchComplete.call(that.element, q, data.suggestions); 572 }); 573 return; 574 } 575 576 if (that.isLocal) { 577 response = that.getSuggestionsLocal(q); 578 } else { 579 if ($.isFunction(serviceUrl)) { 580 serviceUrl = serviceUrl.call(that.element, q); 581 } 582 cacheKey = serviceUrl + '?' + $.param(params || {}); 583 response = that.cachedResponse[cacheKey]; 584 } 585 586 if (response && $.isArray(response.suggestions)) { 587 that.suggestions = response.suggestions; 588 that.suggest(); 589 options.onSearchComplete.call(that.element, q, response.suggestions); 590 } else if (!that.isBadQuery(q)) { 591 that.abortAjax(); 592 593 ajaxSettings = { 594 url: serviceUrl, 595 data: params, 596 type: options.type, 597 dataType: options.dataType 598 }; 599 600 $.extend(ajaxSettings, options.ajaxSettings); 601 602 that.currentRequest = $.ajax(ajaxSettings).done(function (data) { 603 var result; 604 that.currentRequest = null; 605 result = options.transformResult(data, q); 606 that.processResponse(result, q, cacheKey); 607 options.onSearchComplete.call(that.element, q, result.suggestions); 608 }).fail(function (jqXHR, textStatus, errorThrown) { 609 options.onSearchError.call(that.element, q, jqXHR, textStatus, errorThrown); 610 }); 611 } else { 612 options.onSearchComplete.call(that.element, q, []); 613 } 614 }, 615 616 isBadQuery: function (q) { 617 if (!this.options.preventBadQueries){ 618 return false; 619 } 620 621 var badQueries = this.badQueries, 622 i = badQueries.length; 623 624 while (i--) { 625 if (q.indexOf(badQueries[i]) === 0) { 626 return true; 627 } 628 } 629 630 return false; 631 }, 632 633 hide: function () { 634 var that = this, 635 container = $(that.suggestionsContainer); 636 637 if ($.isFunction(that.options.onHide) && that.visible) { 638 that.options.onHide.call(that.element, container); 639 } 640 641 that.visible = false; 642 that.selectedIndex = -1; 643 clearInterval(that.onC
643hangeInterval); 644 $(that.suggestionsContainer).hide(); 645 that.signalHint(null); 646 }, 647 648 suggest: function () { 649 if (!this.suggestions.length) { 650 if (this.options.showNoSuggestionNotice) { 651 this.noSuggestions(); 652 } else { 653 this.hide(); 654 } 655 return; 656 } 657 658 var that = this, 659 options = that.options, 660 groupBy = options.groupBy, 661 formatResult = options.formatResult, 662 value = that.getQuery(that.currentValue), 663 className = that.classes.suggestion, 664 classSelected = that.classes.selected, 665 container = $(that.suggestionsContainer), 666 noSuggestionsContainer = $(that.noSuggestionsContainer), 667 beforeRender = options.beforeRender, 668 html = '', 669 category, 670 formatGroup = function (suggestion, index) { 671 var currentCategory = suggestion.data[groupBy]; 672 673 if (category === currentCategory){ 674 return ''; 675 } 676 677 category = currentCategory; 678 679 return options.formatGroup(suggestion, category); 680 }; 681 682 if (options.triggerSelectOnValidInput && that.isExactMatch(value)) { 683 that.select(0); 684 return; 685 } 686 687 // Build suggestions inner HTML: 688 $.each(that.suggestions, function (i, suggestion) { 689 if (groupBy){ 690 html += formatGroup(suggestion, value, i); 691 } 692 693 html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value, i) + '</div>'; 694 }); 695 696 this.adjustContainerWidth(); 697 698 noSuggestionsContainer.detach(); 699 container.html(html); 700 701 if ($.isFunction(beforeRender)) { 702 beforeRender.call(that.element, container, that.suggestions); 703 } 704 705 that.fixPosition(); 706 container.show(); 707 708 // Select first value by default: 709 if (options.autoSelectFirst) { 710 that.selectedIndex = 0; 711 container.scrollTop(0); 712 container.children('.' + className).first().addClass(classSelected); 713 } 714 715 that.visible = true; 716 that.findBestHint(); 717 }, 718 719 noSuggestions: function() { 720 var that = this, 721 container = $(that.suggestionsContainer), 722 noSuggestionsContainer = $(that.noSuggestionsContainer); 723 724 this.adjustContainerWidth(); 725 726 // Some explicit steps. Be careful here as it easy to get 727 // noSuggestionsContainer removed from DOM if not detached properly. 728 noSuggestionsContainer.detach(); 729 container.empty(); // clean suggestions if any 730 container.append(noSuggestionsContainer); 731 732 that.fixPosition(); 733 734 container.show(); 735 that.visible = true; 736 }, 737 738 adjustContainerWidth: function() { 739 var that = this, 740 options = that.options, 741 width, 742 container = $(that.suggestionsContainer); 743 744 // If width is auto, adjust width before displaying suggestions, 745 // because if instance was created before input had width, it will be zero. 746 // Also it adjusts if input width has changed. 747 if (options.width === 'auto') { 748 width = that.el.outerWidth(); 749 container.css('width', width > 0 ? width : 300); 750 } else if(options.width === 'flex') { 751 // Trust the source! Unset the width property so it will be the max length 752 // the containing elements. 753 container.css('width', ''); 754 } 755 }, 756 757 findBestHint: function () { 758 var that = this, 759 value = that.el.val().toLowerCase(), 760 bestMatch = null; 761 762 if (!value) { 763 return; 764 } 765 766 $.each(that.suggestions, function (i, suggestion) { 767 var foundMatch = suggestion.value.toLowerCase().indexOf(value) === 0; 768 if (foundMatch) { 769 bestMatch = suggestion; 770 } 771 return !foundMatch; 772 }); 773 774 that.signalHint(bestMatch); 775 }, 776 777 signalHint: function (suggestion) { 778 var hintValue = '', 779 that = this; 780 if (suggestion) { 781 hintValue = that.currentValue + suggestion.value.substr(that.currentValue.length); 782 } 783 if (that.hintValue !== hintValue) { 784 that.hintValue = hintValue; 785 that.hint = suggestion; 786 (this.options.onHint || $.noop)(hintValue); 787 } 788 }, 789 790 verifySuggestionsFormat: function (suggestions) { 791 // If suggestions is string array, convert them to supp
791orted format: 792 if (suggestions.length && typeof suggestions[0] === 'string') { 793 return $.map(suggestions, function (value) { 794 return { value: value, data: null }; 795 }); 796 } 797 798 return suggestions; 799 }, 800 801 validateOrientation: function(orientation, fallback) { 802 orientation = $.trim(orientation || '').toLowerCase(); 803 804 if($.inArray(orientation, ['auto', 'bottom', 'top']) === -1){ 805 orientation = fallback; 806 } 807 808 return orientation; 809 }, 810 811 processResponse: function (result, originalQuery, cacheKey) { 812 var that = this, 813 options = that.options; 814 815 result.suggestions = that.verifySuggestionsFormat(result.suggestions); 816 817 // Cache results if cache is not disabled: 818 if (!options.noCache) { 819 that.cachedResponse[cacheKey] = result; 820 if (options.preventBadQueries && !result.suggestions.length) { 821 that.badQueries.push(originalQuery); 822 } 823 } 824 825 // Return if originalQuery is not matching current query: 826 if (originalQuery !== that.getQuery(that.currentValue)) { 827 return; 828 } 829 830 that.suggestions = result.suggestions; 831 that.suggest(); 832 }, 833 834 activate: function (index) { 835 var that = this, 836 activeItem, 837 selected = that.classes.selected, 838 container = $(that.suggestionsContainer), 839 children = container.find('.' + that.classes.suggestion); 840 841 container.find('.' + selected).removeClass(selected); 842 843 that.selectedIndex = index; 844 845 if (that.selectedIndex !== -1 && children.length > that.selectedIndex) { 846 activeItem = children.get(that.selectedIndex); 847 $(activeItem).addClass(selected); 848 return activeItem; 849 } 850 851 return null; 852 }, 853 854 selectHint: function () { 855 var that = this, 856 i = $.inArray(that.hint, that.suggestions); 857 858 that.select(i); 859 }, 860 861 select: function (i) { 862 var that = this; 863 that.hide(); 864 that.onSelect(i); 865 that.disableKillerFn(); 866 }, 867 868 moveUp: function () { 869 var that = this; 870 871 if (that.selectedIndex === -1) { 872 return; 873 } 874 875 if (that.selectedIndex === 0) { 876 $(that.suggestionsContainer).children().first().removeClass(that.classes.selected); 877 that.selectedIndex = -1; 878 that.el.val(that.currentValue); 879 that.findBestHint(); 880 return; 881 } 882 883 that.adjustScroll(that.selectedIndex - 1); 884 }, 885 886 moveDown: function () { 887 var that = this; 888 889 if (that.selectedIndex === (that.suggestions.length - 1)) { 890 return; 891 } 892 893 that.adjustScroll(that.selectedIndex + 1); 894 }, 895 896 adjustScroll: function (index) { 897 var that = this, 898 activeItem = that.activate(index); 899 900 if (!activeItem) { 901 return; 902 } 903 904 var offsetTop, 905 upperBound, 906 lowerBound, 907 heightDelta = $(activeItem).outerHeight(); 908 909 offsetTop = activeItem.offsetTop; 910 upperBound = $(that.suggestionsContainer).scrollTop(); 911 lowerBound = upperBound + that.options.maxHeight - heightDelta; 912 913 if (offsetTop < upperBound) { 914 $(that.suggestionsContainer).scrollTop(offsetTop); 915 } else if (offsetTop > lowerBound) { 916 $(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta); 917 } 918 919 if (!that.options.preserveInput) { 920 that.el.val(that.getValue(that.suggestions[index].value)); 921 } 922 that.signalHint(null); 923 }, 924 925 onSelect: function (index) { 926 var that = this, 927 onSelectCallback = that.options.onSelect, 928 suggestion = that.suggestions[index]; 929 930 that.currentValue = that.getValue(suggestion.value); 931 932 if (that.currentValue !== that.el.val() && !that.options.preserveInput) { 933 that.el.val(that.currentValue); 934 } 935 936 that.signalHint(null); 937 that.suggestions = []; 938 that.selection = suggestion; 939 940 if ($.isFunction(onSelectCallback)) { 941 onSelectCallback.call(that.element, suggestion); 942 } 943 }, 944 945 getValue: function (value) { 946 var that = this, 947 delimiter = that.options.delimiter, 948 currentValue, 949 parts; 950 951 if (!delimiter) { 952 return value; 953 } 954 955 currentValue = that.currentValue; 956 parts = currentValue.split(delimiter); 957 958 if (parts.length === 1) { 959 return value; 960 } 961 962 return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value; 963 }, 964 965 dispose: function () { 966 var that = this; 967 that.el.off('.autocomplete').removeData('autocomplete'); 968 that.disableKillerFn(); 969 $(window).off('resize.autocomplete', that.fixPositionCapture); 970 $(that.suggestionsContainer).remove(); 971 } 972 }; 973 974 // Create chainable jQuery plugin: 975 $.fn.autocomplete = $.fn.devbridgeAutocomplete = function (options, args) { 976 var dataKey = 'autocomplete'; 977 // If function invoked without argument return 978 // instance of the first matched element: 979 if (!arguments.length) { 980 return this.first().data(dataKey); 981 } 982 983 return this.each(function () { 984 var inputElement = $(this), 985 instance = inputElement.data(dataKey); 986 987 if (typeof options === 'string') { 988 if (instance && typeof instance[options] === 'function') { 989 instance[options](args); 990 } 991 } else { 992 // If instance already exists, destroy it: 993 if (instance && instance.dispose) { 994 instance.dispose(); 995 } 996 instance = new Autocomplete(this, options); 997 inputElement.data(dataKey, instance); 998 } 999 }); 1000 }; 1001}));
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.