User:SoledadKabocha/markBlockedPlus.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
![]() | This user script seems to have a documentation page at User:SoledadKabocha/markBlockedPlus. |
/* Based on the original markblocked script [[:ru:MediaWiki:Gadget-markblocked.js]]
by Alex Smotrov et al., original idea by Kalan
Additional inspiration from [[User:Equazcion/sysopdetector.js]]
*/
function defaultPref( cfig, dflt ) {
return ( typeof cfig == typeof dflt ? cfig : dflt );
}
window.mbShowUnregistered = defaultPref( window.mbShowUnregistered, true );
window.mbShowGroups = defaultPref( window.mbShowGroups, true );
window.mbShowEditCount = defaultPref( window.mbShowEditCount, true );
window.mbShowRegDateAbs = defaultPref( window.mbShowRegDateAbs, true );
window.mbRemoveWaitingCSSOnError = defaultPref( window.mbRemoveWaitingCSSOnError, true );
window.mbLinkClassifierRedirWarn = defaultPref( window.mbLinkClassifierRedirWarn, true );
window.mbLinkClassifierTimeoutHack = defaultPref( window.mbLinkClassifierTimeoutHack, true );
var mbLinkClassifierInstalled = ( typeof LinkClassifier == 'object' );
var mbShowRedirectWarning = mbLinkClassifierInstalled && window.mbLinkClassifierRedirWarn;
function markBlocked( container ) {
var contribPageNames = [ 'Contributions' ];
function scrapeContribLink( ) {
//something went wrong, so get local alias for "Contributions" from "my contribs" link on top
var mwCont = /:([^\/]+)\//.exec( $( '#pt-mycontris a' ).attr( 'href' ) );
if( mwCont ) { mwCont = mwCont[1]; contribPageNames[0] = mwCont; }
reallyMarkBlocked( container, contribPageNames );
}
function getContribNameCallback( r, sts, xhr ) {
if ( !r || !r.query ) { scrapeContribLink( ); return; }
if ( !r.query.specialpagealiases ) { scrapeContribLink( ); return; }
for ( pag in r.query.specialpagealiases ) {
if ( pag.realname == 'Contributions' ) {
if ( pag.aliases ) { contribPageNames = pag.aliases; break; }
}
}
reallyMarkBlocked( container, contribPageNames );
}
function getContribNamePanic( xhr, textStatus, errorThrown ) {
//TODO: error reporting
scrapeContribLink( );
}
if ( typeof window.mbLocalContribsName == 'string' ) {
contribPageNames[0] = window.mbLocalContribsName;
reallyMarkBlocked( container, contribPageNames );
}
else if ( $.isArray( window.mbLocalContribsName ) ) {
contribPageNames = window.mbLocalContribsName;
reallyMarkBlocked( container, contribPageNames );
}
else {
var q = {
format:'json',
action:'query',
meta:'siteinfo',
siprop:'specialpagealiases'
}
$.ajax( {
url:mw.util.wikiScript('api'),
dataType:'json',
type:'POST',
data:q,
rawdata:q,
success:getContribNameCallback,
error:getContribNamePanic
} );
}
}
function reallyMarkBlocked( container, contribPageNames ) {
var contentLinks = container ? $(container).find('a') : $('#content a').add('#ca-nstab-user a')
if ( typeof window.mbTempStyle == 'string' && typeof window.mbTemp2Style != 'string' ) window.mbTemp2Style = window.mbTempStyle;
mw.util.addCSS('\
.user-blocked-temp{' + defaultPref(window.mbTempStyle, 'opacity: 0.7; text-decoration: line-through') + '}\
.user-blocked-temp2{' + defaultPref(window.mbTemp2Style, 'opacity: 0.7; text-decoration: line-through') + '}\
.user-blocked-indef{' + defaultPref(window.mbIndefStyle, 'opacity: 0.4; font-style: italic; text-decoration: line-through') + '}\
.user-blocked-tipbox{' + defaultPref(window.mbTipBoxStyle, 'font-size:smaller; background:#FFFFF0; border:1px solid #FEA; padding:0 0.3em; color:#AAA') + '}\
.user-info-tipbox{' + defaultPref(window.mbTipBoxInfoStyle, 'font-size:smaller; background:#FFFFF0; border:1px solid #FEA; padding:0 0.3em; color:#AAA') + '}\
')
var mbTooltip = defaultPref( window.mbTooltip, '; blocked ($1) by $2: $3 ($4 ago)' );
var mbTipBoxCharsToTrim = defaultPref( window.mbTipBoxCharsToTrim, 2 );
//TODO: Figure out a sane way to let users customize the rest of the tooltip.
//get all aliases for user:, user_talk: and special:
var userNS = [];
var specialNS = [];
var nIDs = mw.config.get( 'wgNamespaceIds' );
for(var ns in nIDs) {
if( nIDs[ns] == 2 || nIDs[ns] == 3 ) {
userNS.push( ns.replace(/_/g, ' ') + ':' )
}
else if( nIDs[ns] == -1 ) {
specialNS.push( ns.replace(/_/g, ' ') + ':' )
}
}
var contribNames = contribPageNames;
//RegExp for all titles that are User:| User_talk: | Special:Contributions/ (for userscripts)
var userTitleRX = new RegExp('^'
+ '(' + userNS.join('|')
+ '|(?:' + specialNS.join('|') + ')(?:' + contribNames.join('|') + ')\\/'
+ ')'
+ '([^\\/#]+)$', 'i')
//RegExp for links
var wAP = mw.config.get( 'wgArticlePath' );
var articleRX = new RegExp( '^' + wAP.replace('$1', '') + '([^#]+)' );
var scriptRX = new RegExp( '^' + wgScript + '\\?title=([^#&]+)' );
var userLinks = { };
var url, ma, pgTitle;
//find all "user" links and save them in userLinks : { 'users': [<link1>, <link2>, ...], 'user2': [<link3>, <link3>, ...], ... }
contentLinks.each(function(i, lnk){
var $lnk = $( lnk );
var myId = $lnk.attr( 'id' );
if ( myId && /^sectiontitlecopy\d+$/.test( myId ) ) return; //exclude links added by [[User:Bility/copySectionLink]] to user(talk) pages
if ( $lnk.hasClass( 'mw-headline-anchor' ) ) return; //built-in MW feature like copySectionLink
url = $lnk.attr( 'href' );
if ( !url ) return;
//for compatibility with other scripts which may add redirect=no to links
var redirNoRegex = ( wAP.indexOf( '?' ) == -1 ? /\?redirect=no$/ : /&redirect=no$/ );
url = url.replace( redirNoRegex, '' );
if ( url.charAt(0) != '/' || url.indexOf( 'friendlywelcome=' ) != -1 ) { return; }
else 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];
user = user.replace( /^\s+|\s+$/g, '' );
if ( user == '0' || user == 'newbies' || user.length > 64 ) { $lnk.addClass( 'baduserlink' ); return; }
if ( /[\u0080-\u009F\u00A0\u2000-\u200F\u2028-\u202F\u3000\uE000-\uF8FF\uFFFD]/.test( user ) ) { $lnk.addClass( 'baduserlink' ); return; }
user = user.charAt(0).toUpperCase( ) + user.substr(1);
var doubledPrefix = userTitleRX.exec( user );
if( doubledPrefix ) { $lnk.addClass( 'baduserlink' ); return; }
$lnk.addClass( 'userlink' )
if( !( $.isArray( userLinks[user] ) ) ) userLinks[user] = [ ];
userLinks[user].push( lnk );
})
//convert users into array
var users = [ ];
for ( var u in userLinks ) { users.push( u ); }
if ( users.length === 0 ) { return; }
//API request
var wgServerTime, apiRequests = 0;
var waitingCSS = mw.util.addCSS( 'a.userlink {opacity:' + defaultPref(window.mbLoadingOpacity, 0.85) + '}' );
while ( users.length > 0 ) {
apiRequests++;
var formattedUsers = users.splice(0,50).join('|');
$.post(
mw.util.wikiScript('api') + '?format=json&action=query',
{ list: 'blocks|users',
bklimit: 150, bkusers: formattedUsers, //XXX: I increased this limit from 100...could that cause problems?
bkprop: 'user|by|timestamp|expiry|reason',
uslimit: 51, ususers: formattedUsers, //+1 just to be safe (XXX: why? could this actually cause problems?)
usprop: 'groups|editcount|registration' },
markLinks
)
}
return //done sending API requests
//callback: receive data and mark links
function markLinks(resp, status, xhr){
function countRequestAsDone(bSuccess){
if( --apiRequests == 0 ){ //last response
waitingCSS.disabled = ( bSuccess || window.mbRemoveWaitingCSSOnError );
$('#ca-showblocks').remove() //remove added portlet link (TODO: only if success?)
}
}
wgServerTime = new Date( xhr.getResponseHeader('Date') )
var list, list2, blk, usr, tip, tip2, links, kmax, lnk, curLinkRW, curLinkOldTitle
if( !resp ){
if ( window.mbReportApiErrors === true ){
waitingCSS.disabled = window.mbRemoveWaitingCSSOnError;
throw new Error('markBlockedPlus: No response from API');
}
else{
countRequestAsDone(false); return;
}
}
if( !(list=resp.query) ){
if ( window.mbReportApiErrors === true ){
waitingCSS.disabled = window.mbRemoveWaitingCSSOnError;
var errStr = 'markBlockedPlus: API error';
if ( resp.error ){
errStr += ': code: ' + resp.error.code + ', info: ' + resp.error.info;
}
throw new Error(errStr);
}
else{
countRequestAsDone(false); return;
}
}
else {
list2=list.users;
list=list.blocks;
}
var mbShowRegDate = window.mbShowRegDateAbs || window.mbShowRegDateRel;
var mbUsingNewFeatures = window.mbShowUnregistered || window.mbShowGroups || window.mbShowEditCount || mbShowRegDate;
var mbNewFeaturesValid = mbUsingNewFeatures && list2;
var userNSRX = new RegExp( '^' + '(' + userNS.join('|') + ')', 'i' );
if ( list ){
for( var i=0; i<list.length; i++){
blk = list[i]
if( /^in/.test(blk.expiry) ){
clss = 'user-blocked-indef';
blTime = blk.expiry;
}else{
var rawBlTime = parseTS(blk.expiry) - parseTS(blk.timestamp);
var blTimeRem = parseTS(blk.expiry) - wgServerTime;
var timeToCheck = ( window.mbLongThreshIsRemainingTime === true ? blTimeRem : rawBlTime );
if ( typeof window.mbPseudoIndefThreshold == 'number' && timeToCheck >= window.mbPseudoIndefThreshold ) {
clss = 'user-blocked-indef';
}
else {
clss = 'user-blocked-temp';
if ( typeof window.mbLongThreshold == 'number' && timeToCheck >= window.mbLongThreshold ) clss += '2';
}
blTime = inHours( rawBlTime );
}
if( blk.reason ) {
var preventive = /(pr(otec|even)t|in case|[ei]nsure)/i.test(blk.reason);
if ( /(compr[io]mise|(been|got|was) ?hack|pass[- ]?word|hijack)/i.test(blk.reason) ) {
clss += ( preventive ? ' user-blocked-preventcompromise' : ' user-blocked-compromised' );
}
if ( /(wiki[- ]?break|(self|user)[- ]?request|retir(ed|ement|ing))/i.test(blk.reason) && !( /\b(abus|ban|sock)/i.test(blk.reason) ) ) {
clss += ' user-blocked-wikibreakenforce';
}
if ( /decease|ha(d|s|ve) died|pass(ed|ing)? (away|on)|mortem/i.test(blk.reason) ) {
clss += ' user-blocked-deceased';
}
else if ( /is dead/i.test(blk.reason) && preventive ) {
clss += ' user-blocked-deceased';
}
}
if ( blk.user == blk.by ) clss += ' user-blocked-self';
tip = mbTooltip.replace('$1', blTime).replace('$2', blk.by).replace('$3', blk.reason)
.replace('$4', inHours ( wgServerTime - parseTS(blk.timestamp) ) )
links = userLinks[blk.user]
kmax = ( links === undefined ? 0 : links.length ); //TODO: add error reporting
for (var k=0; k<kmax; k++){
lnk = $(links[k]).addClass(clss);
curLinkRW = '';
curLinkOldTitle = lnk.attr('title') + '';
if( mbShowRedirectWarning && !mbNewFeaturesValid && ( lnk.hasClass( 'redirect' ) || lnk.hasClass( 'mw-redirect' ) ) ){
if ( userNSRX.test( skipLCAppend( curLinkOldTitle ) ) === false ) {
curLinkRW = '\nBlock log for ' + blk.user + '; redirects outside this wiki\'s user namespaces';
}
else if ( tipNameDiffers( curLinkOldTitle, blk.user ) ) {
curLinkRW = '\nBlock log for ' + blk.user + '; redirects to other username or IP';
}
}
if( window.mbTipBox === true ){
$('<span class="user-blocked-tipbox">'+defaultPref( window.mbTipBoxText, 'B' )+'</span>').attr('title', tip.substr(mbTipBoxCharsToTrim) + curLinkRW).insertBefore(lnk)
}else{
if ( lnk.children( ) ) lnk.children( ).removeAttr( 'title' );
lnk.attr( 'title', curLinkOldTitle + tip + curLinkRW )
}
}
}
}
if ( mbNewFeaturesValid ){
for( var i=0; i<list2.length; i++){
usr = list2[i];
tip2 = '';
if( usr.missing === '' ){
if( window.mbShowUnregistered === true ) tip2 = defaultPref( window.mbUnregisteredText, '\nnot registered' )
}
else if( usr.invalid !== '' ){
if ( (window.mbShowGroups === true) && usr.groups ){
tip2 += '\n' + remStar( usr.groups );
}
if ( (window.mbShowEditCount === true) && usr.editcount ){
tip2 += '\n' + usr.editcount + ' edit';
if ( usr.editcount != '1' ) tip2 += 's';
if ( mbShowRegDate && usr.registration ){
tip2 += ' since ';
if ( (window.mbShowRegDateAbs === true) && (window.mbShowRegDateRel === true) ){
tip2 += usr.registration + ' (' + inHours( wgServerTime - parseTS(usr.registration) ) + ' ago)';
}
else if ( window.mbShowRegDateAbs === true ){
tip2 += usr.registration;
}
else /* relative only */{
tip2 += inHours( wgServerTime - parseTS(usr.registration) ) + ' ago';
}
}
}
else{
if ( mbShowRegDate && usr.registration ){
tip2 += '\ncreated ';
if ( (window.mbShowRegDateAbs === true) && (window.mbShowRegDateRel === true) ){
tip2 += usr.registration + ' (' + inHours( wgServerTime - parseTS(usr.registration) ) + ' ago)';
}
else if ( window.mbShowRegDateAbs === true ){
tip2 += usr.registration;
}
else /* relative only */{
tip2 += inHours( wgServerTime - parseTS(usr.registration) ) + ' ago';
}
}
}
}
links=userLinks[usr.name];
kmax = ( links === undefined ? 0 : links.length ); //TODO: add error reporting
for (var k=0; k<kmax; k++){
lnk = $(links[k]);
curLinkRW = '';
curLinkOldTitle = lnk.attr('title') + '';
if ( mbShowRedirectWarning && ( lnk.hasClass( 'redirect' ) || lnk.hasClass( 'mw-redirect' ) ) ){
if ( userNSRX.test( skipLCAppend( curLinkOldTitle ) ) === false ) {
curLinkRW = '\nInfo shown for ' + usr.name + '; redirects outside this wiki\'s user namespaces';
}
else if ( tipNameDiffers( curLinkOldTitle, usr.name ) ) {
curLinkRW = '\nInfo shown for ' + usr.name + '; redirects to other username or IP';
}
}
if( (window.mbTipBoxInfo === true) && tip2 != '' ){
$('<span class="user-info-tipbox">'+defaultPref( window.mbTipBoxInfoText, 'i' )+'</span>').attr('title', tip2.substr(1) + curLinkRW).insertBefore(lnk)
}else{
if ( lnk.children( ) ) lnk.children( ).removeAttr( 'title' );
lnk.attr( 'title', curLinkOldTitle + tip2 + curLinkRW )
}
}
}
}
countRequestAsDone(true);
}
//--------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 5d 6h or 21d
var mm = Math.round(ms/60000)
if( !mm ) return Math.round(ms/1000)+'s'
var hh = Math.floor(mm/60); mm %= 60
var dd = Math.floor(hh/24); hh %= 24
var yy = Math.floor(dd/365);
if ( yy && (window.mbShowYears === true) ){
dd %= 365;
return yy + (dd ? 'yr ' + dd + 'd' : 'yr');
}
else if (dd) return dd + 'd' + ((dd<10 && hh>0)?' '+hh+'h':'');
else return hh + ':' + zz(mm)
}
function zz(v){ //add leading zero to single digits: 6 -> '06'
if( v <= 9 ) v = '0' + v
return v
}
function remStar( oldGrps ) {
var cleanGrps = [];
for ( var i = 0; i < oldGrps.length; i++ ) {
if ( oldGrps[i] != '*' ) cleanGrps.push( oldGrps[i] );
}
return cleanGrps;
}
function skipLCAppend( ttip ) {
var myTtip = ttip;
if ( window.LinkClassifierRedirTitleAppend === true ) {
var lcAppendStrToCheck = '\n⤷';
var lcAppendedMsgPos = myTtip.indexOf( lcAppendStrToCheck );
myTtip = myTtip.substr( lcAppendedMsgPos + lcAppendStrToCheck.length );
}
return myTtip;
}
function tipNameDiffers( ttip, usrnm ) {
var myTtip = skipLCAppend( ttip );
var startPos = myTtip.indexOf( ':' );
if ( startPos <= 0 ) return false; //no valid username found - don't show warning
var fragEnd = mbTooltip.indexOf( '$' );
var fragment;
if ( fragEnd == -1 ) {
fragment = mbTooltip;
}
else if ( fragEnd == 0 ) {
//TODO: To handle this case properly, we should pass the actual values of the
//$-parameters into this function. Until then, document that window.mbTooltip
//should not begin with $
fragment = 'This wont' + Math.random( ) + 'match anything';
}
else {
fragment = mbTooltip.substr( 0, fragEnd );
}
var slashPos = myTtip.indexOf( '/' ); if ( slashPos == -1 ) slashPos = myTtip.length;
var anchorPos = myTtip.indexOf( '#' ); if ( anchorPos == -1 ) anchorPos = myTtip.length;
var lineBrkPos = myTtip.indexOf( '\n' ); if ( lineBrkPos == -1 ) lineBrkPos = myTtip.length;
var blkMsgPos = myTtip.indexOf( fragment ); if ( blkMsgPos == -1 ) blkMsgPos = myTtip.length;
var endPos = Math.min( slashPos, anchorPos, lineBrkPos, blkMsgPos );
startPos++;
if ( endPos <= startPos ) return false; //no valid username found - don't show warning
return ( usrnm != myTtip.substr( startPos, endPos - startPos ) );
}
}//-- end of reallyMarkBlocked
//start on some pages, making sure [[User:SoledadKabocha/linkclassifier2.js]] has finished
var shouldWaitForLinkClassifier = mbLinkClassifierInstalled &&
( window.LinkClassifierSupportsFuncChain === true ) &&
( window.LinkClassifierOnDemand === false ) &&
( window.mbNoAutoStart === false );
var zzAction = mw.config.get( 'wgAction' );
switch( zzAction ){
case 'edit':
case 'submit':
if( !window.mbEnableWhenEditing ) break
case 'purge':
//shouldn't happen on recent MW; action=purge should redirect (or prompt for confirmation?) rather than showing page content
//just give up
break;
case 'view':
if( mw.config.get( 'wgNamespaceNumber' ) === 0 && !( window.mbEnableOnMainspaceDiff === true && document.URL.indexOf( 'diff=' ) != -1 ) ) break
//otherwise continue with default
default: //'history', etc.
if ( shouldWaitForLinkClassifier ) {
if ( window.mbLinkClassifierTimeoutHack ) {
window.LinkClassifierChainedFunc = function () {
setTimeout( markBlocked, 1200 + Math.round( ( Math.random( ) + Math.random( ) ) * 500 ) );
};
}
else {
window.LinkClassifierChainedFunc = markBlocked;
}
}
else
$(function(){
if( window.mbNoAutoStart === true )
mw.util.addPortletLink(defaultPref( window.mbOnDemandLinkLoc, 'p-cactions' ), 'javascript:markBlocked()', defaultPref( window.mbOnDemandLinkText, 'XX' ), 'ca-showblocks')
else
markBlocked()
})
}