From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.

/* ============================================== *\

** CSD Helper - JavaScript CSD Script

**   for Wikipedia

**

** Created by Alex Barley [[User:Ale_jrb]]

**		Tracker: [[User:Ale_jrb/Scripts]]

**

**	You are advised to import this script to your

** monobook.js page - AVOID CREATING YOUR OWN 

** VERSION WHERE POSSIBLE.

**

**	Instructions for this script can be found at

** [[User:Ale_jrb/Scripts]] - refer to this for

** setting details.

\* ============================================== */



// NB. this script relies on [[User:Ale_jrb/Scripts/waLib.js]].

// the following settings are used in this script:

if (csdhDisplayAlways == null)		var csdhDisplayAlways	= false;		// whether to display CSDH even if the page is not tagged

if (notifyByDefaultDec == null) 	var notifyByDefaultDec	= true;			// whether to check the 'notify tagger' box by default when declining a speedy deletion request

if (notifyByDefaultDel == null) 	var notifyByDefaultDel	= false;		// whether to check the 'notify tagger' box by default when changing a speedy deletion rationale

if (notifyByDefaultPrd == null) 	var notifyByDefaultPrd	= true;			// whether to check the 'notify tagger' box by default when converting a speedy deletion to PROD

if (notifyByDefaultNew == null) 	var notifyByDefaultNew	= true;			// whether to check the 'use newbie message' box by default

if (notifyLimit == null) 			var notifyLimit			= 12;			// how many revisions should be retrieved when determining who tagged the page

if (notifyTemplate == null)			var notifyTemplate		= 'User:Ale_jrb/Scripts/CSDHelper'; // the template that should be substituted for notification messages



if (csdhDoRedirect == null)			var csdhDoRedirect		= true;

if (redirectAfterDel == null) 		var redirectAfterDel	= mw.config.get ( 'wgScript' ) + '?title=Category:Candidates_for_speedy_deletion&action=purge#Pages_in_category'; // where to redirect after deletion

if (myDeleteReasons == null)		var myDeleteReasons		= []; 	// any addition speedy deletion reasons to add to the list



if (logOnDecline == null)			var logOnDecline		= false;

if (logOnDeclinePath == null)		var logOnDeclinePath	= '';

if (overwriteDeclineReasons == null)var overwriteDeclineReasons	= false; 		// whether to overwrite the in-build decline reasons with the user defined ones

if (overwriteDeleteReasons == null)	var overwriteDeleteReasons	= false; 		// whether to overwrite the in-build delete reasons with the user defined ones

if (myDeclineReasons == null)		var myDeclineReasons		= []; 	// any addition speedy deletion decline reasons to add to the list

if (myDeclineListing == null)		var myDeclineListing		= '%CRITERION%: %REASON%'; // the appearance of the option in the drop-down box

if (myDeclineSummary == null)		var myDeclineSummary		= 'Speedy deletion %ACTION%. Criterion %CRITERION% does not apply: %REASON%';	// the summary to use when removing a deletion tag from a page because it has been declined or contested

if (myDeclineSummarySpecial == null)var myDeclineSummarySpecial	= 'Speedy deletion %ACTION%. %REASON%';	// the summary to use when removing a deletion tag from a page IN A SEPCIAL CASE. NOTE: %CRITERION% will be blank!





// Top-level helpers

function isPageTagged() {

	return document.getElementById('delete-criterion') != null;

}



function isPageContested() {

	const contestString = 'speedy deletion of this page is contested.';

	return document.body.innerHTML.indexOf(contestString) > -1;

}





function shouldDisplayCsdh() {

	// Always exclude certain pages

	if (mw.config.get('wgNamespaceNumber') == 10 ||

		document.getElementById('noarticletext') ||

		document.getElementById('newarticletext'))

	{

		return false;

	}



	// Otherwise perform display checks

	return csdhDisplayAlways || isPageTagged() || isPageContested();

}



function launchCsdh() {

	if (shouldDisplayCsdh()) {

		// Launch controller.

		this.control = new csdH_controller;

		this.control.attachLinks();

		return true;

	}

}



// Core

function csdH_controller() {

	csdHController 		= this;

	

	this.notifyExempt	= new Array('SDPatrolBot','Ale jrb 2'); // these editors are exempt from being notified (if they added the tag, notification will fail)

	

	csdHController.deleteRegex = /[\s]*\{\{(?:db|speedy ?delet(?:e|ion)|speedy|d|rm|del(?:ete)? ?(?:because)?|csd|nn|[GARFCUTPX][0-9]{1,2})(?:-(?:.+?))?(?:\|(?:.+?))?\}\}[\s]*/gi;

	csdHController.hangonRegex = /[\s]*\{\{(?:hang|hold)(?: |-)?oo?n(?:\|.+?)?\}\}[\s]*/gi;

	

	csdHController.doNotifyDec = '';

	csdHController.doNotifyDel = '';

	csdHController.doNotifyPrd = ''; 



	if (notifyByDefaultDec == true) csdHController.doNotifyDec = ' checked';

	if (notifyByDefaultDel == true) csdHController.doNotifyDel = ' checked';

	if (notifyByDefaultPrd == true) csdHController.doNotifyPrd = ' checked';

	if (notifyByDefaultNew == true) csdHController.doNotifyNew = ' checked';

	

	if (waUser.isSysop == true) {

		csdHController.isSysop = 'yes';

		csdHController.decAction = 'declined';

	} else {

		csdHController.isSysop = 'no';

		csdHController.decAction = 'contested';

	}

	

	// default arrays - it's fine to edit these, but if you simply wish to add additional decline reasons, use the

	// setting for additional options described above.

	this.declineReasons = new Array(

										'G1', 		'Not nonsense - there is meaningful content'],

										'G2', 		'Not a test page'],

										'G3', 		'Not blatantly vandalism or a hoax'],

										'G4', 		'Not previously been deleted via a deletion discussion'],

										'G5', 		'Not created by a banned user, or the page does not violate the user\'s ban'],

										'G6', 		'Deletion of this page may be controversial or is under discussion'],

										'G7', 		'Author has not requested deletion, or other users have added substantial content'],

										'G8', 		'Does not rely on a page that does not exist'],

										'G9', 		'G9 can only be used at the request of, or by, the Wikimedia Foundation'],

										'G10', 	'Not blatantly an attack page or negative, unsourced BLP'],

										'G11', 	'Not unambiguously promotional'],

										'G12', 	'Not an unambiguous copyright infringement, or there is other content to save'],

										'G13',		'Page is not a draft, or has been edited in the last six months'],

										'G14',		'Page is not a disambiguation page, or disambiguates two or more extant pages'],



										'A1', 		'There is sufficient context to identify the subject of the article'],

										'A2', 		'The article is in English, or does not exist at a foreign project'],

										'A3', 		'Contains sufficient content to be a stub'],

										'A7', 		'The article makes a credible assertion of importance or significance, sufficient to pass A7'],

										'A7', 		'A7 does not apply to schools'],

										'A7', 		'A7 does not apply to software'],

										'A9', 		'The article makes a credible assertion of importance or significance, or is not a musical recording'],

										'A10', 	'Page title is a plausible redirect, or it does not substantially duplicate the other topic'],

										'A11',		'Subject is not obviously invented by the page creator'],

										

										'R2', 		'Does not redirect to a different or incorrect namespace'],

										'R3', 		'Is a plausible, useful redirect or is not a redirect at all'],

										'R3',		'Not a recently created redirect - consider [[WP:RfD]]'],

										'R4', 		'Not the same name as a [[WP:Wikimedia Commons|Commons]] file or redirect'],

										'R4', 		'Has incoming [[Wikipedia:File link|file links]] - consider [[WP:RfD]]'],

										

										'F1',		'File is used, or it is not a lower-quality version of an existing image'],

										'F2',		'File is neither corrupt, missing, or empty'],

										'F3',		'File has a suitable license'],

										'F4',		'File has the necessary licensing information'],

										'F5',		'File is used, or it has a free license'],

										'F6',		'File has a use rationale'],

										'F7',		'File has a valid fair-use claim'],

										'F8',		'File is not identical to the Commons version, or has been marked as "Keep local"'],

										'F9',		'Not an unambiguous copyright infringement'],

										'F11',		'Evidence of permission has been given'],

										

										'C1',		'Category is populated or is otherwise allowed to be empty'],

										

										'U1', 		'Not a user page'],

										'U1', 		'Does not apply to user talk pages'],

										'U2', 		'User does exist, or this is not a user page'],

										'U5',		'Not a blatant misuse of Wikipedia as a webhost'

									);

	this.deleteReasons = new Array(

								   		'N/A', 	'You must select a rationale...'],

										'G1', 		'[[WP:PN|Patent nonsense]], meaningless, or incomprehensible'],

										'G2', 		'Test page'],

										'G3', 		'[[WP:VANDAL|Vandalism]]'],

										'G3', 		'[[WP:VANDAL|Vandalism]] - blatant hoax or misinformation'],

										'G4', 		'Recreation of a page that was [[WP:DEL|deleted]] per a [[WP:XFD|deletion discussion]]'],

										'G5', 		'Creation by a [[WP:BAN|banned]] user in violation of ban'],

										'G6', 		'Housekeeping and routine (non-controversial) cleanup'],

										'G7', 		'One author who has requested deletion or blanked the page'],

										'G8', 		'Page dependent on a deleted or nonexistent page'],

										'G8',		'Talk page of a deleted page'],

										'G10', 	'[[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]] that serves no purpose but to threaten or disparage its subject'],

										'G11', 	'Unambiguous [[WP:PROMO|advertising]] or promotion'],

										'G12', 	'Unambiguous [[WP:C|copyright infringement]]'],

										'G13', 	'Abandoned Draft or [[WP:AFC|Article for creation]] — to retrieve it, see [[WP:REFUND/G13]]'],

										'G14', 	'Disambiguation page that disambiguates only one page and whose title ends in (disambiguation) or disambiguates zero extant pages'],

																				

										'A1', 		'Not enough context to identify article\'s subject'],

										'A2', 		'Article in a foreign language that exists on another project'],

										'A3', 		'Article that has no meaningful, substantive content'],

										'A7', 		'No indication that the article may meet the guidelines for inclusion'],

										'A7',		'Article about a real person, which does not credibly indicate the importance or significance of the subject'],

										'A7',		'Article about a band, singer, musician, or musical ensemble that does not credibly indicate the importance or significance of the subject'],

										'A7',		'Article about a web site, blog, web forum, webcomic, podcast, browser game, or similar web content, which does not credibly indicate the importance or significance of the subject'],

										'A7',		'Article about a company, corporation, organization, or group which does not credibly indicate the importance or significance of the subject'],

										'A7',		'Article about a group or club, which does not credibly indicate the importance or significance of the subject'],

										'A7',		'Article about an organized event, which does not credibly indicate the importance or significance of the subject'],

										'A7',		'Article about an individual animal, which does not credibly indicate the importance or significance of the subject'],

										'A9', 		'Article about a musical recording, which does not credibly indicate the importance or significance of the subject and where the artist has no article'],

										'A10', 	'Article where the only content is already existing in another article and where a redirect to the existing article would be implausible'],

										'A11',     'Article about a subject that was obviously invented by the creator or someone they know and which does not credibly indicate the importance or significance of the subject'],

										

										'R2', 		'Cross-[[WP:NS|namespace]] [[WP:R|redirect]] from mainspace'],

										'R3', 		'Recently-created, implausible [[WP:R|redirect]]'],

										'R4', 		'File [[WP:R|redirect]] with no incoming [[Wikipedia:File link|file links]] and the same name as a file or redirect at [[WP:Wikimedia Commons|Wikimedia Commons]]'],

										

										'F1',		'Redundant file  (also on Wikipedia)'],

										'F2',		'Corrupt or empty file, or a file description page for a file on Commons'],

										'F3',		'File licensed as "for non-commercial use only", "no derivative use", "for Wikipedia use only", "used with permission", or GFDL 1.2 only.'],

										'F4',		'Lack of licensing information'],

										'F5',		'Unused non-free media file for more than 7 days'],

										'F6',		'Non-free media file with no [[WP:FUR|non-free use rationale]]'],

										'F7',		'[[WP:NFCC|Invalid]] fair-use rationale'],

										'F8',		'Media file available on Commons'],

										'F9',		'Media file [[WP:COPYVIO|copyright violation]] without credible claim of [[Wikipedia:Non-free content|fair use]] or permission'],

										'F11',		'No evidence of permission for more than 7 days'],

										

										'C1',		'Empty category'],

										

										'U1', 		'User request to delete pages in own userspace'],

										'U2', 		'Userpage or subpage of a nonexistent user'],

										'U3', 		'[[WP:NFC|Non-free]] [[Help:Gallery|gallery]]'],

										'U5',      'Blatant use of [[WP:NOTWEBHOST|Wikipedia as a web host]] by user with no or very few edits'

									);



	// Handle user defined content...

	// declining

	if (overwriteDeclineReasons == true) {

		this.declineReasons.length = 0;

		this.declineReasons = myDeclineReasons;

	} else {

		this.declineReasons = this.declineReasons.concat(myDeclineReasons);

	}



	// deleting

	if (overwriteDeleteReasons == true) {

		this.deleteReasons.length = 0;

		this.deleteReasons = myDeleteReasons;

	} else {

		this.deleteReasons = this.deleteReasons.concat(myDeleteReasons);

	}



	// append necessary options to decline reasons

	var declineReasonsEnd = new Array(

									  	'INVALID', 		'The reason given is not a valid [[WP:CSD|speedy deletion criterion]]'],	// don't touch this

										'DONTPROVIDE', 	'No reason given.'], 						// don't touch this

										'OTHER', 			'Other - provide your own reason below'	// don't touch this

									);

	this.declineReasons = this.declineReasons.concat(declineReasonsEnd);

	

	

	// GUI

	function getStandardElements() {

		if (!(isPageTagged() || isPageContested())) {

			return '';

		}



		let result =

			'<div style="margin: auto; width: 80%; cursor: pointer; background: #dfdfdf; border: 1px solid #cfcfcf; height: 16px; margin-top: 20px;" onclick="csdHController.declinePage();" onmouseover="this.style.border = \'1px solid #333333\';" onmouseout="this.style.border = \'1px solid #cfcfcf\';">Decline Speedy</div>' +

			'<div style="margin: auto; width: 80%; cursor: pointer; background: #dfdfdf; border: 1px solid #cfcfcf; height: 16px; margin-top:  5px;" onclick="csdHController.prodPage();" onmouseover="this.style.border = \'1px solid #333333\';" onmouseout="this.style.border = \'1px solid #cfcfcf\';">Change to PROD</div>' +

			'';

		

		return result;

	}



	function getSysopElements() {

		if (!waUser.isSysop) {

			return '';

		}



		let result =

			'<div style="margin: auto; width: 95%; cursor: pointer; background: #dfdfdf; border: 1px solid #cfcfcf; height: 22px; margin-top: 20px;" onclick="csdHController.deletePage();" onmouseover="this.style.border = \'1px solid #333333\';" onmouseout="this.style.border = \'1px solid #cfcfcf\';">Delete Page</div>' +

			'';



		return result;

	}



	function getCoreElements() {

		let result =

			'<div>' +

				'<div style="width: 596px; border-bottom: 1px solid #aaaaaa; padding: 2px; font-weight: bold;">Handle Speedy Deletion</div>' +

				'<div style="font-size: 11px; margin-left: 7px;">What do you want to do to this page?</div>' +

				'<div style="font-size: 11px; text-align: center; width: 100%;">' +

					getSysopElements() +

					getStandardElements() +

				'</div>' +

			'</div>';



		return result;

	}



	this.showcsdHWindow = function() {

		// grab position of button

		var offsetL = 0;

		var offsetT = 0;

		var thisObject = document.getElementById('ca-speedy');

		

		if (thisObject.offsetParent) {

			do {

				offsetL += thisObject.offsetLeft;

				offsetT += thisObject.offsetTop;

			} while (thisObject = thisObject.offsetParent);

		}

		

		// build window to show user

		if (this.interface == null) { 

			this.interface = new wa_window(document.getElementById('content'));

			this.visible = true;

			this.csdHelperLink.ele_obj.setAttribute('class', 'selected');



			this.interface.win_content = getCoreElements();

		} else {

			this.interface.win_content = getCoreElements();

			

			if (this.visible == true) { 

				this.csdHelperLink.ele_obj.setAttribute('class', '');

				this.interface.win_disp = 'none'; 

				this.interface.applyAll(); 

				this.visible = false; 

				return true; 

			} else { 

				this.csdHelperLink.ele_obj.setAttribute('class', 'selected');

				this.interface.win_disp = 'block';

				this.interface.win_height = 170;

				this.interface.applyAll();

				this.visible = true; 

				return true;

			}

		}

		

		if ( mw.config.get ( 'skin' ) == 'vector' ) {

			this.interface.win_left = offsetL - 17;

			this.interface.win_top = offsetT + 39;

		} else {

			this.interface.win_left = document.getElementById('ca-speedy').offsetLeft - 17;

			this.interface.win_top = -1;

		}

		this.interface.win_width = 600;

		this.interface.win_height = 170;

		this.interface.win_bg = '#fff';

		this.interface.win_bd = '#aaaaaa';

		this.interface.win_bd_wd = 1;

		this.interface.applyAll();

	}

	

	this.declinePage = function() {

		// build the selection options

		var declineOptions = ''; var optionSelected = false;

		for (var i = 0; i < this.declineReasons.length; i++) {

			var selected = '';

			// determine whether this option should be selected

			if (optionSelected == false) {

				if (document.getElementById('delete-criterion') != null) { // if this matches the criterion provided, select it.

					if (document.getElementById('delete-criterion').innerHTML == this.declineReasonsi][0]) { selected = ' selected '; optionSelected = true; }

					if ( (this.declineReasonsi][0 == 'INVALID') && (optionSelected == false) ) { selected = ' selected '; optionSelected = true; }

				} else { // if no criterion was selected, wait until 'other' is selected.

					if (this.declineReasonsi][0 == 'OTHER') { selected = ' selected '; optionSelected = true; }

				}

			}

			

			// build the visible message for use in the drop-down.

			var tempVisible = myDeclineListing.replace(/%CRITERION%/gi, this.declineReasonsi][0]); tempVisible = tempVisible.replace(/%REASON%/gi, this.declineReasonsi][1]);

			declineOptions += '<option'+selected+'>'+tempVisible+'</option>';

		}

		

		if ( mw.config.get ( 'skin' ) == 'vector' ) { var skinPos = '3'; } else { var skinPos = '10'; }

		this.interface.win_content = ''+

								'<div><div style="width: 596px; border-bottom: 1px solid #aaaaaa; padding: 2px; font-weight: bold;">Decline Speedy Deletion</div>'+

								'<div style="font-size: 11px; margin-left: 7px; padding: 0px;">Select the reason for declining the deletion from the list. This text will be used as the edit summary.</div>'+

								'<select id="declineReason" style="font-size: 11px; margin-left: 9px;" onchange="document.getElementById(\'declineText\').value = \'\'">'+declineOptions+'</select>'+

								'<div style="font-size: 11px; margin-left: 7px; margin-top: 8px; padding: 0px;">Or provide your own reason and summary below.</div>'+

								'<input id="declineText" style="margin-left: 9px; font-size: 11px; width: 576px;" type="text" onkeyup="document.getElementById(\'declineReason\').selectedIndex = csdHController.declineReasons.length-1;" />'+

								'<div style="margin-top: 13px; float: right; margin-right: 300px; font-size: 11px; ">-- <a href="#" onclick="csdHController.declineDo();">decline speedy deletion</a> --</div>'+

								'<div style="margin-top: '+skinPos+'px; font-size: 11px; margin-left: 20px; vertical-align: middle; padding: 0px;">notify tagger <input id="notifyTagger"'+csdHController.doNotifyDec+' style="position: relative; top: 3px; " type="checkbox" /></div>'+

								'<div style="margin-top: '+skinPos+'px; font-size: 11px; margin-left: 20px; vertical-align: middle; padding: 0px;">use newbie message <input id="notifyNewbie"'+csdHController.doNotifyNew+' style="position: relative; top: 3px; " type="checkbox" /></div>'+

								'</div>'+

								'';

		this.interface.applyAll();

	}

	this.declineDo = function(callback) {

		// get page content

		if (!callback) var callback = 0;

		switch (callback) {

			default:

				// main vars

				csdHController.declineText = 	document.getElementById('declineText').value;

				csdHController.declineSel = 	document.getElementById('declineReason').selectedIndex;

				csdHController.declineNotify = 	document.getElementById('notifyTagger').checked;

				csdHController.notifyNewbie = 	document.getElementById('notifyNewbie').checked;

			

				if ((csdHController.declineText == '') && (csdHController.declineSel == csdHController.declineReasons.length-1)) {

					// if no reason is typed, byt 'Other' is selected, use the 'No reason provided' option.

					csdHController.declineSel = csdHController.declineReasons.length - 2;

				}

				

				csdHController.declineCategory 	= 	csdHController.declineReasonscsdHController.declineSel][0];

				csdHController.declineReason 	=	csdHController.declineReasonscsdHController.declineSel][1];

				

				// build all messages

				if ( (csdHController.declineCategory == 'INVALID') || (csdHController.declineCategory == 'DONTPROVIDE') ) { // if it's a 'special' case, use the 'special' summary

					var tempSummary = myDeclineSummarySpecial;

					tempSummary = tempSummary.replace(/%ACTION%/gi, csdHController.decAction); tempSummary = tempSummary.replace(/%REASON%/gi, csdHController.declineReason);

					csdHController.editSummary = tempSummary;

				} else if (csdHController.declineCategory == 'OTHER') { // if they've typed a reason, use that

					var tempSummary = myDeclineSummarySpecial;

					tempSummary = tempSummary.replace(/%ACTION%/gi, csdHController.decAction); tempSummary = tempSummary.replace(/%REASON%/gi, csdHController.declineText);

					csdHController.editSummary = tempSummary;

				} else { // otherwise, use the 'normal' summary

					var tempSummary = myDeclineSummary;

					tempSummary = tempSummary.replace(/%ACTION%/gi, csdHController.decAction); tempSummary = tempSummary.replace(/%CRITERION%/gi, csdHController.declineCategory); tempSummary = tempSummary.replace(/%REASON%/gi, csdHController.declineReason);

					csdHController.editSummary = tempSummary;

				}

				csdHController.editSummary += ' ([[User:Ale_jrb/Scripts|CSDH]])';

				

				// start

				this.interface.win_content = ''+

								'<div><div style="width: 596px; border-bottom: 1px solid #aaaaaa; padding: 2px; font-weight: bold;">Working...</div>'+

								'<div style="font-size: 11px; margin-left: 7px;">Please wait while CSDHelper performs the requested operations. This can take several seconds.</div>'+

								'<div id="workingStatus" style="font-size: 11px; margin-left: 15px;">- Retrieving existing content...<br /></div>'+

								'</div>'+

								'';

				

				this.interface.applyAll();

			

				csdHController.pageReq = new wa_mediawikiApi();

				csdHController.pageReq.onCompleteAction = function() { csdHController.declineDo('1'); };

				csdHController.pageReq.getPage(mw.config.get('wgPageName'), notifyLimit, 'user|content');

				break;

				

			case '1':

				document.getElementById('workingStatus').innerHTML += '- Removing tags...<br />';

				csdHController.pageContent = csdHController.pageReq.data'page']['revisions'][0]['content'];

				

				var regReplace = csdHController.deleteRegex;

				csdHController.pageContent = csdHController.pageContent.replace(regReplace, '');

				var regReplace = csdHController.hangonRegex;

				csdHController.pageContent = csdHController.pageContent.replace(regReplace, '');

				csdHController.newContent = csdHController.pageContent; // grab a record of that

				

				document.getElementById('workingStatus').innerHTML += '- Updating page...<br />';

				

				csdHController.editReq = new wa_mediawikiApi();

				csdHController.editReq.onCompleteAction = function() { csdHController.declineDo('2'); };

				csdHController.editReq.editPage(mw.config.get('wgPageName'), csdHController.newContent, csdHController.editSummary, false, 'text');

				break;

				

			case '2':

				// check for notify

				if ( (csdHController.declineNotify == true) || (logOnDecline == true) ) {

					document.getElementById('workingStatus').innerHTML += '- Determining user who tagged page...<br />';

					

					var tags = 0; csdHController.tagger = '';

					var revisionCount = csdHController.pageReq.data'page']['revisions'].length;

					var limit = Math.min(notifyLimit, revisionCount);

					for (var i = 0; i < limit; i++) {

						var thisPage = csdHController.pageReq.data'page']['revisions'][i]['content'];

						

						// count tags

						var regTest = csdHController.deleteRegex;

						var k = 1; var count = 0;

						while (k != null) {

							k = regTest.exec(thisPage);

							if (k != null) count++;

						}

						regTest.lastIndex = 0;

						

						// check if we have fewer than last time (1 was added on that rev)

						if (count < tags) {

							csdHController.tagger = csdHController.pageReq.data'page']['revisions'][i - 1]['user'];

							break;

						} else {

							tags = count;

						}

					}

					if (csdHController.tagger == '') { document.getElementById('workingStatus').innerHTML += '- Could not determine tagger: will not log or notify. Finished.<br />'; break; }

					

					for (var i = 0; i < csdHController.notifyExempt.length; i++) {

						if (csdHController.tagger == csdHController.notifyExempti]) {

							document.getElementById('workingStatus').innerHTML += '- Tagger ('+csdHController.tagger+') is on the tagger exempt list, and will not be notified. Finished.<br />';

							if (logOnDecline == true) { csdHController.declineNotify = false; csdHController.declineDo('3');  } else { return true; }

						}

					}

					

					if ( (logOnDecline == true) && (logOnDeclinePath != '') ) { csdHController.declineDo('3'); } else { csdHController.declineDo('5'); }

					

				} else { window.location.reload(true); csdHController.showcsdHWindow(); }

				break;

				

			case '3':

				// log decline action where relevant

				document.getElementById('workingStatus').innerHTML += '- Logging decline action to \''+logOnDeclinePath+'\' - retrieving page... ';

				

				csdHController.pageReq = new wa_mediawikiApi();

				csdHController.pageReq.onCompleteAction = function() { csdHController.declineDo('4'); };

				csdHController.pageReq.getPage(logOnDeclinePath, 1, 'content');

				

				break;

				

			case '4':

				// we have retrieved the data regarding the log page; move to edit it

				document.getElementById('workingStatus').innerHTML += 'modifying page...<br />';

				//var logOnDeclinePath = logOnDeclinePath.replace(/ /g,'_');

				

				// check whether there is existing content

				var pageData = csdHController.pageReq.data'page'];

				if (pageData'status' == 'OK') {

					var oldContent = pageData'revisions'][0]['content'];

					if ( oldContent === '' ) oldContent = "{| class=\"sortable wikitable\" style=\"font-size: 80%;\" border=\"2\" cellpadding=\"1\" background:#f9f9f9;\"|\n|-\n! style=\"text-align: left\" | Article\n! Tagger\n! Criterion\n! Decline reason\n! Date\n|}";

				} else {

					var oldContent = "{| class=\"sortable wikitable\" style=\"font-size: 80%;\" border=\"2\" cellpadding=\"1\" background:#f9f9f9;\"|\n|-\n! style=\"text-align: left\" | Article\n! Tagger\n! Criterion\n! Decline reason\n! Date\n|}";

				}

				

				// message

				var pageName = mw.config.get('wgPageName').replace(/_/g, ' ');

				var reason = (csdHController.declineCategory == 'OTHER') ? csdHController.declineText : csdHController.declineReason;

				var crit = document.getElementById('delete-criterion').innerHTML;

				var message = "|-\n| [[:" + pageName + "]] || [[User:" + csdHController.tagger + "|" + csdHController.tagger + "]] || [[WP:CSD#" + crit + "|CSD " + crit + "]] || " +reason + " || " + "~~" + "~~" + "~\n";

				

				// add the new row to the table

				var newContent = oldContent.replace('|}', message + '|}');

				

				// build vars

				var editSummary = 'Adding [[:' + pageName + ']] to speedy decline log ([[User:Ale_jrb/Scripts|CSDH]])';

				

				// perform the edit

				csdHController.editReq = new wa_mediawikiApi();

				csdHController.editReq.onCompleteAction = function() { csdHController.declineDo('5'); };

				csdHController.editReq.editPage(logOnDeclinePath, newContent, editSummary, true, 'text');

				break;

				

			case '5':

				// check notify

				if (csdHController.declineNotify != true) { window.location.reload(true); csdHController.showcsdHWindow(); break; }

			

				// output

				document.getElementById('workingStatus').innerHTML += '- Tagged by \''+csdHController.tagger+'\' - notifying user...<br />';

			

				// edit summary

				csdHController.editSummary = 'Notifying about '+csdHController.decAction+' speedy deletion ([[User:Ale_jrb/Scripts|CSDH]])';

				

				// decide whether to use newbie message

				if (csdHController.notifyNewbie) { var useNewbie = 'yes'; } else { var useNewbie = 'no'; }

				

				// fix message - handle other special case

				if (csdHController.declineCategory == 'OTHER') csdHController.declineReason = csdHController.declineText;

				// fix message - handle punctuation at the end of the message (it must be added if absent).

				var p = csdHController.declineReason.substr(csdHController.declineReason.length - 1);

				if ( (p != '.') && (p != '!') && (p != '?') ) csdHController.declineReason += '.';

				

				// message

				var message = '== Speedy deletion '+csdHController.decAction+': [[:'+mw.config.get('wgPageName').replace(/_/g, ' ')+']] =='+"\n"+'{{subst:'+notifyTemplate+'|action=decline|page='+mw.config.get('wgPageName').replace(/_/g, ' ')+'|tagger='+csdHController.tagger+'|declinetext='+csdHController.declineReason+'|admin='+csdHController.isSysop+'|newbie='+useNewbie+'}} ~~'+'~~';

				

				csdHController.saveReq = new wa_mediawikiApi();

				csdHController.saveReq.onCompleteAction = function() { window.location.reload(true); csdHController.showcsdHWindow(); };

				csdHController.saveReq.editPage('User_talk:'+csdHController.tagger, message, csdHController.editSummary, false, 'appendtext');

				break;

			

		}

		

	}

	

	this.prodPage = function() {

		var prodOptions = '';

		

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

		var regTest = /criteria for speedy deletion<\/a><\/i> because (.*?)\.<\/b> <i>For valid criteria,/i

		var rationale = regTest.exec(content);

		if (rationale != null) { rationale = rationale1]; } else { rationale = ''; }

		

		this.interface.win_content = ''+

								'<div><div style="width: 596px; border-bottom: 1px solid #aaaaaa; padding: 2px; font-weight: bold;">Convert to PROD</div>'+

								'<div style="font-size: 11px; margin-left: 7px;">You are asserting that the page cannot be speedy deleted, but <em>can</em> be deleted by proposed deletion. '+

								'Enter the reason for deletion below - if the tagger provided one, it will be filled automatically.</div>'+

								'<input id="prodReason" style="margin-left: 9px; font-size: 11px; width: 576px;" type="text" value="'+rationale+'" />'+

								'<div id="notifyTaggerDiv" style="margin-top: 10px; font-size: 11px; margin-left: 20px; float: left; vertical-align: middle;">notify tagger of conversion <input id="notifyTagger"'+csdHController.doNotifyPrd+' style="position: relative; top: 3px; " type="checkbox" /><br />'+

								'use newbie message <input id="notifyNewbie"'+csdHController.doNotifyNew+' style="position: relative; top: 3px; " type="checkbox" /></div>'+

								'<div style="margin: auto; margin-top: 13px; font-size: 11px; width: 200px; text-align: center;">-- <a href="#" onclick="csdHController.prodDo();">convert to PROD</a> --</div>'+

								'</div>'+

								'';

		this.interface.applyAll();

	}

	this.prodDo = function(callback) {

		if (!callback) var callback = 0;

		switch (callback) {

			default:

				csdHController.prodReason = document.getElementById('prodReason').value;

				csdHController.prodNotify = document.getElementById('notifyTagger').checked;

				csdHController.notifyNewbie = document.getElementById('notifyNewbie').checked;

				

				this.interface.win_content = ''+

								'<div><div style="width: 596px; border-bottom: 1px solid #aaaaaa; padding: 2px; font-weight: bold;">Working...</div>'+

								'<div style="font-size: 11px; margin-left: 7px;">Please wait while CSDHelper performs the requested operations. This can take several seconds.</div>'+

								'<div id="workingStatus" style="font-size: 11px; margin-left: 15px;"></div>'+

								'</div>'+

								'';

				

				this.interface.applyAll();

			

				if (csdHController.prodNotify == true) {

					document.getElementById('workingStatus').innerHTML += '- Searching for tagger...<br />';

					

					if (notifyLimit > 15) notifyLimit = 15;

					csdHController.pageReq = new wa_mediawikiApi();

					csdHController.pageReq.onCompleteAction = function() { csdHController.prodDo('1'); };

					csdHController.pageReq.getPage(mw.config.get('wgPageName'), notifyLimit + 1, 'content|user');

				} else { 

					csdHController.pageReq = new wa_mediawikiApi();

					csdHController.pageReq.onCompleteAction = function() { csdHController.prodDo('3'); };

					csdHController.pageReq.getPage(mw.config.get('wgPageName'), 1, 'content|user');

				}

				break;

				

			case '1':

				var tags = 0; csdHController.tagger = '';

				

				var revisionCount = csdHController.pageReq.data'page']['revisions'].length;

				var limit = Math.min(notifyLimit, revisionCount);

				for (var i = 0; i < limit; i++) {

					var thisPage = csdHController.pageReq.data'page']['revisions'][i]['content'];

					

					// count tags

					var regTest = csdHController.deleteRegex;

					var k = 1; var count = 0;

					while (k != null) {

						k = regTest.exec(thisPage);

						if (k != null) count ++;

					}

					regTest.lastIndex = 0;

					

					// check if we have fewer than last time (1 was added on that rev)

					if (count < tags) {

						csdHController.tagger = csdHController.pageReq.data'page']['revisions'][i - 1]['user'];

						break;

					} else {

						tags = count;

					}

				}

				

				if (csdHController.tagger == '') { document.getElementById('workingStatus').innerHTML += '- Could not determine tagger. Moving to tag page...<br />'; csdHController.prodDo('3'); break; }

				

				for (var i = 0; i < csdHController.notifyExempt.length; i ++) {

					if (csdHController.tagger == csdHController.notifyExempti]) {

						document.getElementById('workingStatus').innerHTML += '- Tagger ('+csdHController.tagger+') is on the tagger exempt list, and will not be notified. Moving to tag page...<br />';

						csdHController.prodDo('3');

						return true;

					}

				}

				

				csdHController.prodDo('2');

				break;

				

			case '2': // notify tagger

				document.getElementById('workingStatus').innerHTML += '- Tagged by \''+csdHController.tagger+'\' - notifying user...<br />';

			

				// edit summary

				csdHController.editSummary = 'Notifying about speedy deletion converted to PROD ([[User:Ale_jrb/Scripts|CSDH]])';

				

				// decide whether to use newbie message

				if (csdHController.notifyNewbie) { var useNewbie = 'yes'; } else { var useNewbie = 'no'; }

				

				// message

				var message = '== Speedy deletion converted to PROD: [[:'+mw.config.get('wgPageName').replace(/_/g, ' ')+']] =='+"\n"+'{{subst:'+notifyTemplate+'|action=convert|page='+mw.config.get('wgPageName').replace(/_/g, ' ')+'|tagger='+csdHController.tagger+'|admin='+csdHController.isSysop+'|newbie='+useNewbie+'}} ~~'+'~~';

				

				csdHController.notifyReq = new wa_mediawikiApi();

				csdHController.notifyReq.onCompleteAction = function() { csdHController.prodDo('3'); };

				csdHController.notifyReq.editPage('User_talk:'+csdHController.tagger, message, csdHController.editSummary, false, 'appendtext');

				break;

				

			case '3':

				document.getElementById('workingStatus').innerHTML += '- Converting tags...<br />';

				csdHController.pageContent = csdHController.pageReq.data'page']['revisions'][0]['content'];

				

				var regReplace = csdHController.deleteRegex;

				csdHController.pageContent = csdHController.pageContent.replace(regReplace, '');

				var regReplace = csdHController.hangonRegex;

				csdHController.pageContent = csdHController.pageContent.replace(regReplace, '');

				

				if (csdHController.prodReason != 'nn') { csdHController.pageContent = '{'+'{subst:prod|'+csdHController.prodReason+'}'+'}\n' + csdHController.pageContent; } else {

					csdHController.pageContent = '{'+'{subst:prod-nn}'+'}\n' + csdHController.pageContent; }

				

				csdHController.newContent = csdHController.pageContent; // grab a record of that

				csdHController.editSummary = 'Speedy deletion converted to PROD ([[User:Ale_jrb/Scripts|CSDH]])';

				

				document.getElementById('workingStatus').innerHTML += '- Updating page...<br />';

				

				csdHController.editReq = new wa_mediawikiApi();

				csdHController.editReq.onCompleteAction = function() { window.location.reload(true); csdHController.showcsdHWindow(); };

				csdHController.editReq.editPage(mw.config.get('wgPageName'), csdHController.newContent, csdHController.editSummary, false, 'text');

				break;

		}

	}

	

	this.deletePage = function() {

		// build the selection options

		var deleteOptions = ''; var optionSelected = false;

		for (var i = 0; i < this.deleteReasons.length; i++) {

			var selected = '';

			if ( (document.getElementById('delete-criterion') != null) && (optionSelected == false) ) { 

				if (document.getElementById('delete-criterion').innerHTML == this.deleteReasonsi][0]) { csdHController.initialRationale = this.deleteReasonsi][0]; selected = ' selected'; optionSelected = true; }

			} else {

				if (this.deleteReasonsi][0 == 'N/A') { selected = ' selected '; optionSelected = true; }

			}

			

			// general

			var visibleReasoning = this.deleteReasonsi][1].replace(/\[\[(?:.*?\|)(.+?)\]\]/g, '$1');

			deleteOptions += '<option value="'+i+'"'+selected+'>'+this.deleteReasonsi][0+': '+visibleReasoning+'</option>';

		}

		

		if (document.getElementById('delete-criterion') == null) { var displayTagCommand = 'display: none;'; } else { var displayTagCommand = 'display: block;'; }

		

		this.interface.win_content = ''+

								'<div><div style="width: 596px; border-bottom: 1px solid #aaaaaa; padding: 2px; font-weight: bold;">Perform Speedy Deletion</div>'+

								'<div style="font-size: 11px; margin-left: 7px;">Select the reason for deletion from the list. The rationale specified by the tagger is automatically selected. If you select a reason that does not '+

								'match the tag, will be given the option of notifying the tagger.</div>'+

								'<select id="deleteReason" style="font-size: 11px; margin-left: 9px; width: 576px;" onchange="'+

									'if (document.getElementById(\'delete-criterion\') != null) {'+

										'if (csdHController.deleteReasons[document.getElementById(\'deleteReason\').selectedIndex][0] != document.getElementById(\'delete-criterion\').innerHTML) { '+

											'if (csdHController.deleteReasons[document.getElementById(\'deleteReason\').selectedIndex][0] == \'N/A\') { document.getElementById(\'notifyTaggerDiv\').style.display = \'none\'; } else { document.getElementById(\'notifyTaggerDiv\').style.display = \'block\'; }'+

										'} else {'+

											'document.getElementById(\'notifyTaggerDiv\').style.display = \'none\'; '+

										'}'+

									'} else { document.getElementById(\'notifyTaggerDiv\').style.display = \'none\'; }'+

									

									'if (csdHController.deleteReasons[document.getElementById(\'deleteReason\').selectedIndex][0] == \'N/A\') {'+

										'document.getElementById(\'performDeletionDiv\').style.display = \'none\'; '+

									'} else {'+

										'document.getElementById(\'performDeletionDiv\').style.display = \'block\'; '+

									'}'+

								'">'+deleteOptions+'</select>'+

								'<div id="notifyTaggerDiv" style="display: none; margin-top: 10px; font-size: 11px; margin-left: 20px; float: left; vertical-align: middle;">notify tagger of rationale change <input id="notifyTagger"'+csdHController.doNotifyDel+' style="position: relative; top: 3px; " type="checkbox" /><br />'+

								'use newbie message <input id="notifyNewbie"'+csdHController.doNotifyNew+' style="position: relative; top: 3px; " type="checkbox" /></div>'+

								'<div id="performDeletionDiv" style="margin: auto; margin-top: 13px; font-size: 11px; width: 200px; text-align: center; '+displayTagCommand+'">-- <a href="#" onclick="csdHController.deleteDo();">perform speedy deletion</a> --</div>'+

								'</div>'+

								'';

		this.interface.applyAll();

	}

	this.deleteDo = function(callback) {

		if (!callback) var callback = 0;

		switch (callback) {

			default:

				csdHController.deleteSel 		= document.getElementById('deleteReason').selectedIndex;

				csdHController.deleteNotify 	= document.getElementById('notifyTagger').checked;

				csdHController.notifyNewbie		= document.getElementById('notifyNewbie').checked;

				if (document.getElementById('delete-criterion') == null) {

					csdHController.allowDelNotify = false;

				} else { csdHController.allowDelNotify = (csdHController.deleteReasonscsdHController.deleteSel][0 != document.getElementById('delete-criterion').innerHTML); }

				

				this.interface.win_content = ''+

								'<div><div style="width: 596px; border-bottom: 1px solid #aaaaaa; padding: 2px; font-weight: bold;">Working...</div>'+

								'<div style="font-size: 11px; margin-left: 7px;">Please wait while CSDHelper performs the requested operations. This can take several seconds.</div>'+

								'<div id="workingStatus" style="font-size: 11px; margin-left: 15px;"></div>'+

								'</div>'+

								'';

				

				this.interface.applyAll();

			

				if ((csdHController.deleteNotify == true) && (csdHController.allowDelNotify == true)) {

					document.getElementById('workingStatus').innerHTML += '- Searching for tagger...<br />';

					

					if (notifyLimit > 15) notifyLimit = 15;

					csdHController.pageReq = new wa_mediawikiApi();

					csdHController.pageReq.onCompleteAction = function() { csdHController.deleteDo('1'); };

					csdHController.pageReq.getPage(mw.config.get('wgPageName'), notifyLimit + 1, 'content|user');

				} else { csdHController.deleteDo('3'); }

				break;

				

			case '1':

				var tags = 0; csdHController.tagger = '';

				

				var revisionCount = csdHController.pageReq.data'page']['revisions'].length;

				var limit = Math.min(notifyLimit, revisionCount);

				for (var i = 0; i < limit; i++) {

					var thisPage = csdHController.pageReq.data'page']['revisions'][i]['content'];

					

					// count tags

					var regTest = csdHController.deleteRegex;

					var k = 1; var count = 0;

					while (k != null) {

						k = regTest.exec(thisPage);

						if (k != null) count ++;

					}

					regTest.lastIndex = 0;

					

					// check if we have fewer than last time (1 was added on that rev)

					if (count < tags) {

						csdHController.tagger = csdHController.pageReq.data'page']['revisions'][i - 1]['user'];

						break;

					} else {

						tags = count;

					}

				}

				

				if (csdHController.tagger == '') { document.getElementById('workingStatus').innerHTML += '- Could not determine tagger. Moving to deletion...<br />'; csdHController.deleteDo('3'); break; }

				

				for (var i = 0; i < csdHController.notifyExempt.length; i ++) {

					if (csdHController.tagger == csdHController.notifyExempti]) {

						document.getElementById('workingStatus').innerHTML += '- Tagger ('+csdHController.tagger+') is on the tagger exempt list, and will not be notified. Moving to deletion...<br />';

						csdHController.deleteDo('3');

						return true;

					}

				}

				

				csdHController.deleteDo('2');

				break;

				

			case '2': // notify tagger

				document.getElementById('workingStatus').innerHTML += '- Tagged by \''+csdHController.tagger+'\' - notifying user...<br />';

			

				// edit summary

				csdHController.editSummary = 'Notifying about altered speedy deletion rationale ([[User:Ale_jrb/Scripts|CSDH]])';

				

				// decide whether to use newbie message

				if (csdHController.notifyNewbie) { var useNewbie = 'yes'; } else { var useNewbie = 'no'; }

				

				// message

				var message = '== Altered speedy deletion rationale: [[:'+mw.config.get('wgPageName').replace(/_/g, ' ')+']] =='+"\n"+'{{subst:'+notifyTemplate+'|action=change|page='+mw.config.get('wgPageName').replace(/_/g, ' ')+'|tagger='+csdHController.tagger+'|newbie='+useNewbie+'}} ~~'+'~~';

				

				csdHController.notifyReq = new wa_mediawikiApi();

				csdHController.notifyReq.onCompleteAction = function() { csdHController.deleteDo('3'); };

				csdHController.notifyReq.editPage('User_talk:'+csdHController.tagger, message, csdHController.editSummary, false, 'appendtext');

				break;

				

			case '3':

				// perform the edit

				document.getElementById('workingStatus').innerHTML += '- Deleting page...<br />';

				var deleteReason = '[[WP:CSD#'+csdHController.deleteReasonscsdHController.deleteSel][0+'|'+csdHController.deleteReasonscsdHController.deleteSel][0+']]: ' + csdHController.deleteReasonscsdHController.deleteSel][1 + ' ([[User:Ale_jrb/Scripts|CSDH]])';

				

				csdHController.deleteReq = new wa_mediawikiApi();

				csdHController.deleteReq.onCompleteAction = function(callback) { 

					if ((callback == null) || (callback == false) || (mw.config.get('wgNamespaceNumber') != 0)) {

						// No talk page

						if (csdhDoRedirect) {

							window.location = redirectAfterDel;

						} else {

							window.location.reload(true);

						}

						csdHController.showcsdHWindow(); 

					} else {

						// Talk page exists

						csdHController.talkpageId = callback;

						document.getElementById('workingStatus').innerHTML = ''+

							'<strong>Warning: this page now has an orphaned talk page. Do you wish to delete it under G8?</strong><br /><br />'+

							'<div style="width: 100%; text-align: center;"><a href="#" onclick="document.getElementById(\'workingStatus\').innerHTML = \'- Deleting talk page...<br />\'; csdHController.deleteDo(\'4\');">Delete</a> | <a href="#" onclick="window.location = redirectAfterDel; csdHController.showcsdHWindow(); ">Ignore</a></div>'+

						'';

					}

				};

				csdHController.deleteReq.performAction('delete', mw.config.get('wgPageName'), deleteReason);

				break;

			

			case '4':

				// delete the talk page - get the title

				var talkPage = 'Talk:' + mw.config.get('wgPageName');

				

				csdHController.deleteReq = new wa_mediawikiApi();

				csdHController.deleteReq.onCompleteAction = function() { window.location = redirectAfterDel; csdHController.showcsdHWindow(); };

				csdHController.deleteReq.performAction('delete', talkPage, '[[WP:CSD#G8|G8]]: Talk page of deleted page. ([[User:Ale_jrb/Scripts|CSDH]])');

				break;

		}

	}

	

	this.displayError = function() {

		this.interface.win_content = ''+

								'<div><div style="width: 596px; border-bottom: 1px solid #aaaaaa; padding: 2px; font-weight: bold;">An error occurred...</div>'+

								'</div>'+

								'';

		this.interface.applyAll();

	}

	

	this.attachLinks = function() {

		this.csdHelperLink 						= new wa_element('li');

		this.csdHelperLink.ele_obj.id			= 'ca-speedy';

		this.csdHelperLink.addScriptEvent('click', function() { csdHController.showcsdHWindow(); });

		

		if ( mw.config.get ( 'skin' ) == 'vector' ) {

			this.csdHelperLink.ele_obj.innerHTML	= '<span><a href="#" title="handle speedy deletion">Speedy</a></span>';

			this.csdHelperLink.attach(document.getElementById('ca-talk'), 'after');

		} else {

			this.csdHelperLink.ele_obj.innerHTML	= '<a href="#" title="handle speedy deletion">speedy</a>';

			this.csdHelperLink.attach(document.getElementById('ca-move'), 'before');

		}

	};

}







// -- run program

function launchCsdHelper() {

	// lib proto

	wa_window.prototype = new wa_document;

	wa_element.prototype = new wa_document;

	

	// run

	launchCsdh();

}



$.getScript("https://en.wikipedia.org/?title=User:Ale_jrb/Scripts/waLib.js&type=text/javascript&action=raw", function() {

	$(document).ready(launchCsdHelper);

});
From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.

/* ============================================== *\

** CSD Helper - JavaScript CSD Script

**   for Wikipedia

**

** Created by Alex Barley [[User:Ale_jrb]]

**		Tracker: [[User:Ale_jrb/Scripts]]

**

**	You are advised to import this script to your

** monobook.js page - AVOID CREATING YOUR OWN 

** VERSION WHERE POSSIBLE.

**

**	Instructions for this script can be found at

** [[User:Ale_jrb/Scripts]] - refer to this for

** setting details.

\* ============================================== */



// NB. this script relies on [[User:Ale_jrb/Scripts/waLib.js]].

// the following settings are used in this script:

if (csdhDisplayAlways == null)		var csdhDisplayAlways	= false;		// whether to display CSDH even if the page is not tagged

if (notifyByDefaultDec == null) 	var notifyByDefaultDec	= true;			// whether to check the 'notify tagger' box by default when declining a speedy deletion request

if (notifyByDefaultDel == null) 	var notifyByDefaultDel	= false;		// whether to check the 'notify tagger' box by default when changing a speedy deletion rationale

if (notifyByDefaultPrd == null) 	var notifyByDefaultPrd	= true;			// whether to check the 'notify tagger' box by default when converting a speedy deletion to PROD

if (notifyByDefaultNew == null) 	var notifyByDefaultNew	= true;			// whether to check the 'use newbie message' box by default

if (notifyLimit == null) 			var notifyLimit			= 12;			// how many revisions should be retrieved when determining who tagged the page

if (notifyTemplate == null)			var notifyTemplate		= 'User:Ale_jrb/Scripts/CSDHelper'; // the template that should be substituted for notification messages



if (csdhDoRedirect == null)			var csdhDoRedirect		= true;

if (redirectAfterDel == null) 		var redirectAfterDel	= mw.config.get ( 'wgScript' ) + '?title=Category:Candidates_for_speedy_deletion&action=purge#Pages_in_category'; // where to redirect after deletion

if (myDeleteReasons == null)		var myDeleteReasons		= []; 	// any addition speedy deletion reasons to add to the list



if (logOnDecline == null)			var logOnDecline		= false;

if (logOnDeclinePath == null)		var logOnDeclinePath	= '';

if (overwriteDeclineReasons == null)var overwriteDeclineReasons	= false; 		// whether to overwrite the in-build decline reasons with the user defined ones

if (overwriteDeleteReasons == null)	var overwriteDeleteReasons	= false; 		// whether to overwrite the in-build delete reasons with the user defined ones

if (myDeclineReasons == null)		var myDeclineReasons		= []; 	// any addition speedy deletion decline reasons to add to the list

if (myDeclineListing == null)		var myDeclineListing		= '%CRITERION%: %REASON%'; // the appearance of the option in the drop-down box

if (myDeclineSummary == null)		var myDeclineSummary		= 'Speedy deletion %ACTION%. Criterion %CRITERION% does not apply: %REASON%';	// the summary to use when removing a deletion tag from a page because it has been declined or contested

if (myDeclineSummarySpecial == null)var myDeclineSummarySpecial	= 'Speedy deletion %ACTION%. %REASON%';	// the summary to use when removing a deletion tag from a page IN A SEPCIAL CASE. NOTE: %CRITERION% will be blank!





// Top-level helpers

function isPageTagged() {

	return document.getElementById('delete-criterion') != null;

}



function isPageContested() {

	const contestString = 'speedy deletion of this page is contested.';

	return document.body.innerHTML.indexOf(contestString) > -1;

}





function shouldDisplayCsdh() {

	// Always exclude certain pages

	if (mw.config.get('wgNamespaceNumber') == 10 ||

		document.getElementById('noarticletext') ||

		document.getElementById('newarticletext'))

	{

		return false;

	}



	// Otherwise perform display checks

	return csdhDisplayAlways || isPageTagged() || isPageContested();

}



function launchCsdh() {

	if (shouldDisplayCsdh()) {

		// Launch controller.

		this.control = new csdH_controller;

		this.control.attachLinks();

		return true;

	}

}



// Core

function csdH_controller() {

	csdHController 		= this;

	

	this.notifyExempt	= new Array('SDPatrolBot','Ale jrb 2'); // these editors are exempt from being notified (if they added the tag, notification will fail)

	

	csdHController.deleteRegex = /[\s]*\{\{(?:db|speedy ?delet(?:e|ion)|speedy|d|rm|del(?:ete)? ?(?:because)?|csd|nn|[GARFCUTPX][0-9]{1,2})(?:-(?:.+?))?(?:\|(?:.+?))?\}\}[\s]*/gi;

	csdHController.hangonRegex = /[\s]*\{\{(?:hang|hold)(?: |-)?oo?n(?:\|.+?)?\}\}[\s]*/gi;

	

	csdHController.doNotifyDec = '';

	csdHController.doNotifyDel = '';

	csdHController.doNotifyPrd = ''; 



	if (notifyByDefaultDec == true) csdHController.doNotifyDec = ' checked';

	if (notifyByDefaultDel == true) csdHController.doNotifyDel = ' checked';

	if (notifyByDefaultPrd == true) csdHController.doNotifyPrd = ' checked';

	if (notifyByDefaultNew == true) csdHController.doNotifyNew = ' checked';

	

	if (waUser.isSysop == true) {

		csdHController.isSysop = 'yes';

		csdHController.decAction = 'declined';

	} else {

		csdHController.isSysop = 'no';

		csdHController.decAction = 'contested';

	}

	

	// default arrays - it's fine to edit these, but if you simply wish to add additional decline reasons, use the

	// setting for additional options described above.

	this.declineReasons = new Array(

										'G1', 		'Not nonsense - there is meaningful content'],

										'G2', 		'Not a test page'],

										'G3', 		'Not blatantly vandalism or a hoax'],

										'G4', 		'Not previously been deleted via a deletion discussion'],

										'G5', 		'Not created by a banned user, or the page does not violate the user\'s ban'],

										'G6', 		'Deletion of this page may be controversial or is under discussion'],

										'G7', 		'Author has not requested deletion, or other users have added substantial content'],

										'G8', 		'Does not rely on a page that does not exist'],

										'G9', 		'G9 can only be used at the request of, or by, the Wikimedia Foundation'],

										'G10', 	'Not blatantly an attack page or negative, unsourced BLP'],

										'G11', 	'Not unambiguously promotional'],

										'G12', 	'Not an unambiguous copyright infringement, or there is other content to save'],

										'G13',		'Page is not a draft, or has been edited in the last six months'],

										'G14',		'Page is not a disambiguation page, or disambiguates two or more extant pages'],



										'A1', 		'There is sufficient context to identify the subject of the article'],

										'A2', 		'The article is in English, or does not exist at a foreign project'],

										'A3', 		'Contains sufficient content to be a stub'],

										'A7', 		'The article makes a credible assertion of importance or significance, sufficient to pass A7'],

										'A7', 		'A7 does not apply to schools'],

										'A7', 		'A7 does not apply to software'],

										'A9', 		'The article makes a credible assertion of importance or significance, or is not a musical recording'],

										'A10', 	'Page title is a plausible redirect, or it does not substantially duplicate the other topic'],

										'A11',		'Subject is not obviously invented by the page creator'],

										

										'R2', 		'Does not redirect to a different or incorrect namespace'],

										'R3', 		'Is a plausible, useful redirect or is not a redirect at all'],

										'R3',		'Not a recently created redirect - consider [[WP:RfD]]'],

										'R4', 		'Not the same name as a [[WP:Wikimedia Commons|Commons]] file or redirect'],

										'R4', 		'Has incoming [[Wikipedia:File link|file links]] - consider [[WP:RfD]]'],

										

										'F1',		'File is used, or it is not a lower-quality version of an existing image'],

										'F2',		'File is neither corrupt, missing, or empty'],

										'F3',		'File has a suitable license'],

										'F4',		'File has the necessary licensing information'],

										'F5',		'File is used, or it has a free license'],

										'F6',		'File has a use rationale'],

										'F7',		'File has a valid fair-use claim'],

										'F8',		'File is not identical to the Commons version, or has been marked as "Keep local"'],

										'F9',		'Not an unambiguous copyright infringement'],

										'F11',		'Evidence of permission has been given'],

										

										'C1',		'Category is populated or is otherwise allowed to be empty'],

										

										'U1', 		'Not a user page'],

										'U1', 		'Does not apply to user talk pages'],

										'U2', 		'User does exist, or this is not a user page'],

										'U5',		'Not a blatant misuse of Wikipedia as a webhost'

									);

	this.deleteReasons = new Array(

								   		'N/A', 	'You must select a rationale...'],

										'G1', 		'[[WP:PN|Patent nonsense]], meaningless, or incomprehensible'],

										'G2', 		'Test page'],

										'G3', 		'[[WP:VANDAL|Vandalism]]'],

										'G3', 		'[[WP:VANDAL|Vandalism]] - blatant hoax or misinformation'],

										'G4', 		'Recreation of a page that was [[WP:DEL|deleted]] per a [[WP:XFD|deletion discussion]]'],

										'G5', 		'Creation by a [[WP:BAN|banned]] user in violation of ban'],

										'G6', 		'Housekeeping and routine (non-controversial) cleanup'],

										'G7', 		'One author who has requested deletion or blanked the page'],

										'G8', 		'Page dependent on a deleted or nonexistent page'],

										'G8',		'Talk page of a deleted page'],

										'G10', 	'[[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]] that serves no purpose but to threaten or disparage its subject'],

										'G11', 	'Unambiguous [[WP:PROMO|advertising]] or promotion'],

										'G12', 	'Unambiguous [[WP:C|copyright infringement]]'],

										'G13', 	'Abandoned Draft or [[WP:AFC|Article for creation]] — to retrieve it, see [[WP:REFUND/G13]]'],

										'G14', 	'Disambiguation page that disambiguates only one page and whose title ends in (disambiguation) or disambiguates zero extant pages'],

																				

										'A1', 		'Not enough context to identify article\'s subject'],

										'A2', 		'Article in a foreign language that exists on another project'],

										'A3', 		'Article that has no meaningful, substantive content'],

										'A7', 		'No indication that the article may meet the guidelines for inclusion'],

										'A7',		'Article about a real person, which does not credibly indicate the importance or significance of the subject'],

										'A7',		'Article about a band, singer, musician, or musical ensemble that does not credibly indicate the importance or significance of the subject'],

										'A7',		'Article about a web site, blog, web forum, webcomic, podcast, browser game, or similar web content, which does not credibly indicate the importance or significance of the subject'],

										'A7',		'Article about a company, corporation, organization, or group which does not credibly indicate the importance or significance of the subject'],

										'A7',		'Article about a group or club, which does not credibly indicate the importance or significance of the subject'],

										'A7',		'Article about an organized event, which does not credibly indicate the importance or significance of the subject'],

										'A7',		'Article about an individual animal, which does not credibly indicate the importance or significance of the subject'],

										'A9', 		'Article about a musical recording, which does not credibly indicate the importance or significance of the subject and where the artist has no article'],

										'A10', 	'Article where the only content is already existing in another article and where a redirect to the existing article would be implausible'],

										'A11',     'Article about a subject that was obviously invented by the creator or someone they know and which does not credibly indicate the importance or significance of the subject'],

										

										'R2', 		'Cross-[[WP:NS|namespace]] [[WP:R|redirect]] from mainspace'],

										'R3', 		'Recently-created, implausible [[WP:R|redirect]]'],

										'R4', 		'File [[WP:R|redirect]] with no incoming [[Wikipedia:File link|file links]] and the same name as a file or redirect at [[WP:Wikimedia Commons|Wikimedia Commons]]'],

										

										'F1',		'Redundant file  (also on Wikipedia)'],

										'F2',		'Corrupt or empty file, or a file description page for a file on Commons'],

										'F3',		'File licensed as "for non-commercial use only", "no derivative use", "for Wikipedia use only", "used with permission", or GFDL 1.2 only.'],

										'F4',		'Lack of licensing information'],

										'F5',		'Unused non-free media file for more than 7 days'],

										'F6',		'Non-free media file with no [[WP:FUR|non-free use rationale]]'],

										'F7',		'[[WP:NFCC|Invalid]] fair-use rationale'],

										'F8',		'Media file available on Commons'],

										'F9',		'Media file [[WP:COPYVIO|copyright violation]] without credible claim of [[Wikipedia:Non-free content|fair use]] or permission'],

										'F11',		'No evidence of permission for more than 7 days'],

										

										'C1',		'Empty category'],

										

										'U1', 		'User request to delete pages in own userspace'],

										'U2', 		'Userpage or subpage of a nonexistent user'],

										'U3', 		'[[WP:NFC|Non-free]] [[Help:Gallery|gallery]]'],

										'U5',      'Blatant use of [[WP:NOTWEBHOST|Wikipedia as a web host]] by user with no or very few edits'

									);



	// Handle user defined content...

	// declining

	if (overwriteDeclineReasons == true) {

		this.declineReasons.length = 0;

		this.declineReasons = myDeclineReasons;

	} else {

		this.declineReasons = this.declineReasons.concat(myDeclineReasons);

	}



	// deleting

	if (overwriteDeleteReasons == true) {

		this.deleteReasons.length = 0;

		this.deleteReasons = myDeleteReasons;

	} else {

		this.deleteReasons = this.deleteReasons.concat(myDeleteReasons);

	}



	// append necessary options to decline reasons

	var declineReasonsEnd = new Array(

									  	'INVALID', 		'The reason given is not a valid [[WP:CSD|speedy deletion criterion]]'],	// don't touch this

										'DONTPROVIDE', 	'No reason given.'], 						// don't touch this

										'OTHER', 			'Other - provide your own reason below'	// don't touch this

									);

	this.declineReasons = this.declineReasons.concat(declineReasonsEnd);

	

	

	// GUI

	function getStandardElements() {

		if (!(isPageTagged() || isPageContested())) {

			return '';

		}



		let result =

			'<div style="margin: auto; width: 80%; cursor: pointer; background: #dfdfdf; border: 1px solid #cfcfcf; height: 16px; margin-top: 20px;" onclick="csdHController.declinePage();" onmouseover="this.style.border = \'1px solid #333333\';" onmouseout="this.style.border = \'1px solid #cfcfcf\';">Decline Speedy</div>' +

			'<div style="margin: auto; width: 80%; cursor: pointer; background: #dfdfdf; border: 1px solid #cfcfcf; height: 16px; margin-top:  5px;" onclick="csdHController.prodPage();" onmouseover="this.style.border = \'1px solid #333333\';" onmouseout="this.style.border = \'1px solid #cfcfcf\';">Change to PROD</div>' +

			'';

		

		return result;

	}



	function getSysopElements() {

		if (!waUser.isSysop) {

			return '';

		}



		let result =

			'<div style="margin: auto; width: 95%; cursor: pointer; background: #dfdfdf; border: 1px solid #cfcfcf; height: 22px; margin-top: 20px;" onclick="csdHController.deletePage();" onmouseover="this.style.border = \'1px solid #333333\';" onmouseout="this.style.border = \'1px solid #cfcfcf\';">Delete Page</div>' +

			'';



		return result;

	}



	function getCoreElements() {

		let result =

			'<div>' +

				'<div style="width: 596px; border-bottom: 1px solid #aaaaaa; padding: 2px; font-weight: bold;">Handle Speedy Deletion</div>' +

				'<div style="font-size: 11px; margin-left: 7px;">What do you want to do to this page?</div>' +

				'<div style="font-size: 11px; text-align: center; width: 100%;">' +

					getSysopElements() +

					getStandardElements() +

				'</div>' +

			'</div>';



		return result;

	}



	this.showcsdHWindow = function() {

		// grab position of button

		var offsetL = 0;

		var offsetT = 0;

		var thisObject = document.getElementById('ca-speedy');

		

		if (thisObject.offsetParent) {

			do {

				offsetL += thisObject.offsetLeft;

				offsetT += thisObject.offsetTop;

			} while (thisObject = thisObject.offsetParent);

		}

		

		// build window to show user

		if (this.interface == null) { 

			this.interface = new wa_window(document.getElementById('content'));

			this.visible = true;

			this.csdHelperLink.ele_obj.setAttribute('class', 'selected');



			this.interface.win_content = getCoreElements();

		} else {

			this.interface.win_content = getCoreElements();

			

			if (this.visible == true) { 

				this.csdHelperLink.ele_obj.setAttribute('class', '');

				this.interface.win_disp = 'none'; 

				this.interface.applyAll(); 

				this.visible = false; 

				return true; 

			} else { 

				this.csdHelperLink.ele_obj.setAttribute('class', 'selected');

				this.interface.win_disp = 'block';

				this.interface.win_height = 170;

				this.interface.applyAll();

				this.visible = true; 

				return true;

			}

		}

		

		if ( mw.config.get ( 'skin' ) == 'vector' ) {

			this.interface.win_left = offsetL - 17;

			this.interface.win_top = offsetT + 39;

		} else {

			this.interface.win_left = document.getElementById('ca-speedy').offsetLeft - 17;

			this.interface.win_top = -1;

		}

		this.interface.win_width = 600;

		this.interface.win_height = 170;

		this.interface.win_bg = '#fff';

		this.interface.win_bd = '#aaaaaa';

		this.interface.win_bd_wd = 1;

		this.interface.applyAll();

	}

	

	this.declinePage = function() {

		// build the selection options

		var declineOptions = ''; var optionSelected = false;

		for (var i = 0; i < this.declineReasons.length; i++) {

			var selected = '';

			// determine whether this option should be selected

			if (optionSelected == false) {

				if (document.getElementById('delete-criterion') != null) { // if this matches the criterion provided, select it.

					if (document.getElementById('delete-criterion').innerHTML == this.declineReasonsi][0]) { selected = ' selected '; optionSelected = true; }

					if ( (this.declineReasonsi][0 == 'INVALID') && (optionSelected == false) ) { selected = ' selected '; optionSelected = true; }

				} else { // if no criterion was selected, wait until 'other' is selected.

					if (this.declineReasonsi][0 == 'OTHER') { selected = ' selected '; optionSelected = true; }

				}

			}

			

			// build the visible message for use in the drop-down.

			var tempVisible = myDeclineListing.replace(/%CRITERION%/gi, this.declineReasonsi][0]); tempVisible = tempVisible.replace(/%REASON%/gi, this.declineReasonsi][1]);

			declineOptions += '<option'+selected+'>'+tempVisible+'</option>';

		}

		

		if ( mw.config.get ( 'skin' ) == 'vector' ) { var skinPos = '3'; } else { var skinPos = '10'; }

		this.interface.win_content = ''+

								'<div><div style="width: 596px; border-bottom: 1px solid #aaaaaa; padding: 2px; font-weight: bold;">Decline Speedy Deletion</div>'+

								'<div style="font-size: 11px; margin-left: 7px; padding: 0px;">Select the reason for declining the deletion from the list. This text will be used as the edit summary.</div>'+

								'<select id="declineReason" style="font-size: 11px; margin-left: 9px;" onchange="document.getElementById(\'declineText\').value = \'\'">'+declineOptions+'</select>'+

								'<div style="font-size: 11px; margin-left: 7px; margin-top: 8px; padding: 0px;">Or provide your own reason and summary below.</div>'+

								'<input id="declineText" style="margin-left: 9px; font-size: 11px; width: 576px;" type="text" onkeyup="document.getElementById(\'declineReason\').selectedIndex = csdHController.declineReasons.length-1;" />'+

								'<div style="margin-top: 13px; float: right; margin-right: 300px; font-size: 11px; ">-- <a href="#" onclick="csdHController.declineDo();">decline speedy deletion</a> --</div>'+

								'<div style="margin-top: '+skinPos+'px; font-size: 11px; margin-left: 20px; vertical-align: middle; padding: 0px;">notify tagger <input id="notifyTagger"'+csdHController.doNotifyDec+' style="position: relative; top: 3px; " type="checkbox" /></div>'+

								'<div style="margin-top: '+skinPos+'px; font-size: 11px; margin-left: 20px; vertical-align: middle; padding: 0px;">use newbie message <input id="notifyNewbie"'+csdHController.doNotifyNew+' style="position: relative; top: 3px; " type="checkbox" /></div>'+

								'</div>'+

								'';

		this.interface.applyAll();

	}

	this.declineDo = function(callback) {

		// get page content

		if (!callback) var callback = 0;

		switch (callback) {

			default:

				// main vars

				csdHController.declineText = 	document.getElementById('declineText').value;

				csdHController.declineSel = 	document.getElementById('declineReason').selectedIndex;

				csdHController.declineNotify = 	document.getElementById('notifyTagger').checked;

				csdHController.notifyNewbie = 	document.getElementById('notifyNewbie').checked;

			

				if ((csdHController.declineText == '') && (csdHController.declineSel == csdHController.declineReasons.length-1)) {

					// if no reason is typed, byt 'Other' is selected, use the 'No reason provided' option.

					csdHController.declineSel = csdHController.declineReasons.length - 2;

				}

				

				csdHController.declineCategory 	= 	csdHController.declineReasonscsdHController.declineSel][0];

				csdHController.declineReason 	=	csdHController.declineReasonscsdHController.declineSel][1];

				

				// build all messages

				if ( (csdHController.declineCategory == 'INVALID') || (csdHController.declineCategory == 'DONTPROVIDE') ) { // if it's a 'special' case, use the 'special' summary

					var tempSummary = myDeclineSummarySpecial;

					tempSummary = tempSummary.replace(/%ACTION%/gi, csdHController.decAction); tempSummary = tempSummary.replace(/%REASON%/gi, csdHController.declineReason);

					csdHController.editSummary = tempSummary;

				} else if (csdHController.declineCategory == 'OTHER') { // if they've typed a reason, use that

					var tempSummary = myDeclineSummarySpecial;

					tempSummary = tempSummary.replace(/%ACTION%/gi, csdHController.decAction); tempSummary = tempSummary.replace(/%REASON%/gi, csdHController.declineText);

					csdHController.editSummary = tempSummary;

				} else { // otherwise, use the 'normal' summary

					var tempSummary = myDeclineSummary;

					tempSummary = tempSummary.replace(/%ACTION%/gi, csdHController.decAction); tempSummary = tempSummary.replace(/%CRITERION%/gi, csdHController.declineCategory); tempSummary = tempSummary.replace(/%REASON%/gi, csdHController.declineReason);

					csdHController.editSummary = tempSummary;

				}

				csdHController.editSummary += ' ([[User:Ale_jrb/Scripts|CSDH]])';

				

				// start

				this.interface.win_content = ''+

								'<div><div style="width: 596px; border-bottom: 1px solid #aaaaaa; padding: 2px; font-weight: bold;">Working...</div>'+

								'<div style="font-size: 11px; margin-left: 7px;">Please wait while CSDHelper performs the requested operations. This can take several seconds.</div>'+

								'<div id="workingStatus" style="font-size: 11px; margin-left: 15px;">- Retrieving existing content...<br /></div>'+

								'</div>'+

								'';

				

				this.interface.applyAll();

			

				csdHController.pageReq = new wa_mediawikiApi();

				csdHController.pageReq.onCompleteAction = function() { csdHController.declineDo('1'); };

				csdHController.pageReq.getPage(mw.config.get('wgPageName'), notifyLimit, 'user|content');

				break;

				

			case '1':

				document.getElementById('workingStatus').innerHTML += '- Removing tags...<br />';

				csdHController.pageContent = csdHController.pageReq.data'page']['revisions'][0]['content'];

				

				var regReplace = csdHController.deleteRegex;

				csdHController.pageContent = csdHController.pageContent.replace(regReplace, '');

				var regReplace = csdHController.hangonRegex;

				csdHController.pageContent = csdHController.pageContent.replace(regReplace, '');

				csdHController.newContent = csdHController.pageContent; // grab a record of that

				

				document.getElementById('workingStatus').innerHTML += '- Updating page...<br />';

				

				csdHController.editReq = new wa_mediawikiApi();

				csdHController.editReq.onCompleteAction = function() { csdHController.declineDo('2'); };

				csdHController.editReq.editPage(mw.config.get('wgPageName'), csdHController.newContent, csdHController.editSummary, false, 'text');

				break;

				

			case '2':

				// check for notify

				if ( (csdHController.declineNotify == true) || (logOnDecline == true) ) {

					document.getElementById('workingStatus').innerHTML += '- Determining user who tagged page...<br />';

					

					var tags = 0; csdHController.tagger = '';

					var revisionCount = csdHController.pageReq.data'page']['revisions'].length;

					var limit = Math.min(notifyLimit, revisionCount);

					for (var i = 0; i < limit; i++) {

						var thisPage = csdHController.pageReq.data'page']['revisions'][i]['content'];

						

						// count tags

						var regTest = csdHController.deleteRegex;

						var k = 1; var count = 0;

						while (k != null) {

							k = regTest.exec(thisPage);

							if (k != null) count++;

						}

						regTest.lastIndex = 0;

						

						// check if we have fewer than last time (1 was added on that rev)

						if (count < tags) {

							csdHController.tagger = csdHController.pageReq.data'page']['revisions'][i - 1]['user'];

							break;

						} else {

							tags = count;

						}

					}

					if (csdHController.tagger == '') { document.getElementById('workingStatus').innerHTML += '- Could not determine tagger: will not log or notify. Finished.<br />'; break; }

					

					for (var i = 0; i < csdHController.notifyExempt.length; i++) {

						if (csdHController.tagger == csdHController.notifyExempti]) {

							document.getElementById('workingStatus').innerHTML += '- Tagger ('+csdHController.tagger+') is on the tagger exempt list, and will not be notified. Finished.<br />';

							if (logOnDecline == true) { csdHController.declineNotify = false; csdHController.declineDo('3');  } else { return true; }

						}

					}

					

					if ( (logOnDecline == true) && (logOnDeclinePath != '') ) { csdHController.declineDo('3'); } else { csdHController.declineDo('5'); }

					

				} else { window.location.reload(true); csdHController.showcsdHWindow(); }

				break;

				

			case '3':

				// log decline action where relevant

				document.getElementById('workingStatus').innerHTML += '- Logging decline action to \''+logOnDeclinePath+'\' - retrieving page... ';

				

				csdHController.pageReq = new wa_mediawikiApi();

				csdHController.pageReq.onCompleteAction = function() { csdHController.declineDo('4'); };

				csdHController.pageReq.getPage(logOnDeclinePath, 1, 'content');

				

				break;

				

			case '4':

				// we have retrieved the data regarding the log page; move to edit it

				document.getElementById('workingStatus').innerHTML += 'modifying page...<br />';

				//var logOnDeclinePath = logOnDeclinePath.replace(/ /g,'_');

				

				// check whether there is existing content

				var pageData = csdHController.pageReq.data'page'];

				if (pageData'status' == 'OK') {

					var oldContent = pageData'revisions'][0]['content'];

					if ( oldContent === '' ) oldContent = "{| class=\"sortable wikitable\" style=\"font-size: 80%;\" border=\"2\" cellpadding=\"1\" background:#f9f9f9;\"|\n|-\n! style=\"text-align: left\" | Article\n! Tagger\n! Criterion\n! Decline reason\n! Date\n|}";

				} else {

					var oldContent = "{| class=\"sortable wikitable\" style=\"font-size: 80%;\" border=\"2\" cellpadding=\"1\" background:#f9f9f9;\"|\n|-\n! style=\"text-align: left\" | Article\n! Tagger\n! Criterion\n! Decline reason\n! Date\n|}";

				}

				

				// message

				var pageName = mw.config.get('wgPageName').replace(/_/g, ' ');

				var reason = (csdHController.declineCategory == 'OTHER') ? csdHController.declineText : csdHController.declineReason;

				var crit = document.getElementById('delete-criterion').innerHTML;

				var message = "|-\n| [[:" + pageName + "]] || [[User:" + csdHController.tagger + "|" + csdHController.tagger + "]] || [[WP:CSD#" + crit + "|CSD " + crit + "]] || " +reason + " || " + "~~" + "~~" + "~\n";

				

				// add the new row to the table

				var newContent = oldContent.replace('|}', message + '|}');

				

				// build vars

				var editSummary = 'Adding [[:' + pageName + ']] to speedy decline log ([[User:Ale_jrb/Scripts|CSDH]])';

				

				// perform the edit

				csdHController.editReq = new wa_mediawikiApi();

				csdHController.editReq.onCompleteAction = function() { csdHController.declineDo('5'); };

				csdHController.editReq.editPage(logOnDeclinePath, newContent, editSummary, true, 'text');

				break;

				

			case '5':

				// check notify

				if (csdHController.declineNotify != true) { window.location.reload(true); csdHController.showcsdHWindow(); break; }

			

				// output

				document.getElementById('workingStatus').innerHTML += '- Tagged by \''+csdHController.tagger+'\' - notifying user...<br />';

			

				// edit summary

				csdHController.editSummary = 'Notifying about '+csdHController.decAction+' speedy deletion ([[User:Ale_jrb/Scripts|CSDH]])';

				

				// decide whether to use newbie message

				if (csdHController.notifyNewbie) { var useNewbie = 'yes'; } else { var useNewbie = 'no'; }

				

				// fix message - handle other special case

				if (csdHController.declineCategory == 'OTHER') csdHController.declineReason = csdHController.declineText;

				// fix message - handle punctuation at the end of the message (it must be added if absent).

				var p = csdHController.declineReason.substr(csdHController.declineReason.length - 1);

				if ( (p != '.') && (p != '!') && (p != '?') ) csdHController.declineReason += '.';

				

				// message

				var message = '== Speedy deletion '+csdHController.decAction+': [[:'+mw.config.get('wgPageName').replace(/_/g, ' ')+']] =='+"\n"+'{{subst:'+notifyTemplate+'|action=decline|page='+mw.config.get('wgPageName').replace(/_/g, ' ')+'|tagger='+csdHController.tagger+'|declinetext='+csdHController.declineReason+'|admin='+csdHController.isSysop+'|newbie='+useNewbie+'}} ~~'+'~~';

				

				csdHController.saveReq = new wa_mediawikiApi();

				csdHController.saveReq.onCompleteAction = function() { window.location.reload(true); csdHController.showcsdHWindow(); };

				csdHController.saveReq.editPage('User_talk:'+csdHController.tagger, message, csdHController.editSummary, false, 'appendtext');

				break;

			

		}

		

	}

	

	this.prodPage = function() {

		var prodOptions = '';

		

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

		var regTest = /criteria for speedy deletion<\/a><\/i> because (.*?)\.<\/b> <i>For valid criteria,/i

		var rationale = regTest.exec(content);

		if (rationale != null) { rationale = rationale1]; } else { rationale = ''; }

		

		this.interface.win_content = ''+

								'<div><div style="width: 596px; border-bottom: 1px solid #aaaaaa; padding: 2px; font-weight: bold;">Convert to PROD</div>'+

								'<div style="font-size: 11px; margin-left: 7px;">You are asserting that the page cannot be speedy deleted, but <em>can</em> be deleted by proposed deletion. '+

								'Enter the reason for deletion below - if the tagger provided one, it will be filled automatically.</div>'+

								'<input id="prodReason" style="margin-left: 9px; font-size: 11px; width: 576px;" type="text" value="'+rationale+'" />'+

								'<div id="notifyTaggerDiv" style="margin-top: 10px; font-size: 11px; margin-left: 20px; float: left; vertical-align: middle;">notify tagger of conversion <input id="notifyTagger"'+csdHController.doNotifyPrd+' style="position: relative; top: 3px; " type="checkbox" /><br />'+

								'use newbie message <input id="notifyNewbie"'+csdHController.doNotifyNew+' style="position: relative; top: 3px; " type="checkbox" /></div>'+

								'<div style="margin: auto; margin-top: 13px; font-size: 11px; width: 200px; text-align: center;">-- <a href="#" onclick="csdHController.prodDo();">convert to PROD</a> --</div>'+

								'</div>'+

								'';

		this.interface.applyAll();

	}

	this.prodDo = function(callback) {

		if (!callback) var callback = 0;

		switch (callback) {

			default:

				csdHController.prodReason = document.getElementById('prodReason').value;

				csdHController.prodNotify = document.getElementById('notifyTagger').checked;

				csdHController.notifyNewbie = document.getElementById('notifyNewbie').checked;

				

				this.interface.win_content = ''+

								'<div><div style="width: 596px; border-bottom: 1px solid #aaaaaa; padding: 2px; font-weight: bold;">Working...</div>'+

								'<div style="font-size: 11px; margin-left: 7px;">Please wait while CSDHelper performs the requested operations. This can take several seconds.</div>'+

								'<div id="workingStatus" style="font-size: 11px; margin-left: 15px;"></div>'+

								'</div>'+

								'';

				

				this.interface.applyAll();

			

				if (csdHController.prodNotify == true) {

					document.getElementById('workingStatus').innerHTML += '- Searching for tagger...<br />';

					

					if (notifyLimit > 15) notifyLimit = 15;

					csdHController.pageReq = new wa_mediawikiApi();

					csdHController.pageReq.onCompleteAction = function() { csdHController.prodDo('1'); };

					csdHController.pageReq.getPage(mw.config.get('wgPageName'), notifyLimit + 1, 'content|user');

				} else { 

					csdHController.pageReq = new wa_mediawikiApi();

					csdHController.pageReq.onCompleteAction = function() { csdHController.prodDo('3'); };

					csdHController.pageReq.getPage(mw.config.get('wgPageName'), 1, 'content|user');

				}

				break;

				

			case '1':

				var tags = 0; csdHController.tagger = '';

				

				var revisionCount = csdHController.pageReq.data'page']['revisions'].length;

				var limit = Math.min(notifyLimit, revisionCount);

				for (var i = 0; i < limit; i++) {

					var thisPage = csdHController.pageReq.data'page']['revisions'][i]['content'];

					

					// count tags

					var regTest = csdHController.deleteRegex;

					var k = 1; var count = 0;

					while (k != null) {

						k = regTest.exec(thisPage);

						if (k != null) count ++;

					}

					regTest.lastIndex = 0;

					

					// check if we have fewer than last time (1 was added on that rev)

					if (count < tags) {

						csdHController.tagger = csdHController.pageReq.data'page']['revisions'][i - 1]['user'];

						break;

					} else {

						tags = count;

					}

				}

				

				if (csdHController.tagger == '') { document.getElementById('workingStatus').innerHTML += '- Could not determine tagger. Moving to tag page...<br />'; csdHController.prodDo('3'); break; }

				

				for (var i = 0; i < csdHController.notifyExempt.length; i ++) {

					if (csdHController.tagger == csdHController.notifyExempti]) {

						document.getElementById('workingStatus').innerHTML += '- Tagger ('+csdHController.tagger+') is on the tagger exempt list, and will not be notified. Moving to tag page...<br />';

						csdHController.prodDo('3');

						return true;

					}

				}

				

				csdHController.prodDo('2');

				break;

				

			case '2': // notify tagger

				document.getElementById('workingStatus').innerHTML += '- Tagged by \''+csdHController.tagger+'\' - notifying user...<br />';

			

				// edit summary

				csdHController.editSummary = 'Notifying about speedy deletion converted to PROD ([[User:Ale_jrb/Scripts|CSDH]])';

				

				// decide whether to use newbie message

				if (csdHController.notifyNewbie) { var useNewbie = 'yes'; } else { var useNewbie = 'no'; }

				

				// message

				var message = '== Speedy deletion converted to PROD: [[:'+mw.config.get('wgPageName').replace(/_/g, ' ')+']] =='+"\n"+'{{subst:'+notifyTemplate+'|action=convert|page='+mw.config.get('wgPageName').replace(/_/g, ' ')+'|tagger='+csdHController.tagger+'|admin='+csdHController.isSysop+'|newbie='+useNewbie+'}} ~~'+'~~';

				

				csdHController.notifyReq = new wa_mediawikiApi();

				csdHController.notifyReq.onCompleteAction = function() { csdHController.prodDo('3'); };

				csdHController.notifyReq.editPage('User_talk:'+csdHController.tagger, message, csdHController.editSummary, false, 'appendtext');

				break;

				

			case '3':

				document.getElementById('workingStatus').innerHTML += '- Converting tags...<br />';

				csdHController.pageContent = csdHController.pageReq.data'page']['revisions'][0]['content'];

				

				var regReplace = csdHController.deleteRegex;

				csdHController.pageContent = csdHController.pageContent.replace(regReplace, '');

				var regReplace = csdHController.hangonRegex;

				csdHController.pageContent = csdHController.pageContent.replace(regReplace, '');

				

				if (csdHController.prodReason != 'nn') { csdHController.pageContent = '{'+'{subst:prod|'+csdHController.prodReason+'}'+'}\n' + csdHController.pageContent; } else {

					csdHController.pageContent = '{'+'{subst:prod-nn}'+'}\n' + csdHController.pageContent; }

				

				csdHController.newContent = csdHController.pageContent; // grab a record of that

				csdHController.editSummary = 'Speedy deletion converted to PROD ([[User:Ale_jrb/Scripts|CSDH]])';

				

				document.getElementById('workingStatus').innerHTML += '- Updating page...<br />';

				

				csdHController.editReq = new wa_mediawikiApi();

				csdHController.editReq.onCompleteAction = function() { window.location.reload(true); csdHController.showcsdHWindow(); };

				csdHController.editReq.editPage(mw.config.get('wgPageName'), csdHController.newContent, csdHController.editSummary, false, 'text');

				break;

		}

	}

	

	this.deletePage = function() {

		// build the selection options

		var deleteOptions = ''; var optionSelected = false;

		for (var i = 0; i < this.deleteReasons.length; i++) {

			var selected = '';

			if ( (document.getElementById('delete-criterion') != null) && (optionSelected == false) ) { 

				if (document.getElementById('delete-criterion').innerHTML == this.deleteReasonsi][0]) { csdHController.initialRationale = this.deleteReasonsi][0]; selected = ' selected'; optionSelected = true; }

			} else {

				if (this.deleteReasonsi][0 == 'N/A') { selected = ' selected '; optionSelected = true; }

			}

			

			// general

			var visibleReasoning = this.deleteReasonsi][1].replace(/\[\[(?:.*?\|)(.+?)\]\]/g, '$1');

			deleteOptions += '<option value="'+i+'"'+selected+'>'+this.deleteReasonsi][0+': '+visibleReasoning+'</option>';

		}

		

		if (document.getElementById('delete-criterion') == null) { var displayTagCommand = 'display: none;'; } else { var displayTagCommand = 'display: block;'; }

		

		this.interface.win_content = ''+

								'<div><div style="width: 596px; border-bottom: 1px solid #aaaaaa; padding: 2px; font-weight: bold;">Perform Speedy Deletion</div>'+

								'<div style="font-size: 11px; margin-left: 7px;">Select the reason for deletion from the list. The rationale specified by the tagger is automatically selected. If you select a reason that does not '+

								'match the tag, will be given the option of notifying the tagger.</div>'+

								'<select id="deleteReason" style="font-size: 11px; margin-left: 9px; width: 576px;" onchange="'+

									'if (document.getElementById(\'delete-criterion\') != null) {'+

										'if (csdHController.deleteReasons[document.getElementById(\'deleteReason\').selectedIndex][0] != document.getElementById(\'delete-criterion\').innerHTML) { '+

											'if (csdHController.deleteReasons[document.getElementById(\'deleteReason\').selectedIndex][0] == \'N/A\') { document.getElementById(\'notifyTaggerDiv\').style.display = \'none\'; } else { document.getElementById(\'notifyTaggerDiv\').style.display = \'block\'; }'+

										'} else {'+

											'document.getElementById(\'notifyTaggerDiv\').style.display = \'none\'; '+

										'}'+

									'} else { document.getElementById(\'notifyTaggerDiv\').style.display = \'none\'; }'+

									

									'if (csdHController.deleteReasons[document.getElementById(\'deleteReason\').selectedIndex][0] == \'N/A\') {'+

										'document.getElementById(\'performDeletionDiv\').style.display = \'none\'; '+

									'} else {'+

										'document.getElementById(\'performDeletionDiv\').style.display = \'block\'; '+

									'}'+

								'">'+deleteOptions+'</select>'+

								'<div id="notifyTaggerDiv" style="display: none; margin-top: 10px; font-size: 11px; margin-left: 20px; float: left; vertical-align: middle;">notify tagger of rationale change <input id="notifyTagger"'+csdHController.doNotifyDel+' style="position: relative; top: 3px; " type="checkbox" /><br />'+

								'use newbie message <input id="notifyNewbie"'+csdHController.doNotifyNew+' style="position: relative; top: 3px; " type="checkbox" /></div>'+

								'<div id="performDeletionDiv" style="margin: auto; margin-top: 13px; font-size: 11px; width: 200px; text-align: center; '+displayTagCommand+'">-- <a href="#" onclick="csdHController.deleteDo();">perform speedy deletion</a> --</div>'+

								'</div>'+

								'';

		this.interface.applyAll();

	}

	this.deleteDo = function(callback) {

		if (!callback) var callback = 0;

		switch (callback) {

			default:

				csdHController.deleteSel 		= document.getElementById('deleteReason').selectedIndex;

				csdHController.deleteNotify 	= document.getElementById('notifyTagger').checked;

				csdHController.notifyNewbie		= document.getElementById('notifyNewbie').checked;

				if (document.getElementById('delete-criterion') == null) {

					csdHController.allowDelNotify = false;

				} else { csdHController.allowDelNotify = (csdHController.deleteReasonscsdHController.deleteSel][0 != document.getElementById('delete-criterion').innerHTML); }

				

				this.interface.win_content = ''+

								'<div><div style="width: 596px; border-bottom: 1px solid #aaaaaa; padding: 2px; font-weight: bold;">Working...</div>'+

								'<div style="font-size: 11px; margin-left: 7px;">Please wait while CSDHelper performs the requested operations. This can take several seconds.</div>'+

								'<div id="workingStatus" style="font-size: 11px; margin-left: 15px;"></div>'+

								'</div>'+

								'';

				

				this.interface.applyAll();

			

				if ((csdHController.deleteNotify == true) && (csdHController.allowDelNotify == true)) {

					document.getElementById('workingStatus').innerHTML += '- Searching for tagger...<br />';

					

					if (notifyLimit > 15) notifyLimit = 15;

					csdHController.pageReq = new wa_mediawikiApi();

					csdHController.pageReq.onCompleteAction = function() { csdHController.deleteDo('1'); };

					csdHController.pageReq.getPage(mw.config.get('wgPageName'), notifyLimit + 1, 'content|user');

				} else { csdHController.deleteDo('3'); }

				break;

				

			case '1':

				var tags = 0; csdHController.tagger = '';

				

				var revisionCount = csdHController.pageReq.data'page']['revisions'].length;

				var limit = Math.min(notifyLimit, revisionCount);

				for (var i = 0; i < limit; i++) {

					var thisPage = csdHController.pageReq.data'page']['revisions'][i]['content'];

					

					// count tags

					var regTest = csdHController.deleteRegex;

					var k = 1; var count = 0;

					while (k != null) {

						k = regTest.exec(thisPage);

						if (k != null) count ++;

					}

					regTest.lastIndex = 0;

					

					// check if we have fewer than last time (1 was added on that rev)

					if (count < tags) {

						csdHController.tagger = csdHController.pageReq.data'page']['revisions'][i - 1]['user'];

						break;

					} else {

						tags = count;

					}

				}

				

				if (csdHController.tagger == '') { document.getElementById('workingStatus').innerHTML += '- Could not determine tagger. Moving to deletion...<br />'; csdHController.deleteDo('3'); break; }

				

				for (var i = 0; i < csdHController.notifyExempt.length; i ++) {

					if (csdHController.tagger == csdHController.notifyExempti]) {

						document.getElementById('workingStatus').innerHTML += '- Tagger ('+csdHController.tagger+') is on the tagger exempt list, and will not be notified. Moving to deletion...<br />';

						csdHController.deleteDo('3');

						return true;

					}

				}

				

				csdHController.deleteDo('2');

				break;

				

			case '2': // notify tagger

				document.getElementById('workingStatus').innerHTML += '- Tagged by \''+csdHController.tagger+'\' - notifying user...<br />';

			

				// edit summary

				csdHController.editSummary = 'Notifying about altered speedy deletion rationale ([[User:Ale_jrb/Scripts|CSDH]])';

				

				// decide whether to use newbie message

				if (csdHController.notifyNewbie) { var useNewbie = 'yes'; } else { var useNewbie = 'no'; }

				

				// message

				var message = '== Altered speedy deletion rationale: [[:'+mw.config.get('wgPageName').replace(/_/g, ' ')+']] =='+"\n"+'{{subst:'+notifyTemplate+'|action=change|page='+mw.config.get('wgPageName').replace(/_/g, ' ')+'|tagger='+csdHController.tagger+'|newbie='+useNewbie+'}} ~~'+'~~';

				

				csdHController.notifyReq = new wa_mediawikiApi();

				csdHController.notifyReq.onCompleteAction = function() { csdHController.deleteDo('3'); };

				csdHController.notifyReq.editPage('User_talk:'+csdHController.tagger, message, csdHController.editSummary, false, 'appendtext');

				break;

				

			case '3':

				// perform the edit

				document.getElementById('workingStatus').innerHTML += '- Deleting page...<br />';

				var deleteReason = '[[WP:CSD#'+csdHController.deleteReasonscsdHController.deleteSel][0+'|'+csdHController.deleteReasonscsdHController.deleteSel][0+']]: ' + csdHController.deleteReasonscsdHController.deleteSel][1 + ' ([[User:Ale_jrb/Scripts|CSDH]])';

				

				csdHController.deleteReq = new wa_mediawikiApi();

				csdHController.deleteReq.onCompleteAction = function(callback) { 

					if ((callback == null) || (callback == false) || (mw.config.get('wgNamespaceNumber') != 0)) {

						// No talk page

						if (csdhDoRedirect) {

							window.location = redirectAfterDel;

						} else {

							window.location.reload(true);

						}

						csdHController.showcsdHWindow(); 

					} else {

						// Talk page exists

						csdHController.talkpageId = callback;

						document.getElementById('workingStatus').innerHTML = ''+

							'<strong>Warning: this page now has an orphaned talk page. Do you wish to delete it under G8?</strong><br /><br />'+

							'<div style="width: 100%; text-align: center;"><a href="#" onclick="document.getElementById(\'workingStatus\').innerHTML = \'- Deleting talk page...<br />\'; csdHController.deleteDo(\'4\');">Delete</a> | <a href="#" onclick="window.location = redirectAfterDel; csdHController.showcsdHWindow(); ">Ignore</a></div>'+

						'';

					}

				};

				csdHController.deleteReq.performAction('delete', mw.config.get('wgPageName'), deleteReason);

				break;

			

			case '4':

				// delete the talk page - get the title

				var talkPage = 'Talk:' + mw.config.get('wgPageName');

				

				csdHController.deleteReq = new wa_mediawikiApi();

				csdHController.deleteReq.onCompleteAction = function() { window.location = redirectAfterDel; csdHController.showcsdHWindow(); };

				csdHController.deleteReq.performAction('delete', talkPage, '[[WP:CSD#G8|G8]]: Talk page of deleted page. ([[User:Ale_jrb/Scripts|CSDH]])');

				break;

		}

	}

	

	this.displayError = function() {

		this.interface.win_content = ''+

								'<div><div style="width: 596px; border-bottom: 1px solid #aaaaaa; padding: 2px; font-weight: bold;">An error occurred...</div>'+

								'</div>'+

								'';

		this.interface.applyAll();

	}

	

	this.attachLinks = function() {

		this.csdHelperLink 						= new wa_element('li');

		this.csdHelperLink.ele_obj.id			= 'ca-speedy';

		this.csdHelperLink.addScriptEvent('click', function() { csdHController.showcsdHWindow(); });

		

		if ( mw.config.get ( 'skin' ) == 'vector' ) {

			this.csdHelperLink.ele_obj.innerHTML	= '<span><a href="#" title="handle speedy deletion">Speedy</a></span>';

			this.csdHelperLink.attach(document.getElementById('ca-talk'), 'after');

		} else {

			this.csdHelperLink.ele_obj.innerHTML	= '<a href="#" title="handle speedy deletion">speedy</a>';

			this.csdHelperLink.attach(document.getElementById('ca-move'), 'before');

		}

	};

}







// -- run program

function launchCsdHelper() {

	// lib proto

	wa_window.prototype = new wa_document;

	wa_element.prototype = new wa_document;

	

	// run

	launchCsdh();

}



$.getScript("https://en.wikipedia.org/?title=User:Ale_jrb/Scripts/waLib.js&type=text/javascript&action=raw", function() {

	$(document).ready(launchCsdHelper);

});

Videos

Youtube | Vimeo | Bing

Websites

Google | Yahoo | Bing

Encyclopedia

Google | Yahoo | Bing

Facebook