Skip to content

Commit 5a15bec

Browse files
author
Tomas Kirda
committed
Rev for 1.4.0 release
1 parent 91dc0d1 commit 5a15bec

File tree

5 files changed

+95
-110
lines changed

5 files changed

+95
-110
lines changed

bower.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "devbridge-autocomplete",
3-
"version": "1.3.0",
3+
"version": "1.4.0",
44
"homepage": "https://github.com/devbridge/jQuery-Autocomplete",
55
"authors": [
66
"Tomas Kirda"

devbridge-autocomplete.jquery.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"ajax",
77
"autocomplete"
88
],
9-
"version": "1.3.0",
9+
"version": "1.4.0",
1010
"author": {
1111
"name": "Tomas Kirda",
1212
"url": "https://github.com/tkirda"

dist/jquery.autocomplete.js

Lines changed: 90 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Ajax Autocomplete for jQuery, version 1.3.0
2+
* Ajax Autocomplete for jQuery, version 1.4.0
33
* (c) 2017 Tomas Kirda
44
*
55
* Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
@@ -49,51 +49,12 @@
4949
UP: 38,
5050
RIGHT: 39,
5151
DOWN: 40
52-
};
52+
},
53+
54+
noop = $.noop;
5355

5456
function Autocomplete(el, options) {
55-
var noop = $.noop,
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-
formatGroup: Autocomplete.formatGroup,
71-
delimiter: null,
72-
zIndex: 9999,
73-
type: 'GET',
74-
noCache: false,
75-
onSearchStart: noop,
76-
onSearchComplete: noop,
77-
onSearchError: noop,
78-
preserveInput: false,
79-
containerClass: 'autocomplete-suggestions',
80-
tabDisabled: false,
81-
dataType: 'text',
82-
currentRequest: null,
83-
triggerSelectOnValidInput: true,
84-
preventBadQueries: true,
85-
lookupFilter: function (suggestion, originalQuery, queryLowerCase) {
86-
return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1;
87-
},
88-
paramName: 'query',
89-
transformResult: function (response) {
90-
return typeof response === 'string' ? $.parseJSON(response) : response;
91-
},
92-
showNoSuggestionNotice: false,
93-
noSuggestionNotice: 'No results',
94-
orientation: 'bottom',
95-
forceFixPosition: false
96-
};
57+
var that = this;
9758

9859
// Shared variables:
9960
that.element = el;
@@ -102,14 +63,14 @@
10263
that.badQueries = [];
10364
that.selectedIndex = -1;
10465
that.currentValue = that.element.value;
105-
that.intervalId = 0;
66+
that.timeoutId = null;
10667
that.cachedResponse = {};
107-
that.onChangeInterval = null;
68+
that.onChangeTimeout = null;
10869
that.onChange = null;
10970
that.isLocal = false;
11071
that.suggestionsContainer = null;
11172
that.noSuggestionsContainer = null;
112-
that.options = $.extend({}, defaults, options);
73+
that.options = $.extend({}, Autocomplete.defaults, options);
11374
that.classes = {
11475
selected: 'autocomplete-selected',
11576
suggestion: 'autocomplete-suggestion'
@@ -127,8 +88,53 @@
12788

12889
$.Autocomplete = Autocomplete;
12990

130-
Autocomplete.formatResult = function (suggestion, currentValue) {
131-
// Do not replace anything if there current value is empty
91+
Autocomplete.defaults = {
92+
ajaxSettings: {},
93+
autoSelectFirst: false,
94+
appendTo: document.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
132138
if (!currentValue) {
133139
return suggestion.value;
134140
}
@@ -144,14 +150,12 @@
144150
.replace(/&lt;(\/?strong)&gt;/g, '<$1>');
145151
};
146152

147-
Autocomplete.formatGroup = function (suggestion, category) {
148-
return '<div class="autocomplete-group"><strong>' + category + '</strong></div>';
153+
function _formatGroup(suggestion, category) {
154+
return '<div class="autocomplete-group">' + category + '</div>';
149155
};
150156

151157
Autocomplete.prototype = {
152158

153-
killerFn: null,
154-
155159
initialize: function () {
156160
var that = this,
157161
suggestionSelector = '.' + that.classes.suggestion,
@@ -162,13 +166,6 @@
162166
// Remove autocomplete attribute to prevent native suggestions:
163167
that.element.setAttribute('autocomplete', 'off');
164168

165-
that.killerFn = function (e) {
166-
if (!$(e.target).closest('.' + that.options.containerClass).length) {
167-
that.killSuggestions();
168-
that.disableKillerFn();
169-
}
170-
};
171-
172169
// html() deals with many types: htmlString or Element or Array or jQuery
173170
that.noSuggestionsContainer = $('<div class="autocomplete-no-suggestion"></div>')
174171
.html(this.options.noSuggestionNotice).get(0);
@@ -195,12 +192,16 @@
195192
container.children('.' + selected).removeClass(selected);
196193
});
197194

195+
198196
// Listen for click event on suggestions list:
199197
container.on('click.autocomplete', suggestionSelector, function () {
200198
that.select($(this).data('index'));
201-
return false;
202199
});
203200

201+
container.on('click.autocomplete', function () {
202+
clearTimeout(that.blurTimeoutId);
203+
})
204+
204205
that.fixPositionCapture = function () {
205206
if (that.visible) {
206207
that.fixPosition();
@@ -228,7 +229,13 @@
228229
},
229230

230231
onBlur: function () {
231-
this.enableKillerFn();
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);
232239
},
233240

234241
abortAjax: function () {
@@ -276,7 +283,7 @@
276283
disable: function () {
277284
var that = this;
278285
that.disabled = true;
279-
clearInterval(that.onChangeInterval);
286+
clearTimeout(that.onChangeTimeout);
280287
that.abortAjax();
281288
},
282289

@@ -344,39 +351,6 @@
344351
$container.css(styles);
345352
},
346353

347-
enableKillerFn: function () {
348-
var that = this;
349-
$(document).on('click.autocomplete', that.killerFn);
350-
},
351-
352-
disableKillerFn: function () {
353-
var that = this;
354-
$(document).off('click.autocomplete', that.killerFn);
355-
},
356-
357-
killSuggestions: function () {
358-
var that = this;
359-
that.stopKillSuggestions();
360-
that.intervalId = window.setInterval(function () {
361-
if (that.visible) {
362-
// No need to restore value when
363-
// preserveInput === true,
364-
// because we did not change it
365-
if (!that.options.preserveInput) {
366-
that.el.val(that.currentValue);
367-
}
368-
369-
that.hide();
370-
}
371-
372-
that.stopKillSuggestions();
373-
}, 50);
374-
},
375-
376-
stopKillSuggestions: function () {
377-
window.clearInterval(this.intervalId);
378-
},
379-
380354
isCursorAtEnd: function () {
381355
var that = this,
382356
valLength = that.el.val().length,
@@ -467,13 +441,13 @@
467441
return;
468442
}
469443

470-
clearInterval(that.onChangeInterval);
444+
clearTimeout(that.onChangeTimeout);
471445

472446
if (that.currentValue !== that.el.val()) {
473447
that.findBestHint();
474448
if (that.options.deferRequestBy > 0) {
475449
// Defer lookup in case when value changes very quickly:
476-
that.onChangeInterval = setInterval(function () {
450+
that.onChangeTimeout = setTimeout(function () {
477451
that.onValueChange();
478452
}, that.options.deferRequestBy);
479453
} else {
@@ -493,7 +467,7 @@
493467
(options.onInvalidateSelection || $.noop).call(that.element);
494468
}
495469

496-
clearInterval(that.onChangeInterval);
470+
clearTimeout(that.onChangeTimeout);
497471
that.currentValue = value;
498472
that.selectedIndex = -1;
499473

@@ -558,12 +532,13 @@
558532
ajaxSettings;
559533

560534
options.params[options.paramName] = q;
561-
params = options.ignoreParams ? null : options.params;
562535

563536
if (options.onSearchStart.call(that.element, options.params) === false) {
564537
return;
565538
}
566539

540+
params = options.ignoreParams ? null : options.params;
541+
567542
if ($.isFunction(options.lookup)){
568543
options.lookup(q, function (data) {
569544
that.suggestions = data.suggestions;
@@ -640,7 +615,7 @@
640615

641616
that.visible = false;
642617
that.selectedIndex = -1;
643-
clearInterval(that.onChangeInterval);
618+
clearTimeout(that.onChangeTimeout);
644619
$(that.suggestionsContainer).hide();
645620
that.signalHint(null);
646621
},
@@ -718,6 +693,7 @@
718693

719694
noSuggestions: function() {
720695
var that = this,
696+
beforeRender = that.options.beforeRender,
721697
container = $(that.suggestionsContainer),
722698
noSuggestionsContainer = $(that.noSuggestionsContainer);
723699

@@ -726,9 +702,15 @@
726702
// Some explicit steps. Be careful here as it easy to get
727703
// noSuggestionsContainer removed from DOM if not detached properly.
728704
noSuggestionsContainer.detach();
729-
container.empty(); // clean suggestions if any
705+
706+
// clean suggestions if any
707+
container.empty();
730708
container.append(noSuggestionsContainer);
731709

710+
if ($.isFunction(beforeRender)) {
711+
beforeRender.call(that.element, container, that.suggestions);
712+
}
713+
732714
that.fixPosition();
733715

734716
container.show();
@@ -862,7 +844,6 @@
862844
var that = this;
863845
that.hide();
864846
that.onSelect(i);
865-
that.disableKillerFn();
866847
},
867848

868849
moveUp: function () {
@@ -965,14 +946,13 @@
965946
dispose: function () {
966947
var that = this;
967948
that.el.off('.autocomplete').removeData('autocomplete');
968-
that.disableKillerFn();
969949
$(window).off('resize.autocomplete', that.fixPositionCapture);
970950
$(that.suggestionsContainer).remove();
971951
}
972952
};
973953

974954
// Create chainable jQuery plugin:
975-
$.fn.autocomplete = $.fn.devbridgeAutocomplete = function (options, args) {
955+
$.fn.devbridgeAutocomplete = function (options, args) {
976956
var dataKey = 'autocomplete';
977957
// If function invoked without argument return
978958
// instance of the first matched element:
@@ -998,4 +978,9 @@
998978
}
999979
});
1000980
};
981+
982+
// Don't overwrite if it already exists
983+
if (!$.fn.autocomplete) {
984+
$.fn.autocomplete = $.fn.devbridgeAutocomplete;
985+
}
1001986
}));

0 commit comments

Comments
 (0)