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