_ _    _ _____  ___   __                       
 __      _(_) | _(_)___ / ( _ ) / /_   ___ ___  _ __ ___  
 \ \ /\ / / | |/ / | |_ \ / _ \| '_ \ / __/ _ \| '_ ` _ \ 
  \ V  V /| |   <| |___) | (_) | (_) | (_| (_) | | | | | |
   \_/\_/ |_|_|\_\_|____/ \___/ \___(_)___\___/|_| |_| |_|

User:Ale jrb/Scripts/csdhelper.js

In today's world, User:Ale jrb/Scripts/csdhelper.js is a topic that has gained great relevance and interest. Whether it's its impact on society, its historical relevance, or its influence on popular culture, User:Ale jrb/Scripts/csdhelper.js has captured the attention of people of all ages and backgrounds. In this article, we will explore this fascinating phenomenon in depth, analyzing its many facets and its evolution over time. From its importance in daily life to its significance in broader areas, User:Ale jrb/Scripts/csdhelper.js has left an indelible mark on today's world, and deserves to be examined and understood in all its complexity.
/* ============================================== *\
** CSD Helper - JavaScript CSD Script
**   for Wikipedia
**
** Created by Alex Barley ]
**		Tracker: ]
**
**	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
** ] - refer to this for
** setting details.
\* ============================================== */

// NB. this script relies on ].
// 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 = /*\{\{(?:db|speedy ?delet(?:e|ion)|speedy|d|rm|del(?:ete)? ?(?:because)?|csd|nn|{1,2})(?:-(?:.+?))?(?:\|(?:.+?))?\}\}*/gi;
	csdHController.hangonRegex = /*\{\{(?:hang|hold)(?: |-)?oo?n(?:\|.+?)?\}\}*/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(
										,
										,
										,
										,
										,
										,
										,
										,
										,
										,
										,
										,
										,
										,

										,
										,
										,
										,
										,
										,
										,
										,
										,
										
										,
										,
										]'],
										] file or redirect'],
										] - consider ]'],
										
										,
										,
										,
										,
										,
										,
										,
										,
										,
										,
										
										,
										,

										,
										
										,
										,
										,
										
									);
	this.deleteReasons = new Array(
								   		,
										], meaningless, or incomprehensible'],
										,
										]'],
										] - blatant hoax or misinformation'],
										] per a ]'],
										] user in violation of ban'],
										,
										,
										,
										,
										] or negative unsourced ] that serves no purpose but to threaten or disparage its subject'],
										] or promotion'],
										]'],
										] — to retrieve it, see ]'],
										,
																				
										,
										,
										,
										,
										,
										,
										,
										,
										,
										,
										,
										,
										,
										,
										
										] ] from mainspace'],
										]'],
										] with no incoming ] and the same name as a file or redirect at ]'],
										
										,
										,
										,
										,
										,
										]'],
										] fair-use rationale'],
										,
										] without credible claim of ] or permission'],
										,
										
										,
										,
										
										,
										
										,
										,
										] ]'],
										] 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(
									  	]'],	// don't touch this
										, 						// don't touch this
											// 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.declineReasons) { selected = ' selected '; optionSelected = true; }
					if ( (this.declineReasons == 'INVALID') && (optionSelected == false) ) { selected = ' selected '; optionSelected = true; }
				} else { // if no criterion was selected, wait until 'other' is selected.
					if (this.declineReasons == 'OTHER') { selected = ' selected '; optionSelected = true; }
				}
			}
			
			// build the visible message for use in the drop-down.
			var tempVisible = myDeclineListing.replace(/%CRITERION%/gi, this.declineReasons); tempVisible = tempVisible.replace(/%REASON%/gi, this.declineReasons);
			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.declineReasons;
				csdHController.declineReason 	=	csdHController.declineReasons;
				
				// 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 += ' (])';
				
				// 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;
				
				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.length;
					var limit = Math.min(notifyLimit, revisionCount);
					for (var i = 0; i < limit; i++) {
						var thisPage = csdHController.pageReq.data;
						
						// 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;
							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.notifyExempt) {
							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;
				if (pageData == 'OK') {
					var oldContent = pageData;
					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| ] || ] || ] || " +reason + " || " + "~~" + "~~" + "~\n";
				
				// add the new row to the table
				var newContent = oldContent.replace('|}', message + '|}');
				
				// build vars
				var editSummary = 'Adding ] to speedy decline log (])';
				
				// 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 (])';
				
				// 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+': ] =='+"\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 = rationale; } 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.length;
				var limit = Math.min(notifyLimit, revisionCount);
				for (var i = 0; i < limit; i++) {
					var thisPage = csdHController.pageReq.data;
					
					// 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;
						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.notifyExempt) {
						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 (])';
				
				// decide whether to use newbie message
				if (csdHController.notifyNewbie) { var useNewbie = 'yes'; } else { var useNewbie = 'no'; }
				
				// message
				var message = '== Speedy deletion converted to PROD: ] =='+"\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;
				
				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 (])';
				
				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.deleteReasons) { csdHController.initialRationale = this.deleteReasons; selected = ' selected'; optionSelected = true; }
			} else {
				if (this.deleteReasons == 'N/A') { selected = ' selected '; optionSelected = true; }
			}
			
			// general
			var visibleReasoning = this.deleteReasons.replace(/\\]/g, '$1');
			deleteOptions += '<option value="'+i+'"'+selected+'>'+this.deleteReasons+': '+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(\'delete-criterion\').innerHTML) { '+
											'if (csdHController.deleteReasons == \'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 == \'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.deleteReasons != 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.length;
				var limit = Math.min(notifyLimit, revisionCount);
				for (var i = 0; i < limit; i++) {
					var thisPage = csdHController.pageReq.data;
					
					// 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;
						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.notifyExempt) {
						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 (])';
				
				// decide whether to use newbie message
				if (csdHController.notifyNewbie) { var useNewbie = 'yes'; } else { var useNewbie = 'no'; }
				
				// message
				var message = '== Altered speedy deletion rationale: ] =='+"\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 = '+'|'+csdHController.deleteReasons+']]: ' + csdHController.deleteReasons + ' (])';
				
				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, ']: Talk page of deleted page. (])');
				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/w/index.php?title=User:Ale_jrb/Scripts/waLib.js&type=text/javascript&action=raw", function() {
	$(document).ready(launchCsdHelper);
});