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