User:Fred Gandt/watchUserContribs.js

if ( ( /^Special:Contributions/ ).test( mw.config.get( "wgPageName" ) ) ) {

"use strict";

let wtchlst = sessionStorage[ "fg-watchlist" ] || {};

const STYL = document.createElement( "style" ),

quotes2Hashes = s => s.replace( /\"/g, "#" ),

firstInputIn = e => e.querySelector( "input" ),

sessionStore = () => sessionStorage[ "fg-watchlist" ] = JSON.stringify( wtchlst ),

sessionClear = () => {

if ( confirm( "The cache will be recompiled the next time you load a user contribs page." ) ) {

delete sessionStorage[ "fg-watchlist" ];

}

},

apiQuery = ( dt, fnc ) => {

dt.format = "json";

$.ajax( {

type: "POST",

url: "/w/api.php",

dataType: dt.format,

data: dt,

success: function( data ) { fnc( data ) },

error: function( data) { console.error( data ) }

} );

},

doTheDo = () => {

$( document ).ready( () => {

const SCTN = document.querySelector( "section.mw-pager-body" ),

LIS = SCTN.querySelectorAll( "li[data-mw-revid]" ),

boSelector = evt => {

let trg = evt.target,

chckd = trg.checked;

LIS.forEach( li => firstInputIn( li ).checked = chckd );

firstInputIn( trg.parentElement === TOPOPT ? BTMOPT : TOPOPT ).checked = chckd;

},

captainKoons = evt => {

let wtchn = evt.target.value === "Watch",

slctd = Array.from( LIS )

.map( li => firstInputIn( li ) )

.filter( npt => npt.checked && !( wtchn && npt.parentElement.classList.contains( "fg-watched" ) ) )

.map( npt => npt.value.replace( /( talk(?=:)|Talk:)/, "" ) );

slctd = [ ...new Set( slctd ) ];

let ttls = slctd.splice( 0, 50 );

while ( ttls.length ) {

let qs = {

action: "watch",

titles: ttls.join( "|" ),

token: mw.user.tokens.values.watchToken

};

if ( !wtchn ) {

qs.unwatch = true;

}

apiQuery( qs, function( data ) {

data.watch.map( r => r.title ).forEach( ttl => {

if ( wtchn ) {

wtchlst[ ttl ] = 1;

} else {

delete wtchlst[ ttl ];

}

SCTN.querySelectorAll( `input[data-title$="${quotes2Hashes( ttl )}"]` ).forEach( npt => npt.parentElement.classList.toggle( "fg-watched", wtchn ) );

} );

sessionStore();

} );

ttls = slctd.splice( 0, 50 );

}

},

makeInput = ( t, v ) => {

let npt = document.createElement( "input" );

npt.type = t;

npt.title = npt.value = v;

npt.dataset.title = quotes2Hashes( v );

npt.classList.toggle( "fg-checkbox", t === "checkbox" );

return npt;

},

makeOptions = () => {

let p = document.createElement( "p" );

npt = makeInput( "checkbox", "(De)select all" );

npt.addEventListener( "change", boSelector, { passive: true } );

p.append( npt );

npt = makeInput( "button", "Watch" );

npt.addEventListener( "click", captainKoons, { passive: true } );

p.append( npt );

p.append( document.createTextNode( " or " ) );

npt = makeInput( "button", "Unwatch" );

npt.addEventListener( "click", captainKoons, { passive: true } );

p.append( npt );

p.append( document.createTextNode( " the selected pages. " ) );

npt = makeInput( "button", "Clear watchlist cache" );

npt.addEventListener( "click", sessionClear, { once: true, passive: true } );

p.append( npt );

return p;

},

TOPOPT = makeOptions(),

BTMOPT = makeOptions();

STYL.textContent = "li.fg-watched{border:0 solid #90d4e9;border-width:1px .5em}input.fg-checkbox{margin:0 1em 0 .5em;vertical-align:-10%}";

document.querySelector( "head" ).append( STYL );

LIS.forEach( li => {

let ttl = li.querySelector( "a.mw-contributions-title" ).title;

li.classList.toggle( "fg-watched", !!wtchlst[ ttl ] );

li.prepend( makeInput( "checkbox", ttl ) );

} );

SCTN.prepend( TOPOPT );

SCTN.append( BTMOPT );

} );

},

compileWatchlist = wlr => {

apiQuery( wlr, function( data ) {

data.watchlistraw.forEach( ttl => wtchlst[ ttl.title ] = 1 );

if ( data.continue ) {

wlr.wrcontinue = data.continue.wrcontinue;

compileWatchlist( wlr );

} else if ( data.hasOwnProperty( "batchcomplete" ) ) {

sessionStore();

doTheDo();

}

} );

};

if ( $.isEmptyObject( wtchlst ) ) {

compileWatchlist( { action: "query", list: "watchlistraw", wrlimit: 500 } );

} else {

wtchlst = JSON.parse( wtchlst );

doTheDo();

}

}