1/** 2* Ajax Autocomplete for jQuery, version 1.4.1 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('ul'); 36 div.classList.add(containerClass); 37 div.classList.add('product_list_widget'); 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({}, 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) : 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 // Remove autocomplete attribute to prevent native suggestions: 167 that.element.setAttribute('autocomplete', 'off' ); 168 169 // html() deals with many types: htmlString or Element or Array or jQuery
170 that.noSuggestionsContainer = $('<div class="autocomplete-no-suggestion post"></div>') 171 .html(this.options.noSuggestionNotice).get(0); 172 173 that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass); 174 175 container = $(that.suggestionsContainer); 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 195 // Listen for click event on suggestions list: 196 container.on('click.autocomplete', suggestionSelector, function () { 197 that.select($(this).data('index')); 198 }); 199 200 container.on('click.autocomplete', function () { 201 clearTimeout(that.blurTimeoutId); 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 if (that.disabled) { 223 return; 224 } 225 that.fixPosition(); 226 227 if (that.el.val().length >= that.options.minChars) { 228 that.onValueChange(); 229 } 230 }, 231 232 onBlur: function () { 233 var that = this, 234 options = that.options, 235 value = that.el.val(), 236 query = that.getQuery(value); 237 238 // If user clicked on a suggestion, hide() will 239 // be canceled, otherwise close suggestions 240 that.blurTimeoutId = setTimeout(function () { 241 that.hide(); 242 243 if (that.selection && that.currentValue !== query) { 244 (options.onInvalidateSelection || $.noop).call(that.element); 245 } 246 }, 200); 247 }, 248 249 abortAjax: function () { 250 var that = this; 251 if (that.currentRequest) { 252 that.currentRequest.abort(); 253 that.currentRequest = null; 254 } 255 }, 256 257 setOptions: function (suppliedOptions) { 258 var that = this, 259 options = that.options; 260 261 this.options = $.extend({}, options, suppliedOptions); 262 263 that.isLocal = $.isArray(options.lookup); 264 265 if (that.isLocal) { 266 options.lookup = that.verifySuggestionsFormat(options.lookup); 267 } 268 269 options.orientation = that.validateOrientation(options.orientation, 'bottom'); 270 271 // Adjust height, width and z-index: 272 /* 273 $(that.suggestionsContainer).css({ 274 'max-height': options.maxHeight + 'px', 275 'width': options.width + 'px', 276 'z-index': options.zIndex 277 }); 278 */ 279 }, 280 281 282 clearCache: function () { 283 this.cachedResponse = {}; 284 this.badQueries = []; 285 }, 286 287 clear: function () { 288 this.clearCache(); 289 this.currentValue = ''; 290 this.suggestions = []; 291 }, 292 293 disable: function () { 294 var that = this; 295 that.disabled = true; 296 clearTimeout(that.onChangeTimeout); 297 that.abortAjax(); 298 }, 299 300 enable: function () { 301 this.disabled = false;
302 }, 303 304 fixPosition: function () { 305 // Use only when container has already its content 306 307 var that = this, 308 $container = $(that.suggestionsContainer), 309 containerParent = $container.parent().get(0); 310 // Fix position automatically when appended to body. 311 // In other cases force parameter must be given. 312 if (containerParent !== document.body && !that.options.forceFixPosition) { 313 return; 314 } 315 316 // Choose orientation 317 var orientation = that.options.orientation, 318 containerHeight = $container.outerHeight(), 319 height = that.el.outerHeight(), 320 offset = that.el.offset(), 321 styles = { 'top': offset.top, 'left': offset.left }; 322 323 if (orientation === 'auto') { 324 var viewPortHeight = $(window).height(), 325 scrollTop = $(window).scrollTop(), 326 topOverflow = -scrollTop + offset.top - containerHeight, 327 bottomOverflow = scrollTop + viewPortHeight - (offset.top + height + containerHeight); 328 329 orientation = (Math.max(topOverflow, bottomOverflow) === topOverflow) ? 'top' : 'bottom'; 330 } 331 332 if (orientation === 'top') { 333 styles.top += -containerHeight; 334 } else { 335 styles.top += height; 336 } 337 338 // If container is not positioned to body, 339 // correct its position using offset parent offset 340 if(containerParent !== document.body) { 341 var opacity = $container.css('opacity'), 342 parentOffsetDiff; 343 344 if (!that.visible){ 345 $container.css('opacity', 0).show(); 346 } 347 348 parentOffsetDiff = $container.offsetParent().offset(); 349 styles.top -= parentOffsetDiff.top; 350 styles.left -= parentOffsetDiff.left; 351 352 if (!that.visible){ 353 $container.css('opacity', opacity).hide(); 354 } 355 } 356 357 if (that.options.width === 'auto') { 358 styles.width = that.el.outerWidth() + 'px'; 359 } 360 361 $container.css(styles); 362 }, 363 364 isCursorAtEnd: function () { 365 var that = this, 366 valLength = that.el.val().length, 367 selectionStart = that.element.selectionStart, 368 range; 369 370 if (typeof selectionStart === 'number') { 371 return selectionStart === valLength; 372 } 373 if (document.selection) { 374 range = document.selection.createRange(); 375 range.moveStart('character', -valLength); 376 return valLength === range.text.length; 377 } 378 return true; 379 }, 380 381 onKeyPress: function (e) { 382 var that = this; 383 384 // If suggestions are hidden and user presses arrow down, display suggestions: 385 if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) { 386 that.suggest(); 387 return; 388 } 389 390 if (that.disabled || !that.visible) { 391 return; 392 } 393 394 switch (e.which) { 395 case keys.ESC: 396 that.el.val(that.currentValue); 397 $('.results-found').remove(); 398 that.hide(); 399 break; 400 case keys.RIGHT: 401 if (that.hint && that.options.onHint && that.isCursorAtEnd()) { 402 that.selectHint(); 403 break; 404 } 405 return; 406 case keys.TAB: 407 if (that.hint && that.options.onHint) { 408 that.selectHint(); 409 return; 410 } 411 if (that.selectedIndex === -1) { 412 that.hide(); 413 return; 414 } 415 that.select(that.selectedIndex); 416 if (that.options.tabDisabled === false) { 417 return; 418 } 419 break; 420 case keys.RETURN: 421 if (that.selectedIndex === -1) { 422 that.hide(); 423 return; 424 } 425 that.select(that.selectedIndex); 426 break; 427 case keys.UP: 428 that.moveUp(); 429 break; 430 case keys.DOWN: 431 that.moveDown(); 432 break; 433 default: 434 return; 435 } 436 437 // Cancel event if function did not return: 438 e.stopImmediatePropagation(); 439 e.preventDefault(); 440 }, 441 442 onKeyUp: function (e) { 443 var that = this; 444 445 if (that.disabled) { 446 return; 447 } 448 449 switch (e.which) { 450 case keys.UP: 451 case keys.DOWN: 452 return; 453 } 454 455 clearTimeout(that.onChangeTimeout); 456 457 if (that.currentValue !== that.el.val()) { 458 that.findBestHint(); 459 if (that.options.deferRequestBy > 0) { 460 // Defer lookup in case when value changes very quickly: 461 that.onChangeTimeout = setTimeout(function () { 462 that.onValueChange(); 463 }, that.options.deferRequestBy); 464 } else { 465 that.onValueChange(); 466 } 467 } 468 }, 469 470 onValueChange: function () { 471 var that = this, 472 options = that.options, 473 value = that.el.val(), 474 query = that.getQuery(value); 475 476 if (that.selection && that.currentValue !== query) { 477 that.selection = null; 478 (options.onInvalidateSelection || $.noop).call(that.element); 479 } 480 481 clearTimeout(that.onChangeTimeout); 482 that.currentValue = value; 483 that.selectedIndex = -1; 484 485 // Check existing suggestion for the match before proceeding: 486 if (options.triggerSelectOnValidInput && that.isExactMatch(query)) { 487 that.select(0); 488 return; 489 } 490 491 if (query.length < options.minChars) { 492 that.hide(); 493 } else { 494 that.getSuggestions(query); 495 } 496 }, 497 498 isExactMatch: function (query) { 499 var suggestions = this.suggestions; 500 501 return (suggestions.length === 1 && suggestions[0].value.toLowerCase() === query.toLowerCase()); 502 }, 503 504 getQuery: function (value) { 505 var delimiter = this.options.delimiter, 506 parts; 507 508 if (!delimiter) { 509 return value; 510 } 511 parts = value.split(delimiter); 512 return $.trim(parts[parts.length - 1]); 513 }, 514 515 getSuggestionsLocal: function (query) { 516 var that = this, 517 options = that.options, 518 queryLowerCase = query.toLowerCase(), 519 filter = options.lookupFilter, 520 limit = parseInt(options.lookupLimit, 10), 521 data; 522 523 data = { 524 suggestions: $.grep(options.lookup, function (suggestion) { 525 return filter(suggestion, query, queryLowerCase); 526 }) 527 }; 528 529 if (limit && data.suggestions.length > limit) { 530 data.suggestions = data.suggestions.slice(0, limit); 531 } 532 533 return data; 534 }, 535 536 getSuggestions: function (q) { 537 var response, 538 that = this, 539 options = that.options, 540 serviceUrl = options.serviceUrl, 541 params, 542 cacheKey, 543 ajaxSettings; 544 545 options.params[options.paramName] = q; 546 547 if (options.onSearchStart.call(that.element, options.params) === false) { 548 return; 549 } 550 551 params = options.ignoreParams ? null : options.params; 552 553 if ($.isFunction(options.lookup)){ 554 options.lookup(q, function (data) { 555 that.suggestions = data.suggestions; 556 that.suggest(); 557 options.onSearchComplete.call(that.element, q, data.suggestions); 558 }); 559 return; 560 } 561 562 if (that.isLocal) { 563 response = that.getSuggestionsLocal(q); 564 } else { 565 if ($.isFunction(serviceUrl)) { 566 serviceUrl = serviceUrl.call(that.element, q); 567 } 568 cacheKey = serviceUrl + '?' + $.param(params || {}); 569 response = that.cachedResponse[cacheKey]; 570 } 571 572 if (response && $.isArray(response.suggestions)) { 573 that.suggestions = response.suggestions; 574 that.suggest(); 575 options.onSearchComplete.call(that.element, q, response.suggestions); 576 } else if (!that.isBadQuery(q)) { 577 that.abortAjax(); 578 579 ajaxSettings = { 580 url: serviceUrl, 581 data: params, 582 type: options.type, 583 dataType: options.dataType 584 }; 585 586 $.extend(ajaxSettings, options.ajaxSettings); 587 588 that.currentRequest = $.ajax(ajaxSettings).done(function (data) { 589 var result; 590 that.currentRequest = null; 591 result = options.transformResult(data, q); 592 that.processResponse(result, q, cacheKey); 593 options.onSearchComplete.call(that.element, q, result.suggestions); 594 }).fail(function (jqXHR, textStatus, errorThrown) { 595 options.onSearchError.call(that.element, q, jqXHR, textStatus, errorThrown); 596 }); 597 } else { 598 options.onSearchComplete.call(that.element, q, []); 599 } 600 }, 601 602 isBadQuery: function (q) { 603 if (!this.options.preventBadQueries){ 604 return false;
605 } 606 607 var badQueries = this.badQueries, 608 i = badQueries.length; 609 610 while (i--) { 611 if (q.indexOf(badQueries[i]) === 0) { 612 return true; 613 } 614 } 615 616 return false; 617 }, 618 619 hide: function () { 620 var that = this, 621 container = $(that.suggestionsContainer); 622 623 if ($.isFunction(that.options.onHide) && that.visible) { 624 that.options.onHide.call(that.element, container); 625 } 626 627 that.visible = false; 628 that.selectedIndex = -1; 629 clearTimeout(that.onChangeTimeout); 630 $(that.suggestionsContainer).hide(); 631 that.signalHint(null); 632 }, 633 634 suggest: function () { 635 if (!this.suggestions.length) { 636 if (this.options.showNoSuggestionNotice) { 637 this.noSuggestions(); 638 } else { 639 this.hide(); 640 } 641 return; 642 } 643 644 var that = this, 645 options = that.options, 646 groupBy = options.groupBy, 647 formatResult = options.formatResult, 648 value = that.getQuery(that.currentValue), 649 className = that.classes.suggestion, 650 classSelected = that.classes.selected, 651 container = $(that.suggestionsContainer), 652 noSuggestionsContainer = $(that.noSuggestionsContainer), 653 beforeRender = options.beforeRender, 654 html = '', 655 category, 656 formatGroup = function (suggestion, index) { 657 var currentCategory = suggestion.data[groupBy]; 658 659 if (category === currentCategory){ 660 return ''; 661 } 662 663 category = currentCategory; 664 665 return options.formatGroup(suggestion, category); 666 }; 667 668 if (options.triggerSelectOnValidInput && that.isExactMatch(value)) { 669 that.select(0); 670 return; 671 } 672 673 // Build suggestions inner HTML: 674 675 $.each(that.suggestions, function (i, suggestion) { 676 if (groupBy){ 677 html += formatGroup(suggestion, value, i); 678 } 679 680 html += '<li class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value, i) + '</li>'; 681 }); 682 683 this.adjustContainerWidth(); 684 685 noSuggestionsContainer.detach(); 686 container.html(html); 687 688 if ($.isFunction(beforeRender)) { 689 beforeRender.call(that.element, container, that.suggestions); 690 } 691 692 that.fixPosition(); 693 container.show(); 694 695 // Select first value by default: 696 if (options.autoSelectFirst) { 697 that.selectedIndex = 0; 698 container.scrollTop(0); 699 container.children('.' + className).first().addClass(classSelected); 700 } 701 702 that.visible = true; 703 that.findBestHint(); 704 }, 705 706 noSuggestions: function() { 707 var that = this, 708 beforeRender = that.options.beforeRender, 709 container = $(that.suggestionsContainer), 710 noSuggestionsContainer = $(that.noSuggestionsContainer); 711 712 this.adjustContainerWidth(); 713 714 // Some explicit steps. Be careful here as it easy to get 715 // noSuggestionsContainer removed from DOM if not detached properly. 716 noSuggestionsContainer.detach(); 717 718 // clean suggestions if any 719 $('.results-found').remove(); 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 supported 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().first().removeClass(that.classes.selected); 871 that.selectedIndex = -1; 872 that.el.val(that.currentValue); 873 that.findBestHint(); 874 return; 875 } 876 877 that.adjustScroll(that.selectedIndex - 1); 878 }, 879 880 moveDown: function () { 881 var that = this; 882 883 if (that.selectedIndex === (that.suggestions.length - 1)) { 884 return; 885 } 886 887 that.adjustScroll(that.selectedIndex + 1); 888 }, 889 890 adjustScroll: function (index) { 891 var that = this, 892 activeItem = that.activate(index); 893 894 if (!activeItem) { 895 return; 896 } 897 898 var offsetTop, 899 upperBound, 900 lowerBound, 901 heightDelta = $(activeItem).outerHeight(); 902 903 offsetTop = activeItem.offsetTop; 904 upperBound = $(that.suggestionsContainer).scrollTop(); 905 lowerBound = upperBound + that.options.maxHeight - heightDelta; 906 907 if (offsetTop < upperBound) { 908 $(that.suggestionsContainer).scrollTop(offsetTop); 909 } else if (offsetTop > lowerBound) { 910 $(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta); 911 } 912 913 if (!that.options.preserveInput) { 914 that.el.val(that.getValue(that.suggestions[index].value)); 915 } 916 that.signalHint(null); 917 }, 918 919 onSelect: function (index) { 920 var that = this, 921 onSelectCallback = that.options.onSelect, 922 suggestion = that.suggestions[index]; 923 924 that.currentValue = that.getValue(suggestion.value); 925 926 if (that.currentValue !== that.el.val() && !that.options.preserveInput) { 927 that.el.val(that.currentValue); 928 } 929 930 that.signalHint(null); 931 that.suggestions = []; 932 that.selection = suggestion; 933 934 if ($.isFunction(onSelectCallback)) { 935 onSelectCallback.call(that.element, suggestion); 936 } 937 }, 938 939 getValue: function (value) { 940 var that = this, 941 delimiter = that.options.delimiter, 942 currentValue, 943 parts; 944 945 if (!delimiter) { 946 return value; 947 } 948 949 currentValue = that.currentValue; 950 parts = currentValue.split(delimiter); 951 952 if (parts.length === 1) { 953 return value; 954 } 955 956 return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value; 957 }, 958 959 dispose: function () { 960 var that = this; 961 that.el.off('.autocomplete').removeData('autocomplete'); 962 $(window).off('resize.autocomplete', that.fixPositionCapture); 963 $(that.suggestionsContainer).remove(); 964 } 965 }; 966 967 // Create chainable jQuery plugin: 968 $.fn.devbridgeAutocomplete = function (options, args) { 969 var dataKey = 'autocomplete'; 970 // If function invoked without argument return 971 // instance of the first matched element: 972 if (!arguments.length) { 973 return this.first().data(dataKey); 974 } 975 976 return this.each(function () { 977 var inputElement = $(this), 978 instance = inputElement.data(dataKey); 979 980 if (typeof options === 'string') { 981 if (instance && typeof instance[options] === 'function') { 982 instance[options](args); 983 } 984 } else { 985 // If instance already exists, destroy it: 986 if (instance && instance.dispose) { 987 instance.dispose(); 988 } 989 instance = new Autocomplete(this, options); 990 inputElement.data(dataKey, instance); 991 } 992 }); 993 }; 994 995 // Don't overwrite if it already exists 996 if (!$.fn.autocomplete) { 997 $.fn.autocomplete = $.fn.devbridgeAutocomplete; 998 } 999}));
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.