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