diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d7a4d4d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/*.user diff --git a/src/jquery.autocomplete.js b/src/jquery.autocomplete.js index 462eb4e..2310f56 100644 --- a/src/jquery.autocomplete.js +++ b/src/jquery.autocomplete.js @@ -5,7 +5,7 @@ * Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license. * For details, see the web site: http://www.devbridge.com/projects/autocomplete/jquery/ * -* Last Review: 07/24/2012 +* Last Review: 11/08/2012 */ /*jslint browser: true, white: true, plusplus: true */ @@ -53,29 +53,28 @@ this.el.data('autocomplete', this); } - $.fn.autocomplete = function (options, optionName) { - - var autocompleteControl; + $.fn.autocomplete = function (options, args) { + var instance; if (typeof options === 'string') { - autocompleteControl = this.data('autocomplete'); - if (typeof autocompleteControl[options] === 'function') { - autocompleteControl[options](optionName); + instance = this.data('autocomplete'); + if (typeof instance[options] === 'function') { + instance[options](args); } } else { - autocompleteControl = new Autocomplete(this.get(0) || $(''), options); + instance = new Autocomplete(this.get(0) || $(''), options); } - return autocompleteControl; - }; + return instance; + }; Autocomplete.prototype = { killerFn: null, initialize: function () { - var me, uid, autocompleteElId; + me = this; uid = Math.floor(Math.random() * 0x100000).toString(16); autocompleteElId = 'Autocomplete_' + uid; @@ -111,12 +110,16 @@ setOptions: function (options) { var o = this.options; + this.extendOptions(options); + if (o.lookup || o.isLocal) { this.isLocal = true; if ($.isArray(o.lookup)) { o.lookup = { suggestions: o.lookup, data: [] }; } } + $('#' + this.mainContainerId).css({ zIndex: o.zIndex }); + this.container.css({ maxHeight: o.maxHeight + 'px', width: o.width }); }, @@ -150,8 +153,11 @@ killSuggestions: function () { var me = this; - this.stopKillSuggestions(); - this.intervalId = window.setInterval(function () { me.hide(); me.stopKillSuggestions(); }, 300); + me.stopKillSuggestions(); + me.intervalId = window.setInterval(function () { + me.hide(); + me.stopKillSuggestions(); + }, 300); }, stopKillSuggestions: function () { @@ -163,9 +169,10 @@ }, onKeyPress: function (e) { - if (this.disabled || !this.enabled) { return; } - // return will exit the function - // and event will not be prevented + if (this.disabled || !this.enabled) { + return; + } + switch (e.keyCode) { case 27: //KEY_ESC: this.el.val(this.currentValue); @@ -178,7 +185,9 @@ return; } this.select(this.selectedIndex); - if (e.keyCode === 9) { return; } + if (e.keyCode === 9) { + return; + } break; case 38: //KEY_UP: this.moveUp(); @@ -189,18 +198,25 @@ default: return; } + + // Cancel event if function did not return: e.stopImmediatePropagation(); e.preventDefault(); }, onKeyUp: function (e) { - if (this.disabled) { return; } + if (this.disabled) { + return; + } + switch (e.keyCode) { case 38: //KEY_UP: case 40: //KEY_DOWN: return; } + clearInterval(this.onChangeInterval); + if (this.currentValue !== this.el.val()) { if (this.options.deferRequestBy > 0) { // Defer lookup in case when value changes very quickly: @@ -217,10 +233,12 @@ this.currentValue = this.el.val(); var q = this.getQuery(this.currentValue); this.selectedIndex = -1; + if (this.ignoreValueChange) { this.ignoreValueChange = false; return; } + if (q === '' || q.length < this.options.minChars) { this.hide(); } else { @@ -231,17 +249,21 @@ getQuery: function (val) { var d, arr; d = this.options.delimiter; - if (!d) { return $.trim(val); } + if (!d) { + return $.trim(val); + } arr = val.split(d); return $.trim(arr[arr.length - 1]); }, getSuggestionsLocal: function (q) { var ret, arr, len, val, i; + arr = this.options.lookup; len = arr.suggestions.length; ret = { suggestions: [], data: [] }; q = q.toLowerCase(); + for (i = 0; i < len; i++) { val = arr.suggestions[i]; if (val.toLowerCase().indexOf(q) === 0) { @@ -249,13 +271,15 @@ ret.data.push(arr.data[i]); } } + return ret; }, getSuggestions: function (q) { - var cr, me; - cr = this.isLocal ? this.getSuggestionsLocal(q) : this.cachedResponse[q]; //dadeta this.options.isLocal || + + cr = this.isLocal ? this.getSuggestionsLocal(q) : this.cachedResponse[q]; + if (cr && $.isArray(cr.suggestions)) { this.suggestions = cr.suggestions; this.data = cr.data; @@ -263,15 +287,21 @@ } else if (!this.isBadQuery(q)) { me = this; me.options.params.query = q; - $.get(this.serviceUrl, me.options.params, function (txt) { me.processResponse(txt); }, 'text'); + $.get(this.serviceUrl, me.options.params, function (txt) { + me.processResponse(txt); + }, 'text'); } }, isBadQuery: function (q) { var i = this.badQueries.length; + while (i--) { - if (q.indexOf(this.badQueries[i]) === 0) { return true; } + if (q.indexOf(this.badQueries[i]) === 0) { + return true; + } } + return false; }, @@ -282,20 +312,32 @@ }, suggest: function () { - if (this.suggestions.length === 0) { this.hide(); return; } var me, len, div, f, v, i, s, mOver, mClick; + me = this; len = this.suggestions.length; f = this.options.fnFormatResult; v = this.getQuery(this.currentValue); - mOver = function (xi) { return function () { me.activate(xi); }; }; - mClick = function (xi) { return function () { me.select(xi); }; }; + + mOver = function (xi) { + return function () { + me.activate(xi); + }; + }; + + mClick = function (xi) { + return function () { + me.select(xi); + }; + }; + this.container.hide().empty(); + for (i = 0; i < len; i++) { s = this.suggestions[i]; div = $((me.selectedIndex === i ? '
' + f(s, this.data[i], v) + '
'); @@ -303,6 +345,7 @@ div.click(mClick(i)); this.container.append(div); } + this.enabled = true; this.container.show(); }, @@ -310,14 +353,24 @@ processResponse: function (text) { /*jslint evil: true */ var response; + try { response = eval('(' + text + ')'); - } catch (err) { return; } - if (!$.isArray(response.data)) { response.data = []; } + } catch (err) { + return; + } + + if (!$.isArray(response.data)) { + response.data = []; + } + if (!this.options.noCache) { this.cachedResponse[response.query] = response; - if (response.suggestions.length === 0) { this.badQueries.push(response.query); } + if (response.suggestions.length === 0) { + this.badQueries.push(response.query); + } } + if (response.query === this.getQuery(this.currentValue)) { this.suggestions = response.suggestions; this.data = response.data; @@ -327,6 +380,7 @@ activate: function (index) { var divs, activeItem; + divs = this.container.children(); // Clear previous selection: if (this.selectedIndex !== -1 && divs.length > this.selectedIndex) { @@ -346,17 +400,23 @@ deactivate: function (div, index) { div.className = ''; - if (this.selectedIndex === index) { this.selectedIndex = -1; } + if (this.selectedIndex === index) { + this.selectedIndex = -1; + } }, select: function (i) { var selectedValue, f; + selectedValue = this.suggestions[i]; + if (selectedValue) { this.el.val(selectedValue); if (this.options.autoSubmit) { f = this.el.parents('form'); - if (f.length > 0) { f.get(0).submit(); } + if (f.length > 0) { + f.get(0).submit(); + } } this.ignoreValueChange = true; this.hide(); @@ -365,73 +425,97 @@ }, change: function (i) { - var selectedValue, fn, me, s, d; + var selectedValue, onChange, me, s, d; + me = this; selectedValue = this.suggestions[i]; + if (selectedValue) { s = me.suggestions[i]; d = me.data[i]; me.el.val(me.getValue(s)); - } - else { + } else { s = ''; d = -1; } - fn = me.options.onChange; - if ($.isFunction(fn)) { fn(s, d, me.el); } + onChange = me.options.onChange; + if ($.isFunction(onChange)) { + onChange(s, d, me.el); + } }, moveUp: function () { - if (this.selectedIndex === -1) { return; } + if (this.selectedIndex === -1) { + return; + } + if (this.selectedIndex === 0) { this.container.children().get(0).className = ''; this.selectedIndex = -1; this.el.val(this.currentValue); return; } + this.adjustScroll(this.selectedIndex - 1); }, moveDown: function () { - if (this.selectedIndex === (this.suggestions.length - 1)) { return; } + if (this.selectedIndex === (this.suggestions.length - 1)) { + return; + } + this.adjustScroll(this.selectedIndex + 1); }, adjustScroll: function (i) { var activeItem, offsetTop, upperBound, lowerBound; + activeItem = this.activate(i); offsetTop = activeItem.offsetTop; upperBound = this.container.scrollTop(); lowerBound = upperBound + this.options.maxHeight - 25; + if (offsetTop < upperBound) { this.container.scrollTop(offsetTop); } else if (offsetTop > lowerBound) { this.container.scrollTop(offsetTop - this.options.maxHeight + 25); } + this.el.val(this.getValue(this.suggestions[i])); }, onSelect: function (i) { - var me, fn, s, d; - me = this; - fn = me.options.onSelect; - s = me.suggestions[i]; - d = me.data[i]; - me.el.val(me.getValue(s)); - if ($.isFunction(fn)) { fn(s, d, me.el); } + var me = this, + callback = me.options.onSelect, + sugestion = me.suggestions[i], + data = me.data[i]; + + me.el.val(me.getValue(sugestion)); + + if ($.isFunction(callback)) { + callback(sugestion, data, me.el); + } }, getValue: function (value) { - var del, currVal, arr, me; - me = this; - del = me.options.delimiter; - if (!del) { return value; } - currVal = me.currentValue; - arr = currVal.split(del); - if (arr.length === 1) { return value; } - return currVal.substr(0, currVal.length - arr[arr.length - 1].length) + value; + var me = this, + separator = me.options.delimiter, + currentValue, + array; + + if (!separator) { + return value; + } + + currentValue = me.currentValue; + array = currentValue.split(separator); + + if (array.length === 1) { + return value; + } + + return currentValue.substr(0, currentValue.length - array[array.length - 1].length) + value; } }; - }(jQuery));