(function($) {

/*

Twinkle.xfd = function twinklexfd {
	/* 기능 비활성화 : 아직 준비 안 됨 (2020-08-12) */
	if (true) {
		return;

	// Disable on:
	// * special pages
	// * non-existent pages
	// * files on Commons, whether there is a local page or not 
	if (mw.config.get('wgNamespaceNumber') < 0 || !mw.config.get('wgArticleId') || (mw.config.get('wgNamespaceNumber') === 6 && document.getElementById('mw-sharedupload'))) {
		return;

	Twinkle.addPortletLink;

Twinkle.xfd.num2order = function twinklexfdNum2order(num) {
	switch (num) {
		case 1: return '';
		case 2: return '2nd';
		case 3: return '3rd';
		default: return num + 'th';

Twinkle.xfd.currentRationale = null;

// error callback on Morebits.status.object
Twinkle.xfd.printRationale = function twinklexfdPrintRationale {
	if (Twinkle.xfd.currentRationale) {
		Morebits.status.printUserText;
		// only need to print the rationale once
		Twinkle.xfd.currentRationale = null;

Twinkle.xfd.callback = function twinklexfdCallback {
	var Window = new Morebits.simpleWindow;
	Window.setTitle('Start a deletion discussion (XfD)');
	Window.setScriptName('Twinkle');
	Window.addFooterLink;
	Window.addFooterLink;

	var form = new Morebits.quickForm(Twinkle.xfd.callback.evaluate);
	var categories = form.append({
		type: 'select',
		name: 'category',
		label: 'Deletion discussion venue:',
		tooltip: 'When activated, a default choice is made, based on what namespace you are in. This default should be the most appropriate; some inappropriate options may be disabled.',
		event: Twinkle.xfd.callback.change_category
	var namespace = mw.config.get('wgNamespaceNumber');

	categories.append({
		type: 'option',
		label: 'AfD (Articles for deletion)',
		selected: namespace === 0, // Main namespace
		value: 'afd'
	categories.append({
		type: 'option',
		label: 'TfD (Templates for discussion)',
		selected: [ 10, 828 ].indexOf(namespace) !== -1, // Template and module namespaces
		value: 'tfd',
		disabled: namespace === 10 && /-stub$/.test(Morebits.pageNameNorm) // Stub templates at CfD
	categories.append({
		type: 'option',
		label: 'FfD (Files for discussion)',
		selected: namespace === 6, // File namespace
		value: 'ffd',
		disabled: namespace !== 6
	categories.append({
		type: 'option',
		label: 'CfD (Categories for discussion)',
		selected: namespace === 14 || (namespace === 10 && /-stub$/.test(Morebits.pageNameNorm)), // Category namespace and stub templates
		value: 'cfd',
		disabled: [ 10, 14 ].indexOf(namespace) === -1 // Disabled outside category and templatespace
	categories.append({
		type: 'option',
		label: 'CfD/S (Categories for speedy renaming)',
		value: 'cfds',
		disabled: namespace !== 14
	categories.append({
		type: 'option',
		label: 'MfD (Miscellany for deletion)',
		selected: [ 0, 6, 10, 14, 828 ].indexOf(namespace) === -1 || Morebits.pageNameNorm.indexOf === 0,
		// Other namespaces, and userboxes in template namespace
		value: 'mfd'
	categories.append({
		type: 'option',
		label: 'RfD (Redirects for discussion)',
		selected: Morebits.wiki.isPageRedirect,
		value: 'rfd'
	categories.append({
		type: 'option',
		label: 'RM (Requested moves)',
		selected: false,
		value: 'rm',
		disabled: namespace === 14

	form.append({
		type: 'checkbox',
		list: [
				label: 'Notify page creator if possible',
				value: 'notify',
				name: 'notifycreator',
				tooltip: "A notification template will be placed on the creator's talk page if this is true.",
				checked: true
		]
	form.append({
		type: 'field',
		label: 'Work area',
		name: 'work_area'

	var previewlink = document.createElement('a');
	$(previewlink).click(function {
		Twinkle.xfd.callbacks.preview(result); // |result| is defined below
	previewlink.style.cursor = 'pointer';
	previewlink.textContent = 'Preview';
	form.append;
	form.append;

	form.append({ type: 'submit' });

	var result = form.render;
	Window.setContent(result);
	Window.display;
	result.previewer = new Morebits.wiki.preview($(result).find('div#twinklexfd-previewbox').last[0]);

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

Twinkle.xfd.callback.change_category = function twinklexfdCallbackChangeCategory(e) {
	var value = e.target.value;
	var form = e.target.form;
	var old_area = Morebits.quickForm.getElements[0];
	var work_area = null;

	var oldreasontextbox = form.getElementsByTagName('textarea')[0];
	var oldreason = oldreasontextbox ? oldreasontextbox.value : '';

	var appendReasonBox = function twinklexfdAppendReasonBox {
		work_area.append({
			type: 'textarea',
			name: 'reason',
			label: 'Reason: ',
			value: oldreason,
			tooltip: 'You can use wikimarkup in your reason. Twinkle will automatically sign your post.'

	form.previewer.closePreview;

	switch (value) {
		case 'afd':
			work_area = new Morebits.quickForm.element({
				type: 'field',
				label: 'Articles for deletion',
				name: 'work_area'
			work_area.append({
				type: 'checkbox',
				list: [
						label: 'Wrap deletion tag with ',
						value: 'noinclude',
						name: 'noinclude',
						tooltip: 'Will wrap the deletion tag in &lt;noinclude&gt; tags, so that it won\'t transclude. This option is not normally required.'
				]
			var afd_category = work_area.append({
				type: 'select',
				name: 'xfdcat',
				label: 'Choose what category this nomination belongs in:'

			afd_category.append;
			afd_category.append;
			afd_category.append;
			afd_category.append;
			afd_category.append;
			afd_category.append;
			afd_category.append;
			afd_category.append;
			afd_category.append;
			afd_category.append;
			afd_category.append;
			afd_category.append;

			// delsort categories list copied off User:Enterprisey/delsort.js, originally taken from WP:DS/C
			var delsortCategories = {
				'People': ['People', 'Academics and educators', 'Actors and filmmakers', 'Artists', 'Authors', 'Bands and musicians', 'Businesspeople', 'Politicians', 'Sportspeople', 'Women', 'Lists of people'],
				'Arts': ['Arts', 'Fictional elements', 'Science fiction and fantasy'],
				'Arts/Culinary': ['Food and drink', 'Wine'],
				'Arts/Language': ['Language', 'Academic journals', 'Bibliographies', 'Journalism', 'Literature', 'Logic', 'News media', 'Philosophy', 'Poetry'],
				'Arts/Performing': ['Albums and songs', 'Dance', 'Film', 'Magic', 'Music', 'Radio', 'Television', 'Theatre', 'Video games'],
				'Arts/Visual arts': ['Visual arts', 'Architecture', 'Fashion', 'Photography'],
				'Arts/Comics and animation': ['Comics and animation', 'Anime and manga', 'Webcomics'],
				'Places of interest': ['Museums and libraries', 'Shopping malls'],
				'Topical': ['Animal', 'Bilateral relations', 'Business', 'Conservatism', 'Conspiracy theories', 'Crime', 'Disability', 'Discrimination', 'Ethnic groups', 'Events', 'Games', 'Health and fitness', 'History', 'Law', 'Military', 'Organizations', 'Paranormal', 'Piracy', 'Politics', 'Terrorism'],
				'Topical/Business': ['Business', 'Advertising', 'Companies', 'Management', 'Finance'],
				'Topical/Culture': ['Beauty pageants', 'Fashion', 'Mythology', 'Popular culture', 'Sexuality and gender'],
				'Topical/Education': ['Education', 'Fraternities and sororities', 'Schools'],
				'Topical/Religion': ['Religion', 'Atheism', 'Bible', 'Buddhism', 'Christianity', 'Islam', 'Judaism', 'Hinduism', 'Paganism', 'Sikhism', 'Spirituality'],
				'Topical/Science': ['Science', 'Archaeology', 'Astronomy', 'Behavioural science', 'Economics', 'Environment', 'Geography', 'Mathematics', 'Medicine', 'Organisms', 'Social science', 'Transportation'],
				'Topical/Sports': ['Sports', 'American football', 'Baseball', 'Basketball', 'Bodybuilding', 'Boxing', 'Cricket', 'Cycling', 'Football', 'Golf', 'Horse racing', 'Ice hockey', 'Rugby union', 'Softball', 'Martial arts', 'Wrestling'],
				'Topical/Technology': ['Technology', 'Aviation', 'Computing', 'Firearms', 'Internet', 'Software', 'Websites'],
				'Wikipedia page type': ['Disambiguations', 'Lists'],
				'Geographic/Africa': ['Africa', 'Egypt', 'Ethiopia', 'Ghana', 'Kenya', 'Laos', 'Mauritius', 'Morocco', 'Nigeria', 'Somalia', 'South Africa', 'Zimbabwe'],
				'Geographic/Asia': ['Asia', 'Afghanistan', 'Bangladesh', 'Bahrain', 'Brunei', 'Cambodia', 'China', 'Hong Kong', 'India', 'Indonesia', 'Japan', 'Korea', 'Malaysia', 'Maldives', 'Mongolia', 'Myanmar', 'Nepal', 'Pakistan', 'Philippines', 'Singapore', 'South Korea', 'Sri Lanka', 'Taiwan', 'Thailand', 'Vietnam'],
				'Geographic/Asia/Central Asia': ['Central Asia', 'Kazakhstan', 'Kyrgyzstan', 'Tajikistan', 'Turkmenistan', 'Uzbekistan'],
				'Geographic/Asia/Middle East': ['Middle East', 'Iran', 'Iraq', 'Israel', 'Jordan', 'Kuwait', 'Lebanon', 'Libya', 'Palestine', 'Saudi Arabia', 'Syria', 'United Arab Emirates', 'Yemen', 'Qatar'],
				'Geographic/Europe': ['Europe', 'Albania', 'Armenia', 'Austria', 'Azerbaijan', 'Belarus', 'Belgium', 'Bosnia and Herzegovina', 'Bulgaria', 'Croatia', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland', 'France', 'Georgia (country)', 'Germany', 'Greece', 'Hungary', 'Iceland', 'Ireland', 'Italy', 'Jersey', 'Kosovo', 'Latvia', 'Lithuania', 'Luxembourg', 'Macedonia', 'Malta', 'Moldova', 'Montenegro', 'Netherlands', 'Norway', 'Poland', 'Portugal', 'Romania', 'Russia', 'Serbia', 'Slovakia', 'Slovenia', 'Spain', 'Sweden', 'Switzerland', 'Turkey', 'Ukraine', 'Yugoslavia'],
				'Geographic/Europe/United Kingdom': ['United Kingdom', 'England', 'Northern Ireland', 'Scotland', 'Wales'],
				'Geographic/Oceania': ['Oceania', 'Antarctica', 'Australia', 'New Zealand'],
				'Geographic/Americas/Canada': ['Canada', 'British Columbia', 'Manitoba', 'Nova Scotia', 'Ontario', 'Quebec', 'Alberta'],
				'Geographic/Americas/Latin America': ['Latin America', 'Caribbean', 'South America', 'Argentina', 'Barbados', 'Belize', 'Bolivia', 'Brazil', 'Chile', 'Colombia', 'Cuba', 'Ecuador', 'El Salvador', 'Guatemala', 'Haiti', 'Mexico', 'Nicaragua', 'Panama', 'Paraguay', 'Peru', 'Puerto Rico', 'Trinidad and Tobago', 'Uruguay', 'Venezuela', 'Grenada'],
				'Geographic/Americas/USA': ['United States of America', 'Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia (U.S. state)', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'Washington, D.C.', 'West Virginia', 'Wisconsin', 'Wyoming'],
				'Geographic/Unsorted': ['Islands']

			var delsort = work_area.append({
				type: 'select',
				multiple: true,
				name: 'delsortCats',
				label: 'Choose deletion sorting categories: ',
				tooltip: 'Select a few categories that are specifically relevant to the subject of the article. Be as precise as possible; categories like People and USA should only be used when no other categories apply.'

			$.each(delsortCategories, function {
				var group = delsort.append;
				list.forEach(function(item) {
					group.append;

			appendReasonBox;
			work_area = work_area.render;
			old_area.parentNode.replaceChild;

			$(work_area).find('[name=delsortCats]')
				.attr
				.select2({
					width: '100%',
					matcher: Morebits.select2.matcher,
					templateResult: Morebits.select2.highlightSearchMatches,
					language: {
						searching: Morebits.select2.queryInterceptor
					// Link text to the page itself
					templateSelection: function(choice) {
						return $('').text(choice.text).attr({
							href: mw.util.getUrl('Wikipedia:WikiProject_Deletion_sorting/' + choice.text),
							target: '_blank'

			mw.util.addCSS(
				// Remove black border
				'.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' +

				// Reduce padding
				'.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' +
				'.select2-results .select2-results__group { padding-top: 1px; padding-bottom: 1px; } ' +

				// Adjust font size
				'.select2-container .select2-dropdown .select2-results { font-size: 13px; }' +
				'.select2-container .selection .select2-selection__rendered { font-size: 13px; }' +

				// Make the tiny cross larger
				'.select2-selection__choice__remove { font-size: 130%; }'
			);
			break;
		case 'tfd':
			work_area = new Morebits.quickForm.element({
				type: 'field',
				label: 'Templates for discussion',
				name: 'work_area'
			work_area.append({
				type: 'div',
				label: 'Userboxes are not eligible for TfD; they go to MfD.'
			var templateOrModule = mw.config.get('wgPageContentModel') === 'Scribunto' ? 'module' : 'template';
			var tfd_category = work_area.append({
				type: 'select',
				label: 'Choose type of action wanted: ',
				name: 'xfdcat',
				event: function(e) {
					var target = e.target;
					// add/remove extra input box
					if (target.value === 'tfm' && !target.form.xfdtarget) { // $(target.parentNode).find("input[name='xfdtarget']").length === 0 ) {
						var xfdtarget = new Morebits.quickForm.element({
							name: 'xfdtarget',
							type: 'input',
							label: 'Other ' + templateOrModule + ' to be merged: ',
							tooltip: 'Required. Should not include the ' + Morebits.string.toUpperCaseFirstChar(templateOrModule) + ': namespace prefix.',
							required: true
						target.parentNode.appendChild(xfdtarget.render);
						$(Morebits.quickForm.getElementContainer(target.form.xfdtarget)).remove;
						target.form.xfdtarget = null;
						// $(target.parentNode).find("input[name='xfdtarget']").remove;
			tfd_category.append;
			tfd_category.append;

			var tfd_template_type = work_area.append({
				type: 'select',
				name: 'templatetype',
				label: 'Deletion tag display style: ',
				tooltip: 'Which  parameter to pass to the TfD tag template.'
			if (templateOrModule === 'module') {
				tfd_template_type.append;
				tfd_template_type.append;
				tfd_template_type.append({ type: 'option', value: 'sidebar', label: 'Sidebar/infobox', selected: $('.infobox').length });
				tfd_template_type.append;
				tfd_template_type.append;

			work_area.append({
				type: 'checkbox',
				list: [
						label: 'Wrap deletion tag with  (for substituted templates only)',
						value: 'noinclude',
						name: 'noinclude',
						tooltip: 'Will wrap the deletion tag in &lt;noinclude&gt; tags, so that it won\'t get substituted along with the template.',
						disabled: templateOrModule === 'module',
						checked: !!$('.box-Subst_only').length // Default to checked if page carries 
				]

			appendReasonBox;
			work_area = work_area.render;
			old_area.parentNode.replaceChild;
			break;
		case 'mfd':
			work_area = new Morebits.quickForm.element({
				type: 'field',
				label: 'Miscellany for deletion',
				name: 'work_area'
			work_area.append({
				type: 'checkbox',
				list: [
						label: 'Wrap deletion tag with ',
						value: 'noinclude',
						name: 'noinclude',
						tooltip: 'Will wrap the deletion tag in &lt;noinclude&gt; tags, so that it won\'t transclude. Select this option for userboxes.'
				]
			if ((mw.config.get('wgNamespaceNumber') === 2 /* User: */ || mw.config.get('wgNamespaceNumber') === 3 /* User talk: */) && mw.config.exists('wgRelevantUserName')) {
				work_area.append({
					type: 'checkbox',
					list: [
							label: 'Also notify owner of userspace if they are not the page creator',
							value: 'notifyuserspace',
							name: 'notifyuserspace',
							tooltip: 'If the user in whose userspace this page is located is not the page creator , notify the userspace owner as well.',
							checked: true
					]
			appendReasonBox;
			work_area = work_area.render;
			old_area.parentNode.replaceChild;
			break;
		case 'ffd':
			work_area = new Morebits.quickForm.element({
				type: 'field',
				label: 'Discussion venues for files',
				name: 'work_area'
			appendReasonBox;
			work_area = work_area.render;
			old_area.parentNode.replaceChild;
			break;
		case 'cfd':
			work_area = new Morebits.quickForm.element({
				type: 'field',
				label: 'Categories for discussion',
				name: 'work_area'
			var isCategory = mw.config.get('wgNamespaceNumber') === 14;
			var cfd_category;
			cfd_category = work_area.append({
				type: 'select',
				label: 'Choose type of action wanted: ',
				name: 'xfdcat',
				event: function(e) {
					var value = e.target.value;
					var target = e.target.form.xfdtarget;
					// update enabled status
					if (value === 'cfd' || value === 'sfd-t') {
						target.disabled = true;
						target.required = false;
						target.disabled = false;
						target.required = true;
					if (isCategory) {
						// update label
						if (value === 'cfs') {
							target.previousSibling.textContent = 'Target categories: ';
							target.previousSibling.textContent = 'Target article: ';
							target.previousSibling.textContent = 'Target category: ';
						// add/remove extra input box
						if (value === 'cfs' && $(target.parentNode).find("input[name='xfdtarget2']").length === 0) {
							var xfdtarget2 = document.createElement('input');
							xfdtarget2.setAttribute;
							xfdtarget2.setAttribute;
							xfdtarget2.setAttribute;
							target.parentNode.appendChild(xfdtarget2);
							$(target.parentNode).find("input[name='xfdtarget2']").remove;
						target.previousSibling.textContent = 'Target stub template: ';

			if (isCategory) {
				cfd_category.append;
				cfd_category.append;
				cfd_category.append;
				cfd_category.append;
				cfd_category.append;
				cfd_category.append;
				cfd_category.append;

			work_area.append({
				type: 'input',
				name: 'xfdtarget',
				label: 'Target category: ', // default, changed above
				disabled: true,
				value: ''
			appendReasonBox;
			work_area = work_area.render;
			old_area.parentNode.replaceChild;
			break;
		case 'cfds':
			work_area = new Morebits.quickForm.element({
				type: 'field',
				label: 'Categories for speedy renaming',
				name: 'work_area'
			var cfds_category = work_area.append({
				type: 'select',
				label: 'C2 sub-criterion: ',
				name: 'xfdcat',
				tooltip: 'See WP:CFDS for full explanations.'
			cfds_category.append;
			cfds_category.append;
			cfds_category.append;
			cfds_category.append;
			cfds_category.append;
			cfds_category.append;

			work_area.append({
				type: 'input',
				name: 'xfdtarget',
				label: 'New name: ',
				value: '',
				required: true
			appendReasonBox;
			work_area = work_area.render;
			old_area.parentNode.replaceChild;
			break;
		case 'rfd':
			work_area = new Morebits.quickForm.element({
				type: 'field',
				label: 'Redirects for discussion',
				name: 'work_area'

			work_area.append({
				type: 'checkbox',
				list: [
						label: 'Notify target page if possible',
						value: 'relatedpage',
						name: 'relatedpage',
						tooltip: "A notification template will be placed on the talk page of this redirect's target if this is true.",
						checked: true
				]
			appendReasonBox;
			work_area = work_area.render;
			old_area.parentNode.replaceChild;
			break;

		case 'rm':
			work_area = new Morebits.quickForm.element({
				type: 'field',
				label: 'Requested moves',
				name: 'work_area'
			work_area.append({
				type: 'checkbox',
				list: [
						label: 'Uncontroversial technical request',
						value: 'rmtr',
						name: 'rmtr',
						tooltip: 'Use this option when you are unable to perform this uncontroversial move yourself because of a technical reason ',
						checked: false,
						event: function {
							form.newname.required = this.checked;
				]
			work_area.append({
				type: 'input',
				name: 'newname',
				label: 'New title: ',
				tooltip: 'Required for technical requests. Otherwise, if unsure of the appropriate title, you may leave it blank.'

			appendReasonBox;
			work_area = work_area.render;
			old_area.parentNode.replaceChild;
			break;

		default:
			work_area = new Morebits.quickForm.element({
				type: 'field',
				label: 'Nothing for anything',
				name: 'work_area'
			work_area = work_area.render;
			old_area.parentNode.replaceChild;
			break;

	// Return to checked state when switching, but no creator notification for CFDS or RM
	if (value === 'cfds' || value === 'rm') {
		form.notifycreator.checked = false;
		form.notifycreator.disabled = true;
		form.notifycreator.checked = true;
		form.notifycreator.disabled = false;

Twinkle.xfd.setWatchPref = function twinklexfdsetWatchPref {
	switch (pref) {
		case 'yes':
			pageobj.setWatchlist(true);
			break;
		case 'no':
			pageobj.setWatchlistFromPreferences(false);
			break;
		default:
			pageobj.setWatchlistFromPreferences(true);
			break;

Twinkle.xfd.callbacks = {
	getDiscussionWikitext: function {
		if (venue === 'cfds') { // CfD/S takes a completely different style
			return '* :' + Morebits.pageNameNorm + ' to :' + params.target + '\u00A0\u2013 ' +
				params.xfdcat + (params.reason ? ': ' + Morebits.string.formatReasonText(params.reason) : '.') + ' ~~~~';
			// U+00A0 NO-BREAK SPACE; U+2013 EN RULE
		if (venue === 'rm') {
			// even if invoked from talk page, propose the subject page for move
			var pageName = new mw.Title(Morebits.pageNameNorm).getSubjectPage.toText;
			return (params.rmtr ?
				'';

		var text = '';

		if (params.delsortCats) { // Only for AFDs
			params.delsortCats.forEach(function (cat) {
				text += '\n';

		return text;
	showPreview: function {
		var templatetext = Twinkle.xfd.callbacks.getDiscussionWikitext;
		if (venue === 'rm') { // RM templates are sensitive to page title
			form.previewer.beginRender(templatetext, params.rmtr ? 'Wikipedia:Requested moves/Technical requests' : mw.Title.newFromText(Morebits.pageNameNorm).getTalkPage.toText);
			form.previewer.beginRender; // Force wikitext
	preview: function(form) {
		// category, reason, xfdcat, xfdtarget, xfdtarget2, delsortCats, newname
		var params = Morebits.quickForm.getInputData(form);

		var venue = params.category;
		// Remove CfD or TfD namespace prefixes
		if (venue === 'tfd' || venue === 'cfd') {
			var namespace_re = new RegExp('^:?' + mw.config.get('wgCanonicalNamespace') + ':', 'i');
			if (params.xfdtarget) { // CfD or TfD
				params.target = Morebits.string.toUpperCaseFirstChar(params.xfdtarget.replace);
			if (params.xfdtarget2) { // CfD
				params.target2 = Morebits.string.toUpperCaseFirstChar(params.xfdtarget2.replace);
			params.target = /^Category:/.test(params.xfdtarget) ? params.xfdtarget : 'Category:' + params.xfdtarget;

		if (venue === 'ffd') {
			// Fetch the uploader
			var page = new Morebits.wiki.page(mw.config.get('wgPageName'));
			page.lookupCreation(function {
				params.uploader = page.getCreator;
				Twinkle.xfd.callbacks.showPreview;
			Twinkle.xfd.callbacks.rfd.findTarget(params, function(params) {
				Twinkle.xfd.callbacks.showPreview;
			Twinkle.xfd.callbacks.showPreview;
			Twinkle.xfd.callbacks.showPreview;
	addToLog: function {
		var usl = new Morebits.userspaceLogger(Twinkle.getPref('xfdLogPageName'));// , 'Adding entry to userspace log');

		usl.initialText =
			"This is a log of all deletion discussion nominations made by this user using Twinkle's XfD module.\n\n" +
			'If you no longer wish to keep this log, you can turn it off using the preferences panel, and ' +
			'nominate this page for speedy deletion under CSD U1.' +
			(Morebits.userIsSysop ? '\n\nThis log does not track XfD-related deletions made using Twinkle.' : '');

		var editsummary = 'Logging ' + params.venue + ' nomination of :' + Morebits.pageNameNorm + '.';
		// Provide Wikipedian TLA style: AfD, RfD, CfDS, RM, SfD, etc.
		var toTLACase = function(str) {
			// return str.toString.toUpperCase.replace;
			return str.toString.toUpperCase.replace(/(.)F(.)(?:-.)?/, '$1f$2');
		// If a logged file is deleted but exists on commons, the wikilink will be blue, so provide a link to the log
		var fileLogLink = mw.config.get('wgNamespaceNumber') === 6 ? ' ([ log])' : '';

		var appendText = '# :' + Morebits.pageNameNorm + '' + fileLogLink + ' nominated at ' + toTLACase(params.venue) + '';
		var extraInfo = '';

		switch (params.venue) {
			case 'tfd':
				if (params.xfdcat === 'tfm') {
					appendText += ' (merge)';
					if (params.target) {
						var contentModel = mw.config.get('wgPageContentModel') === 'Scribunto' ? 'Module:' : 'Template:';
						extraInfo += '; Other ' + contentModel.toLowerCase + ' module):/i.test(params.target)) {
							extraInfo += contentModel;
						extraInfo += params.target + '';
				break;
			case 'mfd':
				if (initialContrib && params.notifyuserspace && params.userspaceOwner !== initialContrib) {
					extraInfo += ' and ';
				break;
			case 'cfd':
				appendText += ' (' + toTLACase(params.xfdcat) + ')';
				if (params.target) {
					var categoryOrTemplate = params.xfdcat.charAt(0) === 's' ? 'Template:' : ':Category:';
					extraInfo += '; ' + params.action + ' to: ' + categoryOrTemplate + params.target + '';
					if (params.xfdcat === 'cfs' && params.target2) {
						extraInfo += ', ' + categoryOrTemplate + params.target2 + '';
				break;
			case 'cfds':
				appendText += ' (' + toTLACase(params.xfdcat) + ')';
				// Ensure there's more than just 'Category:'
				if (params.target && params.target.length > 9) {
					extraInfo += '; New name: :' + params.target + '';
				break;
			case 'rfd':
				if (params.target) {
					extraInfo += '; Target: :' + params.target + '';
					if (params.relatedpage) {
						extraInfo += ' (notified)';
				break;
			case 'rm':
				if (params.rmtr) {
					appendText += ' (technical)';
				if (params.newname) {
					extraInfo += '; New name: :' + params.newname + '';
				break;
			case 'ffd':
			case 'afd':
			default:
				break;

		if (initialContrib) {
			appendText += '; notified ';
		if (extraInfo) {
			appendText += extraInfo;
		appendText += ' ~~~~~';
		if (params.reason) {
			appendText += "\n#* **Reason**: " + params.reason;

		usl.log(appendText, editsummary + Twinkle.getPref('summaryAd'));

	afd: {
		main: function(apiobj) {
			var xmlDoc = apiobj.responseXML;
			var titles = $(xmlDoc).find('allpages p');

			// There has been no earlier entries with this prefix, just go on.
			if (titles.length <= 0) {
				apiobj.params.numbering = apiobj.params.number = '';
				var number = 0;
				for (var i = 0; i < titles.length; ++i) {
					var title = titles[i].getAttribute('title');

					// First, simple test, is there an instance with this exact name?
					if (title === 'Wikipedia:Articles for deletion/' + Morebits.pageNameNorm) {
						number = Math.max;
						continue;

					var order_re = new RegExp('^' +
						RegExp.escape +
						'\\s*\\(\\s*(\\d+)(?:(?:th|nd|rd|st) nom(?:ination)?)?\\s*\\)\\s*$');
					var match = order_re.exec(title);

					// No match; A non-good value
					if (!match) {
						continue;

					// A match, set number to the max of current
					number = Math.max(number, Number(match[1]));
				apiobj.params.number = Twinkle.xfd.num2order(parseInt + 1);
				apiobj.params.numbering = number > 0 ? ' (' + apiobj.params.number + ' nomination)' : '';
			apiobj.params.discussionpage = 'Wikipedia:Articles for deletion/' + Morebits.pageNameNorm + apiobj.params.numbering;

			Morebits.status.info;

			// Updating data for the action completed event
			Morebits.wiki.actionCompleted.redirect = apiobj.params.discussionpage;
			Morebits.wiki.actionCompleted.notice = 'Nomination completed, now redirecting to the discussion page';

			// Tagging article
			var wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), 'Adding deletion tag to article');
			wikipedia_page.setFollowRedirect(true); // should never be needed, but if the article is moved, we would want to follow the redirect
			wikipedia_page.setCallbackParameters(apiobj.params);
			wikipedia_page.load(Twinkle.xfd.callbacks.afd.taggingArticle);
		// Tagging needs to happen before everything else: this means we can check if there is an AfD tag already on the page
		taggingArticle: function(pageobj) {
			var text = pageobj.getPageText;
			var params = pageobj.getCallbackParameters;
			var statelem = pageobj.getStatusElement;

			if (!pageobj.exists) {
				statelem.error("It seems that the page doesn't exist; perhaps it has already been deleted");
				return;

			// Check for existing AfD tag, for the benefit of new page patrollers
			var textNoAfd = text.replace(/<!--.*AfD.*\n\{\{(?:Article for deletion\/dated|AfDM).*\}\}\n<!--.*(?:\n<!--.*)?AfD.*(?:\s*\n)?/g, '');
			if (text !== textNoAfd) {
				if (confirm('An AfD tag was found on this article. Maybe someone beat you to it. \nClick OK to replace the current AfD tag (not recommended), or Cancel to abandon your nomination.')) {
					text = textNoAfd;
					statelem.error;
					window.location.reload;
					return;

			// Now we know we want to go ahead with it, trigger the other AJAX requests

			// Mark the page as curated/patrolled, if wanted
			if (Twinkle.getPref('markXfdPagesAsPatrolled')) {
				pageobj.triage;

			// Starting discussion page
			var wikipedia_page = new Morebits.wiki.page;
			wikipedia_page.setCallbackParameters(params);
			wikipedia_page.load(Twinkle.xfd.callbacks.afd.discussionPage);

			// Today's list
			var date = new Morebits.date(pageobj.getLoadTime);
			wikipedia_page = new Morebits.wiki.page('Wikipedia:Articles for deletion/Log/' +
				date.format, "Adding discussion to today's list");
			wikipedia_page.setFollowRedirect(true);
			wikipedia_page.setCallbackParameters(params);
			wikipedia_page.load(Twinkle.xfd.callbacks.afd.todaysList);
			// Notification to first contributor
			if (params.notifycreator) {
				var thispage = new Morebits.wiki.page(mw.config.get('wgPageName'));
				thispage.setCallbackParameters(params);
				thispage.setLookupNonRedirectCreator(true); // Look for author of first non-redirect revision
				thispage.lookupCreation(Twinkle.xfd.callbacks.afd.userNotification);
			// or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name
				Twinkle.xfd.callbacks.addToLog;

			// List at deletion sorting pages
			if (params.delsortCats) {
				params.delsortCats.forEach(function (cat) {
					var delsortPage = new Morebits.wiki.page;
					delsortPage.setFollowRedirect(true); // In case a category gets renamed
					delsortPage.setCallbackParameters({discussionPage: params.discussionpage});
					delsortPage.load(Twinkle.xfd.callbacks.afd.delsortListing);

			// Remove some tags that should always be removed on AfD.
			text = text.replace(/\{\{\s*(dated prod|dated prod blp|Prod blp\/dated|Proposed deletion\/dated|prod2|Proposed deletion endorsed|Userspace draft)\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\}\s*/ig, '');
			// Then, test if there are speedy deletion-related templates on the article.
			var textNoSd = text.replace(/\{\{\s*(db(-\w*)?|delete|(?:hang|hold)[- ]?on)\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\}\s*/ig, '');
			if (text !== textNoSd && confirm('A speedy deletion tag was found on this page. Should it be removed?')) {
				text = textNoSd;

			var tag = (params.noinclude ? '\n' : '}}\n');

			// Insert tag after short description or any hatnotes
			var wikipage = new Morebits.wikitext.page(text);
			text = wikipage.insertAfterTemplates.getText;

			pageobj.setPageText(text);
			pageobj.setEditSummary('Nominated for deletion; see :' + params.discussionpage + '.' + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchPage'));
			pageobj.setCreateOption('nocreate');
			pageobj.save;
		discussionPage: function(pageobj) {
			var params = pageobj.getCallbackParameters;

			pageobj.setPageText(Twinkle.xfd.callbacks.getDiscussionWikitext);
			pageobj.setEditSummary('Creating deletion discussion page for :' + Morebits.pageNameNorm + '.' + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchDiscussion'));
			pageobj.setCreateOption('createonly');
			pageobj.save(function {
				Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki
		todaysList: function(pageobj) {
			var old_text = pageobj.getPageText + '\n'; // MW strips trailing blanks, but we like them, so we add a fake one
			var params = pageobj.getCallbackParameters;
			var statelem = pageobj.getStatusElement;

			var text = old_text.replace(/(\n+)/, '$1\n');
			if (text === old_text) {
				var linknode = document.createElement('a');
				linknode.setAttribute('href', mw.util.getUrl('Wikipedia:Twinkle/Fixing AFD') + '?action=purge');
				linknode.appendChild(document.createTextNode('How to fix AFD'));
				statelem.error;
				return;
			pageobj.setPageText(text);
			pageobj.setEditSummary('Adding :' + params.discussionpage + '.' + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchList'));
			pageobj.setCreateOption('recreate');
			pageobj.save;
		userNotification: function(pageobj) {
			var params = pageobj.getCallbackParameters;
			var initialContrib = pageobj.getCreator;

			// Disallow warning yourself
			if (initialContrib === mw.config.get('wgUserName')) {
				pageobj.getStatusElement.warn('You (' + initialContrib + ') created this page; skipping user notification');
				return;

			var usertalkpage = new Morebits.wiki.page('User talk:' + initialContrib, 'Notifying initial contributor (' + initialContrib + ')');
			var notifytext = '\n ~~~~';
			usertalkpage.setAppendText(notifytext);
			usertalkpage.setEditSummary('Notification: nomination of :' + Morebits.pageNameNorm + ' at articles for deletion.' + Twinkle.getPref('summaryAd'));
			usertalkpage.setCreateOption('recreate');
			Twinkle.xfd.setWatchPref(usertalkpage, Twinkle.getPref('xfdWatchUser'));
			usertalkpage.setFollowRedirect;
			usertalkpage.append(function onNotifySuccess {
				// add this nomination to the user's userspace log, if the user has enabled it
				if (params.lognomination) {
					Twinkle.xfd.callbacks.addToLog;
				// if user could not be notified, log nomination without mentioning that notification was sent
				if (params.lognomination) {
					Twinkle.xfd.callbacks.addToLog;
		delsortListing: function(pageobj) {
			var discussionPage = pageobj.getCallbackParameters.discussionPage;
			var text = pageobj.getPageText.replace;
			pageobj.setPageText(text);
			pageobj.setEditSummary('Listing :' + discussionPage + '.' + Twinkle.getPref('summaryAd'));
			pageobj.setCreateOption('nocreate');
			pageobj.save;

	tfd: {
		taggingTemplate: function(pageobj) {
			var text = pageobj.getPageText;
			var params = pageobj.getCallbackParameters;
			var tableNewline = params.templatetype === 'standard' || params.templatetype === 'sidebar' ? '\n' : ''; // No newline for inline

			pageobj.setPageText((params.noinclude ? '' : '}}') + tableNewline + text);
			pageobj.setEditSummary('Nominated for deletion; see :' + params.discussionpage + '.' + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchPage'));
			if (params.scribunto) {
				pageobj.setCreateOption('recreate'); // Module /doc might not exist
			pageobj.save;
		taggingTemplateForMerge: function(pageobj) {
			var text = pageobj.getPageText;
			var params = pageobj.getCallbackParameters;
			var tableNewline = params.templatetype === 'standard' || params.templatetype === 'sidebar' ? '\n' : ''; // No newline for inline

			pageobj.setPageText((params.noinclude ? '' : '}}') + tableNewline + text);
			pageobj.setEditSummary('Listed for merging with :' + params.otherTemplateName + '; see :' + params.discussionpage + '.' + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchPage'));
			if (params.scribunto) {
				pageobj.setCreateOption('recreate'); // Module /doc might not exist
			pageobj.save;
		todaysList: function(pageobj) {
			var old_text = pageobj.getPageText;
			var params = pageobj.getCallbackParameters;
			var statelem = pageobj.getStatusElement;

			var added_data = Twinkle.xfd.callbacks.getDiscussionWikitext;

			var text = old_text.replace;
			if (text === old_text) {
				statelem.error('failed to find target spot for the discussion');
				return;
			pageobj.setPageText(text);
			pageobj.setEditSummary('Adding ' + (params.xfdcat === 'tfd' ? 'deletion nomination' : 'merge listing') + ' of :' + Morebits.pageNameNorm + '.' + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchDiscussion'));
			pageobj.setCreateOption('recreate');
			pageobj.save(function {
				Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki
		userNotification: function(pageobj) {
			var initialContrib = pageobj.getCreator;
			var params = pageobj.getCallbackParameters;

			// Disallow warning yourself
			if (initialContrib === mw.config.get('wgUserName')) {
				pageobj.getStatusElement.warn('You (' + initialContrib + ') created this page; skipping user notification');
				return;

			var usertalkpage = new Morebits.wiki.page('User talk:' + initialContrib, 'Notifying initial contributor (' + initialContrib + ')');
			var notifytext = '\n';
			var modNotice = mw.config.get('wgPageContentModel') === 'Scribunto' ? '|module=yes' : '';
			switch (params.xfdcat) {
				case 'tfd':
					notifytext += ' ~~~~';
					break;
				case 'tfm':
					notifytext += ' ~~~~';
					break;
				default:
					alert('twinklexfd in userNotification: unknown TFD action');
					break;

			usertalkpage.setAppendText(notifytext);
			usertalkpage.setEditSummary('Notification: listing of :' + pageobj.getPageName + ' at templates for discussion.' + Twinkle.getPref('summaryAd'));
			usertalkpage.setCreateOption('recreate');
			Twinkle.xfd.setWatchPref(usertalkpage, Twinkle.getPref('xfdWatchUser'));
			usertalkpage.setFollowRedirect;

			// Add this nomination to user's userspace log, if the user has enabled it
			// and it isn't the second template in a TfM nomination
			if ((params.xfdcat === 'tfd' || pageobj.getPageName === Morebits.pageNameNorm) && params.lognomination) {
				usertalkpage.append(function onNotifySuccess {
					Twinkle.xfd.callbacks.addToLog;
					// if user could not be notified, log without mentioning notification
					Twinkle.xfd.callbacks.addToLog;
				usertalkpage.append;

	mfd: {
		main: function(apiobj) {
			var xmlDoc = apiobj.responseXML;
			var titles = $(xmlDoc).find('allpages p');

			// There has been no earlier entries with this prefix, just go on.
			if (titles.length <= 0) {
				apiobj.params.numbering = apiobj.params.number = '';
				var number = 0;
				for (var i = 0; i < titles.length; ++i) {
					var title = titles[i].getAttribute('title');

					// First, simple test, is there an instance with this exact name?
					if (title === 'Wikipedia:Miscellany for deletion/' + Morebits.pageNameNorm) {
						number = Math.max;
						continue;

					var order_re = new RegExp('^' +
							RegExp.escape +
							'\\s*\\(\\s*(\\d+)(?:(?:th|nd|rd|st) nom(?:ination)?)?\\s*\\)\\s*$');
					var match = order_re.exec(title);

					// No match; A non-good value
					if (!match) {
						continue;

					// A match, set number to the max of current
					number = Math.max(number, Number(match[1]));
				apiobj.params.number = Twinkle.xfd.num2order(parseInt + 1);
				apiobj.params.numbering = number > 0 ? ' (' + apiobj.params.number + ' nomination)' : '';
			apiobj.params.discussionpage = 'Wikipedia:Miscellany for deletion/' + Morebits.pageNameNorm + apiobj.params.numbering;

			apiobj.statelem.info('next in order is ' + apiobj.params.discussionpage + '');

			// Tagging page
			var wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), 'Tagging page with deletion tag');
			wikipedia_page.setFollowRedirect(true); // should never be needed, but if the page is moved, we would want to follow the redirect
			wikipedia_page.setCallbackParameters(apiobj.params);
			wikipedia_page.load(Twinkle.xfd.callbacks.mfd.taggingPage);

			// Updating data for the action completed event
			Morebits.wiki.actionCompleted.redirect = apiobj.params.discussionpage;
			Morebits.wiki.actionCompleted.notice = 'Nomination completed, now redirecting to the discussion page';

			// Discussion page
			wikipedia_page = new Morebits.wiki.page;
			wikipedia_page.setCallbackParameters(apiobj.params);
			wikipedia_page.load(Twinkle.xfd.callbacks.mfd.discussionPage);

			// Today's list
			wikipedia_page = new Morebits.wiki.page;
			wikipedia_page.setPageSection(2);
			wikipedia_page.setFollowRedirect(true);
			wikipedia_page.setCallbackParameters(apiobj.params);
			wikipedia_page.load(Twinkle.xfd.callbacks.mfd.todaysList);

			// Notification to first contributor, and notification to owner of userspace (if applicable and required)
			if (apiobj.params.notifycreator) {
				var thispage = new Morebits.wiki.page(mw.config.get('wgPageName'));
				thispage.setCallbackParameters(apiobj.params);
				thispage.lookupCreation(Twinkle.xfd.callbacks.mfd.userNotification);
			// or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name
				Twinkle.xfd.callbacks.addToLog;
		taggingPage: function(pageobj) {
			var text = pageobj.getPageText;
			var params = pageobj.getCallbackParameters;

			pageobj.setPageText((params.noinclude ? '' : '') + text);
			pageobj.setEditSummary('Nominated for deletion; see :' + params.discussionpage + '.' + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchPage'));
			pageobj.setCreateOption('nocreate');
			pageobj.save;
		discussionPage: function(pageobj) {
			var params = pageobj.getCallbackParameters;

			pageobj.setPageText(Twinkle.xfd.callbacks.getDiscussionWikitext);
			pageobj.setEditSummary('Creating deletion discussion page for :' + Morebits.pageNameNorm + '.' + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchDiscussion'));
			pageobj.setCreateOption('createonly');
			pageobj.save(function {
				Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki
		todaysList: function(pageobj) {
			var text = pageobj.getPageText;
			var params = pageobj.getCallbackParameters;
			var statelem = pageobj.getStatusElement;

			var date = new Morebits.date(pageobj.getLoadTime);
			var date_header = date.format;
			var date_header_regex = new RegExp(date.format('', 'utc'));
			var new_data = '';

			if (date_header_regex.test(text)) { // we have a section already
				statelem.info;
				text = text.replace;
				statelem.info;
				text = text.replace;

			pageobj.setPageText(text);
			pageobj.setEditSummary('Adding :' + params.discussionpage + '.' + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchList'));
			pageobj.setCreateOption('recreate');
			pageobj.save;
		userNotification: function(pageobj) {
			var initialContrib = pageobj.getCreator;
			var params = pageobj.getCallbackParameters;

			// Also notify the user who owns the subpage if they are not the creator
			params.userspaceOwner = mw.config.get('wgRelevantUserName');
			if (params.notifyuserspace && params.userspaceOwner !== initialContrib) {
				Twinkle.xfd.callbacks.mfd.userNotificationMain;

			// Disallow warning yourself
			if (initialContrib === mw.config.get('wgUserName')) {
				pageobj.getStatusElement.warn('You (' + initialContrib + ') created this page; skipping user notification');
				// Used to ensure we only add to the userspace
				// log once, after notifying the initial creator
				params.initialContrib = initialContrib;
				// Really notify the creator
				Twinkle.xfd.callbacks.mfd.userNotificationMain;
		userNotificationMain: function {
			var usertalkpage = new Morebits.wiki.page('User talk:' + userTarget, actionName + ' (' + userTarget + ')');
			var notifytext = '\n ~~~~';
			usertalkpage.setAppendText(notifytext);
			usertalkpage.setEditSummary('Notification: nomination of :' + Morebits.pageNameNorm + ' at miscellany for deletion.' + Twinkle.getPref('summaryAd'));
			usertalkpage.setCreateOption('recreate');
			Twinkle.xfd.setWatchPref(usertalkpage, Twinkle.getPref('xfdWatchUser'));
			usertalkpage.setFollowRedirect;
			// Only log once, using the initial creator's notification as our barometer
			if (params.initialContrib === userTarget && params.lognomination) {
				usertalkpage.append(function onNotifySuccess {
					Twinkle.xfd.callbacks.addToLog;
					// if user could not be notified, log without mentioning notification
					Twinkle.xfd.callbacks.addToLog;
				usertalkpage.append;


	ffd: {
		main: function(pageobj) {
			// this is coming in from lookupCreation...!
			var params = pageobj.getCallbackParameters;
			var initialContrib = pageobj.getCreator;
			params.uploader = initialContrib;

			// Adding discussion
			var wikipedia_page = new Morebits.wiki.page;
			wikipedia_page.setFollowRedirect(true);
			wikipedia_page.setCallbackParameters(params);
			wikipedia_page.load(Twinkle.xfd.callbacks.ffd.todaysList);

			// Notification to first contributor
			if (params.notifycreator) {
				// Disallow warning yourself
				if (initialContrib === mw.config.get('wgUserName')) {
					pageobj.getStatusElement.warn('You (' + initialContrib + ') created this page; skipping user notification');
					var usertalkpage = new Morebits.wiki.page('User talk:' + initialContrib, 'Notifying initial contributor (' + initialContrib + ')');
					var notifytext = '\n';
					usertalkpage.setAppendText(notifytext);
					usertalkpage.setEditSummary('Notification: listing of :' + Morebits.pageNameNorm + ' at files for discussion.' + Twinkle.getPref('summaryAd'));
					usertalkpage.setCreateOption('recreate');
					Twinkle.xfd.setWatchPref(usertalkpage, Twinkle.getPref('xfdWatchUser'));
					usertalkpage.setFollowRedirect;
					usertalkpage.append(function onNotifySuccess {
						// add this nomination to the user's userspace log, if the user has enabled it
						if (params.lognomination) {
							Twinkle.xfd.callbacks.addToLog;
						// if user could not be notified, log nomination without mentioning that notification was sent
						if (params.lognomination) {
							Twinkle.xfd.callbacks.addToLog;
			// or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name
				Twinkle.xfd.callbacks.addToLog;
		taggingImage: function(pageobj) {
			var text = pageobj.getPageText;
			var params = pageobj.getCallbackParameters;

			text = text.replace(/\{\{(mtc|(copy |move )?to ?commons|move to wikimedia commons|copy to wikimedia commons)[^}]*\}\}/gi, '');

			pageobj.setPageText('\n' + text);
			pageobj.setEditSummary('Listed for discussion at :' + params.discussionpage + '.' + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchPage'));
			pageobj.setCreateOption('recreate'); // it might be possible for a file to exist without a description page
			pageobj.save;
		todaysList: function(pageobj) {
			var text = pageobj.getPageText;
			var params = pageobj.getCallbackParameters;

			// add date header if the log is found to be empty 
			if (!pageobj.exists) {
				text = '';

			pageobj.setPageText(text + '\n\n' + Twinkle.xfd.callbacks.getDiscussionWikitext);
			pageobj.setEditSummary('Adding :' + Morebits.pageNameNorm + '.' + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchDiscussion'));
			pageobj.setCreateOption('recreate');
			pageobj.save(function {
				Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki

	cfd: {
		taggingCategory: function(pageobj) {
			var text = pageobj.getPageText;
			var params = pageobj.getCallbackParameters;

			var added_data = '';
			editsummary += '; see :' + params.discussionpage + '.';

			pageobj.setPageText(added_data + '\n' + text);
			pageobj.setEditSummary(editsummary + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchPage'));
			pageobj.setCreateOption('recreate'); // since categories can be populated without an actual page at that title
			pageobj.save;
		todaysList: function(pageobj) {
			var old_text = pageobj.getPageText;
			var params = pageobj.getCallbackParameters;
			var statelem = pageobj.getStatusElement;

			var added_data = Twinkle.xfd.callbacks.getDiscussionWikitext;
			var editsummary = 'Adding ' + params.action + ' nomination of :' + Morebits.pageNameNorm + '.';

			var text = old_text.replace;
			if (text === old_text) {
				statelem.error('failed to find target spot for the discussion');
				return;

			pageobj.setPageText(text);
			pageobj.setEditSummary(editsummary + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchDiscussion'));
			pageobj.setCreateOption('recreate');
			pageobj.save(function {
				Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki
		userNotification: function(pageobj) {
			var initialContrib = pageobj.getCreator;
			var params = pageobj.getCallbackParameters;

			// Disallow warning yourself
			if (initialContrib === mw.config.get('wgUserName')) {
				pageobj.getStatusElement.warn('You (' + initialContrib + ') created this page; skipping user notification');
				return;

			var usertalkpage = new Morebits.wiki.page('User talk:' + initialContrib, 'Notifying initial contributor (' + initialContrib + ')');
			var notifytext = '\n ~~~~';
			usertalkpage.setAppendText(notifytext);
			usertalkpage.setEditSummary('Notification: listing of :' + Morebits.pageNameNorm + ' at categories for discussion.' + Twinkle.getPref('summaryAd'));
			usertalkpage.setCreateOption('recreate');
			Twinkle.xfd.setWatchPref(usertalkpage, Twinkle.getPref('xfdWatchUser'));
			usertalkpage.setFollowRedirect;
			usertalkpage.append(function onNotifySuccess {
				// add this nomination to the user's userspace log, if the user has enabled it
				if (params.lognomination) {
					Twinkle.xfd.callbacks.addToLog;
				// if user could not be notified, log nomination without mentioning that notification was sent
				if (params.lognomination) {
					Twinkle.xfd.callbacks.addToLog;

	cfds: {
		taggingCategory: function(pageobj) {
			var text = pageobj.getPageText;
			var params = pageobj.getCallbackParameters;

			pageobj.setPageText('\n' + text);
			pageobj.setEditSummary('Listed for speedy renaming; see Categories for discussion/Speedy.' + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchPage'));
			pageobj.setCreateOption('recreate'); // since categories can be populated without an actual page at that title
			pageobj.save(function {
				if (params.lognomination) {
					// No user notification for CfDS, so just add this nomination to the user's userspace log
					Twinkle.xfd.callbacks.addToLog;
		addToList: function(pageobj) {
			var old_text = pageobj.getPageText;
			var params = pageobj.getCallbackParameters;
			var statelem = pageobj.getStatusElement;

			var text = old_text.replace('BELOW THIS LINE -->', 'BELOW THIS LINE -->\n' + Twinkle.xfd.callbacks.getDiscussionWikitext);
			if (text === old_text) {
				statelem.error('failed to find target spot for the discussion');
				return;

			pageobj.setPageText(text);
			pageobj.setEditSummary('Adding :' + Morebits.pageNameNorm + '.' + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchDiscussion'));
			pageobj.setCreateOption('recreate');
			pageobj.save(function {
				Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki

	rfd: {
		// This gets called both on submit and preview to determine the redirect target
		findTarget: function {
			// Used by regular redirects to find the target, but for all redirects,
			// avoid relying on the client clock to build the log page
			var query = {
				'action': 'query',
				'curtimestamp': true
			if (document.getElementById('softredirect')) {
				// For soft redirects, define the target early
				// to skip target checks in findTargetCallback
				params.target = document.getElementById('softredirect').textContent.replace;
				// Find current target of redirect
				query.titles = mw.config.get('wgPageName');
				query.redirects = true;
			var wikipedia_api = new Morebits.wiki.api('Finding target of redirect', query, Twinkle.xfd.callbacks.rfd.findTargetCallback(callback));
			wikipedia_api.params = params;
			wikipedia_api.post;
		// This is a closure for the callback from the above API request, which gets the target of the redirect
		findTargetCallback: function(callback) {
			return function(apiobj) {
				var $xmlDoc = $(apiobj.responseXML);
				var curtimestamp = $xmlDoc.find('api').attr('curtimestamp');
				apiobj.params.curtimestamp = curtimestamp;
				if (!apiobj.params.target) { // Not a softredirect
					var target = $xmlDoc.find('redirects r').first.attr('to');
					if (!target) {
						var message = 'This page does not appear to be a redirect, aborting';
						if (mw.config.get('wgAction') === 'history') {
							message += '. If this is a soft redirect, try again from the content page, not the page history.';
						apiobj.statelem.error(message);
						return;
					apiobj.params.target = target;
					var section = $xmlDoc.find('redirects r').first.attr('tofragment');
					apiobj.params.section = section;
				callback(apiobj.params);
		main: function(params) {
			var date = new Morebits.date(params.curtimestamp);
			params.logpage = 'Wikipedia:Redirects for discussion/Log/' + date.format;
			params.discussionpage = params.logpage + '#' + Morebits.pageNameNorm;

			// Tagging redirect
			var wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), 'Adding deletion tag to redirect');
			wikipedia_page.setFollowRedirect(false);
			wikipedia_page.setCallbackParameters(params);
			wikipedia_page.load(Twinkle.xfd.callbacks.rfd.taggingRedirect);

			// Updating data for the action completed event
			Morebits.wiki.actionCompleted.redirect = params.logpage;
			Morebits.wiki.actionCompleted.notice = "Nomination completed, now redirecting to today's log";

			// Adding discussion
			wikipedia_page = new Morebits.wiki.page;
			wikipedia_page.setFollowRedirect(true);
			wikipedia_page.setCallbackParameters(params);
			wikipedia_page.load(Twinkle.xfd.callbacks.rfd.todaysList);

			// Notifications
			if (params.notifycreator || params.relatedpage) {
				var thispage = new Morebits.wiki.page(mw.config.get('wgPageName'));
				thispage.setCallbackParameters(params);
				thispage.lookupCreation(Twinkle.xfd.callbacks.rfd.sendNotifications);
			// or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name
				Twinkle.xfd.callbacks.addToLog;
		taggingRedirect: function(pageobj) {
			var text = pageobj.getPageText;
			var params = pageobj.getCallbackParameters;
			pageobj.setPageText('');
			pageobj.setEditSummary('Listed for discussion at :' + params.discussionpage + '.' + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchPage'));
			pageobj.setCreateOption('nocreate');
			pageobj.save;
		todaysList: function(pageobj) {
			var old_text = pageobj.getPageText;
			var params = pageobj.getCallbackParameters;
			var statelem = pageobj.getStatusElement;

			// params.target + sectionHash + "}} ~~~~\n" );
			var added_data = Twinkle.xfd.callbacks.getDiscussionWikitext;
			var text = old_text.replace(/()/, '$1\n' + added_data);
			if (text === old_text) {
				statelem.error('failed to find target spot for the discussion');
				return;

			pageobj.setPageText(text);
			pageobj.setEditSummary('Adding :' + Morebits.pageNameNorm + '.' + Twinkle.getPref('summaryAd'));
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchDiscussion'));
			pageobj.setCreateOption('recreate');
			pageobj.save(function {
				Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki
		sendNotifications: function(pageobj) {
			var initialContrib = pageobj.getCreator;
			var params = pageobj.getCallbackParameters;
			var statelem = pageobj.getStatusElement;

			// Notifying initial contributor
			if (params.notifycreator) {
				// Disallow warning yourself
				if (initialContrib === mw.config.get('wgUserName')) {
					statelem.warn('You (' + initialContrib + ') created this page; skipping user notification');
					Twinkle.xfd.callbacks.rfd.userNotification;

			// Notifying target page's watchers, if not a soft redirect
			if (params.relatedpage) {
				var targetTalk = new mw.Title(params.target).getTalkPage;

				// On the offchance it's a circular redirect
				if (params.target === mw.config.get('wgPageName')) {
					statelem.warn('Circular redirect; skipping target page notification');
					statelem.warn('Soft redirect; skipping target page notification');
					// Don't issue if target talk is the initial contributor's talk or your own
					if (targetTalk.getNameText === initialContrib) {
						statelem.warn('Target is initial contributor; skipping target page notification');
						statelem.warn('You (' + mw.config.get('wgUserName') + ') are the target; skipping target page notification');
					Twinkle.xfd.callbacks.rfd.targetNotification;
		userNotification: function {
			var usertalkpage = new Morebits.wiki.page('User talk:' + initialContrib, 'Notifying initial contributor (' + initialContrib + ')');
			var notifytext = '\n ~~~~';
			usertalkpage.setAppendText(notifytext);
			usertalkpage.setEditSummary('Notification: listing of :' + Morebits.pageNameNorm + ' at redirects for discussion.' + Twinkle.getPref('summaryAd'));
			usertalkpage.setCreateOption('recreate');
			Twinkle.xfd.setWatchPref(usertalkpage, Twinkle.getPref('xfdWatchUser'));
			usertalkpage.setFollowRedirect;
			usertalkpage.append(function onNotifySuccess {
				// add this nomination to the user's userspace log, if the user has enabled it
				if (params.lognomination) {
					Twinkle.xfd.callbacks.addToLog;
				// if user could not be notified, log nomination without mentioning that notification was sent
				if (params.lognomination) {
					Twinkle.xfd.callbacks.addToLog;
		targetNotification: function {
			var targettalkpage = new Morebits.wiki.page;
			var notifytext = '\n ~~~~';
			targettalkpage.setAppendText(notifytext);
			targettalkpage.setEditSummary('Notification: listing of :' + Morebits.pageNameNorm + ' at redirects for discussion.' + Twinkle.getPref('summaryAd'));
			targettalkpage.setCreateOption('recreate');
			Twinkle.xfd.setWatchPref(targettalkpage, Twinkle.getPref('xfdWatchRelated'));
			targettalkpage.setFollowRedirect(true);
			// Add to userspace log even if not notifying the creator
			if (params.lognomination && !params.usertalk) {
				targettalkpage.append(function {
					Twinkle.xfd.callbacks.addToLog;
				targettalkpage.append;

	rm: {
		listAtTalk: function(pageobj) {
			var params = pageobj.getCallbackParameters;

			pageobj.setAppendText('\n\n' + Twinkle.xfd.callbacks.getDiscussionWikitext);
			pageobj.setEditSummary('Proposing move' + (params.newname ? ' to :' + params.newname + '' : '') + Twinkle.getPref('summaryAd'));
			pageobj.setCreateOption('recreate'); // since the talk page need not exist
			Twinkle.xfd.setWatchPref(pageobj, Twinkle.getPref('xfdWatchDiscussion'));
			pageobj.append(function {
				Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki
				// add this nomination to the user's userspace log, if the user has enabled it
				if (params.lognomination) {
					Twinkle.xfd.callbacks.addToLog;

		listAtRMTR: function(pageobj) {
			var text = pageobj.getPageText;
			var params = pageobj.getCallbackParameters;
			var statelem = pageobj.getStatusElement;

			var hiddenCommentRE = /---- and enter on a new line.* -->/;
			var newtext = text.replace(hiddenCommentRE, '$&\n' + Twinkle.xfd.callbacks.getDiscussionWikitext);
			if (text === newtext) {
				statelem.error('failed to find target spot for the entry');
				return;
			pageobj.setPageText(newtext);
			pageobj.setEditSummary('Adding :' + Morebits.pageNameNorm + '.' + Twinkle.getPref('summaryAd'));
			pageobj.save(function {
				Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki
				// add this nomination to the user's userspace log, if the user has enabled it
				if (params.lognomination) {
					Twinkle.xfd.callbacks.addToLog;


Twinkle.xfd.callback.evaluate = function(e) {
	var form = e.target;

	var input = Morebits.quickForm.getInputData(form);
	var lognomination = Twinkle.getPref('logXfdNominations') && Twinkle.getPref('noLogOnXfdNomination').indexOf(input.category) === -1;

	Morebits.simpleWindow.setButtonsEnabled(false);
	Morebits.status.init(form);

	Twinkle.xfd.currentRationale = input.reason;
	Morebits.status.onError(Twinkle.xfd.printRationale);

	if (!input.category) {
		Morebits.status.error;
		return;

	var query, wikipedia_page, wikipedia_api, logpage;
	var params = { reason: input.reason, venue: input.category, lognomination: lognomination }; // Common params
	var date = new Morebits.date; // XXX: avoid use of client clock, still used by TfD, FfD and CfD
	switch (input.category) {

		case 'afd': // AFD
			query = {
				'action': 'query',
				'list': 'allpages',
				'apprefix': 'Articles for deletion/' + Morebits.pageNameNorm,
				'apnamespace': 4,
				'apfilterredir': 'nonredirects',
				'aplimit': 'max' // 500 is max for normal users, 5000 for bots and sysops
			wikipedia_api = new Morebits.wiki.api;
			wikipedia_api.params = $.extend;
			wikipedia_api.post;
			break;

		case 'tfd': // TFD
			Morebits.wiki.addCheckpoint;
			if (input.xfdtarget) {
				var tfdNamespace_re = new RegExp('^:?' + mw.config.get('wgCanonicalNamespace') + ':', 'i');
				input.xfdtarget = Morebits.string.toUpperCaseFirstChar(input.xfdtarget.replace);
				input.xfdtarget = '';

			logpage = 'Wikipedia:Templates for discussion/Log/' + date.format;

			$.extend;
			params.discussionpage = params.logpage + '#' + Morebits.pageNameNorm;

			// Modules can't be tagged, TfD instructions are to place
			// on /doc subpage, so need to tag and watch specially
			params.scribunto = mw.config.get('wgPageContentModel') === 'Scribunto';
			var watch_query = {
				action: 'watch',
				titles: mw.config.get('wgPageName'),
				token: mw.user.tokens.get('watchToken')
			// Tagging template(s)/module(s)
			if (input.xfdcat === 'tfm') { // Merge
				var wikipedia_otherpage;

				// Tag this template/module
				if (params.scribunto) {
					wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName') + '/doc', 'Tagging this module documentation with merge tag');
					params.otherTemplateName = 'Module:' + input.xfdtarget;
					wikipedia_otherpage = new Morebits.wiki.page;

					// Watch tagged module pages as well
					if (Twinkle.getPref('xfdWatchPage') !== 'no') {
						watch_query.titles += '|' + params.otherTemplateName;
						new Morebits.wiki.api.post;
					wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), 'Tagging this template with merge tag');
					params.otherTemplateName = 'Template:' + input.xfdtarget;
					wikipedia_otherpage = new Morebits.wiki.page;
				wikipedia_page.setFollowRedirect(true);
				wikipedia_page.setCallbackParameters(params);
				wikipedia_page.load(Twinkle.xfd.callbacks.tfd.taggingTemplateForMerge);

				// Tag other template/module
				wikipedia_otherpage.setFollowRedirect(true);
				var otherParams = $.extend;
				otherParams.otherTemplateName = Morebits.pageNameNorm;
				wikipedia_otherpage.setCallbackParameters(otherParams);
				wikipedia_otherpage.load(Twinkle.xfd.callbacks.tfd.taggingTemplateForMerge);
				if (params.scribunto) {
					wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName') + '/doc', 'Tagging module documentation with deletion tag');

					// Watch tagged module page as well
					if (Twinkle.getPref('xfdWatchPage') !== 'no') {
						new Morebits.wiki.api.post;
					wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), 'Tagging template with deletion tag');
				wikipedia_page.setFollowRedirect(true); // should never be needed, but if the page is moved, we would want to follow the redirect
				wikipedia_page.setCallbackParameters(params);
				wikipedia_page.load(Twinkle.xfd.callbacks.tfd.taggingTemplate);

			// Updating data for the action completed event
			Morebits.wiki.actionCompleted.redirect = logpage;
			Morebits.wiki.actionCompleted.notice = "Nomination completed, now redirecting to today's log";

			// Adding discussion
			wikipedia_page = new Morebits.wiki.page;
			wikipedia_page.setFollowRedirect(true);
			wikipedia_page.setCallbackParameters(params);
			wikipedia_page.load(Twinkle.xfd.callbacks.tfd.todaysList);

			// Notification to first contributors
			if (input.notifycreator) {
				var involvedpages = [];
				var seenusers = [];
				involvedpages.push(new Morebits.wiki.page(mw.config.get('wgPageName')));
				if (input.xfdcat === 'tfm') {
					if (params.scribunto) {
						involvedpages.push(new Morebits.wiki.page('Module:' + input.xfdtarget));
						involvedpages.push(new Morebits.wiki.page('Template:' + input.xfdtarget));
				involvedpages.forEach(function(page) {
					page.setCallbackParameters(params);
					page.lookupCreation(function(innerpage) {
						var username = innerpage.getCreator;
						if (seenusers.indexOf(username) === -1) {
							seenusers.push(username);
							Twinkle.xfd.callbacks.tfd.userNotification(innerpage);
			// or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name
				Twinkle.xfd.callbacks.addToLog;

			Morebits.wiki.removeCheckpoint;
			break;

		case 'mfd': // MFD
			query = {
				'action': 'query',
				'list': 'allpages',
				'apprefix': 'Miscellany for deletion/' + Morebits.pageNameNorm,
				'apnamespace': 4,
				'apfilterredir': 'nonredirects',
				'aplimit': 'max' // 500 is max for normal users, 5000 for bots and sysops
			wikipedia_api = new Morebits.wiki.api;
			wikipedia_api.params = $.extend;
			wikipedia_api.post;
			break;

		case 'ffd': // FFD
			var dateString = date.format;
			logpage = 'Wikipedia:Files for discussion/' + dateString;
			$.extend;
			params.discussionpage = params.logpage + '#' + Morebits.pageNameNorm;

			Morebits.wiki.addCheckpoint;

			// Updating data for the action completed event
			Morebits.wiki.actionCompleted.redirect = logpage;
			Morebits.wiki.actionCompleted.notice = 'Nomination completed, now redirecting to the discussion page';

			// Tagging file
			wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), 'Adding deletion tag to file page');
			wikipedia_page.setFollowRedirect(true);
			wikipedia_page.setCallbackParameters(params);
			wikipedia_page.load(Twinkle.xfd.callbacks.ffd.taggingImage);

			// Contributor specific edits
			wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'));
			wikipedia_page.setCallbackParameters(params);
			wikipedia_page.lookupCreation(Twinkle.xfd.callbacks.ffd.main);

			Morebits.wiki.removeCheckpoint;
			break;

		case 'cfd':
			Morebits.wiki.addCheckpoint;

			var cfdNamespace_re = new RegExp('^:?' + mw.config.get('wgCanonicalNamespace') + ':', 'i');
			if (input.xfdtarget) {
				input.xfdtarget = input.xfdtarget.replace;
				input.xfdtarget = '';
			if (input.xfdtarget2) {
				input.xfdtarget2 = input.xfdtarget2.replace;

			logpage = 'Wikipedia:Categories for discussion/Log/' + date.format;

			$.extend;
			params.discussionpage = params.logpage + '#' + Morebits.pageNameNorm;

			// Useful for customized actions in edit summaries and the notification template
			var summaryActions = {
				cfd: 'deletion',
				'sfd-t': 'deletion',
				cfm: 'merging',
				cfr: 'renaming',
				'sfr-t': 'renaming',
				cfs: 'splitting',
				cfc: 'conversion'
			params.action = summaryActions[params.xfdcat];

			// Updating data for the action completed event
			Morebits.wiki.actionCompleted.redirect = logpage;
			Morebits.wiki.actionCompleted.notice = "Nomination completed, now redirecting to today's log";

			// Tagging category
			wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), 'Tagging category with ' + params.action + ' tag');
			wikipedia_page.setFollowRedirect(true); // should never be needed, but if the page is moved, we would want to follow the redirect
			wikipedia_page.setCallbackParameters(params);
			wikipedia_page.load(Twinkle.xfd.callbacks.cfd.taggingCategory);

			// Adding discussion to list
			wikipedia_page = new Morebits.wiki.page;
			wikipedia_page.setPageSection(2);
			wikipedia_page.setFollowRedirect(true);
			wikipedia_page.setCallbackParameters(params);
			wikipedia_page.load(Twinkle.xfd.callbacks.cfd.todaysList);

			// Notification to first contributor
			if (input.notifycreator) {
				wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'));
				wikipedia_page.setCallbackParameters(params);
				wikipedia_page.lookupCreation(Twinkle.xfd.callbacks.cfd.userNotification);
			// or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name
				Twinkle.xfd.callbacks.addToLog;

			Morebits.wiki.removeCheckpoint;
			break;

		case 'cfds':
			input.xfdtarget = /^Category:/.test(input.xfdtarget) ? input.xfdtarget : 'Category:' + input.xfdtarget;

			logpage = 'Wikipedia:Categories for discussion/Speedy';
			$.extend;

			// Updating data for the action completed event
			Morebits.wiki.actionCompleted.redirect = logpage;
			Morebits.wiki.actionCompleted.notice = 'Nomination completed, now redirecting to the discussion page';

			// Tagging category
			wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), 'Tagging category with rename tag');
			wikipedia_page.setFollowRedirect(true);
			wikipedia_page.setCallbackParameters(params);
			wikipedia_page.load(Twinkle.xfd.callbacks.cfds.taggingCategory);

			// Adding discussion to list
			wikipedia_page = new Morebits.wiki.page;
			wikipedia_page.setFollowRedirect(true);
			wikipedia_page.setCallbackParameters(params);
			wikipedia_page.load(Twinkle.xfd.callbacks.cfds.addToList);

			break;

		case 'rfd':
			$.extend;
			// find target and pass main as the callback
			Twinkle.xfd.callbacks.rfd.findTarget;
			break;

		case 'rm':
			$.extend;
			var nomPageName = input.rmtr ?
				'Wikipedia:Requested moves/Technical requests' :
				new mw.Title(Morebits.pageNameNorm).getTalkPage.toText;

			Morebits.wiki.actionCompleted.redirect = nomPageName;
			Morebits.wiki.actionCompleted.notice = 'Nomination completed, now redirecting to the discussion page';

			wikipedia_page = new Morebits.wiki.page;
			wikipedia_page.setFollowRedirect(true);
			wikipedia_page.setCallbackParameters(params);

			if (input.rmtr) {
				wikipedia_page.setPageSection(2);
				wikipedia_page.load(Twinkle.xfd.callbacks.rm.listAtRMTR);
				// listAtTalk uses .append, so no need to load the page
				Twinkle.xfd.callbacks.rm.listAtTalk(wikipedia_page);
			break;

		default:
			alert('twinklexfd: unknown XFD discussion venue');
			break;

Twinkle.addInitCallback;