jQuery(function($) { $ .widget( "custom.combobox", { _create : function() { this.wrapper = $("").addClass( "custom-combobox") .insertAfter(this.element); this.element.hide(); this._createAutocomplete(); }, _createAutocomplete : function() { var selected = this.element.children(":selected"), value = selected .val() ? selected.text() : ""; this.input = $("").appendTo(this.wrapper) .val(value).attr("title", "").addClass( "form-control").autocomplete({ delay : 0, minLength : 3, source : $.proxy(this, "_source") }).tooltip({ tooltipClass : "ui-state-highlight" }); this._on(this.input, { autocompleteselect : function(event, ui) { ui.item.option.selected = true; this._trigger("select", event, { item : ui.item.option }); }, autocompletechange : "_removeIfInvalid" }); }, _source : function(request, response) { var matcher = new RegExp($.ui.autocomplete .escapeRegex(request.term), "i"); response(this.element.children("option").map( function() { var text = $(this).text(); if (this.value && (!request.term || matcher .test(text))) return { label : text, value : text, option : this }; })); }, _removeIfInvalid : function(event, ui) { // Selected an item, nothing to do if (ui.item) { return; } // Search for a match (case-insensitive) var value = this.input.val(), valueLowerCase = value .toLowerCase(), valid = false; this.element .children("option") .each( function() { if ($(this).text() .toLowerCase() === valueLowerCase) { this.selected = valid = true; return false; } }); // Found a match, nothing to do if (valid) { return; } // Remove invalid value this.input.val("").attr("title", value + " didn't match any item").tooltip( "open"); this.element.val(""); this._delay(function() { this.input.tooltip("close").attr("title", ""); }, 25000); this.input.data("ui-autocomplete").term = ""; }, _destroy : function() { this.wrapper.remove(); this.element.show(); } }); });