MediaWiki:Gadget-ajaxrecentchanges.js

/* Ajax recent changes and patrolling framework, version [0.0.6a] Originally from: http://en.wikipedia.org/wiki/User:Splarka/ajaxrecentchanges.js

Note:
 * Patrol flags/links will sometimes show up where patrolling is not enabled.
 * This was a bug in the API that generated patrol tokens too often.
 * Fixed in r49000

Todo:
 * Checkbox for batch patrolling

Wontdo:
 * Parse comments
 * Add (talk|contribs|block) links, click their name, lazy
 * Localize error messages or focus on the log message 'logaction' (wrong tense, but it works fine).

if ( !window.arc_i18n ) { var arc_i18n = { 'title': 'Ajax recent changes', 'desc': 'Paginated enhanced ajax recent changes and patrolling.', 'mypatrol': 'My patrol log', 'startstamp': 'Start timestamp (8601)', 'limit': 'Limit', 'showapb': 'Show ajax patrol buttons', 'filterflag': 'Filter by flag', 'minor': 'Minor', 'bot': 'Bot', 'anon': 'Anon', 'redirect': 'Redirect', 'patrolled': 'Patrolled', 'all': 'All', 'filtertype': 'Filter by type', 'edit': 'Edits', 'new': 'New pages', 'log': 'Logs', 'filterns': 'Filter by namespace', 'fetch': 'Fetch', 'noresults': 'Nothing found.', 'diff': 'diff', 'hist': 'hist', 'patrolbtn': 'Patrol', 'logsuffix': ' log', 'patroldone': 'done', 'nsmain': 'MAIN' }; }

$( function {	mw.util.addPortletLink( 'p-tb', '/wiki/Special:BlankPage?blankspecial=ajaxrc', arc_i18n.title, 't-ajax-rc', arc_i18n.desc );	if ( mw.config.get( 'wgCanonicalSpecialPageName' ) && mw.config.get( 'wgCanonicalSpecialPageName' ).toLowerCase == 'blankpage' && mw.util.getParamValue( 'blankspecial' ) == 'ajaxrc' )	{		document.title = arc_i18n.title;		$( ajaxRcForm );	} } );

function ajaxRcForm { mw.util.addPortletLink( 'p-tb', '/wiki/Special:Log/patrol?user=' + encodeURIComponent( mw.config.get( 'wgUserName' ) ), arc_i18n.mypatrol ); // subvert this Special: page to our own needs. var bcon = document.getElementById( 'bodyContent' ) || document.getElementById( 'mw_contentholder' ); mw.util.$content.children( '.firstHeading' )[0] = arc_i18n.title; for ( var i = 0; i < bcon.childNodes.length; i++ ) { var bcur = bcon.childNodes[i]; if ( bcur.id != 'siteSub' && bcur.id != 'contentSub' && bcur.className != 'visualClear' ) { while ( bcur.firstChild ) { bcur.removeChild( bcur.firstChild ); }			if ( bcur.nodeType == 3 ) { bcur.nodeValue = ''; }		}	}

mw.util.addCSS(		'#arc-form {border:1px solid black;padding:.5em;margin:2em;} #arc-out {border:1px solid black;padding:.5em;margin:.5em;}' +		'#arc-fetch {padding:0 1em;margin:0 .5em;} .clear {clear:both;} .arc-box {border:1px solid #bbbbbb;padding:.2em;margin:.5em;}' +		'.arc-cbox {display:block;float:left;width:11em;white-space:nowrap;overflow:hidden;font-size:80%;margin:0 .f2em;}' +		'.arc-box-label {text-align:center;border-bottom:1px solid #bbbbbb;margin-bottom:.3em} .spacer {border:1px solid transparent;margin-right:.5em;}' +		'.arc-patrol {border:2px outset #bbbbbb;background-color:#bbbbbb;color:black;padding:2px;margin:3px;text-decoration:none;}'	);

var form = '' + '' + arc_i18n.startstamp + ':  ' + '' + arc_i18n.limit + ':  ' + '' + arc_i18n.showapb + ' ' +		' ' + arc_i18n.filterflag + ': ' + '' + arc_i18n.minor + ':  ' + '' + arc_i18n.bot + ':  ' + '' + arc_i18n.anon + ':  ' + '' + arc_i18n.redirect + ':  ' + '' + arc_i18n.patrolled + ': <input type="button" name="arc-f-patrolled" id="arc-f-patrolled" value="' + arc_i18n.all + '" onclick="ajaxRcFlagChange(this)" />' + ' ' +		' ' + arc_i18n.filtertype + ': ' + '<input type="checkbox" name="arc-t-edit" id="arc-t-edit" checked="checked" value="edit" /><label for="arc-t-edit">' + arc_i18n.edit + ' ' + '<input type="checkbox" name="arc-t-new" id="arc-t-new" checked="checked" value="new" /><label for="arc-t-new">' + arc_i18n.new + ' ' + '<input type="checkbox" name="arc-t-log" id="arc-t-log" checked="checked" value="log" /><label for="arc-t-log">' + arc_i18n.log + ' ' + ' ' +		' ' + arc_i18n.filterns + '  ' + '' +		'<input type="button" name="fetch" value="' + arc_i18n.fetch + '" id="arc-fetch" onclick="ajaxRcFetch" /> ' + ' ';

bcon.innerHTML += form + ' '; importScriptURI( mw.config.get( 'wgScriptPath' ) + '/api.php?action=query&meta=siteinfo&siprop=namespaces&format=json&callback=ajaxRcFormNamespacesCB&smaxage=2678400&maxage=2678400' ); }

function ajaxRcFetch( timestamp, direction ) { document.getElementById( 'arc-fetch' ).setAttribute( 'disabled', 'disabled' ); var nav = document.getElementById( 'arc-fetchnav' ); if ( nav ) { nav.style.visibility = 'hidden'; }	$( '#arc-fetch' ).after( $.createSpinner( 'arc-spin' ) );

// direction var rcdir = ''; if ( direction ) { rcdir = '&rcdir=' + direction + '&requestid=' + direction; }

// start var rcstart = timestamp || document.getElementById( 'arc-start' ).value; rcstart = rcstart.replace( /[^\d]*/g, '' ); if ( rcstart !== '' && /^\d{14}$/.test( rcstart ) ) { rcstart = '&rcstart=' + rcstart; } else { rcstart = ''; }

// limit var rclimit = parseInt( document.getElementById( 'arc-limit' ).value ); if ( isNaN( rclimit ) ) { rclimit = 100; }	rclimit = '&rclimit=' + rclimit;

// type var tb = document.getElementById( 'arc-t-boxen' ).getElementsByTagName( 'input' ); var rctype = []; for ( var i = 0; i < tb.length; i++ ) { if ( tb[i].checked ) { rctype.push( tb[i].value ); }	}	if ( rctype.length > 0 ) { rctype = '&rctype=' + rctype.join( '|' ); } else { rctype = ''; }

// show (flags) var fb = document.getElementById( 'arc-f-boxen' ).getElementsByTagName( 'input' ); var rcshow = []; for ( i = 0; i < fb.length; i++ ) { if ( fb[i].value != arc_i18n.all ) { rcshow.push( fb[i].value ); }	}	if ( rcshow.length > 0 ) { rcshow = '&rcshow=' + rcshow.join( '|' ); } else { rcshow = ''; }

// namespace var nsb = document.getElementById( 'arc-ns-boxen' ).getElementsByTagName( 'input' ); var rcnamespace = []; for ( i = 0; i < nsb.length; i++ ) { if ( nsb[i].checked ) { rcnamespace.push( nsb[i].value ); }	}	if ( rcnamespace.length > 0 ) { rcnamespace = '&rcnamespace=' + rcnamespace.join( '|' ); } else { rcnamespace = ''; }

// prop & token var rcprop = '&rcprop=user|comment|flags|timestamp|title|ids|sizes|redirect|patrolled|loginfo'; var rctoken = '&rctoken=patrol';

$.ajax( {		url: mw.util.wikiScript( 'api' ) + '?action=query&format=json&rawcontinue=&list=recentchanges' + rcdir + rcstart + rclimit + rctype + rcshow + rcnamespace + rcprop + rctoken	} ).done( function( data ) {		eval( "ajaxRcFetchHandler(" + data + ",'" + data.replace( /\'/g, "`" ) + "')" );	} ); }

function ajaxRcFetchHandler( obj, txt ) { document.getElementById( 'arc-fetch' ).removeAttribute( 'disabled' ); $.removeSpinner( 'arc-spin' ); var out = document.getElementById( 'arc-out' ); var ajaxpatrol = document.getElementById( 'arc-patrol-enable' ).checked; while ( out.firstChild ) { out.removeChild( out.firstChild ); }	if ( obj.error ) { out.appendChild( document.createTextNode( 'API error: ' + obj.error.code + ' - ' + obj.error.info + '\n' ) ); return; }	if ( !obj.query || !obj.query.recentchanges ) { out.appendChild( document.createTextNode( 'Unexpected response: ' + txt + '\n' ) ); return; }	var rc = obj.query.recentchanges; if ( rc.length === 0 ) { out.appendChild( document.createTextNode( arc_i18n.noresults ) ); return; }

var backwards = false; if ( obj.requestid && obj.requestid == 'newer' ) { backwards = true; }

var nav = document.createElement( 'div' ); nav.setAttribute( 'id', 'arc-fetchnav' ); if (		obj['query-continue'] &&		obj['query-continue'].recentchanges &&		obj['query-continue'].recentchanges.rcstart	) {		var rcstart = obj['query-continue'].recentchanges.rcstart; var rcstartnewer = rcstart; var rcstartolder = rcstart; if ( !backwards ) { rcstartnewer = rc[0].timestamp; } else { rcstartolder = rc[0].timestamp; }		addLinkChild( nav, 'javascript:ajaxRcFetch("' + rcstartnewer + '","newer")', 'Newer' ); addText( nav, ' | '); addLinkChild( nav, 'javascript:ajaxRcFetch("' + rcstartolder + '","older")', 'Older' ); } else if ( backwards ) { addLinkChild( nav, 'javascript:ajaxRcFetch', 'Older' ); }	out.appendChild( nav );

var ul = document.createElement( 'ul' ); for ( var i = 0; i < rc.length; i++ ) { var r = rc[i]; var li = document.createElement( 'li' ); var rcid = ''; if ( r.type == 'edit' ) { if ( typeof r.patrolled == 'undefined' && r.rcid && r.patroltoken ) { rcid = '&rcid=' + r.rcid; }			addText( li, '(' );			addLinkChild( li, mw.config.get( 'wgScript' ) + '?oldid=' + r.old_revid + '&diff=' + r.revid + rcid, arc_i18n.diff );			addText( li, ') (' );			addLinkChild( li, mw.config.get( 'wgScript' ) + '?curid=' + r.pageid + '&action=history', arc_i18n.hist );			addText( li, ') . . ' ); if ( typeof r.bot != 'undefined' ) { addText( li, 'b', 'span', 'bot' ); }			if ( typeof r.minor != 'undefined' ) { addText( li, 'm', 'span', 'minor' ); }			if ( rcid !== '' && r.patroltoken ) { addText( li, '!', 'span', 'unpatrolled' ); }			addText( li, ' ' ); addLinkChild( li, mw.config.get( 'wgScript' ) + '?curid=' + r.pageid, r.title ); var size = '' + ( parseInt( r.newlen ) - parseInt( r.oldlen ) ); if ( size.substring( 0, 1 ) != '-' ) { size = '+' + size; }			addText( li, '; ' + r.timestamp.replace( /[TZ]/ig, ' ' ) + ' . . (' + size + ') . . ' ); addLinkChild( li, mw.config.get( 'wgScript' ) + '?title=Special:Contributions&target=' + encodeURIComponent( r.user ), r.user ); if ( r.comment ) { addText( li, ' (' + r.comment + ')', 'i' ); }			if ( ajaxpatrol === true && rcid !== '' && r.patroltoken ) { addLinkChild( li, 'javascript:ajaxRcDoPatrol("' + r.rcid + '","' + encodeURIComponent( encodeURIComponent( r.patroltoken ) ) + '")', arc_i18n.patrolbtn, 'arc-patrol-' + r.rcid, 'arc-patrol' ); }		} else if ( r.type == 'new' ) { if ( typeof r.patrolled == 'undefined' && r.rcid ) { rcid = '&rcid=' + r.rcid; }			addText( li, '(' + arc_i18n.diff + ') (' );			addLinkChild( li, mw.config.get( 'wgScript' ) + '?curid=' + r.pageid + '&action=history', arc_i18n.hist );			addText( li, ') . . ' ); addText( li, 'N', 'span', 'newpage' ); if ( rcid !== '' && r.patroltoken ) { addText( li, '!', 'span', 'unpatrolled' ); }			addText( li, ' ' ); addLinkChild( li, mw.config.get( 'wgScript' ) + '?curid=' + r.pageid + rcid, r.title ); addText( li, '; ' + r.timestamp.replace( /[TZ]/ig, ' ' ) + ' . . (+' + r.newlen + ') . . ' ); addLinkChild( li, mw.config.get( 'wgScript' ) + '?title=Special:Contributions&target=' + encodeURIComponent( r.user ), r.user ); if ( r.comment ) { addText( li, ' (' + r.comment + ')', 'i' ); }			if ( ajaxpatrol === true && rcid !== '' && r.patroltoken ) { addLinkChild( li, 'javascript:ajaxRcDoPatrol("' + r.rcid + '","' + encodeURIComponent( encodeURIComponent( r.patroltoken ) ) + '")', arc_i18n.patrolbtn, 'arc-patrol-' + r.rcid, 'arc-patrol' ); }		} else if ( r.type == 'log' ) { addText( li, '(' );			addLinkChild( li, mw.config.get( 'wgScript' ) + '?title=Special:Log&type=' + r.logtype, r.logtype + arc_i18n, arc_i18n.logsuffix );			addText( li, '); ' + r.timestamp.replace( /[TZ]/ig, ' ' ) + ' . . ' ); addLinkChild( li, mw.config.get( 'wgScript' ) + '?title=Special:Contributions&target=' + encodeURIComponent( r.user ), r.user ); addText( li,' ' + r.logaction + ' ' ); addLinkChild( li, mw.config.get( 'wgScript' ) + '?title=' + encodeURIComponent( r.title ), r.title ); if ( r.comment ) { addText( li, ' (' + r.comment + ')', 'i' ); }		}		if ( backwards && ul.firstChild ) { ul.insertBefore( li, ul.firstChild ); } else { ul.appendChild( li ); }	}	out.appendChild( ul ); }

function ajaxRcDoPatrol( rcid, token ) { $.ajax( {		method: 'POST',		url: mw.util.wikiScript( 'api' ),		data: {			action: 'patrol',			format: 'json',			requestid: rcid,			token: token		}	} ).done( function( data ) {		eval( 'ajaxRcDidPatrol(' + data + ')' );	} ); }

function ajaxRcDidPatrol( obj ) { if ( !obj.requestid ) { return; }	if ( obj.error ) { alert( 'API error in patrolling rcid=' + obj.requestid + ' : ' + obj.error.code + '\n' + obj.error.info ); return; }	var button = document.getElementById( 'arc-patrol-' + obj.requestid ); if ( !button || !obj.patrol ) { return; }	button.setAttribute( 'href', 'javascript:alert("(' + arc_i18n.patroldone + ')");' ); addText( button, ' (' + arc_i18n.patroldone + ')' ); }

function ajaxRcFlagChange( obj ) { var type = obj.getAttribute( 'id' ).substring( 6 ); var val = obj.value; if ( val == type ) { obj.value = '!' + type; } else if ( val == '!' + type ) { obj.value = arc_i18n.all; } else { obj.value = type; } }

function ajaxRcFormNamespacesCB( obj ) { if ( !obj.query || !obj.query.namespaces ) { return; }	var ns = obj.query.namespaces; var nsb = document.getElementById( 'arc-ns-boxen' ); for ( var i in ns ) { if ( typeof i != 'string' || ns[i].id < 0 ) { continue; }		var title = ns[i]['*']; if ( ns[i].id === '' ) { title = arc_i18n.nsmain; }		var canon = ns[i].canonical || ''; addCheckboxChild( nsb, 'arc-ns-' + ns[i].id, i, false, 'arc-ns-' + ns[i].id, title, 'arc-cbox', ns[i].id + ' => ' + canon ); //nsb.appendChild( document.createElement( 'br' ) ); }	var div = nsb.appendChild( document.createElement( 'div' ) ); div.setAttribute( 'class', 'clear' ); }

function addText( obj, txt, elem, classes ) { if ( elem ) { var e = document.createElement( elem ); e.appendChild( document.createTextNode( txt ) ); if ( classes ) { e.setAttribute( 'class', classes ); }		obj.appendChild( e ); return e;	} else { obj.appendChild( document.createTextNode( txt ) ); } }

function addLinkChild( obj, href, text, id, classes, title ) { if ( !obj || !href || !text ) { return false; }	var a = document.createElement( 'a' ); a.setAttribute( 'href', href ); a.appendChild( document.createTextNode( text ) ); if ( id ) { a.setAttribute( 'id', id ); }	if ( classes ) { a.setAttribute( 'class', classes ); }	if ( title ) { a.setAttribute( 'title', title ); }	obj.appendChild( a ); return a; }

function addCheckboxChild( obj, name, value, checked, id, label, classes, title ) { if ( !obj || !name ) { return false; }	var span = document.createElement( 'span' ); var c = document.createElement( 'input' ); c.setAttribute( 'name', name ); c.setAttribute( 'type', 'checkbox' ); if ( value ) { c.setAttribute( 'value', value ); }	if ( checked ) { c.setAttribute( 'checked', 'checked' ); }	if ( title ) { c.setAttribute( 'title', title ); }	span.appendChild( c ); if ( id ) { c.setAttribute( 'id', id ); if ( label ) { var l = document.createElement( 'label' ); l.setAttribute( 'for', id ); l.appendChild( document.createTextNode( label ) ); if ( title ) { l.setAttribute( 'title', title ); }			span.appendChild( l ); }	}	if ( classes ) { span.setAttribute( 'class', classes ); }	obj.appendChild( span ); return span; }