1882b8518123019af079080747fa712cd0a75d65
chmalee
  Fri Jan 27 10:48:29 2023 -0800
Add zoom to exon/codon feature to hgTracks right click menu, refs #2724

diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js
index b7cb4db..cd11a07 100644
--- src/hg/js/hgTracks.js
+++ src/hg/js/hgTracks.js
@@ -2138,31 +2138,31 @@
             }
         }
     }
 
 };
 
   ///////////////////////////////////////
  //// rightClick (aka context menu) ////
 ///////////////////////////////////////
 var rightClick = {
 
     menu: null,
     selectedMenuItem: null,   // currently choosen context menu item (via context menu).
     floatingMenuItem: null,
     currentMapItem:   null,
-    supportZoomCodon: false,  // turns on experimental feature (currently only in larry's tree).
+    supportZoomCodon: true,  // add zoom to exon and zoom to codon to right click menu
     clickedHighlightIdx : null,  // the index (0,1,...) of the highlight item that overlaps the last right-click
 
     makeMapItem: function (id)
     {   // Create a dummy mapItem on the fly
         // (for objects that don't have corresponding entry in the map).
         if (id && id.length > 0 && hgTracks.trackDb) {
             var title;
             var rec = hgTracks.trackDb[id];
             if (rec) {
                 title = rec.shortLabel;
             } else {
                 title = id;
             }
             return {id: id, title: "configure " + title};
         } else {
@@ -2189,35 +2189,31 @@
         }
         return null;
     },
 
     windowOpenFailedMsg: function ()
     {
         warn("Your web browser prevented us from opening a new window.\n\n" +
              "Please change your browser settings to allow pop-up windows from " +
              document.domain + ".");
     },
 
     handleZoomCodon: function (response, status)
     {
         var json = JSON.parse(response);
         if (json.pos) {
-            genomePos.set(json.pos, 3);
-            if (document.TrackForm)
-                document.TrackForm.submit();
-            else
-                document.TrackHeaderForm.submit();
+            imageV2.navigateInPlace("position="+json.pos);
         } else {
             alert(json.error);
         }
     },
 
     handleViewImg: function (response, status)
     {   // handles view image response, which must get new image without imageV2 gimmickery
         jQuery('body').css('cursor', '');
         var str = "<IMG[^>]*SRC='([^']+)'";
         var reg = new RegExp(str);
         var a = reg.exec(response);
         if (a && a[1]) {
             if ( ! window.open(a[1]) ) {
                 rightClick.windowOpenFailedMsg();
             }
@@ -2784,30 +2780,31 @@
                                     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";
@@ -2817,30 +2814,34 @@
                             // 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 "exonNumbers" here, so just use a heuristic to see if it's on
                         if (title.search(/, strand [+-], (Intron|Exon) /)!==-1) {
+                            re = /(Exon) ([1-9]+) of/;
+                            matches = re.exec(title);
+                            if (matches !== null && matches[2].length > 0)
+                                exonNum = matches[2];
                             title = title.split(",")[0];
                         }
 
                         else 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) {
@@ -2879,39 +2880,49 @@
                                         reg = new RegExp("g=([^&]+)");
                                         a = reg.exec(href);
                                         if (a && a[1]) {
                                             table = a[1];
                                         }
                                     }
                                 }
                                 if (name && table) {
                                     o[rightClick.makeImgTag("magnify.png")+" Zoom to codon"] =
                                     {   onclick: function(menuItemClicked, menuObject) {
                                             rightClick.hit(menuItemClicked, menuObject,
                                                         "zoomCodon",
                                                         {name: name, table: table});
                                             return true;}
                                     };
+                                    if (exonNum > 0) {
                                         o[rightClick.makeImgTag("magnify.png")+" Zoom to exon"] = {
                                             onclick: function(menuItemClicked, menuObject) {
-                                            rightClick.hit(menuItemClicked, menuObject,
-                                                          "zoomExon",
-                                                          {name: name, table: table});
+                                                $.ajax({
+                                                        type: "GET",
+                                                        url: "../cgi-bin/hgApi",
+                                                        data: cart.varsToUrlData({ 'db': getDb(),
+                                                                'cmd': "exonToPos", 'num': exonNum,
+                                                                'table': table, 'name': name}),
+                                                        trueSuccess: rightClick.handleZoomCodon,
+                                                        success: catchErrorOrDispatch,
+                                                        error: errorHandler,
+                                                        cache: true
+                                                    });
                                                 return true; }
                                         };
                                     }
                                 }
+                            }
                             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 && href.indexOf("i=mergedItem") === -1) {