MediaWiki:Gadget-PatrolFromHere.js

// 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; });