Mouseover image previews for links on the AQ-3D Wiki.
// ==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);
}