Last active
December 12, 2015 06:39
-
-
Save jrusbatch/4731149 to your computer and use it in GitHub Desktop.
A wrapper for Twitter Bootstrap's typeahead plugin that supports objects.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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