blob: 6edd6d5c724840f85e8b0c286fc562c5dab1e0ae [file] [log] [blame]
Tyler Scott93cae872015-07-21 14:58:23 -06001/*
2 * The following code is a jquery extention written to add autocomplete functionality to bootstrap input groups.
3 *
4 * Usage:
5 *
6 * Then simply call $('.someClass').autoComplete(getSuggestions) on it to enable auto completion.
7 *
8 * Also add
9 * ```
10 * .autoComplete {
11 * position: absolute;
12 * top: 38px;
13 * width: 229px;
14 * }
15 *
16 * .autoComplete .list-group {
17 * margin-bottom: 0;
18 * border: none;
19 * }
20 * ```
21 * to an included css file.
22 *
23 * getSuggestions returns by calling its callback parameter with an array of valid strings.
24 *
25 */
26(function(){
27 "use strict";
28 if (!jQuery){
29 throw new Error("jQuery is required and must be loaded before this script.")
30 }
31 jQuery.fn.extend({
32 /**
33 * @param {Array<String>|getSuggestions}
34 */
35 autoComplete: function(suggestions) {
36
37 var element = $('<div></div>');
38 element.addClass('list-group')
39 .css({
40 'border-top-left-radius': 0,
41 'border-top-right-radius': 0,
42 'width': this.width(),
43 'position': 'absolute',
44 'top': this.parent().height(),
45 'display': 'none',
46 'max-height': '500px',
47 'overflow-y': 'auto'
48 });
49
50 this.focus(function(){
51 element.slideDown();
52 }).blur(function(){
53 element.slideUp();
54 }).before(element);
55
56 var getSuggestions = function(current, callback){
57 callback(suggestions.reduce(function(prev, suggestion){
58 if (current.toLowerCase().indexOf(suggestion.substr(0, current.length).toLowerCase()) === 0){
59 prev.push(suggestion);
60 }
61 return prev;
62 }, []));
63 }
64
65 var setAutoComplete = function(list){
66 element.empty();
67
68 element.html(list.reduce(function(prev, current){
69 return [prev, '<a href="#" class="list-group-item">', current, '</a>'].join("");
70 }, ""));
71
72 }
73
74 if (suggestions instanceof Function){
75 getSuggestions = suggestions;
76 }
77
78 var input = this;
79
80 element.bind('click', 'a', function(){
81 input.val($(this).text());
82 });
83
84 this.keydown(function(e){
85 switch(e.which){
86 case 38: //up
87 var active = element.find('.active');
88 if (active.length === 0){
89 element.find(':first-child').addClass('active');
90 } else {
91 if (!active.is(':first-child')){
92 active.removeClass('active').prev().addClass('active');
93 }
94 }
95 e.preventDefault();
96 break;
97
98 case 40: //down
99 var active = element.find('.active');
100 if (active.length === 0){
101 element.find(':first-child').addClass('active');
102 } else {
103 if (!active.is(':last-child')){
104 active.removeClass('active').next().addClass('active');
105 }
106 }
107 e.preventDefault();
108 break;
109
110 case 13: //Enter
111 var active = element.find('.active');
112 if (active.length === 1){
113 $(this).val(active.text());
114 e.preventDefault();
115 }
116 break;
117
118 case 9: //Tab
119 getSuggestions(input.val(), setAutoComplete);
120 e.preventDefault(); //Don't print tab
121 break;
122 }
123
124 });
125
126 return this;
127
128 }
129 });
130})();
131
132/**
133 * @callback getSuggestions
134 * @param {string} current - The current value of the input field.
135 * @param {function}
136 */