title: Extend the bootstrap-typeahead in order to take an object instead of a string
body: <p>I extended the bootstrap-typeahead in order to take an object instead of a string.<br>
  It works but I would like to know this is the right way to do the things.   </p>
  
  <p>Thanks.</p>
  
  <p>Reference:<br>
  <a href="http://twitter.github.com/bootstrap/javascript.html#typeahead" rel="noreferrer">http://twitter.github.com/bootstrap/javascript.html#typeahead</a>
  <a href="http://twitter.github.com/bootstrap/assets/js/bootstrap-typeahead.js" rel="noreferrer">http://twitter.github.com/bootstrap/assets/js/bootstrap-typeahead.js</a></p>
  
  <pre><code>_.extend($.fn.typeahead.Constructor.prototype, {
      render: function (items) {
          var that = this;
  
          items = $(items).map(function (i, item) {
              i = $(that.options.item)
                  .attr('data-value', item[that.options.display])
                  .attr('data-id', item.id);
              i.find('a').html(that.highlighter(item));
              return i[0];
          });
  
          items.first().addClass('active');
          this.$menu.html(items);
          return this;
      },
      select: function () {
          var val = this.$menu.find('.active').attr('data-value'),
              id = this.$menu.find('.active').attr('data-id');
          this.$element
              .val(this.updater(val, id))
              .change();
          return this.hide()
      }
  });
  
  return function (element, options) {
      var getSource = function () {
              return {
                  id: 2,
                  full_name: 'first_name last_name'
              };
      };
  
      element.typeahead({
          minLength: 3,
          source: getSource,
          display: 'full_name',
          sorter: function (items) {
  
              var beginswith = [],
                  caseSensitive = [],
                  caseInsensitive = [],
                  item,
                  itemDisplayed;
  
              while (item = items.shift()) {
                  itemDisplayed = item[this.options.display];
                  if (!itemDisplayed.toLowerCase().indexOf(this.query.toLowerCase())) {
                      beginswith.push(item);
                  } else if (~itemDisplayed.indexOf(this.query)) {
                      caseSensitive.push(item);
                  } else {
                      caseInsensitive.push(item);
                  }
              }
  
              return beginswith.concat(caseSensitive, caseInsensitive);
          },
          highlighter: function (item) {
              var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&amp;');
  
              return item[this.options.display].replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
                  return '&lt;strong&gt;' + match + '&lt;/strong&gt;';
              });
          },
          matcher: function (item) {
              var value = item[this.options.display];
  
              return {
                  value: ~value.toLowerCase().indexOf(this.query.toLowerCase()),
                  id: item.id
              };
          },
          updater: function (item, userId) {
              options.hiddenInputElement.val(userId);
              return item;
          }
      });
  };
  </code></pre>