diff --git a/bower.json b/bower.json index 62b3e594b..811e3f939 100644 --- a/bower.json +++ b/bower.json @@ -29,6 +29,7 @@ "requirejs-text": "2.x", "underscore": "1.x", "blanket": "1.x", + "jquery-simulate-ext": "~1.3.0", "require-handlebars-plugin": "~1.0.0" }, "ignore": [ diff --git a/index.js b/index.js index c4c996264..6651d6eae 100644 --- a/index.js +++ b/index.js @@ -72,7 +72,10 @@ define(function (require) { /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - COMBOBOX - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - + $('#myCombobox').combobox({ + filterOnKeypress: false, + showOptionsOnKeypress: true + }); // sample method buttons $('#btnComboboxGetSelectedItem').on('click', function () { var selectedItem = $('#myCombobox').combobox('selectedItem'); diff --git a/js/combobox.js b/js/combobox.js index d866d9b64..5ab998884 100644 --- a/js/combobox.js +++ b/js/combobox.js @@ -39,10 +39,12 @@ this.$dropMenu = this.$element.find('.dropdown-menu'); this.$input = this.$element.find('input'); this.$button = this.$element.find('.btn'); + this.$inputGroupBtn = this.$element.find('.input-group-btn'); this.$element.on('click.fu.combobox', 'a', $.proxy(this.itemclicked, this)); this.$element.on('change.fu.combobox', 'input', $.proxy(this.inputchanged, this)); this.$element.on('shown.bs.dropdown', $.proxy(this.menuShown, this)); + this.$input.on('keyup.fu.combobox', $.proxy(this.keypress, this)); // set default selection this.setDefaultSelection(); @@ -53,6 +55,11 @@ this.$button.addClass('disabled'); } + // filter on load in case the first thing they do is press navigational key to pop open the menu + if (this.options.filterOnKeypress) { + this.options.filter(this.$dropMenu.find('li'), this.$input.val(), this); + } + }; Combobox.prototype = { @@ -77,6 +84,7 @@ doSelect: function ($item) { if (typeof $item[0] !== 'undefined') { + $item.addClass('selected'); this.$selectedItem = $item; this.$input.val(this.$selectedItem.text().trim()); } else { @@ -84,6 +92,12 @@ } }, + clearSelection: function () { + this.$selectedItem = null; + this.$input.val(''); + this.$dropMenu.find('li').removeClass('selected'); + }, + menuShown: function () { if (this.options.autoResizeMenu) { this.resizeMenu(); @@ -106,7 +120,7 @@ }, this.$selectedItem.data()); } else { data = { - text: this.$input.val() + text: this.$input.val().trim() }; } @@ -116,11 +130,12 @@ selectByText: function (text) { var $item = $([]); this.$element.find('li').each(function () { - if ((this.textContent || this.innerText || $(this).text() || '').toLowerCase() === (text || '').toLowerCase()) { + if ((this.textContent || this.innerText || $(this).text() || '').trim().toLowerCase() === (text || '').trim().toLowerCase()) { $item = $(this); return false; } }); + this.doSelect($item); }, @@ -185,6 +200,75 @@ this.$element.find('.dropdown-toggle').focus(); }, + keypress: function (e) { + var ENTER = 13; + //var TAB = 9; + var ESC = 27; + var LEFT = 37; + var UP = 38; + var RIGHT = 39; + var DOWN = 40; + + var IS_NAVIGATIONAL = ( + e.which === UP || + e.which === DOWN || + e.which === LEFT || + e.which === RIGHT + ); + + if(this.options.showOptionsOnKeypress && !this.$inputGroupBtn.hasClass('open')){ + this.$button.dropdown('toggle'); + this.$input.focus(); + } + + if (e.which === ENTER) { + e.preventDefault(); + + var selected = this.$dropMenu.find('li.selected').text().trim(); + if(selected.length > 0){ + this.selectByText(selected); + }else{ + this.selectByText(this.$input.val()); + } + + this.$inputGroupBtn.removeClass('open'); + this.inputchanged(e); + } else if (e.which === ESC) { + e.preventDefault(); + this.clearSelection(); + this.$inputGroupBtn.removeClass('open'); + } else if (this.options.showOptionsOnKeypress) { + if (e.which === DOWN || e.which === UP) { + e.preventDefault(); + var $selected = this.$dropMenu.find('li.selected'); + if ($selected.length > 0) { + if (e.which === DOWN) { + $selected = $selected.next(':not(.hidden)'); + } else { + $selected = $selected.prev(':not(.hidden)'); + } + } + + if ($selected.length === 0){ + if (e.which === DOWN) { + $selected = this.$dropMenu.find('li:not(.hidden):first'); + } else { + $selected = this.$dropMenu.find('li:not(.hidden):last'); + } + } + this.$dropMenu.find('li').removeClass('selected'); + $selected.addClass('selected'); + } + } + + // Avoid filtering on navigation key presses + if (this.options.filterOnKeypress && !IS_NAVIGATIONAL) { + this.options.filter(this.$dropMenu.find('li'), this.$input.val(), this); + } + + this.previousKeyPress = e.which; + }, + inputchanged: function (e, extra) { // skip processing for internally-generated synthetic event // to avoid double processing @@ -232,7 +316,34 @@ }; $.fn.combobox.defaults = { - autoResizeMenu: true + autoResizeMenu: true, + filterOnKeypress: false, + showOptionsOnKeypress: false, + filter: function filter (list, predicate, self) { + var visible = 0; + self.$dropMenu.find('.empty-indicator').remove(); + + list.each(function (i) { + var $li = $(this); + var text = $(this).text().trim(); + + $li.removeClass(); + + if (text === predicate) { + $li.addClass('text-success'); + visible++; + } else if (text.substr(0, predicate.length) === predicate) { + $li.addClass('text-info'); + visible++; + } else { + $li.addClass('hidden'); + } + }); + + if (visible === 0) { + self.$dropMenu.append('