PageSourceSearch

https://www.scb.se/js/v-86385/modules/external/jquery-autocomplete.js

js scb.se collected 2026-10-01 08:32:44 UTC 34,373 bytes, 1,006 lines download raw bytes

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, '&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            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.