text:
User:Flewis/twinklefluff.js

if (twinkleConfigExists) {

/** Twinklefluff revert and antivandalism utillity */ // If TwinkleConfig aint exist. if( typeof( TwinkleConfig ) == 'undefined' ) { TwinkleConfig = {}; }

/** TwinkleConfig.revertMaxRevisions (int) defines how many revision to query maximum, maximum possible is 50, default is 50 */ if( typeof( TwinkleConfig.revertMaxRevisions ) == 'undefined' ) { TwinkleConfig.revertMaxRevisions = 50; }

/** TwinkleConfig.userTalkPageMode may take arguments: 'window': open a new window, remmenber the opened window 'tab': opens in a new tab, if possible. 'blank': force open in a new window, even if a such window exist */ if( typeof( TwinkleConfig.userTalkPageMode ) == 'undefined' ) { TwinkleConfig.userTalkPageMode = 'window'; }

/** TwinkleConfig.openTalkPage (array) What types of actions that should result in opening of talk page */ if( typeof( TwinkleConfig.openTalkPage ) == 'undefined' ) { TwinkleConfig.openTalkPage = [ 'agf', 'norm', 'vand' ]; }

/** TwinkleConfig.openTalkPageOnAutoRevert (bool) Defines if talk page should be opened when canling revert from contrib page, this because from there, actions may be multiple, and opening talk page not suitable. If set to true, openTalkPage defines then if talk page will be opened. */ if( typeof( TwinkleConfig.openTalkPageOnAutoRevert ) == 'undefined' ) { TwinkleConfig.openTalkPageOnAutoRevert = false; }

/** TwinkleConfig.openAOLAnonTalkPage may take arguments: true: to open Anon AOL talk pages on revert false: to not open them */ if( typeof( TwinkleConfig.openAOLAnonTalkPage ) == 'undefined' ) { TwinkleConfig.openAOLAnonTalkPage = false; }

/** TwinkleConfig.summaryAd (string) If ad should be added or not to summary, default TWINKLE */ if( typeof( TwinkleConfig.summaryAd ) == 'undefined' ) { TwinkleConfig.summaryAd = " (TW)"; }

/** TwinkleConfig.markRevertedPagesAsMinor (array) What types of actions that should result in marking edit as minor */ if( typeof( TwinkleConfig.markRevertedPagesAsMinor ) == 'undefined' ) { TwinkleConfig.markRevertedPagesAsMinor = [ 'agf', 'norm', 'vand', 'torev' ]; }

/** TwinkleConfig.watchRevertedPages (array) What types of actions that should result in forced addition to watchlist */ if( typeof( TwinkleConfig.watchRevertedPages ) == 'undefined' ) { TwinkleConfig.watchRevertedPages = [ 'agf', 'norm', 'vand', 'torev' ]; }

/** TwinkleConfig.offerReasonOnNormalRevert (boolean) If to offer a promt for extra summary reason for normal reverts, default to true */ if( typeof( TwinkleConfig.offerReasonOnNormalRevert ) == 'undefined' ) { TwinkleConfig.offerReasonOnNormalRevert = true; }

// a list of usernames, usually only bots, that vandalism revert is jumped over, that is // if vandalism revert is choosen on such username, then it's target in on the revision before. // This is for handeling quick bots that makes edits seconds after the original edit is made. // This only affect vandalism rollback, for good faith rollback, it will stop, indicating a bot // has no faith, and for normal rollback, it will rollback that edit. var WHITELIST = [ 'HagermanBot', 'SineBot', 'HBC AIV helperbot', 'HBC AIV helperbot2', 'HBC AIV helperbot3', ]

twinklefluff = { auto: function { if( QueryString.get( 'oldid' ) != wgCurRevisionId ) { // not latest revision return; }

var ntitle = getElementsByClassName( document.getElementById('bodyContent'), 'td', 'diff-ntitle' )[0]; if( ntitle.getElementsByTagName('a')[0].firstChild.nodeValue != 'Current revision' ) { // not latest revision return; }

vandal = ntitle.getElementsByTagName('a')[3].firstChild.nodeValue.replace("'", "\\'");

if( !TwinkleConfig.openTalkPageOnAutoRevert ) { TwinkleConfig.openTalkPage = []; }

return twinklefluff.revert( QueryString.get( 'twinklerevert' ), vandal ); },	normal: function {

var spanTag = function( color, content ) { var span = document.createElement( 'span' ); span.style.color = color; span.appendChild( document.createTextNode( content ) ); return span; }

if( wgNamespaceNumber == -1 && wgCanonicalSpecialPageName == "Contributions" ) { var list = document.evaluate( '//div[@id="bodyContent"]//ul/li[contains(strong, "(top)")]', document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null ); var vandal = document.evaluate( '//div[@id="contentSub"]/a[1]/@title', document, null, XPathResult.STRING_TYPE, null ).stringValue.replace(/^User( talk)?:/, '').replace("'", "\\'");

var revNode = document.createElement('strong'); var revLink = document.createElement('a'); revLink.appendChild( spanTag( 'Black', ' [' ) ); revLink.appendChild( spanTag( 'SteelBlue', 'rollback' ) ); revLink.appendChild( spanTag( 'Black', ']' ) ); revNode.appendChild(revLink);

var revVandNode = document.createElement('strong'); var revVandLink = document.createElement('a'); revVandLink.appendChild( spanTag( 'Black', ' [' ) ); revVandLink.appendChild( spanTag( 'Red', 'vandalism' ) ); revVandLink.appendChild( spanTag( 'Black', ']' ) ); revVandNode.appendChild(revVandLink);

for(var i = 0; i < list.snapshotLength; ++i ) { var current = list.snapshotItem(i);

var href = document.evaluate( 'a[2]/@href', current, null, XPathResult.STRING_TYPE, null ).stringValue; var tmpNode = revNode.cloneNode( true ); tmpNode.firstChild.setAttribute( 'href', href + '&' + QueryString.create( { 'twinklerevert': 'norm' } ) ); current.appendChild( tmpNode ); var tmpNode = revVandNode.cloneNode( true ); tmpNode.firstChild.setAttribute( 'href', href + '&' + QueryString.create( { 'twinklerevert': 'vand' } ) ); current.appendChild( tmpNode ); }

} else { if( wgCanonicalSpecialPageName == "Special:Undelete" ) { //You can't rollback deleted pages! return; }

var body = document.getElementById('bodyContent');

var firstRev = document.evaluate( 'boolean(/div[@class="firstrevisionheader"])', body, null, XPathResult.BOOLEAN_TYPE, null ).booleanValue; if( firstRev ) { // we have first revision here, nothing to do. return; }

try { var otitle = document.getElementById('mw-diff-otitle1').parentNode; var ntitle = document.getElementById('mw-diff-ntitle1').parentNode; } catch( e ) { // no old, nor new title, nothing to do really, return; return; }

var old_rev_url = document.evaluate( '//strong/a/@href', document.getElementById('mw-diff-ntitle2'), null, XPathResult.STRING_TYPE, null ).stringValue;

// Lets first add a [edit this revision] link var query = new QueryString( decodeURI( old_rev_url.split( '?', 2 )[1] ) );

var oldrev = query.get( 'oldid' );

var revertToRevision = document.createElement('div'); revertToRevision.setAttribute( 'id', 'tw-revert-to-revision' ); revertToRevision.style.fontWeight = 'bold';

var revertToRevisionLink = revertToRevision.appendChild( document.createElement('a') ); revertToRevisionLink.href = "javascript:twinklefluff.revertToRevision('" + oldrev + "')"; revertToRevisionLink.appendChild( spanTag( 'Black', '[' ) ); revertToRevisionLink.appendChild( spanTag( 'SaddleBrown', 'restore this version' ) ); revertToRevisionLink.appendChild( spanTag( 'Black', ']' ) );

otitle.insertBefore( revertToRevision, otitle.firstChild );

if( document.getElementById('differences-nextlink') ) { // Not latest revision curVersion = false; return; }			vandal = document.evaluate( 'a', document.getElementById('mw-diff-ntitle2'), null, XPathResult.STRING_TYPE, null ).stringValue;

var revertNode = document.createElement('div'); revertToRevision.setAttribute( 'id', 'tw-revert' );

var agfNode = document.createElement('strong'); var vandNode = document.createElement('strong'); var normNode = document.createElement('strong');

var agfLink = document.createElement('a'); var vandLink = document.createElement('a'); var normLink = document.createElement('a');

agfLink.href = "javascript:twinklefluff.revert('agf', '" + vandal + "')"; vandLink.href = "javascript:twinklefluff.revert('vand', '" + vandal + "')"; normLink.href = "javascript:twinklefluff.revert('norm', '" + vandal + "')";

agfLink.appendChild( spanTag( 'Black', '[' ) ); agfLink.appendChild( spanTag( 'DarkOliveGreen', 'rollback (AGF)' ) ); agfLink.appendChild( spanTag( 'Black', ']' ) );

vandLink.appendChild( spanTag( 'Black', '[' ) ); vandLink.appendChild( spanTag( 'Red', 'rollback (VANDAL)' ) ); vandLink.appendChild( spanTag( 'Black', ']' ) );

normLink.appendChild( spanTag( 'Black', '[' ) ); normLink.appendChild( spanTag( 'SteelBlue', 'rollback' ) ); normLink.appendChild( spanTag( 'Black', ']' ) );

agfNode.appendChild(agfLink); vandNode.appendChild(vandLink); normNode.appendChild(normLink);

revertNode.appendChild( agfNode ); revertNode.appendChild( document.createTextNode(' || ') ); revertNode.appendChild( normNode ); revertNode.appendChild( document.createTextNode(' || ') ); revertNode.appendChild( vandNode );

ntitle.insertBefore( revertNode, ntitle.firstChild ); }	} }

twinklefluff.revert = function revertPage( type, vandal, rev, page ) {

wgPageName = page || wgPageName; wgCurRevisionId = rev || wgCurRevisionId;

Status.init( document.getElementById('bodyContent') ); var params = { type: type, user: vandal }	var query = { 'action': 'query', 'prop': 'revisions', 'titles': wgPageName, 'rvlimit': 50, // max possible 'rvprop': [ 'ids', 'timestamp', 'user', 'comment' ] }	var wikipedia_api = new Wikipedia.api( 'Grabbing data of earlier revisions', query, twinklefluff.callbacks.main ); wikipedia_api.params = params; wikipedia_api.post; }

twinklefluff.revertToRevision = function revertToRevision( oldrev ) {

Status.init( document.getElementById('bodyContent') );

var query = { 'action': 'query', 'prop': 'revisions', 'titles': wgPageName, 'rvlimit': 1, 'rvstartid': oldrev, 'rvprop': [ 'ids', 'timestamp', 'user', 'comment', 'content' ], 'format': 'xml' }

var wikipedia_api = new Wikipedia.api( 'Grabbing data of the earlier revision', query, twinklefluff.callbacks.toRevision.main ); wikipedia_api.params = { rev: oldrev }; wikipedia_api.post; }

twinklefluff.callbacks = { toRevision: { main: function( self ) { var xmlDoc = self.responseXML; self.params.revision = xmlDoc.evaluate('//rev', xmlDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue; var query = { 'title': wgPageName, 'action': 'submit' };			var wikipedia_wiki = new Wikipedia.wiki( 'Reverting page', query, twinklefluff.callbacks.toRevision.reverting ); wikipedia_wiki.params = self.params; wikipedia_wiki.get;

},		reverting: function( self ) { var form = self.responseXML.getElementById( 'editform' ); var text = self.params.revision.textContent;

if( !form ) { self.statelem.error( 'couldn\'t grab element "editform", aborting, this could indicate failed respons from the server' ); return; }

var optional_summary = prompt( "Please, if possible, specify a reason for the revert" ); var summary = sprintf( "Reverted to revision %d by %2$s%s.%s", 				self.params.revision.getAttribute( 'revid' ),				self.params.revision.getAttribute( 'user' ),				optional_summary ? "; " + optional_summary : '',				TwinkleConfig.summaryAd			); var postData = { 'wpMinoredit': TwinkleConfig.markRevertedPagesAsMinor.indexOf( 'torev' ) != -1 ? '' : undefined, 'wpWatchthis': TwinkleConfig.watchRevertedPages.indexOf( 'torev' ) != -1 ? '' : form.wpWatchthis.checked ? '' : undefined, 'wpStarttime': form.wpStarttime.value, 'wpEdittime': form.wpEdittime.value, 'wpAutoSummary': form.wpAutoSummary.value, 'wpEditToken': form.wpEditToken.value, 'wpSummary': summary, 'wpTextbox1': text };			Wikipedia.actionCompleted.redirect = wgPageName; Wikipedia.actionCompleted.notice = "Reversion completed"

self.post( postData ); }	},	main: function( self ) {

var xmlDoc = self.responseXML; var revs = xmlDoc.evaluate( '//rev', xmlDoc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );

if( revs.snapshotLength < 1 ) { self.statitem.error( 'We have less than one additional revision, thus impossible to revert' ); return; }		var top = revs.snapshotItem(0); if( top.getAttribute( 'revid' ) < wgCurRevisionId ) { Status.error( 'Error', [ 'The received top revision id ', htmlNode( 'strong', top.getAttribute('revid') ), ' is less than our current revision id, this could indicate that the current revision has been deleted, the server is lagging, or that bad data has been received. Will stop proceeding at this point.' ] ); return; }		var index = 1; if( wgCurRevisionId != top.getAttribute('revid') ) { Status.warn( 'Warning', [ 'Latest revision ', htmlNode( 'strong', top.getAttribute('revid') ), ' doesn\'t equals our revision ', htmlNode( 'strong', wgCurRevisionId) ] ); if( top.getAttribute( 'user' ) == self.params.user ) { switch( self.params.type ) { case 'vand': Status.info( 'Info', [ 'Latest revision is made by ', htmlNode( 'strong', self.params.user ), ', as we assume vandalism, we continue to revert' ]); break; case 'agf': Status.warn( 'Warning', [ 'Latest revision is made by ', htmlNode( 'strong', self.params.user ), ', as we assume good faith, we stop reverting, as the problem might have been fixed.' ]); return; default: Status.warn( 'Notice', [ 'Latest revision is made by ', htmlNode( 'strong', self.params.user ), ', but we will stop reverting anyway.' ] ); return; }			}			else if( 				self.params.type == 'vand' && 				WHITELIST.indexOf( top.getAttribute( 'user' ) ) != -1 && revs.snapshotLength > 1 &&				revs.snapshotItem(1).getAttribute( 'pageId' ) == wgCurRevisionId 			) { Status.info( 'Info', [ 'Latest revision is made by ', htmlNode( 'strong', top.getAttribute( 'user' ) ), ', a trusted bot, and the revision before was made by our vandal, so we proceed with the revert.' ] ); index = 2; } else { Status.error( 'Error', [ 'Latest revision is made by ', htmlNode( 'strong', top.getAttribute( 'user' ) ), ', so it might already been reverted, stopping reverting.'] ); return; }

}

if( WHITELIST.indexOf( self.params.user ) != -1 ) { switch( self.params.type ) { case 'vand': Status.info( 'Info', [ 'Vandalism revert is choosen on ', htmlNode( 'strong', self.params.user ), ', as this is a whitelisted bot, we assume you wanted to revert vandalism made by the previous user instead.' ] ); index = 2; vandal = revs.snapshotItem(1).getAttribute( 'user' );

break; case 'agf': Status.warn( 'Notice', [ 'Good faith revert is choosen on ', htmlNode( 'strong', self.params.user ), ', as this is a whitelisted bot, it makes no sense at all to revert it as a good faith edit, will stop reverting.' ] ); return;

break; case 'norm': default: var cont = confirm( 'Normal revert is choosen, but the top user (' + self.params.user + ') is a whitelisted bot, do you want to revert the revision before instead?' ); if( cont ) { Status.info( 'Info', [ 'Normal revert is choosen on ', htmlNode( 'strong', self.params.user ), ', as this is a whitelisted bot, and per confirm, we\'ll revert the previous revision instead.' ] ); index = 2; self.params.user = revs.snapshotItem(1).getAttribute( 'user' ); } else { Status.warn( 'Notice', [ 'Normal revert is choosen on ', htmlNode( 'strong', self.params.user ), ', this is a whitelisted bot, but per confirmation, revert on top revision will proceed.' ] ); }				break; }		}		var found = false; var count = 0;

for( var i = index; i < revs.snapshotLength; ++i ) { ++count; if( revs.snapshotItem(i).getAttribute( 'user' ) != self.params.user ) { found = i;				break; }		}

if( ! found ) { self.statelem.error( [ 'No previous revision found, perhaps ', htmlNode( 'strong', self.params.user ), ' is the only contributor, or that the user has made more than ' + TwinkleConfig.revertMaxRevisions + ' edits in a row.' ] ); return;

}

if( count == 0 ) { Status.error( 'Error', "We where to revert zero revisions. As that makes no sense, we'll stop reverting this time. It could be that the edit already have been reverted, but the revision id was still the same." ); return; }

var good_revision = revs.snapshotItem( found );

if( 			self.params.type != 'vand' && 			count > 1 && 			!confirm( self.params.user + ' has done ' + count + ' edits in a row. Are you sure you want to revert them all?' ) 		) { Status.info( 'Notice', 'Stopping reverting per user input' ); return; }

self.params.count = count;

self.params.goodid = good_revision.getAttribute( 'revid' ); self.params.gooduser = good_revision.getAttribute( 'user' );

self.statelem.status( [ ' revision ', htmlNode( 'strong', good_revision.getAttribute( 'revid' ) ), ' that was made ', htmlNode( 'strong', count ), ' revisions ago by ', htmlNode( 'strong', good_revision.getAttribute( 'user' ) ) ] );

var query = { 'action': 'query', 'prop': 'revisions', 'titles': wgPageName, 'rvlimit': 1, 'rvprop': 'content', 'rvstartid': good_revision.getAttribute( 'revid' ) }

var wikipedia_api = new Wikipedia.api( [ 'Getting content for revision ', htmlNode( 'strong', good_revision.getAttribute( 'revid' ) ) ], query, twinklefluff.callbacks.grabbing ); wikipedia_api.params = self.params; wikipedia_api.post; },	grabbing: function( self ) {

xmlDoc = self.responseXML;

self.params.content = xmlDoc.evaluate( '//rev[1]', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;

var query = { 'title': wgPageName, 'action': 'submit' };		var wikipedia_wiki = new Wikipedia.wiki( 'Reverting page', query, twinklefluff.callbacks.reverting ); wikipedia_wiki.params = self.params; wikipedia_wiki.get; },	reverting: function( self ) { var doc = self.responseXML;

var form = doc.getElementById( 'editform' ); if( !form ) { self.statelem.error( 'couldn\'t grab element "editform", aborting, this could indicate failed respons from the server' ); return; }

var text = self.params.content; if( !text ) { self.statelem.error( 'we received no revision, something is wrong, bailing out!' ); return; }

var summary;

switch( self.params.type ) { case 'agf': var extra_summary = prompt( "An optional comment for the edit summary:" ); summary = sprintf( "Reverted good faith edits by %1$s%s.%s", 				self.params.user.replace("\\'", "'"), 				extra_summary ? "; " + extra_summary.toUpperCaseFirstChar : '',				TwinkleConfig.summaryAd			); break; case 'vand': summary = sprintf( "Reverted %d %s by %3$s identified as vandalism to last revision by %4$s.%s", 				self.params.count, 				self.params.count > 1 ? 'edits': 'edit',				self.params.user.replace("\\'", "'"),				self.params.gooduser.replace("\\'", "'"),				TwinkleConfig.summaryAd			); break; case 'norm': if( TwinkleConfig.offerReasonOnNormalRevert ) { var extra_summary = prompt( "An optional comment for the edit summary:" ); }			summary = sprintf( "Reverted %d %s by %3$s%s.%s", 				self.params.count, 				self.params.count > 1 ? 'edits': 'edit',				self.params.user.replace("\\'", "'"),				extra_summary ? "; " + extra_summary.toUpperCaseFirstChar : '',				TwinkleConfig.summaryAd 			); }

Status.info( 'Info', [ 'Open user talk page edit form for user ', htmlNode( 'strong', self.params.user ) ] );

if( TwinkleConfig.openTalkPage.indexOf( self.params.type ) != -1 ) {

var query = { 'title': 'User talk:' + self.params.user, 'action': 'edit', 'vanarticle': wgPageName.replace(/_/g, ' '), 'vanarticlerevid': wgCurRevisionId, 'vanarticlegoodrevid': self.params.goodid, 'type': self.params.type, 'count': self.params.count }

switch( TwinkleConfig.userTalkPageMode ) { case 'tab': window.open( mw.config.get('wgServer') + mw.config.get('wgScriptPath') + '/index.php?' + QueryString.create( query ), '_tab' ); break; case 'blank': window.open( mw.config.get('wgServer') + mw.config.get('wgScriptPath') + '/index.php?' + QueryString.create( query ), '_blank', 'location=no,toolbar=no,status=no,directories=no,scrollbars=yes,width=1200,height=800' ); break; case 'window': default: window.open( mw.config.get('wgServer') + mw.config.get('wgScriptPath') + '/index.php?' + QueryString.create( query ), 'twinklewarnwindow', 'location=no,toolbar=no,status=no,directories=no,scrollbars=yes,width=1200,height=800' ); break; }		}

var postData = { 'wpMinoredit': TwinkleConfig.markRevertedPagesAsMinor.indexOf( self.params.type ) != -1 ? '' : undefined, 'wpWatchthis': TwinkleConfig.watchRevertedPages.indexOf( self.params.type ) != -1 ? '' : form.wpWatchthis.checked ? '' : undefined, 'wpStarttime': form.wpStarttime.value, 'wpEdittime': form.wpEdittime.value, 'wpAutoSummary': form.wpAutoSummary.value, 'wpEditToken': form.wpEditToken.value, 'wpSummary': summary, 'wpTextbox1': text };

Wikipedia.actionCompleted.redirect = wgPageName; Wikipedia.actionCompleted.notice = "Reversion completed"

self.post( postData ); } } }

$( function {		if( QueryString.exists( 'twinklerevert' ) ) {			twinklefluff.auto;		} else {			twinklefluff.normal;		}	} );

title: User:Flewis/twinklefluff.js


text:
User:Flewis/twinklewarn.js

// If TwinkleConfig aint exist. if( typeof( TwinkleConfig ) == 'undefined' ) { TwinkleConfig = {}; }

/** TwinkleConfig.showSharedIPNotice may take arguments: true: to show shared ip notice if an IP address false: to not print the notice */ if( typeof( TwinkleConfig.showSharedIPNotice ) == 'undefined' ) { TwinkleConfig.showSharedIPNotice = true; }

/** TwinkleConfig.watchWarnings (boolean) if true, watch the page which has been dispatched an warning or notice, if false, default applies */ if( typeof( TwinkleConfig.watchWarnings ) == 'undefined' ) { TwinkleConfig.watchWarnings = true; }

/** TwinkleConfig.summaryAd (string) If ad should be added or not to summary, default TWINKLE */ if( typeof( TwinkleConfig.summaryAd ) == 'undefined' ) { TwinkleConfig.summaryAd = " using TW"; }

function twinklewarn { if( wgNamespaceNumber == 3 ) { mw.util.addPortletLink( 'p-cactions', (twinkleConfigExists ? "javascript:twinklewarn.callback" : 'javascript:alert("Your account is too new to use Twinkle.");'), "warn", "tw-warn", "Warn/Notify user", ""); } } $(twinklewarn);

twinklewarn.callback = function twinklewarnCallback { var Window = new SimpleWindow( 600, 400 ); Window.setTitle( "Warn/Notify user" ); var form = new QuickForm( twinklewarn.callback.evaluate );

var main_select = form.append( {			type:'field',			label:'Choose type of warning/notice to issue', 			tooltip:'Choose first the main group you want to issue, then choose an appropriate type to issue.'		} );

var main_group = main_select.append( {			type:'select',			name:'main_group',			event:twinklewarn.callback.change_category 		} );

main_group.append( { type:'option', label:'General Note (1)', value:'level1', selected:true } ); main_group.append( { type:'option', label:'Caution (2)', value:'level2' } ); main_group.append( { type:'option', label:'Warning (3)', value:'level3' } ); main_group.append( { type:'option', label:'Final warning (4)', value:'level4' } ); main_group.append( { type:'option', label:'Only warning (4im)', value:'level4im' } ); main_group.append( { type:'option', label:'Single issue notices', value:'singlenotice' } ); main_group.append( { type:'option', label:'Single issue warnings', value:'singlewarn' } ); if( userIsInGroup( 'sysop' ) ) { main_group.append( { type:'option', label:'Blocking', value:'block' } ); }

main_select.append( { type:'select', name:'sub_group' } ); //Will be empty to begin with.

form.append( { 			type:'input',			name:'article',			label:'Linked article',			value:( QueryString.exists( 'vanarticle' ) ? QueryString.get( 'vanarticle' ) : '' ),			tooltip:'An article might be linked to the notice, either it was a revert to said article that dispatched this notice. Leave empty for no article to be linked'		} );

var more = form.append( { type:'field', label:'Fill in an optional reason and hit \"Submit\"' } ); more.append( { type:'textarea', label:'More:', name:'reason', tooltip:'Perhaps a reason, or that a more detailed notice must be appended' } ); more.append( { type:'submit', label:'Submit' } ); var result = form.render; Window.setContent( result ); Window.display; result.main_group.root = result;

// We must init the first choice (General Note); var evt = document.createEvent( "Event" ); evt.initEvent( 'change', true, true ); result.main_group.dispatchEvent( evt );

}

// This is all the messages that might be dispatched by the code twinklewarn.messages = { level1: { "uw-vandalism1": { label:"Vandalism", summary:"General note: Vandalism" },		"uw-test1": { label:"Editing tests", summary:"General note: Editing tests" },		"uw-delete1": { label:"Page blanking, removal of content or templates", summary:"General note: Page blanking, removal of content or templates" },		"uw-joke1": { label:"Using improper humor", summary:"General note: Using improper humor" },		"uw-create1": { label:"Creating inappropriate pages", summary:"General note: Creating inappropriate pages" },		"uw-upload1": { label:"Uploading inappropriate images", summary:"General note: Uploading inappropriate images" },		"uw-image1": { label:"Image-related vandalism", summary:"General note: Image-related vandalism" },		"uw-spam1": { label:"Adding spam links", summary:"General note: Adding spam links" },		"uw-advert1": { label:"Using Wikipedia for advertising or promotion", summary:"General note: Using Wikipedia for advertising or promotion" },		"uw-npov1": { label:"Not adhering to neutral point of view", summary:"General note: Not adhering to neutral point of view" },		"uw-unsourced1": { label:"Addition of unsourced material without proper citations", summary:"General note: Addition of unsourced material without proper citations" },		"uw-error1": { label:"Introducing deliberate factual errors", summary:"General note: Introducing deliberate factual errors" },		"uw-biog1": { label:"Adding unreferenced controversial information about living persons", summary:"General note: Adding unreferenced controversial information about living persons" },		"uw-defam1": { label:"Defamation not specifically directed", summary:"General note: Defamation not specifically directed" },		"uw-uncen1": { label:"Censorship of material", summary:"General note: Censorship of material" },		"uw-mos1": { label:"Manual of style", summary:"General note: Formatting, date, language, etc (Manual of style)" },		"uw-move1": { label:"Page moves", summary:"General note: Page moves" },		"uw-chat1": { label:"Using talk page as forum", summary:"General note: Using talk page as forum" },		"uw-tpv1": { label:"Refactoring others' talk page comments", summary:"General note: Refactoring others' talk page comments" },		"uw-afd1": { label:"Removing \{\{afd\}\} templates", summary:"General note: Removing \{\{afd\}\} templates" },		"uw-speedy1": { label:"Removing \{\{speedy deletion\}\} templates", summary:"General note: Removing \{\{speedy deletion\}\} templates" },		"uw-npa1": { label:"Personal attack directed at a specific editor", summary:"General note: Personal attack directed at a specific editor" },		"uw-agf1": { label:"Not assuming good faith", summary:"General note: Not assuming good faith" },		"uw-own1": { label:"Ownership of articles", summary:"General note: Ownership of articles" },		"uw-nor1": { label:"Adding original research", summary:"General note: Adding original research, including unpublished syntheses of sourced material" }	},	level2: { "uw-vandalism2": { label:"Vandalism", summary:"Caution: Vandalism" },		"uw-test2": { label:"Editing tests", summary:"Caution: Editing tests" },		"uw-delete2": { label:"Page blanking, removal of content or templates", summary:"Caution: Page blanking, removal of content or templates" },		"uw-joke2": { label:"Using improper humor", summary:"Caution: Using improper humor" },		"uw-create2": { label:"Creating inappropriate pages", summary:"Caution: Creating inappropriate pages" },		"uw-upload2": { label:"Uploading inappropriate images", summary:"Caution: Uploading inappropriate images" },		"uw-image2": { label:"Image-related vandalism", summary:"Caution: Image-related vandalism" },		"uw-spam2": { label:"Adding spam links", summary:"Caution: Adding spam links" },		"uw-advert2": { label:"Using Wikipedia for advertising or promotion", summary:"Caution: Using Wikipedia for advertising or promotion" },		"uw-npov2": { label:"Not adhering to neutral point of view", summary:"Caution: Not adhering to neutral point of view" },		"uw-unsourced2": { label:"Addition of unsourced material without proper citations", summary:"Caution: Addition of unsourced material without proper citations" },		"uw-error2": { label:"Introducing deliberate factual errors", summary:"Caution: Introducing deliberate factual errors" },		"uw-biog2": { label:"Adding unreferenced controversial information about living persons", summary:"Caution: Adding unreferenced controversial information about living persons" },		"uw-defam2": { label:"Defamation not specifically directed", summary:"Caution: Defamation not specifically directed" },		"uw-uncen2": { label:"Censorship of material", summary:"Caution: Censorship of material" },		"uw-mos2": { label:"Manual of style", summary:"Caution: Formatting, date, language, etc (Manual of style)" },		"uw-move2": { label:"Page moves", summary:"Caution: Page moves" },		"uw-chat2": { label:"Using talk page as forum", summary:"Caution: Using talk page as forum" },		"uw-tpv2": { label:"Refactoring others' talk page comments", summary:"Caution: Refactoring others' talk page comments" },		"uw-afd2": { label:"Removing \{\{afd\}\} templates", summary:"Caution: Removing \{\{afd\}\} templates" },		"uw-speedy2": { label:"Removing \{\{speedy deletion\}\} templates", summary:"Caution: Removing \{\{speedy deletion\}\} templates" },		"uw-npa2": { label:"Personal attack directed at a specific editor", summary:"Caution: Personal attack directed at a specific editor" },		"uw-agf2": { label:"Not assuming good faith", summary:"Caution: Not assuming good faith" },		"uw-own2": { label:"Ownership of articles", summary:"Caution: Ownership of articles" },		"uw-nor2": { label:"Adding original research", summary:"Caution: Adding original research, including unpublished syntheses of sourced material" }	},	level3: { "uw-vandalism3": { label:"Vandalism", summary:"Warning: Vandalism" },		"uw-test3": { label:"Editing tests", summary:"Warning: Editing tests" },		"uw-delete3": { label:"Page blanking, removal of content or templates", summary:"Warning: Page blanking, removal of content or templates" },		"uw-joke3": { label:"Using improper humor", summary:"Warning: Using improper humor" },		"uw-create3": { label:"Creating inappropriate pages", summary:"Warning: Creating inappropriate pages" },		"uw-upload3": { label:"Uploading inappropriate images", summary:"Warning: Uploading inappropriate images" },		"uw-image3": { label:"Image-related vandalism", summary:"Warning: Image-related vandalism" },		"uw-spam3": { label:"Adding spam links", summary:"Warning: Adding spam links" },		"uw-advert3": { label:"Using Wikipedia for advertising or promotion", summary:"Warning: Using Wikipedia for advertising or promotion" },		"uw-npov3": { label:"Not adhering to neutral point of view", summary:"Warning: Not adhering to neutral point of view" },		"uw-unsourced3": { label:"Addition of unsourced material without proper citations", summary:"Warning: Addition of unsourced material without proper citations" },		"uw-error3": { label:"Introducing deliberate factual errors", summary:"Warning: Introducing deliberate factual errors" },		"uw-biog3": { label:"Adding unreferenced controversial information about living persons", summary:"Warning: Adding unreferenced controversial information about living persons" },		"uw-defam3": { label:"Defamation not specifically directed", summary:"Warning: Defamation not specifically directed" },		"uw-uncen3": { label:"Censorship of material", summary:"Warning: Censorship of material" },		"uw-mos3": { label:"Manual of style", summary:"Warning: Formatting, date, language, etc (Manual of style)" },		"uw-move3": { label:"Page moves", summary:"Warning: Page moves" },		"uw-chat3": { label:"Using talk page as forum", summary:"Warning: Using talk page as forum" },		"uw-tpv3": { label:"Refactoring others' talk page comments", summary:"Warning: Refactoring others' talk page comments" },		"uw-afd3": { label:"Removing \{\{afd\}\} templates", summary:"Warning: Removing \{\{afd\}\} templates" },		"uw-speedy3": { label:"Removing \{\{speedy deletion\}\} templates", summary:"Warning: Removing \{\{speedy deletion\}\} templates" },		"uw-npa3": { label:"Personal attack directed at a specific editor", summary:"Warning: Personal attack directed at a specific editor" },		"uw-agf3": { label:"Not assuming good faith", summary:"Warning: Not assuming good faith" },		"uw-own3": { label:"Ownership of articles", summary:"Warning: Ownership of articles" },		"uw-nor3": { label:"Adding original research", summary:"Warning: Adding original research, including unpublished syntheses of sourced material" }	},	level4: { "uw-vandalism4": { label:"Vandalism", summary:"Final warning: Vandalism" },		"uw-delete4": { label:"Page blanking, removal of content or templates", summary:"Final warning: Page blanking, removal of content or templates" },		"uw-joke4": { label:"Using improper humor", summary:"Final warning: Using improper humor" },		"uw-create4": { label:"Creating inappropriate pages", summary:"Final warning: Creating inappropriate pages" },		"uw-upload4": { label:"Uploading inappropriate images", summary:"Final warning: Uploading inappropriate images" },		"uw-image4": { label:"Image-related vandalism", summary:"Final warning: Image-related vandalism" },		"uw-spam4": { label:"Adding spam links", summary:"Final warning: Adding spam links" },		"uw-advert4": { label:"Using Wikipedia for advertising or promotion", summary:"Final warning: Using Wikipedia for advertising or promotion" },		"uw-npov4": { label:"Not adhering to neutral point of view", summary:"Final warning: Not adhering to neutral point of view" },		"uw-biog4": { label:"Adding unreferenced controversial information about living persons", summary:"Final warning: Adding unreferenced controversial information about living persons" },		"uw-defam4": { label:"Defamation not specifically directed", summary:"Final warning: Defamation not specifically directed" },		"uw-move4": { label:"Page moves", summary:"Final warning: Page moves" },		"uw-chat4": { label:"Using talk page as forum", summary:"Final warning: Using talk page as forum" },		"uw-afd4": { label:"Removing \{\{afd\}\} templates", summary:"Final warning: Removing \{\{afd\}\} templates" },		"uw-speedy4": { label:"Removing \{\{speedy deletion\}\} templates", summary:"Final warning: Removing \{\{speedy deletion\}\} templates" },		"uw-npa4": { label:"Personal attack directed at a specific editor", summary:"Final warning: Personal attack directed at a specific editor" },		"uw-nor4": { label:"Adding original research", summary:"Final Warning: Adding original research, including unpublished syntheses of sourced material" }	},	level4im: { "uw-vandalism4im": { label:"Vandalism", summary:"Only warning: Vandalism" },		"uw-delete4im": { label:"Page blanking, removal of content or templates", summary:"Only warning: Page blanking, removal of content or templates" },		"uw-create4im": { label:"Creating inappropriate pages", summary:"Only warning: Creating inappropriate pages" },		"uw-upload4im": { label:"Uploading inappropriate images", summary:"Only warning: Uploading inappropriate images" },		"uw-image4im": { label:"Image-related vandalism", summary:"Only warning: Image-related vandalism" },		"uw-spam4im": { label:"Adding spam links", summary:"Only warning: Adding spam links" },		"uw-defam4im": { label:"Defamation not specifically directed", summary:"Only warning: Defamation not specifically directed" },		"uw-move4im": { label:"Page moves", summary:"Only warning: Page moves" },		"uw-npa4im": { label:"Personal attack directed at a specific editor", summary:"Only warning: Personal attack directed at a specific editor" }	},	singlenotice: { "uw-2redirect": { label:"Creating double redirects through bad page moves", summary:"Notice: Creating double redirects through bad page moves" },		"uw-aiv": { label:"Bad AIV report", summary:"Notice: Bad AIV report" },		"uw-articlesig": { label:"Adding signatures to article space", summary:"Notice: Adding signatures to article space" },		"uw-autobiography": { label:"Creating autobiographies", summary:"Notice: Creating autobiographies" },		"uw-badcat": { label:"Adding incorrect categories", summary:"Notice: Adding incorrect categories" },		"uw-bite": { label:"\"Biting\" newcomers", summary:"Notice: \"Biting\" newcomers" },		"uw-coi": { label:"Conflict of Interest", summary:"Notice: Conflict of Interest" },		"uw-c&pmove": { label:"Cut and paste moves", summary:"notice: Cut and paste moves" },		"uw-date": { label:"Unnecessarily changing date formats", summary:"Notice: Unnecessarily changing date formats" },		"uw-deadlink": { label:"Removing proper sources containing dead links", summary:"Notice: Removing proper sources containing dead links" },		"uw-editsummary": { label:"Not using edit summary", summary:"Notice: Not using edit summary" },		"uw-english": { label:"Not communicating in English", summary:"Notice: Not communicating in English" },		"uw-hangon": { label:"User forgets to place a hang-on reasoning to the talk page", summary:"Notice: User forgets to place a hang-on reasoning to the talk page" },		"uw-italicize": { label:"Italicize books, films, albums, magazines, TV series, etc within articles", summary:"Notice: Italicize books, films, albums, magazines, TV series, etc within articles" },		"uw-lang": { label:"Unnecessarily changing between British and American English", summary:"Notice: Unnecessarily changing between British and American English" },		"uw-linking": { label:"For excessive addition of redlinks or repeated blue links", summary:"Notice: For excessive addition of redlinks or repeated blue links" },		"uw-minor": { label:"Incorrect use of minor edits check box", summary:"Notice: Incorrect use of minor edits check box" },		"uw-nonfree": { label:"Uploading replaceable non-free images", summary:"Notice: Uploading replaceable non-free images" },		"uw-notaiv": { label:"Do not report complex abuse to AIV", summary:"Notice: Do not report complex abuse to AIV" },		"uw-notvote": { label:"We use consensus, not voting", summary:"Notice: We use consensus, not voting" },		"uw-preview": { label:"Use preview button to avoid mistakes", summary:"Notice: Use preview button to avoid mistakes" },		"uw-selfrevert": { label:"Reverting self tests", summary:"Notice: Reverting self tests" },		"uw-samename": { label:"Rename request impossible", summary:"Notice: Rename request impossible" },		"uw-sandbox": { label:"Removal of the Sandbox header", summary:"Notice: Removal of the Sandbox header" },		"uw-spellcheck": { label:"Review spelling, etc.", summary:"Notice: Review spelling, etc." },		"uw-socialnetwork": { label:"Wikipedia is not a social network", summary:"Notice: Wikipedia is not a social network" },		"uw-subst": { label:"Remember to subst: templates", summary:"Notice: Remember to subst: templates" },		"uw-talkinarticle": { label:"Talk in article", summary:"Notice: Talk in article" },		"uw-tilde": { label:"Not signing posts", summary:"Notice: Not signing posts" },		"uw-uaa": { label:"Reporting of username to WP:UAA not accepted", summary:"Notice: Reporting of username to WP:UAA not accepted" },		"uw-warn": { label:"Warning vandals", summary:"Notice: Warning vandals" }	},	singlewarn: { "uw-3rr": { label:"Potentially violating the three revert rule", summary:"Warning: Potentially violating the three revert rule" },/*		"uw-attack": { label:"Creating attack pages", summary:"Warning: Creating attack pages" },*/		"uw-bv": { label:"Blatant vandalism", summary:"Warning: Blatant vandalism" },		"uw-canvass": { label:"Canvassing", summary:"Warning: Canvassing" },		"uw-copyright": { label:"Copyright violation", summary:"Warning: Copyright violation" },		"uw-copyright-link": { label:"Linking to copyrighted works violation", summary:"Warning: Linking to copyrighted works violation" },		"uw-hoax": { label:"Creating hoaxes", summary:"Warning: Creating hoaxes" },		"uw-legal": { label:"Making legal threats", summary:"Warning: Making legal threats" },		"uw-longterm": { label:"Long term pattern of vandalism", summary:"Warning: Long term pattern of vandalism" },		"uw-multipleIPs": { label:"Usage of multiple IPs", summary:"Warning: Usage of multiple IPs" },		"uw-pinfo": { label:"Personal info", summary:"Warning: Personal info" },		"uw-redirect": { label:"Creating malicious redirects", summary:"Warning: Creating malicious redirects" },		"uw-upv": { label:"Userpage vandalism", summary:"Warning: Userpage vandalism" },		"uw-tempabuse": { label:"Improper use of warning or blocking template", summary:"Warning: Improper use of warning or blocking template" },		"uw-wrongsummary": { label:"Using inaccurate or inappropriate edit summaries", summary:"Warning: Using inaccurate or inappropriate edit summaries" },		"uw-username": { label:"Username is against policy", summary:"Warning: Username is against policy" },		"uw-userpage": { label:"Userpage or subpage is against policy", summary:"Warning: Userpage or subpage is against policy" }	},	block: { "uw-block1": { 'label':"Block level 1", 'summary':"You have been temporarily blocked" },		"uw-block2": { 'label':"Block level 2", 'summary':"You have been blocked" },		"uw-block3": { 'label':"Block level 3", 'summary':"You have been indefinitely blocked" },		"uw-ablock": { 'label':"Anonymous block", 'summary':"Your IP address has been blocked" },		"uw-sblock": { 'label':"Spam block", 'summary':"You have been blocked for spamming" },		"uw-vblock": { 'label':"Vandalism", 'summary':"You have been blocked for vandalism" },		"uw-voablock": { 'label':"Vandalism-only account", 'summary':"You have been blocked for using a vandalism-only account" },		"uw-dblock": { 'label':"Delete block", 'summary':"You have been blocked for deletion" },		"uw-3block": { 'label':"3RR block", 'summary':"You have been blocked for violation of the 3RR rule" },		"uw-ublock": { 'label':"Username block", 'summary':"You have been blocked for violation of the username policy" },		"uw-uhblock": { 'label':"Username hard block", 'summary':"You have been blocked for blatant violation of the username policy" },		"uw-lblock": { 'label':"Legal Threat Block", 'summary':"You have been blocked for making legal threats" },		"uw-hblock": { 'label':"Harassment Block", 'summary':"You have been blocked for attempting to harass other users" },		"uw-npblock": { 'label':"Creating Nonsense Pages Block", 'summary':"You have been blocked for creating nonsense pages" }	} };

twinklewarn.callback.change_category = function twinklewarnCallbackChangeCategory(e) { var value = e.target.value; var sub_group = e.target.root.sub_group; var messages = twinklewarn.messages[ value ]; sub_group.main_group = value; var old_subvalue = sub_group.value; if( old_subvalue ) { old_subvalue = old_subvalue.replace(/\d*(im)?$/, '' ); var old_subvalue_re = new RegExp( RegExp.escape( old_subvalue ) + "(\\d*(?:im)?)$" ); }

while( sub_group.hasChildNodes ){ sub_group.removeChild( sub_group.firstChild ); }

for( var i in messages ) { var selected = false; if( old_subvalue && old_subvalue_re.test( i ) ) { selected = true; }		var elem = new QuickForm.element( { type:'option', label:"[" + i + "]: " + messages[i].label, value:i, selected: selected } ); sub_group.appendChild( elem.render ); }

if( value == 'block' ) { var more = new QuickForm.element( {				type: 'input',				name: 'block_timer',				label: 'Period of blocking: ',				tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...'			} ); e.target.root.insertBefore( more.render, e.target.root.lastChild ); e.target.root.article.disabled = true; } else if( e.target.root.block_timer ) { e.target.root.removeChild( e.target.root.block_timer.parentNode ); e.target.root.article.disabled = false; } }

twinklewarn.callbacks = { main: function( self ) { var form = self.responseXML.getElementById( 'editform' ); var text = form.wpTextbox1.value;

var history_re = /\<\!\-\-\ Template\:(uw\-.*?)\ \-\-\>.*?(\d{1,2}:\d{1,2}, \d{1,2} \w+ \d{4}) \(UTC\)/g; var history = {}; var latest = { date:new Date( 0 ), type:'' };

var current;

while( ( current = history_re.exec( text ) ) != undefined ) { var current_date = new Date( current[2] + ' UTC' ); if( !( current[1] in history ) || history[ current[1] ] < current_date ) { history[ current[1] ] = current_date; }			if( current_date > latest.date ) { latest.date = current_date; latest.type = current[1]; }		}

var date = new Date;

if( self.params.sub_group in history ) { var temp_time = new Date( history[ self.params.sub_group ] ); temp_time.setUTCHours( temp_time.getUTCHours + 24 );

if( temp_time > date ) { Status.info( 'Info', "an identical " + self.params.sub_group + " has been issued in the last 24 hours" ); if( !confirm( "Will you still add a warning/notice?" ) ) {					self.statelem.info( 'aborted per user request' ); return; }			}		}

latest.date.setUTCMinutes( latest.date.getUTCMinutes + 1 ); // after long debate, one minute is max

if( latest.date > date ) { Status.info('Info', "a " + latest.type + " has been issued in the last minute" ); if( !confirm( "Will you still add a warning/notice?" ) ) {					self.statelem.info( 'aborted per user request' ); return; }		}

var mainheaderRe = /==+\\s*Warnings\\s*==+/; var headerRe = new RegExp( "^==+\\s*(?:" + date.getUTCMonthName + '|' + date.getUTCMonthNameAbbrev + ")\\s+" + date.getUTCFullYear + "\\s*==+", 'm' );

if( text.length > 0 ) { text += "\n"; }

if( !headerRe.exec( text ) ) { Status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += "== " + date.getUTCMonthName + " " + date.getUTCFullYear + " ==\n"; }		if( self.params.main_group == 'block' ) { var time = null; if( /te?mp|^\s*$|min/.exec( self.params.block_timer ) ) { time = ''; } else if( /indef|\*|max/.exec( self.params.block_timer ) ) { time = '|indef=yes'; } else { time = '|time=' + self.params.block_timer; }

text += "\{\{subst:" + self.params.sub_group + time + (self.params.reason ? '|reason=' + self.params.reason : '' ) + "|sig=true\}\}"; } else if( self.params.sub_group == 'uw-username' ) { // ugly hack text += "\{\{subst:" + self.params.sub_group + ( self.params.reason ? '|1=' + self.params.reason : '' ) + "\}\} \~\~\~\~"; } else { text += "\{\{subst:" + self.params.sub_group + ( self.params.article ? '|1=' + self.params.article :  ) + "\}\}" + (self.params.reason ? " " + self.params.reason + "'' ": ' ' ) + "\~\~\~\~"; }

if ( TwinkleConfig.showSharedIPNotice && isIPAddress( wgTitle ) ) { Status.info( 'Info', 'Adding a shared ip notice' ); switch( QueryString.get( 'type' ) ) { case 'vand': text += "\n:If this is a shared IP address, and you didn't make any unconstructive edits, consider creating an account for yourself so you can avoid further irrelevant warnings. "; break; default: text += "\n:If this is a shared IP address, and you didn't make the edit, consider creating an account for yourself so you can avoid further irrelevant notices. "; break; }		}		var postData = { 'wpMinoredit': form.wpMinoredit.checked ? 1 : undefined, 'wpWatchthis': TwinkleConfig.watchWarnings ? 1 : form.wpWatchthis.checked ? 1 : undefined, 'wpStarttime': form.wpStarttime.value, 'wpEdittime': form.wpEdittime.value, 'wpAutoSummary': form.wpAutoSummary.value, 'wpEditToken': form.wpEditToken.value, 'wpSummary': twinklewarn.messages[self.params.main_group][self.params.sub_group].summary + ( self.params.article ? ' on ' + self.params.article +  :  ) + '.' + TwinkleConfig.summaryAd, 'wpTextbox1': text };

self.post( postData ); } }

twinklewarn.callback.evaluate = function twinklewarnCallbackEvaluate(e) {

// First, grab all the values provided by the form var params = { reason: e.target.reason.value, main_group: e.target.main_group.value, sub_group: e.target.sub_group.value, article: e.target.article.value.replace( /^(Image|Category):/i, ':$1:' ), block_timer: e.target.block_timer ? e.target.block_timer.value : null }

Status.init( e.target );

var query = { 'title': wgPageName, 'action': 'submit' };	Wikipedia.actionCompleted.redirect = wgPageName; Wikipedia.actionCompleted.notice = "Warning complete, reloading talk page in some seconds"; var wikipedia_wiki = new Wikipedia.wiki( 'User talk page modification', query, twinklewarn.callbacks.main ); wikipedia_wiki.params = params; wikipedia_wiki.get; }

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/

title: User:Flewis/twinklewarn.js


text:
User:Flewis/twinklearv.js

// If TwinkleConfig aint exist. if( typeof( TwinkleConfig ) == 'undefined' ) { TwinkleConfig = {}; }

/** TwinkleConfig.summaryAd (string) If ad should be added or not to summary, default TWINKLE */ if( typeof( TwinkleConfig.summaryAd ) == 'undefined' ) { TwinkleConfig.summaryAd = " using TW"; }

/** TwinkleConfig.markAIVReportAsMinor (boolean) Defines if a reports to AIV should be marked as minor, if false, default is applied as per preference. */ if( typeof( TwinkleConfig.markAIVReportAsMinor ) == 'undefined' ) { TwinkleConfig.markAIVReportAsMinor = true; }

/** TwinkleConfig.confirmUsernameToAIV (boolean) (deprecated) Defines if a username reports to AIV should be confirmed before sent. */ if( typeof( TwinkleConfig.confirmUsernameToAIV ) == 'undefined' ) { TwinkleConfig.confirmUsernameToAIV = true; }

/** TwinkleConfig.toolboxButtons (string) If id defined in this array, the button of the action is located inthe toolbox instead of in the actions bar. */ if( typeof( TwinkleConfig.toolboxButtons ) == 'undefined' ) { TwinkleConfig.toolboxButtons = []; }

function num2order( num ) { switch( num ) { case 1: return ''; case 2: return '2nd'; case 3: return '3rd'; default: return num + 'th'; } }

$( twinklearv ); function twinklearv{ var username;

if ( wgNamespaceNumber == 3 || wgNamespaceNumber == 2 || ( wgNamespaceNumber == -1 && wgTitle == "Contributions" )){

// If we are on the contributions page, need to parse some then if( wgNamespaceNumber == -1 && wgTitle == "Contributions" ) { username = document.evaluate( 'substring-after(//div[@id="contentSub"]//a[@title="Special:Log"][last]/@href, "user=")', document, null, XPathResult.STRING_TYPE, null).stringValue; } else { username = wgTitle.split( '/' )[0].replace( /\"/, "\\\""); // only first part before any slashes }

if( !username ) { // Something is fishy, there was no user? lets about everything throw "given username was " + username + " and thus makes no sense."; }

var name = isIPAddress( username ) ? 'Report IP' : 'Report'; var title = isIPAddress( username ) ? 'Report IP to Administators' : 'Report user to Administrators'; if (twinkleConfigExists) {			mw.util.addPortletLink( 'p-cactions', "javascript:twinklearv.callback(\"" + username + "\")", "arv", "tw-arv", name, title ); }		else {			mw.util.addPortletLink('p-cactions', 'javascript:alert("Your account is too new to use Twinkle.");', 'arv', 'tw-arv', name, title); }	} }

twinklearv.callback = function twinklearvCallback( uid ) { if( uid == wgUserName ){ alert( 'You don\'t want to report yourself, do you?' ); return; }

var Window = new SimpleWindow( 600, 400 ); Window.setTitle( "Advance Reporting and Vetting" ); //Backronym

var form = new QuickForm( twinklearv.callback.evaluate ); var categories = form.append( {			type: 'select',			name: 'category',			label: 'Select wanted type of report: ',			event: twinklearv.callback.change_category		} ); categories.append( {			type: 'option',			label: 'Vandalism',			value: 'aiv'		} ); categories.append( {			type: 'option',			label: 'Username',			value: 'username'		} ); categories.append( {			type: 'option',			label: 'Sockpuppeter',			value: 'sock'		} );

form.append( {			type: 'field',			label:'Work area',			name: 'work_area'		} ); form.append( {			type: 'hidden',			name: 'uid',			value: uid		} ); var result = form.render; Window.setContent( result ); Window.display;

// We must init the var evt = document.createEvent( "Event" ); evt.initEvent( 'change', true, true ); result.category.dispatchEvent( evt );

}

twinklearv.callback.change_category = function twinklearvCallbackChangeCategory(e) { var value = e.target.value; var root = e.target.form; var old_area; for( var i = 0; i < root.childNodes.length; ++i ) { var node = root.childNodes[i]; if( 			node instanceof Element &&			node.getAttribute( 'name' ) == 'work_area' 		) { old_area = node; break; }	}	var work_area = null;

switch( value ) { default: case 'aiv': work_area = new QuickForm.element( { 				type: 'field',				label: 'Report user for vandalism',				name: 'work_area'			} ); work_area.append( {				type: 'input',				name: 'page',				label: 'Primary linked page: ',				tooltip: 'Leave blank for no linked page in report',				value: QueryString.exists( 'vanarticle' ) ? QueryString.get( 'vanarticle' ) : ,				event: function(e) {					var value = e.target.value;					var root = e.target.form;					if( value ==  ) {						root.badid.disabled = root.goodid.disabled = true;					} else {						root.badid.disabled = false;						root.goodid.disabled = root.badid.value == '';					}				}			} ); work_area.append( {				type: 'input',				name: 'badid',				label: 'Revision ID for target page when vandalised: ',				tooltip: 'Leave blank for no diff link',				value: QueryString.exists( 'vanarticlerevid' ) ? QueryString.get( 'vanarticlerevid' ) : ,				disabled: !QueryString.exists( 'vanarticle' ),				event: function(e) {					var value = e.target.value;					var root = e.target.form;					root.goodid.disabled = value == ;				}			} ); work_area.append( {				type: 'input',				name: 'goodid',				label: 'Last good revision ID before vandalism of target page: ',				tooltip: 'Leave blank for diff link to previous revision',				value: QueryString.exists( 'vanarticlegoodrevid' ) ? QueryString.get( 'vanarticlegoodrevid' ) : '',				disabled: !QueryString.exists( 'vanarticle' ) || QueryString.exists( 'vanarticlerevid' )			} ); work_area.append( {				type: 'checkbox',				name: 'arvtype',				list: [					{ 						label: 'Vandalism after final warning given',						value: 'final'					},					{ 						label: 'Vandalism after recent release of block',						value: 'postblock'					},					{ 						label: 'Evidently vandalism only account',						value: 'vandalonly',						disabled: isIPAddress( root.uid.value )					},					{ 						label: 'Account is evidently a spambot or a compromised account',						value: 'spambot'					}				]			} ); work_area.append( {				type: 'textarea',				name: 'reason',				label: 'Comment: '			} ); work_area.append( { type:'submit' } ); work_area = work_area.render; old_area.parentNode.replaceChild( work_area, old_area ); break; case 'username': work_area = new QuickForm.element( { 				type: 'field',				label: 'Report username violation',				name: 'work_area'			} ); var types = work_area.append( {				type: 'field',				label: 'Type of violation',				tooltip: 'A valid report must include at least one of following types'			} );

types.append ( { 				type:'header', 				label:'Confusing usernames',				tooltip: 'Confusing usernames that make it unduly difficult to identify users by their username'			} ); types.append( {				type: 'checkbox',				name: 'arvtype',				list: [					{ 						label: 'Confusing username',						value: 'confusing',						tooltip: 'Confusing usernames make it unnecessarily difficult to identify contributors'					},					{						label: 'Misleading username',						value: 'misleading',						tooltip: 'Misleading usernames imply relevant, misleading things about the user, such as falsely conveying authority or impersonating another user.'					},					{ 						label: 'Promotional username',						value: 'promotional',						tooltip: 'Promotional usernames are used to promote a group or company on Wikipedia.'					},					{ 						label: 'Offensive username',						value: 'offensive',						tooltip: 'Offensive usernames make harmonious editing difficult or impossible.'					},					{ 						label: 'Disruptive username',						value: 'disruptive',						tooltip: 'Disruptive usernames that disrupt or misuse Wikipedia, or imply an intent to do so' }				]			} );		work_area.append( { type: 'textarea', name: 'reason', label: 'Comment:' } );		work_area.append( { type:'submit' } );		work_area = work_area.render;		old_area.parentNode.replaceChild( work_area, old_area );		break;

case 'sock': work_area = new QuickForm.element( { 				type: 'field',				label: 'Report suspected sockpuppeter',				name: 'work_area'			} ); work_area.append(			{				type: 'dyninput',				name: 'sockpuppet',				label: 'Sockpuppets',				sublabel: 'Sock: ',				tooltip: 'The username of the sockpuppet without the User:-prefix',				min: 2			}		); work_area.append( {				type: 'textarea',				label: 'Evidence:',				name: 'evidence'			} ); work_area.append( { type:'submit' } ); work_area = work_area.render; old_area.parentNode.replaceChild( work_area, old_area ); break; } }

twinklearv.callbacks = { aiv: function( self ) { uid = self.params.uid; reason = self.params.reason; var form = self.responseXML.getElementById('editform');

if( !form ) { self.statelem.error( 'Failed to retrieve edit form.' ); return; }		var text = form.wpTextbox1.value;

var re = new RegExp( "\\{\\{\\s*(?:(?:[Ii][Pp])?[Vv]andal|[Uu]serlinks)\\s*\\|\\s*(?:1=)?\\s*" + RegExp.escape( uid, true ) + "\\s*\\}\\}" );

var myArr; if( ( myArr = re.exec( text ) ) ) { self.statelem.info( 'Report already present, will not add a new one' ); return; }		self.statelem.status( 'Adding new report...' ); var postData = { 'wpMinoredit': TwinkleConfig.markAIVReportAsMinor ? '' : form.wpMinoredit.checked ? '' : undefined, 'wpWatchthis': form.wpWatchthis.checked ? '' : undefined, 'wpStarttime': form.wpStarttime.value, 'wpEdittime': form.wpEdittime.value, 'wpAutoSummary': form.wpAutoSummary.value, 'wpEditToken': form.wpEditToken.value, 'wpSummary': 'Reporting ' + uid + '.'+ TwinkleConfig.summaryAd, 'wpTextbox1': text + '*\{\{' + ( isIPAddress( uid ) ? 'IPvandal' : 'vandal' ) + '|' + (/\=/.test( uid ) ? '1=' :  ) + uid + '\}\} - ' + reason + ' ' +  };

self.post( postData ); },	username: function( self ) { uid = self.params.uid; reason = self.params.reason; var form = self.responseXML.getElementById('editform');

if( !form ) { self.statelem.error( 'Failed to retrieve edit form.' ); return; }		var text = form.wpTextbox1.value;

self.statelem.status( 'Adding new report...' ); var postData = { 'wpMinoredit': form.wpMinoredit.checked ? '' : undefined, 'wpWatchthis': form.wpWatchthis.checked ? '' : undefined, 'wpStarttime': form.wpStarttime.value, 'wpEdittime': form.wpEdittime.value, 'wpAutoSummary': form.wpAutoSummary.value, 'wpEditToken': form.wpEditToken.value, 'wpSummary': 'Reporting ' + uid + '.'+ TwinkleConfig.summaryAd, 'wpTextbox1': text.replace( /-->/, "-->\n" + reason.replace( '\$', "$$$$" ) ) };		self.post( postData ); },	sock: { main: function( self ) { var xmlDoc = self.responseXML; var titles = xmlDoc.evaluate( '//allpages/p/@title', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );

var number = 0; for( var i = 0; i < titles.snapshotLength; ++i ) { var title = titles.snapshotItem(i).value; title = title.replace( /(first|second|third|fourth|fifth|sixth|seventh|eighth|ninth|tenth|eleventh)/, function(v) {						return {							'first': '1st',							'second': '2nd',							'third': '3rd',							'fourth': '4th',							'fifth': '5th',							'sixth': '6th',							'seventh': '7th',							'eighth': '8th',							'ninth': '9th',							'tenth': '10th',							'eleventh': '11th'						}[v];					} ); var n = /\(\s*(\d+)(?:(?:th|nd|rd|st) nom(?:ination)?)?\s*\)\s*$/.exec( title ); if( n && n[1] > number ) { number = n[1]; } else if( number == 0 ) { number = 1; }			}

if( number == 0 ) { self.params.numbering = self.params.number = ''; numbering = number = ''; } else { self.params.number = num2order( parseInt( number ) + 1); self.params.numbering = ' (' + self.params.number + ' nomination)'; }			self.statelem.info( 'next in order is Suspected sock puppets/' + self.params.uid + self.params.numbering + '');

var query = { 'title': 'Wikipedia:Suspected sock puppets/' + self.params.uid + self.params.numbering, 'action': 'submit' };

var wikipedia_wiki = new Wikipedia.wiki( 'Creating discussion page', query, twinklearv.callbacks.sock.discussionPage ); wikipedia_wiki.params = self.params; wikipedia_wiki.get;

var query = { 'title': 'Wikipedia:Suspected sock puppets', 'section': 3, 'action': 'submit' };

var wikipedia_wiki = new Wikipedia.wiki( 'Linking report to open cases', query, twinklearv.callbacks.sock.openCases ); wikipedia_wiki.params = self.params; wikipedia_wiki.get;

var query = { 'title': 'User talk:' + self.params.uid, 'action': 'submit' };

var wikipedia_wiki = new Wikipedia.wiki( 'Notifying suspected sockpuppeter', query, twinklearv.callbacks.sock.notifySock ); wikipedia_wiki.params = self.params; wikipedia_wiki.get;

var query = { 'title': 'User:' + self.params.uid, 'action': 'submit' };

var wikipedia_wiki = new Wikipedia.wiki( 'Tag suspected sockpuppeter', query, twinklearv.callbacks.sock.tagSockpuppeter ); wikipedia_wiki.params = self.params; wikipedia_wiki.get;

var statusIndicator1 = new Status('Tagging suspected sockpuppets', '0%'); var statusIndicator2 = new Status('Notifying suspected sockpuppets', '0%');

var total = self.params.sockpuppets.length * 2;

var onsuccess = function( self ) { var obj = self.params.obj; var total = self.params.total; var now = parseInt( 100 * ++(self.params.current)/total ) + '%'; obj.update( now ); self.statelem.unlink; if( self.params.current >= total ) { obj.info( now + ' (completed)' ); Wikipedia.removeCheckpoint; }			}			var onloaded = onsuccess;

var onloading = function( self ) {}

Wikipedia.addCheckpoint;

var params1 = clone( self.params ); params1.total = total; params1.obj = statusIndicator1; params1.current =  0;

var params2 = clone( self.params ); params2.total = total; params2.obj = statusIndicator2; params2.current =  0;

var socks = self.params.sockpuppets; for( var i = 0; i < socks.length; ++i ) { var query = { 'title': 'User:' + socks[i], 'action': 'submit' };				var wikipedia_wiki = new Wikipedia.wiki( "Tagging of " + socks[i], query, twinklearv.callbacks.sock.tagSockpuppet ); wikipedia_wiki.params = params1; wikipedia_wiki.onloaded = onloaded; wikipedia_wiki.onsuccess = onsuccess; wikipedia_wiki.get; var query = { 'title': 'User talk:' + socks[i], 'action': 'submit' };				var wikipedia_wiki = new Wikipedia.wiki( "Notification for " + socks[i], query, twinklearv.callbacks.sock.notifySock ); wikipedia_wiki.params = params2; wikipedia_wiki.onloaded = onloaded; wikipedia_wiki.onsuccess = onsuccess; wikipedia_wiki.get;

}		},		discussionPage: function( self ) { var form = self.responseXML.getElementById('editform'); var text = "===User:" + self.params.uid + "===\n" + ";Suspected sockpuppeteer\n" + ":\{\{user5|" + self.params.uid + "\}\}\n\n" + ";Suspected sockpuppets\n" + self.params.sockpuppets.map( function(v) { return ":\{\{user5|" + v + "\}\}" } ).join( "\n" ) + "\n\n" + ";Report submission by\n" + "\~\~\~\~\n\n" + ";Evidence\n" + self.params.evidence + "\n\n" + ";Comments\n\n\n" + ";Conclusions\n\n\n" + "\n \n";

var postData = { 'wpMinoredit': form.wpMinoredit.checked ? '' : undefined, 'wpWatchthis': form.wpWatchthis.checked ? '' : undefined, 'wpStarttime': form.wpStarttime.value, 'wpEdittime': form.wpEdittime.value, 'wpAutoSummary': form.wpAutoSummary.value, 'wpEditToken': form.wpEditToken.value, 'wpSummary': "Creating report for User:" + self.params.uid + '.' + TwinkleConfig.summaryAd, 'wpTextbox1': text };			self.post( postData ); },		openCases: function( self ) { var form = self.responseXML.getElementById('editform'); text = form.wpTextbox1.value.replace( //, "$1\n\{\{Wikipedia:Suspected sock puppets/" + self.params.uid + self.params.numbering + "\}\}"); var postData = { 'wpMinoredit': form.wpMinoredit.checked ? '' : undefined, 'wpWatchthis': form.wpWatchthis.checked ? '' : undefined, 'wpStarttime': form.wpStarttime.value, 'wpEdittime': form.wpEdittime.value, 'wpAutoSummary': form.wpAutoSummary.value, 'wpEditToken': form.wpEditToken.value, 'wpSummary': "Adding report for User:" + self.params.uid + '.' + TwinkleConfig.summaryAd, 'wpTextbox1': text };

self.post( postData ); },		tagSockpuppeter: function( self ) { var form = self.responseXML.getElementById('editform'); var text = form.wpTextbox1.value; if( /\{\{sockpuppeteer.*?\}\}/.exec( text ) ) { // already marked as a sock, just ignore then self.onsuccess( self ); Wikipedia.actionCompleted; return; }			var postData = { 'wpMinoredit': form.wpMinoredit.checked ? '' : undefined, 'wpWatchthis': form.wpWatchthis.checked ? '' : undefined, 'wpStarttime': form.wpStarttime.value, 'wpEdittime': form.wpEdittime.value, 'wpAutoSummary': form.wpAutoSummary.value, 'wpEditToken': form.wpEditToken.value, 'wpSummary': "Adding suspected sockpuppeter tag." + TwinkleConfig.summaryAd, 'wpTextbox1': "\{\{sockpuppeteer\}\}\n" + text };

self.post( postData ); },		tagSockpuppet: function( self ) { var form = self.responseXML.getElementById('editform'); var text = form.wpTextbox1.value; if( /\{\{sockpuppet.*?\}\}/.exec( text ) ) { // already marked as a sock, just ignore then self.onsuccess( self ); Wikipedia.actionCompleted; return; }			var postData = { 'wpMinoredit': form.wpMinoredit.checked ? '' : undefined, 'wpWatchthis': form.wpWatchthis.checked ? '' : undefined, 'wpStarttime': form.wpStarttime.value, 'wpEdittime': form.wpEdittime.value, 'wpAutoSummary': form.wpAutoSummary.value, 'wpEditToken': form.wpEditToken.value, 'wpSummary': "Adding suspected sockpuppet tag for suspected sockpuppeter User:" + self.params.uid + '.' + TwinkleConfig.summaryAd, 'wpTextbox1': "\{\{subst:sockpuppet|1=" + self.params.uid + "\}\}\n" + text };

self.post( postData ); },

notifySock: function( self ) { var form = self.responseXML.getElementById('editform'); text = form.wpTextbox1.value; var postData = { 'wpMinoredit': form.wpMinoredit.checked ? '' : undefined, 'wpWatchthis': form.wpWatchthis.checked ? '' : undefined, 'wpStarttime': form.wpStarttime.value, 'wpEdittime': form.wpEdittime.value, 'wpAutoSummary': form.wpAutoSummary.value, 'wpEditToken': form.wpEditToken.value, 'wpSummary': "Notifying about suspicion of sockpuppetering." + TwinkleConfig.summaryAd, 'wpTextbox1': text + "\n\{\{subst:socksuspectnotice|1=" + self.params.uid + self.params.numbering + "\}\} \~\~\~\~" };

self.post( postData ); }	} }

twinklearv.callback.evaluate = function(e) { var form = e.target; var reason = ""; if( form.reason ) { comment = form.reason.value; }	var uid = form.uid.value; switch( form.category.value ) { default: case 'aiv': var types = form.getChecked( 'arvtype' ); if( types.length == 0 && comment == '' ) { alert( 'You must specify some reason' ); return; }

types = types.map( function(v) {				switch(v) {				case 'final':					return 'vandalism after final warning';					break;				case 'postblock':					return 'vandalism after recent release of block';					break;				case 'spambot':					return 'account is evidently a spambot or a compromised account';					break;				case 'vandalonly':					return 'actions evidently indicate a vandalism only account';					break;				}			} ).join( ', ' );

if( form.page.value != '' ) { reason += 'On Category):/i, ':$1:' ) + '';

if( form.badid.value != '' ) { var query = { 'title': form.page.value, 'diff': form.badid.value, 'oldid': form.goodid.value };				reason += ' ([' + mw.config.get('wgServer') + mw.config.get('wgScriptPath') + '/index.php?' + QueryString.create( query ) + ' diff])'; }			reason += ';'; }

if( types ) { reason += " " + types; }		if (comment != '' ) { reason += ". " + comment + '.'; }		Status.init( form );

var query = { 'title': 'Wikipedia:Administrator intervention against vandalism', 'action': 'submit', 'section': 1 };		wikipedia_wiki = new Wikipedia.wiki( 'Processing AIV request', query, twinklearv.callbacks.aiv ); wikipedia_wiki.params = { reason:reason, uid:uid }; wikipedia_wiki.get; break; case 'username': var types = form.getChecked( 'arvtype' ); if( types.length == 0 ) { alert( 'You must specify at least one breached violation' ); return; }		types = types.map( function( v ) { return v.toLowerCaseFirstChar; } );

if( types.length <= 2 ) { types = types.join( ' and ' ); } else { types = [ types.slice( 0, -1 ).join( ', ' ), types.slice( -1 ) ].join( ', and ' ); }		var article = 'a'; if( /aeiouwyh/.test( types[0] ) ) { // non 100% correct, but whatever inlcuding 'h' for Cockney article = 'an'; }		reason = "*\{\{user-uaa|1=" + uid + "\}\} — Violation of username policy because it's " + article + " " + types + " username; "; if (comment != '' ) { reason += "" + comment.toUpperCaseFirstChar + ". "; }		reason += "\~\~\~\~"; Status.init( form );

var query = { 'title': 'Wikipedia:Usernames for administrator attention', 'action': 'submit', 'section': 1 };

wikipedia_wiki = new Wikipedia.wiki( 'Processing UAA request', query, twinklearv.callbacks.username ); wikipedia_wiki.params = { reason:reason, uid:uid }; wikipedia_wiki.get; break; case 'sock': var sockpuppets = form.getTexts( 'sockpuppet' ); var evidence = form.evidence.value; Status.init( form );

var query = { 'action': 'query', 'list': 'allpages', 'apprefix': 'Suspected sock puppets/' + uid, 'apnamespace': 4, 'apfilterredir': 'nonredirects', 'aplimit': userIsInGroup( 'sysop' ) ? 5000 : 500		};

var wikipedia_api = new Wikipedia.api( 'Quering allpages', query, twinklearv.callbacks.sock.main ); wikipedia_api.params = { uid:uid, sockpuppets:sockpuppets, evidence:evidence }; wikipedia_api.post; } }

title: User:Flewis/twinklearv.js