Knockout Custom Binding Autocomplete with Categories

KnockoutJs Jquery Autocomplete with categories. Done with a custom binding

HTML

<script src="http://kendo.cdn.telerik.com/2015.1.225/js/kendo.all.min.js"></script>
<script src="https://bxaccess.com/FundMgmt/scripts/3rdParty/knockout/knockout-3.3.0.js"></script>
<script src="https://bxaccess.com/FundMgmt/scripts/3rdParty/knockout/knockout-kendo.js"></script>
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<link rel="stylesheet" href="http://cdn.kendostatic.com/2015.1.225/styles/kendo.default.min.css">
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2015.1.225/styles/kendo.common.min.css">
<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="http://code.jquery.com/ui/1.11.0/jquery-ui.js"></script>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.11.0/themes/smoothness/jquery-ui.css">
<input type="text" data-bind="dataItemAutocompleteCategories: autocompleteDataItems, addCallback: addDataItem, searchResultContainer: 'searchAutoCompleteCalulator'" id="dataItemSelector" class="form-control search-box" />
<div class="in-page-autocomplete" id="searchAutoCompleteCalulator"></div>
<div class="col-md-5">
  <input id="isPrimary" type='checkbox' data-bind="checked: isPrimary, click: primaryClicked" />
  <span>PRIMARY ONLY</span>
</div>

CSS

.clickable:hover {
  cursor: pointer;
}

.k-grid .k-header {
  display: none;
}

JavaScript

ko.bindingHandlers.dataItemAutocompleteCategories = (function() {
  var self = {};
  var top;

  $.widget("custom.catcomplete", $.ui.autocomplete, {
    _create: function() {
      this._super();
      this.widget().menu("option", "items", "> :not(.ui-autocomplete-category)");
    },
    _renderMenu: function(ul, items) {
      var that = this,
        currentCategory = "";
      $.each(items, function(index, item) {
        var li;
        if (item.category != currentCategory) {
          ul.append("<li class='ui-autocomplete-category'>" + item.category + "</li>");
          currentCategory = item.category;
        }
        li = that._renderItemData(ul, item);
        li.removeClass().addClass("search-result section-item");
        li.text('');
        li.append('<a class="ui-corner-all"><div class="display">' + item.label + '</div></a>');
        if (item.category) {
          li.attr("aria-label", item.category + " : " + item.label);
        }
      });
    }
  });

  var topN = function(data) {
    return function(request, response) {
      var results = $.ui.autocomplete.filter(data, request.term);
      response(results.slice(0, top));
    };
  };

  self.init = function(element, valueAccessor, allBindingsAccessor) {
    var allBindings = allBindingsAccessor();
    var cb = allBindings.addCallback;
    var searchResultContainer = allBindings.searchResultContainer;
    top = allBindings.top || 20;
    var data = valueAccessor();

    $(element).catcomplete({
      source: topN(ko.unwrap(data)),
      select: function(event, ui) {
        cb(ui);
        $(this).val('');
        event.preventDefault();
      },
      appendTo: $('#' + searchResultContainer),
    });
  };
  self.update = function(element, valueAccessor, allBindingsAccessor) {

    $(element).catcomplete('option', 'source', topN(ko.unwrap(valueAccessor())));
    $(element).catcomplete('widget').css({
        'z-index': 10004
      })
      .addClass('sectioned-autocomplete');
  };

  return...