edbd78ba2667d41249ee0c521d49304c038ffeb1
galt
  Fri Feb 19 17:27:19 2021 -0800
Added counts to hgTrackUi facets. refs #25321

diff --git src/hg/js/hui.js src/hg/js/hui.js
index 4255b3e..56db978 100644
--- src/hg/js/hui.js
+++ src/hg/js/hui.js
@@ -30,44 +30,46 @@
 
         // If hide => show then check all subCBs matching view and matCBs
         // If show => show than just enable subCBs for the view.
         if (obj.lastIndex === 0) { // From hide to show
             // If there are matCBs then we will check some subCBs if we just went from hide to show
             matCBs = $("input.matCB:checked");
             if (matCBs.length > 0) {
                 // Get list of all checked abc classes first
                 var classesAbcChecked = [];
                 matCBs.filter(".abc").each( function (i) {
                     var classList = $( this ).attr("class").split(" ");
                     classesAbcChecked.push( 
                             aryRemove(classList,["matCB","changed","disabled","abc"]) );
                 });
 
-                // Walk through checked non-ABC matCBs and sheck related subCBs
-                var subCBs = $("input.subCB").filter("."+view).not(":checked");
+                // Walk through checked non-ABC matCBs and check related subCBs
+                var subCbsSelected = filterCompSubCBsSurviving();
+                var subCBs = $(subCbsSelected).filter("."+view).not(":checked");
                 matCBs.not(".abc").each( function (i) {
                     var classList = $( this ).attr("class").split(" ");
                     classList = aryRemove(classList,["matCB","changed","disabled"]);
                     var subCBsMatching = objsFilterByClasses(subCBs,"and",classList);
                     if (classesAbcChecked.length>0)
                         subCBsMatching = objsFilterByClasses(subCBsMatching,"or",classesAbcChecked);
                     // Check the subCBs that belong to this view and checked matCBs
                     subCBsMatching.each( function (i) {
                         this.checked = true;
                         matSubCBsetShadow(this,true);  // will update "subCfg" if needed
                         hideOrShowSubtrack(this);
                     });
+
                 });
             } // If no matrix, then enabling is all that was needed.
 
             // fix 3-way which may need to go from unchecked to .disabled
             matCBs = $("input.matCB").not(".abc").not(".disabled").not(":checked");
             if (matCBs.length > 0) {
                 $( matCBs ).each( function (i) { matChkBoxNormalize( this, classesHidden ); });
             }
         }
     }
     // fix 3-way matCBs which may need to go from disabled to checked or unchecked depending
     matCBs = matCBsWhichAreComplete(false);
     if (matCBs.length > 0) {
         if ($("select.viewDD").not("[selectedIndex=0]").length === 0) {      // No views visible so
             $( matCBs ).each( function (i) { matCbComplete( this, true ); });// nothing inconsistent
@@ -158,64 +160,65 @@
                     classes = classes.concat( matAbcCBclasses(false) );
                     $( matCBs ).each( function (i) { matChkBoxNormalize( this, classes ); });
                 }
             }
         }
     }
 
     if (matCB.checked)
         exposeAll();  // Unhide composite vis?
     matSubCBsSelected();
 }
 
 function _matSetMatrixCheckBoxes(state)
 {   // matButtons:onclick Set all Matrix checkboxes to state.
     // If additional arguments are passed in, the list of CBs will be narrowed by the classes
+
     var matCBs = $("input.matCB").not(".abc");
     for (var vIx=1;vIx<arguments.length;vIx++) {
         matCBs = $( matCBs ).filter("."+arguments[vIx]);  // Successively limit list
     }
     $( matCBs ).each( function (i) {
         this.checked = state;
         matCbComplete(this,true);
     });
     var subCbs = $("input.subCB");
     for (var sIx=1;sIx<arguments.length;sIx++) {
         subCbs = $( subCbs ).filter("."+arguments[sIx]);  // Successively limit list
     }
     if (state) { // If clicking [+], further limit to only checked ABCs
         var classes = matAbcCBclasses(false);
         subCbs = objsFilterByClasses(subCbs,"not",classes);  // remove unchecked abcCB classes
     }
     $( subCbs ).each( function (i) {
         if (this.checked !== state) {
             this.checked = state;
             matSubCBsetShadow(this,false);
             $(this).change();  // NOTE: if "subCfg" then 'change' event will update it
         }
     });
     if (state)
         exposeAll();  // Unhide composite vis?
     showOrHideSelectedSubtracks();
     matSubCBsSelected();
-    //jQuery(this).css('cursor', '');
 
-    var tbody = normed($(subCbs[0]).parents('tbody.sorting'));
+    var tbody = normed($('tbody.sortable'));
     if (tbody)
          $(tbody).removeClass('sorting');
     return true;
 }
+
 function matSetMatrixCheckBoxes(state)
 { // Called exclusively by matrix [+][-] buttons on click
     var tbody = normed($('tbody.sortable'));
     if (tbody)
          $(tbody).addClass('sorting');
 
     if (arguments.length >= 5)
         waitOnFunction(_matSetMatrixCheckBoxes,state,arguments[1],arguments[2],arguments[3],
                                                                                arguments[4]);
     else if (arguments.length >= 4)
         waitOnFunction(_matSetMatrixCheckBoxes,state,arguments[1],arguments[2],arguments[3]);
     else if (arguments.length >= 3)
         waitOnFunction(_matSetMatrixCheckBoxes,state,arguments[1],arguments[2]);
     else if (arguments.length >= 2)
         waitOnFunction(_matSetMatrixCheckBoxes,state,arguments[1]);
@@ -485,30 +488,35 @@
     } else { // No abcCBs so look for filterBox classes
         return filterCompositeClasses(wantSelected);
     }
     return classes;
 }
 
 function matSubCBsSelected()
 {
 // Displays visible and checked track count
     var counter = normed($('.subCBcount'));
     if (counter) {
         var subCBs =  $("input.subCB");                   // subCfg uses fauxDisabled
         $(counter).text($(subCBs).filter(":enabled:checked").not('.disabled').length + 
                                                             " of " +$(subCBs).length+ " selected");
     }
+
+    if (typeof(ddcl) === "object") {
+        setTimeout(function(){ ddcl.reinitFacetCounts(); }, 50);
+    }
+
 }
 
 /////////////////// subtrack configuration support ////////////////
 function compositeCfgUpdateSubtrackCfgs(inp)
 {
 // Updates all subtrack configuration values when the composite cfg is changed
     // If view association then find it:
     var view = "";
     var list = null;
     var daddy = normed($(inp).parents(".blueBox"));
     if (daddy) {
         var classList = $(daddy).attr("class").split(" ");
         if (classList.length === 2) {
             view = classList[1];
         }
@@ -878,117 +886,203 @@
 { // Called when filterComp selection changes.  Will check/uncheck subtracks
     var subCbsSelected = filterCompSubCBsSurviving();
 
     // Uncheck:
     var subCbsToUnselect = $("input.subCB:checked");// Default all
     if (subCbsToUnselect.length > 0)
         $(subCbsToUnselect).each( function (i) { matSubCBcheckOne(this,false); });
 
     // check:
     if (subCbsSelected.length > 0)
         $(subCbsSelected).each( function (i) { matSubCBcheckOne(this,true); });
 
     // Update count
     matSubCBsSelected();
 
-    var tbody = normed($(subCbsSelected[0]).parents('tbody.sorting'));
+    var tbody = normed($('tbody.sortable'));
     if (tbody)
          $(tbody).removeClass('sorting');
 }
 
 function filterCompositeTrigger()
 { // Called when filterComp selection changes.  Will check/uncheck subtracks
     var tbody = normed($('tbody.sortable'));
     if (tbody)
          $(tbody).addClass('sorting');
 
     waitOnFunction(_filterComposite);
 }
 
 function filterCompositeDone(event)
 { // Called by custom 'done' event
     event.stopImmediatePropagation();
     $(this).unbind( event );
     filterCompositeTrigger();
-    //waitOnFunction(filterCompositeTrigger,$(this));
+
 }
 
 function filterCompositeSelectionChanged(obj)
 {   // filterComposite:onchange Set subtrack selection based upon the changed filter
     // [Not called for filterBy]
-
     var subCBs = $("input.subCB");
     if (subCBs.length > 300) {
         $(obj).one('done',filterCompositeDone);
         return;
     } else
         filterCompositeTrigger();
+
 }
 
+function filterCountFacets(possibleClasses, hiddenViewClasses, possibleSelections)
+{
+
+    possibleClasses = aryRemove(possibleClasses,[ "subCB","changed","disabled" ]);
+
+    if ($(possibleClasses).length > 0) {
+
+	if (aryFind(hiddenViewClasses, possibleClasses[possibleClasses.length-1])=== -1) {
+
+	    for (var vIx=0; vIx < possibleClasses.length; vIx++) {
+		var onePoss = possibleClasses[vIx];
+		if (possibleSelections[onePoss] === undefined) {
+		    possibleSelections[onePoss] = 1;
+		} else {
+		    ++possibleSelections[onePoss];
+		}
+	    }
+
+	}
+    }
+}
+
+function filterCompositeExcludeOptionsQuick(multiSelect, possibleSelections)
+{ // Will mark all options in one filterComposite boxes that are inconsistent with the current
+  // selections in other filterComposite boxes.  Mark is class ".excluded"
+
+    var magic = "Magic23541236746574569670787890877563457346846578452346457435645t794312";
+
+    if (possibleSelections[magic] === undefined) {
+
+	var allSubCBs = $("input.subCB");
+	if (allSubCBs.length === 0) {
+	    return false;
+	}
+
+	// IE takes tooo long, so this should be called only when leaving the filterBy box
+	if (theClient.isIePre11() && $(allSubCBs).filter(":checked").length > 300) 
+	    return false;
+
+	// Walk through all subCBs to get other related tags
+	$( allSubCBs ).each( function (i)  {
+	    // is it viz?
+	    if (this.checked && isFauxDisabled(this,true) === false) {
+		var possibleClasses = $( this ).attr("class").split(" ");
+		// [] because it does not need view filtering
+		filterCountFacets(possibleClasses, [], possibleSelections);  
+	    }
+	});
+	possibleSelections[magic] = 1;  // mark that it has been initialized
+    } 
+
+
+    // Walk through all options in this filterBox to set excluded class
+    var opts = $(multiSelect).children("option");
+    for (var ix = 1;ix < opts.length;ix++) { // All is always allowed
+	var count = possibleSelections[opts[ix].value];
+	if (count === undefined) {
+	    count = 0;
+	}
+	opts[ix].facetCount = count;
+    }
+
+    return true;
+    
+}
+
+
 function filterCompositeExcludeOptions(multiSelect)
 { // Will mark all options in one filterComposite boxes that are inconsistent with the current
   // selections in other filterComposite boxes.  Mark is class ".excluded"
+
     // Compare to the list of all trs
     var allSubCBs = $("input.subCB");
-    if (allSubCBs.length === 0)
+    if (allSubCBs.length === 0) {
         return false;
+    }
 
     // IE takes tooo long, so this should be called only when leaving the filterBy box
     if (theClient.isIePre11() && $(allSubCBs).filter(":checked").length > 300) 
         return false;
 
-    var filterClass = filterCompFilterVar(multiSelect);
-    if (!filterClass || filterClass.length === 0)
+    var filterClass = filterCompFilterVar(multiSelect);  // returns facet name e.g. sample_source
+    if (!filterClass || filterClass.length === 0) {
         return false; 
+    }
 
+    var updated = false;
     // Look at list of CBs that would be selected if all were selected for this filter
-    var subCbsSelected = filterCompSubCBsSurviving(filterClass);
-    if (subCbsSelected.length === 0)
-        return false;
+    var subCbsSelected = filterCompSubCBsSurviving(filterClass);  // apply facet filters for all columns except this one.
+    if (subCbsSelected.length === 0) {
+	updated = true;
+    }
 
     if (allSubCBs.length === subCbsSelected.length) {
         $(multiSelect).children('option.excluded').removeClass('excluded');
-        return true;
+	updated = true;
     }
 
+
     // Walk through all selected subCBs to get other related tags
-    var possibleSelections = [];  // empty array
-    $( subCbsSelected ).each( function (i)  {
+    var possibleSelections  = {};    // empty object acting as hash for selected tracks
+    var possibleSelectionsX = {};    //  for non-selected tracks, used for not selected but choosable facet values.
+
+    hiddenViewClasses=matViewClasses('hidden');
 
+    $( subCbsSelected ).each( function (i)  {
 	var possibleClasses = $( this ).attr("class").split(" ");
-        if ($(possibleClasses).length > 0)
-            possibleClasses = aryRemove(possibleClasses,[ "subCB","changed","disabled" ]);
-        if ($(possibleClasses).length > 0)
-            possibleSelections = possibleSelections.concat(possibleClasses);
+	// is subtrack selected and viz?
+	if (this.checked && isFauxDisabled(this,true) === false) {
+	    filterCountFacets(possibleClasses, hiddenViewClasses, possibleSelections);
+        } else {
+	    filterCountFacets(possibleClasses, hiddenViewClasses, possibleSelectionsX);
+	}
     });
 
     // Walk through all options in this filterBox to set excluded class
-    var updated = false;
-    if (possibleSelections.length > 0) {
     var opts = $(multiSelect).children("option");
     for (var ix = 1;ix < opts.length;ix++) { // All is always allowed
-            if (aryFind(possibleSelections,opts[ix].value) === -1) {
+	var count = possibleSelections[opts[ix].value];
+	if (count === undefined) {
+	    count = possibleSelectionsX[opts[ix].value];
+	    if (count === undefined) {
+		count = 0;
+	    }
+	}
+	opts[ix].facetCount = count;
+	if (count === 0) {
 	    if ($(opts[ix]).hasClass('excluded') === false) {
 		$(opts[ix]).addClass('excluded');
 		updated = true;
 	    }
-            } else if ($(opts[ix]).hasClass('excluded')) {
+	} else {
+	    if ($(opts[ix]).hasClass('excluded')) {
 		$(opts[ix]).removeClass('excluded');
 		updated = true;
 	    }
 	}
     }
+
     return updated;
 }
 
 function filterCompositeClasses(wantSelected)
 {   // returns an array of classes from the dim ABC filterComp classes:
     // converts "matCB abc rep1"[]s to "rep1","rep2"
     var classes = [];
     var abcFBs = $("select.filterComp");
     if (abcFBs.length > 0) {
         $(abcFBs).each( function(i) {
             // Need to walk through list of options
             var ix=0;
             var allAreSelected = false;
             if (this.options[ix].value === "All") {
                 allAreSelected = this.options[ix].selected;