Fantastic Frontier Roblox Wiki
m (Fix domain change.)
m (Attempt at allowing nested tabbers for item tooltips.)
Line 54: Line 54:
 
});
 
});
 
updatePosition();
 
updatePosition();
  +
}
  +
  +
// Measurement to avoid mess-up for our jQuery selector statement, as it's influenced by user input.
  +
function escapeHtml(unsafe) {
  +
return unsafe
  +
.replace(/&/g, "&")
  +
.replace(/</g, "&lt;")
  +
.replace(/>/g, "&gt;")
  +
.replace(/"/g, "&quot;")
  +
.replace(/\\/g, "")
  +
.replace(/'/g, "&#039;");
 
}
 
}
 
 
Line 67: Line 78:
 
$.get("https://fantastic-frontier-roblox.fandom.com/wiki/" + article, function(domString) {
 
$.get("https://fantastic-frontier-roblox.fandom.com/wiki/" + article, function(domString) {
 
var dom = $(domString);
 
var dom = $(domString);
// We're showing information from infoboxes in the tooltip.
+
var queryParts = article.split("#");
var infobox = dom.find(".portable-infobox");
+
  +
// We're showing information from the first infobox in the tooltip.
  +
var infobox = $(dom.find(".portable-infobox"));
  +
  +
if (queryParts.length > 1) { // URL has anchor, look for the right infobox.
  +
var currentTabber = $("#WikiaArticle>div");
  +
var anchorParts = queryParts[1].split("-");
  +
for (var i = 0; i < anchorParts.length; i++) {
  +
var newTabber = $(currentTabber.find(">.tabber>.tabbertab[title='" + escapeHtml(anchorParts[i]) + "']"));
  +
if (newTabber.length > 0) {
  +
currentTabber = newTabber;
  +
}
  +
}
  +
// If our nested tabbers contains any infobox, we'll use that over the first one one the page.
  +
if ($(currentTabber.find(".portable-infobox")).length > 0) {
  +
infobox = $(currentTabber.find(".portable-infobox"));
  +
}
  +
}
  +
 
if (infobox.length > 0) {
 
if (infobox.length > 0) {
  +
infobox = $(infobox[0]);
 
cache[article] = infobox;
 
cache[article] = infobox;
 
if (hovered_article == article)
 
if (hovered_article == article)

Revision as of 02:42, 25 January 2019

$(function() {
    // This element is placed at the bottom of the page. It serves as a tooltip for showing article info on hover.
    var tooltip = $("<div id=\"item-tooltip\"></div>");
    
    // Cache for already-checked items, to save resources.
    var cache = {};
    // To properly load the tooltip when the user changes focus quickly.
    var hovered_article = null;
    // For positioning the tooltip.
    var lastX = 0, lastY = 0;
    
    function updatePosition() {
        tooltip.css({
           "left": Math.min(wind.width() - tooltip.width() - 35, lastX + 10),
           "top":  Math.min(wind.height() - tooltip.height() - 35, lastY + 50)
        });
    }
    
    // Generates a section of key-value pairs to the tooltip.
    function generateSection(sec, items) {
        for (var i = 0; i < items.length; i++) {
            var item = $("<div class=\"it-infoitem\"><span class=\"it-infoitem-key\"></span><span class=\"it-infoitem-val\"></span></div>");
            var current = $(items[i]);
            item.find(".it-infoitem-key").text(current.find(".pi-data-label").text());
            item.find(".it-infoitem-val").html(current.find(".pi-data-value").html()); // To allow for listing.
            sec.append(item);
        }
    }
    
    // Updates the DOM for our tooltip element using the found data.
    function setTooltip(data) {
        tooltip.html("<div class=\"it-header\"><img/><div class=\"it-title\">-</div></div>");
        tooltip.find(".it-title").text(data.find(".pi-title").text());
        tooltip.find(".it-header img").attr("src", data.find(".pi-image img").attr("src"));
        if (data.find(".pi-image .pi-caption").length > 0)
            tooltip.append($("<div class=\"it-infobox it-desc\"></div>").text(data.find(".pi-image .pi-caption").text()));
        var dsec = data.find("section, >.pi-data");
        for (var i = 0; i < dsec.length; i++) {
            var csec = $(dsec[i]);
            var sec = $("<div class=\"it-infobox\"></div>");
            if (csec.is("section")) {
                var ibTitle = csec.find(".pi-header"); // Collapsible sections can have titles.
                if (ibTitle.length > 0)
                    tooltip.append($("<div class=\"it-infobox-title\"></div>").text(ibTitle.text()));
                generateSection(sec, csec.find(".pi-data"));
            } else
                generateSection(sec, csec);
            tooltip.append(sec);
        }
        tooltip.show();
        tooltip.find(".it-infobox").each(function() {
            if (this.offsetHeight < this.scrollHeight)
                sec.addClass("overflow"); // Applies some overflow-look through pseudoclasses.
        });
        updatePosition();
    }
    
    // Measurement to avoid mess-up for our jQuery selector statement, as it's influenced by user input.
    function escapeHtml(unsafe) {
        return unsafe
            .replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;")
            .replace(/"/g, "&quot;")
            .replace(/\\/g, "")
            .replace(/'/g, "&#039;");
    }
    
    // Checks if we have cached data on the article, and loads/sets accordingly.
    function updateTooltip(article) {
        tooltip.hide();
        hovered_article = article;
        if (article in cache) {
            if (cache[article] !== false) {
                setTooltip(cache[article]); // The article data was cached, load it.
            }
        } else { // The article wasn't found in cache, pull it.
            $.get("https://fantastic-frontier-roblox.fandom.com/wiki/" + article, function(domString) {
                var dom = $(domString);
                var queryParts = article.split("#");
                
                // We're showing information from the first infobox in the tooltip.
                var infobox = $(dom.find(".portable-infobox"));
                
                if (queryParts.length > 1) { // URL has anchor, look for the right infobox.
                    var currentTabber = $("#WikiaArticle>div");
                    var anchorParts = queryParts[1].split("-");
                    for (var i = 0; i < anchorParts.length; i++) {
                        var newTabber = $(currentTabber.find(">.tabber>.tabbertab[title='" + escapeHtml(anchorParts[i]) + "']"));
                        if (newTabber.length > 0) {
                            currentTabber = newTabber;
                        }
                    }
                    // If our nested tabbers contains any infobox, we'll use that over the first one one the page.
                    if ($(currentTabber.find(".portable-infobox")).length > 0) {
                        infobox = $(currentTabber.find(".portable-infobox"));
                    }
                }
                
                if (infobox.length > 0) {
                    infobox = $(infobox[0]);
                    cache[article] = infobox;
                    if (hovered_article == article)
                        setTooltip(infobox);
                } else {
                    // No infobox, so we'll mark this article as not having a tooltip.
                    cache[article] = false;
                }
            });
        }
    }
    
    // Assigns hover-logic for an element to display tooltips.
    function setupElement(elem) {
        var article = elem.is("div") ? elem.attr("data-article") : elem.attr("href").substr(6); // Trim away /wiki/.
        elem.hover(function() {
            updateTooltip(article);
        }, function() {
            tooltip.hide();
            hovered_article = null;
        });
    }
    
    // Hook up tooltips to wiki links.
    $("#WikiaArticle a[href^='/wiki/'], #WikiaArticle .tooltip-linker").each(function() {
        setupElement($(this));
    });
    
    // Initialize.
    tooltip.hide();
    $(document.body).append(tooltip);
    var wind = $(window);
    
    // Follow the cursor.
    $(document).on('mousemove', function(e){
        lastX = e.clientX;
        lastY = e.clientY;
        updatePosition();
    });
});