fa4c30bd748a9c94307d007cba26dd1505995e82
hiram
  Fri Oct 30 16:38:22 2020 -0700
add error handline on failed json read and vertical hightline line to indicate where on graph value is and turn off popUp when window scrolls refs #21980

diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js
index 9602be2..2e634c7 100644
--- src/hg/js/hgTracks.js
+++ src/hg/js/hgTracks.js
@@ -4478,123 +4478,134 @@
 
     // 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)
     {
       if (mouseOver.tracks[trackName]) {
       // there should be a more simple jQuery function to bind these events
-      var tdName = "td_data_" + trackName;
-      var tdElement  = document.getElementById(tdName);
+      var tdData = "td_data_" + trackName;
+      var tdElement  = document.getElementById(tdData);
       var id = tdElement.id;
       tdElement.addEventListener('mousemove', mouseOver.mouseInTrackImage);
       tdElement.addEventListener('mouseout', mouseOver.popUpDisappear);
-      var imgName = "img_data_" + trackName;
-      var imgElement  = document.getElementById(imgName);
-      mouseOver.fetchMapData(mouseOver.jsonFileName(imgElement, trackName));
+      var imgData = "img_data_" + trackName;
+      var imgElement  = document.getElementById(imgData);
+      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 ) {
          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 !*!
 //       $('#mouseOverText').hide();       // does not function ?
         var msgWindow = document.querySelector(".wigMouseOver");
         msgWindow.classList.toggle("showMouseOver");
         mouseOver.visible = false;
+//        $('#mouseOverContainer').css('display','none'); // does not work
+        $('#mouseOverLine').css('display','none');
       }
     },
 
     popUpVisible: function () {
       if (! mouseOver.visible) {        // should *NOT* have to keep track !*!
 //      $('#mouseOverText').show();     // does not function ?
       var msgWindow = document.querySelector(".wigMouseOver");
         msgWindow.classList.toggle("showMouseOver");
         mouseOver.visible = true;
+//        $('#mouseOverContainer').css('display','block');  // does not work
+        $('#mouseOverLine').css('display','block');
       }
     },
 
     //the evt.target.id is the img_data_<trackName> element of the track graphic
     mouseInTrackImage: function (evt)
     {
     // the center label also events here, can't use that
     //  plus there is a one pixel line under the center label that has no
     //   id name at all, so verify we are getting the event from the correct
     //   element.
     if (! evt.target.id.includes("img_data_")) { return; }
     var trackName = evt.target.id.replace("img_data_", "");
     if (trackName.length < 1) { return; }	// verify valid trackName
     // 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 tdName = "td_data_" + trackName;
     var tdId  = document.getElementById(tdName);
     var tdRect = tdId.getBoundingClientRect();
     var tdLeft = Math.floor(tdRect.left);
     var tdTop = Math.floor(tdRect.top);
     // find the location of the image itself, this could be the single complete
     //  graphic image of all the tracks, or possibly the single image of the
     //  track itself.  This location also follows the window scrolling and can
     //  even go negative when the web browser scrolls a window that is larger
     //  than the width of the web browser.
     var imageId = document.getElementById(evt.target.id);
     var imageRect = imageId.getBoundingClientRect();
     var imageLeft = Math.floor(imageRect.left);
     var imageTop = Math.floor(imageRect.top);
+    var imageHeight = Math.floor(imageRect.height);
     var srcUrl = evt.target.src;
     var evX = evt.x;      // location of mouse on the web browser screen
     var evY = evt.y;
     var offLeft = Math.max(0, Math.floor(evt.x - tdLeft));
     var windowUp = false;     // see if window is supposed to become visible
     var foundIdx = -1;
     if (mouseOver.spans[trackName]) {
        foundIdx = mouseOver.findRange(offLeft, mouseOver.spans[trackName]);
     }
     // might want to indicate 'no data' when not found
     if (foundIdx > -1) {
       // value to display
       var mouseOverValue = "&nbsp;" + mouseOver.spans[trackName][foundIdx].v + "&nbsp;";
       $('#mouseOverText').html(mouseOverValue);
       var msgWidth = Math.ceil($('#mouseOverText').width());
       var msgHeight = Math.ceil($('#mouseOverText').height());
       var posLeft = evt.x - msgWidth + "px";
       var posTop = tdTop + "px";
       $('#mouseOverContainer').css('left',posLeft);
       $('#mouseOverContainer').css('top',posTop);
+      $('#mouseOverLine').css('left',evt.x + "px");
+      $('#mouseOverLine').css('top',posTop);
+      // Setting the height of this line to the full image height eliminates
+      //  the mouse event area
+//      $('#mouseOverLine').css('height',imageHeight + "px");
+//      $('#mouseOverLine').height(imageHeight + "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)
 
     // =======================================================================
     // receiveData() callback for successful JSON request, receives incoming
     //             JSON data and gets it into global variables for use here.
     //  The incoming 'arr' is a a set of objects where the object key name is
     //      the track name, used here as an array reference: arr[trackName]
     //        (currently only one object per json file, one file for each track,
@@ -4603,91 +4614,107 @@
     //  is an array of span definitions, where each element in the array is a
     //     mapBox definition object:
     //        {x1:n, x2:n, value:s}
     //        where n is an integer in the range: 0..width,
     //        and s is the value string to display
     //     Will need to get them sorted on x1 for efficient searching as
     //     they accumulate in the local data structure here.
     // =======================================================================
     receiveData: function (arr)
     {
       mouseOver.visible = false;
       for (var trackName in arr) {
       mouseOver.spans[trackName] = [];      // start array
       // add a 'mousemove' and 'mouseout' event listener to each track
       //     display object
-      var objectName = "td_data_" + trackName;
-      var objectId  = document.getElementById(objectName);
-      if (! objectId) { return; } // not sure why objects are not found
+      var tdData = "td_data_" + trackName;
+      var tdDataId  = document.getElementById(tdData);
+      if (! tdDataId) { return; } // not sure why objects are not always found
       // there should be a more simple jQuery function to bind these events
-      objectId.addEventListener('mousemove', mouseOver.mouseInTrackImage);
-      objectId.addEventListener('mouseout', mouseOver.popUpDisappear);
-      // would be nice to know when the window is scrolling in the browser so
-      // the text box could disappear.  These do not appear to work.
-      // Beware, onscroll event is continuous while scrolling.
-//    objectId.addEventListener('onscroll', popUpDisappear);
-//    window.addEventListener('onscroll', popUpDisappear);
+      tdDataId.addEventListener('mousemove', mouseOver.mouseInTrackImage);
+      tdDataId.addEventListener('mouseout', mouseOver.popUpDisappear);
       var itemCount = 0;	// just for monitoring purposes
       // save incoming x1,x2,v data into the mouseOver.spans[trackName][] array
       for (var span in arr[trackName]) {
         mouseOver.spans[trackName].push(arr[trackName][span]);
        ++itemCount;
       }
       mouseOver.tracks[trackName] = itemCount;	// merely for debugging watch
       }
     },  //      receiveData: function (arr)
 
+    failedRequest: function(trackName)
+    {
+      if (mouseOver.tracks[trackName]) {
+//      alert("failed request trackName: '"+ trackName + "'");
+        delete mouseOver.tracks[trackName];
+      }
+    },
+
     // =========================================================================
     // fetchMapData() sends JSON request, callback to receiveData() upon return
     // =========================================================================
-    fetchMapData: function (url)
+    fetchMapData: function (url, trackName)
     {
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.onreadystatechange = function() {
        if (4 === this.readyState && 200 === this.status) {
           var mapData = JSON.parse(this.responseText);
           mouseOver.receiveData(mapData);
+       } else {
+          if (4 === this.readyState && 404 === this.status) {
+             failedRequest(trackName);
+          }
        }
     };
     xmlhttp.open("GET", url, true);
     xmlhttp.send();  // sends request and exits this function
                      // the onreadystatechange callback above will trigger
                      // when the data has safely arrived
     },
 
-    getMouseOverData: function ()
+    getData: function ()
     {	// verify hgTracks and hgTracks.trackDb exist before running wild
       if (typeof(hgTracks) !== "undefined") {
         if (typeof (hgTracks.trackDb) !== "undefined") {
           for (var trackName in hgTracks.trackDb) {
-           var isWiggle = false;
            var rec = hgTracks.trackDb[trackName];
+           if (rec.visibility !== 2) { continue; }
+           var isWiggle = false;
            if (rec.type.includes("wig")) { isWiggle = true; }
            if (rec.type.includes("bigWig")) { isWiggle = true; }
            if (! isWiggle) { continue; }
-           var imgName = "img_data_" + trackName;
-           var imgElement  = document.getElementById(imgName);
+           var imgData = "img_data_" + trackName;
+           var imgElement  = document.getElementById(imgData);
            if (imgElement) {
-             mouseOver.fetchMapData(mouseOver.jsonFileName(imgElement, trackName));
+             mouseOver.fetchMapData(mouseOver.jsonFileName(imgElement, trackName), trackName);
            }
          }
        }
      }
     },
 
+    // any scrolling turns the popUp message off
+    scroll: function()
+    {
+    if (mouseOver.visible) { mouseOver.popUpDisappear(); }
+    },
+
     addListener: function () {
-        window.addEventListener('load', mouseOver.getMouseOverData, false);
+        mouseOver.visible = false;
+        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").
             // NOTE: must match TRACK_SEARCH_PAGER in hg/inc/searchTracks.h
             $("input[name=hgt_tsPage]").val(0);