2f73d7595b0e37b159e9c9355f8e99512bf37e81
max
  Wed Sep 10 08:33:03 2025 -0700
Merging Jim Robinsons code into the kent tree. Due to whitespace changes in his IDE, I'm merging this manually.
The original code is at https://github.com/igvteam/ucsc_dev/. changes up to 48816bc are included.
Also adding an API call to get the 2bit file and code to use it.

diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js
index 5e07d09597f..887a8c2fd36 100644
--- src/hg/js/hgTracks.js
+++ src/hg/js/hgTracks.js
@@ -32,30 +32,34 @@
 if (!String.prototype.startsWith) {
   String.prototype.startsWith = function(searchString, position) {
     position = position || 0;
     return this.indexOf(searchString, position) === position;
   };
 }
 
 function initVars()
 {  // There are various entry points, so we call initVars in several places to make sure all is well
     if (typeof(hgTracks) !== "undefined" && !genomePos.original) {
         // remember initial position and size so we can restore it if user cancels
         genomePos.original = genomePos.getOriginalPos();
         genomePos.originalSize = $('#size').text().replace(/,/g, ""); // strip out any commas
         dragSelect.originalCursor = jQuery('body').css('cursor');
 
+        if (typeof (igv) !== "undefined") {
+            igv.initIgvUcsc();
+        }
+
         imageV2.imgTbl = $('#imgTbl');
         // imageV2.enabled === true unless: advancedJavascript===false, or trackSearch, or config pg
         imageV2.enabled = (imageV2.imgTbl && imageV2.imgTbl.length > 0);
 
         // jQuery load function with stuff to support drag selection in track img
         if (theClient.isSafari()) {
             // Safari has the following bug: if we update the hgTracks map dynamically,
             // the browser ignores the changes (even though if you look in the DOM the changes
             // are there). So we have to do a full form submission when the user changes
             // visibility settings or track configuration.
             // As of 5.0.4 (7533.20.27) this is problem still exists in safari.
             // As of 5.1 (7534.50) this problem appears to have been fixed - unfortunately,
             // logs for 7/2011 show vast majority of safari users are pre-5.1 (5.0.5 is by far
             // the most common).
             //
@@ -1958,30 +1962,35 @@
         if (newPortalEnd > hgTracks.chromEnd && bounded) {
             newPortalEnd = hgTracks.chromEnd;
             newPortalStart = newPortalEnd - portalWidthBases;
             recalculate = true;
         }
         if (newPortalStart > 0) {
             var newPos = hgTracks.chromName + ":" + newPortalStart + "-" + newPortalEnd;
             genomePos.set(newPos); // no need to change the size
         }
         if (recalculate && hgTracks.imgBoxBasesPerPixel > 0) { 
             // Need to recalculate X for bounding drag
             portalScrolledX = (closedPortalStart - newPortalStart) / hgTracks.imgBoxBasesPerPixel;
             newOffsetX = portalScrolledX - (hgTracks.imgBoxPortalOffsetX+hgTracks.imgBoxLeftLabel);
         }
 
+        if (typeof (igv) !== "undefined") {
+           igv.updateIgvStartPosition(newPortalStart);
+        }
+
+
         ret = {};
         ret.newX = newOffsetX;
         ret.isOutsideChrom = recalculate;
         return ret;
     }
     function mapTopAndBottom(mapName,east,west)
     {
     // Find the top and bottom px given left and right boundaries
         var mapPortal = { top: -10, bottom: -10 };
         var items = $("map[name='"+mapName+"']").children();
         if ($(items).length>0) {
             $(items).each(function(t) {
                 var loc = this.coords.split(",");
                 var aleft   = parseInt(loc[0]);
                 var aright  = parseInt(loc[2]);
@@ -4278,30 +4287,33 @@
         }
         
         imageV2.loadRemoteTracks();
         makeItemsByDrag.load();
         imageV2.loadSuggestBox();
         imageV2.drawHighlights();
 
         if (imageV2.backSupport) {
             imageV2.setInHistory(false);    // Set this new position into History stack
         } else {
             imageV2.markAsDirtyPage();
         }
         if (typeof showMouseovers !== 'undefined' && showMouseovers) {
             convertTitleTagsToMouseovers();
         }
+        if(typeof window.igvBrowser !== "undefined") {
+           window.igvBrowser.search(genomePos.get());
+        }
     },
 
     updateImgForId: function (html, id, fullImageReload, newJsonRec)
     {   // update row in imgTbl for given id.
         // return true if we successfully pull slice for id and update it in imgTrack.
         var newTr = $(html).find("tr[id='tr_" + id + "']");
         if (newTr.length > 0) {
             var tr = $(document.getElementById("tr_" + id));
             if (tr.length > 0) {
                 $(tr).html(newTr.children());
 
                 // Need to update tr class list too
                 var classes = $(html).find("tr[id='tr_"+ id + "']")[0].className;
                 if (classes && classes.length > 0) {
                     $(tr).removeClass();
@@ -4478,30 +4490,31 @@
         // force reload of whole page via trackform submit
         // This function does not return
         jQuery('body').css('cursor', 'wait');
         if (extraData || cart.updatesWaiting()) {
             var url = cart.addUpdatesToUrl(window.location.href);
             if (extraData) {
                 if ( url.lastIndexOf("?") === -1)
                     url += "?" + extraData;
                 else
                     url += '&' + extraData;
             }
             window.location.assign(url);
             return false;
         }
         document.TrackHeaderForm.submit();
+        window.igv.initUcsc();
     },
 
     updateImgAndMap: function (response, status)
     {   // Handle ajax response with an updated trackMap image, map and optional ideogram. 
         //    and maybe the redLines background too.
         // this.cmd can be used to figure out which menu item triggered this.
         // this.id === appropriate track if we are retrieving just a single track.
 
         // update local hgTracks.trackDb to reflect possible side-effects of ajax request.
 
         var newJson = scrapeVariable(response, "hgTracks");
 
         //alert(JSON.stringify(newJson)); // DEBUG Example
 
         var oldJson = hgTracks;
@@ -6022,15 +6035,55 @@
 
     var skipNotification = localStorage.getItem("hgTracks.hideSpeedNotification");
     dumpCart(loadSeconds, skipNotification);
         
     if (skipNotification)
         return;
 
     msg = "This page took "+loadSeconds+" seconds to load, more than "+maxSeconds+" seconds. We strive to keep "+
         "the UCSC Genome Browser quick and responsive. See our "+
         "<b><a href='../FAQ/FAQtracks.html#speed' target='_blank'>display speed FAQ</a></b> for "+
         "common causes and solutions to slow performance. If this problem continues, you can create a  "+
         "session link via <b>My Data</b> &gt; <b>My Sessions</b> and send the link to <b>genome-www@soe.ucsc.edu</b>.";
     notifBoxSetup("hgTracks", "hideSpeedNotification", msg);
     notifBoxShow("hgTracks", "hideSpeedNotification");
 }
+
+//////////////////////////
+// Attempt to support panning by dragging IGV track.  The dragging works, but its not clear to do on drag end.
+
+document.addEventListener('DOMContentLoaded', function () {
+    if (typeof igv !== 'undefined') {
+
+        // TODO -- probably should use genomePos.get() and set() here, but I (JTR) don't fully understand that
+        // object and calling set() seems to have side effects.  So this variable is a placeholder.
+
+        const pos = {};
+
+        igv.ucscTrackpan = (newPosition) => {
+
+            const positionOffset = (newPosition - hgTracks.imgBoxPortalStart);
+            const pixelOffset = Math.round(positionOffset / hgTracks.imgBoxBasesPerPixel);
+            const newX = -(pixelOffset + hgTracks.imgBoxLeftLabel);
+            var nowPos = newX.toString() + "px";
+            $(".panImg").css({'left': nowPos});
+            $('.tdData').css({'backgroundPosition': nowPos});
+
+            pos.chrom = hgTracks.chromName;
+            pos.start = Math.round(newPosition + 1);
+            pos.end = pos.start + (hgTracks.winEnd - hgTracks.winStart);  // Dragging will not change the bp width
+
+            $('#positionDisplay').text(pos.chrom + ":" + commify(pos.start) + "-" + commify(pos.end));
+        };
+
+        igv.ucscTrackpanEnd = () => {
+            if (imageV2.inPlaceUpdate) {
+                imageV2.navigateInPlace("db=" + getDb() + "&position=" +
+                    encodeURIComponent(pos.chrom + ":" + pos.start + "-" + pos.end),
+                    null, false);
+            } else {
+                document.TrackHeaderForm.submit();
+            }
+        };
+    }
+});
+