7ee3f386e0a4d9489d284ded95d19ba951468e7d
hiram
  Tue Nov 10 09:18:15 2020 -0800
now using fontSize from browser for message box and number of data values for mouseOver mean detection refs and working in reverse display mode #21980

diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js
index 2f7ec64..ea4734b 100644
--- src/hg/js/hgTracks.js
+++ src/hg/js/hgTracks.js
@@ -4455,43 +4455,50 @@
                 imageV2.history.pushState({lastDbPos: newDbPos, position: newPos, hgsid: + sid },title,
                                  "hgTracks?db="+getDb()+"&"+newDbPos+"&hgsid="+sid);
             }
         }
     }
 };
 
   ///////////////////////////////////////
  //// mouseOver data display 2020-10 ///
 ///////////////////////////////////////
 var mouseOver = {
 
     spans: {},
     visible: false,
     tracks: {},
+    maximumWidth: {},
     popUpDelay: 1000,   // one second delay before popUp appears
     popUpTimer: null,// handle from setTimeout to use in clearTimout(popUpTimer)
     delayDone: true,   // mouse has not left element, still receiving move evts
     delayInProgress: false,        // true if working with delay timer done
     mostRecentMouseEvt: null,
+    browserTextSize: 12,
 
     // spans{} - key name is track name, value is an array of
-    //                   objects: {x1, x2, value}
+    //                   objects: {x1, x2, v, c}
+    //           where [x1..x2) is the array index where the value 'v'
+    //           is found, and 'c' is the data value count in this value
+    //           i.e. when c > 1 the value is a 'mean' of 'c' data values
     // visible - keep track of window visible or not, value: true|false
     //           shouldn't need to do this here, the window knows if it
     //           is visible or not, just ask it for status
     // tracks{}  - tracks that were set up initially, key is track name
     //             value is the number of boxes (for debugging)
+    // maximumWidth{} - key is track name, value is length of longest
+    //                         number string as measured when rendered
 
     // given hgt_....png file name, change to trackName_....json file name
     jsonFileName: function(imgElement, trackName)
     {
       var jsonFile=imgElement.src.replace("hgt/hgt_", "hgt/" + trackName + "_");
       jsonFile = jsonFile.replace(".png", ".json");
       return jsonFile;
     },
 
     // called from: updateImgForId when it has updated a track in place
     // need to refresh the event handlers and json data
     updateMouseOver: function (trackName)
     {
       var tdData = "td_data_" + trackName;
       var tdDataId  = document.getElementById(tdData);
@@ -4518,36 +4525,47 @@
               mouseOver.fetchMapData(mouseOver.jsonFileName(imgElement, trackName), trackName);
             }
           }
         }
       }
     },
 
     // given an X coordinate: x, find the index idx
     // in the rects[idx] array where rects[idx].x1 <= x < rects[idx].x2
     // returning -1 when not found
     // if we knew the array was sorted on x1 we could get out early
     //   when x < x1
     findRange: function (x, rects)
     {
       var answer = -1;  // assmume not found
-      for ( var idx in rects ) {
+      var idx = 0;
+      if (hgTracks.revCmplDisp) {
+        var rectsLen = rects.length - 1;
+        for ( idx in rects ) {
+           if ((rects[rectsLen-idx].x1 <= x) && (x < rects[rectsLen-idx].x2)) {
+             answer = rectsLen-idx;
+             break;
+           }
+        }
+      } else {
+        for ( idx in rects ) {
            if ((rects[idx].x1 <= x) && (x < rects[idx].x2)) {
              answer = idx;
              break;
            }
         }
+      }
       return answer;
     },
 
     popUpDisappear: function () {
       if (mouseOver.visible) {        // should *NOT* have to keep track !*!
         mouseOver.visible = false;
         $('#mouseOverText').css('display','none');
         $('#mouseOverVerticalLine').css('display','none');
       }
       if (mouseOver.popUpTimer) {
          clearTimeout(mouseOver.popUpTimer);
          mouseOver.popUpTimer = null;
       }
       mouseOver.delayDone = true;
       mouseOver.delayInProgress = false;
@@ -4575,59 +4593,66 @@
 
     // location of mouse relative to the whole page
     //     even when the top of page has scolled off
     var evtX = Math.floor(evt.pageX);
 //  var evtY = Math.floor(evt.pageY);
 //  var offX = Math.floor(evt.offsetX);       // no need for evtY or offX
 
     // find location of this <td> slice in the image, this is the track
     //   image in the graphic, including left margin and center label
     //   This location follows the window scrolling, could go negative
     var tdId  = document.getElementById(evt.currentTarget.id);
     var tdRect = tdId.getBoundingClientRect();
     var tdLeft = Math.floor(tdRect.left);
     var tdTop = Math.floor(tdRect.top);
     var tdHeight = Math.floor(tdRect.height);
-    // clientX appears to be the X coordinate of the mouse hot spot
+    var tdWidth = Math.floor(tdRect.width);
+    var rightSide = tdLeft + tdWidth;
+    // clientX is the X coordinate of the mouse hot spot
     var clientX = Math.floor(evt.clientX);
     // the graphOffset is the index (x coordinate) into the 'spans' definitions
     //  of the data value boxes for the graph.  The magic number three
     //   is used elsewhere in this code, note the comment on the constant
     //   LEFTADD.
     var graphOffset = Math.max(0, clientX - tdLeft - 3);
+    if (hgTracks.revCmplDisp) {
+       graphOffset = Math.max(0, rightSide - clientX);
+    }
 
     var windowUp = false;     // see if window is supposed to become visible
     var foundIdx = -1;
     if (mouseOver.spans[trackName]) {
        foundIdx = mouseOver.findRange(graphOffset, mouseOver.spans[trackName]);
     }
     // can show 'no data' when not found
-    var mouseOverValue = "no data";
+    var mouseOverValue = "no&nbsp;data";
     if (foundIdx > -1) { // value to display
-      if (mouseOver.spans[trackName][foundIdx].m) {
+      if (mouseOver.spans[trackName][foundIdx].c > 1) {
         mouseOverValue = "&nbsp;mean:&nbsp;" + mouseOver.spans[trackName][foundIdx].v + "&nbsp;";
       } else {
         mouseOverValue = "&nbsp;" + mouseOver.spans[trackName][foundIdx].v + "&nbsp;";
       }
     }
     $('#mouseOverText').html(mouseOverValue);
-    var msgWidth = Math.ceil($('#mouseOverText').width());
+    var msgWidth = mouseOver.maximumWidth[trackName];
+    $('#mouseOverText').width(msgWidth);
     var msgHeight = Math.ceil($('#mouseOverText').height());
     var lineHeight = Math.max(0, tdHeight - msgHeight);
     var lineTop = Math.max(0, tdTop + msgHeight);
     var msgLeft = Math.max(0, clientX - (msgWidth/2) - 3); // with magic 3
     var lineLeft = Math.max(0, clientX - 3);  // with magic 3
+    $('#mouseOverText').css('fontSize',mouseOver.browserTextSize);
     $('#mouseOverText').css('left',msgLeft + "px");
     $('#mouseOverText').css('top',tdTop + "px");
     $('#mouseOverVerticalLine').css('left',lineLeft + "px");
     $('#mouseOverVerticalLine').css('top',lineTop + "px");
     $('#mouseOverVerticalLine').css('height',lineHeight + "px");
     windowUp = true;      // yes, window is to become visible
 
     if (windowUp) {     // the window should become visible
       mouseOver.popUpVisible();
     } else {    // the window should disappear
       mouseOver.popUpDisappear();
     } //      window visible/not visible
     },  //      mouseInTrackImage function (evt)
 
     // timeout calls here upon completion
@@ -4689,35 +4714,57 @@
       for (var trackName in arr) {
       mouseOver.spans[trackName] = [];      // start array
       // add a 'mousemove' and 'mouseout' event listener to each track
       //     display object
       var tdData = "td_data_" + trackName;
       var tdDataId  = document.getElementById(tdData);
       if (! tdDataId) { return; } // not sure why objects are not always found
 // from jQuery doc:
 //  As the .mousemove() method is just a shorthand
 //    for .on( "mousemove", handler ), detaching is possible
 //      using .off( "mousemove" ).
       $( tdDataId ).mousemove(mouseOver.mouseMoveDelay);
       $( tdDataId ).mouseout(mouseOver.popUpDisappear);
       var itemCount = 0;	// just for monitoring purposes
       // save incoming x1,x2,v data into the mouseOver.spans[trackName][] array
+      var lengthLongestNumberString = 0;
+      var longestNumber = 0;
+      var hasMean = false;
       for (var span in arr[trackName]) {
+         if (arr[trackName][span].c > 1) { hasMean = true; }
+         var lenV = arr[trackName][span].v.toString().length;
+         if (lenV > lengthLongestNumberString) {
+	   lengthLongestNumberString = lenV;
+	   longestNumber = arr[trackName][span].v;
+         }
         mouseOver.spans[trackName].push(arr[trackName][span]);
        ++itemCount;
       }
       mouseOver.tracks[trackName] = itemCount;	// merely for debugging watch
+      var mouseOverValue = "";
+      if (hasMean) {
+         mouseOverValue = "&nbsp;mean:&nbsp;" + longestNumber + "&nbsp;";
+      } else {
+         mouseOverValue = "&nbsp;" + longestNumber + "&nbsp;";
+      }
+      $('#mouseOverText').html(mouseOverValue);	// see how big as rendered
+      var maximumWidth = Math.ceil($('#mouseOverText').width());
+      $('#mouseOverText').html("no&nbsp;data");	// might be bigger
+      if (Math.ceil($('#mouseOverText').width() > maximumWidth)) {
+          maximumWidth = Math.ceil($('#mouseOverText').width());
+      }
+      mouseOver.maximumWidth[trackName] = maximumWidth;
       }
     },  //      receiveData: function (arr)
 
     failedRequest: function(trackName)
     {   // failed request to get json data, remove it from the track list
       if (mouseOver.tracks[trackName]) {
         delete mouseOver.tracks[trackName];
       }
     },
 
     // =========================================================================
     // fetchMapData() sends JSON request, callback to receiveData() upon return
     // =========================================================================
     fetchMapData: function (url, trackName)
     {
@@ -4747,30 +4794,33 @@
         var jsonData = trackList[i].getAttribute('jsonData');
         if (jsonData) {
           mouseOver.fetchMapData(jsonData, trackName);
         }
       }
     },
 
     // any scrolling turns the popUp message off
     scroll: function()
     {
     if (mouseOver.visible) { mouseOver.popUpDisappear(); }
     },
 
     addListener: function () {
         mouseOver.visible = false;
+        if (window.browserTextSize) {
+           mouseOver.browserTextSize = window.browserTextSize;
+        }
         window.addEventListener('scroll', mouseOver.scroll, false);
         window.addEventListener('load', mouseOver.getData, false);
     }
 };	//	var mouseOver
 
   //////////////////////
  //// track search ////
 //////////////////////
 var trackSearch = {
 
     searchKeydown: function (event)
     {
         if (event.which === 13) {
             // Required to fix problem on IE and Safari where value of hgt_tSearch is "-"
             //    (i.e. not "Search").