MediaWiki:Gadget-QuickDiff.js

From RuneRealm Wiki

This is an old revision of this page, as edited by Alex (talk | contribs) at 17:15, 17 October 2024. The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

Jump to navigation Jump to search

After saving, you may need to bypass your browser's cache to see the changes. For further information, see Wikipedia:Bypass your cache.

  • In most Windows and Linux browsers: Hold down Ctrl and press F5.
  • In Safari: Hold down ⇧ Shift and click the Reload button.
  • In Chrome and Firefox for Mac: Hold down both ⌘ Cmd+⇧ Shift and press R.
/* <nowiki>
    QuickDiff - quickly view any diff link
    Modified to remove Wikia-specific i18n code; relies on [[MediaWiki:Gadget-QDmodal.js]] and [[MediaWiki:Gadget-QDmodal.css]]

    @author OneTwoThreeFall
    @source <https://dev.fandom.com/wiki/QuickDiff>
    @source <https://dev.fandom.com/wiki/MediaWiki:QuickDiff/code.js>
    @source <https://dev.fandom.com/wiki/MediaWiki:Custom-QuickDiff/i18n.json>
*/

/*jslint browser, long */
/*global jQuery, mediaWiki, dev */

(function ($, mw) {
    "use strict";

    // double-run protection
    if (window.quickDiffLoaded) {
        return;
    }
    window.quickDiffLoaded = true;

    var modal;
    var special = {};

    function isElementOrChildFrontmost(element) {
        var pos = element.getBoundingClientRect();
        var frontmostElement = document.elementFromPoint(pos.left, pos.top);
        return element.contains(frontmostElement);
    }

    // "Special:Diff/12345" and "Special:ComparePages" link detection
    function initSpecialPageStrings() {
        special.diffDefault = mw.util.getUrl("Special:Diff/");
        special.compareDefault = mw.util.getUrl("Special:ComparePages");

        var wiki = mw.config.get("wgDBname");
        var storageKeyDiff = "QuickDiff-specialdiff_" + wiki;
        var storageKeyCompare = "QuickDiff-specialcompare_" + wiki;

        try {
            special.diff = localStorage.getItem(storageKeyDiff);
            special.compare = localStorage.getItem(storageKeyCompare);
        } catch (ignore) {}

        if (special.diff && special.compare) {
            // using stored values - no need for api request
            return;
        }

        $.getJSON(mw.util.wikiScript("api"), {
            action: "parse",
            contentmodel: "wikitext",
            format: "json",
            prop: "text",
            text: "<span class='diff'>[[Special:Diff/]]</span><span class='compare'>[[Special:ComparePages]]</span>",
            disablelimitreport: ""
        }).done(function (data) {
            var $parsed = $(data.parse.text["*"]);

            special.diff = $parsed.find(".diff > a").attr("href");
            special.compare = $parsed.find(".compare > a").attr("href");

            try {
                localStorage.setItem(storageKeyDiff, special.diff);
                localStorage.setItem(storageKeyCompare, special.compare);
            } catch (ignore) {}
        });
    }

    // support for patrolling edits directly from modal
    // ideally this wouldn't be needed and we'd rely on MediaWiki's own handler,
    // but that's run only on document ready and isn't easily reusable
    function initAjaxPatrolHandler() {
        var $spinner = mw.libs.QDmodal.getSpinner();

        $spinner.css({
            "--qdmodal-spinner-size": "2em",
            position: "relative",
            top: "-6px",
            verticalAlign: "top"
        });

        mw.hook("quickdiff.ready").add(function (modal) {
            var $patrolLinks = modal.$element.find(".patrollink[data-mw='interface'] > a");

            $patrolLinks.on("click", function (event) {
                event.preventDefault();

                if ($patrolLinks.is("[disabled]")) {
                    return;
                }

                $patrolLinks.find(".qdmodal-spinner-container").remove().end()
                    .attr("disabled", "").append(" ", $spinner.clone());

                var $spinners = $patrolLinks.find(".qdmodal-spinner-container");

                mw.loader.using("mediawiki.api").done(function () {
                    new mw.Api().postWithToken("patrol", {
                        action: "patrol",
                        rcid: mw.util.getParamValue("rcid", event.target.href)
                    }).done(function (data) {
                        $spinners.removeAttr("style").text("✅")
                            .parent().wrap("<s>");
                    }).fail(function (data) {
                        $spinners.removeAttr("style").text("❌")
                            .parent().removeAttr("disabled");
                    });
                });
            });
        });
    }

    function getDiffTitle($diff) {
        var prevTitle = $diff.find("#mw-diff-otitle1 a").attr("title");
        var currTitle = $diff.find("#mw-diff-ntitle1 a").attr("title");

        if (prevTitle && prevTitle !== currTitle) {
            return "Differences between “" + prevTitle + "” and “" + currTitle + "”";
        }

        return "Differences: " + currTitle;
    }

    function addDiffActions() {
        var prevTitle = modal.$content.find("#mw-diff-otitle1 a").attr("title");
        var currTitle = modal.$content.find("#mw-diff-ntitle1 a").attr("title");

        // collect action links (edit, undo, rollback, patrol) from the diff
        var $actions = modal.$content.find(".diff-ntitle").find(
            ".mw-diff-edit, .mw-diff-undo, .mw-rollback-link, .patrollink, .mw-diff-tool"
        ).clone();

        // remove text nodes (the brackets around each link)
        $actions.contents().filter(function (ignore, element) {
            return element.nodeType === 3;
        }).remove();

        $actions.find("a")
            .addClass("qdmodal-button")
            .attr("target", "_blank");

        // if diff is for one page, add a page history action
        if (prevTitle === currTitle) {
            $actions = $actions.add(
                $("<a>").attr({
                    "class": "qdmodal-button",
                    href: mw.util.getUrl(currTitle, {action: "history"}),
                    target: "_blank"
                }).text("history")
            );
        }

        modal.$footer.append($actions);
    }

    function loadDiff(url) {
        modal.show({
            loading: true,
            title: !modal.visible && "Loading…"
        });

        // add 'action=render' and 'diffonly' params to save some bytes on each request
        url.extend({
            action: "render",
            diffonly: "1"
        });

        // pass through 'bot' param for rollback links if it's in use on the current page
        if (mw.util.getParamValue("bot")) {
            url.extend({bot: "1"});
        }

        $.when(
            $.get(url.getRelativePath()),
            mw.loader.using(["mediawiki.diff.styles", "mediawiki.interface.helpers.styles"])
        ).always(function (response) {
            delete url.query.action;
            delete url.query.diffonly;
            delete url.query.bot;

            var data = {
                url: url,
                buttons: [{
                    text: "open link",
                    href: url.toString(),
                    attr: {"data-disable-quickdiff": ""}
                }],
                content: "Something went wrong while getting the page at “" + url.toString() + "”."
            };
            var $diff;

            if (typeof response[0] === "string") {
                var $content = $(response[0]);
                $diff = $content.filter("table.diff, #mw-rev-deleted-no-diff");

                if (!$diff.length) {
                    // $content is a complete page - see if a diff can be found
                    // needed for diffs from special pages as they ignore action=render URL parameter
                    $diff = $content.find("table.diff");
                }
            }

            if (!$diff || $diff.length === 0) {
                // default content is error msg
                return modal.show(data);
            }

            data.content = $diff;
            data.hook = "quickdiff.ready";
            data.onBeforeShow = addDiffActions;
            data.title = getDiffTitle($diff);

            // if a diff, fire the standard MW hook
            if ($diff.is("table.diff[data-mw='interface']")) {
                mw.hook("wikipage.diff").fire($diff);
            }

            modal.show(data);
        });
    }

    function keydownHandler(event) {
        // only handle key presses if QuickDiff is frontmost
        if (!isElementOrChildFrontmost(modal.$container[0])) {
            return;
        }

        if (event.key === "ArrowLeft") {
            modal.$content.find("#differences-prevlink").trigger("click");
        } else if (event.key === "ArrowRight") {
            modal.$content.find("#differences-nextlink").trigger("click");
        }
    }

    function linkClickHandler(event) {
        // ignore clicks with modifier keys to avoid overriding browser features
        if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
            return;
        }

        // ignore click if link has "data-disable-quickdiff" attribute set
        if (event.currentTarget.dataset.disableQuickdiff !== undefined) {
            return;
        }

        // stop triggering on links with empty href
        if (event.currentTarget.getAttribute('href') === "") {
            return;
        }

        // stop triggering on OOUI icons
        var targetClasses = event.currentTarget.classList;

        for (let targetClass of targetClasses) {
            if (targetClass.startsWith("oo-ui")) {
                return;
            }
        }

        var url = event.currentTarget.href;

        try {
            url = new mw.Uri(url);
        } catch (ignore) {
            // quit if url couldn't be parsed
            // it wouldn't be a link QuickDiff could handle anyway
            return;
        }

        // cross-domain requests not supported
        if (url.host !== location.hostname) {
            return;
        }

        // no fragment check is to ensure section links/collapsible trigger links on diff pages are ignored
        var hasDiffParam = url.query.diff !== undefined
                && url.fragment === undefined;
        var isSpecialDiffLink = url.path.indexOf(special.diff) === 0
                || url.path.indexOf(special.diffDefault) === 0;
        var isSpecialCompareLink = url.path.indexOf(special.compare) === 0
                || url.path.indexOf(special.compareDefault) === 0;

        if (hasDiffParam || isSpecialDiffLink || isSpecialCompareLink) {
            event.preventDefault();
            loadDiff(url);
        }
    }

    function init() {
        var $body = $(document.body);
        modal = new mw.libs.QDmodal("quickdiff-modal");

        // full screen modal
        var css = "#quickdiff-modal { height: 100%; width: 100% }";
        // always show modal footer for UI consistency
        css += "#quickdiff-modal > footer { display: flex }";
        mw.util.addCSS(css);

        // attach to body for compatibility with ajax-loaded content
        // also, one attached event handler is better than hundreds!
        $body.on("click.quickdiff", "a[href]", linkClickHandler);

        // listen for left/right arrow keys, to move between prev/next diff
        $body.on("keydown.quickdiff", keydownHandler);

        initSpecialPageStrings();
        initAjaxPatrolHandler();
    }

    init();
}(jQuery, mediaWiki));
// </nowiki>