Skip to content

Instantly share code, notes, and snippets.

@jrusbatch
Last active December 12, 2015 06:39
Show Gist options
  • Save jrusbatch/4731149 to your computer and use it in GitHub Desktop.
Save jrusbatch/4731149 to your computer and use it in GitHub Desktop.
A wrapper for Twitter Bootstrap's typeahead plugin that supports objects.
(function($) {
'use strict';
function TypeaheadItem(value, selectedText, suggestionText) {
this.value = value;
this.text = selectedText;
this.suggestion = suggestionText || selectedText;
}
TypeaheadItem.prototype.toString = function () {
return JSON.stringify({ value: this.value, text: this.text });
};
TypeaheadItem.prototype.toLowerCase = function () {
return this.suggestion.toLowerCase();
};
TypeaheadItem.prototype.indexOf = function () {
return String.prototype.indexOf.apply(this.suggestion, arguments);
};
TypeaheadItem.prototype.replace = function () {
return String.prototype.replace.apply(this.suggestion, arguments);
};
function ObjectTypeahead(element, options) {
this.options = $.extend({}, options, { updater: this.selectItem, source: this.findObjects });
this.$textElement = $(element);
this.$valueElement = $('input[name="' + options.target + '"]');
if (this.$valueElement.length === 0) {
this.$valueElement =
$('<input type="hidden" />')
.attr({ name: options.target })
.insertAfter(this.$textElement);
}
this.typeahead = this.$textElement.on('blur', this.onTextElementBlur).typeahead(this.options).data('typeahead');
}
// Returns the value that will be used to represent this object
ObjectTypeahead.prototype.getItemValue = function(obj) {
return obj && obj[this.options.value || this.options.target];
};
// Returns the text that should be displayed for this item if it is selected
ObjectTypeahead.prototype.getSelectedItemText = function (obj) {
return obj && obj[this.options.display || this.$textElement.attr('name')];
};
// Returns the text that should be displayed for the given item in the suggestion list
ObjectTypeahead.prototype.getItemSuggestionText = function (obj) {
return obj && obj[this.options.suggest || this.$textElement.attr('name')];
};
// Called by bootstrap when an item is selected
ObjectTypeahead.prototype.selectItem = function (item) {
if (typeof item !== 'string' || item.length === 0) {
return this.clear();
}
var obj = JSON.parse(item);
this.$valueElement.val(obj.value);
return obj.text;
};
// Called by Typeahead to find results for a query
ObjectTypeahead.prototype.findObjects = function (query, callback) {
var source = this.options.source,
deferred = new $.Deferred();
if (source instanceof Function) {
source(query, function (items) {
if (Object.prototype.toString.call(items) === '[object Array]') {
deferred.resolve(items);
}
deferred.reject();
});
} else if (typeof source == 'string' || source instanceof String) {
var parameters = {},
parameter = this.options.parameter || this.$textElement.attr('name');
parameters[parameter] = query;
$.getJSON(source, parameters, function(data) {
deferred.resolve(data);
});
} else {
deferred.reject();
}
return deferred.then(this.createTypeaheadItems).then(callback).promise();
};
ObjectTypeahead.prototype.createTypeaheadItems = function (objects) {
var self = this;
return objects.map(function (obj) {
var itemValue = self.getItemValue(obj),
itemText = self.getSelectedItemText(obj),
itemSuggestionText = self.getItemSuggestionText(obj);
return new TypeaheadItem(itemValue, itemText, itemSuggestionText);
});
};
ObjectTypeahead.prototype.onTextElementBlur = function () {
var typeahead = this.typeahead,
query = typeahead.query,
deferred = new $.Deferred();
if (!query || query.length < typeahead.options.minLength) {
this.clear();
return typeahead.shown ? typeahead.hide() : this;
}
deferred.done(function (items) {
items = $.grep(items, function (item) {
return typeahead.matcher(item);
});
items = typeahead.sorter(items);
typeahead.select(items.length ? items[0] : null);
});
this.findObjects(query, function (items) { deferred.resolve(items); });
};
ObjectTypeahead.prototype.clear = function () {
this.$textElement.val(null);
this.$valueElement.val(null);
};
(function() {
var old = $.fn.objectTypeahead;
$.fn.objectTypeahead = function(option) {
return this.each(function() {
var $this = $(this),
data = $this.data('objectTypeahead'),
options = typeof option == 'object' && option;
if (!data) {
$this.data('objectTypeahead', (data = new ObjectTypeahead(this, options)));
}
if (typeof option == 'string') {
data[option]();
}
});
};
$.fn.objectTypeahead.Constructor = ObjectTypeahead;
$.fn.objectTypeahead.noConflict = function() {
$.fn.objectTypeahead = old;
return this;
};
}());
$(function () {
$(document).on('focus.objectTypeahead', 'input[data-provide="objecttypeahead"]', function (e) {
var $this = $(this);
if (!$this.data('objectTypeahead')) {
e.preventDefault();
$this.objectTypeahead($this.data());
}
});
});
}(jQuery));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment