68fcf0d16d6408ac16200557b687224bd86277e0 angie Wed Apr 13 16:24:49 2016 -0700 Refactored autocompleteCat options so that species autocomplete can do even more customized post-processing of results from server (insert phylo tree matches between dbDb matches and assembly hub matches). refs #15277 diff --git src/hg/js/hgGateway.js src/hg/js/hgGateway.js index 1b93dae..2fde271 100644 --- src/hg/js/hgGateway.js +++ src/hg/js/hgGateway.js @@ -636,121 +636,85 @@ } 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 $("
") .data("item.autocomplete", item) .append($("").html(item.label)) .appendTo(ul); } }); - function removeDups(inList, isDup) { - // Return a list with only unique items from inList, using isDup(a, b) -> true if a =~ b - var inLength = inList.length; - // inListDups is an array of boolean flags for marking duplicates, parallel to inList. - var inListDups = []; - var outList = []; - var i, j; - for (i = 0; i < inLength; i++) { - // If something has already been marked as a duplicate, skip it. - if (! inListDups[i]) { - // the first time we see a value, add it to outList. - outList.push(inList[i]); - for (j = i+1; j < inLength; j++) { - // Now scan the rest of inList to find duplicates of inList[i]. - // We can skip items previously marked as duplicates. - if (!inListDups[j] && isDup(inList[i], inList[j])) { - inListDups[j] = true; - } - } - } - } - return outList; - } - 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. - // If options.searchObj is provided, it is used in addition to baseUrl; first the term is - // looked up in searchObj and then also queried using baseUrl. Values in searchObj - // should have the same structure as the value returned by a baseUrl query. - // options.isDuplicate (if provided) is a function (a, b) -> boolean that returns - // true if autocomplete items a and b are redundant; it is used to remove duplicates - // from autocomplete results. + // 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; - var searchObjResults = []; - _.forEach(options.searchObj, function(results, key) { - if (_.startsWith(key.toUpperCase(), term.toUpperCase())) { - searchObjResults = searchObjResults.concat(results); - } - }); $.getJSON(url) .done(function(results) { - var combinedResults = results.concat(searchObjResults); - // Optionally remove duplicates identified by options.isDuplicate - if (options.isDuplicate) { - combinedResults = removeDups(combinedResults, options.isDuplicate); + if (_.isFunction(options.onServerReply)) { + results = options.onServerReply(results, term); } - cache[term] = combinedResults; - acCallback(combinedResults); + 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.searchObj = options.searchObj || {}; 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.Watermark(options.watermark, '#686868'); } } @@ -860,44 +824,40 @@ var kidObj = autocompleteFromTree(kid); // Clone kid's result list and add own label as category: var kidResults = _.map(kidObj[kidLabel], addMyLabel); // Add kid's mappings to searchObj only if kid is not a leaf. if (kidKids && kidKids.length > 0) { _.assign(searchObj, kidObj); } return kidResults; }) ); } // Exclude some overly broad categories: if (label !== 'root' && label !== 'cellular organisms') { searchObj[label] = myResults; } - return searchObj; - } - - function addAutocompleteCommonNames(searchObj) { - // After searchObj is constructed by autocompleteFromTree, add aliases for - // some common names that map to scientific names in the tree. + // Add aliases for some common names that map to scientific names in the tree. _.forEach(commonToSciNames, function(sciName, commonName) { var label, addMyLabel; if (searchObj[sciName]) { label = sciName + ' (' + commonName + ')'; addMyLabel = addCategory.bind(null, label); searchObj[commonName] = _.map(searchObj[sciName], addMyLabel); } }); + return searchObj; } function makeStripe(id, color, stripeHeight, scrollTop, onClickStripe) { // Return an empty div with specified background color and height var $stripe = $('