MediaWiki:Gadget-PatrolFromHere.js

From Pikipedia, the Pikmin wiki
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)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
// Adds a "Patrol from here" link to the wiki's toolbox
// by Espyo

$(function() {
    'use strict';

    const STORAGE_KEY = 'patrolFromHereOnScript';
    const PATROL_COUNTDOWN = 3;

    var patrolButton = getPatrolButton();
    var nextEditLink = getNextEditLink();
    var patrolFromHereA = null;
    var patrolling = false;
    var countdown = 0;
    var countdownTimer = null;

    // ========== FUNCTIONS ==========

    // Can this user even do patrols, and is it a non-Special page?
    function canPatrol() {
        return mw.user.tokens.values.patrolToken.length > 4 && mw.config.get('wgCanonicalNamespace') !== 'Special';
    }

    // Returns the patrol button on the current page, if it exists.
    function getPatrolButton() {
        var span = document.querySelector('#mw-diff-ntitle4 span.patrollink');
        if(!span) return null;
        return span.children[0];
    }

    // Returns the next edit link on the current page, if it exists.
    function getNextEditLink() {
        return document.querySelector('#differences-nextlink');
    }

    // Starts the "patrol from here" process.
    function startPatrollingFromHere() {
        console.log('Patrol from here script: starting patrolling process due to user input...');
        if(!nextEditLink) {
            patrolFromHereA.innerHTML = 'Patrol from here: already in last edit';
            patrolFromHereA.title = 'Cannot automatically patrol from here since this is already the last edit of the page.';
            patrolFromHereA.onclick = undefined;
        } else {
            patrolFromHereA.innerHTML = 'Patrol from here: patrolling...';
            patrolFromHereA.title = 'The "patrol from here" script is currently patrolling on its own. Click here to stop.';
            patrolFromHereA.onclick = stopPatrolProcess;
        }
        saveScriptDataInStorage();
        patrolling = true;
        doPagePatrolling();
    }

    // Stops the "patrol from here" process.
    function stopPatrolProcess(reachedEnd) {
        clearScriptDataFromStorage();
        patrolling = false;
        if(countdownTimer) clearTimeout(countdownTimer);
        if(reachedEnd) {
            patrolFromHereA.innerHTML = 'Patrol from here: finished!';
            patrolFromHereA.title = 'Stopped patrolling because there are no more diffs.';
            patrolFromHereA.onclick = undefined;
        } else {
            patrolFromHereA.innerHTML = 'Patrol from here: stopped';
            patrolFromHereA.title = 'Stopped patrolling because you cancelled. Click here to start patrolling from here.';
            patrolFromHereA.onclick = startPatrollingFromHere;
        }
    }

    // Patrols the current page and/or moves on to the next one.
    function doPagePatrolling() {
        console.log('Patrol from here script: doing this page\'s patrolling process...');
        if(patrolButton) {
            setupPatrolCallback(function() { goToNextOrFinish(); });
            patrolButton.click();
        } else {
            goToNextOrFinish();
        }
    }

    // Goes to the next page or finishes if there is none.
    function goToNextOrFinish() {
        if(nextEditLink) {
            console.log('Patrol from here script: moving on to the next diff...');
            nextEditLink.click();
        } else {
            console.log('Patrol from here script: finished!');
            stopPatrolProcess(true);
        }
    }

    // Sets up the logic to detect when the patrolling is over.
    // Using an observer and detecting for the deleted patrol button node isn't working for some reason, so we're brute-forcing.
    function setupPatrolCallback(callback) {
        const observer = new MutationObserver(function (mutations) {
            mutations.forEach(function (mutation) {
                if(!getPatrolButton()) {
                    callback();
                    return;
                }
            });
        });

        observer.observe(document.getElementById('mw-diff-ntitle4'), { childList: true, subtree: true });
    }

    // Ticks the countdown when a page loads and is being automatically patrolled.
    function tickCountdown() {
        if(countdown > 1) {
            countdown--;
            patrolFromHereA.innerHTML = 'Patrol from here: patrolling... (in ' + countdown + ')';
            countdownTimer = setTimeout(function() { tickCountdown(); }, 1000);
        } else {
            patrolFromHereA.innerHTML = 'Patrol from here: patrolling...';
            doPagePatrolling();
        }
    }

    // Creates the button in the toolbox.
    function createPatrolFromHereButton() {
        var toolsNav = document.getElementById('p-tb');
        if(!toolsNav) return;
        var toolsUl = toolsNav.children[1].children[0];
        if(!toolsUl) return;

        var patrolFromHereLi = document.createElement('li');
        toolsUl.appendChild(patrolFromHereLi);
        patrolFromHereLi.id = 't-patrolfromherescript';
        patrolFromHereLi.className = 'mw-list-item';

        patrolFromHereA = document.createElement('a');
        patrolFromHereLi.appendChild(patrolFromHereA);
        patrolFromHereA.innerHTML = 'Patrol from here';
        patrolFromHereA.href = 'javascript:;';
        patrolFromHereA.title =
            'Use this to patrol this edit and all subsequent edits of this page automatically. ' +
            'This goes through the diff pages on the wiki one by one until it reaches the end.';
        patrolFromHereA.onclick = startPatrollingFromHere;
    }

    // ========== STORAGE CONTROL ==========

    // Saves the start of the patrolling process onto the local storage so it persists between pages.
    function saveScriptDataInStorage() {
        localStorage.setItem(STORAGE_KEY, mw.config.get('wgArticleId'));
    }

    // Clears the patrolling process data from the local storage.
    function clearScriptDataFromStorage() {
        localStorage.removeItem(STORAGE_KEY, '');
    }

    // Loads the patrolling process data from the local storage if it exists.
    function loadScriptDataFromStorage() {
        var item = localStorage.getItem(STORAGE_KEY);
        if(item == null || item == undefined || item == 'undefined') return null;
        return item;
    }

    // ========== MAIN ==========
    function main() {
        if(!mw.config.get('wgDiffOldId') || !mw.config.get('wgDiffNewId')) return;
        if(!canPatrol()) return;

        createPatrolFromHereButton();

        var patrolData = loadScriptDataFromStorage();
        if(patrolData && patrolData == mw.config.get('wgArticleId')) {
            // Page just loaded in the middle of a "patrol from here" procedure.
            console.log('Patrol from here script: this page that just loaded is in the middle of the patrolling process. Starting countdown...');
            countdown = PATROL_COUNTDOWN + 1;
            tickCountdown();
            patrolFromHereA.onclick = function() { stopPatrolProcess(false); }
            patrolFromHereA.title = 'The "patrol from here" script is currently patrolling on its own. Click here to stop.';
        }
    }
    
    main();
});