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 data"; if (foundIdx > -1) { // value to display - if (mouseOver.spans[trackName][foundIdx].m) { + if (mouseOver.spans[trackName][foundIdx].c > 1) { mouseOverValue = " mean: " + mouseOver.spans[trackName][foundIdx].v + " "; } else { mouseOverValue = " " + mouseOver.spans[trackName][foundIdx].v + " "; } } $('#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 = " mean: " + longestNumber + " "; + } else { + mouseOverValue = " " + longestNumber + " "; + } + $('#mouseOverText').html(mouseOverValue); // see how big as rendered + var maximumWidth = Math.ceil($('#mouseOverText').width()); + $('#mouseOverText').html("no 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").