3ecfb8deb165083b0d3df5081aeb5dda2c4b6ff1 angie Mon Sep 24 10:53:38 2018 -0700 Libifying JS module autocompleteCat (autocomplete with optional category labels) from hgGateway. diff --git src/hg/js/autocompleteCat.js src/hg/js/autocompleteCat.js new file mode 100644 index 0000000..f47c090 --- /dev/null +++ src/hg/js/autocompleteCat.js @@ -0,0 +1,118 @@ +// autocompleteCat: customized JQuery autocomplete plugin that includes watermark and +// can display results broken down by category (for example, genomes from various +// assembly hubs and native genomes). + +// Copyright (C) 2018 The Regents of the University of California + +///////////////////////////// Module: autocompleteCat ///////////////////////////// + +var autocompleteCat = (function() { + // Customize jQuery UI autocomplete to show item categories and support html markup in labels. + // Adapted from https://jqueryui.com/autocomplete/#categories and + // http://forum.jquery.com/topic/using-html-in-autocomplete + // Also adds watermark to input. + $.widget("custom.autocompleteCat", + $.ui.autocomplete, + { + _renderMenu: function(ul, items) { + var that = this; + var currentCategory = ""; + // There's no this._super as shown in the doc, so I can't override + // _create as shown in the doc -- just do this every time we render... + this.widget().menu("option", "items", "> :not(.ui-autocomplete-category)"); + $.each(items, + function(index, item) { + // Add a heading each time we see a new category: + if (item.category && item.category !== currentCategory) { + ul.append("<li class='ui-autocomplete-category'>" + + item.category + "</li>" ); + currentCategory = item.category; + } + that._renderItem( ul, item ); + }); + }, + _renderItem: function(ul, item) { + // In order to use HTML markup in the autocomplete, one has to overwrite + // autocomplete's _renderItem method using .html instead of .text. + // http://forum.jquery.com/topic/using-html-in-autocomplete + return $("<li></li>") + .data("item.autocomplete", item) + .append($("<a></a>").html(item.label)) + .appendTo(ul); + } + }); + + function init($input, options) { + // Set up an autocomplete and watermark for $input, with a callback options.onSelect + // for when the user chooses a result. + // If options.baseUrl is null, the autocomplete will not do anything, but we (re)initialize + // it anyway in case the same input had a previous db's autocomplete in effect. + // options.onServerReply (if given) is a function (Array, term) -> Array that + // post-processes the list of items returned by the server before the list is + // passed back to autocomplete for rendering. + // The following two options apply only when using our locally modified jquery-ui: + // If options.enterSelectsIdentical is true, then if the user hits Enter in the text input + // and their term has an exact match in the autocomplete results, that result is selected. + // options.onEnterTerm (if provided) is a callback function (jqEvent, jqUi) invoked + // when the user hits Enter, after handling enterSelectsIdentical. + + // The function closure allows us to keep a private cache of past searches. + var cache = {}; + + var doSearch = function(term, acCallback) { + // Look up term in searchObj and by sending an ajax request + var timestamp = new Date().getTime(); + var url = options.baseUrl + encodeURIComponent(term) + '&_=' + timestamp; + $.getJSON(url) + .done(function(results) { + if (_.isFunction(options.onServerReply)) { + results = options.onServerReply(results, term); + } + cache[term] = results; + acCallback(results); + }); + // ignore errors to avoid spamming people on flaky network connections + // with tons of error messages (#8816). + }; + + var autoCompleteSource = function(request, acCallback) { + // This is a callback for jqueryui.autocomplete: when the user types + // a character, this is called with the input value as request.term and an acCallback + // for this to return the result to autocomplete. + // See http://api.jqueryui.com/autocomplete/#option-source + var results = cache[request.term]; + if (results) { + acCallback(results); + } else if (options.baseUrl) { + doSearch(request.term, acCallback); + } + }; + + var autoCompleteSelect = function(event, ui) { + // This is a callback for autocomplete to let us know that the user selected + // a term from the list. See http://api.jqueryui.com/autocomplete/#event-select + options.onSelect(ui.item); + $input.blur(); + }; + + // Provide default values where necessary: + options.onSelect = options.onSelect || console.log; + options.enterSelectsIdentical = options.enterSelectsIdentical || false; + + $input.autocompleteCat({ + delay: 500, + minLength: 2, + source: autoCompleteSource, + select: autoCompleteSelect, + enterSelectsIdentical: options.enterSelectsIdentical, + enterTerm: options.onEnterTerm + }); + + if (options.watermark) { + $input.css('color', 'black'); + $input.Watermark(options.watermark, '#686868'); + } + } + + return { init: init }; +}()); // autocompleteCat