AQ3D Wiki Link Preview

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

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==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);
}