Skip to content

Instantly share code, notes, and snippets.

@rf5860
Last active January 10, 2024 22:44
Show Gist options
  • Save rf5860/4da54b689e90f9d8c8c8d01e18fbd886 to your computer and use it in GitHub Desktop.
Save rf5860/4da54b689e90f9d8c8c8d01e18fbd886 to your computer and use it in GitHub Desktop.
FextraLife Tooltips - Adds on-hover tooltips to Fextra Wiki links
// ==UserScript==
// @name FextraLife Tooltips
// @namespace https://.wiki.fextralife.com/
// @version 0.5
// @description Adds on-hover tooltips to Fextra Wiki links
// @author rjf89
// @match *://*.wiki.fextralife.com/*
// @updateURL https://gist.github.com/rf5860/4da54b689e90f9d8c8c8d01e18fbd886/raw/FextraLifeTooltips.user.js
// @downloadURL https://gist.github.com/rf5860/4da54b689e90f9d8c8c8d01e18fbd886/raw/FextraLifeTooltips.user.js
// @grant GM_xmlhttpRequest
// ==/UserScript==
/**
* Function to load the article
* @param e The element that triggered the event
* @param link The link to the article
*/
const hoverFunc = async (e, link) => {
// If a tooltip is already showing, don't show another one
if (document.querySelector('.fl-tooltip')) return;
const content = await loadArticle(link.href);
if (content) showToolTip(e, content);
};
const DomParser = new DOMParser()
const isEmptyOrAnchor = (href) => href == "" || href[0] == "#";
const getArticleName = (href) => href.split("/").pop();
const parseDoc = (data) => DomParser.parseFromString(data, "text/html")
const extractInfobox = (data) => parseDoc(data).querySelector("#infobox")
const getBaseUrl = () => window.location.href.split("/").slice(0, 3).join("/");
/**
* Loads the article with the given URL.
*
* @param {string} articleName - The article URL.
* @returns {Object|null} - The loaded article object or null if the article is invalid.
*/
async function loadArticle(href) {
const articleName = getArticleName(href);
if (isEmptyOrAnchor(articleName)) return null;
try {
// Get the base URL of the current page we're on
const infobox = await ajaxFunc(`${getBaseUrl()}/${articleName}`);
return extractInfobox(infobox);
} catch (error) {
return null;
}
}
/**
* Makes an AJAX request to the specified URL and returns a promise that resolves with the response data.
* Usage example:
* ajaxFunc('https://example.com/data.json')
* .then(data => {
* // Use the data here
* console.log(data);
* })
* .catch(error => {
* // Handle any errors here
* console.error('An error occurred:', error);
* });
* @param {string} url - The URL to make the AJAX request to.
* @returns {Promise} A promise that resolves with the response data if the request is successful, or rejects with an error if the request fails.
*/
const ajaxFunc = url => new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: response => resolve(response.responseText),
onerror: error => reject(error)
});
});
/**
* Displays a tooltip with the given content at the specified event position.
*
* @param {Event} event - The event object that triggered the tooltip.
* @param {Object} content - The content to be displayed in the tooltip.
*/
const showToolTip = (event, content) => {
hoverOut()
const tooltip = document.createElement('div');
tooltip.className = 'fl-tooltip';
tooltip.style.position = 'absolute';
tooltip.style.zIndex = 9999;
tooltip.style.backgroundColor = document.body.style.backgroundColor;
tooltip.style.border = '1px solid black';
tooltip.style.overflow = 'auto';
tooltip.addEventListener('mouseout', hoverOut);
tooltip.appendChild(content);
content.style.position = 'unset';
document.body.appendChild(tooltip);
const tooltipRect = tooltip.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
// Check if the tooltip goes off the right of the viewport
if (event.pageX + tooltipRect.width + 10 > viewportWidth) {
tooltip.style.left = `${event.pageX - tooltipRect.width - 10}px`;
} else {
tooltip.style.left = `${event.pageX + 10}px`;
}
// Check if the tooltip goes off the bottom of the viewport
if (event.pageY + tooltipRect.height + 10 > viewportHeight) {
tooltip.style.top = `${event.pageY - tooltipRect.height - 10}px`;
} else {
tooltip.style.top = `${event.pageY + 10}px`;
}
}
const hoverOut = () => [...document.querySelectorAll('.fl-tooltip')].forEach(tooltip => tooltip.remove());
const hoverOver = (e, link) => {
if (document.querySelectorAll('.fl-tooltip').length != 0) return;
hoverFunc(e, link);
};
(function () {
let links = document.querySelectorAll('a.wiki_link');
links.forEach(function (link) {
link.addEventListener('mouseover', function (e) { hoverOver(e, this) });
link.addEventListener('mouseout', hoverOut);
});
console.info("Tooltips loaded");
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment