/* Watchlyst Greybar Unsin ([[User:Aaron Liu/Watchlyst Greybar Unsin.js]]) 3.3 */
// <syntaxhighlight lang="js">
{ // wrap in braces to avoid pollution
// utility function that adds CSS so that user can easily override it
function addCss( style ) {
mw.loader.addStyleTag( style, document.head.children[ 0 ] );
}
// non-talk namespaces have an even number
if ( mw.config.get( 'wgNamespaceNumber' ) % 2 === 0 && mw.config.get( 'action' ) !== 'history' ) {
// non-talk, non-history and non-watchlist pages don't have this style for some reason
addCss( '.autocomment,.autocomment a,.autocomment a:visited{color:#72777d}' );
}
function getWatchlyst( data ) {
// Don't display if no new watchlist items or if you're on the watchlist page
if ( data.query.watchlist.length !== 0 && mw.config.get( 'wgPageName' ) !== 'Special:Watchlist' ) {
data = data.query.watchlist[ 0 ];
const summary = data.parsedcomment
.replaceAll( '<', '<' ).replaceAll( '>', '>' ).replaceAll( '"', '"' ); // might edit under categorization
const user = '<a href="' + mw.util.getUrl( data.anon ? 'Special:Contributions/' : 'User:' + data.user ) + '">' + data.user + '</a>';
const $dismiss = $( '<button>' ).attr( 'id', 'watchlystDismiss' ).addClass( 'dismissButton' ).text( 'dismiss' )
.on( 'click', () => refreshWatchlyst( data.title, data.timestamp ) );
switch ( data.type ) {
case 'new':
case 'edit': {
const changed = data.type === 'edit' ? 'edited' : ( data.type === 'new' ? 'created' : 'changed' );
const page = data.title;
return $( '<span>' +
'"<a class="mw-changeslist-title" href="' + mw.util.getUrl( page ) + '">' + page + '</a>' +
'" ' + changed + ' by ' + user + ( summary.length === 0 ? '' : ': "' + summary + '"' ) +
'. (<a class="mw-changeslist-diff" href="' + mw.util.getUrl( page, { diff: data.revid } ) + '">diff</a>' +
', <a class="mw-changeslist-hist" href="' + mw.util.getUrl( page, { action: 'history' } ) + '">hist</a>' +
', <a class="mw-changeslist-watchlist" href="' + mw.util.getUrl( 'Special:Watchlist' ) + '">watchlist</a>) </span>' )
.append( $dismiss );
}
case 'categorize': {
const page = data.title;
summary.replace( ' category', ' <a class="mw-changelist-title" href="' + mw.util.getUrl( page ) + '">' + page + ' by ' + user );
}
// so that we can reuse the same code...
/* fall through */
default:
// categorize and log have quite similar formats, so I think external also does.
// tell me if you encounter it
return $( '<span>' +
data.logdisplay.replace( '<', '<' ).replace( '>', '>' ).replace( '"', '"' ) +
( summary.length === 0 ? '' : ': "' + summary + '"' ) +
'. (<a class="mw-changeslist-watchlist" href="' + mw.util.getUrl( 'Special:Watchlist' ) + '">watchlist</a>) </span>' )
.append( $dismiss );
}
}
return '';
}
const $watchlyst = $( '<aside>' ).attr( 'id', 'watchlyst' ); // the parent div
const api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': 'Watchlyst/3.2' } },
parameters: { format: 'json', formatversion: '2', errorformat: 'html' }
} );
function refreshWatchlyst( title = null, timestamp = null ) {
if ( typeof ( title ) === 'string' ) {
// strings are immutable, so lets convert which would bump levels correctly
timestamp = new Date( timestamp );
timestamp.setSeconds( timestamp.getSeconds() + 1 );
timestamp = timestamp.toISOString().slice( 0, 19 ) + 'Z'; // API doesn't accept microseconds
api.postWithToken( 'csrf', { action: 'setnotificationtimestamp', titles: [ title ], timestamp: timestamp } ).done( refreshWatchlyst );
return;
}
$watchlyst.addClass( 'loading' );
/* Find the top unread item in the watchlist.
We only need one item, so set the limit to 1 to ease the load on the server. */
api.get( {
action: 'query', list: 'watchlist', wllimit: 1, wldir: 'older', wlshow: 'unread',
wlexcludeuser: mw.config.get( 'wgUserName' ), wlprop: [ 'parsedcomment', 'ids', 'title', 'user', 'loginfo', 'timestamp' ]
} ).done( ( data ) => {
data = getWatchlyst( data );
if ( data === '' ) {
$watchlyst.remove();
const $sub = $( '#mw-content-subtitle' );
if ( $sub.text() === '' ) {
$sub.remove();
}
} else {
$watchlyst.html( data ).removeClass( 'loading' );
}
} ).fail( ( data ) => {
$watchlyst.removeClass( 'loading' );
for ( const err in data.errors ) {
$watchlyst.html( err.module + ': ' + err.html + ' (' + err.code + ') ' );
}
} );
}
$( () => {
if ( mw.config.get( 'wgCanonicalSpecialPageName' ) !== 'Watchlist' ) {
addCss( ".dismissButton::before { content: '['; color: #202122; } .dismissButton::after { content: ']'; color: #202122; }" );
addCss( `.dismissButton {
background: transparent;
border: 0; padding: 0;
cursor: pointer;
}` );
addCss( '@media (prefers-reduced-motion: no-preference) { #watchlyst { transition: opacity 0.5s; } }' );
try { // determine color of link
addCss( '.dismissButton { color: ' + getComputedStyle( document.querySelector( '.mw-body-content a:link:not([class])' ) ).getPropertyValue( 'color' ) + '; }' );
} catch ( _ ) {
addCss( '.dismissButton { color: #36c; }' );
}
refreshWatchlyst();
addCss( '.loading { opacity: 0; }' );
$( '#mw-content-subtitle' ).prepend( $watchlyst );
}
} );
}
// </syntaxhighlight>