User:DatGuy/mark-rangeblocked.js

/* Adapted from Gadget-markblocked.js with the only addition of supporting rangeblocks

This gadget will pull the user accounts and IPs from the history page and will strike out the users that are currently blocked. function markRangeBlocked( container ) { var ipv6Regex = /^((?=.*::)(?!.*::.+::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i;

// Collect all the links in the page's content var contentLinks = $( container ).find( 'a' );

var mbTooltip = window.mbTooltip || '$1; blocked ($2) by $3: $4 ($5 ago)';

// Get all aliases for user: & user_talk: var userNS = []; for ( var ns in mw.config.get( 'wgNamespaceIds' ) ) { if ( mw.config.get( 'wgNamespaceIds' )[ns] == 2 || mw.config.get( 'wgNamespaceIds' )[ns] == 3 ) { userNS.push( mw.util.escapeRegExp(ns.replace( /_/g, ' ' )) + ':' ); }	}

// Let wikis that are importing this gadget specify the local alias of Special:Contributions if ( window.markblocked_contributions === undefined ) { window.markblocked_contributions = 'Special:Contributions'; }	// RegExp for all titles that are User:| User_talk: | Special:Contributions/ (for userscripts) var userTitleRX = new RegExp( '^(' + userNS.join( '|' ) + '|' + window.markblocked_contributions + '\\/)+([^\\/#]+)$', 'i' );

// RegExp for links // articleRX also matches external links in order to support the noping template var articleRX = new RegExp( mw.config.get( 'wgArticlePath' ).replace('$1', '') + '([^#]+)' ); var scriptRX = new RegExp( '^' + mw.config.get( 'wgScript' ) + '\\?title=([^#&]+)' );

var userLinks = {}; var user, url, ma, pgTitle;

// Find all "user" links and save them in userLinks : { 'users': [, , ...], 'user2': [ , , ...], ... }	contentLinks.each( function( i, lnk ) {		if( $( lnk ).hasClass("mw-changeslist-date") || $( lnk ).parent("span").hasClass("mw-history-undo") || $(lnk).parent("span").hasClass("mw-rollback-link") )		{			return;		}		url = $( lnk ).attr( 'href' );		if ( !url ) {			return;		}		if ( ma = articleRX.exec( url ) ) {			pgTitle = ma[1];		} else if ( ma = scriptRX.exec( url ) ) {			pgTitle = ma[1];		} else {			return;		}		pgTitle = decodeURIComponent( pgTitle ).replace( /_/g, ' ' );		user = userTitleRX.exec( pgTitle );		if ( !user ) {			return;		}		user = user[2];		if( ipv6Regex.test(user) ) user = user.toUpperCase;		$( lnk ).addClass( 'userlink' );		if ( !userLinks[user] ) {			userLinks[user] = [];		}		userLinks[user].push (lnk );	} );

// Convert users into array var ips = []; for ( var u in userLinks ) { if ( mw.util.isIPAddress(u) ) { ips.push( u ); }	}	if ( ips.length === 0 ) { return; }

// API request var serverTime = 0; var apiRequests = ips.length; container.addClass( 'markrangeblocked-loading' ); var api = new mw.Api; while ( ips.length > 0 ) { // Scope function to prevent overwriting currentIP because of async (function ( currentIP ) {           api.post( { list: 'blocks', bklimit: 100, bkip: currentIP, bkprop: 'user|by|timestamp|expiry|range|reason|restrictions' }).done( function(resp, xhr) { markLinks( resp, xhr, currentIP ); });       })( ips.shift ); }

return; // the end

// Callback: receive data and mark links function markLinks( resp, xhr, userLink ) {

serverTime = new Date( xhr.getResponseHeader('Date') ); var list, blk, tip, links, lnk; if ( !resp || !( list = resp.query ) || !( list = list.blocks ) ) { return; }

for ( var i = 0; i < list.length; i++ ) { blk = list[i]; var partial = blk.restrictions && !Array.isArray(blk.restrictions); // Check if partial block var rangeblock = blk.rangestart !== blk.rangeend; // Check if range block // If not rangeblock, hand it off to main function if ( !rangeblock ) continue;

if ( /^in/.test( blk.expiry ) ) { clss = partial ? 'user-blocked-partial' : 'user-blocked-indef'; blTime = blk.expiry; } else { clss = partial ? 'user-blocked-partial' : 'user-blocked-temp'; blTime = inHours ( parseTS( blk.expiry ) - parseTS( blk.timestamp ) ); }			tip = mbTooltip; if (partial) { tip = tip.replace( 'blocked', 'partially blocked' ); }			if (rangeblock) { tip = tip.replace( 'blocked', 'rangeblocked' ); }

tip = tip.replace( '$1', blk.user ) .replace( '$2', blTime ) .replace( '$3', blk.by ) .replace( '$4', blk.reason ) .replace( '$5', inHours ( serverTime - parseTS( blk.timestamp ) ) ); links = userLinks[userLink || blk.user]; for ( var k = 0; links && k < links.length; k++ ) { lnk = $( links[k] ); lnk = lnk.addClass( clss ); if ( window.mbTipBox ) { $( ' # ' ).attr( 'title', tip ).insertBefore( lnk ); } else { lnk.attr( 'title', tip ); }			}		}

if ( --apiRequests === 0 ) { // last response container.removeClass( 'markrangeblocked-loading' ); $( '#ca-showrangeblocks' ).parent.remove; // remove added portlet link }

}

// AUX functions

// 20081226220605 or  2008-01-26T06:34:19Z   -> date function parseTS( ts ) { var m = ts.replace( /\D/g, '' ).match( /(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/ ); return new Date ( Date.UTC( m[1], m[2]-1, m[3], m[4], m[5], m[6] ) ); }

function inHours( ms ) { // milliseconds -> "2:30" or 5,06d or 21d var mm = Math.floor( ms / 60000 ); if ( !mm ) { return Math.floor( ms / 1000 ) + 's'; }		var hh = Math.floor( mm / 60 ); mm = mm % 60; var dd = Math.floor( hh / 24 ); hh = hh % 24; if ( dd ) { return dd + ( dd < 10 ? '.' + zz( hh ) : '' ) + 'd'; }		return hh + ':' + zz( mm ); }

function zz( v ) { // 6 -> '06' if ( v <= 9 ) { v = '0' + v;		} return v;	} }// -- end of main function

// Start on some pages switch ( mw.config.get( 'wgAction' ) ) { case 'edit': case 'submit': break; default: // 'view', 'history', 'purge', ... var maybeAutostart = $.Deferred; if ( window.mbNoAutoStart ) { var portletLink = mw.util.addPortletLink( 'p-cactions', '', 'XX', 'ca-showrangeblocks' ); $( portletLink ).click( function ( e ) {				e.preventDefault;				maybeAutostart.resolve;			} ); } else { maybeAutostart.resolve; }

$.when( $.ready, mw.loader.using( 'mediawiki.util' ), maybeAutostart ).then( function {			var firstTime = true;

mw.hook( 'wikipage.content' ).add( function ( container ) {				// On the first call after initial page load, container is mw.util.$content

// Used to limit mainspace activity to just the diff definitions if ( mw.config.get( 'wgAction' ) === 'view' && mw.config.get( 'wgNamespaceNumber' ) === 0 ) { container = container.find( '.diff-title' ); }

if ( firstTime ) { firstTime = false;

// On page load, also update the namespace tab container = container.add( '#ca-nstab-user' );

mw.util.addCSS('\						.markrangeblocked-loading a.userlink {opacity:' + ( window.mbLoadingOpacity || 0.85 ) + '}\						a.user-blocked-temp {'  + ( window.mbTempStyle || 'opacity: 0.7; text-decoration: line-through' ) + '}\						a.user-blocked-indef {'  + ( window.mbIndefStyle || 'opacity: 0.4; font-style: italic; text-decoration: line-through' ) + '}\						a.user-blocked-partial {'  + ( window.mbPartialStyle || 'text-decoration: underline; text-decoration-style: dotted' ) + '}\						.user-blocked-tipbox {' + ( window.mbTipBoxStyle || 'font-size:smaller; background:#FFFFF0; border:1px solid #FEA; padding:0 0.3em; color:#AAA' ) + '}\					'); }

markRangeBlocked( container ); } );		} ); }