PageSourceSearch

https://www.timelash.com/js/jquery-auto-complete.min.js

js timelash.com collected 2026-10-02 11:42:50 UTC 33,782 bytes, 1,014 lines download raw bytes

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

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.