User:Davey2010/xfdcloser2.js

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/*******************************************************************************
 XFDcloser --- by Evad37
 > A script that assists in closing AFD, CFD, FFD, MFD, RFD, and TFD discussions
--------------------------------------------------------------------------------
* IMPORTANT NOTE:
  You are responsible for any edits/actions performed using XFDcloser! Please
  make sure you understand relevant Wikipedia policies and procedures, and use 
  this tool accordingly.
  (Some pertinent pages are: [[WP:CON]], [[WP:CLOSE]], [[WP:NAC]])
--------------------------------------------------------------------------------
* Version: 2.17 (see page history for full change log)
* Release notes:
  - Remove newline after Tfd nomination template, if present
  - There may occasionally be bugs encountered, please check contributions
    and deletion actions carefully and report any problems
--------------------------------------------------------------------------------
* See [[User:Evad37/XFDcloser]] for installation details and documentation
--------------------------------------------------------------------------------
* Licencing note: Originally published on the English Wikipedia at
  https://en.wikipedia.org/wiki/User:Evad37/XFDcloser.js ( and/or 
  https://en.wikipedia.org/wiki/User:Evad37/XFDcloser/sandbox.js )
  and available under Creative Commons Attribution-ShareAlike 3.0 Unported
  License (CC BY-SA 3.0) https://creativecommons.org/licenses/by-sa/3.0/
* Attribution note: This script incorporates code derived from, and is a direct
  replacement for, these other scripts also written by Evad37:
  - https://en.wikipedia.org/wiki/User:Evad37/CFDcloser.js
  - https://en.wikipedia.org/wiki/User:Evad37/FFDcloser.js
  - https://en.wikipedia.org/wiki/User:Evad37/TFDcloser.js
  Futhermore, it incorporates code copied/derived from:
  - https://en.wikipedia.org/wiki/User:Mr.Z-man/closeAFD2.js
  - https://en.wikipedia.org/wiki/MediaWiki:Gadget-twinkleunlink.js
  - https://en.wikipedia.org/wiki/MediaWiki:Gadget-morebits.js
  Further notes:
   - See the "History" pages for these pages' authors.
   - All of the these pages are also published on Wikipedia, and thus
     also available under the CC BY-SA 3.0 licence.
   - The following box is a reproduction of the copyright and permission notices
     from https://en.wikipedia.org/wiki/User:Mr.Z-man/closeAFD2.js :
 _ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ _ 
|                                                                              |
|	Copyright (c) 2013 Mr.Z-man                                                |
|                                                                              |
| Permission is hereby granted, free of charge, to any person obtaining a copy |
| of this software and associated documentation files (the "Software"), to deal|
| in the Software without restriction, including without limitation the rights |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell    |
| copies of the Software, and to permit persons to whom the Software is        |
| furnished to do so, subject to the following conditions:                     |
|                                                                              |
| The above copyright notice and this permission notice shall be included in   |
| all copies or substantial portions of the Software.                          |
|                                                                              |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR   |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,     |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE  |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER       |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,|
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN    |
| THE SOFTWARE.                                                                |
|_ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ _|
--------------------------------------------------------------------------------
	Have fun, and happy editing! - Evad37
*******************************************************************************/
/* <nowiki> */
mw.loader.using( ['mediawiki.util', 'mediawiki.api', 'mediawiki.Title', 'mediawiki.RegExp',
	'oojs-ui-core', 'oojs-ui-windows', 'jquery.ui.dialog'], function () {
// Load Morebits if not already available
if ( window.Morebits == null ) {
	importScript('MediaWiki:Gadget-morebits.js');
	importStylesheet( 'MediaWiki:Gadget-morebits.css' );
}
$( function($) {

/* == Basic settings and checks == */
/* ******************************* */

/* --- Quick checks that script should be running (then it's safe to define vars and function) --- */

//Check 1: Page is not in edit/history/diff/oldid mode
var thispageurl = window.location.href;
if (thispageurl.indexOf("?") !== -1) {
	console.log("[XFDcloser] Page is in edit, history, diff, or oldid mode");
	return;
}
//Check 2: User is ExtendedConfirmed and/or Sysop
if ( -1 === $.inArray('extendedconfirmed', mw.config.get('wgUserGroups')) && -1 === $.inArray('sysop', mw.config.get('wgUserGroups')) ) {
	console.log("[XFDcloser] User is not extendedconfirmed or sysop");
	return;
}
//Check 3: Page is an XfD page
var thispage = mw.config.get( 'wgPageName' );
var xfdpage_regex = /(Articles_for_deletion\/|Miscellany_for_deletion|User:Cyberbot_I\/AfD's_requiring_attention|Wikipedia:WikiProject_Deletion_sorting\/(?!(Flat|Compact)$)|(Categories|Files|Templates|Redirects)_for_discussion(?!\/(Working|Holding_cell)))(?!\/?Administrator_instructions$)/;
if ( thispage.search(xfdpage_regex) === -1 ) {
	console.log("[XFDcloser] Current page is not an XfD page");
	return;
}

//xfdSetting object holds process-specific setting
//FORLATER: consider making 'xfdSetting' object via constructor

var xfdSetting = {};
	xfdSetting.html = {};
	xfdSetting.wikitext = {};
	xfdSetting.regex = {};
	xfdSetting.fn = {};
	xfdSetting.transcludedOnly = false;

/* --- Load appropriate settings --- */
if ( thispage.includes("Wikipedia:Miscellany_for_deletion") ) {
	//MFD
	xfdSetting.xfd_type      = "mfd";
	xfdSetting.html.head     = "h4";
	xfdSetting.html.list     = "dl";
	xfdSetting.html.listitem = "dd";
	xfdSetting.path          = "Wikipedia:Miscellany for deletion";
	xfdSetting.subpagePath   = "Wikipedia:Miscellany for deletion/";
	xfdSetting.ns_number     = -5; //not an actual ns number, just used here to indicate all subject-spaces 
	xfdSetting.wikitext.closeTop = "{" + "{subst:Mfd top|'''__RESULT__'''}" + "}__TO_TARGET__. __RATIONALE__ __SIG__";
	xfdSetting.wikitext.closeBottom = "{" +"{subst:Mfd bottom}" + "}";
	xfdSetting.wikitext.oldXfd = "{{Old MfD |date=__DATE__ |result='''__RESULT__''' |page=__SUBPAGE__}}\n";
	xfdSetting.wikitext.mergeFrom = "{" + "{mfd-mergefrom|__NOMINATED__|__DEBATE__|__DATE__}" + "}\n";
	xfdSetting.wikitext.mergeTo = "{" + "{mfd-mergeto|__TARGET__|__DEBATE__|__DATE__|__TARGETTALK__}" + "}\n";
	xfdSetting.wikitext.alreadyClosed = "{{#ifeq:{{FULLPAGENAME}}|Wikipedia:Miscellany for deletion|{{collapse bottom}}|}}";
	xfdSetting.regex.nomTemplate = /(?:<noinclude>\s*)?(?:{{mfd[^}}]*}}|<span id="mfd".*?<\/span>&nbsp;\[\[Category:Miscellaneous pages for deletion\|?.*\]\]\s*)(?:\s*<\/noinclude>)?/gi;
	xfdSetting.fn.sectionCalc = function() { //TODO: check this
		return 2;
	};
} else if ( thispage.includes("Categories_for_discussion/") ) {
	//CFD
	xfdSetting.xfd_type      = "cfd";		
	xfdSetting.html.head     = "h4";
	xfdSetting.html.list     = "ul";
	xfdSetting.html.listitem = "li";
	xfdSetting.html.nthSpan  = "2";
	xfdSetting.path          = "Wikipedia:Categories for discussion/Log/";
	xfdSetting.ns_number     = 14;
	xfdSetting.wikitext.closeTop = "{" + "{subst:cfd top}" + "} '''__RESULT__'''__TO_TARGET____RPUNCT__ __RATIONALE__ __SIG__";
	xfdSetting.wikitext.closeBottom = "{" +"{subst:cfd bottom}" + "}";
	xfdSetting.wikitext.oldXfd = "{" + "{Old CfD |__SECTION__ |date=__DATE__ |action=__ACTION__ |result=__RESULT__}" + "}";
	xfdSetting.wikitext.alreadyClosed = "<!-- Template:Cfd top -->";
	xfdSetting.fn.sectionCalc = function(section_number) {
		return (section_number-1)*2;
	};
} else if ( thispage.includes("Files_for_discussion") ) {
	//FFD
	xfdSetting.xfd_type      = "ffd";		
	xfdSetting.html.head     = "h4";
	xfdSetting.html.list     = "dl";
	xfdSetting.html.listitem = "dd";
	xfdSetting.html.nthSpan  = "1";
	xfdSetting.path          = "Wikipedia:Files for discussion/";
	xfdSetting.ns_number     = 6;
	xfdSetting.wikitext.closeTop = "{" + "{subst:ffd top|'''__RESULT__'''}" + "}__TO_TARGET____RPUNCT__ __RATIONALE__ __SIG__";
	xfdSetting.wikitext.closeBottom = "{" +"{subst:ffd bottom}" + "}";
	xfdSetting.wikitext.oldXfd = "{" + "{oldffdfull |date=__DATE__ |result='''__RESULT__''' |page=__SECTION__}" + "}\n";
	xfdSetting.wikitext.pagelinks = "{" + "{subst:ffd2|__PAGE__|multi=yes}" + "}\n";
	xfdSetting.regex.nomTemplate = /{{ffd[^}}]*}}/gi;
	xfdSetting.regex.relistPattern = /{{\s*ffd\s*\|\s*log\s*=\s*[^|}}]*/gi;
	xfdSetting.wikitext.relistReplace = "{{ffd|log=__TODAY__";
	xfdSetting.wikitext.alreadyClosed = "<!--Template:Ffd top-->";
	xfdSetting.fn.sectionCalc = function(section_number) {
		return (section_number-1)*2;
	};
} else if ( thispage.includes("Templates_for_discussion") ) {
	//TFD
	xfdSetting.xfd_type      = "tfd";		
	xfdSetting.html.head     = "h4";
	xfdSetting.html.list     = "ul";
	xfdSetting.html.listitem = "li";
	xfdSetting.html.nthSpan  = "1";
	xfdSetting.path          = "Wikipedia:Templates for discussion/Log/";
	xfdSetting.ns_number     = 10;
	xfdSetting.wikitext.closeTop = "{" + "{subst:Tfd top|'''__RESULT__'''}" + "}__TO_TARGET____RPUNCT__ __RATIONALE__ __SIG__";
	xfdSetting.wikitext.closeBottom = "{" +"{subst:Tfd bottom}" + "}";
	xfdSetting.wikitext.oldXfd = "{" + "{oldtfdfull|date= __DATE__ |result=__RESULT__ |disc=__SECTION__}" + "}\n";
	xfdSetting.wikitext.pagelinks = "* {" + "{tfd links|__PAGE__}" + "}\n";
	xfdSetting.regex.nomTemplate = /(?:<noinclude>[\n\s]*)?{{(?:Template for discussion|Tfm)\/dated[^{}]*(?:{{[^}}]*}}[^}}]*)*?}}(?:[\n\s]*<\/noinclude>)?(\n)?/gi;
	xfdSetting.regex.relistPattern = /Wikipedia:Templates(_|\s){1}for(_|\s){1}discussion\/Log\/\d{4}(_|\s){1}\w*(_|\s){1}\d{1,2}#(?=[^}]*}{2})/gi;
	xfdSetting.wikitext.relistReplace = "Wikipedia:Templates for discussion/Log/__TODAY__#";
	xfdSetting.wikitext.alreadyClosed = "<!-- Tfd top -->";
	xfdSetting.fn.sectionCalc = function(section_number) {
		return (section_number-1)*2;
	};
} else if ( thispage.includes("Redirects_for_discussion") ) {
	//RFD
	xfdSetting.xfd_type      = "rfd";		
	xfdSetting.html.head     = "h4";
	xfdSetting.html.list     = "ul";
	xfdSetting.html.listitem = "li";
	xfdSetting.path          = "Wikipedia:Redirects for discussion/Log/";
	xfdSetting.ns_number     = -5; //not an actual ns number, just used here to indicate all subject-spaces
	xfdSetting.wikitext.closeTop = "{{subst:Rfd top|'''__RESULT__'''}}__TO_TARGET____RPUNCT__ __RATIONALE__ __SIG__";
	xfdSetting.wikitext.closeBottom = "{{subst:Rfd bottom}}";
	xfdSetting.wikitext.oldXfd = "{{Old RfD |date={{subst:date|__FIRSTDATE__}} |result='''__RESULT__''' |page=__DATE__#__SECTION__}}\n";
	xfdSetting.wikitext.alreadyClosed = "<!-- Template:Rfd top-->";
	xfdSetting.regex.nomTemplate = /(^{{.*#invoke:RfD(?:.|\n)*?-->\|content=\n?|\n?<!-- Don't add anything after this line. -->\n}})/g;
	xfdSetting.fn.sectionCalc = function(section_number) {
		return (section_number-1)*2;
	};
} else if ( thispage.search(/(Articles_for_deletion|User:Cyberbot_I|Wikipedia:WikiProject_Deletion_sorting)/) !== -1 ) {
	//AFD
	xfdSetting.xfd_type      = "afd";
	xfdSetting.html.head     = "h3";
	xfdSetting.html.list     = "dl";
	xfdSetting.html.listitem = "dd";
	xfdSetting.html.nthSpan  = "2";
	xfdSetting.path          = "Wikipedia:Articles for deletion/Log/";
	xfdSetting.subpagePath   = "Wikipedia:Articles for deletion/";
	xfdSetting.ns_number     = 0;
	xfdSetting.wikitext.closeTop = "{" + "{subst:Afd top|'''__RESULT__'''}" + "}__TO_TARGET____RPUNCT__ __RATIONALE__ __SIG__";
	xfdSetting.wikitext.closeBottom = "{" +"{subst:Afd bottom}" + "}";
	xfdSetting.wikitext.mergeFrom = "{" + "{Afd-merge from|__NOMINATED__|__DEBATE__|__DATE__}" + "}\n";
	xfdSetting.wikitext.mergeTo = "{" + "{Afd-merge to|__TARGET__|__DEBATE__|__DATE__}" + "}\n";
	xfdSetting.wikitext.alreadyClosed = "<!--Template:Afd bottom-->";
	xfdSetting.regex.nomTemplate = /(?:{{[Aa]rticle for deletion\/dated|<!-- Please do not remove or change this AfD message)(?:.|\n)*?}}(?:(?:.|\n)+this\ point -->)?\s*/g;
	xfdSetting.fn.sectionCalc = function() { 
		return 2;
	};
	if ( thispage.search(/(User:Cyberbot_I|Wikipedia:WikiProject_Deletion_sorting)/) !== -1 ) {
		xfdSetting.transcludedOnly = true;
	}
} else {
	return;
}

/* --- Common vars --- */
//Vars to be avaialbale for all subfunctions are stored in the xfdc object
var xfdc = {};
xfdc.script_advert = " ([[WP:XFDC|XFDcloser]])";
xfdc.tasks_completed = {};
xfdc.tasks_to_do = {};
xfdc.task_errors = {};
xfdc.monthnames = [  // array needed to convert zero-indexed month number from getUTCMonth()
		"January",
		"February",
		"March",
		"April",
		"May",
		"June",
		"July",
		"August",
		"September",
		"October",
		"November",
		"December"
	];
xfdc.namespaces = mw.config.get('wgFormattedNamespaces');
xfdc.namespaces["0"] = "article"; // is blank by default
xfdc.namespaces["-5"] = "(any) non-talk";  //not an actual namespace, used here all for subject namespaces
xfdc.namespaces["-4"] = "(any) talk";  //not an actual namespace, used here all for talk namespaces
xfdc.unlink_ns = ["0", "10", "100", "118"]; //main, template, portal, draft
xfdc.eins = 4; //Wikipedia:
// Set sysop status
xfdc.isSysop = false;
xfdc.sig = sig = "<small>[[Wikipedia:NACD|(non-admin closure)]]</small> ~~" + "~~";
if ( -1 !== $.inArray( 'sysop', mw.config.get( 'wgUserGroups' ) ) ) {
	xfdc.isSysop = true;
	xfdc.sig = "~~" + "~~";
}
//Keep track of how many closes/relists have been started and completed
xfdc.started = 0; 
xfdc.finished = 0;
//Warn if unloading before closes/relists are completed
$(window).on("beforeunload", function(e) {
	if ( xfdc.started > xfdc.finished ) {
		e.returnValue = "";
		return "";		
	}
});
//Keep track of afd logpage edits using deferred objects, to know when it is safe to read the wikitext
xfdc.afdLogEdit = [$.Deferred().resolve()];

/* === Common functions === */
// Subfunctions are stored in XFDcloser object
var XFDcloser = {};

/* -- Misc. functions -- */

//Object manipulation
XFDcloser.val2key = function(val, obj) {
    for ( var k in obj ) {
        if ( obj[k] === val ) {
            return k;
        }
    }
};

//Make a link in HTML
XFDcloser.makeLink = function(linktarget, linktext) {
	if ( linktext == null ) {
		linktext = linktarget;
	}
	return '<a href="https://en.wikipedia.org/wiki/' +
		mw.util.wikiUrlencode(linktarget) + '" target="_blank">' + linktext + '</a>';
};	

//Make api error details message
XFDcloser.makeApiErrorMsg = function(code, jqxhr) {
	var $details = $("<span>").css("font-size", "90%").text("– Details: ");
	var $italics = $("<span>").css("font-style", "italic").appendTo($details);
	if ( code === "http" && jqxhr.textStatus === "error" ) {
		$italics.text("HTTP error " + jqxhr.xhr.status);
	} else if ( code === "http" ) {
		$italics.text("HTTP error: " + jqxhr.textStatus);
	} else if ( code === "ok-but-empty" ) {
		$italics.text("Got an empty response from the server");
	} else {
		$italics.text("API error: " + code);
	}
	return $details;
};

/* multiButtonConfirm - uses OOjs UI to create a multi-button confirm dialogue
* Parameters:
* title   {string}    - title for the dialogue
* message {string}    - message for the dialogue. HTML tags are escaped, newline characters are ignored.
* buttons {array}     - array of objects which each represent a buttons to show at the bottom of the prompt.
*	Each object can have the following keys:
*		"label" : {string}       - label to show on the button
*		"action": {string|null}  - return value if that button is selected by user
*		"flags" : {string|null}  - "safe", "primary", "progressive", or "destructive"
*									per https://www.mediawiki.org/wiki/OOjs_UI/Elements/Flagged
* callback {function} - callback function for after a button is selected (passed that button's "action" value)
* options {object}    - key/value pairs where the key is an option and the value is a boolean.
*	Available options are:
*		"verbose" : {true|false} - format as a verbose message (left- instead of center-aligned)
*		"unescape": {true|false} - unescape the following simple HTML tags (no attributes) in the
*			message: <br>, <p>, <ul>, <li> (and closing tags or self-closed tags)
*/
XFDcloser.multiButtonConfirm = function(title, message, buttons, callback, options) {
	var messageDialog = new OO.ui.MessageDialog();
	var windowManager = new OO.ui.WindowManager();
	$( 'body' ).append( windowManager.$element );
	windowManager.addWindows( [ messageDialog ] );
	instance = windowManager.openWindow( messageDialog, {
		title: title,
		message: message,
		verbose: options && options.verbose,
		actions: buttons
	} );
	instance.opened.then( function() {
		if ( options && options.unescape ) {
			// Unescape escaped html
			$("label.oo-ui-messageDialog-message").filter(":visible").html( $("label.oo-ui-messageDialog-message").filter(":visible").text().replace(/&lt;(\/?(?:br|p|ul|li)\s?\/?)&gt;/g,"<$1>") );
			// Resize dialogue to fit
			messageDialog.updateSize();
		}
	} );
	instance.closed.then( function ( data ) {
		if ( data && data.action ) {
			callback(data.action);
		} else {
			callback(null);
		}
		windowManager.destroy();
	} );
};

/* -- Title-related functions -- */

// Check namespace
XFDcloser.checkNamespace = function(pagetitle, expectedNamespaceId) {
	var t = new mw.Title( pagetitle );
	if ( expectedNamespaceId !== -5 ) {
		//true for in expected namespace, false otherwise
		return (t.getNamespaceId() === expectedNamespaceId);
	} else {
		//MFD & RFD can be in any namespace
		return true;
	}
};

// Get a page's talk page
XFDcloser.getTalkPage = function(p) {
	var t = new mw.Title( p );
	if ( t.getNamespaceId()%2 === 1 ) {
		// Page is itself a talk page
		return "";
	}
	return mw.Title.newFromText( t.getMain(), t.getNamespaceId()+1 ).getPrefixedText();
};

// Get a talk page's subject page
XFDcloser.getSubjectPage = function(p) {
	var t = new mw.Title( p );
	if ( t.getNamespaceId()%2 !== 1 ) {
		// Page is itself a subject page
		return "";
	}
	return mw.Title.newFromText( t.getMain(), t.getNamespaceId()-1 ).getPrefixedText();
};

// Clean up page title text, including replacing underscores with spaces 
XFDcloser.getPageText = function(p) {
	var t = mw.Title.newFromText( decodeURIComponent(p) );
	if (t) {
		return t.getPrefixedText();
	} else {
		//FORLATER: is this needed and/or the best way to handle a null t?...
		//      t should only be null if p is a bad page title... 
		return p.replace(/_/g, " ");
	}
};

/* -- Make warning text if initial sanity checks fail -- */
XFDcloser.doSanityChecks = function(nom_data, input_data, relisting) {
	
	if ( input_data == null ) input_data = false;
	if ( relisting !== true ) relisting = false;
	
	var warnings = "";
	//Check for mass actions:
	if ( (!relisting&&(!input_data||input_data.after!=="noAction")) || (relisting&&(xfdSetting.xfd_type==="ffd"||xfdSetting.xfd_type==="tfd")) ) {
		if ( nom_data.pages.length > 3 ) {
			warnings += "<p>Mass actions will be peformed (" + nom_data.pages.length +
			" nominated pages detected).";
			if ( nom_data.pages.length > 50 ) {
				warnings += "<br/>(only the first 50 pages may be processed, depending on the " +
				"closing options selected)</p>";
			} else {
				warnings += "</p>";
			}
		}
	}
	//Check target page namespace:
	if (input_data && input_data.target && !XFDcloser.checkNamespace(input_data.target, xfdSetting.ns_number)) {
		warnings += "<p>Target page \"" + input_data.target + "\" is not in the " +
		xfdc.namespaces[(xfdSetting.ns_number).toString()] +
		" namespace.</p>";
	}
	//Check namespaces of nominated pages
	var wrong_ns_pages = "";
	for ( var i=0; i<nom_data.pages.length; i++ ) {
		if ( !nom_data.correctNS[i] ) {
			wrong_ns_pages += "<li>" + nom_data.pages[i] + "</li>";
		}
	}
	if ( wrong_ns_pages !== "" ) {
		warnings += "The following pages are not in the " +
		xfdc.namespaces[(xfdSetting.ns_number).toString()] +
		" namespace:<ul>" + wrong_ns_pages + "</ul>";
	}
	
	return warnings;
};

/* -- Retrieve extra info for pages/nomination -- */
XFDcloser.getExtraInfo = function(jquery_selector) {
	var pages = $(jquery_selector).data("pages");
	var section_header = $(jquery_selector).data("section_header");
	var nom_page = $(jquery_selector).data("nom_page");

	// Discussion link:
	var nom_anchor = "";
	if (xfdSetting.xfd_type !== "afd") {
		nom_anchor = "#" + mw.util.wikiUrlencode(section_header);
	}
	disc_link = nom_page + nom_anchor;
	$(jquery_selector).data("disc_link", disc_link);
	
	// Talk pages, existence checks, namespace checks:
	apiFailedCallback_getExtraInfo = function(api_code, api_result) {
		// change to basic-closure mode
		$(jquery_selector).data("pages", [-1]);
		$(jquery_selector).show();
		// show error message
		var $details = XFDcloser.makeApiErrorMsg(api_code, api_result);
		$(jquery_selector + "_loading")
		.attr("class", "error")
		.css({"display": "inline", "font-size":"90%"})
		.text("[Error retrieving page information (reload the page to try again) ")
		.append($details)
		.append("]");
	};
	
	apiCallback_getPagesInfo = function(result) {
		var page_ids = result.query.pageids;
		var talkPages = new Array (page_ids.length); // an array for just the talk pages
		var pageExists = new Array (page_ids.length);
		var talkExists = new Array (page_ids.length);
		var correctNS = new Array (page_ids.length);
		for (var i=0; i<page_ids.length; i++) {
			title = result.query.pages[ page_ids[i] ].title;
			j = pages.indexOf(title);
			if ( j === -1 ) {
				$(jquery_selector).data("pages", [-1]);
				$(jquery_selector).show();
				$(jquery_selector + "_loading")
				.html("[Error retrieving page information (reload the page to try again) " +
				"<span style='font-size:90%;'>Details: <i>API query result " +
				"included unexpected page title \"" + XFDcloser.makeLink(title) + "\"</i></span>]")
				.attr("class", "error")
				.css({"display": "inline", "font-size":"90%"});
				return;
			}
			talkPages[j] = XFDcloser.getTalkPage(title);
			pageExists[j] = page_ids[i] > 0;
			talkExists[j] = result.query.pages[ page_ids[i] ].talkid > 0;
			if ( xfdSetting.xfd_type === "mfd" || xfdSetting.xfd_type === "rfd" ) {
				correctNS[j] = true;
			} else {
				correctNS[j] = result.query.pages[ page_ids[i] ].ns === xfdSetting.ns_number;
			}
		}
		$(jquery_selector)
			.data("talkPages", talkPages)
			.data("pageExists", pageExists)
			.data("talkExists", talkExists)
			.data("correctNS", correctNS);
		//Check if nom_date is also done
		if ( $(jquery_selector).data("nom_date") != null ) {
			//All done
			$(jquery_selector).show();
			$(jquery_selector + "_loading").remove();
		}
	};
	
	if ( pages[0] !== -1 ) {
		//get page info from api
		new mw.Api().get( {
			action: 'query',
			titles: pages.join("|"),
			prop: 'info',
			inprop: 'talkid',
			indexpageids: 1,
			rawcontinue: ''
		} ).done( apiCallback_getPagesInfo )
		.fail( apiFailedCallback_getExtraInfo );
	} else {
		//Check if nom_date is also done
		if ( $(jquery_selector).data("nom_date") != null ) {
			//All done
			$(jquery_selector).show();
			$(jquery_selector + "_loading").remove();
		}
	}

	
	// Nomination date:
	var nomdate = "";
	
	apiCallback_getNomInfo = function(result) {
		//get nomination date from when nom page was created
		var p_id = result.query.pageids[0];
		var timestamp = result.query.pages[p_id].revisions[0].timestamp;
		var d = new Date(timestamp);
		nomdate = d.getUTCDate().toString()+' '+xfdc.monthnames[d.getUTCMonth()]+' '+d.getUTCFullYear().toString();
		$(jquery_selector).data("nom_date", nomdate);
		//Check if getPagesInfo is also done
		if ( pages[0] === -1 || $(jquery_selector).data("talkPages") != null ) {
			//All done
			$(jquery_selector).show();
			$(jquery_selector + "_loading").remove();
		}			
	};
	
	if (xfdSetting.xfd_type !== "afd" && xfdSetting.xfd_type !== "mfd" ) {
		nomdate = nom_page.split(xfdSetting.path)[1];
		$(jquery_selector).data("nom_date", nomdate);
		if ( xfdSetting.xfd_type === "rfd" && $(jquery_selector).data("first_date") === "" ) {
			// For an RfD with no first comment date detected, use the nom page date in dmy format
			$(jquery_selector).data("first_date", nomdate.replace(/(\d+) (\w*) (\d+)/g, "$3 $2 $1"));
		}
		//Check if getPagesInfo is also done
		if ( pages[0] === -1 || $(jquery_selector).data("talkPages") != null ) {
			//All done
			$(jquery_selector).show();
			$(jquery_selector + "_loading").remove();
		}				
	} else {
		//Api query to get nomination page content
		new mw.Api().get( {
			action: 'query',
			titles: nom_page,
			prop: 'revisions',
			rvprop: 'timestamp',
			rvdir: 'newer',
			rvlimit: '1',
			indexpageids: 1,
			rawcontinue: ''
		} ).done( apiCallback_getNomInfo )
		.fail( apiFailedCallback_getExtraInfo ); 
	}				

};


/* -- Resolving redirects -- */
// If nominated pages are redirects (at venues other than RfD), the script can't know if this was
// appropriate, where results such as 'Delete' should be applied to the target, or an out-of-process
// redirection, where results such as 'Delete' should be applied to the redirect 
XFDcloser.resolveRedirects = function(nom_data, pass_through, doRelist) {
	// If there are no redirects, just get on with closing or relisting
	doNext = function() {
		if ( doRelist ) {
			XFDcloser.processRelist(nom_data, pass_through);
		} else {
			XFDcloser.processClose(nom_data, pass_through);
		}		
	};
	
	// Callbacks for api query
	apiFailedCallback_getRedirectInfo = function(api_code, api_result) {
		var $details = XFDcloser.makeApiErrorMsg(api_code, api_result);
		$("#XFDcloser-closing")
		.attr("class", "error")
		.css({"display": "inline", "font-size":"90%"})
		.text("[Error checking redirect status of nominated page(s) ")
		.append($details)
		.append("]");
	};
	
	apiCallback_getRedirectInfo = function(result) {
		if ( result.query.redirects ) {
			// Redirects are present, need to ask user what to do

			var redir_from = new Array(result.query.redirects.length);
			var redir_to = new Array(result.query.redirects.length);
			var redir_list = new Array(result.query.redirects.length);
			for (var i=0; i<result.query.redirects.length; i++) {
				redir_from[i] = result.query.redirects[i].from;
				redir_to[i] = result.query.redirects[i].to;
				redir_list[i] = redir_from[i] + " → " + redir_to[i];
			}
			var redirects_msg = "The following nominated pages are redirects to other pages:<ul><li>" +
				redir_list.join("</li><li>") + "</li></ul>";
				
			//Callback for multiButtonConfirm dialog
			mbcCallback_useTargets = function(action) {
				if ( action === 'accept' ) {
					// Build a reference object linking titles to their page ids
					var title_ids = {};
					for (var tt=0; tt<result.query.pageids.length; tt++) {
						title_ids[ result.query.pageids[tt] ] = result.query.pages[ result.query.pageids[tt] ].title;
					}
					//update nom data
					for (var ii=0; ii<redir_from.length; ii++) {
						var ndp_index = nom_data.pages.indexOf(redir_from[ii]);
						var target_id = XFDcloser.val2key(redir_to[ii], title_ids);
						if ( ndp_index !== -1 ) {
							nom_data.pages[ndp_index] = redir_to[ii];
							nom_data.talkPages[ndp_index] = XFDcloser.getTalkPage(redir_to[ii]);
							nom_data.pageExists[ndp_index] = target_id > 0;
							nom_data.talkExists[ndp_index] = result.query.pages[target_id].talkid > 0;
							if ( xfdSetting.xfd_type === "mfd" || xfdSetting.xfd_type === "rfd" ) {
								nom_data.correctNS[ndp_index] = true;
							} else {
								nom_data.correctNS[ndp_index] = result.query.pages[target_id].ns === xfdSetting.ns_number;
							}
						}
					}
					doNext();
				} else if ( action === 'reject' ) {
					//Just get on with closing or relisting 
					doNext();
				} else {
					//Abort closing
					$("#XFDcloser-closing")
						.siblings()
							.show()
						.end()
						.remove();
					return;
				}			
			};

			XFDcloser.multiButtonConfirm('Use redirects or targets?', redirects_msg, [
					{ label: 'Cancel', flags: 'safe' },
					{ label: 'Use redirects', action: 'reject' },
					{ label: 'Use targets', action: 'accept', flags: 'progressive' }
				], mbcCallback_useTargets, {'verbose': true, 'unescape': true} );

		} else {
			// No redirects present, just get on with closing or relisting 
			doNext();
		}
	};
	
	// Get redirect status / targets, if any, from Api
	new mw.Api().get( {
		action: 'query',
		titles: nom_data.pages.join("|"),
		redirects: 1,
		prop: 'info',
		inprop: 'talkid',
		indexpageids: 1
	} ).done( apiCallback_getRedirectInfo )
	.fail( apiFailedCallback_getRedirectInfo );
};


/* -- Messaging- and task-related functions -- */

//Show a task, apply styling if specified, append text if specified
XFDcloser.updateTask = function(output_id, task_index, styling, append_text, remove_selector) {
	jquery_selector = "#" + output_id + " > div > p.XFDcloser-task-" + task_index;
	if ( styling ) {
		$(jquery_selector).show().css(styling);
	} else {
		$(jquery_selector).show();
	}
	if ( append_text != null ) {
		$(jquery_selector).append(append_text);
		//make sure errors are at the end
		$errors = $(jquery_selector).children("span.error");
		if ($errors.length > 0) {
			$errors.detach().appendTo( $(jquery_selector) );
		}
	}
	if ( remove_selector != null ) {
		$(jquery_selector).find(remove_selector).remove();
	}

};

//Show a task has beeen started
XFDcloser.taskStarted = function(output_id, task_index) {
	applystyle = {
		"color": "#000090"
	};
	XFDcloser.updateTask(output_id, task_index, applystyle, "<img class='XFDcloser-working' src='//upload.wikimedia.org/wikipedia/commons/thumb/f/f8/Ajax-loader%282%29.gif/40px-Ajax-loader%282%29.gif'  width='20' height='5' />");
};

//Show a task has beeen completed
//FORLATER: Implemenet a third parameter "with errors" : true|false which would cause it to append "Done (with errors)" instead of "Done!".
//      Tasks would count completed edits/actions as well as failed edits/actions, and would compare the sum of completed & failed as well
//      as the number of completed against the total to do, i.e.
//      IF (completed === total) THEN taskFinished(output_id, task_index) ELSE IF (completed + failed === total) THEN taskFinished(output_id, task_index, true)
XFDcloser.taskFinished = function(output_id, task_index) {
	applystyle = {
		"color": "#005000"
	};
	XFDcloser.updateTask(output_id, task_index, applystyle, " Done.", "img.XFDcloser-working");
	XFDcloser.checkIfAllDone(output_id, true);
};

//Show a task has an error
//FORLATER: better error messagaing system may be to place error messages in separate <p> at the end(?)
XFDcloser.taskError = function(output_id, task_index, error_msg) {
	applystyle = {
		"color": "#ff0000"
	};
	XFDcloser.updateTask(output_id, task_index, applystyle, "<span class='error' style='display:inline;font-size:98%;'>&#32;[Error: " + error_msg + "]</span>", "img.XFDcloser-working");
	if ( xfdc.task_errors[output_id][task_index] !== 1 ) {
		xfdc.task_errors[output_id][task_index] = 1;
		XFDcloser.checkIfAllDone(output_id);
	}
};

//Show a warning for a task
XFDcloser.taskWarning = function(output_id, task_index, warn_msg) {
	XFDcloser.updateTask(output_id, task_index, null, "<span class='error' style='display:inline;font-size:98%;color:#A60C7D;'>&#32;[" + warn_msg + "]</span>", "img.XFDcloser-working");
};

//Show that everything is done
XFDcloser.allTasksDone = function(output_id, result) {
	xfdc.finished++;
	if ( result === "relist" ) {
		finished_msg = "Discussion <strong>relisted</strong> (reload page to see the actual relist)";
	} else {
		finished_msg = "Closed as <strong>" + result + "</strong> (reload page to see the actual close)";
	}
	$("#" + output_id).prepend(finished_msg).prev().css("text-decoration", "line-through");
	$("#" + output_id).find(".XFDcloser-inprogress").remove();
};

//Check if tasks are done (including done with errors)
XFDcloser.checkIfAllDone = function(output_id, increment_tasks_completed) {
	if (increment_tasks_completed) {
		xfdc.tasks_completed[output_id]++;
	}
	var sum_errors = xfdc.task_errors[output_id].reduce(function(t, v) {
		if (isNaN(v)) {
			return t;
		} else {
			return t + v;
		}
	}, 0);
	if (xfdc.tasks_completed[output_id] + sum_errors === xfdc.tasks_to_do[output_id]) {
		nom_data = $("#" + output_id).data();
		res = nom_data.res;
		XFDcloser.allTasksDone(output_id, res);
	}
};


// Api failure: When there is an api fail, the more specific failure callback function uses this
// to pass the message on
XFDcloser.Api_failed = function(output_id, task_index, attempted_action, api_code, api_result ) {
	
	var $details = XFDcloser.makeApiErrorMsg(api_code, api_result);
	var $msg = $("<span>")
		.append("Could not " + attempted_action + " ")
		.append($details);	

	XFDcloser.taskError( output_id, task_index, $msg.html() );
};

//Notify that tasks are aborted - discussion was already closed 
XFDcloser.abortTasks = function(output_id, tasks) {
	for (var ii = 0; ii < tasks.length; ii++) {
		XFDcloser.updateTask(output_id, tasks[ii],
			{"color": "#cc5555"},
			"<span class='error' style='display:inline;font-size:98%;'>&#32;Aborted</span>",
			"img.XFDcloser-working");
	}
	xfdc.finished++;
	var finished_msg = "<strong>Aborted closing</strong>: discussion was already closed (reload page to see the actual close)";
	$("#" + output_id).prepend(finished_msg).prev().css("text-decoration", "line-through");
	$("#" + output_id).find(".XFDcloser-inprogress").remove();
};

/* -- Show/hide closed discussions -- */
$(document).ready( function () {
	$( document.body ).append('<div id="XFDcloser-showhide">'+
		'<a id="XFDcloser-showhide-hide">Hide closed discussions</a>'+
		'<a id="XFDcloser-showhide-show">Show closed discussions</a>'+
		'</div>');
	$("#XFDcloser-showhide").css({
    "bottom": "0",
    "display": "block",
    "position": "fixed",
    "right": "0",
    "z-index": "100",
    "padding": "5px",
    "box-shadow": "0 2px 4px rgba(0,0,0,0.5)",
    "background-color": "#FEF9E6",
	"border": "1px solid #aaa",
	"border-radius": "5px",
	"font-size": "85%"
	});
	$('#XFDcloser-showhide-show').hide();
	$('#XFDcloser-showhide-hide').on('click', function() {
		$(".xfd-closed, .tfd-closed").hide();
		$('#XFDcloser-showhide-show').show();
		$('#XFDcloser-showhide-hide').hide();
	});
	$('#XFDcloser-showhide-show').on('click', function() {
		$(".xfd-closed, .tfd-closed").show();
		$('#XFDcloser-showhide-show').hide();
		$('#XFDcloser-showhide-hide').show();
	});
} );

/* == Set up inline links and set data for each discussion == */
/* ********************************************************** */

//Fix for WP:TFD#Completed_discussions
if ( xfdSetting.xfd_type === "tfd" ) {
	$("#Completed_discussions").parent().nextAll("h4").children().addClass("XFDcloser-ignore");
}
$(xfdSetting.html.head + " > span.mw-headline").not(".XFDcloser-ignore").each(function( headings_index ) {
	$(".mw-headline-number", this).prependTo($(this).parent());		// fix for "Auto-number headings" preference

	var section_header = $(":first-child", this).text();
	if (!section_header) {									// section header wasn't a link
		section_header = $(this).text();
		$(this).wrapInner("<span></span>");     			// wrap with span so it can be styled later
	}

	var discussion_element_class;

	if (xfdSetting.xfd_type === "afd") {		//closed afd's have headings inside the coloured div
		discussion_element_class = $(this).parent().parent().attr("class");				
		if (!discussion_element_class.includes("xfd-closed")) {
			discussion_element_class = "";
		}
	} else {
		discussion_element_class = $(this).parent().next().attr("class");
	}

	if ( discussion_element_class ) {										// only closed discussion have element with any class set
		if (xfdSetting.xfd_type !== "afd") {								// closed afd's already have these classes
			$(this).parent().addClass("boilerplate tfd vfd xfd-closed");	// add classes to enable hiding of closed section headers
		}
	} else {
		var sectionlink = $(this).next().find("a").not(".mw-editsection-visualeditor, .autoCloserButton").attr('href');
		var editsection = sectionlink.split("section=")[1];
		if ( editsection.search("T")<0 ) {
			// if true, we're on the subpage
			if ( xfdSetting.transcludedOnly ) {
				//pages like  WP:WikiProject Deletion sorting/New York  should only have links added to transcluded section
				return;
			}
			nom_page = XFDcloser.getPageText(thispage);
		} else {
			// if false, section is transcluded from a subpage
			nom_page = XFDcloser.getPageText( sectionlink.split("title=")[1].split("&")[0] );
			if ( nom_page === "Wikipedia:Redirects for discussion/Header" || nom_page === "Wikipedia:Redirect/Deletion reasons" ) {
				// ignore headings transcuded from these pages
				return;
			}
			editsection = editsection.substr(2);							// remove "T-" from section number
		}

		var pages=[];
		var firstdate = "";

		if ( xfdSetting.xfd_type === "cfd" ) {
			//CFDs will have basic closure only - no further actions (per WT:CFD discussion https://en.wikipedia.org/wiki/Wikipedia_talk:Categories_for_discussion/Archive_16#Closure_script )
			pages = [-1];
		} else if ( xfdSetting.xfd_type === "rfd" || xfdSetting.xfd_type === "mfd" ) {
			//For MFD, closed discussion are within a collapsed table
			$("table.collapsible").has("div.xfd-closed").addClass("xfd-closed");
			// MFD & RFD have nominated page links prior to span with classes plainlinks, lx
			var $pages_mr = $(this).parent().nextUntil(xfdSetting.html.head + ", div.xfd-closed, table.xfd-closed")
				.find(xfdSetting.html.listitem).has("span.plainlinks.lx").children("span")
				.filter(":first-child").children("a, span.plainlinks:not(.lx)").filter(":first-child");
			$pages_mr.each(function( ii ) {
				pages[ii] = XFDcloser.getPageText($(this).text());
			});
			var disc_text = $(this).parent().nextUntil(xfdSetting.html.head + ", div.xfd-closed, table.xfd-closed").text();
			if ( xfdSetting.xfd_type === "rfd" ) {
				// ignore relisted discussions, and non-boxed closed discussions
				if ( disc_text.includes("Relisted, see Wikipedia:Redirects for discussion") ||
					disc_text.includes("Closed discussion, see full discussion") ) {
					return;
				}
				var firstdate_patt = /(?:\d\d:\d\d, )(\d{1,2} \w+ \d{4})(?: \(UTC\))/;
				var firstdate_match = firstdate_patt.exec(disc_text);
				if (firstdate_match != null ) {
					firstdate = firstdate_match[1];
				}
			}
		} else {
			// AFD & TFD have nominated page links inside span with classes plainlinks, nourlexpansion
			var $pages = $(this).parent().nextUntil(xfdSetting.html.head + ", div.xfd-closed")
				.find(xfdSetting.html.listitem + " > span.plainlinks.nourlexpansion")
				.filter(":nth-child(" + xfdSetting.html.nthSpan + ")").children("a")
				.filter(":first-child");
			//var pages = new Array($pages.length);
			$pages.each(function( ii ) {
				pages[ii] = XFDcloser.getPageText($(this).text());
			});
		}

		// sanity check - is title non-null?
		if (pages[0] == null ) { 
			//Still offer a "basic" close using the section header
			pages = [-1];
		}
		
		// quick closes
		var quick_close_span = "";
		if ( pages[0] !== -1 ) {
			quick_close_span = "<span style='margin-left:13px;font-size:92%;' class='XFDcloser-link-quickclose'>" +
				"[<a class='XFDcloser-link-quickclose-toggle' title='quickClose discussion...'>quickClose</a>" +
				"<span class='XFDcloser-link-quickclose-options' style='display:none;'>&thinsp;" +
					"<a style='cursor:pointer;' title='quickKeep: close as \"keep\", remove nomination " +
					"templates, add old xfd templates to talk pages' class='XFDcloser-link-qk'>qK</a>" +
					" <strong>&middot;</strong> " +
					"<a style='cursor:pointer;' title='quickDelete: close as \"delete\", delete nominated " +
					"pages & their talk pages & redirects, optionally unlink backlinks" + "' class='XFDcloser-link-qd'>qD</a>" +
					"&nbsp;<span title='Cancel' class='XFDcloser-quickclose-cancel'></span>" +
				"</span>" +
				"]" +
			"</span>";
		}
		
		// Add links after heading
		$(this).append("<span id=XFDClosing_" + headings_index + " class='XFDClosing' style='font-size:85%;margin-left:13px;font-weight:normal'>" +
			"<span style='margin-left:13px;'>[<a style='cursor:pointer;' title='Close discussion...' class='XFDcloser-link-close'>Close</a>]</span>" +
			quick_close_span +
			"<span style='margin-left:13px;'>[<a style='cursor:pointer;' title='Relist discussion...' class='XFDcloser-link-relist'>Relist</a>]</span>" +
		"</span>" +
		"<span id='XFDClosing_" + headings_index + "_loading' style='font-size:85%;margin-left:13px;font-weight:normal;'>[XFDcloser loading...]</span>");
		
		// Store nomination data
		$("#XFDClosing_" + headings_index).hide()
		.data("pages", pages)
		.data("nom_page", nom_page)
		.data("section_header", section_header)
		.data("editsection", editsection)
		.data("close_span_id", "XFDClosing_" + headings_index)
		.data("first_date", firstdate);
		
		// Get extra data to store - talk pages, correct namespace, page existance, talk page existance, discussion link, nomination date
		XFDcloser.getExtraInfo("#XFDClosing_" + headings_index);				
	}
}); //end of .each()

// CFDs don't get relisted
if (xfdSetting.xfd_type === "cfd") {
	$("a.XFDcloser-link-relist").parent().empty();
}

// Only TFD allows non-admins to close as "delete" (by listing as "orphan" in holding cell)
if ( !xfdc.isSysop ) {
	if ( xfdSetting.xfd_type !== "tfd" ) {
		$("a.XFDcloser-link-qd").remove();
	} else {
		$("a.XFDcloser-link-qd").attr("title", "quickDelete: close as \"delete\", tag nominated " +
		"templates with {" + "{being deleted}" + "}, add nominated templates to the holding cell " +
		"as \"orphan\"");
	}
}

//Adjust  qd  description for RfD
if ( xfdSetting.xfd_type === "rfd" ) {
	$("a.XFDcloser-link-qd").attr("title", "quickDelete: close as \"delete\", delete nominated " +
	"pages & their talk pages");
}


// When a [Close] link is clicked:
$("a.XFDcloser-link-close").click(function(){
	$(this).parent().parent().children().hide().last().after("<span id='XFDcloser-closing'>Closing...</span>");
	var nom_data = $(this).parent().parent().data();
	XFDcloser.openInterface(nom_data, false);
});

// When a [Relist] link is clicked:
$("a.XFDcloser-link-relist").click(function(){
	$(this).parent().parent().children().hide().last().after("<span id='XFDcloser-closing'>Relisting...</span>");
	var nom_data = $(this).parent().parent().data();
	XFDcloser.openInterface(nom_data, true);
});

// When [QuickClose] is clicked, show quick close links
$("a.XFDcloser-link-quickclose-toggle").click(function(){
	$(this).hide().next().show();
});

// Cancelling QuickClose
$(".XFDcloser-quickclose-cancel")
	.html("&nbsp;x&nbsp;")
	.css({
		"cursor": "pointer",
		"border": "1px solid #777",
		"border-radius": "10px",
		"font-weight": "bold",
		"font-size": "90%",
		"color": "#777",
		"padding": "0px",
		"margin": "0px 1px"
	})
	.click(function(){
		$(this).parent().hide().prev().show();
	});

//When a  qK  link is clicked:
$("a.XFDcloser-link-qk").click(function(){
	var $XFDClosing_span = $(this).parent().parent().parent();
	$XFDClosing_span.children().hide().last().after("<span id='XFDcloser-closing'>Closing...</span>");
	var nom_data = $XFDClosing_span.data();
	
	//Processing function, can be used as a callback
	var process_qK = function(action) {
		if ( action === "proceed" ) {
			qk_input_data = {
				result: "keep",
				resultPunct: ".",
				rationale: "",
				after: "doKeepActions"
			};
			$XFDClosing_span.data("res", "keep");
			if ( xfdSetting.xfd_type==="rfd" ) {
				// no need to resolve redirects - just start processing the close
				XFDcloser.processClose(nom_data, qk_input_data);
			} else {
				// need to check for redirects before proceeding
				XFDcloser.resolveRedirects(nom_data, qk_input_data, false);
			}
		} else {
			$XFDClosing_span.children().show().last().remove();
			return;			
		}
	};
	
	//Warn if mass actions will be peformed
	var warnings = XFDcloser.doSanityChecks(nom_data);
	if (warnings.length) {
		XFDcloser.multiButtonConfirm('Warning', warnings, [
				{ label: 'Cancel', flags: 'safe' },
				{ label: 'Continue', action: 'proceed', flags: 'progressive' }
			], process_qK, {'verbose': true, 'unescape': true} );		
	} else {
		process_qK("proceed");
	}
	

});

//When a  qD  link is clicked:
$("a.XFDcloser-link-qd").click(function(){
	var $XFDClosing_span = $(this).parent().parent().parent();
	$XFDClosing_span.children().hide().last().after("<span id='XFDcloser-closing'>Closing...</span>");
	var nom_data = $XFDClosing_span.data();

	//Processing function, can be used as a callback
	var process_qD = function(action) {
		if ( action === "proceed" ) {
			if ( xfdc.isSysop ) {
				qd_input_data = {
					result: "delete",
					resultPunct: ".",
					rationale: "",
					after: "doDeletion",
					deletetalk: true,
					deleteredir: true
				};
				if ( xfdSetting.xfd_type === "afd" || xfdSetting.xfd_type === "ffd" ) {
					qd_input_data.unlinkbackl = true;
				}
				if ( xfdSetting.xfd_type === "rfd" ) {
					qd_input_data.deleteredir = false;
				}
			} else if ( xfdSetting.xfd_type === "tfd" ) {
				qd_input_data = {
					result: "delete",
					rationale: "",
					after: "holdingCell",
					holdingcell: "ready",
				};		
			} else {
				//currently non-admins can only close TFDs as delete
				$XFDClosing_span.children().show().last().remove();
				return; 
			}
			$XFDClosing_span.data("res", "delete");
			if ( xfdSetting.xfd_type==="rfd" ) {
				// no need to resolve redirects - just start processing the close
				XFDcloser.processClose(nom_data, qd_input_data);
			} else {
				// need to check for redirects before proceeding
				XFDcloser.resolveRedirects(nom_data, qd_input_data, false);
			}
		
		} else {
			$XFDClosing_span.children().show().last().remove();
			return;			
		}
	};	
	
	//Warn if mass actions will be peformed
	var warnings = XFDcloser.doSanityChecks(nom_data);
	if (warnings.length) {
		XFDcloser.multiButtonConfirm('Warning', warnings, [
				{ label: 'Cancel', flags: 'safe' },
				{ label: 'Continue', action: 'proceed', flags: 'progressive' }
			], process_qD, {'verbose': true, 'unescape': true} );		
	} else {
		process_qD("proceed");
	}


});


/* == Interface-related functions == */
/* ********************************* */

//Show the interface
XFDcloser.openInterface = function(nom_data, relisting) {
	//HTML for interface
	var $interfaceDiv = $('<div>')
	.attr('id', 'closeXFD-interface')
	.css({
		"background-color": "#f0f0f0",
		"padding": "0.5em 1em",
		"font-size": "110%"
	}).html(
		'<h4 id="closeXFD-interface-header" class="closeXFD-showFor-relisting"><span id="closeXFD-interface-header-action">Closing</span> discussion <em>' + nom_data.section_header + '</em> <span id="closeXFD-pagecount" style="font-size:80%;">(<span id="closeXFD-pagecount-pages" style="background:#ff8;padding:1px;">' + nom_data.pages.length + ' pages</span> <a id="closeXFD-pagecount-show" style="display:inherit;">[show]</a><a id="closeXFD-pagecount-hide" style="display:none;">[hide]</a>)</span></h4>'+
		'<ul id="closeXFD-nominatedpages" style="display:none;font-size:90%;" class="closeXFD-showFor-relisting"><li>' + nom_data.pages.join('</li><li>') + '</li></ul>'+
		'<hr class="closeXFD-showFor-relisting">'+
		'<div style="text-align:center;clear:both;" class="closeXFD-nonSysop-only">'+
			'<em>See the <a href="https://en.wikipedia.org/wiki/WP:NACD" target="_blank">WP:NACD</a> guideline for advice on appropriate and inappropriate closures</em>' +
		'</div><hr class="closeXFD-nonSysop-only">' +
		'<div style="float:left; width:99%; padding-bottom:1%;" id="closeXFD-resultContainer"><strong style="margin-right: 2em;">Result</strong> '+
			'<span style="white-space:nowrap;">(&thinsp;'+
				'<input type="checkbox" name="closeXFD-speedy" id="closeXFD-speedy" /><label for="closeXFD-speedy">Speedy</label>'+
			'&thinsp;)</span><br/>'+
			'<span class="closeXFD-option"><input type="radio" name="closeXFD-result" class="closeXFD-keepResult" id="closeXFD-result-keep"  /><label for="closeXFD-result-keep">Keep</label></span>'+
			'<span class="closeXFD-option closeXFD-FFD-na"><input type="radio" name="closeXFD-result" class="closeXFD-keepResult" id="closeXFD-result-redir" /><label for="closeXFD-result-redir">Redirect</label></span>'+
			'<span class="closeXFD-option closeXFD-RFD-only"><input type="radio" name="closeXFD-result" class="closeXFD-keepResult" id="closeXFD-result-softredir" /><label for="closeXFD-result-softredir">Soft redirect</label></span>'+
			'<span class="closeXFD-option"><input type="radio" name="closeXFD-result" class="closeXFD-keepResult" id="closeXFD-result-nocon" /><label for="closeXFD-result-nocon">No consensus</label></span>'+
			'<span class="closeXFD-option closeXFD-FFD-na closeXFD-RFD-na"><input type="radio" name="closeXFD-result" id="closeXFD-result-merge" /><label for="closeXFD-result-merge">Merge</label></span>'+
			'<span class="closeXFD-option closeXFD-sysop-only closeXFD-nonSysop-TFDonly"><input type="radio" name="closeXFD-result" class="closeXFD-deleteResult" id="closeXFD-result-delete"  /><label for="closeXFD-result-delete">Delete</label></span>'+
			'<span class="closeXFD-option closeXFD-nonSysop-only closeXFD-TFD-na"><input type="radio" name="closeXFD-result" class="closeXFD-deleteResult" id="closeXFD-result-delete-disabled" disabled="disabled"  /><label for="closeXFD-result-delete-disabled" style="color:#777;">Delete <span title="Non-admin closure is not appropriate when the result will require action by an administrator (per [[WP:BADNAC]])" class="closeXFD-interface-tooltip"></span></label></span>'+
			'<span class="closeXFD-option closeXFD-sysop-only closeXFD-RFD-na" id="closeXFD-resultContainer-softdel"><input type="radio" name="closeXFD-result" class="closeXFD-deleteResult" id="closeXFD-result-softdel" /><label for="closeXFD-result-softdel">Soft delete</label></span>'+
			'<span class="closeXFD-option"><input type="radio" name="closeXFD-result" id="closeXFD-result-custom" /><label for="closeXFD-result-custom">Custom</label></span>'+
			'<p id="closeXFD-closetype-notes"></p>'+
		'</div>'+
		'<div style="clear:both;padding-bottom:1%;" id="closeXFD-resultContainer-custom" class="closeXFD-visibleFor-customResult"> '+
			'<label for="closeXFD-result-custom-input">Custom result:</label>'+
			'<input type="text" name="closeXFD-result-custom-input" id="closeXFD-result-custom-input" />'+
		'</div>'+
		'<div style="clear:both;padding-bottom:1%;" id="closeXFD-resultContainer-target" class="closeXFD-visibleFor-mergeResult closeXFD-visibleFor-redirectResult">'+
			'<label id="closeXFD-result-target-label" for="closeXFD-result-target">Target:</label> '+
			'<input type="text" name="closeXFD-result-target" id="closeXFD-result-target" />'+
		'</div>'+
		'<div style="clear:both;" class="closeXFD-showFor-relisting"><strong id="closeXFD-rationale-label">Additional rationale</strong> (optional):<br/>'+
			'<textarea id="closeXFD-rationale" rows=4 style="width:99%;"></textarea>'+
		'</div>'+
		'<div style="clear:both;padding-bottom:1%;">&nbsp;'+
			'<input type="checkbox" name="closeXFD-rationale-sentence" id="closeXFD-rationale-sentence" /><label for="closeXFD-rationale-sentence">Rationale is not a new sentence</label>'+
		'</div>'+
		'<div id="closeXFD-afterContainer" class="closeXFD-listOfOptions"><strong>After closing:</strong><br/>'+
			'<span class="closeXFD-visibleFor-keepResult closeXFD-visibleFor-customResult closeXFD-basic-na"><input type="radio" name="closeXFD-after" id="closeXFD-after-doKeepActions" /><label for="closeXFD-after-doKeepActions">Update pages and talk pages <span title="Remove nomination templates, and add old xfd templates to talk pages" class="closeXFD-interface-tooltip"></span> </label><br /></span>'+
			'<span class="closeXFD-visibleFor-mergeResult closeXFD-basic-na"><input type="radio" name="closeXFD-after" id="closeXFD-after-doMergeActions" /><label for="closeXFD-after-doMergeActions">Update pages and talk pages <span title="Replace nomination templates with merge templates, and add old xfd templates to talk pages" class="closeXFD-interface-tooltip"></span></label><br /></span>'+
			'<span class="closeXFD-visibleFor-redirectResult closeXFD-basic-na"><input type="radio" name="closeXFD-after" id="closeXFD-after-doRedirectActions" /><label for="closeXFD-after-doRedirectActions">Redirect pages and update talk pages <span title="Redirect nominated pages to the specified target, and add old xfd templates to talk pages" class="closeXFD-interface-tooltip"></span></label><br /></span>'+
			'<span class="closeXFD-visibleFor-deleteResult closeXFD-visibleFor-customResult closeXFD-sysop-only closeXFD-basic-na"><input type="radio" name="closeXFD-after" id="closeXFD-after-doDeletion" /><label for="closeXFD-after-doDeletion">Delete pages <span title="Delete nominated pages and (unless otherwise specified) their talk pages" class="closeXFD-interface-tooltip"></span></label><br /></span>'+
			'<span class="closeXFD-visibleFor-deleteResult closeXFD-visibleFor-customResult closeXFD-TFD-only closeXFD-basic-na"><input type="radio" name="closeXFD-after" id="closeXFD-after-holdingCell" /><label for="closeXFD-after-holdingCell">List pages at holding cell <span title="Replace nomination template with {' + '{Being deleted}' + '}, and list at the specified holding cell section" class="closeXFD-interface-tooltip"></span></label><br /></span>'+
			'<span class="closeXFD-visibleFor-deleteResult closeXFD-visibleFor-customResult closeXFD-nonSysop-only closeXFD-basic-na"><input type="radio" name="closeXFD-after" id="closeXFD-after-tagSpeedy" /><label for="closeXFD-after-tagSpeedy">Tag for speedy deletion <span title="Tag nominated pages for G6 speedy deletion, and (unless otherwise specified) tag their talk pages for G8 speedy deletion" class="closeXFD-interface-tooltip"></span></label><br /></span>'+
			'<span><input type="radio" name="closeXFD-after" id="closeXFD-after-noAction" /><label for="closeXFD-after-noAction">No automated actions</label><br /></span>'+
		'</div>'+
		'<div id="closeXFD-optionsContainer" class="closeXFD-listOfOptions closeXFD-visibleFor-deleteResult closeXFD-visibleFor-customResult closeXFD-basic-na"><strong>Options:</strong><br/>'+
			'<input type="checkbox" name="closeXFD-option-deletetalk" id="closeXFD-option-deletetalk" /><label for="closeXFD-option-deletetalk">Do not <span class="closeXFD-sysop-only">delete talk pages</span><span class="closeXFD-nonSysop-only">tag talk pages for speedy deletion</span></label><br /></span>'+
			'<span class="closeXFD-sysop-only closeXFD-RFD-na"><input type="checkbox" name="closeXFD-option-deleteredir" id="closeXFD-option-deleteredir" /><label for="closeXFD-option-deleteredir">Also delete redirects</label><br /></span>'+
			'<span class="closeXFD-TFD-na closeXFD-MFD-na"><input type="checkbox" name="closeXFD-option-unlinkbackl" id="closeXFD-option-unlinkbackl" /><label for="closeXFD-option-unlinkbackl">Unlink backlinks</label><br /></span>'+
			'<span class="closeXFD-sysop-only closeXFD-FFD-na"><input type="checkbox" style="visibility:hidden;" /><a id="closeXFD-option-deleteAndRedir">Delete and redirect...</a></span>'+
		'</div>'+
		'<div id="closeXFD-redirectOptionsContainer" class="closeXFD-listOfOptions closeXFD-visibleFor-redirectResult closeXFD-basic-na" style="display:none;"><strong>Options:</strong><br/>'+
			'<span id="closeXFD-redirectOptionsContainer-deleteFirst" class="closeXFD-sysop-only"><input type="checkbox" name="closeXFD-redirectOption-deleteFirst" id="closeXFD-redirectOption-deleteFirst" /><label for="closeXFD-redirectOption-deleteFirst">Delete before redirecting</label><br /></span>'+
			'<span id="closeXFD-rcatOptionsContainer-on">'+
				'<input type="radio" name="closeXFD-rcatOptions" id="closeXFD-rcatOptions-on" />'+ 
				'<label for="closeXFD-result-redir-rcats"><a href="https://en.wikipedia.org/wiki/Template:R_template_index" target="_blank">Rcats</a>: <span title="Full markup required, e.g. &quot;{'+'{R to section}'+'}&quot;. Multiple rcats can be specified, e.g. &quot;{'+'{R from book}'+'}{'+'{R to anchor}'+'}&quot;. Leave blank if unsure which rcat to use." class="closeXFD-interface-tooltip"></span></label>'+
			'</span><input type="text" name="closeXFD-result-redir-rcats" id="closeXFD-result-redir-rcats" /><br />'+
			'<span id="closeXFD-rcatOptionsContainer-off"><input type="radio" checked name="closeXFD-rcatOptions" id="closeXFD-rcatOptions-off" /><label for="closeXFD-redirectOption-rcats-off">Do not add rcats <span title="Most redirects should have an rcat specified. Only use this option if you are sure that no rcats are appropriate." class="closeXFD-interface-tooltip"></span></label></span>'+ 
		'</div>'+					
		'<div id="closeXFD-holdingcellContainer" class="closeXFD-basic-na">'+
			'<div id="closeXFD-holdingcellsections" class="closeXFD-listOfOptions closeXFD-visibleFor-deleteResult closeXFD-TFD-only "><strong>Holding cell section:</strong><br/>'+
				'<input type="radio" name="closeXFD-holdingcell" id="closeXFD-holdingcell-review" /><label for="closeXFD-holdingcell-review">Review</label><br />'+
				'<input type="radio" name="closeXFD-holdingcell" id="closeXFD-holdingcell-convert" /><label for="closeXFD-holdingcell-convert">Convert</label><br />'+
				'<input type="radio" name="closeXFD-holdingcell" id="closeXFD-holdingcell-substitute" /><label for="closeXFD-holdingcell-substitute">Substitute</label><br />'+
				'<input type="radio" name="closeXFD-holdingcell" id="closeXFD-holdingcell-orphan" /><label for="closeXFD-holdingcell-orphan">Orphan</label><br />'+
				'<input type="radio" name="closeXFD-holdingcell" id="closeXFD-holdingcell-ready" /><label for="closeXFD-holdingcell-ready">Ready for deletion</label>'+
			'</div>'+
			'<div id="closeXFD-holdingcellsections-merge" class="closeXFD-listOfOptions closeXFD-visibleFor-mergeResult closeXFD-TFD-only"><strong>Holding cell section:</strong><br/>'+
				'<input type="radio" name="closeXFD-holdingcell" id="closeXFD-holdingcell-merge-arts" /><label for="closeXFD-holdingcell-merge-arts">Merge (Arts)</label><br />'+
				'<input type="radio" name="closeXFD-holdingcell" id="closeXFD-holdingcell-merge-geopolgov" /><label for="closeXFD-holdingcell-merge-geopolgov">Merge (Geography, politics and governance)</label><br />'+
				'<input type="radio" name="closeXFD-holdingcell" id="closeXFD-holdingcell-merge-religion" /><label for="closeXFD-holdingcell-merge-religion">Merge (Religion)</label><br />'+
				'<input type="radio" name="closeXFD-holdingcell" id="closeXFD-holdingcell-merge-sports" /><label for="closeXFD-holdingcell-merge-sports">Merge (Sports)</label><br />'+
				'<input type="radio" name="closeXFD-holdingcell" id="closeXFD-holdingcell-merge-other" /><label for="closeXFD-holdingcell-merge-other">Merge (Other)</label><br />'+
				'<input type="radio" name="closeXFD-holdingcell" id="closeXFD-holdingcell-merge-meta" /><label for="closeXFD-holdingcell-merge-">Merge (Meta)</label>'+
			'</div>'+
		'</div>'+
		'<div style="text-align:center;clear:both;" id="closeXFD-interface-buttons" class="closeXFD-showFor-relisting">'+
			'<button id="closeXFD-interface-closedisc" style="margin:1em;">Close Discussion</button>'+
			'<button id="closeXFD-interface-preview" style="margin:1em;">Preview</button>'+
			'<button id="closeXFD-interface-relistdisc" style="margin:1em;" class="closeXFD-showFor-relisting closeXFD-relist-only">Relist Discussion</button>'+
			'<button id="closeXFD-interface-previewRelist" style="margin:1em;" class="closeXFD-showFor-relisting closeXFD-relist-only">Preview</button>'+
			'<button id="closeXFD-interface-cancel" style="margin:1em;" class="closeXFD-showFor-relisting">Cancel</button>'+
		'</div>'+
		'<p id="closeXFD-preview-output" class="closeXFD-showFor-relisting"></p>'
	);
	
	var interfaceWindow = new Morebits.simpleWindow( Math.min(900, Math.floor(window.innerWidth*0.8)), Math.floor(window.innerHeight*0.9) );
	interfaceWindow.setTitle( "XFDcloser" );
	interfaceWindow.addFooterLink("script help", "WP:XFDC");
	interfaceWindow.addFooterLink("feedback", "WT:XFDC");
	interfaceWindow.setContent( $interfaceDiv.get(0) );
	interfaceWindow.display();
	$('a.ui-dialog-titlebar-close.ui-corner-all').remove();
	
	//Apply styles
	$interfaceDiv.parent().css("background-color", "#f0f0f0");
	$('span.closeXFD-option').css({
		"white-space": "nowrap",
		"min-width": "8em",
		"margin-right": "0.3em",
		"display": "block",
		"float": "left"
	}).after("<wbr>");
	$('.closeXFD-listOfOptions').css({
		"float": "left",
		"width": "45%",
		"padding-bottom": "1%"
	});
	$("#closeXFD-preview-output").css({
		"background-color": "#ffffff",
		"border": "1px solid #777777",
		"margin-top": "0px",
		"padding": "0px 10px",
		"display": "none"
	});
	$(".closeXFD-interface-tooltip").html("&thinsp;?&thinsp;").css({
		"border": "1px solid #33a",
		"border-radius": "10px",
		"font-weight": "bold",
		"font-size": "80%",
		"color": "#22a",
		"padding": "0px"
	});
	
	//Hide fields only available for certain results
	$('.closeXFD-visibleFor-keepResult, .closeXFD-visibleFor-deleteResult, .closeXFD-visibleFor-redirectResult, .closeXFD-visibleFor-mergeResult, .closeXFD-visibleFor-customResult').hide();
	
	//Remove non-relevent fields, based on xfdSetting.xfd_type, sysop status, etc
	var redirect_label = "Redirect";
	if (xfdSetting.xfd_type === "rfd") {
		redirect_label = "Retarget";
		$('#closeXFD-result-redir').next().text(redirect_label);
		$(".closeXFD-RFD-na").remove();
	} else {
		$(".closeXFD-RFD-only").remove();
	}	
	if (xfdSetting.xfd_type !== "tfd") {
		$(".closeXFD-TFD-only").remove();
	} else {
		$(".closeXFD-TFD-na").remove();
	}
	if (xfdSetting.xfd_type === "ffd") {
		$(".closeXFD-FFD-na").remove();
	}
	if (xfdSetting.xfd_type === "mfd") {
		$(".closeXFD-MFD-na").remove();
	}	
	if (xfdc.isSysop) {
		$(".closeXFD-nonSysop-only").remove();
	} else if ( xfdSetting.xfd_type === "tfd" ) {
		$(".closeXFD-sysop-only").not(".closeXFD-nonSysop-TFDonly").remove();
		$("#closeXFD-after-tagSpeedy").parent().remove(); // not currently allowed at any XfD
	} else {
		$(".closeXFD-sysop-only").remove();
		$("#closeXFD-after-tagSpeedy").parent().remove(); // not currently allowed at any XfD
	}

	//Show/hide page count (or basic close info)
	if (nom_data.pages.length === 1 && nom_data.pages[0] !== -1 ) {
		$("#closeXFD-pagecount-pages").text("1 page"); //change plural to singular
	}
	$("#closeXFD-pagecount-show, #closeXFD-pagecount-hide").click(function() {
		$("#closeXFD-nominatedpages, #closeXFD-pagecount-show, #closeXFD-pagecount-hide").toggle();
	});
	
	//If relisting, adjust labels and remove non-relist stuff
	var basic_close_note = "";
	if (relisting) {
		$("#closeXFD-interface-header-action").text("Relisting");
		$("#closeXFD-rationale-label").text("Relist comment");
		$("#closeXFD-rationale").attr("rows", 2);
		$("#closeXFD-interface, #closeXFD-interface-buttons").children().not(".closeXFD-showFor-relisting").remove();
		if (nom_data.pages[0] === -1) {
			$("#closeXFD-pagecount-pages").text("basic relist only");
			basic_relist_note = '<li>Nominated pages were not detected. You can still relist this ' + xfdSetting.xfd_type.toUpperCase() + ' discussion, but updating the nomination templates will need to be done manually &ndash; see <a href="https://en.wikipedia.org/wiki/WP:' + xfdSetting.xfd_type.toUpperCase() + 'AI" target="_blank">WP:' + xfdSetting.xfd_type.toUpperCase() + 'AI</a> for instructions.</li>';
			$("#closeXFD-nominatedpages").html(basic_relist_note);
		}
	} else {
		$(".closeXFD-relist-only").remove();
		basic_close_note = '<li>Nominated pages were not detected. You can still close this ' + xfdSetting.xfd_type.toUpperCase() + ' discussion, but further actions to implement the close will need to be done manually &ndash; see <a href="https://en.wikipedia.org/wiki/WP:' + xfdSetting.xfd_type.toUpperCase() + 'AI" target="_blank">WP:' + xfdSetting.xfd_type.toUpperCase() + 'AI</a> for instructions.</li>';
	}
	
	if ( nom_data.pages[0] === -1 && !relisting ) {				 // Basic close only
		$(".closeXFD-basic-na").remove();						 // Remove non-applicable options
		$("#closeXFD-after-noAction").prop("checked", true);	 // Make sure "No automated actions" is checked
		$("#closeXFD-pagecount-pages").text("basic close only"); // Replace page count/list with basic close info
		$("#closeXFD-nominatedpages").html(basic_close_note);
	}
	
	//Now show modal
	$("#closeXFD-modal").css("display", "");
	
	//On clicking a result, hide & clear fields not for that result, and show fields that are for that result
	$('#closeXFD-resultContainer').click(function() {
		var selected_after;
		if( $('#closeXFD-result-keep').prop('checked') || $('#closeXFD-result-nocon').prop('checked') ) {
			//Keep-type result
			$('.closeXFD-visibleFor-deleteResult, .closeXFD-visibleFor-mergeResult, .closeXFD-visibleFor-customResult, .closeXFD-visibleFor-redirectResult').not('.closeXFD-visibleFor-keepResult').hide().find('input').prop('checked', false).val('');
			$('#closeXFD-result-redir').next().text(redirect_label);
			$('.closeXFD-visibleFor-keepResult').show();
			//default 'after' selection:
			selected_after = $('input[name="closeXFD-after"]:checked');
			if (typeof $(selected_after).val() == "undefined") {
				$('#closeXFD-after-doKeepActions').prop('checked', true);
			}
		} else if ( $('#closeXFD-result-redir').prop('checked') ) {
			//Redirect (keep) result
			$('.closeXFD-visibleFor-keepResult, .closeXFD-visibleFor-deleteResult, .closeXFD-visibleFor-mergeResult, .closeXFD-visibleFor-customResult').not('.closeXFD-visibleFor-redirectResult').hide().find('input').prop('checked', false).val('');
			$('#closeXFD-result-target-label').text(redirect_label + " to:");
			if (xfdSetting.xfd_type === "afd") {
				//Default rcat for AFDs
				$("#closeXFD-result-redir-rcats").val("{"+"{R to related topic}"+"}");
			}
			$('.closeXFD-visibleFor-redirectResult').not("#closeXFD-redirectOptionsContainer").show();
			//default 'after' selection:
			selected_after = $('input[name="closeXFD-after"]:checked');
			if (typeof $(selected_after).val() == "undefined") {
				$('#closeXFD-after-doRedirectActions').prop('checked', true);
				$("#closeXFD-redirectOptionsContainer").show();
				$('#closeXFD-rcatOptions-on').prop('checked', true);
				$('#closeXFD-result-redir-rcats').prop('disabled', false);
			}
		} else if ( $('#closeXFD-result-softredir').prop('checked') ) {
			//Soft redirect result (RFD only)
			$('.closeXFD-visibleFor-keepResult, .closeXFD-visibleFor-deleteResult, .closeXFD-visibleFor-mergeResult, .closeXFD-visibleFor-customResult').not('.closeXFD-visibleFor-redirectResult').hide().find('input').prop('checked', false).val('');
			$('#closeXFD-result-target-label').text("Soft redirect to:");
			$('.closeXFD-visibleFor-redirectResult').not("#closeXFD-redirectOptionsContainer").show();
			//default 'after' selection:
			selected_after = $('input[name="closeXFD-after"]:checked');
			if (typeof $(selected_after).val() == "undefined") {
				$('#closeXFD-after-doRedirectActions').prop('checked', true);
				$("#closeXFD-redirectOptionsContainer").show();
				$('#closeXFD-rcatOptions-on').prop('checked', true);
			$('#closeXFD-result-redir-rcats').prop('disabled', false);
			}
		} else if ( $('#closeXFD-result-merge').prop('checked') ) {
			//Merge result
			$('.closeXFD-visibleFor-deleteResult, .closeXFD-visibleFor-keepResult, .closeXFD-visibleFor-customResult, .closeXFD-visibleFor-redirectResult').not('.closeXFD-visibleFor-mergeResult').hide().find('input').prop('checked', false).val('');
			$('#closeXFD-result-redir').next().text(redirect_label);
			$('#closeXFD-result-target-label').text("Merge to:");
			$('.closeXFD-visibleFor-mergeResult').not('#closeXFD-holdingcellsections-merge').show();
			//default 'after' selection:
			selected_after = $('input[name="closeXFD-after"]:checked');
			if (typeof $(selected_after).val() == "undefined") {
				$('#closeXFD-after-doMergeActions').prop('checked', true);
				$('#closeXFD-holdingcellsections-merge').show();
			}
		} else if ( $('#closeXFD-result-custom').prop('checked') ) {
			//Custom result
			$('.closeXFD-visibleFor-deleteResult, .closeXFD-visibleFor-keepResult, .closeXFD-visibleFor-mergeResult, .closeXFD-visibleFor-redirectResult').not('.closeXFD-visibleFor-customResult').hide().find('input').prop('checked', false).val('');
			$('#closeXFD-result-redir').next().text(redirect_label);
			$('.closeXFD-visibleFor-customResult').show();
			if ( $('#closeXFD-after-doDeletion').prop('checked') || $('#closeXFD-after-tagSpeedy').prop('checked') ) {
				$('#closeXFD-optionsContainer').show();
			} else {
				$('#closeXFD-optionsContainer').hide();
			}
			if ( $('#closeXFD-after-holdingCell').prop('checked') ) {
				$('#closeXFD-holdingcellsections').show();
			} else {
				$('#closeXFD-holdingcellsections-merge, #closeXFD-holdingcellsections').hide();
			}
		} else if ( $('#closeXFD-result-delete').prop('checked') || $('#closeXFD-result-softdel').prop('checked') ) {
			//Delete-type result
			$('.closeXFD-visibleFor-customResult, .closeXFD-visibleFor-keepResult, .closeXFD-visibleFor-mergeResult, .closeXFD-visibleFor-redirectResult').not('.closeXFD-visibleFor-deleteResult').hide().find('input').prop('checked', false).val('');
			$('#closeXFD-result-redir').next().text(redirect_label);
			$('.closeXFD-visibleFor-deleteResult').not('#closeXFD-OptionsContainer, #closeXFD-holdingcellsections, #closeXFD-holdingcellsections-merge').show();
			//default 'after' selection:
			selected_after = $('input[name="closeXFD-after"]:checked');
			if (typeof $(selected_after).val() == "undefined") {
				$('#closeXFD-after-doDeletion, #closeXFD-after-tagSpeedy').prop('checked', true);
				$('#closeXFD-OptionsContainer').show();
				$('#closeXFD-option-deleteredir, #closeXFD-option-unlinkbackl').prop('checked', true);
			}
		}
		interfaceWindow.setHeight(Math.floor(window.innerHeight*0.9));
	});
	
	//On clicking an after action, show/hide other options as appropriate
	$('#closeXFD-afterContainer').click(function() {
		//Deletion-related options should be shown if  a "deletion actions" is checked, and hidden again when it is unchecked
		if ( $('#closeXFD-result-delete').prop('checked') || $('#closeXFD-result-softdel').prop('checked') || $('#closeXFD-result-custom').prop('checked') ) {
			if ( $('#closeXFD-after-doDeletion').prop('checked') || $('#closeXFD-after-tagSpeedy').prop('checked') ) {
				$('#closeXFD-optionsContainer').show();
			} else {
				$('#closeXFD-optionsContainer').hide();
			}
		} else if ( $('#closeXFD-result-redir').prop('checked') || $('#closeXFD-result-softredir').prop('checked') ) {
			//Similarly for other redirection-related options
			if ( $('#closeXFD-after-noAction').prop('checked') ) {
				$('#closeXFD-redirectOptionsContainer').hide();
			} else {
				$('#closeXFD-redirectOptionsContainer').show();
				$('#closeXFD-rcatOptions-on').prop('checked', true);
				$('#closeXFD-result-redir-rcats').prop('disabled', false);
			}						
		}
		//And holding cell sections should only be shown when appropriate "after" is selected (TFD-only)
		if ( xfdSetting.xfd_type === "tfd" ) {
			if ( $('#closeXFD-after-doMergeActions').prop('checked') ) {
				$('#closeXFD-holdingcellsections-merge').show();
			} else if ( $('#closeXFD-after-holdingCell').prop('checked') ) {
				$('#closeXFD-holdingcellsections').show();
			} else {
				$('#closeXFD-holdingcellsections-merge, #closeXFD-holdingcellsections').hide();
			}
		}
	});

	//For soft delete, prepend rationale with REFUND message
	$('#closeXFD-resultContainer-softdel').children().click(function() {
		old_rationale = $('#closeXFD-rationale').val();
		if (!old_rationale.includes("[[WP:REFUND]]")) {
			$('#closeXFD-rationale').val("[[WP:REFUND]] applies. " + old_rationale);
		}
	});

	//For delete before redirect, adjust result label
	$('#closeXFD-redirectOption-deleteFirst').on('change', function() {
		var $redir_label = $('#closeXFD-result-redir').next();
		var rLabel = redirect_label;
		if ( $('#closeXFD-result-softredir').prop('checked') ) {
			// overrides for soft redirect
			$redir_label = $('#closeXFD-result-softredir').next();
			rLabel = "Soft redirect";
		}
		if ( $redir_label.text() === rLabel ) {
			$redir_label.text("Delete and " + rLabel.toLowerCase());
		} else {
			$redir_label.text(rLabel);
		}
	});
	//Switch from "Delete" to "Delete and redirect"
	$('#closeXFD-option-deleteAndRedir').click(function() {
		$('.closeXFD-visibleFor-keepResult, .closeXFD-visibleFor-deleteResult, .closeXFD-visibleFor-mergeResult, .closeXFD-visibleFor-customResult').not('.closeXFD-visibleFor-redirectResult').hide().find('input').prop('checked', false).val('');
		$('#closeXFD-result-target-label').text(redirect_label + " to:");
		if (xfdSetting.xfd_type === "afd") {
			//Default rcat for AFDs
			$("#closeXFD-result-redir-rcats").val("{"+"{R to related topic}"+"}");
		}
		$('.closeXFD-visibleFor-redirectResult').not("#closeXFD-redirectOptionsContainer").show();
		//default 'after' selection:
		selected_after = $('input[name="closeXFD-after"]:checked');
		if (typeof $(selected_after).val() == "undefined") {
			$('#closeXFD-after-doRedirectActions').prop('checked', true);
			$("#closeXFD-redirectOptionsContainer").show();
			$('#closeXFD-rcatOptions-on').prop('checked', true);
			$('#closeXFD-result-redir-rcats').prop('disabled', false);
		}
		$('#closeXFD-redirectOption-deleteFirst').prop('checked', true);
		$('#closeXFD-result-redir').prop('checked', true).next().text("Delete and " + redirect_label.toLowerCase());
	});
	//Switch between rcats being on/off
	$('#closeXFD-rcatOptionsContainer-off').children().click(function() {
		$('#closeXFD-rcatOptions-off').prop('checked', true);
		$('#closeXFD-result-redir-rcats').prop('disabled', true);
	});
	$('#closeXFD-rcatOptionsContainer-on').children().click(function() {
		$('#closeXFD-rcatOptions-on').prop('checked', true);
		$('#closeXFD-result-redir-rcats').prop('disabled', false);
	});	

	// Get the modal element
	//var $modal = $('#closeXFD-modal');
	
	
	// --- On button clicks: --- //

	// Cancel button clicked
	$('#closeXFD-interface-cancel').click(function() {
		interfaceWindow.close();
		$("#XFDcloser-closing")
			.siblings()
				.show()
			.end()
			.remove();
	});
	
	// Close discussion button clicked
	$('#closeXFD-interface-closedisc').click(function() {
		var input_data = new XFDcloser.getInterfaceData();
		
		//If there's an error, show error message
		if (input_data.errorID) {
			$(".closeXFD-errorNote").parent().css("border", "");
			$(".closeXFD-errorNote").remove(); //remove old messages
			XFDcloser.showInterfaceError(input_data.errorID);
			return;
		}
		
		//Processing function, can be used as a callback
		var process_close = function(action) {
			if ( action === "proceed" ) {
				interfaceWindow.close();
				// make result avaialable for later
				$("#" + nom_data.close_span_id).data("res", input_data.result);
				if ( input_data.after==="noAction" || xfdSetting.xfd_type==="rfd" ) {
					// no need to resolve redirects - just start processing the close
					XFDcloser.processClose(nom_data, input_data);
				} else {
					// need to check for redirects before proceeding
					XFDcloser.resolveRedirects(nom_data, input_data, false);
				}
			}
		};
		
		//If not just closing: check each page (& target), add warning if wrong namespace
		//FORLATER: Consider checking if target page exists... perhaps not urgent, given that close 
		//          can be previewed.
		var warnings = XFDcloser.doSanityChecks(nom_data, input_data);
		if (warnings.length) {
			XFDcloser.multiButtonConfirm('Warning', warnings, [
					{ label: 'Cancel', flags: 'safe' },
					{ label: 'Continue', action: 'proceed', flags: 'progressive' }
				], process_close, {'verbose': true, 'unescape': true} );		
		} else {
			process_close("proceed");
		}
		
	});

	// Preview button clicked
	$('#closeXFD-interface-preview').click(function() {
		input_data = new XFDcloser.getInterfaceData();
		
		//remove old error messages
		$(".closeXFD-errorNote").parent().css("border", "");
		$(".closeXFD-errorNote").remove(); 
		
		if (input_data.result && input_data.errorID!=="#closeXFD-resultContainer-target") {
			var preview_wikitext="";
			var to_target = "";
			if (input_data.target){
				to_target = " to " + input_data.targetLink;
			}
			if (input_data.rationale) {
				preview_wikitext = "The result of the discussion was '''" + input_data.result + "'''" + to_target + input_data.resultPunct + " " + input_data.rationale + " " + xfdc.sig;
			} else {
				preview_wikitext = "The result of the discussion was '''" + input_data.result + "'''" + to_target + ". " + xfdc.sig;
			}
			new mw.Api().get( {
				action: 'parse',
				contentmodel: 'wikitext',
				text: preview_wikitext,
			} ).done(function( result ) {
				preview_html = result.parse.text["*"];
				$("#closeXFD-preview-output").html(preview_html).show().find("a").attr("target", "_blank");
			}).fail(function(api_code, api_result) {
				var details = "";
				if ( api_code === "http" ) {
					details = "<span style='font-size:85%;'>Details: <i>HTTP error: " + api_result.textStatus + "</i></span>"; // api_result contains the jqXHR object
				} else if ( api_code === "ok-but-empty" ) {
					details = "<span style='font-size:85%;'>Details: <i>Got an empty response from the server</i></span>";
				} else {
					details = "<span style='font-size:85%;'>Details: <i>API error: " + api_code + "</i></span>";
				}
				$("#closeXFD-preview-output").html("Error: Preview failed " + details).show();
			});
		} else {
			XFDcloser.showInterfaceError(input_data.errorID);
		}
	});
	
	// Relist discussion button clicked
	$('#closeXFD-interface-relistdisc').click(function() {
		var relist_comment = $('#closeXFD-rationale').val().replace(/(\|)(?!(?:[^\[]*]|[^\{]*}))/g, "&#124;");

		//Processing function, can be used as a callback
		var process_relist = function(action) {
			if ( action === "proceed" ) {
				interfaceWindow.close();
				// make result avaialable for later
				$("#" + nom_data.close_span_id).data("res", "relist");
				if ( xfdSetting.xfd_type==="ffd" || xfdSetting.xfd_type==="tfd" ) {
					XFDcloser.resolveRedirects(nom_data, relist_comment, true);
				} else {
					XFDcloser.processRelist(nom_data, relist_comment);
				}
			}
		};
		
		var warnings = XFDcloser.doSanityChecks(nom_data, null, true);
		if (warnings.length) {
			XFDcloser.multiButtonConfirm('Warning', warnings, [
					{ label: 'Cancel', flags: 'safe' },
					{ label: 'Continue', action: 'proceed', flags: 'progressive' }
				], process_relist, {'verbose': true, 'unescape': true} );		
		} else {
			process_relist("proceed");
		}

	});
	
	// Preview (relisting) button clicked
	$('#closeXFD-interface-previewRelist').click(function() {
		relist_comment = $('#closeXFD-rationale').val().replace(/(\|)(?!(?:[^\[]*]|[^\{]*}))/g, "&#124;");
		var preview_wikitext = "{" + "{Relist|1=" + relist_comment + "}}";
		new mw.Api().get( {
			action: 'parse',
			contentmodel: 'wikitext',
			text: preview_wikitext,
		} ).done(function( result ) {
			preview_html = result.parse.text["*"];
			$("#closeXFD-preview-output").html(preview_html).show().find("a").attr("target", "_blank");
		}).fail(function(api_code, api_result) {
			var details = "";
			if ( api_code === "http" ) {
				details = "<span style='font-size:85%;'>Details: <i>HTTP error: " + api_result.textStatus + "</i></span>"; // api_result contains the jqXHR object
			} else if ( api_code === "ok-but-empty" ) {
				details = "<span style='font-size:85%;'>Details: <i>Got an empty response from the server</i></span>";
			} else {
				details = "<span style='font-size:85%;'>Details: <i>API error: " + api_code + "</i></span>";
			}
			$("#closeXFD-preview-output").text("Error: Preview failed " + details).show();
		});

	});
		
};

XFDcloser.showInterfaceError = function(error_id) {
	error_note = '<p class="closeXFD-errorNote" style="color:#f00; font-style:italic; font-size:90%;">Required field!</p>';
	$(error_id).css("border", "1px solid #ff0000");
	$(error_id).fadeTo( "fast", 0.33 ).fadeTo( "fast", 0.8 ).fadeTo( "fast", 0.4 ).fadeTo( "fast", 1 ).append(error_note);
};


//Get data from interface
XFDcloser.getInterfaceData = function() {
	
	//Result
	var selected_result = $('input[name="closeXFD-result"]:checked');
	if (typeof $(selected_result).val() == "undefined") {
		this.errorID = "#closeXFD-resultContainer";  //error in not choosing a result
		return;
	}
	selected_result_label = selected_result.next().text().toLowerCase();
	if (selected_result_label == "custom") {
		if( $('#closeXFD-result-custom-input').val().length === 0 ) {
			this.errorID = "#closeXFD-resultContainer-custom";  //error in not specifying a custom result
			return;
		}
		this.result = $('#closeXFD-result-custom-input').val();
	} else {
		this.result = selected_result_label;
	}

	//Speedy result
	if ($('#closeXFD-speedy').prop('checked')) {
		this.result = 'speedy ' + this.result;
	}
	
	//Soft redirect
	if ( selected_result_label == "soft redirect" || selected_result_label == "delete and soft redirect" ) {
		this.softredir = true;
		//adjust label to simplify code below for target
		selected_result_label = "redirect";		
	}
	
	//Merge/redirect target
	if ( selected_result_label == "retarget" || selected_result_label == "delete and retarget" || selected_result_label == "delete and redirect" ) {
		//adjust label to simplify code below for target
		selected_result_label = "redirect";
	}
	if (selected_result_label == "merge" || selected_result_label == "redirect" ) {
		if( ($("#closeXFD-result-target").length !== 0) && ($('#closeXFD-result-target').val().length === 0) ) {
			this.errorID = "#closeXFD-resultContainer-target";  //error in not specifying a target
			return;
		}
	
		if ($("#closeXFD-result-target").length !== 0) {
			var targetTitleObj = mw.Title.newFromText( $('#closeXFD-result-target').val() );
			if ( targetTitleObj ) {
				this.target = targetTitleObj.getPrefixedText();
				
				var targetFrag = "";
				if ( targetTitleObj.getFragment() ) {
					targetFrag = "#" + targetTitleObj.getFragment();
				}
				this.targetFrag = targetFrag;
				var targetNS = targetTitleObj.getNamespaceId();
				if ( targetNS === 6 || targetNS === 14 ) {
					this.targetLink = "[[:" + this.target + targetFrag + "]]";
				} else {
					this.targetLink = "[[" + this.target + targetFrag + "]]";
				}
			} else {
				alert("Bad " + selected_result_label + "target: the title is invalid.");
				this.errorID = "#closeXFD-resultContainer-target";  //error for invalid target
				return;				
			}

		}
		
		if ( $('#closeXFD-rcatOptions-on').prop('checked') ) {
			if ($("#closeXFD-result-redir-rcats").length !== 0) {
				this.rcats = $('#closeXFD-result-redir-rcats').val();
			} else {
				this.rcats = "";
			}
		} else {
			this.rcats = false;
		}
		
	
	}
	
	//Make redirect; delete before redirect
	if (selected_result_label == "redirect") {
		this.makeRedirect = true;
		this.delAndRedir = $('#closeXFD-redirectOption-deleteFirst').prop('checked');
	}
	
	//Rationale
	this.rationale = $('#closeXFD-rationale').val();

	//Punctuation after result
	if ( $('#closeXFD-rationale-sentence').prop('checked') && this.rationale ) {
		this.resultPunct = '';
	} else {
		this.resultPunct = '.';
	}
	
	//After
	var selected_after = $('input[name="closeXFD-after"]:checked');
	if (typeof $(selected_after).val() == "undefined") {
		this.errorID = "#closeXFD-afterContainer";  //error in not choosing an "after" action
		return;
	}
	this.after = $(selected_after).attr('id').slice(15);
	
	//Options
	if ( ($("#closeXFD-option-deletetalk").length !== 0) && ($("#closeXFD-option-deletetalk").is(":visible"))) {
		this.deletetalk = !( $('#closeXFD-option-deletetalk').prop('checked') );
	}
	if ( ($("#closeXFD-option-deleteredir").length !== 0) && ($("#closeXFD-option-deleteredir").is(":visible"))) {
		this.deleteredir = $('#closeXFD-option-deleteredir').prop('checked');
	}
	if ( ($("#closeXFD-option-unlinkbackl").length !== 0) && ($("#closeXFD-option-unlinkbackl").is(":visible"))) {
		this.unlinkbackl = $('#closeXFD-option-unlinkbackl').prop('checked');
	}
	
	//Holding cell
	if ( (($("#closeXFD-holdingcellsections").length !== 0) && ($("#closeXFD-holdingcellsections").is(":visible"))) || (($("#closeXFD-holdingcellsections-merge").length !== 0) && ($("#closeXFD-holdingcellsections-merge").is(":visible"))) ) {
		var selected_holdingcell = $('input[name="closeXFD-holdingcell"]:checked');
		if (typeof $(selected_holdingcell).val() == "undefined") { //error in not choosing a holding cell section
				this.errorID = "#closeXFD-holdingcellsections";
			if ($("#closeXFD-holdingcellsections-merge").is(":visible")) {
				this.errorID += "-merge";
			} 
			return;
		}
		this.holdingcell = $(selected_holdingcell).attr('id').slice(21);
	}
	
};


// Function to unlink a link, or comment out a file, from a wikitext string. Returns the updated
// wikitext as a string, without checking if any changes have been made
// - derived from the Twinkle unlink module
// ( https://en.wikipedia.org/wiki/MediaWiki:Gadget-twinkleunlink.js ) and morebits.js library
// ( https://en.wikipedia.org/wiki/MediaWiki:Gadget-morebits.js )
XFDcloser.unlink = function (wikitext, unlinkThese, ns) {
	// Remove image/file usages
	if ( xfdSetting.xfd_type === "ffd" ) {
		// Start building regex strings
		normal_regex_str = "(";
		gallery_regex_str = "(";
		free_regex_str = "(";
		for ( var i=0; i<unlinkThese.length; i++ ) {
			// Take off namespace prefix
			filename = unlinkThese[i].replace(/^.*?:/, "");
			// For regex matching: first character can be either upper or lower case, special
			// characters need to be escaped, spaces can be either spaces or underscores
			filename_regex_str = "[" + mw.RegExp.escape(filename.slice(0, 1).toUpperCase()) +
			mw.RegExp.escape(filename.slice(0, 1).toLowerCase()) + "]" +
			mw.RegExp.escape(filename.slice(1)).replace(/ /g, "[ _]");
			// Add to regex strings
			normal_regex_str += "\\[\\[\\s*(?:[Ii]mage|[Ff]ile)\\s*:\\s*" + filename_regex_str +
			"\\s*\\|?.*?(?:(?:\\[\\[.*?\\]\\]).*?)*\\]\\]";
			gallery_regex_str += "^\\s*(?:[Ii]mage|[Ff]ile):\\s*" + filename_regex_str + ".*?$";
			free_regex_str += "\\|\\s*(?:[\\w\\s]+\\=)?\\s*(?:(?:[Ii]mage|[Ff]ile):\\s*)?" +
			filename_regex_str;
			
			if ( i+1 === unlinkThese.length ) {
				normal_regex_str += ")(?![^<]*?-->)";
				gallery_regex_str += ")(?![^<]*?-->)";
				free_regex_str += ")(?![^<]*?-->)";
			} else {
				normal_regex_str += "|";
				gallery_regex_str += "|";
				free_regex_str += "|";				
			}
		}

		// Check for normal file usage, i.e. [[File:Foobar.png|...]]
		var normal_regex = new RegExp( normal_regex_str, "g");
		wikitext = wikitext.replace(normal_regex, "");
		
		// Check for gallery usage, i.e. instances that must start on a new line, eventually
		// preceded with some space, and must include File: or Image: prefix
		var gallery_regex = new RegExp( gallery_regex_str, "mg" );
		wikitext = wikitext.replace(gallery_regex, "");
		
		// Check for free usages, for example as template argument, might have the File: or Image:
		// prefix excluded, but must be preceeded by an |
		var free_regex = new RegExp( free_regex_str, "mg" );
		wikitext = wikitext.replace(free_regex, "");
	}
	
	// Remove links
	// Start building regex strings
	var simple_regex_str = "\\[\\[:?(";
	var named_regex_str = "\\[\\[:?(?:";
	for ( var ii=0; ii<unlinkThese.length; ii++ ) {
		// For regex matching: first character can be either upper or lower case, special
		// characters need to be escaped, spaces can be either spaces or underscores
		var unlink_regex_str = "[" + mw.RegExp.escape(unlinkThese[ii].slice(0, 1).toUpperCase()) +
			mw.RegExp.escape(unlinkThese[ii].slice(0, 1).toLowerCase()) + "]" +
			mw.RegExp.escape(unlinkThese[ii].slice(1)).replace(/ /g, "[ _]");
		// Add to regex strings
		simple_regex_str += unlink_regex_str;
		named_regex_str += unlink_regex_str;
		if ( ii+1 !== unlinkThese.length ) {
			simple_regex_str += "|";
			named_regex_str += "|";			
		}
	}	
	simple_regex_str += ")(?:#[^\\|\\]]*?)?\\]\\](?![^<]*?-->)";
	named_regex_str += ")(?:#[^\\|\\]]*?)?\\|([^\\[\\]\\n\\r]+?)\\]\\](?![^<]*?-->)";
	var simple_regex = new RegExp( simple_regex_str, "g" );
	var named_regex = new RegExp( named_regex_str, "g" );
	//Set index articles for names, which should be treated like disambiguation pages, will contain
	//one of these templates
	var name_set_index_regex = /\{\{\s*(?:[Gg]iven[ _]name|[Ss]urnames?|[Nn]ickname|[Ff]irst[ _]name|[Ff]orename|[Dd]isambigN(?:ame|m)?)\s*(?:\|.*?)*?\}\}/g;
	if ( ns === 10 ) {
		//Within navbox templates, remove links entirely, including the preceding *'s and the following newline
		var navbox_regex = new RegExp("\\{\\{[Nn]avbox(?: with collapsible groups| with columns)?\\s*\\|" +
			"(?:.|\\n)*?(?:(?:\\{\\{" +			// accounts for templates within the navbox
				"(?:.|\\n)*?(?:(?:\\{\\{" +		// accounts for templates within templates within the navbox
					"(?:.|\\n)*?" +
				"\\}\\})(?:.|\\n)*?)*?" +
			"\\}\\})(?:.|\\n)*?)*" +
		"\\}\\}", "g");
		var navbox_simple_regex = new RegExp( "\\*+\\s+" + simple_regex_str + "[\\r\\t\\f\\v ]*\\n", "g" );
		var navbox_named_regex = new RegExp( "\\*+\\s+" + named_regex_str + "[\\r\\t\\f\\v ]*\\n", "g" );
		//Find navbox templates
		var navboxes = wikitext.match(navbox_regex);
		if ( navboxes ) {
			// remove regex matches from wikitext
			for ( var jj=0; jj<navboxes.length; jj++ ) {
				replacement = navboxes[jj].replace(navbox_simple_regex, "").replace(navbox_named_regex, "");
				wikitext = wikitext.replace(navboxes[jj], replacement);
			}
		}
	} else if ( ns === -7 || ( ns === 0 && name_set_index_regex.test(wikitext) ) ) {
		//For disambiguation pages, entirely remove list items containing a backlink, including the
		//preceding *'s and the following newline (but skiping list items with multiple links)
		var dab_simple_regex = new RegExp( "\\*+[^\\[\\]\\n\\r]*" + simple_regex_str + "[^\\[\\]\\n\\r]*\\n", "g" );
		var dab_named_regex = new RegExp( "\\*+[^\\[\\]\\n\\r]*" + named_regex_str + "[^\\[\\]\\n\\r]*\\n", "g" );	
		wikitext = wikitext.replace(dab_simple_regex, "").replace(dab_named_regex, "");
	} else {
		//For See also sections, entirely remove list items containing a backlink, including the
		//preceding *'s and the following newline (but skiping list items with multiple links)
		var seealso_regex = /==+\s*[Ss]ee [Aa]lso\s*==+\n+(?:^.*\n*)*?(?:(?===+)|$)/gm;
		var seealso_simple_regex = new RegExp( "\\*+[^\\[\\]\\n\\r]*" + simple_regex_str + "[^\\[\\]\\n\\r]*\\n", "g" );
		var seealso_named_regex = new RegExp( "\\*+[^\\[\\]\\n\\r]*" + named_regex_str + "[^\\[\\]\\n\\r]*\\n", "g" );
		var seealso = wikitext.match(seealso_regex);
		if ( seealso ) {
			// remove regex matches from wikitext
			for ( var kk=0; kk<seealso.length; kk++ ) {
				replacement = (seealso[kk]+"\n").replace(seealso_simple_regex, "").replace(seealso_named_regex, "");
				wikitext = wikitext.replace(seealso[kk].trim(), replacement.trim());
			}
		}
	}
	wikitext = wikitext.replace(simple_regex, "$1").replace(named_regex, "$1");

	return wikitext;
};


/* == Closing-related functions == */
/* ********************************* */
	//TASKS (all possible tasks listed here, but only a subset to be done for any particular close):
	//0:  Close section on nomination page
	//1:  IF doKeepActions OR doMergeActions, add old xfd template to each talk page
	//2:  IF doKeepActions, remove nomination template from each nominated page
	//3:  IF doMergeActions AND NOT tfd, replace nom template with merge template for each nom page,
	//        and add merge tag to target's talk page
	//4:  IF doDeletion, delete nominated page
	//5:  IF tagSpeedy, tag nominated page for speedy deletion
	//6:  IF holdingCell OR ( doMergeActions AND tfd ), replace nomination template with
	//        being deleted template, or with speedy-del if 'ready for deletion'
	//7:  IF holdingCell OR ( doMergeActions AND tfd ), list at holding cell		
	//8:  IF deletetalk, delete talk pages
	//9:  IF tagTalkSpeedy, tag talk pages for speedy deletion
	//10: IF deleteredir, find and delete redirects
	//11: IF unlinkbackl, notify which pages are to have links unlinked - if confirmed, unlink them
	//12: IF doRedirectActions AND NOT delAndRedir, redirect each nominated page to target
	//13: IF doRedirectActions AND delAndRedir, delete each nom page and then redirect it to target
	//14: IF doRedirectActions, remove circular links from target
	
	
	//FORLATER: When getting section content, use  rvsection=  parameter in the api call
	
//Define all task functions, and then can start processing the close	
XFDcloser.doTask = new Array(12);

// --- Task 0:  Close section on nomination page --- //
XFDcloser.doTask[0] = function(nom_data, input_data, next_tasks) {
	
	// Notify task is started
	XFDcloser.taskStarted(nom_data.close_span_id, "0");
	
	// Make top and bottom wikitext
	var to_target = "";
	if ( input_data.target ) {
		to_target = " to " + input_data.targetLink;
	}		
	var xfd_close_top = xfdSetting.wikitext.closeTop
		.replace(/__RESULT__/, input_data.result)
		.replace(/__TO_TARGET__/, to_target)
		.replace(/__RPUNCT__/, input_data.resultPunct)
		.replace(/__RATIONALE__/, input_data.rationale)
		.replace(/__SIG__/, xfdc.sig);
	var xfd_close_editsum = "/* " + nom_data.section_header + " */ Closed " +
		"as " + input_data.result + xfdc.script_advert;
	
	// Callback functions for processing api failures
	apiFailedCallback_editNomPage = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "0", "edit page " +
			XFDcloser.makeLink(nom_data.nom_page) + "; could not close discussion", code, result );
	};	
	apiFailedCallback_getNomPage = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "0", "read contents of page " +
			XFDcloser.makeLink(nom_data.nom_page) + "; could not close discussion", code, result );
	};			

	// Function to edit with the api
	apiEditNomPage = function (new_section_text) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: nom_data.nom_page,
			section: nom_data.editsection,
			text: new_section_text,
			summary: xfd_close_editsum
		} ).done( function() {
			XFDcloser.taskFinished(nom_data.close_span_id, "0");
			XFDcloser.doPostCloseTasks(nom_data, input_data, next_tasks);
		} ).fail( apiFailedCallback_editNomPage );
	};
	
	// Callback functions for processing api results
	apiCallback_getNomPage = function (result) {
		var p_id = result.query.pageids;
		var p_contents = result.query.pages[ p_id ].revisions[ 0 ][ '*' ];
		if ( p_contents.includes(xfdSetting.wikitext.alreadyClosed) ) {
			XFDcloser.abortTasks(nom_data.close_span_id, next_tasks);
			return;
		}
		var section_heading = p_contents.slice(0, p_contents.indexOf("\n"));
		var section_contents = p_contents.slice(p_contents.indexOf("\n")+1)
			.replace(/({{closing}}|{{AfDh}}|{{AfDb}}|\{\{REMOVE THIS TEMPLATE WHEN CLOSING THIS AfD\|.?\}\}|<noinclude>\[\[Category:Relisted AfD debates\|.*?\]\]<\/noinclude>)/gi, "");
		var updated_section = "";
		if ( xfdSetting.xfd_type === "afd" || xfdSetting.xfd_type === "mfd" ) {
			updated_section = xfd_close_top + "\n" + section_heading + "\n" +
			section_contents.trim() + "\n" + xfdSetting.wikitext.closeBottom;
		} else {
			updated_section = section_heading + "\n" + xfd_close_top + "\n" +
			section_contents.trim() + "\n" + xfdSetting.wikitext.closeBottom;
		}		
		// Perform edit to close XFD discussion
		apiEditNomPage(updated_section);
	};
	
	// Get nomination page content and remove {Closing} etc templates if present
	new mw.Api().get( {
			action: 'query',
			titles: nom_data.nom_page,
			prop: 'revisions',
			rvprop: 'content',
			rvsection: nom_data.editsection,
			indexpageids: 1
		} ).done( apiCallback_getNomPage )
		.fail( apiFailedCallback_getNomPage );
	
};


// --- Task 1:  Add old xfd template to each talk page --- //
XFDcloser.doTask[1] = function(nom_data, input_data) {
	// Notify task is started
	XFDcloser.taskStarted(nom_data.close_span_id, "1");	
	
	var edits_made = 0; // var to be incremented after each edit
	var editsum = "Old " + xfdSetting.xfd_type.toUpperCase() + " – " + nom_data.nom_date +
		": " + input_data.result + xfdc.script_advert;
		
	// - - - Common functions - - - //
	// Callback function for processing api failures			
	apiFailedCallback_editTalk = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "1", "edit talk page(s)", code, result);
	};

	// Function to make an edit with the api - prepending content
	apiEditTalk = function (pagetitle) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: pagetitle,
			prependtext: oldxfd_wikitext,
			summary: editsum
		} ).done( function() {
			edits_made++;
			if (edits_made === nom_data.pages.length) {
				XFDcloser.taskFinished(nom_data.close_span_id, "1");
			}
		}).fail( apiFailedCallback_editTalk ); 
	};

	apiEditTalk_append = function (pagetitle) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: pagetitle,
			appendtext: '\n' + oldxfd_wikitext,
			summary: editsum
		} ).done( function() {
			edits_made++;
			if (edits_made === nom_data.pages.length) {
				XFDcloser.taskFinished(nom_data.close_span_id, "1");
			}
		}).fail( apiFailedCallback_editTalk ); 
	};
	
	// Function to make an edit with the api to the redirect's target - prepending content
	apiEditTalkRedirTarget = function (pagetitle, prepend_content) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: pagetitle,
			prependtext: prepend_content,
			summary: editsum,
			redirect: 1
		} ).done( function() {
			edits_made++;
			if (edits_made === nom_data.pages.length) {
				XFDcloser.taskFinished(nom_data.close_span_id, "1");
			}
		}).fail( apiFailedCallback_editTalk ); 
	};
	
	// Function to make an edit with the api - replacing content
	apiEditTalk_replace = function (pagetitle, replacementtext) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: pagetitle,
			text: replacementtext,
			summary: editsum
		} ).done( function() {
			edits_made++;
			if (edits_made === nom_data.pages.length) {
				XFDcloser.taskFinished(nom_data.close_span_id, "1");
			}
		}).fail( apiFailedCallback_editTalk ); 
	};
	
	if ( xfdSetting.xfd_type === "afd" ) {
		// - - - Task 1 (AFD) - - - //
		//Parts of this task derived from https://en.wikipedia.org/wiki/User:Mr.Z-man/closeAFD2.js
		
		// Functions to be used inside callback functions
		makeNewWikitext = function(wikitext) {
			var oldafdpatt = /\n?\{\{(old|afd) ?(old|afd) ?(multi|full)?((.|\n)*?)\}\}\n?/i;
			var numtest = /[A-z]+([0-9])/i;
			var oldafdmulti = "{{Old AfD multi";
			old = oldafdpatt.exec(wikitext);
			var count = 0;
			if ( old ) {
				count = 1;
				var parts = old[4].split("|");
				var params = {};
				
				for ( var i=0; i<parts.length; i++ ) {
					if ( !parts[i].trim() ) {
						continue;
					}
					pieces = parts[i].split("=");
					params[pieces[0].trim()] = pieces[1].trim();
					var num = numtest.exec(pieces[0].trim());
					if (num) {
						res = parseInt(num[1]);
						if (res > count) {
							count = res;
						}
					}
				}
				for (var p in params) {
					oldafdmulti += " |"+p+"="+params[p];
				}	
			}
			count++;
			c = count.toString();
			if (count == 1) {
				c = '';
			}
			var debate = nom_data.nom_page.replace(xfdSetting.subpagePath, "");
			oldafdmulti += " |date" + c + "=" + nom_data.nom_date +
				" |result" + c + "='''" + input_data.result + "'''" +
				" |page" + c + "=" + debate + '}}';
			if ( count > 1 ) {
				newtext = wikitext.replace(oldafdpatt, "\n"+oldafdmulti+"\n");
			} else { // [[WP:Talk page layout]] is too complicated to automate, so don't try
				newtext = oldafdmulti+'\n'+wikitext;
			}
			return newtext;
		};			
		
		// Callback function for processing api failure			
		apiFailedCallback_getTalkPage = function (code, result) {
			XFDcloser.Api_failed(nom_data.close_span_id, "1", "read contents of talk page(s)", code, result);
		};
		
		// Callback function for processing api results
		apiCallback_getTalkPage = function (result) {
			//Get old wikitext, or set to empty string if page doesn't exist (page id is negative)
			var oldwikitext = "";
			var page_ids = result.query.pageids;
			for (var i = 0; i < page_ids.length; i++) {
				var p_title = result.query.pages[ page_ids[i] ].title;
				if (parseInt(page_ids[i]) > 0) {
					oldwikitext = result.query.pages[ page_ids[i] ].revisions[ 0 ][ '*' ];
				} else {
					oldwikitext = "";
				}
				// Check namespace and that subject page exists
				j = nom_data.talkPages.indexOf(p_title);
				if ( j === -1 ) {
					XFDcloser.taskError(nom_data.close_span_id, "1", "API: query result included " +
						"unexpected talk page title \"" + XFDcloser.makeLink(title) + "\"; " +
						"this talk page will not be edited");
				} else if ( !nom_data.pageExists[j] ) {
					XFDcloser.taskError(nom_data.close_span_id, "1", "The subject page for \"" +
						XFDcloser.makeLink(p_title) + "\" does not exist; this talk page will not be edited");
				} else {
					apiEditTalk_replace(p_title, makeNewWikitext(oldwikitext));
				}
				
			}
		};

		//get talk page wikitext through api
		new mw.Api().get( {
			action: 'query',
			titles: nom_data.talkPages.join("|"),
			prop: 'revisions',
			rvprop: 'content',
			indexpageids: 1,
			rawcontinue: ''
		} ) .done( apiCallback_getTalkPage )
			.fail( apiFailedCallback_getTalkPage );

	} else {
		// - - - Task 1 (RFD, MFD) - - - //
		oldxfd_wikitext = xfdSetting.wikitext.oldXfd
			.replace(/__DATE__/, nom_data.nom_date)
			.replace(/__SECTION__/, nom_data.section_header)
			.replace(/__RESULT__/, input_data.result)
			.replace(/__FIRSTDATE__/, nom_data.first_date)
			.replace(/__SUBPAGE__/, nom_data.nom_page.replace(xfdSetting.subpagePath, ""));

		//API callbacks
		apiFailedCallback_getTalkRedirectInfo = function (code, result) {
			XFDcloser.Api_failed(nom_data.close_span_id, "1", "read contents of talk page(s)", code, result);
		};
		
		apiCallback_getTalkRedirectInfo = function (result) {
			//Check if each page is a redirect
			var page_ids = result.query.pageids;
			//count adjustment, needed in case any empty strings were filtered out
			var edits_made = nom_data.pages.length - page_ids.length;
			for (var i = 0; i < page_ids.length; i++) {
				var talktitle = result.query.pages[ page_ids[i] ].title;
				if ( result.query.pages[ page_ids[i] ].redirect === "" ) {
					// Is a redirect...
					if ( xfdSetting.xfd_type === "rfd" ) {
						// for RFD, ask what to do
						var response = confirm("\"" + talktitle + "\" is currently a redirect. Okay " +
							"to replace with Old " + xfdSetting.xfd_type.toUpperCase() + " template?");
						if ( response ) {
							// Make the edit, replacing previous content
							apiEditTalk_replace(talktitle, oldxfd_wikitext); 
						} else {
							// Skip
							XFDcloser.taskWarning(nom_data.close_span_id, "1", "\"" + talktitle + "\" skipped");
							edits_made++;
							if (edits_made === nom_data.pages.length) {
								XFDcloser.taskFinished(nom_data.close_span_id, "1");
							}
						}
					} else if ( xfdSetting.xfd_type === "mfd" ) {
						// For MFD, edit edirect target & use altpage parameter
						var altpage = XFDcloser.getSubjectPage(talktitle);
						apiEditTalkRedirTarget(talktitle, oldxfd_wikitext.replace("}}", " |altpage="+altpage+"}}"));
					} else {
						// For other venues, append rather than prepend the template
						apiEditTalk_append(talktitle);
					}
				} else {
					// Not a redirect - just make the edit
					apiEditTalk(talktitle);
				}
			}
			
		};
		
		//Make a filtered list of talkpages which will be checked for redirect status
		var filtered_talkpages = "";
		if ( xfdSetting.xfd_type === "rfd" || xfdSetting.xfd_type === "mfd" ) {
			// Filter out empty strings from talk page array (i.e. nominated pages which are themselves talkpages)
			filtered_talkpages = nom_data.talkPages.join("|").replace(/\|+/g, "|").replace(/^\||\|$/g, "");
		} else {
			// For each talk page, check subject page exists
			for (ii = 0; ii < nom_data.pages.length; ii++) {
				if ( !nom_data.pageExists[ii] ) {
					XFDcloser.taskError(nom_data.close_span_id, "1", "\"" + XFDcloser.makeLink(nom_data.pages[ii]) +
					"\" does not exist; its talk page will not be edited");
				} else if ( filtered_talkpages === "" ) {
					filtered_talkpages = nom_data.talkPages[ii];
				} else {
					filtered_talkpages += "|" + nom_data.talkPages[ii];
				}
			}
		}
		if ( filtered_talkpages === "" ) {
			// nothing to do
			XFDcloser.taskFinished(nom_data.close_span_id, "1");
			XFDcloser.taskWarning(nom_data.close_span_id, "1", "none found");
		} else {
			// get talk page redirect status from api
			new mw.Api().get( {
				action: 'query',
				prop: 'info',
				titles: filtered_talkpages,
				indexpageids: 1
			} ).done( apiCallback_getTalkRedirectInfo )
			.fail( apiFailedCallback_getTalkRedirectInfo );
		}
		
	}
};		


// --- Task 2: Remove nom template from nominated pages (and then prepend text if specified)  --- //		
XFDcloser.doTask[2] = function(nom_data, input_data, prepend_wikitext, task_number) {
	
	// Default values if params are not defined
	if ( prepend_wikitext == null ) {
		prepend_wikitext = "";
	}
	if ( task_number == null ) {
		task_number = "2";
		// Notify task 2 is started
		XFDcloser.taskStarted(nom_data.close_span_id, task_number);
	}

	var editsum = xfdSetting.xfd_type.toUpperCase() + " closed as " +
		input_data.result + xfdc.script_advert;
	var edits_made = 0; // var to be incremented after each edit
	
	// Callback functions for processing api failures
	apiFailedCallback_editPages = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, task_number, "edit nominated "+
		"page(s)", code, result);
	};
		
	apiFailedCallback_getPages = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, task_number, "read contents of "+
		"nominated page(s)", code, result);
	};

	// Function to edit each nominated page with the api
	apiEditPages = function (pagetitle, new_section_text) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: pagetitle,
			text: new_section_text,
			summary: editsum
		} ).done( function() {
			edits_made++;
			if (edits_made === nom_data.pages.length) {
				XFDcloser.taskFinished(nom_data.close_span_id, task_number);
			}
		} ).fail( apiFailedCallback_editNomPage );
	};			

	// Callback function for processing api results
	apiCallback_getPages = function (result) {
		//Get old wikitext, or make error if page doesn't exist, or if page is in wrong namespace
		var oldwikitext = "";
		var page_ids = result.query.pageids;
		for (var i = 0; i < page_ids.length; i++) {
			var p_title = result.query.pages[ page_ids[i] ].title;

			// Check that subject page exists
			j = nom_data.pages.indexOf(p_title);
			if ( j === -1 ) {
				XFDcloser.taskError(nom_data.close_span_id, "2", "API: query result included " +
				"unexpected page title \"" + XFDcloser.makeLink(p_title) + "\"; this page will not be edited");
			} else if ( parseInt(page_ids[i]) < 0 ) { //more recent info than nom_data.pageExists[j] 
				XFDcloser.taskError(nom_data.close_span_id, "2", "The page \"" +
				XFDcloser.makeLink(p_title) + "\" does not exist, and will not be edited");
			} else {						
				oldwikitext = result.query.pages[ page_ids[i] ].revisions[ 0 ][ '*' ];
				if ( nom_data.pages[j] === input_data.target ) {
					// Don't prepend anything to redirect target page - just remove nom template
					updated_wikitext = oldwikitext.replace(xfdSetting.regex.nomTemplate, "");
				} else {
					updated_wikitext = prepend_wikitext +
					oldwikitext.replace(xfdSetting.regex.nomTemplate, "");
				}
				//Warn if no changes made
				if ( updated_wikitext === oldwikitext || updated_wikitext === prepend_wikitext + oldwikitext ) {
					XFDcloser.taskWarning(nom_data.close_span_id, "2", "Warning: " +
					"nomination template not found on page \"" + XFDcloser.makeLink(p_title) + "\"");
				}
				apiEditPages(p_title, updated_wikitext);
			}
		}
	};
	
	//get each page's wikitext through api
	new mw.Api().get( {
		action: 'query',
		titles: nom_data.pages.join("|"),
		prop: 'revisions',
		rvprop: 'content',
		indexpageids: 1,
		rawcontinue: ''
	} ) .done( apiCallback_getPages )
		.fail( apiFailedCallback_getPages );
	
};


// --- Task 3: Replace nomination template with merge template for each nominated page --- //
// Task only for AFDs and MFDs. All CFDs are basic closes, 'merge' isn't a likely
// outcome for FFDs/RFDs (but could still be closed as such via Custom result), and
// TFDs use {Being deleted} and the holding cell.
XFDcloser.doTask[3] = function(nom_data, input_data) {
	//Parts of this function derived from https://en.wikipedia.org/wiki/User:Mr.Z-man/closeAFD2.js
	
	// Notify task is started
	XFDcloser.taskStarted(nom_data.close_span_id, "3");

	// Add 'merge from' template to target's talk page,
	var merge_editsum = "[[" + nom_data.disc_link + "]] closed as " +
		input_data.result + xfdc.script_advert;
	var target_talk = XFDcloser.getTalkPage(input_data.target);
	if ( target_talk === "" ) {
		//If target is itself a talkpage, use the target
		target_talk = input_data.target;
	}
	var today = new Date();
	var curdate = today.getUTCDate().toString() + " " + xfdc.monthnames[today.getUTCMonth()] +
	" " + today.getUTCFullYear().toString();
	var debate = nom_data.nom_page.replace(xfdSetting.subpagePath, "");
	var mergefrom_wikitext = "";
	for (var i=0; i<nom_data.pages.length; i++) {
		mergefrom_wikitext+= xfdSetting.wikitext.mergeFrom
			.replace(/__NOMINATED__/, nom_data.pages[i])
			.replace(/__DEBATE__/, debate)
			.replace(/__DATE__/, curdate);
	}
	var mergeto_wikitext = xfdSetting.wikitext.mergeTo
		.replace(/__TARGET__/, input_data.target)
		.replace(/__DEBATE__/, debate)
		.replace(/__DATE__/, curdate)		
		.replace(/__TARGETTALK__/, target_talk); //for MFDs

	// Callback function for processing api failures
	apiFailedCallback_editTargetTalk = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "3", "edit merge target talk page \"" +
		XFDcloser.makeLink(target_talk) + "\"", code, result );
		// still try to replace nom template(s) with 'merge to' template(s) using XFDcloser.doTask[2]
		XFDcloser.doTask[2](nom_data, input_data, mergeto_wikitext, "3");
	};
	
	// function to edit target talk page
	editTargetTalk = function() {
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: target_talk,
			prependtext: mergefrom_wikitext,
			summary: merge_editsum
		} ).done( function() {
			// now replace nom template(s) with 'merge to' template(s) using XFDcloser.doTask[2]
			XFDcloser.doTask[2](nom_data, input_data, mergeto_wikitext, "3");
		}).fail( apiFailedCallback_editTargetTalk );
	};

	// Check if the target is one of the nominated pages
	var j = nom_data.pages.indexOf(input_data.target);
	if ( j !== -1 ) {
		// No need to add merge from template - this was a nominated page, and will have an old xfd
		// template put on its talkpage specify the merge result
		// So just replace nom template(s) with 'merge to' template(s) using XFDcloser.doTask[2]
		XFDcloser.doTask[2](nom_data, input_data, mergeto_wikitext, "3");
	} else {
		editTargetTalk();
	}
			
};


// --- Task 4: Delete nominated pages --- //
XFDcloser.doTask[4] = function(nom_data) { //input_data not needed
	// Notify task is started
	XFDcloser.taskStarted(nom_data.close_span_id, "4");

	var del_count = 0; // var to be incremented after each deletion.
	
	// Reason for deletion log
	var del_reason = "[[" + nom_data.disc_link + "]]" + xfdc.script_advert;
	
	// Callback functions for processing api failures
	apiFailedCallback_deletePages = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "4", "delete page(s)", code, result );
	};

	//Function to delete with the api
	apiDeletePage = function(pagetitle) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'delete',
			title: pagetitle,
			reason: del_reason,
		} ).done( function() {
			del_count++;
			if (del_count === nom_data.pages.length) {
				XFDcloser.taskFinished(nom_data.close_span_id, "4");
			}
		} ).fail( apiFailedCallback_deletePages );
	};

	// For each page, check it exists, then delete with api
	for (var i=0; i < nom_data.pages.length; i++) {
		if ( !nom_data.pageExists[i] ) {
			//page already doesn't exist, presumbaly already deleted - don't try to delete it again
			XFDcloser.taskWarning(nom_data.close_span_id, "4", "\"" + XFDcloser.makeLink(nom_data.pages[i]) + 
			"\" skipped: does not exist (may have already been deleted by others)");
			del_count++;
			if (del_count === nom_data.pages.length) {
				XFDcloser.taskFinished(nom_data.close_span_id, "4");
			}
		} else {
			apiDeletePage(nom_data.pages[i]);
		}
		
	}
};


// --- Task 5: tagWithSpeedy --- //
XFDcloser.doTask[5] = function(nom_data) { //input_data not needed
	// Notify task is started
	XFDcloser.taskStarted(nom_data.close_span_id, "5");
	
	var edits_made = 0; // var to be incremented after each edit. 
	var speedy_tag_editsum = "[[WP:G6|G6]] Speedy deletion nomination, per [[" +
		nom_data.disc_link + "]]" + xfdc.script_advert;
	var speedy_tag_wikitext = "{"+"{Db-xfd|fullvotepage=" + nom_data.disc_link + "}"+"}\n";
	
	// Callback functions for processing api failures
	apiFailedCallback_tagSpeedy = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "5", "tag page(s) for " +
		"speedy deletion", code, result );
	};
	
	apiEditTagSpeedy = function (page_title) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: page_title,
			prependtext: speedy_tag_wikitext,
			summary: speedy_tag_editsum,
			nocreate: true
		} ).done( function() {
			edits_made++;
			if (edits_made === nom_data.pages.length) {
				XFDcloser.taskFinished(nom_data.close_span_id, "5");
			}
		} ).fail( apiFailedCallback_tagSpeedy );
	};			
	
	// For each page, check it exists, then tag for deletion
	for (var i=0; i < nom_data.pages.length; i++) {
		//Check namespace
		if ( !nom_data.pageExists[i] ) {
			XFDcloser.taskWarning(nom_data.close_span_id, "5", "\"" +
			XFDcloser.makeLink(nom_data.talkPages[i]) + "\" skipped: does not exist]");
			edits_made++;
			if (edits_made === nom_data.pages.length) {
				XFDcloser.taskFinished(nom_data.close_span_id, "5");
			}
		} else {
			apiEditTagSpeedy(nom_data.pages[i]);
		}
	}
};


// --- Task 6: add Being deleted ---
// TFD only
XFDcloser.doTask[6] = function(nom_data, input_data) {
	// Notify task is started
	XFDcloser.taskStarted(nom_data.close_span_id, "6");
	
	var editsum = ""; // will set later
	var replacement_wikitext = ""; // will set later
	var edits_made = 0; // var to be incremented after each edit.
	var merge_wikitext = "";
	if (input_data.target) {
		merge_wikitext = "|merge=" + input_data.target;
	}
	
	if ( input_data.holdingcell === "ready" ) {
		editsum = "[[WP:G6|G6]] Speedy deletion nomination, per [[" + nom_data.disc_link +
		"]]" + xfdc.script_advert;
		replacement_wikitext = "{" + "{Db-xfd|fullvotepage=" + nom_data.disc_link + "}" + "}";
	} else {
		editsum = "Per [[" + nom_data.disc_link + "]], added {" + "{being deleted}" +"}" +
		xfdc.script_advert;
		replacement_wikitext = "{" + "{Being deleted|" + nom_data.nom_date + "|" +
		mw.util.wikiUrlencode(nom_data.section_header) + merge_wikitext + "}" + "}";
	}

	// Callback functions for processing api failures
	apiFailedCallback_editTemplate = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "6", "edit template(s)", code, result );
	};
	
	apiFailedCallback_getTemplates = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "6", "read contents of " +
		"nominated page(s)", code, result );
	};
	
	//Function to make the edit
	apiEditTemplate = function(tempalte_title, new_content, es) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: tempalte_title,
			text: new_content,
			summary: es
		} ).done( function() {
			edits_made++;
			if (edits_made === nom_data.pages.length) {
				XFDcloser.taskFinished(nom_data.close_span_id, "6");
			}
		} ).fail( apiFailedCallback_editTemplate );
	};

	// Callback functions for processing api result
	apiCallback_getTemplates = function (result) {
		var page_ids = result.query.pageids;
		for (var i = 0; i < page_ids.length; i++) {
			var t_contents = "";
			var t_title = result.query.pages[ page_ids[i] ].title;
			var j = nom_data.pages.indexOf(t_title);
			if ( j === -1 ) {
				XFDcloser.taskError(nom_data.close_span_id, "6", "API: query result included " +
				"unexpected page title \"" + XFDcloser.makeLink(t_title) + "\"; " +
				"this page will not be edited");
			} else if (parseInt(page_ids[i]) < 0) {
				XFDcloser.taskError(nom_data.close_span_id, "6", "\"" + XFDcloser.makeLink(t_title) + "\" " + 
				"does not seem to exist (may have been deleted)");
			} else {
				// Replace {Template for discussion/dated} or {Tfm/dated} (which
				// may or may not be noincluded) with noincluded {Being deleted}					
				t_contents = result.query.pages[ page_ids[i] ].revisions[ 0 ][ '*' ];
				var updated_content = "";
				var e_s = "";
				//If this is the merge target, don't add Being deleted - just remove nom template
				if ( input_data.target && t_title===input_data.target ) {
					updated_content = t_contents.replace(xfdSetting.regex.nomTemplate, "");
					e_s = "[[" + nom_data.disc_link + "]] closed as " +
					input_data.result + xfdc.script_advert;
				} else {
					e_s = editsum;
					if ( t_contents.search(xfdSetting.regex.nomTemplate) < 0 ) {
						updated_content = "<no" + "include>" + replacement_wikitext +
						"</no" + "include>" + t_contents;
					} else {
						updated_content = t_contents.replace(xfdSetting.regex.nomTemplate, "<no" + "include>" + replacement_wikitext + "</no" + "include>");
					}
				}

				apiEditTemplate(t_title, updated_content, e_s);
				
			}
		}
		
	};

	//get page wikitext through api
	new mw.Api().get( {
		action: 'query',
		titles: nom_data.pages.join("|"),
		prop: 'revisions',
		rvprop: 'content',
		indexpageids: 1,
		rawcontinue: ''
	} ).done( apiCallback_getTemplates )
	.fail( apiFailedCallback_getTemplates );

};


// --- Task 7: Add to Holding cell --- //
// TFD only
XFDcloser.doTask[7] = function(nom_data, input_data) {
	// Notify task is started
	XFDcloser.taskStarted(nom_data.close_span_id, "7");

	//get holding cell section number for editing
	var holdingCellSectionNumbers = {
		"review": 2,
		"convert": 11,
		"substitute": 12,
		"orphan": 13,
		"ready": 14,   // (ready for deletion)
		"merge-arts": 4,
		"merge-geopolgov": 5,   // (geography, politics and governance)
		"merge-religion": 6,
		"merge-sports": 7,
		"merge-trasnport": 8,
		"merge-other": 9,
		"merge-meta": 10
	};
	var holdCellSection = holdingCellSectionNumbers[input_data.holdingcell];
	
	//Build up wikitext to append to holding cell section
	var delete_param = "";
	if ( holdCellSection === 14 ) {						// 14 is "Ready for deletion"
		delete_param = "|delete=1";
	}
	var section_param = "";
	if (nom_data.pages.length>1 || nom_data.pages[0]!==nom_data.section_header) {
		section_param = "|section=" + nom_data.section_header;
	}
	var wikitext_append = "";
	for (i = 0; i < nom_data.pages.length; i++) {
		//Check existance and namespace
		if ( !nom_data.correctNS[i] ) {
			XFDcloser.taskError(nom_data.close_span_id, "7", "\"" + XFDcloser.makeLink(nom_data.pages[i]) + "\" is " + 
			"not in the " + xfdc.namespaces[xfdSetting.ns_number.toString()] + " namespace, " +
			"and will not be listed at the holding cell");
		} else if ( !nom_data.pageExists[i] ) {
			XFDcloser.taskWarning(nom_data.close_span_id, "7", "\"" +
			XFDcloser.makeLink(nom_data.talkPages[i]) + "\" does not exist, and will not be listed at the holding cell");
		} else {
			wikitext_append += "*{" + "{tfdl|" + nom_data.pages[i].slice(9) +
			"|" + nom_data.nom_date + section_param + delete_param + "}}\n";
		}
	}
	if ( wikitext_append === "" ) {
		//If all don't exist or are in wrong namespace, then there's nothing to do
		return;
	}

	var editsum = "Listing template(s) per [[" + nom_data.disc_link + "]]" + xfdc.script_advert;

	var holdingcellpagetitle = xfdSetting.path.slice(0,-4) + "Holding cell";

	// Callback functions for processing api failures
	apiFailedCallback_getHoldCell = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "7", "read contents of \"" +
		XFDcloser.makeLink(holdingcellpagetitle) + "\"; could not add templates to holding cell", code, result );
	};

	apiFailedCallback_editHoldCell = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "7", "edit \"" +
		XFDcloser.makeLink(holdingcellpagetitle), code, result );
	};		
	
	//Function to make an edit
	apiEditHoldingCell = function(updated_section_wikitext) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: holdingcellpagetitle,
			section: holdCellSection,
			text: updated_section_wikitext,
			summary: editsum
		} ).done( function() {
			XFDcloser.taskFinished(nom_data.close_span_id, "7");
		} ).fail( apiFailedCallback_editHoldCell );
	};
	
	// Callback function for processing api results			
	apiCallback_getHoldCell = function (result) {
		var p_id = result.query.pageids;
		//get page contents, make all headings level 3 so sections can be counted
		var p_contents = result.query.pages[ p_id ].revisions[ 0 ][ '*' ].replace(/====/gi, "===");
		var sections_array = p_contents.split("===");
		var section_heading = sections_array[(holdCellSection*2-1)];
		var section_contents = sections_array[(holdCellSection*2)];
		//remove "* ''None currently''" except if inside a <!--html comment-->
		section_contents = section_contents.replace(/\n*^\*\s*''None currently''\s*$(?![^<]*?-->)/gim, "");
		var heading_level = "===";
		if ( (4 <= holdCellSection) && ( holdCellSection <= 10) ) {			//Merge subsections
			heading_level = "====";
		}
		var updated_section = heading_level + section_heading + heading_level + "\n" +
			section_contents.trim() + "\n" + wikitext_append;

		apiEditHoldingCell(updated_section);

	};

	//get holding cell contents
	new mw.Api().get( {
		action: 'query',
		titles: holdingcellpagetitle,
		prop: 'revisions',
		rvprop: 'content',
		indexpageids: 1,
		rawcontinue: ''
	} ).done( apiCallback_getHoldCell )
	.fail( apiFailedCallback_getHoldCell );

};


// --- Task 8: deleteTalk --- //
XFDcloser.doTask[8] = function(nom_data) { //input_data not needed
	// Notify task is started
	XFDcloser.taskStarted(nom_data.close_span_id, "8");

	var del_count = 0; // var to be incremented after each deletion
	var skip_count = 0;
	
	// Reason for deletion log
	var delTalk_reason = "[[WP:CSD#G8|G8]]: Talk page of deleted page." + xfdc.script_advert;
	
	// Callback functions for processing api failures
	apiFailedCallback_deletePages = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "8", "delete talk page(s)", code, result );
	};

	//Function to delete with the api
	apiDeleteTalk = function(pagetitle) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'delete',
			title: pagetitle,
			reason: delTalk_reason,
		} ).done( function() {
			del_count++;
			if ( del_count === nom_data.talkPages.length ) {
				XFDcloser.taskFinished(nom_data.close_span_id, "8");
			}
		} ).fail( apiFailedCallback_deletePages );
	};			
	
	//For each talk page, check namespace, and that it exists, then delete it
	for (var i=0; i < nom_data.talkPages.length; i++) {
		if ( nom_data.talkPages[i] === "" ) {
			// The nominated page is itself a talkpage (MfD/RfD) - skip
			del_count++;
			skip_count++;
			if ( del_count === nom_data.talkPages.length ) {
				XFDcloser.taskFinished(nom_data.close_span_id, "8");
				if ( skip_count === del_count ) {
					XFDcloser.taskWarning(nom_data.close_span_id, "8", "none found");
				}
			}
		} else if ( !nom_data.talkExists[i] ) {
			// Talk page already doesn't exist, either it was already deleted or was never created -
			// don't try to delete it again.
			XFDcloser.taskWarning(nom_data.close_span_id, "8", "\"" +
			XFDcloser.makeLink(nom_data.talkPages[i]) + "\" skipped: does not exist");
			del_count++;
			if ( del_count === nom_data.talkPages.length ) {
				XFDcloser.taskFinished(nom_data.close_span_id, "8");
			}
		} else {
			apiDeleteTalk(nom_data.talkPages[i]);
		}
	}

};


// --- Task 9: tagTalkWithSpeedy --- //
XFDcloser.doTask[9] = function(nom_data) { //input_data not needed
	// Notify task is started
	XFDcloser.taskStarted(nom_data.close_span_id, "9");

	var edits_made = 0; // var to be incremented after each edit
	var skip_count = 0;

	// Reason for edit summary
	var speedy_tag_editsum = "[[WP:G8|G8]] Speedy deletion nomination, per [[" +
		nom_data.disc_link + "]]" + xfdc.script_advert;
	var speedy_tag_wikitext = "{"+"{Db-talk}"+"}\n";
	
	// Callback functions for processing api failures
	apiFailedCallback_tagTalkSpeedy = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "9", "tag talk page(s) for " +
		"speedy deletion", code, result );
	};
	
	apiEditTagTalkSpeedy = function (page_title) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: page_title,
			prependtext: speedy_tag_wikitext,
			summary: speedy_tag_editsum
		} ).done( function() {
			edits_made++;
			if ( edits_made === nom_data.talkPages.length ) {
				XFDcloser.taskFinished(nom_data.close_span_id, "9");
			}
		} ).fail( apiFailedCallback_tagTalkSpeedy );
	};			
	
	//For each talk page, check that it exists, then tag it
	for (var i=0; i < nom_data.talkPages.length; i++) {
		if ( nom_data.talkPages[i] === "" ) {
			// The nominated page is itself a talkpage (MfD/RfD) - skip
			edits_made++;
			skip_count++;
			if ( edits_made === nom_data.talkPages.length ) {
				XFDcloser.taskFinished(nom_data.close_span_id, "8");
				if ( skip_count === edits_made ) {
					XFDcloser.taskWarning(nom_data.close_span_id, "8", "none found");
				}
			}
		} else if ( !nom_data.talkExists[i] ) {
			// Talk page already doesn't exist, either it was already deleted or was never created -
			// don't try to delete it again.
			XFDcloser.taskWarning(nom_data.close_span_id, "9", "\"" +
			XFDcloser.makeLink(nom_data.talkPages[i]) + "\" skipped: does not exist");
			edits_made++;
			if ( edits_made === nom_data.talkPages.length ) {
				XFDcloser.taskFinished(nom_data.close_span_id, "9");
			}
		} else {
			apiEditTagTalkSpeedy(nom_data.talkPages[i]);
		}
	}
};


// --- Task 10: deleteRedirects --- //
XFDcloser.doTask[10] = function(nom_data, input_data) {
	// Notify task(s) started
	XFDcloser.taskStarted(nom_data.close_span_id, "10");
	if ( input_data.deletetalk ) {
		XFDcloser.taskStarted(nom_data.close_span_id, "15");
	}
	// Finding redirect(s) to delete
	var nomtitles = nom_data.pages.join("|");
	
	// Arrays to be filled in later
	var redirect_titles = []; 
	var redirect_talk_titles = [];
	var redirect_talk_ids = [];
	
	// var to increment with each deletion
	var delr_count = 0;
	var delrt_count = 0;
	var delrt_skip = 0;
	
	// Reason for deletion log
	var delr_reason = "Delete redirect: [[" + nom_data.disc_link + "]] closed as " +
		input_data.result + xfdc.script_advert;
	var delrt_reason = "[[WP:CSD#G8|G8]]: Talk page of deleted page." + xfdc.script_advert;
	
	// Callback functions for processing api failures
	apiFailedCallback_getRedirects = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "10", "get redirects", code, result);
	};

	apiFailedCallback_deleteRedir = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "10", "delete redirect(s)", code, result);
	};

	apiFailedCallback_getRedirectTalks = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "15", "get redirects' talk pages", code, result);
	};
	
	apiFailedCallback_deleteRedirTalk = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "15", "delete redirects' talk pages", code, result);
	};
	
	// Function to delete a redirect
	apiDeleteRedir = function(pagetitle) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'delete',
			title: pagetitle,
			reason: delr_reason
		} ).done( function() {
			delr_count++;
			if (delr_count === redirect_titles.length) {
				// All done
				XFDcloser.taskFinished(nom_data.close_span_id, "10");
			}
		} ).fail( apiFailedCallback_deleteRedir );
	};
	
	// Function to delete a redirect talkpage
	apiDeleteRedirTalk = function(talkpage_id) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'delete',
			pageid: talkpage_id,
			reason: delrt_reason
		} ).done( function() {
			delrt_count++;
			if ( delrt_count + delrt_skip === redirect_talk_ids.length ) {
				// All done
				XFDcloser.taskFinished(nom_data.close_span_id, "15");
			}
		} ).fail( apiFailedCallback_deleteRedirTalk );
	};
	
	// Callback functions for processing api results
	apiCallback_getRedirectTalks = function(result) {
		if ( result.query.pageids ) {
			redirect_talk_ids = redirect_talk_ids.concat(result.query.pageids);
		}
		if ( redirect_talk_ids.length === redirect_talk_titles.length ) {
			// All done - start deleting
			if ( redirect_talk_ids.length === 0 ) {
				// No pages to delete
				XFDcloser.taskFinished(nom_data.close_span_id, "15");
				XFDcloser.taskWarning(nom_data.close_span_id, "15", "none found");
			} else {
				for (var jj=0; jj<redirect_talk_ids.length; jj++) {
					if ( redirect_talk_ids[jj] > 0 ) {
						// Page exists: delete it
						apiDeleteRedirTalk(redirect_talk_ids[jj]);
					} else {
						// Page doesn't exists: skip it, check if all done
						delrt_skip++;
						if ( delrt_skip === redirect_talk_ids.length ) {
							// No pages to delete
							XFDcloser.taskFinished(nom_data.close_span_id, "15");
							XFDcloser.taskWarning(nom_data.close_span_id, "15", "none found");
						} else if ( delrt_count + delrt_skip === redirect_talk_ids.length ) {
							// All done
							XFDcloser.taskFinished(nom_data.close_span_id, "15");
						}		
					}
					
				}	
			}				
		}
	};
	
	apiCallback_getRedirects = function(result) {
		var pid = result.query.pageids;
		for (var ii=0; ii<pid.length; ii++)
			if (result.query.pages[ pid[ii] ].redirects) {
				var redirs = result.query.pages[ pid[ii] ].redirects;
				for (var jj=0; jj<redirs.length; jj++) {
					redirect_titles.push( redirs[jj].title );
					if ( input_data.deletetalk && !XFDcloser.checkNamespace(redirs[jj].title, 2) ) {
						redirect_talk_titles.push( XFDcloser.getTalkPage(redirs[jj].title) );
					}
				}
			}
		if (redirect_titles.length === 0) {
			// Mark as closed - no redirects found
			XFDcloser.taskFinished(nom_data.close_span_id, "10");
			XFDcloser.taskWarning(nom_data.close_span_id, "10", "no redirects found");
			if ( input_data.deletetalk ) {
				XFDcloser.taskFinished(nom_data.close_span_id, "15");
				XFDcloser.taskWarning(nom_data.close_span_id, "15", "no redirects found");
			}
		} else {
			// Warn if there will be mass action, allow user to cancel
			if ( redirect_titles.length >= 10 ) {
				warn_redir = confirm("WARNING:\n Mass action to be peformed: delete " +
				redirect_titles.length + " redirects.\nDo you want to proceed?");
				if ( !warn_redir ) {
					XFDcloser.taskError(nom_data.close_span_id, "11", "cancelled by user");
					if ( input_data.deletetalk ) {
						XFDcloser.taskError(nom_data.close_span_id, "15", "cancelled by user");
					}
					return;
				}
			}
			
			// Delete redirects
			for (var i=0; i<redirect_titles.length; i++) {
				apiDeleteRedir(redirect_titles[i]);
			}
			if ( input_data.deletetalk ) {
				if ( redirect_talk_titles.length === 0 ) {
					// No pages to delete
					XFDcloser.taskFinished(nom_data.close_span_id, "15");
					XFDcloser.taskWarning(nom_data.close_span_id, "15", "none found");
				} else {
					// Delete redirects' talk pages - first get talkpage id's from api, so we don't
					// attempt to delete non-existant talkpages (50 at a time - limit for api query)
					for (var j=0; j<redirect_talk_titles.length; j+=50) {
						new mw.Api().get( {
							action: 'query',
							titles: redirect_talk_titles.slice(j, j+49).join("|"),
							indexpageids: 1
						} ).done( apiCallback_getRedirectTalks )
						.fail( apiFailedCallback_getRedirectTalks );					
					}
				}					
			}
		}
	};
	
	new mw.Api().get( {
		action: 'query',
		titles: nomtitles,
		prop: 'redirects',
		rdlimit: 500,
		indexpageids: 1
	} ).done( apiCallback_getRedirects )
	.fail( apiFailedCallback_getRedirects );

};


// --- Task 11: unlinkBacklinks --- //
// AFD and FFD only
XFDcloser.doTask[11] = function(nom_data, input_data) {
	// This section derived from the Twinkle unlink module:
	//  https://en.wikipedia.org/wiki/MediaWiki:Gadget-twinkleunlink.js 
	
	// Notify task is started
	XFDcloser.taskStarted(nom_data.close_span_id, "11");
	
	var query = {};
	// Arrays to hold concated results, will conatins objects
	// such as {"pageid": 785732, "ns": 4, "title": "Wikipedia:About the Sandbox"}
	var blresults = []; 
	var iuresults = [];
	// var to hold the total number of backlinks & image usages to be processed, for checking if
	// everything is done
	var bl_and_iu_total = 0;
	// vars to be incremented
	var count_results = 0;
	var count_unlinked = 0;
	var count_skipped = 0;
	// Reason to be used with edit summary
	var	edit_sum = "Removing link(s): [[" + nom_data.disc_link + "]] " +
		"closed as " + input_data.result + xfdc.script_advert;
	if ( xfdSetting.xfd_type === "ffd" ) {
		edit_sum =  "Removing link(s) / file usage(s): [[" + nom_data.disc_link + "]] " +
		"closed as " + input_data.result + xfdc.script_advert;
	}
	
	// modal interface html
	var unlink_modal_html = '<div id="unlinkXFD-modal" style="display:none;">'+
		'<div id="unlinkXFD-interface">'+
			'<h4 id="unlinkXFD-interface-header">XFDcloser Unlink</h4>'+
			'<div id="unlinkXFD-interface-content">'+
			'<p>All <span id="unlinkXFD-pagecount"></span> pages listed below may be edited '+
			'(unless backlinks are only present due to transclusion of a template).</p>'+
			'<p>To process only some of these pages, use Twinkle\'s unlink tool instead.</p>'+
			'<p style="font-size:90%;">Use with caution, after reviewing the pages listed below. '+
			'Note that the use of high speed, high volume editing software (such as this tool and '+
			'Twinkle\'s unlink tool) is subject to <a href="https://en.wikipedia.org/wiki/WP:ASSISTED" '+
			'target="_blank">Wikipedia:Bot policy#Assisted editing guidelines</a>.</p>'+ 
			'<hr>'+
			'</div>'+
			'<div style="text-align:center;clear:both;" id="unlinkXFD-interface-buttons">'+
				'<button id="unlinkXFD-interface-unlink" style="margin:1em;">Unlink</button>'+
				'<button id="unlinkXFD-interface-cancel" style="margin:1em;">Cancel</button>'+
			'</div>'+
		'</div>'+
	'</div>';	

	apiFailedCallback_editBlPages = function(code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "11", "remove backlink(s)", code, result );
	};	
	
	//6) Edit pages
	editBlPages = function(page_title, replace_wikitext) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: page_title,
			text: replace_wikitext,
			summary: edit_sum,
			minor: 1,
			nocreate: 1
		} ).done( function() {
			count_unlinked++;
			if (count_unlinked + count_skipped === bl_and_iu_total) {
				XFDcloser.taskFinished(nom_data.close_span_id, "11");
				if ( count_skipped > 0 ) {
					XFDcloser.taskWarning(nom_data.close_span_id, "11", count_skipped +
					" page(s) with no direct backlinks skipped, details logged to javascript console " +
					"<span style='font-size:85%;'>(<a href='http://webmasters.stackexchange.com/questions/8525/how-to-open-the-javascript-console-in-different-browsers#answer-77337' " +
					"target='_blank' class='external'>?</a>)</span>");
				}
			}
		} ).fail( apiFailedCallback_editBlPages );

	};
	
	apiFailedCallback_getWikitext = function(code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "11", "read contents of page(s); could not " + 
		"remove backlink(s)", code, result );
	};
	
	//4) For each backlink's wikitext, pass it through the unlink function; edit the page if needed
	var changed = 0; //var to increment, so not too many edits are made at once
	apiCallback_getWikitext = function(result) {
		var page_ids = result.query.pageids;
		for ( var pp = 0; pp<page_ids.length; pp++ ) {
			old_wikitext = result.query.pages[ page_ids[pp] ].revisions[0]['*'];
			//If this is a disambiguation page, change namespace to -7
			if ( result.query.pages[ page_ids[pp] ].categories ) {
				result.query.pages[ page_ids[pp] ].ns = -7;
			}
			new_wikitext = XFDcloser.unlink(old_wikitext, nom_data.pages, result.query.pages[ page_ids[pp] ].ns);
			if ( old_wikitext !== new_wikitext ) {
				changed++;
				// Pause for a bit before editing
				var start = new Date().getTime();
				for (var i = 0; i < 1e7; i++) { //non-infinite loop that shoud still take awhile 
					if ( (new Date().getTime() - start) > 600 ) { //delay in milliseconds
						break;
					}
				}
				// edit summary depeneds on if it is a file or not
				editBlPages(result.query.pages[ page_ids[pp] ].title, new_wikitext);
	
			} else { // skip (and log to console)
				count_skipped++;
				console.log("[XFDcloser] Skipped unlinking \"" +
				result.query.pages[ page_ids[pp] ].title + "\" (no backlinks found)");
				if (count_unlinked + count_skipped === bl_and_iu_total) {
					XFDcloser.taskFinished(nom_data.close_span_id, "11");
					if ( count_skipped > 0 ) {
						XFDcloser.taskWarning(nom_data.close_span_id, "11", count_skipped +
						" page(s) with no direct backlinks skipped, details logged to javascript console " +
						"<span style='font-size:85%;'>(<a href='http://webmasters.stackexchange.com/questions/8525/how-to-open-the-javascript-console-in-different-browsers#answer-77337' " +
						"target='_blank' class='external'>?</a>)</span>");
					}
				}
			}
		}
	};
	
	//3) List affected pages, notify that Twinkle is an alternative
	processBacklinks = function() {
		// Make modal interface
		$("body").prepend(unlink_modal_html);
		
		//Change list items into links
		

		//Apply styles
		$("#unlinkXFD-modal").css({
			"display": "none",
			"position": "fixed",
			"z-index": "1",
			"left": "0",
			"top": "0",
			"width": "100%",
			"height": "100%",
			"overflow": "auto",
			"background-color": "rgba(0,0,0,0.4)"
		});
		$("#unlinkXFD-interface").css({
			"background-color": "#f0f0f0",
			"margin": "15% auto",
			"padding": "2px 20px",
			"border": "1px solid #888",
			"width": "80%",
			"max-width": "60em",
			"font-size": "90%",
			"overflow": "auto"
		});
		$("#unlinkXFD-interface-content").css({
			"border": "1px solid #ccc",
			"overflow-y": "scroll",
			"max-height": "20em",
			"background": "#fdfdfd"
		});

		// Arrays to hold just the titles
		var bltitles = []; 
		var iutitles = [];
		
		//convert results (arrays of objects) to titles (arrays of strings), removing duplicates
		if ( iuresults.length !== 0 ) {
			for (var iui = 0; iui < iuresults.length; iui++) {
				if ( $.inArray(iuresults[iui].title, iutitles) === -1 ) {
					iutitles.push(iuresults[iui].title);
				}
			}
		}
		for (var bli = 0; bli < blresults.length; bli++) {
			if ( $.inArray(blresults[bli].title, bltitles) === -1 && $.inArray(blresults[bli].title, iutitles) === -1 ) {
				bltitles.push(blresults[bli].title);
			}
		}
		
		// List file usage in modal (if any)
		if ( iutitles.length > 1 ) {
			$("#unlinkXFD-interface-content").append("<h5>File usage (and backlinks, if any):</h5>"+
			"<ul><li>" + iutitles.join('</li><li>') + '</li></ul>');
		} else if ( iutitles.length === 1 ) {
			var append_iu = "<h5>File usage (and backlinks, if any):</h5>"+
			"<ul><li>" + iutitles[0] + "</li></ul>";
			$("#unlinkXFD-interface-content").append(append_iu);
		}
		
		// List backlinks in modal (if any)
		if ( bltitles.length > 1 ) {
			$("#unlinkXFD-interface-content").append("<h5>Backlinks:</h5>"+
			"<ul><li>" + bltitles.join('</li><li>') + '</li></ul>');
		} else if ( bltitles.length === 1 ) {
			var append_bl = "<h5>Backlinks:</h5>"+
			"<ul><li>" + bltitles[0] + "</li></ul>";
			$("#unlinkXFD-interface-content").append(append_bl);
		}

		
		//Change plaintext titles into links
		$("#unlinkXFD-interface-content li").each(function() {
			var tt = $(this).text();
			var ttlink = '<a href="https://en.wikipedia.org/wiki/' +
				mw.util.wikiUrlencode(tt) + '" target="_blank">' +
				tt + '</a>';
			$(this).html(ttlink);
		});
		
		
		//Count pages
		$("#unlinkXFD-pagecount").text(iutitles.length + bltitles.length);
		
		// Now show modal
		$("#unlinkXFD-modal").show();
		
		// Cancel button clicked
		$("#unlinkXFD-interface-cancel, #unlinkXFD-interface-close").click(function(){
			unlinking_cancelled = true;
			$("#unlinkXFD-modal").remove();
			XFDcloser.taskFinished(nom_data.close_span_id, "11");
			XFDcloser.taskWarning(nom_data.close_span_id, "11", "cancelled by user");	
		});

		
		// Unlink button clicked
		$("#unlinkXFD-interface-unlink").click(function(){
			$("#unlinkXFD-modal").remove();
			bl_and_iu_titles = iutitles.concat(bltitles);
			bl_and_iu_total = bl_and_iu_titles.length;
			// get wikitext of titles, check if in the dab category
			for (var ii=0; ii<bl_and_iu_total; ii+=50) {
				new mw.Api().get( {
					action: "query",
					titles: bl_and_iu_titles.slice(ii, ii+49).join("|"),
					prop: "categories|revisions",
					clcategories: "Category:All disambiguation pages",
					rvprop: "content",
					indexpageids: 1
				} ).done( apiCallback_getWikitext )
				.fail( apiFailedCallback_getWikitext );
			}	
		});

	};

	apiFailedCallback_getBacklinks = function(code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "11", "get backlinks", code, result );
	};	

	//2) Callback to process api results
	apiCallback_getBacklinks = function (result) {
		if ( result.query.backlinks ) {
			blresults = blresults.concat(result.query.backlinks);
		}
		if ( result.query.imageusage ) {
			iuresults = iuresults.concat(result.query.imageusage);
		}		
		if ( result.continue ) {
			// More results to come, do another query
			jQuery.extend(query, result.continue);
			if ( !result.continue.blcontinue ) delete query.blcontinue;
			if ( !result.continue.iucontinue ) delete query.iucontinue;
			new mw.Api().get( query )
			.done( apiCallback_getBacklinks )
			.fail( apiFailedCallback_getBacklinks );
		} else {
			// All results for this page done
			count_results++;
			if ( count_results === nom_data.pages.length ) {
				// All nominated pages done, check if there is anything to do
				if ( blresults.length === 0 && iuresults.length === 0 ) {
					//no backlinks or image usages
					XFDcloser.taskFinished(nom_data.close_span_id, "11");
					XFDcloser.taskWarning(nom_data.close_span_id, "11", "none found");
					return;
				} else {
					processBacklinks();				
				}
			} else {
				// Start query for next nominated page
				query.bltitle = nom_data.pages[count_results];
				if ( query.iutitle ) query.iutitle = nom_data.pages[count_results];
				delete query.continue;
				delete query.blcontinue;
				delete query.iucontinue;
				new mw.Api().get( query )
				.done( apiCallback_getBacklinks )
				.fail( apiFailedCallback_getBacklinks );				
			}
			
		}
	};
		
	
	//1) Get backlinks (in namespaces 0, 10, 100, 118)
	if( xfdSetting.xfd_type === "ffd" ) {  // File:
		query = {
			action: 'query',
			list: [ 'backlinks', 'imageusage' ],
			bltitle: nom_data.pages[0],
			iutitle: nom_data.pages[0],
			blfilterredir: 'nonredirects',
			iufilterredir: 'nonredirects',
			bllimit: xfdc.isSysop ? 5000 : 500, // Max for sysop / normal
			iulimit: xfdc.isSysop ? 5000 : 500, // Max for sysop / normal
			blnamespace: xfdc.unlink_ns,
			iunamespace: xfdc.unlink_ns
		};
	} else {
		query = {
			action: 'query',
			indexpageids: 1,
			list: 'backlinks',
			bltitle: nom_data.pages[0],
			blfilterredir: 'nonredirects',
			bllimit: xfdc.isSysop ? 5000 : 500, // Max for sysop / normal
			blnamespace: xfdc.unlink_ns
		};
	}
	new mw.Api().get( query )
	.done( apiCallback_getBacklinks )
	.fail( apiFailedCallback_getBacklinks );
};


// --- Task 12: redirect each nominated page to target --- //
XFDcloser.doTask[12] = function(nom_data, input_data, delete_first, task_number) {
	
	// Default values if params are not defined
	if ( delete_first == null ) {
		delete_first = false;
	}
	if ( task_number == null ) {
		task_number = "12";
	}

	// Notify task is started
	XFDcloser.taskStarted(nom_data.close_span_id, task_number);
	
	var count_r = 0; // var to increment after each edit
	
	var rcatshell = "\n\n{{Rcat shell}}";
	if ( input_data.rcats === false ) {
		rcatshell = "";
	} else if ( input_data.rcats !== "" ) {
		rcatshell = "\n\n{{Rcat shell|\n" + input_data.rcats + "\n}}"; 
	}
	
	// Replacement wikitext to make redirect
	if ( input_data.softredir ) {
		redirect_wikitext = "{{Soft redirect|" + input_data.target + input_data.targetFrag + "}}" + rcatshell;
	} else {
		redirect_wikitext = "#REDIRECT " + input_data.targetLink + rcatshell;
	}
	
	// Edit summary
	var edit_sum = "[[" + nom_data.disc_link + "]] closed as " + input_data.result + xfdc.script_advert;
	var del_reason = "[[" + nom_data.disc_link + "]]" + xfdc.script_advert;
	
	// Callback functions
	apiFailedCallback_editMakeRedirect = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, task_number, "edit page(s) to make redirect(s)", code, result );
	};		

	apiFailedCallback_deleteBeforeRedir = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, task_number, "delete page(s)", code, result );
	};
	
	//Function to make a redirect
	apiEditMakeRedirect = function(page_title) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: page_title,
			text: redirect_wikitext,
			summary: edit_sum
		} ).done( function() {
			count_r++;
			if (count_r === nom_data.pages.length) {
				XFDcloser.taskFinished(nom_data.close_span_id, task_number);
			}
		} ).fail( apiFailedCallback_editMakeRedirect );
	};
	
	
	// Function to delete a redirect
	apiDeleteBeforeRedir = function(pagetitle) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'delete',
			title: pagetitle,
			reason: del_reason,
		} ).done( function() {
			//Page deleted, now create redirect in its place
			apiEditMakeRedirect(pagetitle);
		} ).fail( apiFailedCallback_deleteBeforeRedir );
	};
	
	// For each page, check that it isn't the target, then make it into a redirect
	for (var i=0; i<nom_data.pages.length; i++) {
		if (nom_data.pages[i] === input_data.target) {
			//Skip (don't make a page redirect to itself) - just increment count and check if done
			count_r++;
			if (count_r === nom_data.pages.length) {
				XFDcloser.taskFinished(nom_data.close_span_id, task_number);
			}
		} else if (delete_first) {
			apiDeleteBeforeRedir(nom_data.pages[i]);
		} else {
			apiEditMakeRedirect(nom_data.pages[i]);
		}
	}
};			


// --- Task 13: Delete each nominated page and then redirect --- //
XFDcloser.doTask[13] = function(nom_data, input_data) {
	// Do by calling task 12 with extra parameters
	XFDcloser.doTask[12](nom_data, input_data, true, "13");
};				

// --- Task 14: Unlink circular links from redirect target --- //
XFDcloser.doTask[14] = function(nom_data, input_data) {
	
	editsum = "Unlinking circular redirects – [[" + nom_data.disc_link + "]] closed as " +
	input_data.result + xfdc.script_advert;
	
	// Notify task is started
	XFDcloser.taskStarted(nom_data.close_span_id, "14");
	
	// Api failure callbacks
	apiFailedCallback_editRedirectTarget = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "14", "edit redirect target", code, result );
	};		
	apiFailedCallback_getRedirectTarget = function (code, result) {
		XFDcloser.Api_failed(nom_data.close_span_id, "14", "delete page(s)", code, result );
	};
	
	// Function to edit redirect
	apiEditRedirectTarget = function (new_wikitext) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: input_data.target,
			text: new_wikitext,
			summary: editsum
		} ).done( function() {
			XFDcloser.taskFinished(nom_data.close_span_id, "14");
		} ).fail( apiFailedCallback_editNomPage );
	};		

	// Callback function for processing api result
	apiCallback_getRedirectTarget = function (result) {	
		var p_id = result.query.pageids;
		var p_wikitext = result.query.pages[ p_id ].revisions[ 0 ][ '*' ];
		// Check if redirect target is one of the nominated pages (if it is, then index is not -1)
		var j = nom_data.pages.indexOf(input_data.target);
		// Unlink nomated pages
		if ( j !== -1 ) {
			// Don't want to unlink self-links, which may be to a section on the page
			var unlink_pages = nom_data.pages.slice(); //first duplicate array...
			unlink_pages.splice(j); //...then remove target from duplicted array
			p_wikitext = XFDcloser.unlink(p_wikitext, unlink_pages);
		} else {
			p_wikitext = XFDcloser.unlink(p_wikitext, nom_data.pages);
		}

		// Check if any changes need to be made; if redirect target is a nominated page, remove
		// nomination template and adjust edit summary
		if ( p_wikitext === result.query.pages[ p_id ].revisions[ 0 ][ '*' ] ) {
			if ( j !== -1 ) {
				// No links to unlink, and the redirect target is one of the nominated pages
				p_wikitext = p_wikitext.replace(xfdSetting.regex.nomTemplate, "");
				editsum = xfdSetting.xfd_type.toUpperCase() + " closed as " +
				input_data.result + xfdc.script_advert;
				XFDcloser.taskWarning(nom_data.close_span_id, "14", "none found");
			} else {
				// No links to unlink, and no changes to be made
				XFDcloser.taskFinished(nom_data.close_span_id, "14");
				XFDcloser.taskWarning(nom_data.close_span_id, "14", "none found");
				return;
			}
		} else if ( j !== -1 ) {
			// There are links to unlink, and the redirect target is one of the nominated pages
			p_wikitext = p_wikitext.replace(xfdSetting.regex.nomTemplate, "");
			editsum = xfdSetting.xfd_type.toUpperCase() + " closed as " +
			input_data.result + "; unlinking circular redirects" + xfdc.script_advert;
		}			
		// Now make the edit
		apiEditRedirectTarget(p_wikitext);
	};

	// Get redirect target wikitext
	new mw.Api().get( {
		action: 'query',
		titles: input_data.target,
		prop: 'revisions',
		rvprop: 'content',
		indexpageids: 1,
		rawcontinue: ''
	} ).done( apiCallback_getRedirectTarget )
	.fail( apiFailedCallback_getRedirectTarget );
	
};		

// --- Task 15: Delete redirect talk pages --- //
// (done as part of task 10)
XFDcloser.doTask[15] = function() {
	return;
};

// Function to commence post-close tasks (which should not be started until xfd discussion is actually closed)
XFDcloser.doPostCloseTasks = function(nom_data, input_data, tasks) {
	for (var ii = 1; ii < tasks.length; ii++) {
		XFDcloser.doTask[ tasks[ii] ](nom_data, input_data);
	}
};


//Process closing info
XFDcloser.processClose = function(nom_data, input_data) {

	xfdc.started++;
	
	$XFDClosing = $("#" + nom_data.close_span_id);
	
	var task_12_text = "Making page(s) into redirect(s)...";
	if ( xfdSetting.xfd_type === "rfd" ) {
		task_12_text = "Retargeting redirect(s)...";
	}
	
	tasks_html = "<div style='width:80%;font-size:92%;color:#777;float:left;clear:right;padding:0px 10px;'>"+
	"<p class='XFDcloser-task-0' style='display:none;margin:0;'>"+
		"Closing discussion....</p>"+
	"<p class='XFDcloser-task-1' style='display:none;margin:0;'>"+
		"Updating talk page(s)...</p>"+
	"<p class='XFDcloser-task-2' style='display:none;margin:0;'>"+
		"Updating page(s)...</p>"+
	"<p class='XFDcloser-task-3' style='display:none;margin:0;'>"+
		"Updating page(s)...</p>"+
	"<p class='XFDcloser-task-4' style='display:none;margin:0;'>"+
		"Deleting page(s)...</p>"+
	"<p class='XFDcloser-task-5' style='display:none;margin:0;'>"+
		"Tagging page(s) for speedy deletion...</p>"+
	"<p class='XFDcloser-task-6' style='display:none;margin:0;'>"+
		"Updating template(s)...</p>"+
	"<p class='XFDcloser-task-7' style='display:none;margin:0;'>"+
		"Listing at holding cell...</p>"+
	"<p class='XFDcloser-task-8' style='display:none;margin:0;'>"+
		"Deleting talk page(s)...</p>"+
	"<p class='XFDcloser-task-9' style='display:none;margin:0;'>"+
		"Tagging talk page(s) for speedy deletion...</p>"+
	"<p class='XFDcloser-task-10' style='display:none;margin:0;'>"+
		"Deleting redirects...</p>"+
	"<p class='XFDcloser-task-15' style='display:none;margin:0;'>"+
		"Deleting redirects' talk pages...</p>"+
	"<p class='XFDcloser-task-11' style='display:none;margin:0;'>"+
		"Unlinking backlinks...</p>"+
	"<p class='XFDcloser-task-12' style='display:none;margin:0;'>"+
		task_12_text + "</p>"+
	"<p class='XFDcloser-task-13' style='display:none;margin:0;'>"+
		"Deleting page(s) and replacing with redirect(s)...</p>"+
	"<p class='XFDcloser-task-14' style='display:none;margin:0;'>"+
		"Unlinking circular links on redirect target...</p>"+
	"</div>";
	
	$XFDClosing.html(tasks_html).prev().css("float", "left");
	
	tasksToDo = [];
	xfdc.tasks_completed[nom_data.close_span_id] = 0; //number of tasks completed
	
	tasksToDo.push(0); //closeSection
	if ( input_data.after === "doKeepActions" ){
		tasksToDo.push(1); //addOldXfd
		tasksToDo.push(2); //removeNomTemplate
	} else if ( input_data.after === "doRedirectActions" ) {
		tasksToDo.push(1); //addOldXfd
		if ( input_data.delAndRedir ) {
			tasksToDo.push(13); //deleteThenRedirect
		} else {
			tasksToDo.push(12); //redirect
		}
		if ( !input_data.softredir ) {
			tasksToDo.push(14); //removeCircularLinks
		}
	} else if ( input_data.after === "doMergeActions" && xfdSetting.xfd_type !== "tfd" ) {
		tasksToDo.push(1); //addOldXfd
		tasksToDo.push(3); //addMergeTemplates
	} else if ( input_data.after === "doDeletion" ) {
		tasksToDo.push(4); //peformDelete
		if ( input_data.deletetalk ) {
			tasksToDo.push(8); //deleteTalk
		}
	} else if ( input_data.after === "tagSpeedy" ) {
		tasksToDo.push(5); //tagWithSpeedy
		if ( input_data.deletetalk ) {
			tasksToDo.push(9); //tagTalkWithSpeedy
		}				
	} else if ( (input_data.after === "holdingCell") ||
				(input_data.after === "doMergeActions" && xfdSetting.xfd_type === "tfd") ) {
		tasksToDo.push(6); //addBeingDeleted
		tasksToDo.push(7); //addToHoldingCell
		if ( input_data.deletetalk ) {
			tasksToDo.push(9); //tagTalkWithSpeedy
		}	
	}

	if ( input_data.deleteredir && (input_data.after !== "noAction") ) {
		tasksToDo.push(10); //deleteRedirects
		if ( input_data.deletetalk ) {
			tasksToDo.push(15); //deleteRedirectsTalks
		}
	}
	if ( input_data.unlinkbackl && (input_data.after !== "noAction") ) {
		tasksToDo.push(11); //unlinkBacklinks
	}
	
	xfdc.tasks_to_do[nom_data.close_span_id] = tasksToDo.length; //number of tasks to be done
	xfdc.task_errors[nom_data.close_span_id] = new Array(tasksToDo.length);

	// Notify which tasks are to be done
	for (var i = 0; i < tasksToDo.length; i++){
		XFDcloser.updateTask(nom_data.close_span_id, tasksToDo[i].toString());
	}

	//Close section (and then start doing post-close tasks)
	XFDcloser.doTask[0](nom_data, input_data, tasksToDo);

};


/* == Relisting-related functions == */
/* ********************************* */

//Steps

//1: Grab wikitext from old discussion (and if AFD, also get log page it is transcluded onto)
//2: Append relisting note to wikitext from old discussion
//3: [non-AFD only] Make closed-as-relist-wikitext, removing discussion but keeping page links
//4: Replace old log page entry with closed-as-relist-wikitext [non-AFD] / remove (replace with "")
//   transclusion from old log page [AFD]
//5: Add discussion/transclusion to today's log page
//6: Update nom templates on nominated pages

//note: CfD doesn't reslist

XFDcloser.processRelist = function(nom_data, relist_comment) {
	// Parts of this section derived from https://en.wikipedia.org/wiki/User:Mr.Z-man/closeAFD2.js

	xfdc.started++;
	$XFDClosing = $("#" + nom_data.close_span_id);
	$XFDClosing.prev().css("float", "left");	
	var afdLogEditIndex = 0;
	if ( xfdSetting.xfd_type === "afd" ) {
		// add a Deferred() for when this relist has finished editing the log page, keep track of it
		// using its array index
		afdLogEditIndex = xfdc.afdLogEdit.push($.Deferred()) - 1;
		$("<div>")
		.attr('id', 'XFDcloser-waiting-'+afdLogEditIndex)
		.css({
			'width':'80%',
			'font-size':'92%',
			'color':'#777',
			'float':'left',
			'clear':'right',
			'padding':'0px 10px'
		})
		.text("Waiting... (to avoid edit conflicts, previous relistings need to be completed)")
		.appendTo($XFDClosing);
	}
	var startRelist = function(nom_data, relist_comment, afdLogEditIndex) {
		$XFDClosing = $("#" + nom_data.close_span_id);
			
		$("#XFDcloser-waiting-"+afdLogEditIndex).hide();
		// Today's new, new log page path
		var today = new Date();
		var curdate = today.getUTCFullYear().toString() + " " + xfdc.monthnames[today.getUTCMonth()] +
			" " + today.getUTCDate().toString();
		var todays_logpage = xfdSetting.path + curdate;
		
		var task1_text = "Removing from old log page...";
		if ( xfdSetting.xfd_type == "afd" ) {
			task1_text = "Removing from <span id='" + nom_data.close_span_id + "_oldlogpage'>old log page</span>...";
		} else if ( xfdSetting.xfd_type == "ffd" || xfdSetting.xfd_type == "rfd" ||  xfdSetting.xfd_type == "tfd" ) {
			task1_text = "Removing from " + XFDcloser.makeLink(nom_data.nom_page, "old log page") + "...";
		}
		
		// Tasks to be done
		tasks_html = "<div style='width:80%;font-size:92%;color:#777;float:left;clear:right;padding:0px 10px;'>"+
		"<p class='XFDcloser-task-0' style='display:none;margin:0;'>"+
			"Editing discussion....</p>"+
		"<p class='XFDcloser-task-1' style='display:none;margin:0;'>"+
			task1_text + "</p>"+
		"<p class='XFDcloser-task-2' style='display:none;margin:0;'>"+
			"Adding to " + XFDcloser.makeLink(todays_logpage, "today's log page") + "...</p>"+
		"<p class='XFDcloser-task-3' style='display:none;margin:0;'>"+
			"Updating link in nominations template(s)...</p>"+
		"</div>";
		$XFDClosing.html(tasks_html);

		// Tasks to be done depend on xfd type
		if ( xfdSetting.xfd_type === "afd" ) {
			tasksToDo = [0, 1, 2];
		} else if ( xfdSetting.xfd_type === "mfd" ) {
			tasksToDo = [0];
		} else if ( xfdSetting.xfd_type === "rfd" ) {
			tasksToDo = [1, 2];
		} else {
			tasksToDo = [1, 2, 3];		
		}
		xfdc.tasks_to_do[nom_data.close_span_id] = tasksToDo.length;
		xfdc.task_errors[nom_data.close_span_id] = new Array(tasksToDo.length);
		xfdc.tasks_completed[nom_data.close_span_id] = 0; //number of tasks completed
		for (var i = 0; i < tasksToDo.length; i++){
			XFDcloser.updateTask(nom_data.close_span_id, tasksToDo[i].toString());
		}
		
		// Edit summaries
		var editsum_closeAsRelist = "/* " + nom_data.section_header + " */ Closed as relisted on " +
			"[[" + todays_logpage + "#" + nom_data.section_header + "|" + curdate + "]]" +
			xfdc.script_advert;
		if ( xfdSetting.xfd_type === "rfd" ) {
			editsum_closeAsRelist = "/* " + nom_data.section_header + " */ Relisted on " +
			"[[" + todays_logpage + "#" + nom_data.section_header + "|" + curdate + "]]" +
			xfdc.script_advert;
		}
		var editsum_relistingOn = "Relisting discussion on " + curdate + xfdc.script_advert;
		var	editsum_newLogpageSection = "Relisting \"" + nom_data.section_header +
			"\"" +xfdc.script_advert;
		var	editsum_logpageTransclusion = "Relisting [[" + nom_data.nom_page +
			"]]" + xfdc.script_advert;
		var	editsum_discussion = "Relisting discussion" + xfdc.script_advert;
		var editsum_updateNomTemplate = "Updating " + xfdSetting.xfd_type.toUpperCase() +
			" template: discussion was relisted" + xfdc.script_advert;

		// Vars to be available to multiple subfunctions
		var new_disc_wikitext = "";
		var closed_disc_wikitext = "";
		var oldlogtitle = "";
		var nomTemplates_updated = 0; //var to increment

		// Callbacks for functions to edit pages via api
		apiFailedCallback_editRelistDisc = function(code, result) {
			XFDcloser.Api_failed(nom_data.close_span_id, "0", "edit " +
			xfdSetting.xfd_type.toUpperCase() + " discussion", code, result );
		};
		apiCallback_editRelistDisc = function() {
			XFDcloser.taskFinished(nom_data.close_span_id, "0");
		};
		
		apiFailedCallback_editOldLogpage = function(code, result) {
			XFDcloser.Api_failed(nom_data.close_span_id, "1", "edit old " +
			xfdSetting.xfd_type.toUpperCase() + " log page", code, result );
			if ( afdLogEditIndex ) {
				xfdc.afdLogEdit[afdLogEditIndex].resolve();
			}
		};
		apiCallback_editOldLogpage = function() {
			XFDcloser.taskFinished(nom_data.close_span_id, "1");
			if ( afdLogEditIndex ) {
				xfdc.afdLogEdit[afdLogEditIndex].resolve();
			}
		};
		
		apiFailedCallback_editNewLogpage = function(code, result) {
			XFDcloser.Api_failed(nom_data.close_span_id, "2", "edit old " +
			xfdSetting.xfd_type.toUpperCase() + " log page", code, result );
		};	
		apiCallback_editNewLogpage = function() {
			XFDcloser.taskFinished(nom_data.close_span_id, "2");
		};
		
		apiFailedCallback_editNomTemplates = function(code, result) {
			XFDcloser.Api_failed(nom_data.close_span_id, "3", "update link in nomination " +
			"template(s)", code, result );
		};
		apiCallback_editNomTemplates = function() {
			nomTemplates_updated++;
			if (nomTemplates_updated === nom_data.pages.length) {
				XFDcloser.taskFinished(nom_data.close_span_id, "3");
			}
		};

		
		// Function to edit pages via api
		apiEditPage = function (api_params, done_callback, fail_callback) {
			new mw.Api().postWithToken( 'csrf', api_params)
				.done( done_callback )
				.fail( fail_callback );
		};

		// Function to update nomination templates can be updated
		updateNomTemplates = function() {
			// Calbacks for getting pages' wikitext
			apiFailedCallback_getPagesWikitext = function(code, result) {
				XFDcloser.Api_failed(nom_data.close_span_id, "3", "read content of pages", code, result);
			};
			apiCallback_getPagesWikitext = function(result) {
				//Get old wikitext, or make error if page doesn't exist, or if page is in wrong namespace
				var oldwikitext = "";
				var page_ids = result.query.pageids;
				for (var i = 0; i < page_ids.length; i++) {
					var p_title = result.query.pages[ page_ids[i] ].title;

					// Check that subject page exists
					j = nom_data.pages.indexOf(p_title);
					if ( j === -1 ) {
						XFDcloser.taskError(nom_data.close_span_id, "3", "API: query result included " +
							"unexpected page title \"" + XFDcloser.makeLink(p_title) + "\"; " +
							"this page will not be edited");
					} else if ( parseInt(page_ids[i]) < 0 ) { //more recent info than nom_data.pageExists[j] 
						XFDcloser.taskError(nom_data.close_span_id, "3", "The page \"" +
							XFDcloser.makeLink(p_title) + "\" does not exist, and will not be edited");
					} else {						
						oldwikitext = result.query.pages[ page_ids[i] ].revisions[ 0 ][ '*' ];
						replace_str = xfdSetting.wikitext.relistReplace.replace("__TODAY__", curdate);
						updated_wikitext = oldwikitext.replace(xfdSetting.regex.relistPattern, replace_str);
						apiEditPage({
							action: 'edit',
							title: p_title,
							text: updated_wikitext,
							summary: editsum_updateNomTemplate
						}, apiCallback_editNomTemplates, apiFailedCallback_editNomTemplates);
					}
				}
			};
			if ( nom_data.pages[0]===-1 ) {
				XFDcloser.taskError(nom_data.close_span_id, "3", "nominated pages were not detected, " +
				"and were not edited");
				return;
			}
			//get each nominated page's wikitext through api
			new mw.Api().get( {
				action: 'query',
				titles: nom_data.pages.join("|"),
				prop: 'revisions',
				rvprop: 'content',
				indexpageids: 1,
				rawcontinue: ''
			} ) .done( apiCallback_getPagesWikitext )
				.fail( apiFailedCallback_getPagesWikitext );
			
		};


		// Callbacks for getting new & old logpage (AFDs)
		apiFailedCallback_getLogpages = function(code, result) {
			XFDcloser.Api_failed(nom_data.close_span_id, "0", "read contents of " +
			"log page(s)", code, result );
			if ( afdLogEditIndex ) {
				xfdc.afdLogEdit[afdLogEditIndex].resolve();
			}
		};	
		apiCallback_getLogpages = function(result) {
			//get wikitext
			var newlogtext = "";
			var oldlogtext = "";
			log_ids = result.query.pageids;
			if ( result.query.pages[ log_ids[0] ].title === todays_logpage ) {
				//log_ids[0] is the new log page
				newlogtext = result.query.pages[ log_ids[0] ].revisions[ 0 ][ '*' ];
				oldlogtext = result.query.pages[ log_ids[1] ].revisions[ 0 ][ '*' ];
			} else {
				//log_ids[0] is the old log page
				newlogtext = result.query.pages[ log_ids[1] ].revisions[ 0 ][ '*' ];
				oldlogtext = result.query.pages[ log_ids[0] ].revisions[ 0 ][ '*' ];
			}
			
			//test for transclusion of nompage
			var t = mw.RegExp.escape(nom_data.nom_page);
			var patt = new RegExp("<!-- ?\\{\\{" + t + "\\}\\} ?-->", 'i' );
			var res = patt.exec(oldlogtext);
			if (res) {
				XFDcloser.taskError(nom_data.close_span_id, "0", "Discussion appears to be relisted " +
				"already [relist aborted]");
				xfdc.afdLogEdit[afdLogEditIndex].resolve();
				return;
			}
			
			// Updated new log wikitext:
			var newlogreg = new RegExp("<!-- Add new entries to the TOP of the following list -->", "i");
			newlogtext = newlogtext.replace(newlogreg, "<!-- Add new entries to the TOP of the following list -->\n"+
			"{" + "{" + nom_data.nom_page + "}}<!--Relisted-->");
			// Updated old log wikitext:
			var oldlogreg = new RegExp("(\\{\\{" + t + "\\}\\})", 'i' );
			// Check if tranbsclusion found by regex
			var transclusion_found = oldlogreg.test(oldlogtext);
			oldlogtext = oldlogtext.replace(oldlogreg, "<!-- $1 -->");
			
			//Now we can make the edits...
			//Edit discussion
			apiEditPage({
				action: 'edit',
				title: nom_data.nom_page,
				text: new_disc_wikitext,
				summary: editsum_discussion
			}, apiCallback_editRelistDisc, apiFailedCallback_editRelistDisc);
			
			//Edit old logpage, or show error if transclusion was not found
			if ( transclusion_found ) {
				apiEditPage({
					action: 'edit',
					title: oldlogtitle,
					text: oldlogtext,
					summary: editsum_logpageTransclusion
				}, apiCallback_editOldLogpage, apiFailedCallback_editOldLogpage);
			} else {
				XFDcloser.taskError(nom_data.close_span_id, "1", "Transclusion \"{{" + t + "}}\" "+
				"was not found on old log page, and could not be commented out");
				xfdc.afdLogEdit[afdLogEditIndex].resolve();
			}	
			
			//Edit today's logpage
			apiEditPage({
				action: 'edit',
				title: todays_logpage,
				text: newlogtext,
				summary: editsum_logpageTransclusion
			}, apiCallback_editNewLogpage, apiFailedCallback_editNewLogpage);				
			
		};
		
		// Callbacks for getting today's logpage (TFDs & RFDs)
		apiFailedCallback_getTodaysLogpage = function(code, result) {
			XFDcloser.Api_failed(nom_data.close_span_id, "0", "read contents of " +
			"today's log page \"" +  XFDcloser.makeLink(todays_logpage) + "\"", code, result );
		};	
		apiCallback_getTodaysLogpage = function(result) {
			var _id = result.query.pageids;
			var _contents = result.query.pages[ _id ].revisions[ 0 ][ '*' ];
			var _h4 = _contents.match("====");
			if ( _h4 ) {
				// there is at least 1 level 4 heading on page - can prepend to section #2
				params_editNewLog = {
					action: 'edit',
					title: todays_logpage,
					section: 2,
					prependtext: new_disc_wikitext,
					summary: editsum_newLogpageSection
				};
			} else {
				// there are no level 4 headings on page - can append to section #1
				params_editNewLog = {
					action: 'edit',
					title: todays_logpage,
					section: 1,
					appendtext: "\n" + new_disc_wikitext,
					summary: editsum_newLogpageSection
				};
			
			}
			//Now we can make the edits... 
			//Edit old logpage
			apiEditPage({
				action: 'edit',
				title: nom_data.nom_page,
				section: nom_data.editsection,
				text: closed_disc_wikitext,
				summary: editsum_closeAsRelist
			}, apiCallback_editOldLogpage, apiFailedCallback_editOldLogpage);
			
			//Edit today's logpage
			apiEditPage(params_editNewLog, apiCallback_editNewLogpage, apiFailedCallback_editNewLogpage);
			
			if ( xfdSetting.xfd_type !== "rfd" ) {
				//Update nom templates on nominated pages		
				updateNomTemplates();
			}
		};
		
		// Callbacks for getting relisting info
		apiFailedCallback_getRelistingInfo = function(code, result) {
			XFDcloser.Api_failed(nom_data.close_span_id, "0", "read contents of " +
			 xfdSetting.xfd_type.toUpperCase() + " discussion page", code, result );
			if ( afdLogEditIndex ) {
				xfdc.afdLogEdit[afdLogEditIndex].resolve();
			}
		};	
		apiCallback_getRelistingInfo = function (result) {
			//grab wikitext of section
			var p_id = result.query.pageids;
			var disc_wikitext = result.query.pages[ p_id ].revisions[ 0 ][ '*' ];
			var sec_heading = disc_wikitext.slice(0, disc_wikitext.indexOf("\n"));
			
			//Check for number of relists, wikitext for relist template
			var relist_number = 1;
			relists = disc_wikitext.match(/\[\[Wikipedia:Deletion process#Relisting discussions\|Relisted\]\]/g);
			if ( relists ) {
				relist_number = relists.length + 1;
			}
			if ( !relist_comment ) {
				relist_comment = "";
			}
			var relist_template = "\n{" + "{subst:Relist|1=" + relist_comment + "|2=" + relist_number + "}}\n";
			
			//Make wikitext for the new (relisted) discussion
			new_disc_wikitext = disc_wikitext.trim() + relist_template;
			if ( xfdSetting.xfd_type === "afd" ) {
				new_disc_wikitext = new_disc_wikitext.replace(/\[\[Wikipedia:Articles for deletion\/Log\/\d{4} \w+ \d{1,2}#/, "[["+todays_logpage+"#");
			} else if ( xfdSetting.xfd_type === "ffd" || xfdSetting.xfd_type === "tfd" ) {
				//Make wikitext for the closed (as relist) discussion
				var xfd_close_top = xfdSetting.wikitext.closeTop
					.replace(/__RESULT__/, "relisted")
					.replace(/__TO_TARGET__/, " on [[" + todays_logpage + "#" +
						nom_data.section_header + "|" + curdate + "]]")
					.replace(/__RPUNCT__/, ".")
					.replace(/__RATIONALE__/, "")
					.replace(/__SIG__/, xfdc.sig);
				var pages_list = "";
				if ( nom_data.pages[0] !== -1 ) {
					for (var i=0; i<nom_data.pages.length; i++) {
						relative_title = nom_data.pages[i].replace(/.*?:/, ""); // remove namespace
						pages_list += xfdSetting.wikitext.pagelinks.replace("__PAGE__", relative_title);
					}
				}
				closed_disc_wikitext = sec_heading + "\n" + xfd_close_top + "\n" + pages_list +
				xfdSetting.wikitext.closeBottom;
			} else if ( xfdSetting.xfd_type === "mfd" ) {
				//Find first linebeak after last pagelinks templats
				var split_index = new_disc_wikitext.indexOf("\n", new_disc_wikitext.lastIndexOf(":{{pagelinks"));
				var top_wikitext = new_disc_wikitext.slice(0, split_index);
				var bottom_wikitext = new_disc_wikitext.slice(split_index+1);
				//Make wikitext for the now relisted discussion
				closed_disc_wikitext = top_wikitext.trim() + "\n{{subst:mfdr}}\n" + bottom_wikitext.trim();			
			} else if ( xfdSetting.xfd_type === "rfd" ) {
				var top_wikitext_rfd = "====" + nom_data.section_header + "====";
				if ( disc_wikitext.indexOf("*<span id=") !== disc_wikitext.lastIndexOf("*<span id=") ) {
					//Multiple redirects were nominted, so keep nominated redirects' anchors
					//Find first linebeak after last span with id
					var split_index_1 = disc_wikitext.indexOf("\n", disc_wikitext.indexOf("*<span id=")-2);
					var split_index_2 = disc_wikitext.indexOf("\n", disc_wikitext.lastIndexOf("*<span id="));
					var rfd_anchors = disc_wikitext.slice(split_index_1, split_index_2)
						.replace(/\*<span/g, "<span")
						.replace(/^(?!<span).*$\n?/gm, "")
						.replace(/>.*$\s*/gm, "></span>")
						.trim();
					top_wikitext_rfd += "\n<noinclude>" + rfd_anchors + "</noinclude>";
				}
				//Make wikitext for the closed (as relist) discussion
				closed_disc_wikitext = top_wikitext_rfd + "\n{{subst:rfd relisted|page=" + curdate +
				"|" + nom_data.section_header + "}}";
			}
			
			//Check that discussion is not already closed
			if (disc_wikitext.indexOf('xfd-closed') !== -1) {
				XFDcloser.taskError(nom_data.close_span_id, "1", "Discussion has been closed: " +
				"relist aborted");
				if ( afdLogEditIndex ) {
					xfdc.afdLogEdit[afdLogEditIndex].resolve();
				}
				return;
			}		

			//For AFDs, check that it's still transcluded to old log page...		
			if ( xfdSetting.xfd_type === "afd" ) {
				//First get old logpage title
				for (var ii=0; ii<result.query.embeddedin.length; ii++) {
					if ( result.query.embeddedin[ii].title.indexOf(xfdSetting.path) !== -1) {
						oldlogtitle = result.query.embeddedin[ii].title;
					}
				}
				if ( oldlogtitle === "" ) {
					//not transcluded anywhere, can skip removal from old log page
					XFDcloser.taskError(nom_data.close_span_id, 0, "Old log page not found: " +
					"relist aborted");
					xfdc.afdLogEdit[afdLogEditIndex].resolve();
				} else {
					//set link in task message
					var oldlogpagelink = XFDcloser.makeLink(oldlogtitle, "old log page");
					$("#" + nom_data.close_span_id + "_oldlogpage").html(oldlogpagelink);
					//get old logpage wikitext, process via callback function
					new mw.Api().get( {
						action: 'query',
						titles: oldlogtitle + "|" + todays_logpage,
						prop: 'revisions',
						rvprop: 'content',
						indexpageids: 1,
						rawcontinue: ''
					} ) .done( apiCallback_getLogpages )
						.fail( apiFailedCallback_getLogpages );
				}
			} else if ( xfdSetting.xfd_type === "tfd" || xfdSetting.xfd_type === "rfd" ) {
				//New discussions on top of log page, so need to check out current log page wikitext
				new mw.Api().get( {
					action: 'query',
					titles: todays_logpage,
					prop: 'revisions',
					rvprop: 'content',
					indexpageids: 1,
					rawcontinue: ''
				} ) .done( apiCallback_getTodaysLogpage )
					.fail( apiFailedCallback_getTodaysLogpage );
			} else if ( xfdSetting.xfd_type === "mfd" ) { 
				//Now we can make the edit nomination subpage (MFDs)
				apiEditPage({
					action: 'edit',
					title: nom_data.nom_page,
					section: nom_data.editsection,
					text: closed_disc_wikitext,
					summary: editsum_relistingOn
				}, apiCallback_editRelistDisc, apiFailedCallback_editRelistDisc);
				//Rest of relist process done by bot, per WP:MFDAI
			} else { 
				//Now we can make the edits... (FFDs)
				//Edit old logpage
				apiEditPage({
					action: 'edit',
					title: nom_data.nom_page,
					section: nom_data.editsection,
					text: closed_disc_wikitext,
					summary: editsum_closeAsRelist
				}, apiCallback_editOldLogpage, apiFailedCallback_editOldLogpage);
				
				//Edit today's logpage
				apiEditPage({
					action: 'edit',
					title: todays_logpage,
					appendtext: "\n" + new_disc_wikitext,
					summary: editsum_newLogpageSection
				}, apiCallback_editNewLogpage, apiFailedCallback_editNewLogpage);

				//Update nom templates on nominated pages (FFDs)
				updateNomTemplates();

			}

		};

		//Step 1: get relisting info (discussion wikitext, and if AFD, log page it is transcluded onto)
	
		var get_disc_params = {
				action: 'query',
				titles: nom_data.nom_page,
				prop: 'revisions',
				indexpageids: 1,
				rawcontinue: 1,
				rvprop: 'content',
				rvsection: nom_data.editsection
			};
		if (xfdSetting.xfd_type === "afd") {
			jQuery.extend(get_disc_params, { 
				list: 'embeddedin',
				eititle: nom_data.nom_page,
				einamespace: xfdc.eins,
				eifilterredir: 'nonredirects',
				eilimit: 500
			});
			// Need to fetch whole page, in order to check if afd is already closed
			delete get_disc_params.rvsection;
		}
		new mw.Api().get( get_disc_params )
		.done( apiCallback_getRelistingInfo )
		.fail( apiFailedCallback_getRelistingInfo );	
	};
	if ( afdLogEditIndex ) {
		$.when(xfdc.afdLogEdit[afdLogEditIndex-1]).then(function(){
			startRelist(nom_data, relist_comment, afdLogEditIndex);
		});
	} else {
		startRelist(nom_data, relist_comment);
	}
};

//End of full file closure wrappers
});
});
/* </nowiki> */