677dcea9305f7851a04cc8d7204479ffa8e0554c
max
  Fri May 16 08:46:02 2025 -0700
fixing right-click exon options for gene pred tracks, refs #35756

diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js
index 6fa8a66894e..c63f3a3fa31 100644
--- src/hg/js/hgTracks.js
+++ src/hg/js/hgTracks.js
@@ -2737,36 +2737,49 @@
     },
 
     makeImgTag: function (img)
     {   // Return img tag with explicit dimensions for img (dimensions are currently hardwired).
         // This fixes the "weird shadow problem when first loading the right-click menu"
         // seen in FireFox 3.X, which occurred b/c FF doesn't actually fetch the image until
         // the menu is being shown.
         return "<img style='width:16px; height:16px; border-style:none;' src='../images/" +
                 img + "' />";
     },
 
 
     // CGIs now use HTML tags, e.g. "<b>Transcript:</b> ENST00000297261.7<br><b>Strand:</b>"
     mouseOverToLabel: function(title)
     {
-        if (title.search(/<b>Transcript:<[/]b>/) !== -1) {
+        if (title.search(/<b>Transcript: ?<[/]b>/) !== -1) {
             title = title.split("<br>")[0].split("</b>")[1];
         }
         return title;
     },
 
+    // when "exonNumbers on", the mouse over text is not a good item description for the right-click menu
+    // "exonNumbers on" is the default for genePred/bigGenePred tracks but can also be actived for bigBed and others
+    // We don't have the value of the tdb variable "exonNumbers" here, so just use a heuristic to see if it's on
+    mouseOverToExon: function(title)
+    {
+        var exonNum = 0;
+        var exonRe = /(Exon) ([1-9]+) /;
+        var matches = exonRe.exec(title);
+        if (matches !== null && matches[2].length > 0)
+            exonNum = matches[2];
+        return exonNum;
+    },
+
     load: function (img)
     {
         rightClick.menu = img.contextMenu(function() {
             popUp.cleanup();   // Popup box is not getting closed properly so must do it here
             if ( ! rightClick.selectedMenuItem )  // This is literally an edge case so ignore
                 return;
 
             var o; // TODO: Break this giant routine with shared vars into some sub-functions
             var str;
             var rec = null;
             var menu = [];
             var selectedImg = rightClick.makeImgTag("greenChecksm.png");
             var blankImg    = rightClick.makeImgTag("invisible16.png");
             var done = false;
             if (rightClick.selectedMenuItem && rightClick.selectedMenuItem.id) {
@@ -2865,67 +2878,68 @@
                                     o[str] = { onclick:
                                                 rightClick.makeHitCallback(visStrings[i])
                                              };
                                     menu.push(o);
                                 }
                             }
                             done = true;
                         }
                     }
                 }
 
                 if (done) {
                     o = {};
                     var any = false;
                     var title = rightClick.selectedMenuItem.title || "feature";
-                    var exonNum = 0;
                     var maxLength = 60;
-                    if (title.length > maxLength) {
-                        title = title.substring(0, maxLength) + "...";
-                    }
 
                     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
                             || rec.type.indexOf("bigBarChart") === 0) {
                                 a = /i=([^&]+)/.exec(href);
                                 if (a && a[1]) {
                                     title = a[1];
                                 }
                             }
                         }
 
-                        // when "exonNumbers on", the mouse over text is not a good item description for the right-click menu
-                        // "exonNumbers on" is the default for genePred/bigGenePred tracks but can also be actived for bigBed and others
-                        // We don't have the value of the tdb variable "exonNumbers" here, so just use a heuristic to see if it's on
+                        // pick out the exon number from the mouseover text
+                        // Probably should be a data-exonNum tag on the DOM element
+                        var exonNum = rightClick.mouseOverToExon(title);
 
+                        // remove special genePred exon mouseover html text
                         // CGIs now use HTML tags, e.g. "<b>Transcript:</b> ENST00000297261.7<br><b>Strand:</b>"
                         title = rightClick.mouseOverToLabel(title);
 
+                        if (title.length > maxLength) {
+                            title = title.substring(0, maxLength) + "...";
+                        }
+
                         if (isHgc && ( href.indexOf('g=gtexGene')!== -1 
                                             || href.indexOf('g=unip') !== -1 
                                             || href.indexOf('g=knownGene') !== -1 )) {
                             // For GTEx gene and UniProt mouseovers, replace title (which may be a tissue name) with 
                             // item (gene) name. Also need to unescape the urlencoded characters and the + sign.
                             a = /i=([^&]+)/.exec(href);
                             if (a && a[1]) {
                                 title = decodeURIComponent(a[1].replace(/\+/g, " "));
                             }
                         }
 
                         if (displayItemFunctions) {
                             o[rightClick.makeImgTag("magnify.png") + " Zoom to " +  title] = {
                                 onclick: function(menuItemClicked, menuObject) {
                                             rightClick.hit(menuItemClicked, menuObject,