AQ3D Wiki Link Preview

Mouseover image previews for links on the AQ-3D Wiki.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name            AQ3D Wiki Link Preview
// @namespace       https://github.com/ExploitAQ3D
// @version         1.0.0
// @description     Mouseover image previews for links on the AQ-3D Wiki.
// @match           http://aq-3d.wikidot.com/*
// @require         https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js
// @icon            https://i.imgur.com/fZ2e8Mx.png
// @license         MIT
// Original Author Credit (https://github.com/biglavis/)
// Original script by biglavis (https://greasyfork.org/en/scripts/488835-wikiview-aqw-link-preview)
// Modified for AQ3D by ExploitAQ3D
// ==/UserScript==

// prevent jQuery conflicts between script and site
var $ = window.jQuery.noConflict(true);

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

var mouseHover = false;
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://aq-3d.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://aq-3d.wikidot.com/" + this.textContent); },
        mouseout: function() { unhovered(); }
    });
});

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

        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();

    const template = document.createElement('template');
    template.innerHTML = html;

    return template.content;
}

async function wikimg(url, signal) {
    if (!url.startsWith("http://aq-3d.wikidot.com/")) return;

    if (window.location.hostname !== "aq-3d.wikidot.com") {
        url = "https://api.codetabs.com/v1/proxy?quest=" + url;
    }

    const doc = await fetchParse(url, signal).catch(() => undefined);
    if (!doc) {
        return;
    }

    const type = $(doc).find("#breadcrumbs > a:last").text();

    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://aq-3d.wikidot.com" + $(link).attr("href"));
                    if (images) {
                        return images;
                    }
                }
            }
            return;

        case "World":
        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")];
                }
            }
    }
}

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;

    wikimg(url, signal).then((images) => {

        if (!images) return;

        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}">`);
            }
        });

        waitForImagesToLoad("#preview img").then(() => {

            const scale = Math.min(1, maxwidth / $("#preview").width());

            $("#preview img").each(function() {
                this.style.height = this.offsetHeight * scale + "px";
            });

            $("#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 waitForImagesToLoad(selector) {
    const imagePromises = $(selector).toArray().map(img => {

        if (img.complete) {
            return Promise.resolve();
        }

        return new Promise((resolve) => {
            img.addEventListener('load', resolve, { once: true });
            img.addEventListener('error', resolve, { once: true });
        });
    });

    return Promise.all(imagePromises);
}