Greasy Fork

Greasy Fork is available in English.

WikiView: AQW Link Preview

Adds image previews for links on the official AQW Wiki, AQW character pages, and AQW account management.

当前为 2025-06-23 提交的版本,查看 最新版本

// ==UserScript==
// @name            WikiView: AQW Link Preview
// @namespace       https://github.com/biglavis/
// @version         2.0.1
// @description     Adds image previews for links on the official AQW Wiki, AQW character pages, and AQW account management.
// @match           http://aqwwiki.wikidot.com/*
// @match           https://account.aq.com/CharPage?id=*
// @match           https://account.aq.com/AQW/Inventory
// @match           https://account.aq.com/AQW/BuyBack
// @match           https://account.aq.com/AQW/WheelProgress
// @match           https://account.aq.com/AQW/House
// @exclude         http://aqwwiki.wikidot.com/book-of-lore-badges
// @exclude         http://aqwwiki.wikidot.com/character-page-badges
// @require         https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js
// @icon            https://www.aq.com/favicon.ico
// @license         MIT
// ==/UserScript==

var mousePos = { x: -1, y: -1 };
$(document).mousemove(function(event) {
    mousePos.x = event.clientX;
    mousePos.y = event.clientY;
});

var mouseHover = false; // flag to prevent spam
var controller = null;
var timeout = null;

// wiki links; char page
$("#page-content a, .card.m-2.m-lg-3 a").on({
    mouseover: function() { hovered(this.href); },
    mouseout: function() { unhovered(); }
});

// acct mgmt inventory; wiki recent changes
$("#inventoryRendered, #site-changes-list").on("mouseover", function() {
    $(this).find("a").on({
        mouseover: function() { hovered(this.href); },
        mouseout: function() { unhovered(); }
    });
});

// acct mgmt inventory, wheel, house
$("#listinvFull, #wheel, table.table.table-sm.table-bordered").on("mouseover", function() {
    $(this).find("tbody td:first-child").on({
        mouseover: function() { hovered("http://aqwwiki.wikidot.com/" + this.textContent.split(/\sx\d+/)[0]); },
        mouseout: function() { unhovered(); }
    });
});

// acct mgmt buyback
$("#listinvBuyBk2").on("mouseover", function() {
    $(this).find("tbody td:nth-child(2)").on({
        mouseover: function() { hovered("http://aqwwiki.wikidot.com/" + this.textContent); },
        mouseout: function() { unhovered(); }
    });
});

function hovered(link) {
    if (!mouseHover) {
        mouseHover = true;
        controller = new AbortController();

        // show preview if hovered for 100ms
        timeout = setTimeout(function() {
            showPreview(link, controller.signal);
        }, 100);
    }
}

function unhovered() {
    clearTimeout(timeout);
    mouseHover = false;
    controller.abort();
    removePreview();
}

async function fetchParse(url, signal) {
    const response = await fetch(url, { signal });
    const html = await response.text();

    return new DOMParser().parseFromString(html, "text/html");
}

async function wikimg(url, signal) {
    // fetch and parse
    if (!url.startsWith("http://aqwwiki.wikidot.com/")) return;

    if (window.location.hostname !== "aqwwiki.wikidot.com") {
        url = "https://api.codetabs.com/v1/proxy?quest=" + url; // use proxy to bypass CORS
    }

    try {
        var doc = await fetchParse(url, signal);
    } catch {
        return;
    }

    // get page type
    const type = $(doc).find("#breadcrumbs > a:last").text();
    
    // get images
    switch (type) {
        case "AQWorlds Wiki":
            if ($(doc).find("#page-content span:first").text().includes("usually refers to")) {
                for (let link of $(doc).find("#page-content a")) {
                    var images = await wikimg("http://aqwwiki.wikidot.com" + $(link).attr("href"));
                    if (images) {
                        return images;
                    }
                }
            }
            return;

        case "Events":
        case "Factions":
        case "Game Menu":
        case "Quests":
        case "Shops":
        case "Hair Shops":
        case "Merge Shops":
        case "Enhancements":
            return;

        case "Book of Lore Badges":
        case "Character Page Badges":
            var image = $(doc).find("#page-content img:last");
            if (image.length) {
                return [image.prop("src")];
            }
            return;

        case "Classes":
        case "Armors":
            var image0 = $(doc).find("#wiki-tab-0-0:last img:first");
            var image1 = $(doc).find("#wiki-tab-0-1:last img:first");

            if (image0.length && image1.length) {
                return [image0.prop("src"), image1.prop("src")]
            }

        default:
            var image = $(doc).find("#page-content > img:last");
            if (image.length && !image.prop("src").includes("image-tags")) {
                return [image.prop("src")];
            }

            for (image of $(doc).find("#wiki-tab-0-0 img").toArray()) {
                if (!$(image).prop("src").includes("image-tags")) {
                    return [$(image).prop("src")];
                }
            }

            for (image of $(doc).find("#page-content > .collapsible-block img").toArray()) {
                if (!$(image).prop("src").includes("image-tags")) {
                    return [$(image).prop("src")];
                }
            }
    }
}

async function showPreview(url, signal) {
    $("body").append('<div id="preview" style="position:fixed; display:flex"></div>');

    const maxwidth = $(window).width() * 0.45;
    const maxheight = $(window).height() * 0.65;

    // get images
    const images = await wikimg(url, signal)

    // return if no images found
    if (!images) return;

    // add images to new div
    images.forEach(src => {
        if (images.length === 1) {
            $("#preview").append(`<img style="max-width:${maxwidth}px; max-height:${maxheight}px; height:auto; width:auto;" src="${src}">`);
        } else {
            $("#preview").append(`<img style="height:${maxheight}px;" src="${src}">`);
        }
    });

    // wait for images to load
    waitForImg("#preview img", function() {

        // scale images down if div width > max width
        $("#preview img").each(function() {
            this.style.height = this.offsetHeight * Math.min(1, maxwidth / $("#preview").width()) + "px";
        });

        // position div
        $("#preview").css("top", mousePos.y - (mousePos.y / $(window).height()) * $("#preview").height() + "px");
        if (mousePos.x < $(window).width() / 2) {
            $("#preview").css("left", mousePos.x + Math.min(100, $(window).width() - $("#preview").width() - mousePos.x) + "px");
        } else {
            $("#preview").css("right", $(window).width() - mousePos.x + Math.min(100, mousePos.x - $("#preview").width()) + "px");
        }
    });
}

function removePreview() {
    $("#preview").remove();
}

function waitForImg(selector, callback) {
    const wait = setInterval(function(){
        try {
            if( $(selector).toArray().every(e => $(e)[0].complete) ) {
                callback();
                clearInterval(wait);
            }
        } catch {
            clearInterval(wait);
        }
    }, 25);
}