4390310b0fa270367bc7b18e5728bfb1a2a9670d hiram Mon Nov 2 16:05:32 2020 -0800 mouse over popup now has a one second delay before appearing refs #21980 diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js index 92e8630..b552cf4 100644 --- src/hg/js/hgTracks.js +++ src/hg/js/hgTracks.js @@ -4455,58 +4455,62 @@ 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: {}, -// popUpDelay: 100000, // can not get this to work ? + 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, // spans{} - key name is track name, value is an array of // objects: {x1, x2, value} // 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) // 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 tdData = "td_data_" + trackName; var tdElement = document.getElementById(tdData); var id = tdElement.id; - tdElement.addEventListener('mousemove', mouseOver.mouseInTrackImage); + tdElement.addEventListener('mousemove', mouseOver.mouseMoveDelay); tdElement.addEventListener('mouseout', mouseOver.popUpDisappear); 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 @@ -4516,43 +4520,47 @@ 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'); } -// mouseOver.popUpDelay = 100000; + if (mouseOver.popUpTimer) { + clearTimeout(mouseOver.popUpTimer); + mouseOver.popUpTimer = null; + } + mouseOver.delayDone = true; + mouseOver.delayInProgress = false; }, 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'); } -// mouseOver.popUpDelay = 10; }, //the evt.target.id is the img_data_ 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 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 @@ -4595,72 +4603,94 @@ $('#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',tdHeight + "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) -/* this doesn't work, claims there is an error in security policy + // timeout calls here upon completion + delayCompleted: function() + { + mouseOver.delayDone = true; + // mouse could just be sitting there with no events, if there + // have been events during the timer, the evt has been recorded + // so the popUp appears where the mouse is while it moved during the + // time delay since mostRecentMouseEvt is up to date to now + // If mouse has moved out of element during timeout, the + // delayInProgress will be false and nothing happens. + if (mouseOver.delayInProgress) { + mouseOver.mouseInTrackImage(mouseOver.mostRecentMouseEvt); + } + }, + + // all mouse move events come here even during timeout mouseMoveDelay: function (evt) { - if (mouseOver.popUpDelay == 100000) { // first time here - mouseOver.popUpDelay -= 1; // no longer first time - setTimeout(mouseOver.mouseInTrackImage(evt), mouseOver.popUpDelay); - } else if (mouseOver.popUpDelay > 10) { - return; // wait for first one to complete before issuing more + mouseOver.mostRecentMouseEvt = evt; // record evt for delayCompleted + if (mouseOver.delayInProgress) { + if (mouseOver.delayDone) { + mouseOver.mouseInTrackImage(evt); // OK to trigger event now + return; } else { - mouseOver.mouseInTrackImage(evt); // after first one is done, pass them along + return; // wait for delay to be done + } } + mouseOver.delayDone = false; + mouseOver.delayInProgress = true; + if (mouseOver.popUpTimer) { + clearTimeout(mouseOver.popUpTimer); + mouseOver.popUpTimer = null; + } + mouseOver.popUpTimer = setTimeout(mouseOver.delayCompleted, mouseOver.popUpDelay); }, -*/ // ======================================================================= // 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, // this may change to have multiple tracks in one json file.) // The value associated with each track name // 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 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 - tdDataId.addEventListener('mousemove', mouseOver.mouseInTrackImage); + tdDataId.addEventListener('mousemove', mouseOver.mouseMoveDelay); 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 + "'");