1/** 2* Ajax Autocomplete for jQuery, version %version% 3* (c) 2015 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, plusplus: true, vars: 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 function Autocomplete(el, options) { 55 var noop = function () { }, 56 that = this, 57 defaults = { 58 ajaxSettings: {}, 59 autoSelectFirst: false, 60 appendTo: document.body, 61 serviceUrl: null, 62 lookup: null, 63 onSelect: null, 64 width: 'auto', 65 minChars: 1, 66 maxHeight: 300, 67 deferRequestBy: 0, 68 params: {}, 69 formatResult: Autocomplete.formatResult, 70 delimiter: null, 71 zIndex: 9999, 72 type: 'GET', 73 noCache: false, 74 onSearchStart: noop, 75 onSearchComplete: noop, 76 onSearchError: noop, 77 preserveInput: false, 78 containerClass: 'autocomplete-suggestions', 79 tabDisabled: false, 80 dataType: 'text', 81 currentRequest: null, 82 triggerSelectOnValidInput: true, 83 preventBadQueries: true, 84 lookupFilter: function (suggestion, originalQuery, queryLowerCase) { 85 return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1; 86 }, 87 paramName: 'query', 88 transformResult: function (response) { 89 return typeof response === 'string' ? $.parseJSON(response) : response; 90 }, 91 showNoSuggestionNotice: false, 92 noSuggestionNotice: 'No results', 93 orientation: 'bottom', 94 forceFixPosition: false 95 }; 96 97 // Shared variables: 98 that.element = el; 99 that.el = $(el); 100 that.suggestions = []; 101 that.badQueries = []; 102 that.selectedIndex = -1; 103 that.currentValue = that.element.value; 104 that.intervalId = 0; 105 that.cachedResponse = {}; 106 that.onChangeInterval = null; 107 that.onChange = null; 108 that.isLocal = false; 109 that.suggestionsContainer = null; 110 that.noSuggestionsContainer = null; 111 that.options = $.extend({}, defaults, options); 112 that.classes = { 113 selected: 'autocomplete-selected', 114 suggestion: 'autocomplete-suggestion' 115 }; 116 that.hint = null; 117 that.hintValue = ''; 118 that.selection = null; 119 120 // Initialize and set options: 121 that.initialize(); 122 that.setOptions(options); 123 } 124 125 Autocomplete.utils = utils; 126 127 $.Autocomplete = Autocomplete; 128 129 Autocomplete.formatResult = function (suggestion, currentValue) { 130 var pattern = '(' + utils.escapeRegExChars(currentValue) + ')'; 131 132 return suggestion.value 133 .replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>') 134 .replace(/&/g, '&') 135 .replace(/</g, '<') 136 .replace(/>/g, '>') 137 .replace(/"/g, '"') 138 .replace(/<(\/?strong)>/g, '<$1>'); 139 }; 140 141 Autocomplete.prototype = { 142 143 killerFn: null, 144 145 initialize: function () { 146 var that = this, 147 suggestionSelector = '.' + that.classes.suggestion, 148 selected = that.classes.selected, 149 options = that.options, 150 container; 151 152 // Remove autocomplete attribute to prevent native suggestions: 153 that.element.setAttribute('autocomplete', 'off'); 154 155 that.killerFn = function (e) { 156 if ($(e.target).closest('.' + that.options.containerClass).length === 0) { 157 that.killSuggestions(); 158 that.disableKillerFn(); 159 } 160 }; 161 162 // html() deals with many types: htmlString or Element or Array or jQuery
163 that.noSuggestionsContainer = $('<div class="autocomplete-no-suggestion"></div>') 164 .html(this.options.noSuggestionNotice).get(0); 165 166 that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass); 167 168 container = $(that.suggestionsContainer); 169 170 container.appendTo(options.appendTo); 171 172 // Only set width if it was provided: 173 if (options.width !== 'auto') { 174 container.width(options.width); 175 } 176 177 // Listen for mouse over event on suggestions list: 178 container.on('mouseover.autocomplete', suggestionSelector, function () { 179 that.activate($(this).data('index')); 180 }); 181 182 // Deselect active element when mouse leaves suggestions container: 183 container.on('mouseout.autocomplete', function () { 184 that.selectedIndex = -1; 185 container.children('.' + selected).removeClass(selected); 186 }); 187 188 // Listen for click event on suggestions list: 189 container.on('click.autocomplete', suggestionSelector, function () { 190 that.select($(this).data('index')); 191 }); 192 193 that.fixPositionCapture = function () { 194 if (that.visible) { 195 that.fixPosition(); 196 } 197 }; 198 199 $(window).on('resize.autocomplete', that.fixPositionCapture); 200 201 that.el.on('keydown.autocomplete', function (e) { that.onKeyPress(e); }); 202 that.el.on('keyup.autocomplete', function (e) { that.onKeyUp(e); }); 203 that.el.on('blur.autocomplete', function () { that.onBlur(); }); 204 that.el.on('focus.autocomplete', function () { that.onFocus(); }); 205 that.el.on('change.autocomplete', function (e) { that.onKeyUp(e); }); 206 that.el.on('input.autocomplete', function (e) { that.onKeyUp(e); }); 207 }, 208 209 onFocus: function () { 210 var that = this; 211 that.fixPosition(); 212 if (that.options.minChars === 0 && that.el.val().length === 0) { 213 that.onValueChange(); 214 } 215 }, 216 217 onBlur: function () { 218 this.enableKillerFn(); 219 }, 220 221 abortAjax: function () { 222 var that = this; 223 if (that.currentRequest) { 224 that.currentRequest.abort(); 225 that.currentRequest = null; 226 } 227 }, 228 229 setOptions: function (suppliedOptions) { 230 var that = this, 231 options = that.options; 232 233 $.extend(options, suppliedOptions); 234 235 that.isLocal = $.isArray(options.lookup); 236 237 if (that.isLocal) { 238 options.lookup = that.verifySuggestionsFormat(options.lookup); 239 } 240 241 options.orientation = that.validateOrientation(options.orientation, 'bottom'); 242 243 // Adjust height, width and z-index: 244 $(that.suggestionsContainer).css({ 245 'max-height': options.maxHeight + 'px', 246 'width': options.width + 'px', 247 'z-index': options.zIndex 248 }); 249 }, 250 251 252 clearCache: function () { 253 this.cachedResponse = {}; 254 this.badQueries = []; 255 }, 256 257 clear: function () { 258 this.clearCache(); 259 this.currentValue = ''; 260 this.suggestions = []; 261 }, 262 263 disable: function () { 264 var that = this; 265 that.disabled = true; 266 clearInterval(that.onChangeInterval); 267 that.abortAjax(); 268 }, 269 270 enable: function () { 271 this.disabled = false; 272 }, 273 274 fixPosition: function () { 275 // Use only when container has already its content 276 277 var that = this, 278 $container = $(that.suggestionsContainer), 279 containerParent = $container.parent().get(0); 280 // Fix position automatically when appended to body. 281 // In other cases force parameter must be given. 282 if (containerParent !== document.body && !that.options.forceFixPosition) { 283 return; 284 } 285 286 // Choose orientation 287 var orientation = that.options.orientation, 288 containerHeight = $container.outerHeight(), 289 height = that.el.outerHeight(), 290 offset = that.el.offset(), 291 styles = { 'top': offset.top, 'left': offset.left }; 292 293 if (orientation === 'auto') { 294 var viewPortHeight = $(window).height(), 295 scrollTop = $(window).scrollTop(), 296 topOverflow = -scrollTop + offset.top - containerHeight, 297 bottomOverflow = scrollTop + viewPortHeight - (offset.top + height + containerHeight); 298 299 orientation = (Math.max(topOverflow, bottomOverflow) === topOverflow) ? 'top' : 'bottom'; 300 } 301 302 if (orientation === 'top') { 303 styles.top += -containerHeight; 304 } else { 305 styles.top += height; 306 } 307 308 // If container is not positioned to body, 309 // correct its position using offset parent offset 310 if(containerParent !== document.body) { 311 var opacity = $container.css('opacity'), 312 parentOffsetDiff; 313 314 if (!that.visible){ 315 $container.css('opacity', 0).show(); 316 } 317 318 parentOffsetDiff = $container.offsetParent().offset(); 319 styles.top -= parentOffsetDiff.top; 320 styles.left -= parentOffsetDiff.left; 321 322 if (!that.visible){ 323 $container.css('opacity', opacity).hide(); 324 } 325 } 326 327 // -2px to account for suggestions border. 328 if (that.options.width === 'auto') { 329 //styles.width = (that.el.outerWidth() - 2) + 'px'; 330 styles.width = (that.el.outerWidth() + 23) + 'px'; 331 } 332 333 $container.css(styles); 334 }, 335 336 enableKillerFn: function () { 337 var that = this; 338 $(document).on('click.autocomplete', that.killerFn); 339 }, 340 341 disableKillerFn: function () { 342 var that = this; 343 $(document).off('click.autocomplete', that.killerFn); 344 }, 345 346 killSuggestions: function () { 347 var that = this; 348 that.stopKillSuggestions(); 349 that.intervalId = window.setInterval(function () { 350 that.hide(); 351 that.stopKillSuggestions(); 352 }, 50); 353 }, 354 355 stopKillSuggestions: function () { 356 window.clearInterval(this.intervalId); 357 }, 358 359 isCursorAtEnd: function () { 360 var that = this, 361 valLength = that.el.val().length, 362 selectionStart = that.element.selectionStart, 363 range; 364 365 if (typeof selectionStart === 'number') { 366 return selectionStart === valLength; 367 } 368 if (document.selection) { 369 range = document.selection.createRange(); 370 range.moveStart('character', -valLength); 371 return valLength === range.text.length; 372 } 373 return true; 374 }, 375 376 onKeyPress: function (e) { 377 var that = this; 378 379 // If suggestions are hidden and user presses arrow down, display suggestions: 380 if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) { 381 that.suggest(); 382 return; 383 } 384 385 if (that.disabled || !that.visible) { 386 return; 387 } 388 389 switch (e.which) { 390 case keys.ESC: 391 that.el.val(that.currentValue); 392 that.hide(); 393 break; 394 case keys.RIGHT: 395 if (that.hint && that.options.onHint && that.isCursorAtEnd()) { 396 that.selectHint(); 397 break; 398 } 399 return; 400 case keys.TAB: 401 if (that.hint && that.options.onHint) { 402 that.selectHint(); 403 return; 404 } 405 if (that.selectedIndex === -1) { 406 that.hide(); 407 return; 408 } 409 that.select(that.selectedIndex); 410 if (that.options.tabDisabled === false) { 411 return; 412 } 413 break; 414 case keys.RETURN: 415 if (that.selectedIndex === -1) { 416 that.hide(); 417 return; 418 } 419 that.select(that.selectedIndex); 420 break; 421 case keys.UP: 422 that.moveUp(); 423 break; 424 case keys.DOWN: 425 that.moveDown(); 426 break; 427 default: 428 return; 429 } 430 431 // Cancel event if function did not return: 432 e.stopImmediatePropagation(); 433 e.preventDefault(); 434 }, 435 436 onKeyUp: function (e) { 437 var that = this; 438 439 if (that.disabled) { 440 return; 441 } 442 443 switch (e.which) { 444 case keys.UP: 445 case keys.DOWN: 446 return; 447 } 448 449 clearInterval(that.onChangeInterval); 450 451 if (that.currentValue !== that.el.val()) { 452 that.findBestHint(); 453 if (that.options.deferRequestBy > 0) { 454 // Defer lookup in case when value changes very quickly: 455 that.onChangeInterval = setInterval(function () { 456 that.onValueChange(); 457 }, that.options.deferRequestBy); 458 } else { 459 that.onValueChange(); 460 } 461 } 462 }, 463 464 onValueChange: function () { 465 var that = this, 466 options = that.options, 467 value = that.el.val(), 468 query = that.getQuery(value); 469 470 if (that.selection && that.currentValue !== query) { 471 that.selection = null; 472 (options.onInvalidateSelection || $.noop).call(that.element); 473 } 474 475 clearInterval(that.onChangeInterval); 476 that.currentValue = value; 477 that.selectedIndex = -1; 478 479 // Check existing suggestion for the match before proceeding: 480 if (options.triggerSelectOnValidInput && that.isExactMatch(query)) { 481 that.select(0); 482 return; 483 } 484 485 if (query.length < options.minChars) { 486 that.hide(); 487 } else { 488 that.getSuggestions(query); 489 } 490 }, 491 492 isExactMatch: function (query) { 493 var suggestions = this.suggestions; 494 495 return (suggestions.length === 1 && suggestions[0].value.toLowerCase() === query.toLowerCase()); 496 }, 497 498 getQuery: function (value) { 499 var delimiter = this.options.delimiter, 500 parts; 501 502 if (!delimiter) { 503 return value; 504 } 505 parts = value.split(delimiter); 506 return $.trim(parts[parts.length - 1]); 507 }, 508 509 getSuggestionsLocal: function (query) { 510 var that = this, 511 options = that.options, 512 queryLowerCase = query.toLowerCase(), 513 filter = options.lookupFilter, 514 limit = parseInt(options.lookupLimit, 10), 515 data; 516 517 data = { 518 suggestions: $.grep(options.lookup, function (suggestion) { 519 return filter(suggestion, query, queryLowerCase); 520 }) 521 }; 522 523 if (limit && data.suggestions.length > limit) { 524 data.suggestions = data.suggestions.slice(0, limit); 525 } 526 527 return data; 528 }, 529 530 getSuggestions: function (q) { 531 var response, 532 that = this, 533 options = that.options, 534 serviceUrl = options.serviceUrl, 535 params, 536 cacheKey, 537 ajaxSettings; 538 539 options.params[options.paramName] = q; 540 params = options.ignoreParams ? null : options.params; 541 542 if (options.onSearchStart.call(that.element, options.params) === false) { 543 return; 544 } 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 && $.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 clearInterval(that.onChangeInterval); 623 $(that.suggestionsContainer).hide(); 624 that.signalHint(null); 625 }, 626 627 suggest: function () { 628 if (this.suggestions.length === 0) { 629 if (this.options.showNoSuggestionNotice) { 630
630 this.noSuggestions(); 631 } else { 632 this.hide(); 633 } 634 return; 635 } 636 637 var that = this, 638 options = that.options, 639 groupBy = options.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 '<div class="autocomplete-group"><strong>' + category + '</strong></div>'; 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) + '</div>'; 673 }); 674 675 if (Modernizr.touch) 676 html += '<div class="' + className + ' text-center"><a href="javascript:;" class="btn btn-danger form-control" onclick="$([data-toggle=typeahead]).autocomplete(\'hide\')">Kapat</a></div>'; 677 678 this.adjustContainerWidth(); 679 680 noSuggestionsContainer.detach(); 681 container.html(html); 682 683 if ($.isFunction(beforeRender)) { 684 beforeRender.call(that.element, container); 685 } 686 687 that.fixPosition(); 688 container.show(); 689 690 // Select first value by default: 691 if (options.autoSelectFirst) { 692 that.selectedIndex = 0; 693 container.scrollTop(0); 694 container.children('.' + className).first().addClass(classSelected); 695 } 696 697 that.visible = true; 698 that.findBestHint(); 699 }, 700 701 noSuggestions: function() { 702 var that = this, 703 container = $(that.suggestionsContainer), 704 noSuggestionsContainer = $(that.noSuggestionsContainer); 705 706 this.adjustContainerWidth(); 707 708 // Some explicit steps. Be careful here as it easy to get 709 // noSuggestionsContainer removed from DOM if not detached properly. 710 noSuggestionsContainer.detach(); 711 container.empty(); // clean suggestions if any 712 container.append(noSuggestionsContainer); 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 // -2px to account for suggestions border. 730 if (options.width === 'auto') { 731 width = that.el.outerWidth() - 2; 732 container.width(width > 0 ? width : 300); 733 } 734 }, 735 736 findBestHint: function () { 737 var that = this, 738 value = that.el.val().toLowerCase(), 739 bestMatch = null; 740 741 if (!value) { 742 return; 743 } 744 745 $.each(that.suggestions, function (i, suggestion) { 746 var foundMatch = suggestion.value.toLowerCase().indexOf(value) === 0; 747 if (foundMatch) { 748 bestMatch = suggestion; 749 } 750 return !foundMatch; 751 }); 752 753 that.signalHint(bestMatch); 754 }, 755 756 signalHint: function (suggestion) { 757 var hintValue = '', 758 that = this; 759 if (suggestion) { 760 hintValue = that.currentValue + suggestion.value.substr(that.currentValue.length); 761 } 762 if (that.hintValue !== hintValue) { 763 that.hintValue = hintValue; 764 that.hint = suggestion; 765 (this.options.onHint || $.noop)(hintValue); 766 } 767 }, 768 769 verifySuggestionsFormat: function (suggestions) { 770 // If suggestions is string array, convert them to supp
770orted format: 771 if (suggestions.length && typeof suggestions[0] === 'string') { 772 return $.map(suggestions, function (value) { 773 return { value: value, data: null }; 774 }); 775 } 776 777 return suggestions; 778 }, 779 780 validateOrientation: function(orientation, fallback) { 781 orientation = $.trim(orientation || '').toLowerCase(); 782 783 if($.inArray(orientation, ['auto', 'bottom', 'top']) === -1){ 784 orientation = fallback; 785 } 786 787 return orientation; 788 }, 789 790 processResponse: function (result, originalQuery, cacheKey) { 791 var that = this, 792 options = that.options; 793 794 result.suggestions = that.verifySuggestionsFormat(result.suggestions); 795 796 // Cache results if cache is not disabled: 797 if (!options.noCache) { 798 that.cachedResponse[cacheKey] = result; 799 if (options.preventBadQueries && result.suggestions.length === 0) { 800 that.badQueries.push(originalQuery); 801 } 802 } 803 804 // Return if originalQuery is not matching current query: 805 if (originalQuery !== that.getQuery(that.currentValue)) { 806 return; 807 } 808 809 that.suggestions = result.suggestions; 810 that.suggest(); 811 }, 812 813 activate: function (index) { 814 var that = this, 815 activeItem, 816 selected = that.classes.selected, 817 container = $(that.suggestionsContainer), 818 children = container.find('.' + that.classes.suggestion); 819 820 container.find('.' + selected).removeClass(selected); 821 822 that.selectedIndex = index; 823 824 if (that.selectedIndex !== -1 && children.length > that.selectedIndex) { 825 activeItem = children.get(that.selectedIndex); 826 $(activeItem).addClass(selected); 827 return activeItem; 828 } 829 830 return null; 831 }, 832 833 selectHint: function () { 834 var that = this, 835 i = $.inArray(that.hint, that.suggestions); 836 837 that.select(i); 838 }, 839 840 select: function (i) { 841 var that = this; 842 that.hide(); 843 that.onSelect(i); 844 }, 845 846 moveUp: function () { 847 var that = this; 848 849 if (that.selectedIndex === -1) { 850 return; 851 } 852 853 if (that.selectedIndex === 0) { 854 $(that.suggestionsContainer).children().first().removeClass(that.classes.selected); 855 that.selectedIndex = -1; 856 that.el.val(that.currentValue); 857 that.findBestHint(); 858 return; 859 } 860 861 that.adjustScroll(that.selectedIndex - 1); 862 }, 863 864 moveDown: function () { 865 var that = this; 866 867 if (that.selectedIndex === (that.suggestions.length - 1)) { 868 return; 869 } 870 871 that.adjustScroll(that.selectedIndex + 1); 872 }, 873 874 adjustScroll: function (index) { 875 var that = this, 876 activeItem = that.activate(index); 877 878 if (!activeItem) { 879 return; 880 } 881 882 var offsetTop, 883 upperBound, 884 lowerBound, 885 heightDelta = $(activeItem).outerHeight(); 886 887 offsetTop = activeItem.offsetTop; 888 upperBound = $(that.suggestionsContainer).scrollTop(); 889 lowerBound = upperBound + that.options.maxHeight - heightDelta; 890 891 if (offsetTop < upperBound) { 892 $(that.suggestionsContainer).scrollTop(offsetTop); 893 } else if (offsetTop > lowerBound) { 894 $(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta); 895 } 896 897 if (!that.options.preserveInput) { 898 that.el.val(that.getValue(that.suggestions[index].value)); 899 } 900 that.signalHint(null); 901 }, 902 903 onSelect: function (index) { 904 var that = this, 905 onSelectCallback = that.options.onSelect, 906 suggestion = that.suggestions[index]; 907 908 that.currentValue = that.getValue(suggestion.value); 909 910 if (that.currentValue !== that.el.val() && !that.options.preserveInput) { 911 that.el.val(that.currentValue); 912 } 913 914 that.signalHint(null); 915 that.suggestions = []; 916 that.selection = suggestion; 917 918 if ($.isFunction(onSelectCallback)) { 919 onSelectCallback.call(that.element, suggestion); 920 } 921 }, 922 923 getValue: function (value) { 924 var that = this, 925 delimiter = that.options.delimiter, 926 currentValue, 927 parts; 928 929 if (!delimiter) { 930 return value; 931 } 932 933 currentValue = that.currentValue; 934 parts = currentValue.split(delimiter); 935 936 if (parts.length === 1) { 937 return value; 938 } 939 940 return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value; 941 }, 942 943 dispose: function () { 944 var that = this; 945 that.el.off('.autocomplete').removeData('autocomplete'); 946 that.disableKillerFn(); 947 $(window).off('resize.autocomplete', that.fixPositionCapture); 948 $(that.suggestionsContainer).remove(); 949 } 950 }; 951 952 // Create chainable jQuery plugin: 953 $.fn.autocomplete = $.fn.devbridgeAutocomplete = function (options, args) { 954 var dataKey = 'autocomplete'; 955 // If function invoked without argument return 956 // instance of the first matched element: 957 if (arguments.length === 0) { 958 return this.first().data(dataKey); 959 } 960 961 return this.each(function () { 962 var inputElement = $(this), 963 instance = inputElement.data(dataKey); 964 965 if (typeof options === 'string') { 966 if (instance && typeof instance[options] === 'function') { 967 instance[options](args); 968 } 969 } else { 970 // If instance already exists, destroy it: 971 if (instance && instance.dispose) { 972 instance.dispose(); 973 } 974 instance = new Autocomplete(this, options); 975 inputElement.data(dataKey, instance); 976 } 977 }); 978 }; 979}));
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.