JAVASCRIPT   8

jquery.autocomplete.js

Guest on 14th September 2021 08:46:24 PM

  1. /**
  2.  
  3. *  Ajax Autocomplete for jQuery, version 1.2.27
  4.  
  5. *  (c)  Tomas Kirda
  6.  
  7. *
  8.  
  9. *  Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
  10.  
  11. *  For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete
  12.  
  13. */
  14.  
  15.  
  16.  
  17. /*jslint  browser: true, white: true, plusplus: true, vars: true */
  18.  
  19. /*global define, window, document, jQuery, exports, require */
  20.  
  21.  
  22.  
  23. // Expose plugin as an AMD module if AMD loader is present:
  24.  
  25. (function (factory) {
  26.  
  27.     'use strict';
  28.  
  29.     if (typeof define === 'function' && define.amd) {
  30.  
  31.         // AMD. Register as an anonymous module.
  32.  
  33.         define(['jquery'], factory);
  34.  
  35.     } else if (typeof exports === 'object' && typeof require === 'function') {
  36.  
  37.         // Browserify
  38.  
  39.         factory(require('jquery'));
  40.  
  41.     } else {
  42.  
  43.         // Browser globals
  44.  
  45.         factory(jQuery);
  46.  
  47.     }
  48.  
  49. }(function ($) {
  50.  
  51.     'use strict';
  52.  
  53.  
  54.  
  55.     var
  56.  
  57.         utils = (function () {
  58.  
  59.             return {
  60.  
  61.                 escapeRegExChars: function (value) {
  62.  
  63.                     return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
  64.  
  65.                 },
  66.  
  67.                 createNode: function (containerClass) {
  68.  
  69.                     var div = document.createElement('div');
  70.  
  71.                     div.className = containerClass;
  72.  
  73.                     div.style.position = 'absolute';
  74.  
  75.                     div.style.display = 'none';
  76.  
  77.                     return div;
  78.  
  79.                 }
  80.  
  81.             };
  82.  
  83.         }()),
  84.  
  85.  
  86.  
  87.         keys = {
  88.  
  89.             ESC: 27,
  90.  
  91.             TAB: 9,
  92.  
  93.             RETURN: 13,
  94.  
  95.             LEFT: 37,
  96.  
  97.             UP: 38,
  98.  
  99.             RIGHT: 39,
  100.  
  101.             DOWN: 40
  102.  
  103.         };
  104.  
  105.  
  106.  
  107.     function Autocomplete(el, options) {
  108.  
  109.         var noop = function () { },
  110.  
  111.             that = this,
  112.  
  113.             defaults = {
  114.  
  115.                 ajaxSettings: {},
  116.  
  117.                 autoSelectFirst: false,
  118.  
  119.                 appendTo: document.body,
  120.  
  121.                 serviceUrl: null,
  122.  
  123.                 lookup: null,
  124.  
  125.                 onSelect: null,
  126.  
  127.                 width: 'auto',
  128.  
  129.                 minChars: 1,
  130.  
  131.                 maxHeight: 300,
  132.  
  133.                 deferRequestBy: 0,
  134.  
  135.                 params: {},
  136.  
  137.                 formatResult: Autocomplete.formatResult,
  138.  
  139.                 delimiter: null,
  140.  
  141.                 zIndex: 9999,
  142.  
  143.                 type: 'GET',
  144.  
  145.                 noCache: false,
  146.  
  147.                 onSearchStart: noop,
  148.  
  149.                 onSearchComplete: noop,
  150.  
  151.                 onSearchError: noop,
  152.  
  153.                 preserveInput: false,
  154.  
  155.                 containerClass: 'autocomplete-suggestions',
  156.  
  157.                 tabDisabled: false,
  158.  
  159.                 dataType: 'text',
  160.  
  161.                 currentRequest: null,
  162.  
  163.                 triggerSelectOnValidInput: true,
  164.  
  165.                 preventBadQueries: true,
  166.  
  167.                 lookupFilter: function (suggestion, originalQuery, queryLowerCase) {
  168.  
  169.                     return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1;
  170.  
  171.                 },
  172.  
  173.                 paramName: 'query',
  174.  
  175.                 transformResult: function (response) {
  176.  
  177.                     return typeof response === 'string' ? $.parseJSON(response) : response;
  178.  
  179.                 },
  180.  
  181.                 showNoSuggestionNotice: false,
  182.  
  183.                 noSuggestionNotice: 'No results',
  184.  
  185.                 orientation: 'bottom',
  186.  
  187.                 forceFixPosition: false
  188.  
  189.             };
  190.  
  191.  
  192.  
  193.         // Shared variables:
  194.  
  195.         that.element = el;
  196.  
  197.         that.el = $(el);
  198.  
  199.         that.suggestions = [];
  200.  
  201.         that.badQueries = [];
  202.  
  203.         that.selectedIndex = -1;
  204.  
  205.         that.currentValue = that.element.value;
  206.  
  207.         that.intervalId = 0;
  208.  
  209.         that.cachedResponse = {};
  210.  
  211.         that.onChangeInterval = null;
  212.  
  213.         that.onChange = null;
  214.  
  215.         that.isLocal = false;
  216.  
  217.         that.suggestionsContainer = null;
  218.  
  219.         that.noSuggestionsContainer = null;
  220.  
  221.         that.options = $.extend({}, defaults, options);
  222.  
  223.         that.classes = {
  224.  
  225.             selected: 'autocomplete-selected',
  226.  
  227.             suggestion: 'autocomplete-suggestion'
  228.  
  229.         };
  230.  
  231.         that.hint = null;
  232.  
  233.         that.hintValue = '';
  234.  
  235.         that.selection = null;
  236.  
  237.  
  238.  
  239.         // Initialize and set options:
  240.  
  241.         that.initialize();
  242.  
  243.         that.setOptions(options);
  244.  
  245.     }
  246.  
  247.  
  248.  
  249.     Autocomplete.utils = utils;
  250.  
  251.  
  252.  
  253.     $.Autocomplete = Autocomplete;
  254.  
  255.  
  256.  
  257.     Autocomplete.formatResult = function (suggestion, currentValue) {
  258.  
  259.         // Do not replace anything if there current value is empty
  260.  
  261.         if (!currentValue) {
  262.  
  263.             return suggestion.value;
  264.  
  265.         }
  266.  
  267.  
  268.  
  269.         var pattern = '(' + utils.escapeRegExChars(currentValue) + ')';
  270.  
  271.  
  272.  
  273.         return suggestion.value
  274.  
  275.             .replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>')
  276.  
  277.             .replace(/&/g, '&amp;')
  278.  
  279.             .replace(/</g, '&lt;')
  280.  
  281.             .replace(/>/g, '&gt;')
  282.  
  283.             .replace(/"/g, '&quot;')
  284.  
  285.             .replace(/&lt;(\/?strong)&gt;/g, '<$1>');
  286.  
  287.     };
  288.  
  289.  
  290.  
  291.     Autocomplete.prototype = {
  292.  
  293.  
  294.  
  295.         killerFn: null,
  296.  
  297.  
  298.  
  299.         initialize: function () {
  300.  
  301.             var that = this,
  302.  
  303.                 suggestionSelector = '.' + that.classes.suggestion,
  304.  
  305.                 selected = that.classes.selected,
  306.  
  307.                 options = that.options,
  308.  
  309.                 container;
  310.  
  311.  
  312.  
  313.             // Remove autocomplete attribute to prevent native suggestions:
  314.  
  315.             that.element.setAttribute('autocomplete', 'off');
  316.  
  317.  
  318.  
  319.             that.killerFn = function (e) {
  320.  
  321.                 if ($(e.target).closest('.' + that.options.containerClass).length === 0) {
  322.  
  323.                     that.killSuggestions();
  324.  
  325.                     that.disableKillerFn();
  326.  
  327.                 }
  328.  
  329.             };
  330.  
  331.  
  332.  
  333.             // html() deals with many types: htmlString or Element or Array or jQuery
  334.  
  335.             that.noSuggestionsContainer = $('<div class="autocomplete-no-suggestion"></div>')
  336.  
  337.                                           .html(this.options.noSuggestionNotice).get(0);
  338.  
  339.  
  340.  
  341.             that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass);
  342.  
  343.  
  344.  
  345.             container = $(that.suggestionsContainer);
  346.  
  347.  
  348.  
  349.             container.appendTo(options.appendTo);
  350.  
  351.  
  352.  
  353.             // Only set width if it was provided:
  354.  
  355.             if (options.width !== 'auto') {
  356.  
  357.                 container.width(options.width);
  358.  
  359.             }
  360.  
  361.  
  362.  
  363.             // Listen for mouse over event on suggestions list:
  364.  
  365.             container.on('mouseover.autocomplete', suggestionSelector, function () {
  366.  
  367.                 that.activate($(this).data('index'));
  368.  
  369.             });
  370.  
  371.  
  372.  
  373.             // Deselect active element when mouse leaves suggestions container:
  374.  
  375.             container.on('mouseout.autocomplete', function () {
  376.  
  377.                 that.selectedIndex = -1;
  378.  
  379.                 container.children('.' + selected).removeClass(selected);
  380.  
  381.             });
  382.  
  383.  
  384.  
  385.             // Listen for click event on suggestions list:
  386.  
  387.             container.on('click.autocomplete', suggestionSelector, function () {
  388.  
  389.                 that.select($(this).data('index'));
  390.  
  391.             });
  392.  
  393.  
  394.  
  395.             that.fixPositionCapture = function () {
  396.  
  397.                 if (that.visible) {
  398.  
  399.                     that.fixPosition();
  400.  
  401.                 }
  402.  
  403.             };
  404.  
  405.  
  406.  
  407.             $(window).on('resize.autocomplete', that.fixPositionCapture);
  408.  
  409.  
  410.  
  411.             that.el.on('keydown.autocomplete', function (e) { that.onKeyPress(e); });
  412.  
  413.             that.el.on('keyup.autocomplete', function (e) { that.onKeyUp(e); });
  414.  
  415.             that.el.on('blur.autocomplete', function () { that.onBlur(); });
  416.  
  417.             that.el.on('focus.autocomplete', function () { that.onFocus(); });
  418.  
  419.             that.el.on('change.autocomplete', function (e) { that.onKeyUp(e); });
  420.  
  421.             that.el.on('input.autocomplete', function (e) { that.onKeyUp(e); });
  422.  
  423.         },
  424.  
  425.  
  426.  
  427.         onFocus: function () {
  428.  
  429.             var that = this;
  430.  
  431.  
  432.  
  433.             that.fixPosition();
  434.  
  435.  
  436.  
  437.             if (that.el.val().length >= that.options.minChars) {
  438.  
  439.                 that.onValueChange();
  440.  
  441.             }
  442.  
  443.         },
  444.  
  445.  
  446.  
  447.         onBlur: function () {
  448.  
  449.             this.enableKillerFn();
  450.  
  451.         },
  452.  
  453.  
  454.  
  455.         abortAjax: function () {
  456.  
  457.             var that = this;
  458.  
  459.             if (that.currentRequest) {
  460.  
  461.                 that.currentRequest.abort();
  462.  
  463.                 that.currentRequest = null;
  464.  
  465.             }
  466.  
  467.         },
  468.  
  469.  
  470.  
  471.         setOptions: function (suppliedOptions) {
  472.  
  473.             var that = this,
  474.  
  475.                 options = that.options;
  476.  
  477.  
  478.  
  479.             $.extend(options, suppliedOptions);
  480.  
  481.  
  482.  
  483.             that.isLocal = $.isArray(options.lookup);
  484.  
  485.  
  486.  
  487.             if (that.isLocal) {
  488.  
  489.                 options.lookup = that.verifySuggestionsFormat(options.lookup);
  490.  
  491.             }
  492.  
  493.  
  494.  
  495.             options.orientation = that.validateOrientation(options.orientation, 'bottom');
  496.  
  497.  
  498.  
  499.             // Adjust height, width and z-index:
  500.  
  501.             $(that.suggestionsContainer).css({
  502.  
  503.                 'max-height': options.maxHeight + 'px',
  504.  
  505.                 'width': options.width + 'px',
  506.  
  507.                 'z-index': options.zIndex
  508.  
  509.             });
  510.  
  511.         },
  512.  
  513.  
  514.  
  515.  
  516.  
  517.         clearCache: function () {
  518.  
  519.             this.cachedResponse = {};
  520.  
  521.             this.badQueries = [];
  522.  
  523.         },
  524.  
  525.  
  526.  
  527.         clear: function () {
  528.  
  529.             this.clearCache();
  530.  
  531.             this.currentValue = '';
  532.  
  533.             this.suggestions = [];
  534.  
  535.         },
  536.  
  537.  
  538.  
  539.         disable: function () {
  540.  
  541.             var that = this;
  542.  
  543.             that.disabled = true;
  544.  
  545.             clearInterval(that.onChangeInterval);
  546.  
  547.             that.abortAjax();
  548.  
  549.         },
  550.  
  551.  
  552.  
  553.         enable: function () {
  554.  
  555.             this.disabled = false;
  556.  
  557.         },
  558.  
  559.  
  560.  
  561.         fixPosition: function () {
  562.  
  563.             // Use only when container has already its content
  564.  
  565.  
  566.  
  567.             var that = this,
  568.  
  569.                 $container = $(that.suggestionsContainer),
  570.  
  571.                 containerParent = $container.parent().get(0);
  572.  
  573.             // Fix position automatically when appended to body.
  574.  
  575.             // In other cases force parameter must be given.
  576.  
  577.             if (containerParent !== document.body && !that.options.forceFixPosition) {
  578.  
  579.                 return;
  580.  
  581.             }
  582.  
  583.  
  584.  
  585.             // Choose orientation
  586.  
  587.             var orientation = that.options.orientation,
  588.  
  589.                 containerHeight = $container.outerHeight(),
  590.  
  591.                 height = that.el.outerHeight(),
  592.  
  593.                 offset = that.el.offset(),
  594.  
  595.                 styles = { 'top': offset.top, 'left': offset.left };
  596.  
  597.  
  598.  
  599.             if (orientation === 'auto') {
  600.  
  601.                 var viewPortHeight = $(window).height(),
  602.  
  603.                     scrollTop = $(window).scrollTop(),
  604.  
  605.                     topOverflow = -scrollTop + offset.top - containerHeight,
  606.  
  607.                     bottomOverflow = scrollTop + viewPortHeight - (offset.top + height + containerHeight);
  608.  
  609.  
  610.  
  611.                 orientation = (Math.max(topOverflow, bottomOverflow) === topOverflow) ? 'top' : 'bottom';
  612.  
  613.             }
  614.  
  615.  
  616.  
  617.             if (orientation === 'top') {
  618.  
  619.                 styles.top += -containerHeight;
  620.  
  621.             } else {
  622.  
  623.                 styles.top += height;
  624.  
  625.             }
  626.  
  627.  
  628.  
  629.             // If container is not positioned to body,
  630.  
  631.             // correct its position using offset parent offset
  632.  
  633.             if(containerParent !== document.body) {
  634.  
  635.                 var opacity = $container.css('opacity'),
  636.  
  637.                     parentOffsetDiff;
  638.  
  639.  
  640.  
  641.                     if (!that.visible){
  642.  
  643.                         $container.css('opacity', 0).show();
  644.  
  645.                     }
  646.  
  647.  
  648.  
  649.                 parentOffsetDiff = $container.offsetParent().offset();
  650.  
  651.                 styles.top -= parentOffsetDiff.top;
  652.  
  653.                 styles.left -= parentOffsetDiff.left;
  654.  
  655.  
  656.  
  657.                 if (!that.visible){
  658.  
  659.                     $container.css('opacity', opacity).hide();
  660.  
  661.                 }
  662.  
  663.             }
  664.  
  665.  
  666.  
  667.             // -2px to account for suggestions border.
  668.  
  669.             if (that.options.width === 'auto') {
  670.  
  671.                 styles.width = (that.el.outerWidth() - 2) + 'px';
  672.  
  673.             }
  674.  
  675.  
  676.  
  677.             $container.css(styles);
  678.  
  679.         },
  680.  
  681.  
  682.  
  683.         enableKillerFn: function () {
  684.  
  685.             var that = this;
  686.  
  687.             $(document).on('click.autocomplete', that.killerFn);
  688.  
  689.         },
  690.  
  691.  
  692.  
  693.         disableKillerFn: function () {
  694.  
  695.             var that = this;
  696.  
  697.             $(document).off('click.autocomplete', that.killerFn);
  698.  
  699.         },
  700.  
  701.  
  702.  
  703.         killSuggestions: function () {
  704.  
  705.             var that = this;
  706.  
  707.             that.stopKillSuggestions();
  708.  
  709.             that.intervalId = window.setInterval(function () {
  710.  
  711.                 if (that.visible) {
  712.  
  713.                     that.el.val(that.currentValue);
  714.  
  715.                     that.hide();
  716.  
  717.                 }
  718.  
  719.  
  720.  
  721.                 that.stopKillSuggestions();
  722.  
  723.             }, 50);
  724.  
  725.         },
  726.  
  727.  
  728.  
  729.         stopKillSuggestions: function () {
  730.  
  731.             window.clearInterval(this.intervalId);
  732.  
  733.         },
  734.  
  735.  
  736.  
  737.         isCursorAtEnd: function () {
  738.  
  739.             var that = this,
  740.  
  741.                 valLength = that.el.val().length,
  742.  
  743.                 selectionStart = that.element.selectionStart,
  744.  
  745.                 range;
  746.  
  747.  
  748.  
  749.             if (typeof selectionStart === 'number') {
  750.  
  751.                 return selectionStart === valLength;
  752.  
  753.             }
  754.  
  755.             if (document.selection) {
  756.  
  757.                 range = document.selection.createRange();
  758.  
  759.                 range.moveStart('character', -valLength);
  760.  
  761.                 return valLength === range.text.length;
  762.  
  763.             }
  764.  
  765.             return true;
  766.  
  767.         },
  768.  
  769.  
  770.  
  771.         onKeyPress: function (e) {
  772.  
  773.             var that = this;
  774.  
  775.  
  776.  
  777.             // If suggestions are hidden and user presses arrow down, display suggestions:
  778.  
  779.             if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) {
  780.  
  781.                 that.suggest();
  782.  
  783.                 return;
  784.  
  785.             }
  786.  
  787.  
  788.  
  789.             if (that.disabled || !that.visible) {
  790.  
  791.                 return;
  792.  
  793.             }
  794.  
  795.  
  796.  
  797.             switch (e.which) {
  798.  
  799.                 case keys.ESC:
  800.  
  801.                     that.el.val(that.currentValue);
  802.  
  803.                     that.hide();
  804.  
  805.                     break;
  806.  
  807.                 case keys.RIGHT:
  808.  
  809.                     if (that.hint && that.options.onHint && that.isCursorAtEnd()) {
  810.  
  811.                         that.selectHint();
  812.  
  813.                         break;
  814.  
  815.                     }
  816.  
  817.                     return;
  818.  
  819.                 case keys.TAB:
  820.  
  821.                     if (that.hint && that.options.onHint) {
  822.  
  823.                         that.selectHint();
  824.  
  825.                         return;
  826.  
  827.                     }
  828.  
  829.                     if (that.selectedIndex === -1) {
  830.  
  831.                         that.hide();
  832.  
  833.                         return;
  834.  
  835.                     }
  836.  
  837.                     that.select(that.selectedIndex);
  838.  
  839.                     if (that.options.tabDisabled === false) {
  840.  
  841.                         return;
  842.  
  843.                     }
  844.  
  845.                     break;
  846.  
  847.                 case keys.RETURN:
  848.  
  849.                     if (that.selectedIndex === -1) {
  850.  
  851.                         that.hide();
  852.  
  853.                         return;
  854.  
  855.                     }
  856.  
  857.                     that.select(that.selectedIndex);
  858.  
  859.                     break;
  860.  
  861.                 case keys.UP:
  862.  
  863.                     that.moveUp();
  864.  
  865.                     break;
  866.  
  867.                 case keys.DOWN:
  868.  
  869.                     that.moveDown();
  870.  
  871.                     break;
  872.  
  873.                 default:
  874.  
  875.                     return;
  876.  
  877.             }
  878.  
  879.  
  880.  
  881.             // Cancel event if function did not return:
  882.  
  883.             e.stopImmediatePropagation();
  884.  
  885.             e.preventDefault();
  886.  
  887.         },
  888.  
  889.  
  890.  
  891.         onKeyUp: function (e) {
  892.  
  893.             var that = this;
  894.  
  895.  
  896.  
  897.             if (that.disabled) {
  898.  
  899.                 return;
  900.  
  901.             }
  902.  
  903.  
  904.  
  905.             switch (e.which) {
  906.  
  907.                 case keys.UP:
  908.  
  909.                 case keys.DOWN:
  910.  
  911.                     return;
  912.  
  913.             }
  914.  
  915.  
  916.  
  917.             clearInterval(that.onChangeInterval);
  918.  
  919.  
  920.  
  921.             if (that.currentValue !== that.el.val()) {
  922.  
  923.                 that.findBestHint();
  924.  
  925.                 if (that.options.deferRequestBy > 0) {
  926.  
  927.                     // Defer lookup in case when value changes very quickly:
  928.  
  929.                     that.onChangeInterval = setInterval(function () {
  930.  
  931.                         that.onValueChange();
  932.  
  933.                     }, that.options.deferRequestBy);
  934.  
  935.                 } else {
  936.  
  937.                     that.onValueChange();
  938.  
  939.                 }
  940.  
  941.             }
  942.  
  943.         },
  944.  
  945.  
  946.  
  947.         onValueChange: function () {
  948.  
  949.             var that = this,
  950.  
  951.                 options = that.options,
  952.  
  953.                 value = that.el.val(),
  954.  
  955.                 query = that.getQuery(value);
  956.  
  957.  
  958.  
  959.             if (that.selection && that.currentValue !== query) {
  960.  
  961.                 that.selection = null;
  962.  
  963.                 (options.onInvalidateSelection || $.noop).call(that.element);
  964.  
  965.             }
  966.  
  967.  
  968.  
  969.             clearInterval(that.onChangeInterval);
  970.  
  971.             that.currentValue = value;
  972.  
  973.             that.selectedIndex = -1;
  974.  
  975.  
  976.  
  977.             // Check existing suggestion for the match before proceeding:
  978.  
  979.             if (options.triggerSelectOnValidInput && that.isExactMatch(query)) {
  980.  
  981.                 that.select(0);
  982.  
  983.                 return;
  984.  
  985.             }
  986.  
  987.  
  988.  
  989.             if (query.length < options.minChars) {
  990.  
  991.                 that.hide();
  992.  
  993.             } else {
  994.  
  995.                 that.getSuggestions(query);
  996.  
  997.             }
  998.  
  999.         },
  1000.  
  1001.  
  1002.  
  1003.         isExactMatch: function (query) {
  1004.  
  1005.             var suggestions = this.suggestions;
  1006.  
  1007.  
  1008.  
  1009.             return (suggestions.length === 1 && suggestions[0].value.toLowerCase() === query.toLowerCase());
  1010.  
  1011.         },
  1012.  
  1013.  
  1014.  
  1015.         getQuery: function (value) {
  1016.  
  1017.             var delimiter = this.options.delimiter,
  1018.  
  1019.                 parts;
  1020.  
  1021.  
  1022.  
  1023.             if (!delimiter) {
  1024.  
  1025.                 return value;
  1026.  
  1027.             }
  1028.  
  1029.             parts = value.split(delimiter);
  1030.  
  1031.             return $.trim(parts[parts.length - 1]);
  1032.  
  1033.         },
  1034.  
  1035.  
  1036.  
  1037.         getSuggestionsLocal: function (query) {
  1038.  
  1039.             var that = this,
  1040.  
  1041.                 options = that.options,
  1042.  
  1043.                 queryLowerCase = query.toLowerCase(),
  1044.  
  1045.                 filter = options.lookupFilter,
  1046.  
  1047.                 limit = parseInt(options.lookupLimit, 10),
  1048.  
  1049.                 data;
  1050.  
  1051.  
  1052.  
  1053.             data = {
  1054.  
  1055.                 suggestions: $.grep(options.lookup, function (suggestion) {
  1056.  
  1057.                     return filter(suggestion, query, queryLowerCase);
  1058.  
  1059.                 })
  1060.  
  1061.             };
  1062.  
  1063.  
  1064.  
  1065.             if (limit && data.suggestions.length > limit) {
  1066.  
  1067.                 data.suggestions = data.suggestions.slice(0, limit);
  1068.  
  1069.             }
  1070.  
  1071.  
  1072.  
  1073.             return data;
  1074.  
  1075.         },
  1076.  
  1077.  
  1078.  
  1079.         getSuggestions: function (q) {
  1080.  
  1081.             var response,
  1082.  
  1083.                 that = this,
  1084.  
  1085.                 options = that.options,
  1086.  
  1087.                 serviceUrl = options.serviceUrl,
  1088.  
  1089.                 params,
  1090.  
  1091.                 cacheKey,
  1092.  
  1093.                 ajaxSettings;
  1094.  
  1095.  
  1096.  
  1097.             options.params[options.paramName] = q;
  1098.  
  1099.             params = options.ignoreParams ? null : options.params;
  1100.  
  1101.  
  1102.  
  1103.             if (options.onSearchStart.call(that.element, options.params) === false) {
  1104.  
  1105.                 return;
  1106.  
  1107.             }
  1108.  
  1109.  
  1110.  
  1111.             if ($.isFunction(options.lookup)){
  1112.  
  1113.                 options.lookup(q, function (data) {
  1114.  
  1115.                     that.suggestions = data.suggestions;
  1116.  
  1117.                     that.suggest();
  1118.  
  1119.                     options.onSearchComplete.call(that.element, q, data.suggestions);
  1120.  
  1121.                 });
  1122.  
  1123.                 return;
  1124.  
  1125.             }
  1126.  
  1127.  
  1128.  
  1129.             if (that.isLocal) {
  1130.  
  1131.                 response = that.getSuggestionsLocal(q);
  1132.  
  1133.             } else {
  1134.  
  1135.                 if ($.isFunction(serviceUrl)) {
  1136.  
  1137.                     serviceUrl = serviceUrl.call(that.element, q);
  1138.  
  1139.                 }
  1140.  
  1141.                 cacheKey = serviceUrl + '?' + $.param(params || {});
  1142.  
  1143.                 response = that.cachedResponse[cacheKey];
  1144.  
  1145.             }
  1146.  
  1147.  
  1148.  
  1149.             if (response && $.isArray(response.suggestions)) {
  1150.  
  1151.                 that.suggestions = response.suggestions;
  1152.  
  1153.                 that.suggest();
  1154.  
  1155.                 options.onSearchComplete.call(that.element, q, response.suggestions);
  1156.  
  1157.             } else if (!that.isBadQuery(q)) {
  1158.  
  1159.                 that.abortAjax();
  1160.  
  1161.  
  1162.  
  1163.                 ajaxSettings = {
  1164.  
  1165.                     url: serviceUrl,
  1166.  
  1167.                     data: params,
  1168.  
  1169.                     type: options.type,
  1170.  
  1171.                     dataType: options.dataType
  1172.  
  1173.                 };
  1174.  
  1175.  
  1176.  
  1177.                 $.extend(ajaxSettings, options.ajaxSettings);
  1178.  
  1179.  
  1180.  
  1181.                 that.currentRequest = $.ajax(ajaxSettings).done(function (data) {
  1182.  
  1183.                     var result;
  1184.  
  1185.                     that.currentRequest = null;
  1186.  
  1187.                     result = options.transformResult(data, q);
  1188.  
  1189.                     that.processResponse(result, q, cacheKey);
  1190.  
  1191.                     options.onSearchComplete.call(that.element, q, result.suggestions);
  1192.  
  1193.                 }).fail(function (jqXHR, textStatus, errorThrown) {
  1194.  
  1195.                     options.onSearchError.call(that.element, q, jqXHR, textStatus, errorThrown);
  1196.  
  1197.                 });
  1198.  
  1199.             } else {
  1200.  
  1201.                 options.onSearchComplete.call(that.element, q, []);
  1202.  
  1203.             }
  1204.  
  1205.         },
  1206.  
  1207.  
  1208.  
  1209.         isBadQuery: function (q) {
  1210.  
  1211.             if (!this.options.preventBadQueries){
  1212.  
  1213.                 return false;
  1214.  
  1215.             }
  1216.  
  1217.  
  1218.  
  1219.             var badQueries = this.badQueries,
  1220.  
  1221.                 i = badQueries.length;
  1222.  
  1223.  
  1224.  
  1225.             while (i--) {
  1226.  
  1227.                 if (q.indexOf(badQueries[i]) === 0) {
  1228.  
  1229.                     return true;
  1230.  
  1231.                 }
  1232.  
  1233.             }
  1234.  
  1235.  
  1236.  
  1237.             return false;
  1238.  
  1239.         },
  1240.  
  1241.  
  1242.  
  1243.         hide: function () {
  1244.  
  1245.             var that = this,
  1246.  
  1247.                 container = $(that.suggestionsContainer);
  1248.  
  1249.  
  1250.  
  1251.             if ($.isFunction(that.options.onHide) && that.visible) {
  1252.  
  1253.                 that.options.onHide.call(that.element, container);
  1254.  
  1255.             }
  1256.  
  1257.  
  1258.  
  1259.             that.visible = false;
  1260.  
  1261.             that.selectedIndex = -1;
  1262.  
  1263.             clearInterval(that.onChangeInterval);
  1264.  
  1265.             $(that.suggestionsContainer).hide();
  1266.  
  1267.             that.signalHint(null);
  1268.  
  1269.         },
  1270.  
  1271.  
  1272.  
  1273.         suggest: function () {
  1274.  
  1275.             if (this.suggestions.length === 0) {
  1276.  
  1277.                 if (this.options.showNoSuggestionNotice) {
  1278.  
  1279.                     this.noSuggestions();
  1280.  
  1281.                 } else {
  1282.  
  1283.                     this.hide();
  1284.  
  1285.                 }
  1286.  
  1287.                 return;
  1288.  
  1289.             }
  1290.  
  1291.  
  1292.  
  1293.             var that = this,
  1294.  
  1295.                 options = that.options,
  1296.  
  1297.                 groupBy = options.groupBy,
  1298.  
  1299.                 formatResult = options.formatResult,
  1300.  
  1301.                 value = that.getQuery(that.currentValue),
  1302.  
  1303.                 className = that.classes.suggestion,
  1304.  
  1305.                 classSelected = that.classes.selected,
  1306.  
  1307.                 container = $(that.suggestionsContainer),
  1308.  
  1309.                 noSuggestionsContainer = $(that.noSuggestionsContainer),
  1310.  
  1311.                 beforeRender = options.beforeRender,
  1312.  
  1313.                 html = '',
  1314.  
  1315.                 category,
  1316.  
  1317.                 formatGroup = function (suggestion, index) {
  1318.  
  1319.                         var currentCategory = suggestion.data[groupBy];
  1320.  
  1321.  
  1322.  
  1323.                         if (category === currentCategory){
  1324.  
  1325.                             return '';
  1326.  
  1327.                         }
  1328.  
  1329.  
  1330.  
  1331.                         category = currentCategory;
  1332.  
  1333.  
  1334.  
  1335.                         return '<div class="autocomplete-group"><strong>' + category + '</strong></div>';
  1336.  
  1337.                     };
  1338.  
  1339.  
  1340.  
  1341.             if (options.triggerSelectOnValidInput && that.isExactMatch(value)) {
  1342.  
  1343.                 that.select(0);
  1344.  
  1345.                 return;
  1346.  
  1347.             }
  1348.  
  1349.  
  1350.  
  1351.             // Build suggestions inner HTML:
  1352.  
  1353.             $.each(that.suggestions, function (i, suggestion) {
  1354.  
  1355.                 if (groupBy){
  1356.  
  1357.                     html += formatGroup(suggestion, value, i);
  1358.  
  1359.                 }
  1360.  
  1361.  
  1362.  
  1363.                 html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value) + '</div>';
  1364.  
  1365.             });
  1366.  
  1367.  
  1368.  
  1369.             this.adjustContainerWidth();
  1370.  
  1371.  
  1372.  
  1373.             noSuggestionsContainer.detach();
  1374.  
  1375.             container.html(html);
  1376.  
  1377.  
  1378.  
  1379.             if ($.isFunction(beforeRender)) {
  1380.  
  1381.                 beforeRender.call(that.element, container);
  1382.  
  1383.             }
  1384.  
  1385.  
  1386.  
  1387.             that.fixPosition();
  1388.  
  1389.             container.show();
  1390.  
  1391.  
  1392.  
  1393.             // Select first value by default:
  1394.  
  1395.             if (options.autoSelectFirst) {
  1396.  
  1397.                 that.selectedIndex = 0;
  1398.  
  1399.                 container.scrollTop(0);
  1400.  
  1401.                 container.children('.' + className).first().addClass(classSelected);
  1402.  
  1403.             }
  1404.  
  1405.  
  1406.  
  1407.             that.visible = true;
  1408.  
  1409.             that.findBestHint();
  1410.  
  1411.         },
  1412.  
  1413.  
  1414.  
  1415.         noSuggestions: function() {
  1416.  
  1417.              var that = this,
  1418.  
  1419.                  container = $(that.suggestionsContainer),
  1420.  
  1421.                  noSuggestionsContainer = $(that.noSuggestionsContainer);
  1422.  
  1423.  
  1424.  
  1425.             this.adjustContainerWidth();
  1426.  
  1427.  
  1428.  
  1429.             // Some explicit steps. Be careful here as it easy to get
  1430.  
  1431.             // noSuggestionsContainer removed from DOM if not detached properly.
  1432.  
  1433.             noSuggestionsContainer.detach();
  1434.  
  1435.             container.empty(); // clean suggestions if any
  1436.  
  1437.             container.append(noSuggestionsContainer);
  1438.  
  1439.  
  1440.  
  1441.             that.fixPosition();
  1442.  
  1443.  
  1444.  
  1445.             container.show();
  1446.  
  1447.             that.visible = true;
  1448.  
  1449.         },
  1450.  
  1451.  
  1452.  
  1453.         adjustContainerWidth: function() {
  1454.  
  1455.             var that = this,
  1456.  
  1457.                 options = that.options,
  1458.  
  1459.                 width,
  1460.  
  1461.                 container = $(that.suggestionsContainer);
  1462.  
  1463.  
  1464.  
  1465.             // If width is auto, adjust width before displaying suggestions,
  1466.  
  1467.             // because if instance was created before input had width, it will be zero.
  1468.  
  1469.             // Also it adjusts if input width has changed.
  1470.  
  1471.             // -2px to account for suggestions border.
  1472.  
  1473.             if (options.width === 'auto') {
  1474.  
  1475.                 width = that.el.outerWidth() - 2;
  1476.  
  1477.                 container.width(width > 0 ? width : 300);
  1478.  
  1479.             }
  1480.  
  1481.         },
  1482.  
  1483.  
  1484.  
  1485.         findBestHint: function () {
  1486.  
  1487.             var that = this,
  1488.  
  1489.                 value = that.el.val().toLowerCase(),
  1490.  
  1491.                 bestMatch = null;
  1492.  
  1493.  
  1494.  
  1495.             if (!value) {
  1496.  
  1497.                 return;
  1498.  
  1499.             }
  1500.  
  1501.  
  1502.  
  1503.             $.each(that.suggestions, function (i, suggestion) {
  1504.  
  1505.                 var foundMatch = suggestion.value.toLowerCase().indexOf(value) === 0;
  1506.  
  1507.                 if (foundMatch) {
  1508.  
  1509.                     bestMatch = suggestion;
  1510.  
  1511.                 }
  1512.  
  1513.                 return !foundMatch;
  1514.  
  1515.             });
  1516.  
  1517.  
  1518.  
  1519.             that.signalHint(bestMatch);
  1520.  
  1521.         },
  1522.  
  1523.  
  1524.  
  1525.         signalHint: function (suggestion) {
  1526.  
  1527.             var hintValue = '',
  1528.  
  1529.                 that = this;
  1530.  
  1531.             if (suggestion) {
  1532.  
  1533.                 hintValue = that.currentValue + suggestion.value.substr(that.currentValue.length);
  1534.  
  1535.             }
  1536.  
  1537.             if (that.hintValue !== hintValue) {
  1538.  
  1539.                 that.hintValue = hintValue;
  1540.  
  1541.                 that.hint = suggestion;
  1542.  
  1543.                 (this.options.onHint || $.noop)(hintValue);
  1544.  
  1545.             }
  1546.  
  1547.         },
  1548.  
  1549.  
  1550.  
  1551.         verifySuggestionsFormat: function (suggestions) {
  1552.  
  1553.             // If suggestions is string array, convert them to supported format:
  1554.  
  1555.             if (suggestions.length && typeof suggestions[0] === 'string') {
  1556.  
  1557.                 return $.map(suggestions, function (value) {
  1558.  
  1559.                     return { value: value, data: null };
  1560.  
  1561.                 });
  1562.  
  1563.             }
  1564.  
  1565.  
  1566.  
  1567.             return suggestions;
  1568.  
  1569.         },
  1570.  
  1571.  
  1572.  
  1573.         validateOrientation: function(orientation, fallback) {
  1574.  
  1575.             orientation = $.trim(orientation || '').toLowerCase();
  1576.  
  1577.  
  1578.  
  1579.             if($.inArray(orientation, ['auto', 'bottom', 'top']) === -1){
  1580.  
  1581.                 orientation = fallback;
  1582.  
  1583.             }
  1584.  
  1585.  
  1586.  
  1587.             return orientation;
  1588.  
  1589.         },
  1590.  
  1591.  
  1592.  
  1593.         processResponse: function (result, originalQuery, cacheKey) {
  1594.  
  1595.             var that = this,
  1596.  
  1597.                 options = that.options;
  1598.  
  1599.  
  1600.  
  1601.             result.suggestions = that.verifySuggestionsFormat(result.suggestions);
  1602.  
  1603.  
  1604.  
  1605.             // Cache results if cache is not disabled:
  1606.  
  1607.             if (!options.noCache) {
  1608.  
  1609.                 that.cachedResponse[cacheKey] = result;
  1610.  
  1611.                 if (options.preventBadQueries && result.suggestions.length === 0) {
  1612.  
  1613.                     that.badQueries.push(originalQuery);
  1614.  
  1615.                 }
  1616.  
  1617.             }
  1618.  
  1619.  
  1620.  
  1621.             // Return if originalQuery is not matching current query:
  1622.  
  1623.             if (originalQuery !== that.getQuery(that.currentValue)) {
  1624.  
  1625.                 return;
  1626.  
  1627.             }
  1628.  
  1629.  
  1630.  
  1631.             that.suggestions = result.suggestions;
  1632.  
  1633.             that.suggest();
  1634.  
  1635.         },
  1636.  
  1637.  
  1638.  
  1639.         activate: function (index) {
  1640.  
  1641.             var that = this,
  1642.  
  1643.                 activeItem,
  1644.  
  1645.                 selected = that.classes.selected,
  1646.  
  1647.                 container = $(that.suggestionsContainer),
  1648.  
  1649.                 children = container.find('.' + that.classes.suggestion);
  1650.  
  1651.  
  1652.  
  1653.             container.find('.' + selected).removeClass(selected);
  1654.  
  1655.  
  1656.  
  1657.             that.selectedIndex = index;
  1658.  
  1659.  
  1660.  
  1661.             if (that.selectedIndex !== -1 && children.length > that.selectedIndex) {
  1662.  
  1663.                 activeItem = children.get(that.selectedIndex);
  1664.  
  1665.                 $(activeItem).addClass(selected);
  1666.  
  1667.                 return activeItem;
  1668.  
  1669.             }
  1670.  
  1671.  
  1672.  
  1673.             return null;
  1674.  
  1675.         },
  1676.  
  1677.  
  1678.  
  1679.         selectHint: function () {
  1680.  
  1681.             var that = this,
  1682.  
  1683.                 i = $.inArray(that.hint, that.suggestions);
  1684.  
  1685.  
  1686.  
  1687.             that.select(i);
  1688.  
  1689.         },
  1690.  
  1691.  
  1692.  
  1693.         select: function (i) {
  1694.  
  1695.             var that = this;
  1696.  
  1697.             that.hide();
  1698.  
  1699.             that.onSelect(i);
  1700.  
  1701.         },
  1702.  
  1703.  
  1704.  
  1705.         moveUp: function () {
  1706.  
  1707.             var that = this;
  1708.  
  1709.  
  1710.  
  1711.             if (that.selectedIndex === -1) {
  1712.  
  1713.                 return;
  1714.  
  1715.             }
  1716.  
  1717.  
  1718.  
  1719.             if (that.selectedIndex === 0) {
  1720.  
  1721.                 $(that.suggestionsContainer).children().first().removeClass(that.classes.selected);
  1722.  
  1723.                 that.selectedIndex = -1;
  1724.  
  1725.                 that.el.val(that.currentValue);
  1726.  
  1727.                 that.findBestHint();
  1728.  
  1729.                 return;
  1730.  
  1731.             }
  1732.  
  1733.  
  1734.  
  1735.             that.adjustScroll(that.selectedIndex - 1);
  1736.  
  1737.         },
  1738.  
  1739.  
  1740.  
  1741.         moveDown: function () {
  1742.  
  1743.             var that = this;
  1744.  
  1745.  
  1746.  
  1747.             if (that.selectedIndex === (that.suggestions.length - 1)) {
  1748.  
  1749.                 return;
  1750.  
  1751.             }
  1752.  
  1753.  
  1754.  
  1755.             that.adjustScroll(that.selectedIndex + 1);
  1756.  
  1757.         },
  1758.  
  1759.  
  1760.  
  1761.         adjustScroll: function (index) {
  1762.  
  1763.             var that = this,
  1764.  
  1765.                 activeItem = that.activate(index);
  1766.  
  1767.  
  1768.  
  1769.             if (!activeItem) {
  1770.  
  1771.                 return;
  1772.  
  1773.             }
  1774.  
  1775.  
  1776.  
  1777.             var offsetTop,
  1778.  
  1779.                 upperBound,
  1780.  
  1781.                 lowerBound,
  1782.  
  1783.                 heightDelta = $(activeItem).outerHeight();
  1784.  
  1785.  
  1786.  
  1787.             offsetTop = activeItem.offsetTop;
  1788.  
  1789.             upperBound = $(that.suggestionsContainer).scrollTop();
  1790.  
  1791.             lowerBound = upperBound + that.options.maxHeight - heightDelta;
  1792.  
  1793.  
  1794.  
  1795.             if (offsetTop < upperBound) {
  1796.  
  1797.                 $(that.suggestionsContainer).scrollTop(offsetTop);
  1798.  
  1799.             } else if (offsetTop > lowerBound) {
  1800.  
  1801.                 $(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta);
  1802.  
  1803.             }
  1804.  
  1805.  
  1806.  
  1807.             if (!that.options.preserveInput) {
  1808.  
  1809.                 that.el.val(that.getValue(that.suggestions[index].value));
  1810.  
  1811.             }
  1812.  
  1813.             that.signalHint(null);
  1814.  
  1815.         },
  1816.  
  1817.  
  1818.  
  1819.         onSelect: function (index) {
  1820.  
  1821.             var that = this,
  1822.  
  1823.                 onSelectCallback = that.options.onSelect,
  1824.  
  1825.                 suggestion = that.suggestions[index];
  1826.  
  1827.  
  1828.  
  1829.             that.currentValue = that.getValue(suggestion.value);
  1830.  
  1831.  
  1832.  
  1833.             if (that.currentValue !== that.el.val() && !that.options.preserveInput) {
  1834.  
  1835.                 that.el.val(that.currentValue);
  1836.  
  1837.             }
  1838.  
  1839.  
  1840.  
  1841.             that.signalHint(null);
  1842.  
  1843.             that.suggestions = [];
  1844.  
  1845.             that.selection = suggestion;
  1846.  
  1847.  
  1848.  
  1849.             if ($.isFunction(onSelectCallback)) {
  1850.  
  1851.                 onSelectCallback.call(that.element, suggestion);
  1852.  
  1853.             }
  1854.  
  1855.         },
  1856.  
  1857.  
  1858.  
  1859.         getValue: function (value) {
  1860.  
  1861.             var that = this,
  1862.  
  1863.                 delimiter = that.options.delimiter,
  1864.  
  1865.                 currentValue,
  1866.  
  1867.                 parts;
  1868.  
  1869.  
  1870.  
  1871.             if (!delimiter) {
  1872.  
  1873.                 return value;
  1874.  
  1875.             }
  1876.  
  1877.  
  1878.  
  1879.             currentValue = that.currentValue;
  1880.  
  1881.             parts = currentValue.split(delimiter);
  1882.  
  1883.  
  1884.  
  1885.             if (parts.length === 1) {
  1886.  
  1887.                 return value;
  1888.  
  1889.             }
  1890.  
  1891.  
  1892.  
  1893.             return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value;
  1894.  
  1895.         },
  1896.  
  1897.  
  1898.  
  1899.         dispose: function () {
  1900.  
  1901.             var that = this;
  1902.  
  1903.             that.el.off('.autocomplete').removeData('autocomplete');
  1904.  
  1905.             that.disableKillerFn();
  1906.  
  1907.             $(window).off('resize.autocomplete', that.fixPositionCapture);
  1908.  
  1909.             $(that.suggestionsContainer).remove();
  1910.  
  1911.         }
  1912.  
  1913.     };
  1914.  
  1915.  
  1916.  
  1917.     // Create chainable jQuery plugin:
  1918.  
  1919.     $.fn.autocomplete = $.fn.devbridgeAutocomplete = function (options, args) {
  1920.  
  1921.         var dataKey = 'autocomplete';
  1922.  
  1923.         // If function invoked without argument return
  1924.  
  1925.         // instance of the first matched element:
  1926.  
  1927.         if (arguments.length === 0) {
  1928.  
  1929.             return this.first().data(dataKey);
  1930.  
  1931.         }
  1932.  
  1933.  
  1934.  
  1935.         return this.each(function () {
  1936.  
  1937.             var inputElement = $(this),
  1938.  
  1939.                 instance = inputElement.data(dataKey);
  1940.  
  1941.  
  1942.  
  1943.             if (typeof options === 'string') {
  1944.  
  1945.                 if (instance && typeof instance[options] === 'function') {
  1946.  
  1947.                     instance[options](args);
  1948.  
  1949.                 }
  1950.  
  1951.             } else {
  1952.  
  1953.                 // If instance already exists, destroy it:
  1954.  
  1955.                 if (instance && instance.dispose) {
  1956.  
  1957.                     instance.dispose();
  1958.  
  1959.                 }
  1960.  
  1961.                 instance = new Autocomplete(this, options);
  1962.  
  1963.                 inputElement.data(dataKey, instance);
  1964.  
  1965.             }
  1966.  
  1967.         });
  1968.  
  1969.     };
  1970.  
  1971. }));

Raw Paste


Login or Register to edit or fork this paste. It's free.