MediaWiki:Gadget-TimelinePreview.js

From Angelina Jordan Wiki
Revision as of 18:00, 11 November 2025 by Most2dot0 (talk | contribs) (bugfix)

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// Gadget: Timeline hover previews (system popup integration)
// Requires: mediawiki.api, jquery, mediawiki.util

mw.loader.using(['mediawiki.api', 'jquery', 'mediawiki.util']).then(function () {
    var api = new mw.Api();
    var timelineCache = null;

    function capitalizeFirst(str) {
        return str.charAt(0).toUpperCase() + str.slice(1);
    }

    function loadTimeline() {
        if (timelineCache) return $.Deferred().resolve();
        return api.get({
            action: 'parse',
            page: 'Timeline',
            prop: 'text',
            formatversion: 2
        }).then(function (data) {
            var html = $('<div>').html(data.parse.text);
            timelineCache = {};
            html.find('dt').each(function () {
                var dt = $(this);
                var id = dt.find('span[id]').attr('id');
                if (!id) return;
                var ddList = [];
                var el = dt.next();
                while (el.length && el.prop('tagName') === 'DD') {
                    var clone = el.clone();
                    // Remove references
                    clone.find('sup.reference').remove();
                    // Strip links, keep text
                    clone.find('a').replaceWith(function () {
                        return $(this).text();
                    });
                    ddList.push(clone.html());
                    el = el.next();
                }
                if (ddList.length) {
                    timelineCache[id] = capitalizeFirst(ddList.join('<br>').trim());
                }
            });
        });
    }

    function showTimelineInSystemPopup($link, id) {
        if (!timelineCache || !timelineCache[id]) return;
        var attempts = 0;
        var maxAttempts = 20;
        var interval = 50; // ms

        var timer = setInterval(function () {
            attempts++;
            var popup = $link.data('mwePopups'); // previous method worked
            if (popup && popup.$content && popup.$content.length) {
                popup.$content.html(
                    $('<div>').css({ padding: '0.5em' }).html(timelineCache[id])
                );
                clearInterval(timer);
            } else if (attempts >= maxAttempts) {
                clearInterval(timer);
            }
        }, interval);
    }

    function onHover() {
        var $link = $(this);
        var href = $link.attr('href') || '';
        var match =
            href.match(/\/Timeline#(\d{4}-\d{2}-\d{2})$/) ||
            href.match(/^#(\d{4}-\d{2}-\d{2})$/);
        if (!match) return;
        var id = match[1];

        loadTimeline().then(function () {
            showTimelineInSystemPopup($link, id);
        });
    }

    function bindTimelineLinks(context) {
        $(context)
            .find('a[href*="Timeline#"], a[href^="#"]')
            .each(function () {
                var $link = $(this);
                var href = $link.attr('href') || '';
                if (
                    href.match(/Timeline#\d{4}-\d{2}-\d{2}$/) ||
                    href.match(/^#\d{4}-\d{2}-\d{2}$/)
                ) {
                    $link.off('.timelinePreview')
                        .on('mouseenter.timelinePreview', onHover);
                }
            });
    }

    // Initial binding
    mw.hook('wikipage.content').add(bindTimelineLinks);

    // Observe dynamically revealed content
    var observer = new MutationObserver(function (mutations) {
        mutations.forEach(function (m) {
            Array.from(m.addedNodes).forEach(function (node) {
                if (node.nodeType === 1) bindTimelineLinks(node);
            });
        });
    });
    observer.observe(document.body, { childList: true, subtree: true });
});