MediaWiki:Gadget-readableRC-core.js

From [N8]
Revision as of 18:46, 9 August 2021 by Banri (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

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.
// <nowiki>
// Formats the rows on Special:RecentChanges where all the information runs together
// into three columns (page, diff/byte change, and user links) to make it more readable
//
// @author Iiii_I_I_I
//
// @TODO: - how to make it run automatically instead of on button click?
//        - column-ize and format nested rows too?

(function($, mw) {
    function runReadableRC($content) {
        if (!$content.hasClass('mw-changeslist')) {
            return;
        }

        $content.addClass('gadget-rc-enabled');

        var rows = document.querySelectorAll('.mw-changeslist-line');

        for (var i = 0, total = rows.length; i < total; i++) {
            var row = rows[i];

            // nested rows
            if (row.classList.contains('mw-rcfilters-ui-changesListWrapperWidget-enhanced-nested')) {
                row.setAttribute('colspan', '3');
                row.classList.add('gadget-rc-nested');

                cleanUserLinks(row);
                cleanSummary(row);
            }
            // top-level rows
            else if (row.classList.contains('mw-changeslist-log') && row.classList.contains('mw-collapsible')) {
                // multiple logs, eg.:
                // 09:55 (Upload log) . . [Ohana; Jezwin ANO (2×); Mustawa (5×); Gaga Lady (8×)]
                cleanMultipleLogs(row);
            }
            else if (row.className.includes('mw-changeslist-log')) {
                // single log, eg.:
                // 19:01 (User creation log) . . User account Kyooraku (talk | contribs | block) was created
                cleanSingleLog(row);
            }
            else if (row.classList.contains('mw-collapsible')) {
                // multiple edits, eg.:
                // 07:40 Defender of Varrock (3 changes | history) . . (+100) . . [Alchez; 173.216.229.37 (2×)]
                cleanMultipleEdits(row);
            }
            else {
                // single edit, eg.:
                // 08:23 Huge XP lamp (Thieving) (diff | hist) . . (+36) . . Thingummywut (talk | contribs | block) (Noting its single use.) [rollback 1 edit]
                cleanSingleEdits(row);
            }
        }
    }

    function insert(row, newNode, referenceNode) {
        return row.querySelector(referenceNode).parentNode.insertBefore(row.querySelector(newNode), row.querySelector(referenceNode));
    }

    function wrap(referenceNode, wrapperEl, wrapperClass) {
        var wrapper = document.createElement(wrapperEl);

        referenceNode.parentNode.insertBefore(wrapper, referenceNode);
        wrapper.appendChild(referenceNode);
        wrapper.classList.add(wrapperClass);
    }

    function cleanMultipleLogs(row) {
        // log name
        wrap(row.querySelector('.mw-rc-unwatched'), 'td', 'gadget-rc-logname');

        // remove square brackets from grouped usernames
        var users = row.querySelector('.changedby').childNodes;

        users[0].textContent = users[0].textContent.slice(1); // [
        users[users.length - 1].textContent = users[users.length - 1].textContent.slice(0, -1); // ]

        // placeholder column with separator dots
        wrap(row.querySelector('.mw-changeslist-separator'), 'td', 'gadget-rc-logdots');

        // rename <td>
        row.querySelector('.mw-changeslist-line-inner').className = 'gadget-rc-logentry';

        // rearrange newly created <td>s
        insert(row, '.gadget-rc-logname', '.gadget-rc-logentry');
        insert(row, '.gadget-rc-logdots', '.gadget-rc-logentry');

        // stupid empty node
        row.querySelector('.gadget-rc-logentry').removeChild(row.querySelector('.gadget-rc-logentry').childNodes[1]);
    }

    function cleanSingleLog(row) {
        // log name
        wrap(row.querySelector('.mw-changeslist-line-inner').childNodes[1], 'td', 'gadget-rc-logname');

        // add parentheses back around log name
        row.querySelector('.gadget-rc-logname').innerHTML = '(' + row.querySelector('.gadget-rc-logname').innerHTML + ')';

        // remove leftover parentheses from original location
        var parentheses = row.querySelector('.mw-changeslist-line-inner').childNodes;

        row.querySelector('.mw-changeslist-line-inner').removeChild(parentheses[0]); // (
        row.querySelector('.mw-changeslist-line-inner').removeChild(parentheses[1]); // )

        // placeholder column with separator dots
        wrap(row.querySelector('.mw-changeslist-separator'), 'td', 'gadget-rc-logdots');

        cleanUserLinks(row);
        cleanSummary(row);

        // rename <td>
        row.querySelector('.mw-changeslist-line-inner').className = 'gadget-rc-logentry';

        // rearrange newly created <td>s
        insert(row, '.gadget-rc-logname', '.gadget-rc-logentry');
        insert(row, '.gadget-rc-logdots', '.gadget-rc-logentry');
    }

    function cleanMultipleEdits(row) {
        // page name
        wrap(row.querySelector('.mw-title'), 'td', 'gadget-rc-pagename');

        // "x changes" -> "x diffs"
        if (row.querySelector('.mw-changeslist-groupdiff')) {
            var newDiff = row.querySelector('.mw-changeslist-groupdiff').childNodes[0].nodeValue.replace('changes', 'diffs');

            row.querySelector('.mw-changeslist-groupdiff').childNodes[0].nodeValue = newDiff;
        }
        // new pages have a text node instead of a link
        else {
            var newDiff = row.querySelector('.mw-changeslist-line-inner').childNodes[2].nodeValue.replace('changes', 'diffs');

            row.querySelector('.mw-changeslist-line-inner').childNodes[2].nodeValue = newDiff;
        }

        // "history" -> "hist"
        if (row.querySelector('.mw-changeslist-history')) {
            row.querySelector('.mw-changeslist-history').childNodes[0].nodeValue = 'hist';
        }
        // nonexistent pages (redirect-suppressed move or deleted) have a text node instead of a link
        else {
            var newHist = row.querySelector('.mw-changeslist-line-inner').childNodes[4].nodeValue.replace('history', 'hist');

            row.querySelector('.mw-changeslist-line-inner').childNodes[4].nodeValue = newHist;
        }

        // list of user(s)
        wrap(row.querySelector('.changedby'), 'td', 'gadget-rc-userlinks');

        // remove square brackets from grouped usernames
        var users = row.querySelector('.changedby').childNodes;

        users[0].textContent = users[0].textContent.slice(1); // [
        users[users.length - 1].textContent = users[users.length - 1].textContent.slice(0, -1); // ]

        // rename <td>
        row.querySelector('.mw-changeslist-line-inner').className = 'gadget-rc-diff';

        // rearrange newly created <td>s
        insert(row, '.gadget-rc-userlinks', '.gadget-rc-diff');
        insert(row, '.gadget-rc-pagename', '.gadget-rc-userlinks');
        insert(row, '.gadget-rc-diff', '.gadget-rc-userlinks');
    }

    function cleanSingleEdits(row) {
        // page name
        wrap(row.querySelector('.mw-title'), 'td', 'gadget-rc-pagename');

        // "rollback x edit(s)" -> "rollback"
        // link does not exist if it is a page creation or user does not have the right
        if (row.querySelector('.mw-rollback-link')) {
            row.querySelector('.mw-rollback-link a').childNodes[0].nodeValue = 'rollback';
        }

        // user info
        $('.mw-userlink, .mw-usertoollinks, .comment, .mw-rollback-link, .mw-tag-markers, .history-deleted', row).wrapAll('<td class="gadget-rc-userlinks" />');

        cleanUserLinks(row);
        cleanSummary(row);

        // rename <td>
        row.querySelector('.mw-changeslist-line-inner').className = 'gadget-rc-diff';

        // rearrange newly created <td>s
        insert(row, '.gadget-rc-userlinks', '.gadget-rc-diff');
        insert(row, '.gadget-rc-pagename', '.gadget-rc-userlinks');
        insert(row, '.gadget-rc-diff', '.gadget-rc-userlinks');
    }

    function cleanUserLinks(row) {
        // (talk | contribs | block) -> (t | c | b)
        // possible variations: (talk), (talk | contribs), (talk | block), (talk | contribs | block), (username removed)

        // if username has been revdeled (shows "(username removed)"), leave
        if (row.querySelector('.history-deleted')) {
            return;
        }

        row.querySelector('.mw-usertoollinks-talk').childNodes[0].nodeValue = 't';

        // IPs don't have dedicated contribs link, it's linked in their username
        if (row.querySelector('.mw-usertoollinks-contribs')) {
            row.querySelector('.mw-usertoollinks-contribs').childNodes[0].nodeValue = 'c';
        }

        // non-admins don't have block link
        if (row.querySelector('.mw-usertoollinks-block')) {
            row.querySelector('.mw-usertoollinks-block').childNodes[0].nodeValue = 'b';
        }

        // remove spaces around pipes
        var toolLinks = row.querySelector('.mw-usertoollinks').childNodes;

        if (toolLinks.length > 3) {
            toolLinks[2].nodeValue = '|';
            if (toolLinks[4].nodeValue === ' | ') toolLinks[4].nodeValue = '|';
        }
    }

    function cleanSummary(row) {
        if (row.querySelector('.comment')) {
            var nodes = row.querySelector('.comment').childNodes;
            var firstNode = nodes[0].textContent;
            var lastNode = nodes[nodes.length - 1].textContent;

            // remove parentheses around edit summary
            // if summary is pure text, ie. no links
            if (nodes.length === 1) {
                nodes[0].textContent = firstNode.slice(1, -1);
            }
            // else there are links in the summary (section links, user-created links, etc.)
            else {
                nodes[0].textContent = firstNode.slice(1); // (
                nodes[nodes.length - 1].textContent = lastNode.slice(0, -1); // )
            }

            // in eg. "(→ Trivia)" only the arrow, not the section title, is a link;
            // move title into <a> for a larger click target
            if (row.querySelector('.autocomment')) {
                var arrow = row.querySelector('.comment a');

                arrow.appendChild(row.querySelector('.autocomment'));
                arrow.nextSibling.remove(); // remove leftover whitespace node
            }
        }
    }

    function init() {
        var button = new OO.ui.ButtonWidget({
            icon: 'viewCompact',
            label: 'ReadableRC',
            title: 'Format RecentChanges into columns for readability',
            classes: ['gadget-rc-button'],
            flags: [
                'primary',
                'progressive'
            ]
        });

        button.on('click', function() {
            runReadableRC($('.mw-changeslist'));
            mw.hook('wikipage.content').add(runReadableRC);
            button.setDisabled(true);
        });

        mw.hook('structuredChangeFilters.ui.initialized').add(function() {
            $('.mw-rcfilters-ui-liveUpdateButtonWidget').append(button.$element);
        });
    }

    $(init);
})(jQuery, mediaWiki);

// </nowiki>