8fa873a8a5051f55f7d6f6473e438b314e0c4bc1
max
  Mon Dec 16 07:57:12 2024 -0800
trying to improve the hide tracks function again, refs #34959

diff --git src/hg/js/utils.js src/hg/js/utils.js
index 28f0a0a..1b9c28a 100644
--- src/hg/js/utils.js
+++ src/hg/js/utils.js
@@ -480,75 +480,99 @@
         cartVals.push(0);
     } else if (tdbIsFolderContent(rec)) {
         // supertrack children need to have _sel set to trigger superttrack reshaping
         cartVars.push(id);
         cartVals.push('hide');
 
         cartVars.push(id+"_sel");
         cartVals.push(0);
     } else {
         // normal, top-level track
         cartVars.push(id);
         cartVals.push('hide');
     }
 }
 
-function tdbFindChildless(trackDb, delTracks) {
-    /* Find parents that have no children left anymore in hgTracks.trackDb if you remove delTracks.
-     * return obj with o.loneParents as array of [parent, array of children] , and o.others as an array of all other tracks*/
-    others = [];
-
+function tdbCountChildren(trackDb, parentType) {
+    /* return dicts with count of children, uses either the .parentTrack or .topParent trackDb attributes */
     var familySize = {};
     var families = {};
     // sort trackDb into object topParent -> count of children
     for (var trackName of Object.keys(hgTracks.trackDb)) {
         var rec = hgTracks.trackDb[trackName];
-        if (rec.topParent===undefined) {
-            //others.push(trackName);
-            continue; // ignore top-level tracks
+        // when looking at superTracks, only look at those children that are in superTracks
+        if (rec[parentType]===undefined) {
+            continue; // ignore tracks without parents
         }
 
-        var topParent = rec.topParent;
-        if (!familySize.hasOwnProperty(topParent)) {
-            familySize[topParent] = 0;
-            families[topParent] = [];
+        var parentTrack = rec[parentType];
+        if (!familySize.hasOwnProperty(parentTrack)) {
+            familySize[parentTrack] = 0;
+            families[parentTrack] = [];
         }
-        familySize[topParent]++;
-        families[topParent].push(trackName);
+        familySize[parentTrack]++;
+        families[parentTrack].push(trackName);
     }
 
+    var ret = {};
+    ret.familySize = familySize;
+    ret.families = families;
+    return ret;
+}
+
+function tdbFindChildless(trackDb, delTracks) {
+    /* Find parents that have no children left anymore in hgTracks.trackDb if you remove delTracks.
+     * return obj with o.loneParents as array of [parent, array of children] , and o.others as an array of all other tracks
+     * The caller needs the children names, as we want to hide the children with Javascript right away */
+
+    // This functions uses a somewhat weird strategy: it counts the children of the composites, also counts the children of superTracks,
+    // and compares both at the end. There may be a better strategy but our data structures are so strange that I didn't know what
+    // else to do.
+    var parentType = "parentTrack";
+    others = [];
+
+    var compLinks = tdbCountChildren(trackDb, "parentTrack"); // look only at direct parents: superTracks and composites
+    var topLinks = tdbCountChildren(trackDb, "topParent");  // look at top parents, so only superTracks
+
     // decrease the parent's count for each track to delete
     for (var delTrack of delTracks) {
         var tdbRec = hgTracks.trackDb[delTrack];
-        if (tdbRec.topParent)
-            familySize[tdbRec.topParent]--;
+        let parentName = tdbRec.parentTrack;
+        if (parentName)
+            compLinks.familySize[parentName]--;
+
+        parentName = tdbRec.topParent;
+        if (parentName)
+            topLinks.familySize[parentName]--;
     }
 
     // for the parents of deleted tracks with a count of 0, create an array of [parentName, children]
     var loneParents = [];
     var doneParents = [];
     for (delTrack of delTracks) {
-        var parentName = hgTracks.trackDb[delTrack].topParent;
+        var parentName = hgTracks.trackDb[delTrack].parentTrack;
+        var topParentName = hgTracks.trackDb[delTrack].topParent;
         if (parentName) {
-            if (familySize[parentName]===0) {
+            // hide a superTrack parent only if that superTrack does not have any other tracks open
+            if (compLinks.familySize[parentName]===0 && topLinks.familySize[topParentName]===0) {
                 if (!doneParents.includes(parentName)) {
-                    loneParents.push([parentName, families[parentName]]);
+                    loneParents.push([parentName, compLinks.families[parentName]]);
                     doneParents.push(parentName);
                 }
             } else
-                for (var child of families[parentName])
-                    // but do not hide tracks of lone parents that are not in delTracks
+                for (var child of compLinks.families[parentName])
+                    // do not hide tracks of lone parents that are not in delTracks
                     if (delTracks.includes(child))
                         others.push(child);
         } else {
             others.push(delTrack);
         }
     }
 
     o = {};
     o.loneParents = loneParents;
     o.others = others;
     return o;
 }
 
 function aryFind(ary,val)
 {// returns the index of a value on the array or -1;