Jegged-Tooltips

Adds tooltips to Jegged game gudies.

// ==UserScript==
// @name        Jegged-Tooltips
// @description Adds tooltips to Jegged game gudies.
// @version 1.4
// @license GPL-3.0-or-later
// @include http://jegged.com/*
// @include https://jegged.com/*
// @run-at document-end
// @namespace https://gf.qytechs.cn/users/115055
// ==/UserScript==

let HOLD_TOOLTIP = false;
let CURRENT_LINK_TARGET = null;

function showCardTooltip(card, elem, x, y) {
    removeCardTooltip();
    
    let tooltip = document.createElement("div");
    const margin = 10;
    const maxWidth = 650;
    
    let left = x;
    let top = y + window.scrollY;
       
    tooltip.id = "JeggedTooltip";
    tooltip.style.position = "absolute";
    tooltip.style.zIndex = 500;

    tooltip.style.maxWidth = `${maxWidth}px`;
    tooltip.style.marginTop = `${margin}px`;
    tooltip.style.marginBottom = `${margin}px`;
    tooltip.style.marginRight = `${margin}px`;
    tooltip.style.marginLeft = `${margin}px`;
            
    card.style.marginTop = 0;
    card.style.marginBottom = 0;
       
    tooltip.append(card); 
    document.body.append(tooltip);
  
    const elemRect = elem.getBoundingClientRect();
    const tooltipRect = tooltip.getBoundingClientRect();
    let headerHeight;
    
    const headerElem = document.getElementById("header");
    if (headerElem !== null) {
        headerHeight = headerElem.getBoundingClientRect().height;
    } else {
        headerHeight = 0;
    }
    
    // if destination position goes beyond viewport width, clamp position
    // within viewport or at 0
    if (x + tooltipRect.width + (margin*2) > window.innerWidth) {
        left = Math.max(window.innerWidth - tooltipRect.width - (margin*2), 0)
    }

    // never cover target element with tooltip
    if (y < elemRect.top + elemRect.height + window.scrollY) {
        top = elemRect.top + elemRect.height + window.scrollY;
    }
    
    tooltip.style.left = `${left}px`;
    tooltip.style.top = `${top}px`;
    
    // if destination position goes beyond viewport height, 
    // and tooltip fits above elem, display above elem
    if (top + tooltipRect.height + (margin*2) > window.scrollY + window.innerHeight) {
        if (elemRect.top - tooltipRect.height - (margin*2) - headerHeight >= 0) {
            top = window.scrollY + elemRect.top - tooltipRect.height - (margin*2);
        }    
    }
    
    tooltip.style.left = `${left}px`;
    tooltip.style.top = `${top}px`;
}

function removeCardTooltip() {
    const tooltip = document.getElementById("JeggedTooltip");
    if (tooltip) {
        tooltip.remove();        
    }
}

async function onMouseEnterLink(event) {
    CURRENT_LINK_TARGET = event.target;
    const href = event.target.href;
    const url = URL.parse(href);
    
    if (url.origin !== window.location.origin) {
        // only follow same-origin links
        return;
    }
    
    if (url.hash === "") {
        // only follow anchor links 
        return;   
    }
    
    
    let x = event.x;
    let y = event.y;
    
    const onMouseMove = (e) => {
        x = e.x;
        y = e.y;
    }
    
    // keep latest cursor position for when tooltip content is ready
    document.addEventListener("mousemove", onMouseMove);
    
    try {
        const r = await fetch(url)
        if (!r.ok) {
          throw new Error(`Response status: ${r.status}`);
        }

        const text = await r.text();

        const parser = new DOMParser();
        const page = parser.parseFromString(text, "text/html");

        const destination = page.getElementById(url.hash.slice(1));
        if (destination === null) {
            throw new Error(`Destination anchor not found on linked page.`)
        }

        let card; 
        if (destination.closest(".alert") !== null) {
            // if destination belongs to a card-style alert, use this
            card = destination.closest(".alert");
        } else if (destination.closest("tr") !== null) {
            // if destination belongs to a table, use nearest row
            card = document.createElement("div");
            card.innerHTML = destination.closest("tr").innerHTML;
            card.classList = "alert alert-secondary";
        } else {
            // element does not have associated card, do not display tooltip
            return;
        };
        
        // double check target after fetch has returned
        if (event.target === CURRENT_LINK_TARGET) {
            showCardTooltip(card, event.target, x, y);            
        }
    } finally {
        document.removeEventListener("mousemove", onMouseMove);
    }
}

function onMouseLeaveLink() {
    if (HOLD_TOOLTIP === false) {
        CURRENT_LINK_TARGET = null;
        removeCardTooltip();        
    }
}

function onKeyDown(e) {
    if (e.key === "Shift") {
        HOLD_TOOLTIP = !HOLD_TOOLTIP;
        
        if (!HOLD_TOOLTIP) {
            removeCardTooltip();
        }
    } else if (e.key === "Escape") {
        removeCardTooltip();
    }
}

function onScroll(e) {
    if (!HOLD_TOOLTIP) {
        removeCardTooltip();
    }
}

function addEventListenersToLinks() {
    const links = document.getElementsByTagName("a");
    
    for (const l of links) {
        l.addEventListener("mouseenter", onMouseEnterLink);
        l.addEventListener("mouseleave", onMouseLeaveLink);
    }
    
    document.addEventListener("keydown", onKeyDown);
    document.addEventListener("scroll", onScroll);
}

(() => {
    addEventListenersToLinks();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址