# Overriding sorter and matcher in Bootstrap Typeahead

In this code, I'm overriding the sorter and matcher for Twitter-Bootstrap's Typeahead functionality. The reason for the override is to allow state (technically Jurisdiction) abbreviations to be used to match the full state name. I've reduced the length of the source array in this example for brevity, but a full version is available on this fiddle. I also decided to make it so that duplicate entries could not be made.

I went out of my way to make JSHint like my code, but I'd basically like any suggestions. This is my first time doing something like this and I'm relatively new with Javascript anyway. The only things I'm not interested in is white space as I simply formatted this based upon JSFiddle's TidyUp function, the css (as I didn't create it), and suggestions to override highlighter as I don't see the need.

Note: In production the number of inputs is dynamic, which is why I am selecting them each time.

If you need to view the Typeahead source code to understand my overrides, you can download it here

## HTML for Example

<input class="jurisdiction">
<br/>
<input class="jurisdiction">
<br/>
<input class="jurisdiction" value="Alberta">


## Javascript

var myTypeAhead = ! function ($) { // To make JSHint happy var states = [{ //shortened for brevity name: 'Alabama', abbreviation: 'AL' }, { name: 'Alberta', abbreviation: 'AB' }], stateNames = [], overrides = { matcher: matcherOverride, sorter: sorterOverride }, usedStateNames = getUsedStateNames(); //Populate stateNames$.each(states, function () {
stateNames.push(this.name);
});

function getState(stateName) {
return $.grep(states, function (e) { return e.name === stateName; })[0]; } function matcherOverride(item) { var state = getState(item); var query = this.query.toLowerCase(); // true if entered value contained in state name or abbreviation return ~state.name.toLowerCase().indexOf(query) || ~state.abbreviation.toLowerCase().indexOf(query); } function sorterOverride(items) { //Orders state names based upon match type var beginswith = [], caseSensitive = [], caseInsensitive = [], abbreviationMatch = [], item = items.shift(), state, lcQuery = this.query.toLowerCase(); while (item) { state = getState(item); if (state && !state.abbreviation.toLowerCase().indexOf(lcQuery)) { abbreviationMatch.push(state.name); } else if (!item.toLowerCase().indexOf(lcQuery)) { beginswith.push(item); } else if (~item.indexOf(this.query)) { caseSensitive.push(item); } else { caseInsensitive.push(item); } item = items.shift(); } return abbreviationMatch.concat(beginswith, caseSensitive, caseInsensitive); } function UpdateDataSource(e) { //$(e).typeahead(overrides).data('typeahead').source = getDataSourceArray();
$(e).typeahead(overrides).data('typeahead').source = getDataSourceArray(e.value); } function getDataSourceArray(currentValue) { usedStateNames = removeArray(usedStateNames, [currentValue]); return removeArray(stateNames, usedStateNames); } function removeArray(original, toRemove) { return$.grep(original, function (a) {
return !~$.inArray(a, toRemove); }); } function getUsedStateNames() { var usedStateNames = [];$('.jurisdiction').each(function () {
usedStateNames.push(this.value);
});
return usedStateNames;
}
$(document).on("focus", ".jurisdiction", function () { UpdateDataSource(this); });$(document).on("change", ".jurisdiction", function () {
//Disallow repeats or unlisted values
if (!~stateNames.indexOf(this.value) || ~usedStateNames.indexOf(this.value)) {
this.value = "";
}
});
\$(document).on("blur", ".jurisdiction", function () {
usedStateNames.push(this.value);
});
}(window.jQuery);

• which bootstrap version are you using ? – Muhammed Mar 12 '14 at 12:16