fecc0bfffd30dccc9998aa0c2e5a6064d487bbb1
chmalee
  Fri Apr 10 15:39:54 2020 -0700
Initial work on merging multiple bigBed items that span the window, refs #25133

diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js
index 7fb1abe..3494f3a 100644
--- src/hg/js/hgTracks.js
+++ src/hg/js/hgTracks.js
@@ -714,30 +714,43 @@
     // Verify that click-competing operation (e.g. dragging) isn't currently occurring.
     { 
         return (posting.blockUseMap === false); 
     },
 
     blockTheMapOnMouseMove: function (ev)
     {
         if (!posting.blockUseMap && mouse.hasMoved(ev)) {
             posting.blockUseMap=true;
         }
     },
 
     mapClk: function ()
     {
         var done = false;
+        // if we clicked on a merged item then show all the items, similar to clicking a
+        // dense track to turn it to pack
+        if (this && this.href && this.href.indexOf("i=mergedItem") !== -1) {
+            var id = this.href.slice(this.href.indexOf("&g="));
+            id = id.split(/&[^=]+=/)[1];
+            updateObj={};
+            updateObj[id+".doMergeItems"] = 0;
+            hgTracks.trackDb[id][id+".doMergeItems"] = 0;
+            cart.setVarsObj(updateObj,null,false);
+            imageV2.requestImgUpdate(id, id + ".doMergeItems=0");
+            return false;
+        }
+
         if (false && imageV2.inPlaceUpdate) {
             // XXXX experimental and only turned on in larrym's tree.
             // Use in-place update if the map item just modifies the current position (this is nice
             // because it's faster and preserves the users relative position in the track image).
             //
             // First test handles next/prev item.
             var str = "/cgi-bin/hgTracks\\?position=([^:]+):(.+)&hgsid=(\\d+)" +
                                                                     "&(hgt.(next|prev)Item=[^&]+)";
             var reg = new RegExp(str);
             var a = reg.exec(this.href);
             if (a && a[1] && a[1] === hgTracks.chromName) {
                 imageV2.navigateInPlace("position=" + encodeURIComponent(a[1] + ":" + a[2]) + 
                                                                             "&" + a[4], null, true);
                 done = true;
             } else {
@@ -2899,31 +2912,46 @@
                     } else {
                         genomePos.setByCoordinates(newPos.chrom, newPos.start, newPos.end);
                         jQuery('body').css('cursor', 'wait');
                         document.TrackHeaderForm.submit();
                     }
                 }
             }
 
         } else if (cmd === 'removeHighlight') {
 
             var highlights = hgTracks.highlight.split("|");
             highlights.splice(rightClick.clickedHighlightIdx, 1); // splice = remove element from array
             hgTracks.highlight = highlights.join("|");
             cart.setVarsObj({'highlight' : hgTracks.highlight});
             imageV2.highlightRegion();
-
+        } else if (cmd === 'toggleMerge') {
+            // toggle both the cart (if the user goes to trackUi)
+            // and toggle args[key], if the user doesn't leave hgTracks
+            var key = id + ".doMergeItems";
+            var updateObj = {};
+            if (args[key] === 1) {
+                args[key] = 0;
+                updateObj[key] = 0;
+                cart.setVarsObj(updateObj,null,false);
+                imageV2.requestImgUpdate(id, id + ".doMergeItems=0");
+            } else {
+                args[key] = 1;
+                updateObj[key] = 1;
+                cart.setVars(updateObj,null,false);
+                imageV2.requestImgUpdate(id, id + ".doMergeItems=1");
+            }
         } else {   // if ( cmd in 'hide','dense','squish','pack','full','show' )
             // Change visibility settings:
             //
             // First change the select on our form:
             rec = hgTracks.trackDb[id];
             selectUpdated = vis.update(id, cmd);
 
             // Now change the track image
             if (imageV2.enabled && cmd === 'hide') {
                 // Hide local display of this track and update server side cart.
                 // Subtracks controlled by 2 settings so del vis and set sel=0.
                 if (tdbIsSubtrack(rec)) {
                     // Remove subtrack level vis and explicitly uncheck.
                     cart.setVars( [ id, id+"_sel" ], [ '[]', 0 ] ); 
                 } else if (tdbIsFolderContent(rec)) {
@@ -3088,31 +3116,31 @@
                                 }
                             }
                             done = true;
                         }
                     }
                 }
 
                 if (done) {
                     o = {};
                     var any = false;
                     var title = rightClick.selectedMenuItem.title || "feature";
                     var maxLength = 60;
                     if (title.length > maxLength) {
                         title = title.substring(0, maxLength) + "...";
                     }
-                    if (isGene || isHgc || id === "wikiTrack") {
+                    if ((isGene || isHgc || id === "wikiTrack") && href.indexOf("i=mergedItem") === -1) {
                         // Add "Open details..." item
                         var displayItemFunctions = false;
                         if (rec) {
                             if (rec.type.indexOf("wig") === 0
                             ||  rec.type.indexOf("bigWig") === 0
                             ||  id === "wikiTrack") {
                                 displayItemFunctions = false;
                             } else if (rec.type.indexOf("expRatio") === 0) {
                                 displayItemFunctions = title !== "zoomInMore";
                             } else {
                                 displayItemFunctions = true;
                             }
                             // For barChart mouseovers, replace title (which may be a category 
                             // name+value) with item name
                             if (rec.type.indexOf("barChart") === 0
@@ -3190,31 +3218,31 @@
                             }
                             o[rightClick.makeImgTag("dnaIcon.png")+" Get DNA for "+title] = {
                                 onclick: function(menuItemClicked, menuObject) {
                                     rightClick.hit(menuItemClicked, menuObject, "getDna");
                                     return true; }
                             };
                         }
                         o[rightClick.makeImgTag("bookOut.png")+
                                                 " Open details page in new window..."] = {
                             onclick: function(menuItemClicked, menuObject) {
                                 rightClick.hit(menuItemClicked, menuObject, "openLink");
                                 return true; }
                         };
                         any = true;
                     }
-                    if (href && href.length  > 0) {
+                    if (href && href.length  > 0 && href.indexOf("i=mergedItem") === -1) {
                         // Add "Show details..." item
                         if (title.indexOf("Click to alter ") === 0) {
                             // suppress the "Click to alter..." items
                         } else if (rightClick.selectedMenuItem.href.indexOf("cgi-bin/hgTracks")
                                                                                         !== -1) {
                             // suppress menu items for hgTracks links (e.g. Next/Prev map items).
                         } else {
                             var item;
                             if (title === "zoomInMore")
                                 // avoid showing menu item that says
                                 // "Show details for zoomInMore..." (redmine 2447)
                                 item = rightClick.makeImgTag("book.png") + " Show details...";
                             else
                                 item = rightClick.makeImgTag("book.png")+" Show details for "+
                                        title + "...";
@@ -3260,30 +3288,45 @@
                     o[rightClick.makeImgTag("folderWrench.png")+" Configure "+rec.shortLabel +
                       " track set..."] = {
                         onclick: function(menuItemClicked, menuObject) {
                             rightClick.hit(menuItemClicked, menuObject, "hgTrackUi_follow");
                             return true; }
                       };
                 }
                 if (jQuery.floatMgr) {
                     o[(rightClick.selectedMenuItem.id === rightClick.floatingMenuItem ?
                             selectedImg : blankImg) + " float"] = {
                         onclick: function(menuItemClicked, menuObject) {
                             rightClick.hit(menuItemClicked, menuObject, "float");
                             return true; }
                     };
                 }
+                // add a toggle to hide/show the merged item(s)
+                mergeTrack = rightClick.selectedMenuItem.id + ".doMergeItems";
+                if (rec.hasOwnProperty(mergeTrack)) {
+                    var hasMergedItems = rec[mergeTrack] === 1;
+                    titleStr = rightClick.makeImgTag("wrench.png") + " ";
+                    if (hasMergedItems) {
+                        titleStr += "Show merged items";
+                    } else {
+                        titleStr += "Merge items that span the current region";
+                    }
+                    o[titleStr] = {onclick: function(menuItemClick, menuObject) {
+                        rightClick.hit(menuItemClick, menuObject, "toggleMerge", rec);
+                        return true; }
+                    };
+                }
                 menu.push($.contextMenu.separator);
                 menu.push(o);
             }
 
             menu.push($.contextMenu.separator);
             if (hgTracks.highlight && rightClick.clickedHighlightIdx!==null) {
 
                 if (hgTracks.highlight.search(getDb() + '.') === 0) {
                     var currentlySeen = ($('#highlightItem').length > 0); 
                     o = {};
                     // Jumps to highlight when not currently seen in image
                     var text = (currentlySeen ? " Zoom" : " Jump") + " to highlight";
                     o[rightClick.makeImgTag("highlightZoom.png") + text] = {
                         onclick: rightClick.makeHitCallback('jumpToHighlight')
                     };