5ef3bb90e839f6d218c6ce5988adb111d600d3df chmalee Mon Apr 22 16:34:02 2024 -0700 Make some special tooltips have a longer delay than normal item tooltips. This includes the track title tooltips. diff --git src/hg/js/utils.js src/hg/js/utils.js index 20152b3..9c7e6f4 100644 --- src/hg/js/utils.js +++ src/hg/js/utils.js @@ -4016,87 +4016,104 @@ /* Called after 500ms of the mouse being stationary, show a new tooltip * if we are over a new mouseover-able element */ e = triggeringMouseMoveEv; if (mousedNewItem && !(mouseIsOverPopup(e, currTooltip, 0))) { mousemoveController.abort(); hideMouseoverText(currTooltip); showMouseoverText(triggeringMouseMoveEv); } } function mousemoveHelper(e) { /* Helper function for deciding whether to keep a tooltip visible upon a mousemove event */ if (mousemoveTimer) { clearTimeout(mousemoveTimer); } + let isDelayedTooltip = lastMouseoverEle.getAttribute("tooltipDelay"); + if (isDelayedTooltip !== null && isDelayedTooltip === "delayed") { + mousemoveTimer = setTimeout(mousemoveTimerHelper, 3000, e, this); + mousedNewItem = true; + mousemoveController.abort(); + hideMouseoverText(this); + } else { mousemoveTimer = setTimeout(mousemoveTimerHelper, 500, e, this); // we are moving the mouse away, hide the tooltip regardless how much time has passed if (!(mouseIsOverPopup(e, this) || mouseIsOverItem(e, this))) { mousemoveController.abort(); hideMouseoverText(this); return; } } +} function showMouseoverText(ev) { /* If a tooltip is not visible, show the tooltip text right away. If a tooltip * is visble, do nothing as the mousemove event helper will re-call us * after hiding the tooltip that is shown */ ev.preventDefault(); let referenceElement = lastMouseoverEle; if (!tooltipIsVisible()) { let tooltipDivId = "#" + referenceElement.getAttribute("mouseoverid"); let tooltipDiv = $(tooltipDivId)[0]; if (!tooltipDiv) { return; } mouseoverContainer.replaceChildren(); let divCpy = tooltipDiv.cloneNode(true); divCpy.childNodes.forEach(function(n) { mouseoverContainer.appendChild(n); }); positionMouseover(ev, referenceElement, mouseoverContainer, ev.pageX, ev.pageY); mouseoverContainer.classList.add("isShown"); mouseoverContainer.style.opacity = "1"; mouseoverContainer.style.visibility = "visible"; mouseoverContainer.setAttribute("origItemMouseoverId", referenceElement.getAttribute("mouseoverid")); + + // some tooltips are special and have a long delay, make sure the container knows + // that too + let isDelayed = referenceElement.getAttribute("tooltipDelay"); + if (isDelayed !== null && isDelayed === "delayed") { + mouseoverContainer.setAttribute("isDelayedTooltip", "delayed"); + } else { + mouseoverContainer.setAttribute("isDelayedTooltip", "normal"); + } // Events all get their own unique id but they are tough to keep track of if we // want to remove one. We can use the AbortController interface to let the // web browser automatically raise a signal when the event is fired and remove // appropriate event mousemoveController = new AbortController(); let callback = mousemoveHelper.bind(mouseoverContainer); mousedNewItem = false; clearTimeout(mouseoverTimer); mouseoverTimer = undefined; // allow the user to mouse over the mouse over, (eg. clicking a link or selecting text) document.addEventListener("mousemove", callback, {signal: mousemoveController.signal}); document.addEventListener("scroll", callback, {signal: mousemoveController.signal}); } } function showMouseover(e) { /* Helper function for showing a mouseover. Uses a timeout function to allow * user to not immediately see all available tooltips. */ e.preventDefault(); // make the mouseover div: let ele1 = e.currentTarget; let text = ele1.getAttribute("mouseoverText"); - if (ele1.getAttribute("mouseoverId") === null) { + if (ele1.getAttribute("mouseoverid") === null) { if (text.length > 0) { let newEl = document.createElement("span"); newEl.style = "max-width: 400px"; // max width of the mouseover text newEl.innerHTML = text; let newDiv = document.createElement("div"); newDiv.className = "tooltip"; newDiv.style.position = "fixed"; newDiv.style.display = "inline-block"; if (ele1.title) { newDiv.id = replaceReserved(ele1.title); ele1.setAttribute("originalTitle", ele1.title); ele1.title = ""; } else { newDiv.id = replaceReserved(text); @@ -4120,33 +4137,39 @@ // this element on their way to mousing over the shown mouseover mousedNewItem = true; lastMouseoverEle = ele1; if (mouseoverTimer) { // user is moving their mouse around, make sure where they stop is what we show clearTimeout(mouseoverTimer); } if (mousemoveTimer) { // user is moving their mouse around and has triggered a potentially triggered // a new pop up, clear the move timeout clearTimeout(mousemoveTimer); } // If there is no tooltip present, we want a small but noticeable delay // before showing a tooltip if (canShowNewMouseover) { + // some tooltips are special and have a longer delay + let isDelayedTooltip = ele1.getAttribute("tooltipDelay"); + if (isDelayedTooltip !== null && isDelayedTooltip === "delayed") { + mouseoverTimer = setTimeout(showMouseoverText, 3000, e); + } else { mouseoverTimer = setTimeout(showMouseoverText, 500, e); } } +} function addMouseover(ele1, text = null, ele2 = null) { /* Adds wrapper elements to control various mouseover events */ if (!mouseoverContainer) { mouseoverContainer = document.createElement("div"); mouseoverContainer.className = "tooltip"; mouseoverContainer.style.position = "fixed"; mouseoverContainer.style.display = "inline-block"; mouseoverContainer.style.visibility = "hidden"; mouseoverContainer.style.opacity = "0"; mouseoverContainer.id = "mouseoverContainer"; let tooltipTextSize = localStorage.getItem("tooltipTextSize"); if (tooltipTextSize === null) {tooltipTextSize = window.browserTextSize;} mouseoverContainer.style.fontSize = tooltipTextSize + "px"; document.body.append(mouseoverContainer); @@ -4155,33 +4178,38 @@ if (ele1) { ele1.setAttribute("mouseoverText", text); ele1.addEventListener("mouseover", showMouseover, {capture: true}); } } function titleTagToMouseover(mapEl) { /* for a given area tag, extract the title text into a div that can be positioned * like a standard tooltip mouseover next to the item */ addMouseover(mapEl, mapEl.title); } function convertTitleTagsToMouseovers() { /* make all the title tags in the document have mouseovers */ $("[title]").each(function(i, a) { - if (a.title.startsWith("click & drag to scroll")) + if (a.title !== undefined && a.title.startsWith("click & drag to scroll")) a.title = ""; else if (a.title !== undefined && a.title.length > 0) { + if (a.title.startsWith("Click to alter the display density") || + a.title.startsWith("drag select or click to zoom")) { + // these tooltips have a longer delay: + a.setAttribute("tooltipDelay", "delayed"); + } titleTagToMouseover(a); } }); /* Mouseover should clear if you leave the document window altogether */ document.body.addEventListener("mouseleave", (ev) => { clearTimeout(mouseoverTimer); if (mousemoveController) { mousemoveController.abort(); } hideMouseoverText(mouseoverContainer); canShowNewMouseover = false; // let mouseovers show up again upon moving back in to the window // but only need the event once // use capture: true to force this event to happen // before the regular mouseover event document.body.addEventListener("mouseover", (evt) => {