PageSourceSearch

https://fundes.edu.co/public/js/jquery.autocomplete.js

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

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.