PageSourceSearch

https://www.west-dunbarton.gov.uk/scripts/libs/jquery.autocomplete.js?d=639255793602714753

js west-dunbarton.gov.uk collected 2026-09-24 09:34:45 UTC 34,236 bytes, 997 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.