मीडियाविकि:Gadget-Twinkle.js

Sarkarverse से
नेविगेशन पर जाएँ खोज पर जाएँ

ध्यान दें: प्रकाशित करने के बाद बदलाव देखने के लिए आपको अपने ब्राउज़र के कैश को हटाना पड़ सकता है।

  • Firefox/Safari: Reload क्लिक समय Shift दबाएँ, या फिर Ctrl-F5 या Ctrl-R दबाएँ (Mac पर ⌘-R)
  • Google Chrome: Ctrl-Shift-R दबाएँ (Mac पर ⌘-Shift-R)
  • Internet Explorer/Edge: Refresh पर क्लिक करते समय Ctrl दबाएँ, या Ctrl-F5 दबाएँ
  • Opera: Ctrl-F5 दबाएँ।
/**
 * +-------------------------------------------------------------------------+
 * |                  === WARNING: GLOBAL GADGET FILE ===                    |
 * |                Changes to this page affect many users.                  |
 * |           Please discuss changes at [[WT:TW]] before editing.           |
 * +-------------------------------------------------------------------------+
 *
 * Imported from github [https://github.com/azatoth/twinkle].
 * All changes should be made in the repository, otherwise they will be lost.
 *
 * To update this script from github, you must have a local repository set up. Then
 * follow the instructions at [https://github.com/azatoth/twinkle/blob/master/README.md].
 *
 * ----------
 *
 * This is AzaToth's Twinkle, the popular script sidekick for newbies, admins, and
 * every Wikipedian in between. Visit [[WP:TW]] for more information.
 */

//<nowiki>

( function ( window, document, $, undefined ) { // Wrap with anonymous function

var Twinkle = {};
window.Twinkle = Twinkle;  // allow global access

// for use by custom modules (normally empty)
Twinkle.initCallbacks = [];
Twinkle.addInitCallback = function twinkleAddInitCallback( func ) {
	Twinkle.initCallbacks.push( func );
};

Twinkle.defaultConfig = {};
/**
 * Twinkle.defaultConfig.twinkle and Twinkle.defaultConfig.friendly
 *
 * This holds the default set of preferences used by Twinkle. (The |friendly| object holds preferences stored in the FriendlyConfig object.)
 * It is important that all new preferences added here, especially admin-only ones, are also added to
 * |Twinkle.config.sections| in twinkleconfig.js, so they are configurable via the Twinkle preferences panel.
 * For help on the actual preferences, see the comments in twinkleconfig.js.
 */
Twinkle.defaultConfig.twinkle = {
	 // General
	summaryAd: " ([[WP:TW|TW]])",
	deletionSummaryAd: " ([[WP:TW|TW]])",
	protectionSummaryAd: " ([[WP:TW|TW]])",
	userTalkPageMode: "window",
	dialogLargeFont: false,
	 // Fluff (revert and rollback)
	openTalkPage: [ "agf", "norm", "vand" ],
	openTalkPageOnAutoRevert: false,
	markRevertedPagesAsMinor: [ "vand" ],
	watchRevertedPages: [ "agf", "norm", "vand", "torev" ],
	offerReasonOnNormalRevert: true,
	confirmOnFluff: false,
	showRollbackLinks: [ "diff", "others" ],
	 // CSD
	watchSpeedyPages: [ "व3", "व4", "व6", "व6ल", "व6स", "व6फ़" ],
	markSpeedyPagesAsPatrolled: true,
	// these next two should probably be identical by default
	notifyUserOnSpeedyDeletionNomination: [ "शीह", "व1", "व2", "व3", "व4", "व5", "व6", "ल1", "ल2", "ल4", "व6ल", "फ़1", "फ़2", "फ़3", "फ़4", "फ़5", "फ़6", "व6फ़", "सा1", "स2", "व6स" ],
	welcomeUserOnSpeedyDeletionNotification: [ "शीह", "व1", "व2", "व3", "व4", "व5", "व6", "ल1", "ल2", "ल4", "व6ल", "फ़1", "फ़2", "फ़3", "फ़4", "फ़5", "फ़6", "व6फ़", "सा1", "स2", "व6स" ],
	openUserTalkPageOnSpeedyDelete: [ "शीह", "व1", "व2", "व3", "व4", "व5", "व6", "ल1", "ल2", "ल4", "व6ल", "फ़1", "फ़2", "फ़3", "फ़4", "फ़5", "फ़6", "व6फ़", "सा1", "स2", "व6स" ],
	deleteTalkPageOnDelete: false,
	deleteSysopDefaultToTag: false,
	speedyWindowHeight: 500,
	speedyWindowWidth: 800,
	logSpeedyNominations: false,
	speedyLogPageName: "शीह लॉग",
	noLogOnSpeedyNomination: [ "स1" ],
	 // Unlink
	unlinkNamespaces: [ "0", "100" ],
	 // Warn
	defaultWarningGroup: "1",
	showSharedIPNotice: true,
	watchWarnings: true,
	blankTalkpageOnIndefBlock: false,
	 // XfD
	xfdWatchDiscussion: "default",
	xfdWatchList: "no",
	xfdWatchPage: "default",
	xfdWatchUser: "default",
	 // Hidden preferences
	revertMaxRevisions: 50,
	batchdeleteChunks: 50,
	batchDeleteMinCutOff: 5,
	batchMax: 5000,
	batchProtectChunks: 50,
	batchProtectMinCutOff: 5,
	batchundeleteChunks: 50,
	batchUndeleteMinCutOff: 5,
	deliChunks: 500,
	deliMax: 5000,
};

// now some skin dependent config.
if ( mw.config.get( "skin" ) === "vector" ) {
	Twinkle.defaultConfig.twinkle.portletArea = "right-navigation";
	Twinkle.defaultConfig.twinkle.portletId   = "p-twinkle";
	Twinkle.defaultConfig.twinkle.portletName = "TW";
	Twinkle.defaultConfig.twinkle.portletType = "menu";
	Twinkle.defaultConfig.twinkle.portletNext = "p-search";
} else {
	Twinkle.defaultConfig.twinkle.portletArea =  null;
	Twinkle.defaultConfig.twinkle.portletId   = "p-cactions";
	Twinkle.defaultConfig.twinkle.portletName = null;
	Twinkle.defaultConfig.twinkle.portletType = null;
	Twinkle.defaultConfig.twinkle.portletNext = null;
}

Twinkle.defaultConfig.friendly = {
	 // Tag
	groupByDefault: true,
	watchTaggedPages: true,
	markTaggedPagesAsMinor: false,
	markTaggedPagesAsPatrolled: true,
	tagArticleSortOrder: "cat",
	customTagList: [],
	 // Welcome
	topWelcomes: false,
	watchWelcomes: true,
	welcomeHeading: "स्वागत",
	insertHeadings: true,
	insertUsername: true,
	insertSignature: true,  // sign welcome templates, where appropriate
	quickWelcomeMode: "norm",
	quickWelcomeTemplate: "welcome",
	maskTemplateInSummary: true,
	customWelcomeList: [],
	 // Talkback
	markTalkbackAsMinor: true,
	insertTalkbackSignature: true,  // always sign talkback templates
	talkbackHeading: "सन्देश",
	adminNoticeHeading: "Notice",
	 // Shared
	markSharedIPAsMinor: true
};

Twinkle.getPref = function twinkleGetPref( name ) {
	var result;
	if ( typeof Twinkle.prefs === "object" && typeof Twinkle.prefs.twinkle === "object" ) {
		// look in Twinkle.prefs (twinkleoptions.js)
		result = Twinkle.prefs.twinkle[name];
	} else if ( typeof window.TwinkleConfig === "object" ) {
		// look in TwinkleConfig
		result = window.TwinkleConfig[name];
	}

	if ( result === undefined ) {
		return Twinkle.defaultConfig.twinkle[name];
	}
	return result;
};

Twinkle.getFriendlyPref = function twinkleGetFriendlyPref(name) {
	var result;
	if ( typeof Twinkle.prefs === "object" && typeof Twinkle.prefs.friendly === "object" ) {
		// look in Twinkle.prefs (twinkleoptions.js)
		result = Twinkle.prefs.friendly[ name ];
	} else if ( typeof window.FriendlyConfig === "object" ) {
		// look in FriendlyConfig
		result = window.FriendlyConfig[ name ];
	}

	if ( result === undefined ) {
		return Twinkle.defaultConfig.friendly[ name ];
	}
	return result;
};



/**
 * **************** twAddPortlet() ****************
 *
 * Adds a portlet menu to one of the navigation areas on the page.
 * This is necessarily quite a hack since skins, navigation areas, and
 * portlet menu types all work slightly different.
 *
 * Available navigation areas depend on the skin used.
 * Monobook:
 *  "column-one", outer div class "portlet", inner div class "pBody". Existing portlets: "p-cactions", "p-personal", "p-logo", "p-navigation", "p-search", "p-interaction", "p-tb", "p-coll-print_export"
 *  Special layout of p-cactions and p-personal through specialized styles.
 * Vector:
 *  "mw-panel", outer div class "portal", inner div class "body". Existing portlets/elements: "p-logo", "p-navigation", "p-interaction", "p-tb", "p-coll-print_export"
 *  "left-navigation", outer div class "vectorTabs" or "vectorMenu", inner div class "" or "menu". Existing portlets: "p-namespaces", "p-variants" (menu)
 *  "right-navigation", outer div class "vectorTabs" or "vectorMenu", inner div class "" or "menu". Existing portlets: "p-views", "p-cactions" (menu), "p-search"
 *  Special layout of p-personal portlet (part of "head") through specialized styles.
 * Modern:
 *  "mw_contentwrapper" (top nav), outer div class "portlet", inner div class "pBody". Existing portlets or elements: "p-cactions", "mw_content"
 *  "mw_portlets" (sidebar), outer div class "portlet", inner div class "pBody". Existing portlets: "p-navigation", "p-search", "p-interaction", "p-tb", "p-coll-print_export"
 *
 * @param String navigation -- id of the target navigation area (skin dependant, on vector either of "left-navigation", "right-navigation", or "mw-panel")
 * @param String id -- id of the portlet menu to create, preferably start with "p-".
 * @param String text -- name of the portlet menu to create. Visibility depends on the class used.
 * @param String type -- type of portlet. Currently only used for the vector non-sidebar portlets, pass "menu" to make this portlet a drop down menu.
 * @param Node nextnodeid -- the id of the node before which the new item should be added, should be another item in the same list, or undefined to place it at the end.
 *
 * @return Node -- the DOM node of the new item (a DIV element) or null
 */
function twAddPortlet( navigation, id, text, type, nextnodeid )
{
	//sanity checks, and get required DOM nodes
	var root = document.getElementById( navigation );
	if ( !root ) {
		return null;
	}

	var item = document.getElementById( id );
	if ( item ) {
		if ( item.parentNode && item.parentNode === root ) {
			return item;
		}
		return null;
	}

	var nextnode;
	if ( nextnodeid ) {
		nextnode = document.getElementById(nextnodeid);
	}

	//verify/normalize input
	type = ( skin === "vector" && type === "menu" && ( navigation === "left-navigation" || navigation === "right-navigation" )) ? "menu" : "";
	var outerDivClass;
	var innerDivClass;
	switch ( skin )
	{
		case "vector":
			if ( navigation !== "portal" && navigation !== "left-navigation" && navigation !== "right-navigation" ) {
				navigation = "mw-panel";
			}
			outerDivClass = ( navigation === "mw-panel" ) ? "portal" : ( type === "menu" ? "vectorMenu extraMenu" : "vectorTabs extraMenu" );
			innerDivClass = ( navigation === "mw-panel" ) ? "body" : ( type === "menu" ? "menu" : "" );
			break;
		case "modern":
			if ( navigation !== "mw_portlets" && navigation !== "mw_contentwrapper" ) {
				navigation = "mw_portlets";
			}
			outerDivClass = "portlet";
			innerDivClass = "pBody";
			break;
		default:
			navigation = "column-one";
			outerDivClass = "portlet";
			innerDivClass = "pBody";
			break;
	}

	// Build the DOM elements.
	var outerDiv = document.createElement( "div" );
	outerDiv.className = outerDivClass + " emptyPortlet";
	outerDiv.id = id;
	if ( type === "menu" ) {
		// Fix drop-down arrow image in Vector skin
		outerDiv.style.backgroundImage = 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAQCAMAAAAlM38UAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA9QTFRFsbGxmpqa3d3deXl58/n79CzHcQAAAAV0Uk5T/////wD7tg5TAAAAMklEQVR42mJgwQoYBkqYiZEZAhiZUFRDxWGicEPA4nBRhNlAcYQokpVMDEwD6kuAAAMAyGMFQVv5ldcAAAAASUVORK5CYII=")';
		outerDiv.style.backgroundPosition = "right 60%";
	}
	if ( nextnode && nextnode.parentNode === root ) {
		root.insertBefore( outerDiv, nextnode );
	} else {
		root.appendChild( outerDiv );
	}

	var h5 = document.createElement( "h5" );
	if ( type === "menu" ) {
		var span = document.createElement( "span" );
		span.appendChild( document.createTextNode( text ) );
		h5.appendChild( span );

		var a = document.createElement( "a" );
		a.href = "#";

		$( a ).click(function ( e ) {
			e.preventDefault();
		});

		span = document.createElement( "span" );
		span.appendChild( document.createTextNode( text ) );
		a.appendChild( span );
		h5.appendChild( a );
	} else {
		h5.appendChild( document.createTextNode( text ) );
	}
	outerDiv.appendChild( h5 );

	var innerDiv = document.createElement( "div" ); // Not strictly necessary with type vectorTabs, or other skins.
	innerDiv.className = innerDivClass;
	outerDiv.appendChild(innerDiv);

	var ul = document.createElement( "ul" );
	innerDiv.appendChild( ul );

	return outerDiv;
}


/**
 * **************** twAddPortletLink() ****************
 * Builds a portlet menu if it doesn't exist yet, and add the portlet link.
 * @param task: Either a URL for the portlet link or a function to execute.
 */
function twAddPortletLink( task, text, id, tooltip )
{
	if ( Twinkle.getPref("portletArea") !== null ) {
		twAddPortlet( Twinkle.getPref( "portletArea" ), Twinkle.getPref( "portletId" ), Twinkle.getPref( "portletName" ), Twinkle.getPref( "portletType" ), Twinkle.getPref( "portletNext" ));
	}
	var link = mw.util.addPortletLink( Twinkle.getPref( "portletId" ), typeof task === "string" ? task : "#", text, id, tooltip );
	if ( $.isFunction( task ) ) {
		$( link ).click(function ( ev ) {
			task();
			ev.preventDefault();
		});
	}
	return link;
}

// Check if account is experienced enough to use Twinkle
var twinkleUserAuthorized = Morebits.userIsInGroup( "autoconfirmed" ) || Morebits.userIsInGroup( "confirmed" );

/*
 ****************************************
 *** friendlyshared.js: Shared IP tagging module
 ****************************************
 * Mode of invocation:     Tab ("Shared")
 * Active on:              Existing IP user talk pages
 * Config directives in:   FriendlyConfig
 */

Twinkle.shared = function friendlyshared() {
	if( mw.config.get('wgNamespaceNumber') === 3 && Morebits.isIPAddress(mw.config.get('wgTitle')) ) {
		var username = mw.config.get('wgTitle').split( '/' )[0].replace( /\"/, "\\\""); // only first part before any slashes
		twAddPortletLink( function(){ Twinkle.shared.callback(username); }, "साझा आइ॰पी॰", "friendly-shared", "साझा आइ॰पी॰ पता टैगिंग" );
	}
};

Twinkle.shared.callback = function friendlysharedCallback( uid ) {
	var Window = new Morebits.simpleWindow( 600, 400 );
	Window.setTitle( "साझा आइ॰पी॰ पता टैगिंग" );
	Window.setScriptName( "Twinkle" );
	Window.addFooterLink( "Twinkle help", "WP:TW/DOC#shared" );

	var form = new Morebits.quickForm( Twinkle.shared.callback.evaluate );

	var div = form.append( { type: 'div', id: 'sharedip-templatelist' } );
	div.append( { type: 'header', label:'साझा आइ॰पी॰ पता साँचे' } );
	div.append( { type: 'radio', name: 'shared', list: Twinkle.shared.standardList,
		event: function( e ) {
			Twinkle.shared.callback.change_shared( e );
			e.stopPropagation();
		}
	} );

	var org = form.append( { type:'field', label:'नीचे आइ॰पी॰ पते के स्वामी/संचालक का नाम, होस्ट-नाम तथा संपर्क-सूचना (यदि लागू हो तो) भरें, और \"Submit\" बटन पर क्लिक करें।' } );
	org.append( {
			type: 'input',
			name: 'organization',
			label: 'संगठन का नाम (वैकल्पिक)',
			disabled: true,
			tooltip: 'इनमें से कुछ साँचे एक वैकल्पिक पैरामीटर की सुविधा प्रदान करते हैं, जिसमें आई॰पी॰ पतों के स्वामी या इनको संचालित करने वाले संगठन का नाम भरा जा सकता है। आप वह नाम यहाँ डाल सकते हैं। यदि आवश्यक हो तो wikimarkup का प्रयोग कर सकते हैं।'
		}
	);
	org.append( {
			type: 'input',
			name: 'host',
			label: 'होस्ट नाम (वैकल्पिक)',
			disabled: true,
			tooltip: 'ये साँचे होस्ट नाम के लिए एक वैकल्पिक पैरामीटर स्वीकार करते हैं। आप होस्ट नाम (जैसे proxy.example.com) यहाँ डाल सकते हैं।'
		}
	);
	org.append( {
			type: 'input',
			name: 'contact',
			label: 'संगठन के संपर्कसूत्र (सिर्फ संगठन के अनुरोध पर भरें)',
			disabled: true,
			tooltip: 'इनमें से कुछ साँचे संगठनो के संपर्कसूत्र के लिए एक वैकल्पिक पैरामीटर स्वीकार करते हैं। इस पैरामीटर का इस्तेमाल संगठन के विशेष अनुरोध पर ही किया जाना चाहिए। यदि आवश्यक हो तो wikimarkup का प्रयोग कर सकते हैं।'
		}
	);
	
	form.append( { type:'submit' } );

	var result = form.render();
	Window.setContent( result );
	Window.display();

	$(result).find('div#sharedip-templatelist').addClass('quickform-scrollbox');
};

Twinkle.shared.standardList = [
	{
		label: '{{shared IP}}: मानक साझा आइ॰पी॰ साँचा',
		value: 'shared IP',
		tooltip: 'आइ॰पी॰ सदस्य वार्ता पृष्ठ पर प्रयोग हेतु साँचा। यह साँचा आइ॰पी॰ सदस्य तथा उन लोगों को जो उसे चेतावनी देना चाहते हैं या प्रतिबन्धित करना चाहते हैं, को उपयोगी जानकारी उपलब्ध करता है।'
	},
	{ 
		label: '{{shared IP edu}}: शैक्षिक संस्थानों के लिए संशोधित साझा आइ॰पी॰ साँचा',
		value: 'shared IP edu'
	},
	{
		label: '{{shared IP public}}: सार्वजनिक टर्मिनलों के लिए संशोधित साझा आइ॰पी॰ साँचा',
		value: 'shared IP public'
	},
	{
		label: '{{shared IP gov}}: सरकारी सुविधाओं या एजेंसियों के लिए संशोधित साझा आइ॰पी॰ साँचा',
		value: 'shared IP gov'
	},
	{
		label: '{{dynamicIP}}: अस्थिर पतों(dynamic IP) वाले संगठनो के लिए संशोधित साझा आइ॰पी॰ साँचा',
		value: 'dynamicIP'
	},
	{ 
		label: '{{ISP}}: इंटरनेट सेवा प्रदाता(ISP) संगठनों(खासकर प्रॉक्सीज़) के लिए संशोधित साझा आइ॰पी॰ साँचा',
		value: 'ISP'
	},
	{ 
		label: '{{mobileIP}}: मोबाइल फोन कंपनी और उनके ग्राहकों के लिए संशोधित साझा आइ॰पी॰ साँचा',
		value: 'mobileIP'
	}
];

Twinkle.shared.callback.change_shared = function friendlysharedCallbackChangeShared(e) {
	e.target.form.contact.disabled = ( e.target.value === 'shared IP edu' ) ? false : true;
	e.target.form.organization.disabled = false;
	e.target.form.host.disabled = false;
};

Twinkle.shared.callbacks = {
	main: function( pageobj ) {
		var params = pageobj.getCallbackParameters();
		var pageText = pageobj.getPageText();
		var found = false;
		var text = '{{';

		for( var i=0; i < Twinkle.shared.standardList.length; i++ ) {
			var tagRe = new RegExp( '(\\{\\{' + Twinkle.shared.standardList[i].value + '(\\||\\}\\}))', 'im' );
			if( tagRe.exec( pageText ) ) {
				Morebits.status.warn( 'Info', 'सदस्य वार्ता पृष्ठ पर {{' + Twinkle.shared.standardList[i].value + '}} पाया गया। टैगिंग रद्द कर डी गयी है।' );
				return;
			}
		}

		Morebits.status.info( 'Info', 'साझा आइ॰पी॰ पता साँचा सदस्य के वार्ता पृष्ठ में ऊपर-ऊपर जोड़ा जाएगा।' );
		text += params.value + '|' + params.organization;
		if( params.value === 'shared IP edu' && params.contact !== '') {
			text += '|' + params.contact;
		}
		if( params.host !== '' ) {
			text += '|host=' + params.host;
		}
		text += '}}\n\n';

		var summaryText = '{{[[सा:' + params.value + '|' + params.value + ']]}} साँचा जोड़ा।';
		pageobj.setPageText(text + pageText);
		pageobj.setEditSummary(summaryText + Twinkle.getPref('summaryAd'));
		pageobj.setMinorEdit(Twinkle.getFriendlyPref('markSharedIPAsMinor'));
		pageobj.setCreateOption('recreate');
		pageobj.save();
	}
};

Twinkle.shared.callback.evaluate = function friendlysharedCallbackEvaluate(e) {
	var shared = e.target.getChecked( 'shared' );
	if( !shared || shared.length <= 0 ) {
		alert( 'आपको प्रयोग करने के लिए एक साझा आइ॰पी साँचा चुनना होगा।' );
		return;
	}
	
	var value = shared[0];
	
	if( e.target.organization.value === '') {
		alert( 'आपको {{' + value + '}} साँचे के लिए संगठन का नाम देना होगा।' );
		return;
	}
	
	var params = {
		value: value,
		organization: e.target.organization.value,
		host: e.target.host.value,
		contact: e.target.contact.value
	};

	Morebits.simpleWindow.setButtonsEnabled( false );
	Morebits.status.init( e.target );

	Morebits.wiki.actionCompleted.redirect = mw.config.get('wgPageName');
	Morebits.wiki.actionCompleted.notice = "टैगिंग सम्पूर्ण, वार्ता पन्ना कुछ ही क्षणों में रीलोड होगा";

	var wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), "सदस्य वार्ता पृष्ठ सम्पादन");
	wikipedia_page.setFollowRedirect(true);
	wikipedia_page.setCallbackParameters(params);
	wikipedia_page.load(Twinkle.shared.callbacks.main);
};

/*
 ****************************************
 *** friendlytag.js: Tag module
 ****************************************
 * Mode of invocation:     Tab ("Tag")
 * Active on:              Existing articles; file pages with a corresponding file
 *                         which is local (not on Commons); existing user subpages
 *                         and existing subpages of Wikipedia:Articles for creation;
 *                         all redirects
 * Config directives in:   FriendlyConfig
 */

Twinkle.tag = function friendlytag() {
	// redirect tagging
	if( Morebits.wiki.isPageRedirect() ) {
		Twinkle.tag.mode = 'redirect';
		twAddPortletLink( Twinkle.tag.callback, "टैग", "friendly-tag", "पुनर्निर्देश टैग" );
	}
	// file tagging
	else if( mw.config.get('wgNamespaceNumber') === 6 && !document.getElementById("mw-sharedupload") && document.getElementById("mw-imagepage-section-filehistory") ) {
		Twinkle.tag.mode = 'file';
		twAddPortletLink( Twinkle.tag.callback, "टैग", "friendly-tag", "फ़ाइल रखरखाव टैग" );
	}
	// article tagging
	else if( mw.config.get('wgNamespaceNumber') === 0 && mw.config.get('wgCurRevisionId') ) {
		Twinkle.tag.mode = 'article';
		twAddPortletLink( Twinkle.tag.callback, "टैग", "friendly-tag", "लेख रखरखाव टैग" );
	}
};

Twinkle.tag.callback = function friendlytagCallback( uid ) {
	var Window = new Morebits.simpleWindow( 630, (Twinkle.tag.mode === "article") ? 450 : 400 );
	Window.setScriptName( "Twinkle" );
	// anyone got a good policy/guideline/info page/instructional page link??
	Window.addFooterLink( "Twinkle help", "WP:TW/DOC#tag" );

	var form = new Morebits.quickForm( Twinkle.tag.callback.evaluate );

	switch( Twinkle.tag.mode ) {
		case 'article':
			Window.setTitle( "लेख रखरखाव टैगिंग" );

			form.append( {
					type: 'checkbox',
					list: [
						{
							label: 'यदि संभव  हो तो {{अनेक समस्याएँ}} द्वारा वर्गीकृत करें',
							value: 'group',
							name: 'group',
							tooltip: 'यदि {{अनेक समस्याएँ}} द्वारा  स्वीकृत 3 से अधिक साँचों का प्रयोग कर रहे हों और ये चैकबौक्स checked हो, तो सभी स्वीकृत साँचे एक {{अनेक समस्याएँ}} साँचे में एकत्रित कर दिए जायेंगे।',
							checked: Twinkle.getFriendlyPref('groupByDefault')
						}
					]
				}
			);

			form.append({
				type: 'select',
				name: 'sortorder',
				label: 'यह सूची देखें:',
				tooltip: 'You can change the default view order in your Twinkle preferences (WP:TWPREFS).',
				event: Twinkle.tag.updateSortOrder,
				list: [
					{ type: 'option', value: 'cat', label: 'वर्ग अनुसार', selected: Twinkle.getFriendlyPref('tagArticleSortOrder') === 'cat' },
					{ type: 'option', value: 'alpha', label: 'वर्णमाला अनुसार', selected: Twinkle.getFriendlyPref('tagArticleSortOrder') === 'alpha' }
				]
			});

			form.append( { type: 'div', id: 'tagWorkArea' } );

			if( Twinkle.getFriendlyPref('customTagList').length ) {
				form.append( { type: 'header', label: 'Custom tags' } );
				form.append( { type: 'checkbox', name: 'articleTags', list: Twinkle.getFriendlyPref('customTagList') } );
			}
			break;

		case 'file':
			Window.setTitle( "फ़ाइल रखरखाव टैगिंग" );

			// TODO: perhaps add custom tags TO list of checkboxes

			form.append({ type: 'header', label: 'लाइसेंस और स्रोत समस्या टैग' });
			form.append({ type: 'checkbox', name: 'imageTags', list: Twinkle.tag.file.licenseList } );

			form.append({ type: 'header', label: 'सफ़ाई टैग' } );
			form.append({ type: 'checkbox', name: 'imageTags', list: Twinkle.tag.file.cleanupList } );

			form.append({ type: 'header', label: 'विकिमीडिया कॉमन्स सम्बन्धी टैग' });
			form.append({ type: 'checkbox', name: 'imageTags', list: Twinkle.tag.file.commonsList } );
			break;

		case 'redirect':
			Window.setTitle( "Redirect tagging" );

			form.append({ type: 'header', label:'गलत एवं अलग वर्तनी, काल और वचन' });
			form.append({ type: 'checkbox', name: 'redirectTags', list: Twinkle.tag.spellingList });

			form.append({ type: 'header', label:'अन्य नाम' });
			form.append({ type: 'checkbox', name: 'redirectTags', list: Twinkle.tag.alternativeList });

			form.append({ type: 'header', label:'रखरखाव' });
			form.append({ type: 'checkbox', name: 'redirectTags', list: Twinkle.tag.administrativeList });
			break;

		default:
			alert("Twinkle.tag: unknown mode " + Twinkle.tag.mode);
			break;
	}

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

	var result = form.render();
	Window.setContent( result );
	Window.display();

	if (Twinkle.tag.mode === "article") {
		// fake a change event on the sort dropdown, to initialize the tag list
		var evt = document.createEvent("Event");
		evt.initEvent("change", true, true);
		result.sortorder.dispatchEvent(evt);
	}
};

Twinkle.tag.checkedTags = [];

Twinkle.tag.updateSortOrder = function(e) {
	var sortorder = e.target.value;
	var $workarea = $(e.target.form).find("div#tagWorkArea");

	Twinkle.tag.checkedTags = e.target.form.getChecked("articleTags");
	if (!Twinkle.tag.checkedTags) {
		Twinkle.tag.checkedTags = [];
	}

	// function to generate a checkbox, with appropriate subgroup if needed
	var makeCheckbox = function(tag, description) {
		var checkbox = { value: tag, label: "{{" + tag + "}}: " + description };
		if (Twinkle.tag.checkedTags.indexOf(tag) !== -1) {
			checkbox.checked = true;
		}
		if (tag === "वैश्वीकरण") {
			checkbox.subgroup = {
				name: 'globalize',
				type: 'select',
				list: [
					{ label: "{{वैश्वीकरण}}: लेख विषय का विश्वव्यापी दृष्टिकोण नहीं दर्शाता है", value: "वैश्वीकरण" },
					{
						label: "क्षेत्र-विशिष्ट {{वैश्वीकरण}} उपसाँचे",
						list: [
							{ label: "{{वैश्वीकरण/अंग्रेज़ी}}: लेख मुख्य रूप से अंग्रेज़ी वक्ताओं का दृष्टिकोण दर्शाता है", value: "वैश्वीकरण/अंग्रेज़ी" },
							{ label: "{{वैश्वीकरण/यूरोप}}: लेख मुख्य रूप से यूरोपीय दृष्टिकोण दर्शाता है", value: "वैश्वीकरण/यूरोप" },
							{ label: "{{वैश्वीकरण/भारत}}: लेख मुख्य रूप से भारतीय दृष्टिकोण दर्शाता है", value: "वैश्वीकरण/भारत" }
						]
					}
				]
			};
		}
		return checkbox;
	};

	// categorical sort order
	if (sortorder === "cat") {
		var div = new Morebits.quickForm.element({
			type: "div",
			id: "tagWorkArea"
		});

		// function to iterate through the tags and create a checkbox for each one
		var doCategoryCheckboxes = function(subdiv, array) {
			var checkboxes = [];
			$.each(array, function(k, tag) {
				var description = Twinkle.tag.article.tags[tag];
				checkboxes.push(makeCheckbox(tag, description));
			});
			subdiv.append({
				type: "checkbox",
				name: "articleTags",
				list: checkboxes
			});
		};

		var i = 0;
		// go through each category and sub-category and append lists of checkboxes
		$.each(Twinkle.tag.article.tagCategories, function(title, content) {
			div.append({ type: "header", id: "tagHeader" + i, label: title });
			var subdiv = div.append({ type: "div", id: "tagSubdiv" + i++ });
			if ($.isArray(content)) {
				doCategoryCheckboxes(subdiv, content);
			} else {
				$.each(content, function(subtitle, subcontent) {
					subdiv.append({ type: "div", label: [ Morebits.htmlNode("b", subtitle) ] });
					doCategoryCheckboxes(subdiv, subcontent);
				});
			}
		});

		var rendered = div.render();
		$workarea.replaceWith(rendered);
		var $rendered = $(rendered);
		$rendered.find("h5").css({ 'font-size': '110%', 'margin-top': '1em' });
		$rendered.find("div").filter(":has(span.quickformDescription)").css({ 'margin-top': '0.4em' });
	}
	// alphabetical sort order
	else {
		var checkboxes = [];
		$.each(Twinkle.tag.article.tags, function(tag, description) {
			checkboxes.push(makeCheckbox(tag, description));
		});
		var tags = new Morebits.quickForm.element({
			type: "checkbox",
			name: "articleTags",
			list: checkboxes
		});
		$workarea.empty().append(tags.render());
	}
};


// Tags for ARTICLES start here

Twinkle.tag.article = {};

// A list of all article tags, in alphabetical order
// To ensure tags appear in the default "categorized" view, add them to the tagCategories hash below.

Twinkle.tag.article.tags = {
	"अत्यधिक विवरण": "लेख में अनावश्यक अत्यधिक विवरण है",
	"अद्यतन": "लेख में नई जानकारी जोड़ने की आवश्यकता है",
	"अस्पष्ट": "लेख भ्रामक अथवा अस्पष्ट है",
	"अतिरंजित": "लेख में अतिरंजित शब्दावली का प्रयोग है जो सत्यापित जानकारी जोड़े बिना विषयवस्तु का प्रचार करती है",
	"अविश्वसनीय स्रोत": "लेख में दिये गए सन्दर्भों के विश्वसनीय न होने की आशंका है",
	"आत्मकथा": "लेख आत्मकथा है एवं ग़ैर तटस्थ दृष्टिकोण का हों सकता है",
	"उद्धरण कम": "लेख में संदर्भ हैं परन्तु उद्धरण अपर्याप्त हैं",
	"उद्धरण शैली": "लेख में अस्पष्ट अथवा परस्पर-विरोधी शैली के उद्धरण हैं",
	"उद्धरणहीन": "लेख में संदर्भ हैं परन्तु उद्धरण नहीं हैं",
	"उल्लेखनीयता": "लेख की विषयवस्तु उल्लेखनीयता दिशानिर्देशों पर खरी नहीं उतरती",
	"एक स्रोत": "लेख मुख्य रूप से अथवा पूर्णतया एक स्रोत पर निर्भर करता है",
	"एकाकी": "लेख से बहुत कम अथवा कोई भी लेख नहीं जुड़ते",
	"कम दृष्टिकोण": "लेख सभी महत्वपूर्ण दृष्टिकोण नहीं दर्शाता, केवल कुछ को दर्शाता है",
	"कहानी": "लेख में कहानी का सारांश बहुत लम्बा है",
	"काम जारी": "लेख पर इस समय काम चल रहा है और लेख में काफ़ी विस्तार अथवा सुधार किया जा रहा है",
	"को विलय": "इस लेख का एक और लेख में विलय कर देना चाहिए",
	"काल्पनिक परिप्रेक्ष्य": "लेख का विषय कल्पना पर आधारित है और लेख को वास्तविकता के परिप्रेक्ष्य से लिखने की आवश्यकता है",
	"कॉपी पेस्ट": "लेख किसी स्रोत से कॉपी-पेस्ट किया गया है",
	"खराब अनुवाद": "लेख किसी और भाषा से खराब तरीके से अनूदित किया गया है",
	"गद्य": "लेख सूची आरूप में है जिसे गद्य का प्रयोग करके बेहतर दर्शाया जा सकता है",
	"ग़ैर मुक्त": "लेख में ग़ैर मुक्त सामग्री का अत्यधिक अथवा अनुचित उपयोग है",
	"छोटी भूमिका": "लेख की भूमिका बहुत छोटी है और विस्तारित की जानी चाहिए",
	"जीवनी स्रोत कम": "जीवित व्यक्ति की जीवनी में सत्यापन हेतु अतिरिक्त स्रोतों की आवश्यकता है",
	"जीवनी स्रोतहीन": "जीवित व्यक्ति की जीवनी जिसमें कोई संदर्भ नहीं हैं",
	"दृष्टिकोण": "लेख की तटस्थता इस समय विवादित है",
	"दृष्टिकोण जाँच": "लेख को तटस्थता जाँच के लिए नामित करें",
	"धोखा": "लेख सम्पूर्णतया धोखा हो सकता है",
	"नया असमीक्षित लेख": "लेख को बाद में जाँचने के लिये चिन्हित करें",
	"निबंध": "लेख निबंध की तरह लिखा है और ठीक करने की आवश्यकता है",
	"पुराना": "लेख में पुरानी जानकारी है जिसे अद्यतन की आवश्यकता है",
	"प्रसंग": "लेख का प्रसंग अपर्याप्त है",
	"प्रतिलिपि सम्पादन": "लेख को व्याकरण, शैली, सामंजस्य, लहजे अथवा वर्तनी के लिए प्रतिलिपि सम्पादन की आवश्यकता है",
	"प्रशंसक दृष्टिकोण": "लेख प्रशंसक के दृष्टिकोण से लिखा है",
	"प्राथमिक स्रोत": "लेख प्राथमिक स्रोतों पर अत्यधिक रूप से निर्भर है। लेख में तृतीय पक्ष के स्रोतों की आवश्यकता है।",
	"बाहरी कड़ियाँ": "लेख कि बाहरी कड़ियाँ विकी नीतियों एवं दिशानिर्देशों के उल्लंघन में हैं",
	"बन्द सिरा": "लेख में दूसरे लेखों की कड़ियाँ बहुत कम हैं अथवा नहीं हैं",
	"बड़े सम्पादन": "लेख में कुछ समय के लिये बड़े सम्पादन किये जा रहे हैं",
	"भाग": "लेख को भागों में विभाजित करने की आवश्यकता है",
	"भूमिका नहीं": "लेख में भूमिका नहीं है, लिखी जानी चाहिए",
	"भूमिका फिर लिखें": "लेख की भूमिका को दिशानिर्देशों के अनुसार पुनर्लेखन की आवश्यकता है",
	"भ्रामक": "भ्रामक शब्दों के प्रयोग से लेख में पक्षपात उत्पन्न हो रहा है",
	"में विलय": "एक और लेख का इस लेख में विलय कर देना चाहिए",
	"मूल शोध": "लेख में मूल शोध अथवा असत्यापित दावे हैं",
	"लम्बा": "लेख बहुत लम्बा है",
	"लम्बी भूमिका": "लेख की भूमिका बहुत लम्बी है, छोटी की जानी चाहिए",
	"लहजा": "लेख का लहजा ठीक नहीं हैं",
	"विकिफ़ाइ": "लेख को विकिफिकेशन की आवश्यकता है",
	"विलय": "लेख का एक और लेख से विलय कर देना चाहिए",
	"विवादित": "लेख की तथ्यात्मक सटीकता संदिग्ध है",
	"विशेषज्ञ": "लेख को विषय के विशेषज्ञ से ध्यान की आवश्यकता है",
	"विज्ञापन": "लेख विज्ञापन की तरह लिखा है",
	"वैश्वीकरण": "लेख विषय का विश्वव्यापी दृष्टिकोण नहीं दर्शाता है",
	"संदर्भ सिर्फ़ कड़ी": "स्रोतों के लिए सिर्फ़ यूआरएल का प्रयोग हुआ है, जिनके टूटने की संभावना है",
	"सफ़ाई": "लेख को  ठीक करने की आवश्यकता है",
	"सिर्फ़ कहानी": "लेख लगभग सम्पूर्णतः कहानी का सारांश है",
	"स्रोत कम": "लेख को सत्यापन के लिए अतिरिक्त संदर्भ एवं स्रोतों की आवश्यकता है",
	"स्रोतहीन": "लेख स्रोतहीन है",
	"स्वयं प्रकाशित स्रोत": "लेख में स्वप्रकाशित स्रोतों का अनुचित प्रयोग है",
	"हालही झुकाव": "लेख हाल की घटनाओं की ओर झुका हुआ है",
	"हिन्दी नहीं": "लेख हिन्दी के स्थान पर किसी और भाषा में लिखा है एवं अनूदित करने की आवश्यकता है",
	"ज्ञानकोषीय नहीं": "लेख में ज्ञानकोष के लिये अनुपयुक्त जानकारी है जो वि:नहीं के विरुद्ध है",
	"श्रेणी कम": "लेख को अतिरिक्त श्रेणियों की आवश्यकता है",
	"श्रेणीहीन": "लेख श्रेणीहीन है"
};

// A list of tags in order of category
// Tags should be in alphabetical order within the categories
// Add new categories with discretion - the list is long enough as is!

Twinkle.tag.article.tagCategories = {
	"सफ़ाई एवं रखरखाव": {
		"सामान्य सफ़ाई": [
			"सफ़ाई",
			"प्रतिलिपि सम्पादन",
			"विकिफ़ाइ"
		],
		"अवांछित सामग्री": [
			"कॉपी पेस्ट",
			"बाहरी कड़ियाँ",
			"ग़ैर मुक्त",
			"ज्ञानकोषीय नहीं"
		],
		"संरचना, रूप, एवं भूमिका": [
			"भूमिका नहीं",
			"भूमिका फिर लिखें",
			"लम्बी भूमिका",
			"छोटी भूमिका",
			"भाग",
			"लम्बा"
		],
		"काल्पनिक विषयवस्तु संबंधी सफ़ाई": [
			"सिर्फ़ कहानी",
			"काल्पनिक परिप्रेक्ष्य",
			"कहानी"
		]
	},
	"सामग्री संबंधी आम मुद्दे": {
		"उल्लेखनीयता": [
			"उल्लेखनीयता"  // has subcategories and special-cased code
		],
		"लेखन शैली": [
			"विज्ञापन",
			"निबंध",
			"गद्य",
			"अस्पष्ट",
			"लहजा"
		],
		"जानकारी एवं विवरण": [
			"प्रसंग",
			"विशेषज्ञ",
			"अत्यधिक विवरण",
			"पुराना",
			"अद्यतन"
		],
		"तटस्थता, पक्षपात एवं तथ्यात्मक सटीकता": [
			"आत्मकथा",
			"विवादित",
			"धोखा",
			"वैश्वीकरण",  // has subcategories and special-cased code
			"दृष्टिकोण",
			"दृष्टिकोण जाँच",
			"प्रशंसक दृष्टिकोण",
			"कम दृष्टिकोण",
			"हालही झुकाव",
			"अतिरंजित",
			"भ्रामक"
		],
		"सत्यापन एवं स्रोत": [
			"जीवनी स्रोत कम",
			"जीवनी स्रोतहीन",
			"मूल शोध",
			"प्राथमिक स्रोत",
			"स्रोत कम",
			"स्वयं प्रकाशित स्रोत",
			"स्रोतहीन",
			"अविश्वसनीय स्रोत"
		]
	},
	"सामग्री संबंधी विशिष्ट मुद्दे": {
		"भाषा": [
			"हिन्दी नहीं",
			"खराब अनुवाद"
		],
		"कड़ियाँ": [
			"बन्द सिरा",
			"एकाकी"
		],
		"संदर्भ शैली": [
			"उद्धरण शैली",
			"संदर्भ सिर्फ़ कड़ी",
			"उद्धरण कम",
			"उद्धरणहीन"
		],
		"श्रेणियाँ": [
			"श्रेणीहीन",
			"श्रेणी कम"
		]
	},
	"विलय": [
		"विलय",
		"को विलय",
		"में विलय"
	],
	"सूचनात्मक": [
		"बड़े सम्पादन",
		"नया असमीक्षित लेख",
		"काम जारी"
	]
};

// Tags for REDIRECTS start here

Twinkle.tag.spellingList = [
	{
		label: '{{R from abbreviation}}: संक्षिप्त नाम से पुनर्निर्देशन',
		value: 'R from abbreviation' 
	},
	{
		label: '{{R to list entry}}: \"छोटी चीज़ों कि सूची\" प्रकार के लेख को पुनर्निर्देशन(ऐसे विषयों के लिये जो अपने-आप में सम्पूर्ण लेख जितने उल्लेखनीय नहीं हैं)',
		value: 'R to list entry' 
	},
	{
		label: '{{R to section}}: {{R to list entry}} जैसा, परंतु तब प्रयोग करें जब सूची अनुभाजित हो और पुनर्निर्देशन किसी अनुभाग को किया जा रहा हो',
		value: 'R to section' 
	},
	{
		label: '{{R from misspelling}}: गलत वर्तनी अथवा टंकन में गलती से पुनर्निर्देशन',
		value: 'R from misspelling' 
	},
	{
		label: '{{R from alternative spelling}}: अलग वर्तनी से पुनर्निर्देशन',
		value: 'R from alternative spelling' 
	},
	{
		label: '{{R from plural}}: बहुवचन से एकवचन को पुनर्निर्देशन',
		value: 'R from plural' 
	},
	{
		label: '{{R from related word}}: सम्बंधित शब्द से पुनर्निर्देशन',
		value: 'R from related word' 
	},
	/*{
		label: '{{R with possibilities}}: redirect from a more specific title to a more general, less detailed article, hence something which can and should be expanded',
		value: 'R with possibilities' 
	},*/
	{
		label: '{{R from member}}: किसी समूह के सदस्य से उस समूह, संगठन अथवा टीम इत्यादि को पुनर्निर्देशन',
		value: 'R from member' 
	}
];

Twinkle.tag.alternativeList = [
	{
		label: '{{R from alternative name}}: किसी और नाम, तख़ल्लुस, निकनेम, अथवा पर्यायवाची से पुनर्निर्देशन',
		value: 'R from alternative name' 
	},
	{
		label: '{{R from full name}}: पूरे नाम से पुनर्निर्देशन',
		value: 'R from full name' 
	},
	{
		label: '{{R from surname}}: उपनाम से पुनर्निर्देशन',
		value: 'R from surname' 
	},
	{
		label: '{{R from historic name}}: किसी ऐसे नाम से पुनर्निर्देशन जो ऐतिहासिक रूप से जगह से जुड़ा हुआ है',
		value: 'R from historic name',
		tooltip: 'उदहारण: उत्तरांचल से उत्तराखण्ड, मद्रास से चेन्नई'
	},
	{
		label: '{{R from scientific name}}: वैज्ञानिक नाम से आम नाम को पुनर्निर्देशन',
		value: 'R from scientific name' 
	},
	{
		label: '{{R to scientific name}}: आम नाम से वैज्ञानिक नाम को पुनर्निर्देशन',
		value: 'R to scientific name' 
	},
	{
		label: '{{R from name and country}}: देश सहित नाम से सिर्फ़ जगह के नाम को पुनर्निर्देशन',
		value: 'R from name and country' 
	},
	{
		label: '{{R from alternative language}}: किसी दूसरी भाषा के नाम से हिन्दी भाषा को पुनर्निर्देशन',
		value: 'R from alternative language' 
	}/*,
	{
		label: '{{R from ASCII}}: redirect from a title in basic ASCII to the formal article title, with differences that are not diacritical marks (accents, umlauts, etc.)',
		value: 'R from ASCII' 
	},
	{
		label: '{{R from title without diacritics}}: redirect to the article title with diacritical marks (accents, umlauts, etc.)',
		value: 'R from title without diacritics'
	}*/
];

Twinkle.tag.administrativeList = [
	{
		label: '{{R from merge}}: विलय किये गए पन्ने से पुनर्निर्देशन(सम्पादन इतिहास संरक्षित करने के लिये)',
		value: 'R from merge',
		tooltip: 'इसका प्रयोग तब करें जब दो सम्बन्धित विषयों के लेखों का विलय किया गया हो। एक ही विषय पर बने दो लेखों के लिये {{R from duplicated article}} का प्रयोग करें।'
	},
	{
		label: '{{R to disambiguation page}}: बहुविकल्पी पन्ने को पुनर्निर्देशन',
		value: 'R to disambiguation page' 
	},
	{
		label: '{{R from duplicated article}}: इसी विषय पर बने दूसरे लेख को पुनर्निर्देशन',
		value: 'R from duplicated article' 
	},
	{
		label: '{{R to decade}}: वर्ष से दशक को पुनर्निर्देशन',
		value: 'R to decade' 
	},
	{
		label: '{{R from shortcut}}: विकिपीडिया शॉर्टकट से पुनर्निर्देशन',
		value: 'R from shortcut' 
	},
	{
		label: '{{R from EXIF}}: redirect of a wikilink created from JPEG EXIF information (i.e. the \"metadata\" section on some image description pages)',
		value: 'R from EXIF' 
	}/*,
	{
		label: '{{R from school}}: redirect from a school article that had very little information',
		value: 'R from school'
	}*/
];

// maintenance tags for FILES start here

Twinkle.tag.file = {};

Twinkle.tag.file.licenseList = [
	{ label: '{{Bsr}}: source info consists of bare image URL/generic base URL only', value: 'Bsr' },
	{ label: '{{Non-free reduce}}: non-low-resolution fair use image (or too-long audio clip, etc)', value: 'Non-free reduce' },
	{ label: '{{Non-free reduced}}: fair use media which has been reduced (old versions need to be deleted)', value: 'Non-free reduced' }
];

Twinkle.tag.file.cleanupList = [
	{ label: '{{BadJPEG}}: JPEG that should be PNG or SVG', value: 'Bad JPEG' },
	{
		label: '{{Should be SVG}}: PNG, GIF or JPEG should be vector graphics', value: 'Should be SVG',
		subgroup: {
			name: 'svgCategory',
			type: 'select',
			list: [
				{ label: '{{Should be SVG|other}}', value: 'other' },
				{ label: '{{Should be SVG|alphabet}}: character images, font examples, etc.', value: 'alphabet' },
				{ label: '{{Should be SVG|chemical}}: chemical diagrams, etc.', value: 'chemical' },
				{ label: '{{Should be SVG|circuit}}: electronic circuit diagrams, etc.', value: 'circuit' },
				{ label: '{{Should be SVG|coat of arms}}: coats of arms', value: 'coat of arms' },
				{ label: '{{Should be SVG|diagram}}: diagrams that do not fit any other subcategory', value: 'diagram' },
				{ label: '{{Should be SVG|emblem}}: emblems, free/libre logos, insignias, etc.', value: 'emblem' },
				{ label: '{{Should be SVG|fair use}}: fair-use images, fair-use logos', value: 'fair use' },
				{ label: '{{Should be SVG|flag}}: flags', value: 'flag' },
				{ label: '{{Should be SVG|graph}}: visual plots of data', value: 'graph' },
				{ label: '{{Should be SVG|logo}}: logos', value: 'logo' },
				{ label: '{{Should be SVG|map}}: maps', value: 'map' },
				{ label: '{{Should be SVG|music}}: musical scales, notes, etc.', value: 'music' },
				{ label: '{{Should be SVG|physical}}: "realistic" images of physical objects, people, etc.', value: 'physical' },
				{ label: '{{Should be SVG|symbol}}: miscellaneous symbols, icons, etc.', value: 'symbol' }
			]
		}
	}
];

Twinkle.tag.file.commonsList = [
	{ label: '{{Move to Commons}}: मुक्त मीडिया जिसे कॉमन्स पर होना चाहिये', value: 'Move to Commons' },
	{ label: '{{Do not move to Commons}} (सार्वजनिक क्षेत्र समस्या): फ़ाइल संयुक्त राष्ट्र अमेरिका में सार्वजनिक क्षेत्र में है परंतु स्रोत देश में नहीं', value: 'Do not move to Commons' },
	{ label: '{{Do not move to Commons}} (अन्य कारण)', value: 'Do not move to Commons_reason' },
	{ label: '{{NowCommons}}: फ़ाइल कॉमन्स पर उपलब्ध है', value: 'subst:ncd' }
];

// Contains those article tags that can be grouped into {{multiple issues}}.
// This list includes synonyms.
Twinkle.tag.groupHash = [
 	'advert',
	'विज्ञापन',
	'autobiography',
	'आत्मकथा',
	'BLPrefimprove',
	'refimproveBLP',
	'BLP sources',
	'BLPsources',
	'जीवनी स्रोत कम',
	'BLP unsourced',
	'BLPunsourced',
	'BLPunreferenced',
	'BLPunref',
	'unrefBLP',
	'unreferencedBLP',
	'जीवनी स्रोतहीन',
	'citation style',
	'citationstyle',
	'citation-style',
	'उद्धरण शैली',
	'उद्धरण अनुपस्थित',
	'citations missing',
	'सफ़ाई',
	'cleanup',
	'laundry',
	'laundrylists',
	'organize',
	'restructure',
	'reorganisation',
	'spam',
	'confusing',
	'अस्पष्ट',
	'context',
	'प्रसंग',
	'contradict',
	'copy edit',
	'copyedit',
	'प्रतिलिपि सम्पादन',
	'प्रतिलिपि संपादन',
	'dead end',
	'deadend',
	'बन्द सिरा',
	'disputed',
	'विवादित',
	'essay-like',
	'essay',
	'निबंध',
	'examplefarm',
	'expert',
	'विशेषज्ञ',
	'external links',
	'बाहरी कड़ियाँ',
	'fanpov',
	'fansite',
	'प्रशंसक दृष्टिकोण',
	'globalize',
	'वैश्वीकरण',
	'hoax',
	'धोखा',
	'howto',
	'incomplete',
	'in-universe',
	'काल्पनिक परिप्रेक्ष्य',
	'lead missing',
	'intromissing',
	'भूमिका नहीं',
	'lead rewrite',
	'introrewrite',
	'भूमिका फिर लिखें',
	'lead too long',
	'intro length',
	'intro-toolong',
	'लम्बी भूमिका',
	'lead too short',
	'intro-tooshort',
	'छोटी भूमिका',
	'like resume',
	'likeresume',
	'newsrelease',
	'notability',
	'notable',
	'उल्लेखनीयता',
	'onesource',
	'one source',
	'एक स्रोत',
	'original research',
	'मूल शोध',
	'orphan',
	'do-attempt',
	'एकाकी',
	'out of date',
	'पुराना',
	'अत्यधिक विवरण',
	'fancruft',
	'peacock',
	'अतिरंजित',
	'plot',
	'कहानी',
	'POV',
	'NPOV',
	'pov',
	'npov',
	'दृष्टिकोण',
	'दृष्टिकोण जाँच',
	'pov-check',
	'प्राथमिक स्रोत',
	'primarysources',
	'prose',
	'गद्य',
	'recent',
	'recentism',
	'हालही झुकाव',
	'moreref',
	'morerefs',
	'morereferences',
	'refimprove',
	'स्रोत कम',
	'sections',
	'भाग',
	'self-published',
	'स्वयं प्रकाशित स्रोत',
	'story',
	'synthesis',
	'tone',
	'लहजा',
	'travel guide',
	'travelguide',
	'trivia',
	'unencyclopedic',
	'unreferenced',
	'unref',
	'स्रोतहीन',
	'स्रोत हीन',
	'update',
	'अद्यतन',
	'लम्बा',
	'verylong',
	'long',
	'भ्रामक',
	'weasel',
	'wikify',
	'विकिफ़ाइ',
	'विकिफाइ',
	'विकिफाई',
	'विकिफ़ाई'
];

Twinkle.tag.callbacks = {
	main: function( pageobj ) {
		var params = pageobj.getCallbackParameters(),
		    tagRe, tagText = '', summaryText = '',
		    tags = [], groupableTags = [],

		// Remove tags that become superfluous with this action
			pageText = pageobj.getPageText().replace(/\{\{\s*(नया असमीक्षित लेख|Userspace draft)\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\}\s*/ig, ""),

		    i;
		
		if( Twinkle.tag.mode !== 'redirect' ) {
			// Check for preexisting tags and separate tags into groupable and non-groupable arrays
			for( i = 0; i < params.tags.length; i++ ) {
				tagRe = new RegExp( '(\\{\\{' + params.tags[i] + '(\\||\\}\\}))', 'im' );
				if( !tagRe.exec( pageText ) ) {
					if( Twinkle.tag.groupHash.indexOf(params.tags[i]) !== -1 && 
							(params.tags[i] !== 'वैश्वीकरण' || params.globalizeSubcategory === 'वैश्वीकरण' )) {
						// don't add to multipleissues for globalize subcats
						groupableTags = groupableTags.concat( params.tags[i] );
					} else {
						tags = tags.concat( params.tags[i] );
					}
				} else {
					Morebits.status.info( 'Info', 'Found {{' + params.tags[i] +
						'}} on the article already...excluding' );
				}
			}

			if( params.group && groupableTags.length >= 3 ) {
				Morebits.status.info( 'Info', 'स्वीकृत टैग {{अनेक समस्याएँ}} द्वारा वर्गीकृत किये जा रहे हैं' );

				groupableTags.sort();
				tagText += '{{अनेक समस्याएँ';
				summaryText += ' {{[[साँचा:अनेक समस्याएँ|अनेक समस्याएँ]]}} निम्न प्राचलों के साथ:';
				for( i = 0; i < groupableTags.length; i++ ) {
					tagText += '|' + groupableTags[i] +
						'={{subst:CURRENTMONTHNAME}} {{subst:CURRENTYEAR}}';

					if( i === (groupableTags.length - 1) ) {
						summaryText += ' और';
					} else if ( i < (groupableTags.length - 1) && i > 0 ) {
						summaryText += ',';
					}
					summaryText += ' ' + groupableTags[i];
				}
				tagText += '}}\n';
			} else {
				tags = tags.concat( groupableTags );
			}
		} else {
			// Check for pre-existing tags
			for( i = 0; i < params.tags.length; i++ ) {
				tagRe = new RegExp( '(\\{\\{' + params.tags[i] + '(\\||\\}\\}))', 'im' );
				if( !tagRe.exec( pageText ) ) {
					tags = tags.concat( params.tags[i] );
				} else {
					Morebits.status.info( 'Info', 'Found {{' + params.tags[i] +
						'}} on the redirect already...excluding' );
				}
			}
		}

		tags.sort();
		for( i = 0; i < tags.length; i++ ) {
			var currentTag = "";
			if( tags[i] === 'श्रेणीहीन' || tags[i] === 'श्रेणी कम' ) {
				pageText += '\n\n{{' + tags[i] +
					'|date={{subst:CURRENTMONTHNAME}} {{subst:CURRENTYEAR}}}}';
			} else {
				if( tags[i] === 'वैश्वीकरण' ) {
					currentTag += '{{' + params.globalizeSubcategory;
				} else {
					currentTag += ( Twinkle.tag.mode === 'redirect' ? '\n' : '' ) + '{{' + tags[i];
				}

				// prompt for other parameters, based on the tag
				switch( tags[i] ) {
					case 'सफ़ाई':
						var reason = prompt('आप वैकल्पिक रूप से इस लेख में सफ़ाई की आवश्यकता का कारण दे सकते हैं।\n' +
							" कोई भी कारण न देने के लिये OK पर क्लिक करें। {{सफ़ाई}} टैग छोड़ने के लिये Cancel पर क्लिक करें।", "");
						if (reason === null) {
							continue;
						} else if (reason !== "") {
							currentTag += '|reason=' + reason;
						}
						break;
					case 'कॉपी पेस्ट':
						var url = prompt('कृपया स्रोत यू॰आर॰एल(URL) दें जहाँ से आपको लगता है इसे कॉपी पेस्ट किया गया है।\n' +
							"यदि आपको नहीं पता तो OK पर क्लिक करें। {{कॉपी पेस्ट}} टैग छोड़ने के लिये Cancel पर क्लिक करें।", "");
						if (url === null) {
							continue;
						} else if (url !== "") {
							currentTag += '|url=' + url;
						}
						break;
					case 'हिन्दी नहीं':
						var langname = prompt('कृपया उस भाषा का नाम बताएँ जिसमें आपके विचार से यह लेख लिखा हुआ है।\n' +
							"यदि आप नहीं जानते तो OK पर क्लिक करें। {{हिन्दी नहीं}} टैग छोड़ने के लिये Cancel पर क्लिक करें।", "");
						if (langname === null) {
							continue;
						} else if (langname !== "") {
							currentTag += '|1=' + langname;
						}
						break;
					case 'खराब अनुवाद':
						var roughlang = prompt('कृपया स्रोत भाषा का नाम दें जिससे यह लेख अनूदित किया गया है।  \n' +
							"यदि आप नहीं जानते तो OK पर क्लिक करें। {{खराब अनुवाद}} टैग छोड़ने के लिये Cancel पर क्लिक करें।", "");
						if (roughlang === null) {
							continue;
						} else if (roughlang !== "") {
							currentTag += '|1=' + roughlang;
						}
						break;
					case 'विशेषज्ञ':
						var wikiproject = prompt('Please enter the name of a WikiProject which might be able to help recruit an expert.  \n' +
							"Just click OK if you don't know.  To skip the {{expert-subject}} tag, click Cancel.", "");
						if (wikiproject === null) {
							continue;
						} else if (wikiproject !== "") {
							currentTag += '|1=' + wikiproject;
						}
						break;
					case 'विलय':
					case 'को विलय':
					case 'में विलय':
						var param = prompt('कृपया विलय में शामिल अन्य लेखों के नाम बताएँ।  \n' +
							"एक से अधिक लेखों के नाम डालने के लिये उनके बीच में वर्टिकल पाइप (|) का प्रयोग करें।  \n" +
							"यह जानकारी आवश्यक है। नाम डालने के बाद OK दबाएँ, विलय टैग छोड़ने के लिये Cancel दबाएँ।", "");
						if (param === null) {
							continue;
						} else if (param !== "") {
							currentTag += '|' + param;
						}
						break;
					default:
						break;
				}

				currentTag += Twinkle.tag.mode === 'redirect' ? '}}' : '|date={{subst:CURRENTMONTHNAME}} {{subst:CURRENTYEAR}}}}\n';
				tagText += currentTag;
			}

			if ( i > 0 || groupableTags.length > 3 ) {
				if( i === (tags.length - 1) ) {
					summaryText += ' और';
				} else if ( i < (tags.length - 1) ) {
					summaryText += ',';
				}
			}

			summaryText += ' {{[[';
			if( tags[i] === 'वैश्वीकरण' ) {
				summaryText += "साँचा:" + params.globalizeSubcategory + '|' + params.globalizeSubcategory;
			} else {
				summaryText += (tags[i].indexOf(":") !== -1 ? tags[i] : ("साँचा:" + tags[i] + "|" + tags[i]));
			}
			summaryText += ']]}} जोड़े';
		}

		if( Twinkle.tag.mode === 'redirect' ) {
			pageText += tagText;
		} else {
			// smartly insert the new tags after any hatnotes. Regex is a bit more
			// complicated than it'd need to be, to allow templates as parameters,
			// and to handle whitespace properly.
			pageText = pageText.replace(/^\s*(?:((?:\s*\{\{\s*(?:about|correct title|dablink|distinguish|for|other\s?(?:hurricaneuses|people|persons|places|uses(?:of)?)|redirect(?:-acronym)?|see\s?(?:also|wiktionary)|selfref|the)\d*\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\})+(?:\s*\n)?)\s*)?/i,
				"$1" + tagText);
		}
		summaryText += /*' tag' + ( ( tags.length + ( groupableTags.length > 3 ? 1 : 0 ) ) > 1 ? 's' : '' ) +
			' to ' + Twinkle.tag.mode +*/ Twinkle.getPref('summaryAd');

		pageobj.setPageText(pageText);
		pageobj.setEditSummary(summaryText);
		pageobj.setWatchlist(Twinkle.getFriendlyPref('watchTaggedPages'));
		pageobj.setMinorEdit(Twinkle.getFriendlyPref('markTaggedPagesAsMinor'));
		pageobj.setCreateOption('nocreate');
		pageobj.save();

		if( Twinkle.getFriendlyPref('markTaggedPagesAsPatrolled') ) {
			pageobj.patrol();
		}
	},

	file: function friendlytagCallbacksFile(pageobj) {
		var text = pageobj.getPageText();
		var params = pageobj.getCallbackParameters();
		var summary = "Adding ";

		// Add maintenance tags
		if (params.tags.length) {

			var tagtext = "", currentTag;
			$.each(params.tags, function(k, tag) {
				// when other commons-related tags are placed, remove "move to Commons" tag
				if (["subst:ncd", "Do not move to Commons_reason", "Do not move to Commons",
					"Now Commons"].indexOf(tag) !== -1) {
					text = text.replace(/\{\{(mtc|(copy |move )?to ?commons|move to wikimedia commons|copy to wikimedia commons)[^}]*\}\}/gi, "");
				}

				currentTag = "{{" + (tag === "Do not move to Commons_reason" ? "Do not move to Commons" : tag);

				var input;
				switch (tag) {
					case "subst:ncd":
						input = prompt( "{{" + (tag === "subst:ncd" ? "Now Commons" : tag) +
							"}} - Enter the name of the image on Commons (if different from local name), excluding the File: prefix:", "" );
						if (input === null) {
							return true;  // continue
						} else if (input !== "") {
							currentTag += '|1=' + input;
						}
						break;
					case "Do not move to Commons_reason":
						input = prompt( "{{Do not move to Commons}} - Enter the reason why this image should not be moved to Commons (required). To skip the tag, click Cancel:", "" );
						if (input === null) {
							return true;  // continue
						} else if (input !== "") {
							currentTag += "|reason=" + input;
						}
						break;
					case "Non-free reduced":
						//remove {{non-free reduce}} and redirects
						text = text.replace(/\{\{\s*(Template\s*:\s*)?(Non-free reduce|Nfr|Nonfree reduce)\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\}\s*/ig, "");
						currentTag += "|date={{subst:date}}";
						break;
					default:
						break;  // don't care
				}

				if (tag === "Should be SVG") {
					currentTag += "|" + params.svgSubcategory;
				}

				currentTag += "}}\n";

				tagtext += currentTag;
				summary += "{{" + tag + "}}, ";

				return true;  // continue
			});

			if (!tagtext) {
				pageobj.getStatusElement().warn("User canceled operation; nothing to do");
				return;
			}

			text = tagtext + text;
		}

		pageobj.setPageText(text);
		pageobj.setEditSummary(summary.substring(0, summary.length - 2) + Twinkle.getPref('summaryAd'));
		pageobj.setWatchlist(Twinkle.getFriendlyPref('watchTaggedPages'));
		pageobj.setMinorEdit(Twinkle.getFriendlyPref('markTaggedPagesAsMinor'));
		pageobj.setCreateOption('nocreate');
		pageobj.save();

		if( Twinkle.getFriendlyPref('markTaggedPagesAsPatrolled') ) {
			pageobj.patrol();
		}
	}
};

Twinkle.tag.callback.evaluate = function friendlytagCallbackEvaluate(e) {
	var form = e.target;
	var params = {};

	switch (Twinkle.tag.mode) {
		case 'article':
			params.tags = form.getChecked( 'articleTags' );
			params.group = form.group.checked;
			params.globalizeSubcategory = form["articleTags.वैश्वीकरण"] ? form["articleTags.वैश्वीकरण"].value : null;
			break;
		case 'file':
			params.svgSubcategory = form["imageTags.svgCategory"] ? form["imageTags.svgCategory"].value : null;
			params.tags = form.getChecked( 'imageTags' );
			break;
		case 'redirect':
			params.tags = form.getChecked( 'redirectTags' );
			break;
		default:
			alert("Twinkle.tag: unknown mode " + Twinkle.tag.mode);
			break;
	}

	if( !params.tags.length ) {
		alert( 'You must select at least one tag!' );
		return;
	}

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

	Morebits.wiki.actionCompleted.redirect = mw.config.get('wgPageName');
	Morebits.wiki.actionCompleted.notice = "टैगिंग संपूर्ण, पन्ना कुछ ही क्षणों में रीलोड होगा";
	if (Twinkle.tag.mode === 'redirect') {
		Morebits.wiki.actionCompleted.followRedirect = false;
	}

	var wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), "Tagging " + Twinkle.tag.mode);
	wikipedia_page.setCallbackParameters(params);
	switch (Twinkle.tag.mode) {
		case 'article':
			/* falls through */
		case 'redirect':
			wikipedia_page.load(Twinkle.tag.callbacks.main);
			return;
		case 'file':
			wikipedia_page.load(Twinkle.tag.callbacks.file);
			return;
		default:
			alert("Twinkle.tag: unknown mode " + Twinkle.tag.mode);
			break;
	}
};

/*
 ****************************************
 *** friendlytalkback.js: Talkback module
 ****************************************
 * Mode of invocation:     Tab ("TB")
 * Active on:              Existing user talk pages
 * Config directives in:   FriendlyConfig
 */

Twinkle.talkback = function friendlytalkback() {
	if( mw.config.get('wgNamespaceNumber') === 3 ) {
		var username = mw.config.get('wgTitle').split( '/' )[0].replace( /\"/, "\\\""); // only first part before any slashes
		$(twAddPortletLink("#", "सन्देश", "friendly-talkback", "सरल सन्देश", "")).click(function() { Twinkle.talkback.callback(username); });
	}
};

Twinkle.talkback.callback = function friendlytalkbackCallback( uid ) {
	if( uid === mw.config.get('wgUserName') ){
		alert( 'Is it really so bad that you\'re talking back to yourself?' );
		return;
	}

	var Window = new Morebits.simpleWindow( 600, 350 );
	Window.setTitle( "सन्देश" );
	Window.setScriptName( "Twinkle" );
	Window.addFooterLink( "{{सन्देश}} पर जानकारी", "साँचा:सन्देश" );
	Window.addFooterLink( "Twinkle help", "WP:TW/DOC#talkback" );

	var form = new Morebits.quickForm( Twinkle.talkback.callback.evaluate );

	form.append( { type: 'radio', name: 'tbtarget',
				list: [ {
						label: 'मेरे वार्ता पृष्ठ पर',
						value: 'mytalk',
						checked: 'true' },
					{
						label: 'किसी अन्य सदस्य के वार्ता पृष्ठ पर',
						value: 'usertalk' },
					{
						label: 'प्रबंधक सूचनापट',
						value: 'an' },
					{
						label: 'किसी अन्य पृष्ठ पर',
						value: 'other' } ],
				event: Twinkle.talkback.callback.change_target
			} );

	form.append( {
			type: 'field',
			label: 'Work area',
			name: 'work_area'
		} );

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

	var result = form.render();
	Window.setContent( result );
	Window.display();

	// We must init the
	var evt = document.createEvent( "Event" );
	evt.initEvent( 'change', true, true );
	result.tbtarget[0].dispatchEvent( evt );
};

Twinkle.talkback.prev_page = '';
Twinkle.talkback.prev_section = '';
Twinkle.talkback.prev_message = '';

Twinkle.talkback.callback.change_target = function friendlytagCallbackChangeTarget(e) {
	var value = e.target.values;
	var root = e.target.form;
	var old_area;

	if(root.section) {
		Twinkle.talkback.prev_section = root.section.value;
	}
	if(root.message) {
		Twinkle.talkback.prev_message = root.message.value;
	}
	if(root.page) {
		Twinkle.talkback.prev_page = root.page.value;
	}

	for( var i = 0; i < root.childNodes.length; ++i ) {
		var node = root.childNodes[i];
		if (node instanceof Element && node.getAttribute( 'name' ) === 'work_area' ) {
			old_area = node;
			break;
		}
	}
	var work_area = new Morebits.quickForm.element( { 
			type: 'field',
			label: 'सन्देश जानकारी',
			name: 'work_area'
		} );

	switch( value ) {
		case 'mytalk':
			/* falls through */
		default:
			work_area.append( { 
					type:'input',
					name:'section',
					label:'सम्बंधित अनुभाग (वैकल्पिक)',
					tooltip:'आपके वार्ता पन्ने के उस अनुभाग का नाम जहाँ आपने सन्देश छोड़ा है। अनुभाग की जगह सिर्फ़ वार्ता पन्ने की कड़ी छोड़ने के लिये खाली छोड़ दें।',
					value: Twinkle.talkback.prev_section
				} );
			break;
		case 'usertalk':
			work_area.append( { 
					type:'input',
					name:'page',
					label:'सदस्य',
					tooltip:'उस सदस्य का नाम जिसके वार्ता पन्ने पर आपने सन्देश छोड़ा है।',
					value: Twinkle.talkback.prev_page
				} );
			
			work_area.append( { 
					type:'input',
					name:'section',
					label:'सम्बंधित अनुभाग (वैकल्पिक)',
					tooltip:'उस अनुभाग का नाम जहाँ आपने सन्देश छोड़ा है। अनुभाग की जगह सिर्फ़ वार्ता पन्ने की कड़ी छोड़ने के लिये खाली छोड़ दें।',
					value: Twinkle.talkback.prev_section
				} );
			break;
		case 'an':
			work_area.append( {
					type:'input',
					name:'section',
					label:'सम्बंधित अनुभाग (वैकल्पिक)',
					tooltip:'उस अनुभाग का नाम जहाँ आपने सन्देश छोड़ा है। अनुभाग की जगह सिर्फ़ सूचनापट की कड़ी छोड़ने के लिये खाली छोड़ दें।',
					value: Twinkle.talkback.prev_section
				} );
			break;
		case 'other':
			work_area.append( { 
					type:'input',
					name:'page',
					label:'पन्ने का पूरा नाम',
					tooltip:'उस पन्ने का पूरा नाम जिस पर आपने सन्देश छोड़ा है। उदहारण: "विकिपीडिया:चौपाल"।',
					value: Twinkle.talkback.prev_page
				} );
			
			work_area.append( { 
					type:'input',
					name:'section',
					label:'सम्बंधित अनुभाग (वैकल्पिक)',
					tooltip:'उस अनुभाग का नाम जहाँ आपने सन्देश छोड़ा है। अनुभाग की जगह सिर्फ़ वार्ता पन्ने की कड़ी छोड़ने के लिये खाली छोड़ दें।',
					value: Twinkle.talkback.prev_section
				} );
			break;
	}

	if (value !== "an") {
		work_area.append( { type:'textarea', label:'अतिरिक्त सन्देश (वैकल्पिक):', name:'message', tooltip:'कोई सन्देश जो आप सन्देश साँचे के बाद छोड़ना चाहेंगे।' } );
	}

	work_area = work_area.render();
	root.replaceChild( work_area, old_area );
	if (root.message) {
		root.message.value = Twinkle.talkback.prev_message;
	}
};

Twinkle.talkback.callback.evaluate = function friendlytalkbackCallbackEvaluate(e) {
	var tbtarget = e.target.getChecked( 'tbtarget' )[0];
	var page = null;
	var section = e.target.section.value;
	if( tbtarget === 'usertalk' || tbtarget === 'other' ) {
		page = e.target.page.value;
		
		if( tbtarget === 'usertalk' ) {
			if( !page ) {
				alert( 'आपको उस सदस्य का नाम बताना होगा जिसके वार्ता पन्ने पर आपने सन्देश छोड़ा है।' );
				return;
			}
		} else {
			if( !page ) {
				alert( 'यदि आपका सन्देश सदस्य वार्ता पन्ने की जगह किसी और पन्ने पर है तो आपको उस पन्ने का पूरा नाम बताना होगा।' );
				return;
			}
		}
	} else if (tbtarget === "an") {
		page = 'विकिपीडिया:प्रबंधक सूचनापट';
	}

	var message;
	if (e.target.message) {
		message = e.target.message.value;
	}

	Morebits.simpleWindow.setButtonsEnabled( false );
	Morebits.status.init( e.target );

	Morebits.wiki.actionCompleted.redirect = mw.config.get('wgPageName');
	Morebits.wiki.actionCompleted.notice = "सन्देश दे दिया, वार्ता पन्ना कुछ ही क्षणों में रीलोड होगा";

	var talkpage = new Morebits.wiki.page(mw.config.get('wgPageName'), "सन्देश जोड़ा जा रहा है");
	var tbPageName = (tbtarget === 'mytalk') ? mw.config.get('wgUserName') : page;

	var text;
	if ( tbtarget === "an" ) {
		text = "\n{{subst:ANI-notice|thread=";
		text += section + "|noticeboard=" + tbPageName + "}} --~~~~";

		talkpage.setEditSummary("प्रबंधक सूचनापट पर चर्चा का नोटिस" + Twinkle.getPref('summaryAd'));
	} else {
		//clean talkback heading: strip section header markers, were erroneously suggested in the documentation
		text = '\n==' + Twinkle.getFriendlyPref('talkbackHeading').replace(/^\s*=+\s*(.*?)\s*=+$\s*/, "$1") + '==\n{{सन्देश|';
		text += tbPageName;

			if( section ) {
				text += '|' + section;
			}
	
			text += '|ts=~~~~~}}';
	
			if( message ) {
				text += '\n' + message + '  ~~~~';
			} else if( Twinkle.getFriendlyPref('insertTalkbackSignature') ) {
				text += '\n~~~~';
		}
	
		talkpage.setEditSummary("सन्देश [[" + (tbtarget === 'other' ? '' : 'सदस्य वार्ता:') + tbPageName +
			(section ? ('#' + section) : '') + "]] पर" + Twinkle.getPref('summaryAd'));
	}

	talkpage.setAppendText(text);
	talkpage.setCreateOption('recreate');
	talkpage.setMinorEdit(Twinkle.getFriendlyPref('markTalkbackAsMinor'));
	talkpage.setFollowRedirect(true);
	talkpage.append();
};

/*
 ****************************************
 *** friendlywelcome.js: Welcome module
 ****************************************
 * Mode of invocation:     Tab ("Wel"), or from links on diff pages
 * Active on:              Existing user talk pages, diff pages
 * Config directives in:   FriendlyConfig
 */

Twinkle.welcome = function friendlywelcome() {
	if( Morebits.queryString.exists( 'friendlywelcome' ) ) {
		if( Morebits.queryString.get( 'friendlywelcome' ) === 'auto' ) {
			Twinkle.welcome.auto();
		} else {
			Twinkle.welcome.semiauto();
		}
	} else {
		Twinkle.welcome.normal();
	}
};

Twinkle.welcome.auto = function() {
	if( Morebits.queryString.get( 'action' ) !== 'edit' ) {
		// userpage not empty, aborting auto-welcome
		return;
	}

	Twinkle.welcome.welcomeUser();
};

Twinkle.welcome.semiauto = function() {
	Twinkle.welcome.callback( mw.config.get( 'wgTitle' ).split( '/' )[0].replace( /\"/, "\\\"") );
};

Twinkle.welcome.normal = function() {
	if( Morebits.queryString.exists( 'diff' ) ) {
		// check whether the contributors' talk pages exist yet
		var $oList = $("#mw-diff-otitle2").find("span.mw-usertoollinks a.new:contains(talk)").first();
		var $nList = $("#mw-diff-ntitle2").find("span.mw-usertoollinks a.new:contains(talk)").first();

		if( $oList.length > 0 || $nList.length > 0 ) {
			var spanTag = function( color, content ) {
				var span = document.createElement( 'span' );
				span.style.color = color;
				span.appendChild( document.createTextNode( content ) );
				return span;
			};

			var welcomeNode = document.createElement('strong');
			var welcomeLink = document.createElement('a');
			welcomeLink.appendChild( spanTag( 'Black', '[' ) );
			welcomeLink.appendChild( spanTag( 'Goldenrod', 'welcome' ) );
			welcomeLink.appendChild( spanTag( 'Black', ']' ) );
			welcomeNode.appendChild(welcomeLink);

			if( $oList.length > 0 ) {
				var oHref = $oList.attr("href");

				var oWelcomeNode = welcomeNode.cloneNode( true );
				oWelcomeNode.firstChild.setAttribute( 'href', oHref + '&' + Morebits.queryString.create( { 'friendlywelcome': Twinkle.getFriendlyPref('quickWelcomeMode')==='auto'?'auto':'norm' } ) + '&' + Morebits.queryString.create( { 'vanarticle': mw.config.get( 'wgPageName' ).replace(/_/g, ' ') } ) );
				$oList[0].parentNode.parentNode.appendChild( document.createTextNode( ' ' ) );
				$oList[0].parentNode.parentNode.appendChild( oWelcomeNode );
			}

			if( $nList.length > 0 ) {
				var nHref = $nList.attr("href");

				var nWelcomeNode = welcomeNode.cloneNode( true );
				nWelcomeNode.firstChild.setAttribute( 'href', nHref + '&' + Morebits.queryString.create( { 'friendlywelcome': Twinkle.getFriendlyPref('quickWelcomeMode')==='auto'?'auto':'norm' } ) + '&' + Morebits.queryString.create( { 'vanarticle': mw.config.get( 'wgPageName' ).replace(/_/g, ' ') } ) );
				$nList[0].parentNode.parentNode.appendChild( document.createTextNode( ' ' ) );
				$nList[0].parentNode.parentNode.appendChild( nWelcomeNode );
			}
		}
	}
	if( mw.config.get( 'wgNamespaceNumber' ) === 3 ) {
		var username = mw.config.get( 'wgTitle' ).split( '/' )[0].replace( /\"/, "\\\""); // only first part before any slashes
		twAddPortletLink( function(){ Twinkle.welcome.callback(username); }, "स्वागत", "friendly-welcome", "सदस्य स्वागत" );
	}
};

Twinkle.welcome.welcomeUser = function welcomeUser() {
	Morebits.status.init( document.getElementById('mw-content-text') );
	$( '#catlinks' ).remove();

	var params = {
		value: Twinkle.getFriendlyPref('quickWelcomeTemplate'),
		article: Morebits.queryString.exists( 'vanarticle' ) ? Morebits.queryString.get( 'vanarticle' ) : '',
		mode: 'auto'
	};

	Morebits.wiki.actionCompleted.redirect = mw.config.get('wgPageName');
	Morebits.wiki.actionCompleted.notice = "स्वागत संपूर्ण, वार्ता पन्ना कुछ ही क्षणों में रीलोड होगा";

	var wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), "User talk page modification");
	wikipedia_page.setFollowRedirect(true);
	wikipedia_page.setCallbackParameters(params);
	wikipedia_page.load(Twinkle.welcome.callbacks.main);
};

Twinkle.welcome.callback = function friendlywelcomeCallback( uid ) {
	if( uid === mw.config.get('wgUserName') ){
		alert( 'आपका बहुत बहुत स्वागत है!' );
		return;
	}	
	var Window = new Morebits.simpleWindow( 600, 400 );
	Window.setTitle( "सदस्य स्वागत" );
	Window.setScriptName( "Twinkle" );
	//Window.addFooterLink( "Welcoming Committee", "WP:WC" );
	Window.addFooterLink( "Twinkle help", "WP:TW/DOC#welcome" );

	var form = new Morebits.quickForm( Twinkle.welcome.callback.evaluate, 'change' );

	form.append( {
			type: 'input',
			name: 'article',
			label: 'सम्बन्धित लेख (यदि साँचे द्वारा स्वीकृत)',
			value:( QueryString.exists( 'vanarticle' ) ? QueryString.get( 'vanarticle' ) : '' ),
			tooltip: 'स्वागत में एक लेख की कड़ी जोड़ी जा सकती है, यदि स्वागत साँचे द्वारा स्वीकृत हो। ऐसे साँचों के आगे * लगा है। किसी भी लेख की कड़ी न जोड़ने के लिये खाली छोड़ दें।',
			event: function( event ) {
				event.stopPropagation();
			}
		} );

	form.append( { type:'header', label:'मानक स्वागत' } );
	form.append( { type: 'radio', name: 'Standard', list: Twinkle.welcome.StandardList } );
 
	form.append( { type:'header', label:'संक्षिप्त स्वागत' } );
	form.append( { type: 'radio', name: 'short', list: Twinkle.welcome.shortList } );

	form.append( {type:'header', label:'मूल जानकारी सहित स्वागत' } );
	form.append( {type: 'radio', name: 'basic', list: Twinkle.welcome.basicList } );

	form.append( {type:'header', label:'ग्राफ़िक मेन्यू सहित स्वागत' } );
	form.append( {type:'radio', name: 'graphic', list: Twinkle.welcome.graphicList } );
	
	if( Twinkle.getFriendlyPref('customWelcomeList').length ) {
		form.append( { type:'header', label:'Custom templates' } );
		form.append( { type: 'radio', name: 'custom', list: Twinkle.getFriendlyPref('customWelcomeList') } );
	}

	form.append( { type:'header', label:'Potential problem user templates' } );
	form.append( { type: 'radio', name: 'problem', list: Twinkle.welcome.problemList } );

	form.append( { type:'header', label:'IP सदस्य स्वागत' } );
	form.append( { type: 'radio', name: 'anonymous', list: Twinkle.welcome.anonymousList } );

	var result = form.render();
	Window.setContent( result );
	Window.display();

};

Twinkle.welcome.StandardList = [
	{
		label: '{{Welcome}}: मानक स्वागत*',//standard welcome
		value: 'Welcome'
	}
];	

Twinkle.welcome.shortList = [
	{ 
		label: '{{Welcomeshort}}: संक्षिप्त स्वागत',//short welcome
		value: 'Welcomeshort',
		tooltip: 'Includes section heading.'
	},
	{ 
		label: '{{W-short}}: concise; won\'t overwhelm',
		value: 'W-short||',
		tooltip: 'This template is similar to {{Welcomeshort}} but supports many different options.  Includes a signature.'
	},
	{ 
		label: '{{WelcomeSimple}}: सिम्पल स्वागत',//simple welcome
		value: 'WelcomeSimple',
		tooltip: 'Won\'t overwhelm new users.  Includes section heading.'
	}
];
 
Twinkle.welcome.basicList = [
	{
		label: '{{Welcome-personal}}: includes a plate of cookies',
		value: 'Welcome-personal',
		tooltip: 'A personal welcome with an introduction from you and a plate of cookies.  Includes section heading and signature.'
	},
	{ 
		label: '{{Welcome-belated}}: welcome for users with more substantial contributions',
		value: 'Welcome-belated'
	},
	{ 
		label: '{{W-basic}}: standard template, similar to {{Welcome}} with additional options',
		value: 'W-basic',
		tooltip: 'This template is similar to {{Welcome}} but supports many different options.  Includes a signature.'
	},
	{ 
		label: '{{W-shout}}: extroverted message with bold advice',
		value: 'W-shout',
		tooltip: 'This template is similar to {{WelcomeShout}} but supports many different options.  Includes a signature.'
	}
];
 
Twinkle.welcome.graphicList = [
	{ 
		label: '{{WelcomeMenu}}: कड़ियों की सूची के साथ स्वागत',//welcome with menu of links
		value: 'WelcomeMenu',
		tooltip: 'Contains a welcome message and many useful links broken up into different sections.  Includes signature.' 
	},
	{ 
		label: '{{Welcomeg}}: {{WelcomeMenu}} जैसा',//similar to {{WelcomeMenu}}
		value: 'Welcomeg',
		tooltip: 'Contains a welcome message and many useful links broken up into different sections.  Includes signature.'
	},
	{ 
		label: '{{Welcomeh}}: {{Welcomeg}} जैसा, अनुभाग नाम के साथ',//same as {{Welcomeg}} but with a section heading
		value: 'Welcomeh',
		tooltip: 'Contains a section heading, a welcome message and many useful links broken up into different sections.  Includes section heading and signature.'
	},
	{ 
		label: '{{W-graphical}}: graphical menu format to ease transition from the graphic-heavy web',
		value: 'W-graphical',
		tooltip: 'This template is similar to {{Welcomeg}} but has fewer links.  Supports many different options.  Includes a signature.'
	},
	{ 
		label: '{{W-screen}}: graphical; designed to fit the size of the user\'s screen',
		value: 'W-screen',
		tooltip: 'This template is a nice graphical welcome with many different options.  Includes a signature.'
	}
];

Twinkle.welcome.problemList = [
	{ 
		label: '{{Welcomelaws}}: welcome with information about copyrights, npov, the sandbox, and vandalism',
		value: 'Welcomelaws'
	},
	{ 
		label: '{{Firstarticle}}: for someone whose first article did not meet page creation guidelines*',
		value: 'Firstarticle'
	},
	{ 
		label: '{{Welcomevandal}}: for someone whose initial efforts appear to be vandalism*',
		value: 'Welcomevandal',
		tooltip: 'Includes a section heading.'
	},
	{ 
		label: '{{Welcomenpov}}: for someone whose initial efforts do not adhere to the neutral point of view policy*',
		value: 'Welcomenpov'
	},
	{ 
		label: '{{Welcomespam}}: welcome with additional discussion of anti-spamming policies*',
		value: 'Welcomespam'
	},
	{ 
		label: '{{Welcomeunsourced}}: for someone whose initial efforts are uncited*',
		value: 'Welcomeunsourced'
	},
	{ 
		label: '{{Welcomeauto}}: for someone who created an autobiographical article*',
		value: 'Welcomeauto'
	},
	{ 
		label: '{{Welcome-COI}}: for someone who created an article about a subject with which they have a conflict of interest*',
		value: 'Welcome-COI'
	}
];

Twinkle.welcome.anonymousList = [
	{
		label: '{{Welcome-anon}}: for anonymous users; encourages getting a username*',
		value: 'Welcome-anon'
	},
	{
		label: '{{Welcomeanon2}}: similar to {{Welcome-anon}} but with hints and tips*',
		value: 'Welcomeanon2',
		tooltip: 'Includes section heading.'
	},
	{
		label: '{{Welc-anon}}: similar to {{Welcome-anon}} but with a border and section heading',
		value: 'Welc-anon||',
		tooltip: 'Includes section heading.'
	},
	{
		label: '{{Welcome-anon-vandal}}: for anonymous users who have vandalized a page*',
		value: 'Welcome-anon-vandal',
		tooltip: 'Includes a section heading and signature.'
	},
	{
		label: '{{Welcome-anon-vandalism-fighter}}: for anonymous users who fight vandalism, urging them to create an account*',
		value: 'Welcome-anon-vandalism-fighter', 
		tooltip: 'Includes section heading.'
	}
];

// Set to true if template does not already have heading
Twinkle.welcome.headingHash = {
	'Welcome': true,
	'Welcomeshort': false,
	'WelcomeSimple': false,
	'Welcom': false,
	'Welcome-personal': false,
	'WelcomeMenu': true,
	'Welcomeg': true,
	'Welcomeh': false,
	'Welcome-belated': false,
	'W-basic': true,
	'W-shout': true,
	'W-short||': true,
	'W-graphical': true,
	'W-screen': true,
	'Welcomelaws': true,
	'Firstarticle': true,
	'Welcomevandal': false,
	'Welcomenpov': true,
	'Welcomespam': true,
	'Welcomeunsourced': true,
	'Welcomeauto': false,
	'Welcome-COI': true,
	'Welcome-anon': true,
	'Welcomeanon2': false,
	'Welc-anon||': false,
	'Welcome-anon-vandalism-fighter': false,
	'Welcome-anon-vandal': false
};

// Set to true if template already has signature
Twinkle.welcome.signatureHash = {
	'Welcome': false,
	'Welcomeshort': false,
	'WelcomeSimple': false,
	'Welcom': true,
	'Welcome-personal': false,
	'WelcomeMenu': true,
	'Welcomeg': true,
	'Welcomeh': true,
	'Welcome-belated': true,
	'W-basic': true,
	'W-shout': true,
	'W-short||': true,
	'W-graphical': true,
	'W-screen': true,
	'Welcomelaws': false,
	'Firstarticle': true,
	'Welcomevandal': true,
	'Welcomenpov': false,
	'Welcomespam': false,
	'Welcomeunsourced': false,
	'Welcome-COI': false,
	'Welcome-anon': false,
	'Welcomeanon2': false,
	'Welc-anon||': false,
	'Welcome-anon-vandalism-fighter': false,
	'Welcome-anon-vandal': true
};

/* Set to true if template supports article
 * name from art template parameter 
 */
Twinkle.welcome.artHash = {
	'Welcome': true,
	'Welcomeshort': false,
	'WelcomeSimple': false,
	'Welcom': false,
	'Welcome-personal': false,
	'WelcomeMenu': false,
	'Welcomeg': false,
	'Welcomeh': false,
	'Welcome-belated': false,
	'W-basic': false,
	'W-shout': false,
	'W-short||': false,
	'W-graphical': false,
	'W-screen': false,
	'Welcomelaws': false,
	'Firstarticle': false,
	'Welcomevandal': false,
	'Welcomenpov': false,
	'Welcomespam': false,
	'Welcomeunsourced': false,
	'Welcomeauto': true,
	'Welcome-COI': false,
	'Welcome-anon': true,
	'Welcomeanon2': true,
	'Welc-anon||': false,
	'Welcome-anon-vandalism-fighter': true,
	'Welcome-anon-vandal': false
};

/* Set to true if template supports article
 * name from vanarticle template parameter 
 */
Twinkle.welcome.vandalHash = {
	'Welcome': false,
	'Welcomeshort': false,
	'WelcomeSimple': false,
	'Welcom': false,
	'Welcome-personal': false,
	'WelcomeMenu': false,
	'Welcomeg': false,
	'Welcomeh': false,
	'Welcome-belated': false,
	'W-basic': false,
	'W-shout': false,
	'W-short||': false,
	'W-graphical': false,
	'W-screen': false,
	'Welcomelaws': false,
	'Firstarticle': true,
	'Welcomevandal': true,
	'Welcomenpov': true,
	'Welcomespam': true,
	'Welcomeunsourced': true,
	'Welcomeauto': false,
	'Welcome-COI': false,
	'Welcome-anon': false,
	'Welcomeanon2': false,
	'Welc-anon||': false,
	'Welcome-anon-vandalism-fighter': false,
	'Welcome-anon-vandal': true
};

Twinkle.welcome.callbacks = {
	main: function( pageobj ) {
		var params = pageobj.getCallbackParameters();
		var oldText = pageobj.getPageText();

		// abort if mode is auto and form is not empty
		if( pageobj.exists() && params.mode === 'auto' ) {
			Morebits.status.info( 'Warning', 'User talk page not empty; aborting automatic welcome' );
			Morebits.wiki.actionCompleted.event();
			return;
		}
		
		var text = '';
		Morebits.status.info( 'Info', 'स्वागत सन्देश सदस्य के वार्ता पन्ने के सबसे ' +
			( Twinkle.getFriendlyPref('topWelcomes') ? 'ऊपर' : 'नीचे' ) +
			' जोड़ा जाएगा।' );/*Will add the welcome template to the top or bottom of the user\'s talk page*/
		if( !Twinkle.getFriendlyPref('topWelcomes') ) {
			text += oldText + '\n';
		}
		
		if( Twinkle.welcome.headingHash[ params.value ] && Twinkle.getFriendlyPref('insertHeadings') ) {
			Morebits.status.info( 'Info', 'स्वागत सन्देश के लिये नया अनुभाग बनाया जाएगा' );//Will create a new heading for the welcome
			// strip section header markers from pref, to preserve backwards compatibility
			text += "== " + Twinkle.getFriendlyPref('welcomeHeading').replace(/^\s*=+\s*(.*?)\s*=+$\s*/, "$1") + " ==\n";
		}
		
		Morebits.status.info( 'Info', 'Will substitute the {{' + params.value + '}} welcome template' );
		text += '{{subst:' + params.value;
		
		if( Twinkle.welcome.artHash[ params.value ] ) {
			if( Twinkle.getFriendlyPref('insertUsername') && params.value.substring(2,0) !== 'W-' ) {
				Morebits.status.info( 'Info', 'स्वागत सन्देश में आपका सदस्य नाम जोड़ा जाएगा' );//Will add your username to the template
				text += '|' + mw.config.get('wgUserName');
			}
			
			if( params.article ) {
				Morebits.status.info( 'Info', 'स्वागत सन्देश में लेख की कड़ी जोड़ी जाएगी' );//Will add article link to the template
				text += '|art=' + params.article;
			}
		} else if( Twinkle.welcome.vandalHash[ params.value ] ) {
			if( params.article ) {
				Morebits.status.info( 'Info', 'स्वागत सन्देश में लेख की कड़ी जोड़ी जाएगी' );
			}
			text += '|' + params.article;
			
			if( Twinkle.getFriendlyPref('insertUsername') ) {
				Morebits.status.info( 'Info', 'स्वागत सन्देश में आपका सदस्य नाम जोड़ा जाएगा' );
				text += '|' + mw.config.get('wgUserName');
			}
		} else if( Twinkle.getFriendlyPref('insertUsername') ) {
			Morebits.status.info( 'Info', 'स्वागत सन्देश में आपका सदस्य नाम जोड़ा जाएगा' );
			text += '|' + mw.config.get('wgUserName');
		} 
		
		text += '}}';
		
		if( !Twinkle.welcome.signatureHash[ params.value ] && Twinkle.getFriendlyPref('insertSignature') ) {
			Morebits.status.info( 'Info', 'सन्देश के बाद आपके हस्ताक्षर जोड़े जाएँगे' );//Will add your signature after the welcome
			text += ' \n~~~~';
		}
		
		if( Twinkle.getFriendlyPref('topWelcomes') ) {
			text += '\n\n' + oldText;
		}
 
		var summaryText = "सदस्य के वार्ता पन्ने पर " + ( Twinkle.getFriendlyPref('maskTemplateInSummary') ? 'स्वागत सन्देश' : ( '{{[[साँचा:' + params.value + '|' + params.value + ']]}}' ) ) +
			" जोड़ा";
		pageobj.setPageText(text);
		pageobj.setEditSummary(summaryText + Twinkle.getPref('summaryAd'));
		pageobj.setWatchlist(Twinkle.getFriendlyPref('watchWelcomes'));
		pageobj.setCreateOption('recreate');
		pageobj.save();
	}
};

Twinkle.welcome.callback.evaluate = function friendlywelcomeCallbackEvaluate(e) {
	// Ignore if a change to the text field triggered this event
	if( e.target.name === 'article' ) {
		return;
	}

	var params = {
		value: e.target.values,
		article: e.target.form.article.value,
		mode: 'manual'
	};

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

	Morebits.wiki.actionCompleted.redirect = mw.config.get('wgPageName');
	Morebits.wiki.actionCompleted.notice = "स्वागत संपूर्ण, वार्ता पन्ना कुछ ही क्षणों में रीलोड होगा";

	var wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), "User talk page modification");
	wikipedia_page.setFollowRedirect(true);
	wikipedia_page.setCallbackParameters(params);
	wikipedia_page.load(Twinkle.welcome.callbacks.main);
};

/*
 ****************************************
 *** twinklearv.js: ARV module
 ****************************************
 * Mode of invocation:     Tab ("ARV")
 * Active on:              Existing and non-existing user pages, user talk pages, contributions pages
 * Config directives in:   TwinkleConfig
 */

Twinkle.arv = function twinklearv() {

	var username = Morebits.getPageAssociatedUser();
	if ( username === false ) {
		return;
	}

	var title = Morebits.isIPAddress( username ) ? 'Report IP to administrators' : 'Report user to administrators';

	twAddPortletLink( function(){ Twinkle.arv.callback(username); }, "ARV", "tw-arv", title );
};

Twinkle.arv.callback = function ( uid ) {
	if ( !twinkleUserAuthorized ) {
		alert("Your account is too new to use Twinkle.");
		return;
	}
	if ( uid === mw.config.get('wgUserName') ) {
		alert( 'You don\'t want to report yourself, do you?' );
		return;
	}

	var Window = new Morebits.simpleWindow( 600, 500 );
	Window.setTitle( "Advance Reporting and Vetting" ); //Backronym
	Window.setScriptName( "Twinkle" );
	Window.addFooterLink( "Guide to AIV", "WP:GAIV" );
	Window.addFooterLink( "UAA instructions", "WP:UAAI" );
	Window.addFooterLink( "About SPI", "WP:SPI" );
	Window.addFooterLink( "Twinkle help", "WP:TW/DOC#arv" );

	var form = new Morebits.quickForm( Twinkle.arv.callback.evaluate );
	var categories = form.append( {
			type: 'select',
			name: 'category',
			label: 'Select report type: ',
			event: Twinkle.arv.callback.changeCategory
		} );
	categories.append( {
			type: 'option',
			label: 'Vandalism (WP:AIV)',
			value: 'aiv'
		} );
	categories.append( {
			type: 'option',
			label: 'Username (WP:UAA)',
			value: 'username'
		} );
	categories.append( {
			type: 'option',
			label: 'Sockpuppeteer (WP:SPI)',
			value: 'sock'
		} );
	categories.append( {
			type: 'option',
			label: 'Sockpuppet (WP:SPI)',
			value: 'puppet'
		} );
	form.append( {
			type: 'field',
			label:'Work area',
			name: 'work_area'
		} );
	form.append( { type:'submit' } );
	form.append( {
			type: 'hidden',
			name: 'uid',
			value: uid
		} );
	
	var result = form.render();
	Window.setContent( result );
	Window.display();

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

Twinkle.arv.callback.changeCategory = function (e) {
	var value = e.target.value;
	var root = e.target.form;
	var old_area = Morebits.quickForm.getElements(root, "work_area")[0];
	var work_area = null;

	switch( value ) {
	case 'aiv':
		/* falls through */
	default:
		work_area = new Morebits.quickForm.element( { 
				type: 'field',
				label: 'Report user for vandalism',
				name: 'work_area'
			} );
		work_area.append( {
				type: 'input',
				name: 'page',
				label: 'Primary linked page: ',
				tooltip: 'Leave blank to not link to the page in the report',
				value: Morebits.queryString.exists( 'vanarticle' ) ? Morebits.queryString.get( 'vanarticle' ) : '',
				event: function(e) {
					var value = e.target.value;
					var root = e.target.form;
					if( value === '' ) {
						root.badid.disabled = root.goodid.disabled = true;
					} else {
						root.badid.disabled = false;
						root.goodid.disabled = root.badid.value === '';
					}
				}
			} );
		work_area.append( {
				type: 'input',
				name: 'badid',
				label: 'Revision ID for target page when vandalised: ',
				tooltip: 'Leave blank for no diff link',
				value: Morebits.queryString.exists( 'vanarticlerevid' ) ? Morebits.queryString.get( 'vanarticlerevid' ) : '',
				disabled: !Morebits.queryString.exists( 'vanarticle' ),
				event: function(e) {
					var value = e.target.value;
					var root = e.target.form;
					root.goodid.disabled = value === '';
				}
			} );
		work_area.append( {
				type: 'input',
				name: 'goodid',
				label: 'Last good revision ID before vandalism of target page: ',
				tooltip: 'Leave blank for diff link to previous revision',
				value: Morebits.queryString.exists( 'vanarticlegoodrevid' ) ? Morebits.queryString.get( 'vanarticlegoodrevid' ) : '',
				disabled: !Morebits.queryString.exists( 'vanarticle' ) || Morebits.queryString.exists( 'vanarticlerevid' )
			} );
		work_area.append( {
				type: 'checkbox',
				name: 'arvtype',
				list: [
					{ 
						label: 'Vandalism after final (level 4 or 4im) warning given',
						value: 'final'
					},
					{ 
						label: 'Vandalism after recent (within 1 day) release of block',
						value: 'postblock'
					},
					{ 
						label: 'Evidently a vandalism-only account',
						value: 'vandalonly',
						disabled: Morebits.isIPAddress( root.uid.value )
					},
					{ 
						label: 'Account is evidently a spambot or a compromised account',
						value: 'spambot'
					},
					{ 
						label: 'Account is a promotion-only account',
						value: 'promoonly'
					}
				]
			} );
		work_area.append( {
				type: 'textarea',
				name: 'reason',
				label: 'Comment: '
			} );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	case 'username':
		work_area = new Morebits.quickForm.element( { 
				type: 'field',
				label: 'Report username violation',
				name: 'work_area'
			} );
		work_area.append ( { 
				type:'header', 
				label:'Type(s) of inappropriate username',
				tooltip: 'Wikipedia does not allow usernames that are misleading, promotional, offensive or disruptive. Domain names and e-mail addresses are likewise prohibited. These criteria apply to both usernames and signatures. Usernames that are inappropriate in another language, or that represent an inappropriate name with misspellings and substitutions, or do so indirectly or by implication, are still considered inappropriate.'
			} );
		work_area.append( {
				type: 'checkbox',
				name: 'arvtype',
				list: [
					{
						label: 'Misleading username',
						value: 'misleading',
						tooltip: 'Misleading usernames imply relevant, misleading things about the contributor. For example, misleading points of fact, an impression of undue authority, or the suggestion that the account is operated by a group, project or collective rather than one individual.'
					},
					{ 
						label: 'Promotional username',
						value: 'promotional',
						tooltip: 'Promotional usernames are advertisements for a company, website or group. Please do not report these names to UAA unless the user has also made promotional edits related to the name.'
					},
					{ 
						label: 'Offensive username',
						value: 'offensive',
						tooltip: 'Offensive usernames make harmonious editing difficult or impossible.'
					},
					{ 
						label: 'Disruptive username',
						value: 'disruptive',
						tooltip: 'Disruptive usernames include outright trolling or personal attacks, or otherwise show a clear intent to disrupt Wikipedia.'
					}
				]
			} );
		work_area.append( {
				type: 'textarea',
				name: 'reason',
				label: 'Comment:'
			} );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;

	case 'puppet':
		work_area = new Morebits.quickForm.element( { 
				type: 'field',
				label: 'Report suspected sockpuppet',
				name: 'work_area'
			} );
		work_area.append(
			{
				type: 'input',
				name: 'sockmaster',
				label: 'Sockpuppeteer',
				tooltip: 'The username of the sockpuppeteer (sockmaster) without the User:-prefix'
			}
		);
		work_area.append( {
				type: 'textarea',
				label: 'Evidence:',
				name: 'evidence',
				tooltip: 'Enter your evidence. It should make clear that each of these users is likely to be abusing multiple accounts. Usually this means diffs, page histories or other information that justifies why the users are a) the same and b) disruptive. This should purely be evidence and information needed to judge the matter. Avoid all other discussion that is not evidence of sockpuppetry or other multiple account abuse.'
			} );
		work_area.append( {
				type: 'checkbox',
				list: [
					{
						label: 'Request CheckUser evidence',
						name: 'checkuser',
						tooltip: 'CheckUser is a tool used to obtain technical evidence related to a sock-puppetry allegation. It will not be used without good cause, which you must clearly demonstrate. Make sure your evidence explains why CheckUser is appropriate.'
					},
					{
						label: 'Notify reported users',
						name: 'notify',
						tooltip: 'Notification is not mandatory. In many cases, especially of chronic sockpuppeteers, notification may be counterproductive. However, especially in less egregious cases involving users who has not been reported before, notification may make the cases fairer and also appear to be fairer in the eyes of the accused. Use your judgment.'
					}
				]
			} );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	case 'sock':
		work_area = new Morebits.quickForm.element( { 
				type: 'field',
				label: 'Report suspected sockpuppeteer',
				name: 'work_area'
			} );
		work_area.append(
			{
				type: 'dyninput',
				name: 'sockpuppet',
				label: 'Sockpuppets',
				sublabel: 'Sock: ',
				tooltip: 'The username of the sockpuppet without the User:-prefix',
				min: 2
			}
		);
		work_area.append( {
				type: 'textarea',
				label: 'Evidence:',
				name: 'evidence',
				tooltip: 'Enter your evidence. It should make clear that each of these users is likely to be abusing multiple accounts. Usually this means diffs, page histories or other information that justifies why the users are a) the same and b) disruptive. This should purely be evidence and information needed to judge the matter. Avoid all other discussion that is not evidence of sockpuppetry or other multiple account abuse.'
			} );
		work_area.append( {
				type: 'checkbox',
				list: [ {
					label: 'Request CheckUser evidence',
					name: 'checkuser',
					tooltip: 'CheckUser is a tool used to obtain technical evidence related to a sock-puppetry allegation. It will not be used without good cause, which you must clearly demonstrate. Make sure your evidence explains why CheckUser is appropriate.'
				}, {
					label: 'Notify reported users',
					name: 'notify',
					tooltip: 'Notification is not mandatory. In many cases, especially of chronic sockpuppeteers, notification may be counterproductive. However, especially in less egregious cases involving users who has not been reported before, notification may make the cases fairer and also appear to be fairer in the eyes of the accused. Use your judgment.'
				} ]
			} );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	}
};

Twinkle.arv.callback.evaluate = function(e) {

	var form = e.target;
	var reason = "";
	var comment = "";
	if ( form.reason ) {
		comment = form.reason.value;
	}
	var uid = form.uid.value;

	var types;
	switch( form.category.value ) {

		// Report user for vandalism
		case 'aiv':
			/* falls through */
		default:
			types = form.getChecked( 'arvtype' );
			if( !types.length && comment === '' ) {
				alert( 'You must specify some reason' );
				return;
			}

			types = types.map( function(v) {
					switch(v) {
						case 'final':
							return 'vandalism after final warning';
						case 'postblock':
							return 'vandalism after recent release of block';
						case 'spambot':
							return 'account is evidently a spambot or a compromised account';
						case 'vandalonly':
							return 'actions evidently indicate a vandalism-only account';
						case 'promoonly':
							return 'account is being used only for promotional purposes';
						default:
							return 'unknown reason';
					}
				} ).join( '; ' );


			if ( form.page.value !== '' ) {
			
				// add a leading : on linked page namespace to prevent transclusion
				reason = 'On [[' + form.page.value.replace( /^(Image|Category|File):/i, ':$1:' ) + ']]';

				if ( form.badid.value !== '' ) {
					var query = {
						'title': form.page.value,
						'diff': form.badid.value,
						'oldid': form.goodid.value
					};
					reason += ' ({{diff|' + form.page.value + '|' + form.badid.value + '|' + form.goodid.value + '|diff}})';
				}
				reason += ':';
			}

			if ( types ) {
				reason += " " + types;
			}
			if (comment !== "" ) {
				reason += (reason === "" ? "" : ". ") + comment;
			}
			reason += ". ~~~~";
			reason = reason.replace(/\r?\n/g, "\n*:");  // indent newlines

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

			Morebits.wiki.actionCompleted.redirect = "Wikipedia:Administrator intervention against vandalism";
			Morebits.wiki.actionCompleted.notice = "Reporting complete";

			var aivPage = new Morebits.wiki.page( 'Wikipedia:Administrator intervention against vandalism', 'Processing AIV request' );
			aivPage.setPageSection( 1 );
			aivPage.setFollowRedirect( true );
			
			aivPage.load( function() {
				var text = aivPage.getPageText();

				// check if user has already been reported
				if (new RegExp( "\\{\\{\\s*(?:(?:[Ii][Pp])?[Vv]andal|[Uu]serlinks)\\s*\\|\\s*(?:1=)?\\s*" + RegExp.escape( uid, true ) + "\\s*\\}\\}" ).test(text)) {
					aivPage.getStatusElement().info( 'Report already present, will not add a new one' );
					return;
				}
				aivPage.getStatusElement().status( 'Adding new report...' );
				aivPage.setEditSummary( 'Reporting [[Special:Contributions/' + uid + '|' + uid + ']].' + Twinkle.getPref('summaryAd') );
				aivPage.setAppendText( '\n*{{' + ( Morebits.isIPAddress( uid ) ? 'IPvandal' : 'vandal' ) + '|' + (/\=/.test( uid ) ? '1=' : '' ) + uid + '}} &ndash; ' + reason );
				aivPage.append();
			} );
			break;
			
		// Report inappropriate username
		case 'username':
			types = form.getChecked( 'arvtype' );
			if( !types.length ) {
				alert( 'You must specify at least one breached violation' );
				return;
			}
			types = types.map( Morebits.string.toLowerCaseFirstChar );

			if ( types.length <= 2 ) {
				types = types.join( ' and ' );
			} else {
				types = [ types.slice( 0, -1 ).join( ', ' ), types.slice( -1 ) ].join( ' and ' );
			}
			var article = 'a';
			if ( /[aeiouwyh]/.test( types[0] ) ) { // non 100% correct, but whatever, inlcuding 'h' for Cockney
				article = 'an';
			}
			reason = "*{{user-uaa|1=" + uid + "}} &ndash; Violation of username policy as " + article + " " + types + " username. ";
			if (comment !== '' ) {
				reason += Morebits.string.toUpperCaseFirstChar(comment) + ". ";
			}
			reason += "~~~~";
			reason = reason.replace(/\r?\n/g, "\n*:");  // indent newlines

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

			Morebits.wiki.actionCompleted.redirect = "Wikipedia:Usernames for administrator attention";
			Morebits.wiki.actionCompleted.notice = "Reporting complete";

			var uaaPage = new Morebits.wiki.page( 'Wikipedia:Usernames for administrator attention', 'Processing UAA request' );
			uaaPage.setFollowRedirect( true );

			uaaPage.load( function() {
				var text = uaaPage.getPageText();
				
				// check if user has already been reported
				if (new RegExp( "\\{\\{\\s*user-uaa\\s*\\|\\s*(1\\s*=\\s*)?" + RegExp.escape(uid, true) + "\\s*(\\||\\})" ).test(text)) {
					uaaPage.getStatusElement().error( 'User is already listed.' );
					return;
				}
				uaaPage.getStatusElement().status( 'Adding new report...' );
				uaaPage.setEditSummary( 'Reporting [[Special:Contributions/' + uid + '|' + uid + ']].'+ Twinkle.getPref('summaryAd') );
				uaaPage.setPageText( text.replace( /List begins below this line.\s*-->/, "List begins below this line.\n-->\n" + reason ) );  // add at top
				uaaPage.save();
			} );
			break;
			
		// WP:SPI
		case "sock":
			/* falls through */
		case "puppet":
			var sockParameters = {
				evidence: form.evidence.value.trimRight(), 
				checkuser: form.checkuser.checked, 
				notify: form.notify.checked
			};

			var puppetReport = form.category.value === "puppet";
			if (puppetReport && !(form.sockmaster.value.trim())) {
				if (!confirm("You have not entered a sockmaster account for this puppet. Do you want to report this account as a sockpuppeteer instead?")) {
					return;
				}
				puppetReport = false;
			}

			sockParameters.uid = puppetReport ? form.sockmaster.value.trimRight() : uid;
			sockParameters.sockpuppets = puppetReport ? [uid] : $.map( $('input:text[name=sockpuppet]',form), function(o){ return $(o).val(); });

			Morebits.simpleWindow.setButtonsEnabled( false );
			Morebits.status.init( form );
			Twinkle.arv.processSock( sockParameters );
			break;

	}
};

Twinkle.arv.processSock = function( params ) {
	Morebits.wiki.addCheckpoint(); // prevent notification events from causing an erronous "action completed"
	
	// notify all user accounts if requested
	if (params.notify && params.sockpuppets.length>0) {
	
		var notifyEditSummary = "Notifying about suspicion of sockpuppeteering." + Twinkle.getPref('summaryAd');
		var notifyText = "\n\n{{subst:socksuspectnotice|1=" + params.uid + "}} ~~~~";
		
		// notify user's master account
		var masterTalkPage = new Morebits.wiki.page( 'User talk:' + params.uid, 'Notifying suspected sockpuppeteer' );
		masterTalkPage.setFollowRedirect( true );
		masterTalkPage.setEditSummary( notifyEditSummary );
		masterTalkPage.setAppendText( notifyText );
		masterTalkPage.append();

		var statusIndicator = new Morebits.status( 'Notifying suspected sockpuppets', '0%' );
		var total = params.sockpuppets.length;
		var current =   0;
		
		// display status of notifications as they progress
		var onSuccess = function( sockTalkPage ) {
			var now = parseInt( 100 * ++(current)/total, 10 ) + '%';
			statusIndicator.update( now );
			sockTalkPage.getStatusElement().unlink();
			if ( current >= total ) {
				statusIndicator.info( now + ' (completed)' );
			}
		};
		
		var socks = params.sockpuppets;

		// notify each puppet account
		for( var i = 0; i < socks.length; ++i ) {
			var sockTalkPage = new Morebits.wiki.page( 'User talk:' + socks[i], "Notification for " +  socks[i] );
			sockTalkPage.setFollowRedirect( true );
			sockTalkPage.setEditSummary( notifyEditSummary );
			sockTalkPage.setAppendText( notifyText );
			sockTalkPage.append( onSuccess );
		}
	}

	// prepare the SPI report
	var text = "\n\n{{subst:SPI report|socksraw=" +
		params.sockpuppets.map( function(v) { 
				return "* {{" + ( Morebits.isIPAddress( v ) ? "checkip" : "checkuser" ) + "|1=" + v + "}}";
			} ).join( "\n" ) + "\n|evidence=" + params.evidence + " \n";
		
	if ( params.checkuser ) {
		text += "|checkuser=yes";
	}
	text += "}}";

	var reportpage = 'Wikipedia:Sockpuppet investigations/' + params.uid;

	Morebits.wiki.actionCompleted.redirect = reportpage;
	Morebits.wiki.actionCompleted.notice = "Reporting complete";

	var spiPage = new Morebits.wiki.page( reportpage, 'Retrieving discussion page' );
	spiPage.setFollowRedirect( true );
	spiPage.setEditSummary( 'Adding new report for [[Special:Contributions/' + params.uid + '|' + params.uid + ']].'+ Twinkle.getPref('summaryAd') );
	spiPage.setAppendText( text );
	spiPage.append();
	
	Morebits.wiki.removeCheckpoint();  // all page updates have been started
};

/*
 ****************************************
 *** twinklebatchdelete.js: Batch delete module (sysops only)
 ****************************************
 * Mode of invocation:     Tab ("D-batch")
 * Active on:              Existing and non-existing non-articles, and Special:PrefixIndex
 * Config directives in:   TwinkleConfig
 */


Twinkle.batchdelete = function twinklebatchdelete() {
	if( Morebits.userIsInGroup( 'sysop' ) && (mw.config.get( 'wgNamespaceNumber' ) > 0 || mw.config.get( 'wgCanonicalSpecialPageName' ) === 'Prefixindex') ) {
		twAddPortletLink( Twinkle.batchdelete.callback, "D-batch", "tw-batch", "Delete pages found in this category/on this page" );
	}
};

Twinkle.batchdelete.unlinkCache = {};
Twinkle.batchdelete.callback = function twinklebatchdeleteCallback() {
	var Window = new Morebits.simpleWindow( 800, 400 );
	Window.setTitle( "Batch deletion" );
	Window.setScriptName( "Twinkle" );
	Window.addFooterLink( "Twinkle help", "WP:TW/DOC#batchdelete" );

	var form = new Morebits.quickForm( Twinkle.batchdelete.callback.evaluate );
	form.append( {
			type: 'checkbox',
			list: [
				{ 
					label: 'Delete pages',
					name: 'delete_page',
					value: 'delete',
					checked: true
				},
				{
					label: 'Remove backlinks to the page',
					name: 'unlink_page',
					value: 'unlink',
					checked: true
				},
				{
					label: 'Delete redirects to deleted pages',
					name: 'delete_redirects',
					value: 'delete_redirects',
					checked: true
				}
			]
		} );
	form.append( {
			type: 'textarea',
			name: 'reason',
			label: 'Reason: '
		} );

	var query;
	if( mw.config.get( 'wgNamespaceNumber' ) === 14 ) {  // Category:

		query = {
			'action': 'query',
			'generator': 'categorymembers',
			'gcmtitle': mw.config.get( 'wgPageName' ),
			'gcmlimit' : Twinkle.getPref('batchMax'), // the max for sysops
			'prop': [ 'categories', 'revisions' ],
			'rvprop': [ 'size' ]
		};
	} else if( mw.config.get( 'wgCanonicalSpecialPageName' ) === 'Prefixindex' ) {

		var gapnamespace, gapprefix;
		if(Morebits.queryString.exists( 'from' ) )
		{
			gapnamespace = Morebits.queryString.get( 'namespace' );
			gapprefix = Morebits.string.toUpperCaseFirstChar( Morebits.queryString.get( 'from' ) );
		}
		else
		{
			var pathSplit = location.pathname.split('/');
			if (pathSplit.length < 3 || pathSplit[2] !== "Special:PrefixIndex") {
				return;
			}
			var titleSplit = pathSplit[3].split(':');
			gapnamespace = mw.config.get("wgNamespaceIds")[titleSplit[0].toLowerCase()];
			if ( titleSplit.length < 2 || typeof gapnamespace === 'undefined' )
			{
				gapnamespace = 0;  // article namespace
				gapprefix = pathSplit.splice(3).join('/');
			}
			else
			{
				pathSplit = pathSplit.splice(4);
				pathSplit.splice(0,0,titleSplit.splice(1).join(':'));
				gapprefix = pathSplit.join('/');
			}
		}

		query = {
			'action': 'query',
			'generator': 'allpages',
			'gapnamespace': gapnamespace ,
			'gapprefix': gapprefix,
			'gaplimit' : Twinkle.getPref('batchMax'), // the max for sysops
			'prop' : ['categories', 'revisions' ],
			'rvprop': [ 'size' ]
		};
	} else {
		query = {
			'action': 'query',
			'generator': 'links',
			'titles': mw.config.get( 'wgPageName' ),
			'gpllimit' : Twinkle.getPref('batchMax'), // the max for sysops
			'prop': [ 'categories', 'revisions' ],
			'rvprop': [ 'size' ]
		};
	}

	var wikipedia_api = new Morebits.wiki.api( 'Grabbing pages', query, function( self ) {
			var xmlDoc = self.responseXML;
			var snapshot = xmlDoc.evaluate('//page[@ns != "6" and not(@missing)]', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );  // 6 = File: namespace
			var list = [];
			for ( var i = 0; i < snapshot.snapshotLength; ++i ) {
				var object = snapshot.snapshotItem(i);
				var page = xmlDoc.evaluate( '@title', object, null, XPathResult.STRING_TYPE, null ).stringValue;
				var size = xmlDoc.evaluate( 'revisions/rev/@size', object, null, XPathResult.NUMBER_TYPE, null ).numberValue;

				var disputed = xmlDoc.evaluate( 'boolean(categories/cl[@title="Category:Contested candidates for speedy deletion"])', object, null, XPathResult.BOOLEAN_TYPE, null ).booleanValue;
				list.push( {label:page + ' (' + size + ' bytes)' + ( disputed ? ' (DISPUTED CSD)' : '' ), value:page, checked:!disputed });
			}
			self.params.form.append( {
					type: 'checkbox',
					name: 'pages',
					list: list
				} );
			self.params.form.append( { type:'submit' } );

			var result = self.params.form.render();
			self.params.Window.setContent( result );
		} );

	wikipedia_api.params = { form:form, Window:Window };
	wikipedia_api.post();
	var root = document.createElement( 'div' );
	Morebits.status.init( root );
	Window.setContent( root );
	Window.display();
};

Twinkle.batchdelete.currentDeleteCounter = 0;
Twinkle.batchdelete.currentUnlinkCounter = 0;
Twinkle.batchdelete.currentdeletor = 0;
Twinkle.batchdelete.callback.evaluate = function twinklebatchdeleteCallbackEvaluate(event) {
	Morebits.wiki.actionCompleted.notice = 'Status';
	Morebits.wiki.actionCompleted.postfix = 'batch deletion is now complete';
	mw.config.set('wgPageName', mw.config.get('wgPageName').replace(/_/g, ' '));  // for queen/king/whatever and country!
	var pages = event.target.getChecked( 'pages' );
	var reason = event.target.reason.value;
	var delete_page = event.target.delete_page.checked;
	var unlink_page = event.target.unlink_page.checked;
	var delete_redirects = event.target.delete_redirects.checked;
	if( ! reason ) {
		return;
	}
	Morebits.simpleWindow.setButtonsEnabled( false );
	Morebits.status.init( event.target );
	if( !pages ) {
		Morebits.status.error( 'Error', 'nothing to delete, aborting' );
		return;
	}

	function toCall( work ) {
		if( work.length === 0 &&  Twinkle.batchdelete.currentDeleteCounter <= 0 && Twinkle.batchdelete.currentUnlinkCounter <= 0 ) {
			window.clearInterval( Twinkle.batchdelete.currentdeletor );
			Morebits.wiki.removeCheckpoint();
			return;
		} else if( work.length !== 0 && ( Twinkle.batchdelete.currentDeleteCounter <= Twinkle.getPref('batchDeleteMinCutOff') || Twinkle.batchdelete.currentUnlinkCounter <= Twinkle.getPref('batchDeleteMinCutOff')  ) ) {
			Twinkle.batchdelete.unlinkCache = []; // Clear the cache
			var pages = work.shift();
			Twinkle.batchdelete.currentDeleteCounter += pages.length;
			Twinkle.batchdelete.currentUnlinkCounter += pages.length;
			for( var i = 0; i < pages.length; ++i ) {
				var page = pages[i];
				var query = {
					'action': 'query',
					'titles': page
				};
				var wikipedia_api = new Morebits.wiki.api( 'Checking if page ' + page + ' exists', query, Twinkle.batchdelete.callbacks.main );
				wikipedia_api.params = { page:page, reason:reason, unlink_page:unlink_page, delete_page:delete_page, delete_redirects:delete_redirects };
				wikipedia_api.post();
			}
		}
	}
	var work = Morebits.array.chunk( pages, Twinkle.getPref('batchdeleteChunks') );
	Morebits.wiki.addCheckpoint();
	Twinkle.batchdelete.currentdeletor = window.setInterval( toCall, 1000, work );
};

Twinkle.batchdelete.callbacks = {
	main: function( self ) {
		var xmlDoc = self.responseXML;
		var normal = xmlDoc.evaluate( '//normalized/n/@to', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
		if( normal ) {
			self.params.page = normal;
		}
		var exists = xmlDoc.evaluate( 'boolean(//pages/page[not(@missing)])', xmlDoc, null, XPathResult.BOOLEAN_TYPE, null ).booleanValue;

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

		var query, wikipedia_api;
		if( self.params.unlink_page ) {
			query = {
				'action': 'query',
				'list': 'backlinks',
				'blfilterredir': 'nonredirects',
				'blnamespace': [0, 100], // main space and portal space only
				'bltitle': self.params.page,
				'bllimit': Morebits.userIsInGroup( 'sysop' ) ? 5000 : 500 // 500 is max for normal users, 5000 for bots and sysops
			};
			wikipedia_api = new Morebits.wiki.api( 'Grabbing backlinks', query, Twinkle.batchdelete.callbacks.unlinkBacklinksMain );
			wikipedia_api.params = self.params;
			wikipedia_api.post();
		} else {
			--Twinkle.batchdelete.currentUnlinkCounter;
		}
		if( self.params.delete_page ) {
			if (self.params.delete_redirects)
			{
				query = {
					'action': 'query',
					'list': 'backlinks',
					'blfilterredir': 'redirects',
					'bltitle': self.params.page,
					'bllimit': Morebits.userIsInGroup( 'sysop' ) ? 5000 : 500 // 500 is max for normal users, 5000 for bots and sysops
				};
				wikipedia_api = new Morebits.wiki.api( 'Grabbing redirects', query, Twinkle.batchdelete.callbacks.deleteRedirectsMain );
				wikipedia_api.params = self.params;
				wikipedia_api.post();
			}

			var wikipedia_page = new Morebits.wiki.page( self.params.page, 'Deleting page ' + self.params.page );
			wikipedia_page.setEditSummary(self.params.reason + Twinkle.getPref('deletionSummaryAd'));
			wikipedia_page.deletePage(function( apiobj ) { 
					--Twinkle.batchdelete.currentDeleteCounter;
					var link = document.createElement( 'a' );
					link.setAttribute( 'href', mw.util.wikiGetlink(self.params.page) );
					link.setAttribute( 'title', self.params.page );
					link.appendChild( document.createTextNode( self.params.page ) );
					apiobj.statelem.info( [ 'completed (' , link , ')' ] );
				} );	
		} else {
			--Twinkle.batchdelete.currentDeleteCounter;
		}
	},
	deleteRedirectsMain: function( self ) {
		var xmlDoc = self.responseXML;
		var snapshot = xmlDoc.evaluate('//backlinks/bl/@title', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );

		var total = snapshot.snapshotLength;

		if( snapshot.snapshotLength === 0 ) {
			return;
		}

		var statusIndicator = new Morebits.status('Deleting redirects for ' + self.params.page, '0%');

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


		Morebits.wiki.addCheckpoint();
		if( snapshot.snapshotLength === 0 ) {
			statusIndicator.info( '100% (completed)' );
			Morebits.wiki.removeCheckpoint();
			return;
		}

		var params = $.extend({}, self.params);
		params.current = 0;
		params.total = total;
		params.obj = statusIndicator;


		for ( var i = 0; i < snapshot.snapshotLength; ++i ) {
			var title = snapshot.snapshotItem(i).value;
			var wikipedia_page = new Morebits.wiki.page( title, "Deleting " + title );
			wikipedia_page.setEditSummary('[[WP:CSD#G8|G8]]: Redirect to deleted page "' + self.params.page + '"' + Twinkle.getPref('deletionSummaryAd'));
			wikipedia_page.setCallbackParameters(params);
			wikipedia_page.deletePage(onsuccess);
		}
	},
	unlinkBacklinksMain: function( self ) {
		var xmlDoc = self.responseXML;
		var snapshot = xmlDoc.evaluate('//backlinks/bl/@title', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );

		if( snapshot.snapshotLength === 0 ) {
			--Twinkle.batchdelete.currentUnlinkCounter;
			return;
		}

		var statusIndicator = new Morebits.status('Unlinking backlinks to ' + self.params.page, '0%');

		var total = snapshot.snapshotLength * 2;

		var onsuccess = function( self ) {
			var obj = self.params.obj;
			var total = self.params.total;
			var now = parseInt( 100 * ++(self.params.current)/total, 10 ) + '%';
			obj.update( now );
			self.statelem.unlink();
			if( self.params.current >= total ) {
				obj.info( now + ' (completed)' );
				--Twinkle.batchdelete.currentUnlinkCounter;
				Morebits.wiki.removeCheckpoint();
			}
		};

		Morebits.wiki.addCheckpoint();
		if( snapshot.snapshotLength === 0 ) {
			statusIndicator.info( '100% (completed)' );
			--Twinkle.batchdelete.currentUnlinkCounter;
			Morebits.wiki.removeCheckpoint();
			return;
		}
		self.params.total = total;
		self.params.obj = statusIndicator;
		self.params.current =   0;

		for ( var i = 0; i < snapshot.snapshotLength; ++i ) {
			var title = snapshot.snapshotItem(i).value;
			var wikipedia_page = new Morebits.wiki.page( title, "Unlinking on " + title );
			var params = $.extend( {}, self.params );
			params.title = title;
			params.onsuccess = onsuccess;
			wikipedia_page.setCallbackParameters(params);
			wikipedia_page.load(Twinkle.batchdelete.callbacks.unlinkBacklinks);
		}
	},
	unlinkBacklinks: function( pageobj ) {
		var params = pageobj.getCallbackParameters();
		if( ! pageobj.exists() ) {
			// we probably just deleted it, as a recursive backlink
			params.onsuccess( { params: params, statelem: pageobj.getStatusElement() } );
			Morebits.wiki.actionCompleted();
			return;
		}
		var text;

		if( params.title in Twinkle.batchdelete.unlinkCache ) {
			text = Twinkle.batchdelete.unlinkCache[ params.title ];
		} else {
			text = pageobj.getPageText();
		}
		var old_text = text;
		var wikiPage = new Morebits.wikitext.page( text );
		wikiPage.removeLink( params.page );

		text = wikiPage.getText();
		Twinkle.batchdelete.unlinkCache[ params.title ] = text;
		if( text === old_text ) {
			// Nothing to do, return
			params.onsuccess( { params: params, statelem: pageobj.getStatusElement() } );
			Morebits.wiki.actionCompleted();
			return;
		}
		pageobj.setEditSummary('Removing link(s) to deleted page ' + self.params.page + Twinkle.getPref('deletionSummaryAd'));
		pageobj.setPageText(text);
		pageobj.setCreateOption('nocreate');
		pageobj.save(params.onsuccess);
	}
};

/*
 ****************************************
 *** twinklebatchprotect.js: Batch protect module (sysops only)
 ****************************************
 * Mode of invocation:     Tab ("P-batch")
 * Active on:              Existing project pages and user pages; existing and
 *                         non-existing categories; Special:PrefixIndex
 * Config directives in:   TwinkleConfig
 */


Twinkle.batchprotect = function twinklebatchprotect() {
	if( Morebits.userIsInGroup( 'sysop' ) && ((mw.config.get( 'wgArticleId' ) > 0 && (mw.config.get( 'wgNamespaceNumber' ) === 2 ||
		mw.config.get( 'wgNamespaceNumber' ) === 4)) || mw.config.get( 'wgNamespaceNumber' ) === 14 ||
		mw.config.get( 'wgCanonicalSpecialPageName' ) === 'Prefixindex') ) {
		twAddPortletLink( Twinkle.batchprotect.callback, "P-batch", "tw-pbatch", "Protect pages linked from this page" );
	}
};

Twinkle.batchprotect.unlinkCache = {};
Twinkle.batchprotect.callback = function twinklebatchprotectCallback() {
	var Window = new Morebits.simpleWindow( 800, 400 );
	Window.setTitle( "Batch protection" );
	Window.setScriptName( "Twinkle" );
	//Window.addFooterLink( "Protection templates", "Template:Protection templates" );
	Window.addFooterLink( "Protection policy", "WP:PROT" );
	Window.addFooterLink( "Twinkle help", "WP:TW/DOC#protect" );

	var form = new Morebits.quickForm( Twinkle.batchprotect.callback.evaluate );
	form.append({
			type: 'checkbox',
			name: 'editmodify',
			event: Twinkle.protect.formevents.editmodify,
			list: [
				{
					label: 'Modify edit protection',
					value: 'editmodify',
					tooltip: 'Only for existing pages.',
					checked: true
				}
			]
		});
	var editlevel = form.append({
			type: 'select',
			name: 'editlevel',
			label: 'Edit protection:',
			event: Twinkle.protect.formevents.editlevel
		});
	editlevel.append({
			type: 'option',
			label: 'All',
			value: 'all'
		});
	editlevel.append({
			type: 'option',
			label: 'Autoconfirmed',
			value: 'autoconfirmed'
		});
	editlevel.append({
			type: 'option',
			label: 'Sysop',
			value: 'sysop',
			selected: true
		});
	form.append({
			type: 'select',
			name: 'editexpiry',
			label: 'Expires:',
			event: function(e) {
				if (e.target.value === 'custom') {
					Twinkle.protect.doCustomExpiry(e.target);
				}
			},
			list: [
				{ label: '1 hour', value: '1 hour' },
				{ label: '2 hours', value: '2 hours' },
				{ label: '3 hours', value: '3 hours' },
				{ label: '6 hours', value: '6 hours' },
				{ label: '12 hours', value: '12 hours' },
				{ label: '1 day', value: '1 day' },
				{ label: '2 days', selected: true, value: '2 days' },
				{ label: '3 days', value: '3 days' },
				{ label: '4 days', value: '4 days' },
				{ label: '1 week', value: '1 week' },
				{ label: '2 weeks', value: '2 weeks' },
				{ label: '1 month', value: '1 month' },
				{ label: '2 months', value: '2 months' },
				{ label: '3 months', value: '3 months' },
				{ label: '1 year', value: '1 year' },
				{ label: 'indefinite', value:'indefinite' },
				{ label: 'Custom...', value: 'custom' }
			]
		});

	form.append({
			type: 'checkbox',
			name: 'movemodify',
			event: Twinkle.protect.formevents.movemodify,
			list: [
				{
					label: 'Modify move protection',
					value: 'movemodify',
					tooltip: 'Only for existing pages.',
					checked: true
				}
			]
		});
	var movelevel = form.append({
			type: 'select',
			name: 'movelevel',
			label: 'Move protection:',
			event: Twinkle.protect.formevents.movelevel
		});
	movelevel.append({
			type: 'option',
			label: 'All',
			value: 'all'
		});
	movelevel.append({
			type: 'option',
			label: 'Autoconfirmed',
			value: 'autoconfirmed'
		});
	movelevel.append({
			type: 'option',
			label: 'Sysop',
			value: 'sysop',
			selected: true
		});
	form.append({
			type: 'select',
			name: 'moveexpiry',
			label: 'Expires:',
			event: function(e) {
				if (e.target.value === 'custom') {
					Twinkle.protect.doCustomExpiry(e.target);
				}
			},
			list: [
				{ label: '1 hour', value: '1 hour' },
				{ label: '2 hours', value: '2 hours' },
				{ label: '3 hours', value: '3 hours' },
				{ label: '6 hours', value: '6 hours' },
				{ label: '12 hours', value: '12 hours' },
				{ label: '1 day', value: '1 day' },
				{ label: '2 days', selected: true, value: '2 days' },
				{ label: '3 days', value: '3 days' },
				{ label: '4 days', value: '4 days' },
				{ label: '1 week', value: '1 week' },
				{ label: '2 weeks', value: '2 weeks' },
				{ label: '1 month', value: '1 month' },
				{ label: '2 months', value: '2 months' },
				{ label: '3 months', value: '3 months' },
				{ label: '1 year', value: '1 year' },
				{ label: 'indefinite', value:'indefinite' },
				{ label: 'Custom...', value: 'custom' }
			]
		});

	form.append({
			type: 'checkbox',
			name: 'createmodify',
			event: function twinklebatchprotectFormCreatemodifyEvent(e) {
				e.target.form.createlevel.disabled = !e.target.checked;
				e.target.form.createexpiry.disabled = !e.target.checked || (e.target.form.createlevel.value === 'all');
				e.target.form.createlevel.style.color = e.target.form.createexpiry.style.color = (e.target.checked ? "" : "transparent");
			},
			list: [
				{
					label: 'Modify create protection',
					value: 'createmodify',
					tooltip: 'Only for pages that do not exist.',
					checked: true
				}
			]
		});
	var createlevel = form.append({
			type: 'select',
			name: 'createlevel',
			label: 'Create protection:',
			event: Twinkle.protect.formevents.createlevel
		});
	createlevel.append({
			type: 'option',
			label: 'All',
			value: 'all'
		});
	createlevel.append({
			type: 'option',
			label: 'Autoconfirmed',
			value: 'autoconfirmed'
		});
	createlevel.append({
			type: 'option',
			label: 'Sysop',
			value: 'sysop',
			selected: true
		});
	form.append({
			type: 'select',
			name: 'createexpiry',
			label: 'Expires:',
			event: function(e) {
				if (e.target.value === 'custom') {
					Twinkle.protect.doCustomExpiry(e.target);
				}
			},
			list: [
				{ label: '1 hour', value: '1 hour' },
				{ label: '2 hours', value: '2 hours' },
				{ label: '3 hours', value: '3 hours' },
				{ label: '6 hours', value: '6 hours' },
				{ label: '12 hours', value: '12 hours' },
				{ label: '1 day', value: '1 day' },
				{ label: '2 days', value: '2 days' },
				{ label: '3 days', value: '3 days' },
				{ label: '4 days', value: '4 days' },
				{ label: '1 week', value: '1 week' },
				{ label: '2 weeks', value: '2 weeks' },
				{ label: '1 month', value: '1 month' },
				{ label: '2 months', value: '2 months' },
				{ label: '3 months', value: '3 months' },
				{ label: '1 year', value: '1 year' },
				{ label: 'indefinite', selected: true, value: 'indefinite' },
				{ label: 'Custom...', value: 'custom' }
			]
		});

	form.append( {
			type: 'textarea',
			name: 'reason',
			label: 'Reason (for protection log): '
		} );

	var query;

	if( mw.config.get( 'wgNamespaceNumber' ) === 14 ) {  // categories
		query = {
			'action': 'query',
			'generator': 'categorymembers',
			'gcmtitle': mw.config.get( 'wgPageName' ),
			'gcmlimit' : Twinkle.getPref('batchMax'), // the max for sysops
			'prop': 'revisions',
			'rvprop': 'size'
		};
	} else if( mw.config.get( 'wgCanonicalSpecialPageName' ) === 'Prefixindex' ) {
		query = {
			'action': 'query',
			'generator': 'allpages',
			'gapnamespace': Morebits.queryString.exists('namespace') ? Morebits.queryString.get( 'namespace' ) : document.getElementById('namespace').value,
			'gapprefix': Morebits.queryString.exists('from') ? Morebits.string.toUpperCaseFirstChar(Morebits.queryString.get( 'from' ).replace('+', ' ')) :
				Morebits.string.toUpperCaseFirstChar(document.getElementById('nsfrom').value),
			'gaplimit' : Twinkle.getPref('batchMax'), // the max for sysops
			'prop': 'revisions',
			'rvprop': 'size'
		};
	} else {
		query = {
			'action': 'query',
			'gpllimit' : Twinkle.getPref('batchMax'), // the max for sysops
			'generator': 'links',
			'titles': mw.config.get( 'wgPageName' ),
			'prop': 'revisions',
			'rvprop': 'size'
		};
	}

	var statusdiv = document.createElement("div");
	statusdiv.style.padding = '15px';  // just so it doesn't look broken
	Window.setContent(statusdiv);
	Morebits.status.init(statusdiv);
	Window.display();

	var statelem = new Morebits.status("Grabbing list of pages");

	var wikipedia_api = new Morebits.wiki.api( 'loading...', query, function(apiobj) {
			var xml = apiobj.responseXML;
			var $pages = $(xml).find('page');
			var list = [];
			$pages.each(function(index, page) {
				var $page = $(page);
				var title = $page.attr('title');
				var isRedir = $page.attr('redirect') === ""; // XXX ??
				var missing = $page.attr('missing') === ""; // XXX ??
				var size = $page.find('rev').attr('size');

				var metadata = [];
				if (missing) {
					metadata.push("page does not exist");
				} else {
					if (isRedir) {
						metadata.push("redirect");
					}
					metadata.push(size + " bytes");
				}
				list.push( { label: title + (metadata.length ? (' (' + metadata.join('; ') + ')') : '' ), value: title, checked: true });
			});
			form.append({ type: 'header', label: 'Pages to protect' });
			form.append( {
					type: 'checkbox',
					name: 'pages',
					list: list
				} );
			form.append( { type:'submit' } );

			var result = form.render();
			Window.setContent( result );
		}, statelem );

	wikipedia_api.post();
};

Twinkle.batchprotect.currentProtectCounter = 0;
Twinkle.batchprotect.currentprotector = 0;
Twinkle.batchprotect.callback.evaluate = function twinklebatchprotectCallbackEvaluate(event) {
	var pages = event.target.getChecked( 'pages' );
	var reason = event.target.reason.value;
	var editmodify = event.target.editmodify.checked;
	var editlevel = event.target.editlevel.value;
	var editexpiry = event.target.editexpiry.value;
	var movemodify = event.target.movemodify.checked;
	var movelevel = event.target.movelevel.value;
	var moveexpiry = event.target.moveexpiry.value;
	var createmodify = event.target.createmodify.checked;
	var createlevel = event.target.createlevel.value;
	var createexpiry = event.target.createexpiry.value;

	if( ! reason ) {
		alert("You've got to give a reason, you rouge admin!");
		return;
	}

	Morebits.simpleWindow.setButtonsEnabled(false);
	Morebits.status.init( event.target );

	if( !pages ) {
		Morebits.status.error( 'Error', 'Nothing to protect, aborting' );
		return;
	}

	var toCall = function twinklebatchprotectToCall( work ) {
		if( work.length === 0 && Twinkle.batchprotect.currentProtectCounter <= 0 ) {
			Morebits.status.info( 'work done' );
			window.clearInterval( Twinkle.batchprotect.currentprotector );
			Twinkle.batchprotect.currentprotector = Twinkle.batchprotect.currentProtectCounter = 0;
			Morebits.wiki.removeCheckpoint();
			return;
		} else if( work.length !== 0 && Twinkle.batchprotect.currentProtectCounter <= Twinkle.getPref('batchProtectMinCutOff') ) {
			var pages = work.shift();
			Twinkle.batchprotect.currentProtectCounter += pages.length;
			for( var i = 0; i < pages.length; ++i ) {
				var page = pages[i];
				var query = {
					'action': 'query',
					'titles': page
				};
				var wikipedia_api = new Morebits.wiki.api( 'Checking if page ' + page + ' exists', query, Twinkle.batchprotect.callbacks.main );
				wikipedia_api.params = {
					page: page,
					reason: reason,
					editmodify: editmodify,
					editlevel: editlevel,
					editexpiry: editexpiry,
					movemodify: movemodify,
					movelevel: movelevel,
					moveexpiry: moveexpiry,
					createmodify: createmodify,
					createlevel: createlevel,
					createexpiry: createexpiry
				};
				wikipedia_api.post();
			}
		}
	};
	var work = Morebits.array.chunk( pages, Twinkle.getPref('batchProtectChunks') );
	Morebits.wiki.addCheckpoint();
	Twinkle.batchprotect.currentprotector = window.setInterval( toCall, 1000, work );
};

Twinkle.batchprotect.callbacks = {
	main: function( apiobj ) {
		var xml = apiobj.responseXML;
		var normal = $(xml).find('normalized n').attr('to');
		if( normal ) {
			apiobj.params.page = normal;
		}

		var exists = ($(xml).find('page').attr('missing') !== "");

		var page = new Morebits.wiki.page(apiobj.params.page, "Protecting " + apiobj.params.page);
		var takenAction = false;
		if (exists && apiobj.params.editmodify) {
			page.setEditProtection(apiobj.params.editlevel, apiobj.params.editexpiry);
			takenAction = true;
		}
		if (exists && apiobj.params.movemodify) {
			page.setMoveProtection(apiobj.params.movelevel, apiobj.params.moveexpiry);
			takenAction = true;
		}
		if (!exists && apiobj.params.createmodify) {
			page.setCreateProtection(apiobj.params.createlevel, apiobj.params.createexpiry);
			takenAction = true;
		}
		if (!takenAction) {
			Morebits.status.warn("Protecting " + apiobj.params.page, "page " + (exists ? "exists" : "does not exist") + "; nothing to do, skipping");
			return;
		}

		page.setEditSummary(apiobj.params.reason);

		page.protect(function(pageobj) {
			--Twinkle.batchprotect.currentProtectCounter;
			var link = document.createElement( 'a' );
			link.setAttribute( 'href', mw.util.wikiGetlink( apiobj.params.page ) );
			link.appendChild( document.createTextNode( apiobj.params.page ) );
			pageobj.getStatusElement().info( [ 'completed (' , link , ')' ] );
		} );
	}
};

/*
 ****************************************
 *** twinklebatchundelete.js: Batch undelete module
 ****************************************
 * Mode of invocation:     Tab ("Und-batch")
 * Active on:              Existing user pages
 * Config directives in:   TwinkleConfig
 */


Twinkle.batchundelete = function twinklebatchundelete() {
	if( mw.config.get("wgNamespaceNumber") !== mw.config.get("wgNamespaceIds").user || 
		!mw.config.get("wgArticleId") ) {
		return;
	}
	if( Morebits.userIsInGroup( 'sysop' ) ) {
		twAddPortletLink( Twinkle.batchundelete.callback, "Und-batch", "tw-batch-undel", "Undelete 'em all" );
	}
};

Twinkle.batchundelete.callback = function twinklebatchundeleteCallback() {
	var Window = new Morebits.simpleWindow( 800, 400 );
	Window.setScriptName("Twinkle");
	Window.setTitle("Batch undelete")
	var form = new Morebits.quickForm( Twinkle.batchundelete.callback.evaluate );
	form.append( {
			type: 'textarea',
			name: 'reason',
			label: 'Reason: '
		} );

	var query = {
		'action': 'query',
		'generator': 'links',
		'titles': mw.config.get("wgPageName"),
		'gpllimit' : Twinkle.getPref('batchMax') // the max for sysops
	};
	var wikipedia_api = new Morebits.wiki.api( 'Grabbing pages', query, function( self ) {
			var xmlDoc = self.responseXML;
			var snapshot = xmlDoc.evaluate('//page[@missing]', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );
			var list = [];
			for ( var i = 0; i < snapshot.snapshotLength; ++i ) {
				var object = snapshot.snapshotItem(i);
				var page = xmlDoc.evaluate( '@title', object, null, XPathResult.STRING_TYPE, null ).stringValue;
				list.push( {label:page, value:page, checked: true });
			}
			self.params.form.append( {
					type: 'checkbox',
					name: 'pages',
					list: list
				}
			);
			self.params.form.append( { type:'submit' } );

			var result = self.params.form.render();
			self.params.Window.setContent( result );


		}  );
	wikipedia_api.params = { form:form, Window:Window };
	wikipedia_api.post();
	var root = document.createElement( 'div' );
	Morebits.status.init( root );
	Window.setContent( root );
	Window.display();
};
Twinkle.batchundelete.currentUndeleteCounter = 0;
Twinkle.batchundelete.currentundeletor = 0;
Twinkle.batchundelete.callback.evaluate = function( event ) {
	Morebits.wiki.actionCompleted.notice = 'Status';
	Morebits.wiki.actionCompleted.postfix = 'batch undeletion is now completed';

	var pages = event.target.getChecked( 'pages' );
	var reason = event.target.reason.value;
	if( ! reason ) {
		alert("You need to give a reason, you cabal crony!");
		return;
	}
	Morebits.simpleWindow.setButtonsEnabled(false);
	Morebits.status.init( event.target );

	if( !pages ) {
		Morebits.status.error( 'Error', 'nothing to undelete, aborting' );
		return;
	}

	var work = Morebits.array.chunk( pages, Twinkle.getPref('batchUndeleteChunks') );
	Morebits.wiki.addCheckpoint();
	Twinkle.batchundelete.currentundeletor = window.setInterval( Twinkle.batchundelete.callbacks.main, 1000, work, reason );
};

Twinkle.batchundelete.callbacks = {
	main: function( work, reason ) {
		if( work.length === 0 && Twinkle.batchundelete.currentUndeleteCounter <= 0 ) {
			Morebits.status.info( 'work done' );
			window.clearInterval( Twinkle.batchundelete.currentundeletor );
			Morebits.wiki.removeCheckpoint();
			return;
		} else if( work.length !== 0 && Twinkle.batchundelete.currentUndeleteCounter <= Twinkle.getPref('batchUndeleteMinCutOff') ) {
			var pages = work.shift();
			Twinkle.batchundelete.currentUndeleteCounter += pages.length;
			for( var i = 0; i < pages.length; ++i ) {
				var title = pages[i];
				var query = { 
					'token': mw.user.tokens.get().editToken,
					'title': title,
					'action': 'undelete',
					'reason': reason + Twinkle.getPref('deletionSummaryAd')
				};
				var wikipedia_api = new Morebits.wiki.api( "Undeleting " + title, query, function( self ) { 
						--Twinkle.batchundelete.currentUndeleteCounter;
						var link = document.createElement( 'a' );
						link.setAttribute( 'href', mw.util.wikiGetlink(self.itsTitle) );
						link.setAttribute( 'title', self.itsTitle );
						link.appendChild( document.createTextNode(self.itsTitle) );
						self.statelem.info( ['completed (',link,')'] );

					});
				wikipedia_api.itsTitle = title;
				wikipedia_api.post();

			}
		}
	}
};

/*
 ****************************************
 *** twinkleconfig.js: Preferences module
 ****************************************
 * Mode of invocation:     Adds configuration form to Wikipedia:Twinkle/Preferences and user 
                           subpages named "/Twinkle preferences", and adds ad box to the top of user 
                           subpages belonging to the currently logged-in user which end in '.js'
 * Active on:              What I just said.  Yeah.
 * Config directives in:   TwinkleConfig

 I, [[User:This, that and the other]], originally wrote this.  If the code is misbehaving, or you have any
 questions, don't hesitate to ask me.  (This doesn't at all imply [[WP:OWN]]ership - it's just meant to
 point you in the right direction.)  -- TTO
 */



Twinkle.config = {};

Twinkle.config.commonEnums = {
	watchlist: { yes: "ध्यानसूची में जोड़ें", no: "ध्यानसूची में नहीं जोड़ें", "default": "आपकी वरीयताओं अनुसार चलें" },
	talkPageMode: { window: "एक नई विंडो में, पहले से खुले वार्ता पृष्ठ की जगह", tab: "एक नए टैब में", blank: "एक बिलकुल नई विंडो में" }
};

Twinkle.config.commonSets = {
	csdCriteria: {
		"शीह": "विशिष्ट कारण ({{शीह}})",
		"व1": "व1", "व2": "व2", "व3": "व3", "व4": "व4", "व5": "व5", "व6": "व6", "व6ल": "व6ल", "व6फ़": "व6फ़", "व6स": "व6स",
		"ल1": "ल1", "ल2": "ल2", "ल4": "ल4",
		"फ़1": "फ़1", "फ़2": "फ़2", "फ़3": "फ़3", "फ़4": "फ़4", "फ़5": "फ़5", "फ़6": "फ़6",
		"सा1": "सा1",
		"स1": "स1", "स2": "स2"  // db-multiple is not listed here because it is treated differently within twinklespeedy
	},
	csdCriteriaDisplayOrder: [
		"शीह",
		"व1", "व2", "व3", "व4", "व5", "व6",
		"ल1", "ल2", "ल4", "व6ल",
		"फ़1", "फ़2", "फ़3", "फ़4", "फ़5", "फ़6", "व6फ़",
		"सा1",
		"स1", "स2", "व6स"
	],
	csdCriteriaNotificationDisplayOrder: [
		"शीह",
		"व1", "व2", "व3", "व4", "व5", "व6",
		"ल1", "ल2", "ल4", "व6ल",
		"फ़1", "फ़2", "फ़3", "फ़4", "फ़5", "फ़6", "व6फ़",
		"सा1",
		"स1", "स2", "व6स"
	],
	namespacesNoSpecial: {
		"0": "लेख",
		"1": "वार्ता",
		"2": "सदस्य",
		"3": "सदस्य वार्ता",
		"4": "विकिपीडिया",
		"5": "विकिपीडिया वार्ता",
		"6": "चित्र",
		"7": "चित्र वार्ता",
		"8": "मीडियाविकि",
		"9": "मीडियाविकि वार्ता",
		"10": "साँचा",
		"11": "साँचा वार्ता",
		"12": "सहायता",
		"13": "सहायता वार्ता",
		"14": "श्रेणी",
		"15": "श्रेणी वार्ता",
		"100": "प्रवेशद्वार",
		"101": "प्रवेशद्वार वार्ता"
	}
};


/**
 * Section entry format:
 *
 * {
 *   title: <human-readable section title>,
 *   adminOnly: <true for admin-only sections>,
 *   hidden: <true for advanced preferences that rarely need to be changed - they can still be modified by manually editing twinkleoptions.js>,
 *   inFriendlyConfig: <true for preferences located under FriendlyConfig rather than TwinkleConfig>,
 *   preferences: [
 *     {
 *       name: <TwinkleConfig property name>,
 *       label: <human-readable short description - used as a form label>,
 *       helptip: <(optional) human-readable text (using valid HTML) that complements the description, like limits, warnings, etc.>
 *       adminOnly: <true for admin-only preferences>,
 *       type: <string|boolean|integer|enum|set|customList> (customList stores an array of JSON objects { value, label }),
 *       enumValues: <for type = "enum": a JSON object where the keys are the internal names and the values are human-readable strings>,
 *       setValues: <for type = "set": a JSON object where the keys are the internal names and the values are human-readable strings>,
 *       setDisplayOrder: <(optional) for type = "set": an array containing the keys of setValues (as strings) in the order that they are displayed>,
 *       customListValueTitle: <for type = "customList": the heading for the left "value" column in the custom list editor>,
 *       customListLabelTitle: <for type = "customList": the heading for the right "label" column in the custom list editor>
 *     },
 *     . . .
 *   ]
 * },
 * . . .
 *
 */

Twinkle.config.sections = [
{
	title: "सामान्य",
	preferences: [
		// TwinkleConfig.summaryAd (string)
		// Text to be appended to the edit summary of edits made using Twinkle
		{
			name: "summaryAd",
			label: "ट्विंकल के सम्पादन सारांश में जोड़ने हेतु \"ऐड\"",
			helptip: "यह स्पेस से शुरू होना चाहिए, और छोटा होना चाहिए।",
			type: "string"
		},

		// TwinkleConfig.deletionSummaryAd (string)
		// Text to be appended to the edit summary of deletions made using Twinkle
		{
			name: "deletionSummaryAd",
			label: "पृष्ठ हटाते समय सम्पादन सारांश में जोड़ने हेतु \"ऐड\"",
			helptip: "यह आम-तौर पर सामान्य ऐड ही रखी जाती है।",
			adminOnly: true,
			type: "string"
		},

		// TwinkleConfig.protectionSummaryAd (string)
		// Text to be appended to the edit summary of page protections made using Twinkle
		{
			name: "protectionSummaryAd",
			label: "पृष्ठ सुरक्षित करते समय सम्पादन सारांश में जोड़ने हेतु \"ऐड\"",
			helptip: "यह आम-तौर पर सामान्य ऐड ही रखी जाती है।",
			adminOnly: true,
			type: "string"
		},

		// TwinkleConfig.userTalkPageMode may take arguments:
		// 'window': open a new window, remember the opened window
		// 'tab': opens in a new tab, if possible.
		// 'blank': force open in a new window, even if such a window exists
		{
			name: "userTalkPageMode",
			label: "वार्ता पृष्ठ खोलते समय उसे खोलें",
			type: "enum",
			enumValues: Twinkle.config.commonEnums.talkPageMode
		},

		// TwinkleConfig.dialogLargeFont (boolean)
		{
			name: "dialogLargeFont",
			label: "ट्विंकल की विंडो में बड़े पाठ का प्रयोग करें",
			type: "boolean"
		}
	]
},

{
	title: "Revert and rollback",  // twinklefluff module
	preferences: [
		// TwinkleConfig.openTalkPage (array)
		// What types of actions that should result in opening of talk page
		{
			name: "openTalkPage",
			label: "Open user talk page after these types of reversions",
			type: "set",
			setValues: { agf: "AGF rollback", norm: "Normal rollback", vand: "Vandalism rollback", torev: "\"Restore this version\"" }
		},

		// TwinkleConfig.openTalkPageOnAutoRevert (bool)
		// Defines if talk page should be opened when calling revert from contrib page, because from there, actions may be multiple, and opening talk page not suitable. If set to true, openTalkPage defines then if talk page will be opened.
		{
			name: "openTalkPageOnAutoRevert",
			label: "Open user talk page when invoking rollback from user contributions",
			helptip: "Often, you may be rolling back many pages at a time from a vandal's contributions page, so it would be unsuitable to open the user talk page. Hence, this option is off by default. When this is on, the desired options must be enabled in the previous setting for this to work.",
			type: "boolean"
		},

		// TwinkleConfig.markRevertedPagesAsMinor (array)
		// What types of actions that should result in marking edit as minor
		{
			name: "markRevertedPagesAsMinor",
			label: "Mark as minor edit for these types of reversions",
			type: "set",
			setValues: { agf: "AGF rollback", norm: "Normal rollback", vand: "Vandalism rollback", torev: "\"Restore this version\"" }
		},

		// TwinkleConfig.watchRevertedPages (array)
		// What types of actions that should result in forced addition to watchlist
		{
			name: "watchRevertedPages",
			label: "Add pages to watchlist for these types of reversions",
			type: "set",
			setValues: { agf: "AGF rollback", norm: "Normal rollback", vand: "Vandalism rollback", torev: "\"Restore this version\"" }
		},

		// TwinkleConfig.offerReasonOnNormalRevert (boolean)
		// If to offer a prompt for extra summary reason for normal reverts, default to true
		{
			name: "offerReasonOnNormalRevert",
			label: "Prompt for reason for normal rollbacks",
			helptip: "\"Normal\" rollbacks are the ones that are invoked from the middle [rollback] link.",
			type: "boolean"
		},

		{
			name: "confirmOnFluff",
			label: "Provide a confirmation message before reverting",
			helptip: "For users of pen or touch devices, and chronically indecisive people.",
			type: "boolean"
		},

		// TwinkleConfig.showRollbackLinks (array)
		// Where Twinkle should show rollback links (diff, others, mine, contribs)
		// Note from TTO: |contribs| seems to be equal to |others| + |mine|, i.e. redundant, so I left it out heres
		{
			name: "showRollbackLinks",
			label: "Show rollback links on these pages",
			type: "set",
			setValues: { diff: "Diff pages", others: "Contributions pages of other users", mine: "My contributions page" }
		}
	]
},

{
	title: "साझा आइ॰पी॰ पता टैगिंग",
	inFriendlyConfig: true,
	preferences: [
		{
			name: "markSharedIPAsMinor",
			label: "साझा आइ॰पी॰ पता टैगिंग को छोटा बदलाव चिन्हित करें",
			type: "boolean"
		}
	]
},

{
	title: "शीघ्र हटाना (शीह)",
	preferences: [
		
		// TwinkleConfig.watchSpeedyPages (array)
		// Whether to add speedy tagged pages to watchlist
		{
			name: "watchSpeedyPages",
			label: "निम्न मापदंडों से नामांकन करते समय लेख को ध्यानसूची में डालें",
			type: "set",
			setValues: Twinkle.config.commonSets.csdCriteria,
			setDisplayOrder: Twinkle.config.commonSets.csdCriteriaDisplayOrder
		},

		//TwinkleConfig.NotifySelfSpeedy (boolean)
		{
			name: "NotifySelfSpeedy",
			label: "स्वयं बनाए पृष्ठों को शीघ्र हटाने हेतु चिन्हित करते समय सूचित करें",
			helptip: "यदि आप स्वयं बनाए किसी पृष्ठ को शीघ्र हटाने हेतु चिन्हित कर रहे होंगे, तो आपको एक जावास्क्रिप्ट एलर्ट द्वारा सूचित करेगा। साथ ही यदि आप अपने बनाए किसी पृष्ठ को स1 के अतिरिक्त किसी मापदंड के अंतर्गत चिन्हित कर रहे होंगे तो आपको यह जानकारी देकर आपसे नामांकन के लिए पुष्टि लेगा।",
			type: "boolean"
		},

		// TwinkleConfig.markSpeedyPagesAsPatrolled (boolean)
		// If, when applying speedy template to page, to mark the page as patrolled (if the page was reached from NewPages)
		{
			name: "markSpeedyPagesAsPatrolled",
			label: "नामांकन करते समय लेख को जाँचा हुआ (patrolled) चिन्हित करें (यदि संभव हो)",
			helptip: "पृष्ठ जाँचे हुए तभी चिन्हित किये जाएँगे यदि उनपर विशेष:नए_पृष्ठ द्वारा जाया गया हो।",
			type: "boolean"
		},

		// TwinkleConfig.notifyUserOnSpeedyDeletionNomination (array)
		// What types of actions should result that the author of the page being notified of nomination
		{
			name: "notifyUserOnSpeedyDeletionNomination",
			label: "निम्न मापदंडों से नामांकन करते समय पृष्ठ निर्माता को सूचित करें",
			helptip: "यदि आप नामांकन विंडो में से सूचित करना चुनते हैं और यहाँ उपयुक्त चेकबॉक्स चेक करते हैं, पृष्ठ निर्माता को तभी सूचित किया जाएगा।",
			type: "set",
			setValues: Twinkle.config.commonSets.csdCriteria,
			setDisplayOrder: Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder
		},

		// TwinkleConfig.welcomeUserOnSpeedyDeletionNotification (array of strings)
		// On what types of speedy deletion notifications shall the user be welcomed
		// with a "firstarticle" notice if his talk page has not yet been created.
/*		{
			name: "welcomeUserOnSpeedyDeletionNotification",
			label: "Welcome page creator alongside notification when tagging with these criteria",
			helptip: "The welcome is issued only if the user is notified about the deletion, and only if their talk page does not already exist. The template used is {{<a href=\"" + mw.util.wikiGetlink("Template:Firstarticle") + "\">firstarticle</a>}}.",
			type: "set",
			setValues: Twinkle.config.commonSets.csdCriteria,
			setDisplayOrder: Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder
		},
*/
		// TwinkleConfig.openUserTalkPageOnSpeedyDelete (array of strings)
		// What types of actions that should result user talk page to be opened when speedily deleting (admin only)
		{
			name: "openUserTalkPageOnSpeedyDelete",
			label: "निम्न मापदंडों के अंतर्गत पृष्ठ हटाते समय पृष्ठ निर्माता का वार्ता पृष्ठ खोलें",
			adminOnly: true,
			type: "set",
			setValues: Twinkle.config.commonSets.csdCriteria,
			setDisplayOrder: Twinkle.config.commonSets.csdCriteriaDisplayOrder
		},

		// TwinkleConfig.deleteTalkPageOnDelete (boolean)
		// If talk page if exists should also be deleted (CSD G8) when spedying a page (admin only)
		{
			name: "deleteTalkPageOnDelete",
			label: "\"वार्ता पृष्ठ भी हटाएँ\" चेकबॉक्स को डिफ़ॉल्ट रूप से चेक करें",
			adminOnly: true,
			type: "boolean"
		},

		// TwinkleConfig.deleteSysopDefaultToTag (boolean)
		// Make the CSD screen default to "tag" instead of "delete" (admin only)
		{
			name: "deleteSysopDefaultToTag",
			label: "नामांकन को हटाने के बजाए डिफ़ॉल्ट रखें",
			adminOnly: true,
			type: "boolean"
		},

		// TwinkleConfig.speedyWindowWidth (integer)
		// Defines the width of the Twinkle SD window in pixels
		{
			name: "speedyWindowWidth",
			label: "विंडो की चौड़ाई (पिक्सेल में)",
			type: "integer"
		},

		// TwinkleConfig.speedyWindowWidth (integer)
		// Defines the width of the Twinkle SD window in pixels
		{
			name: "speedyWindowHeight",
			label: "विंडो की ऊँचाई (पिक्सेल में)",
			helptip: "यदि आपके पास बड़ा मॉनिटर है तो आप इसे बढ़ाना पसंद करेंगे।",
			type: "integer"
		},

		{
			name: "logSpeedyNominations",
			label: "सभी शीघ्र हटाने के नामांकनों का अपने सदस्य नामस्थान में लॉग रखें",
			helptip: "चूँकि आम सदस्य अपने हटाए हुए योगदान नहीं देख सकते हैं, अपने सदस्य नामस्थान में नामांकनों का लॉग रखना ट्विंकल द्वारा किये गए नामांकनों की सूची पाने का आसान तरीका है।",
			type: "boolean"
		},
		{
			name: "speedyLogPageName",
			label: "सदस्य नामस्थान का लॉग इस पृष्ठ पर रखें",
			helptip: "यहाँ अपने सदस्य उप-पृष्ठ का नाम दें। इसमें अपना सदस्य नाम एवं नामस्थान ना जोड़ें। यह तभी काम करता है यदि आप सदस्य नामस्थान लॉग सक्षम करें।",
			type: "string"
		},
		{
			name: "noLogOnSpeedyNomination",
			label: "सदस्य नामस्थान लॉग में निम्न मापदंडों से किये गए नामांकनों की प्रविष्टि ना जोड़ें",
			type: "set",
			setValues: Twinkle.config.commonSets.csdCriteria,
			setDisplayOrder: Twinkle.config.commonSets.csdCriteriaDisplayOrder
		}
	]
},

{
	title: "टैग",
	inFriendlyConfig: true,
	preferences: [
		{
			name: "watchTaggedPages",
			label: "पृष्ठों को रखरखाव के लिए टैग करते समय उन्हें ध्यानसूची में जोड़ें",
			type: "boolean"
		},
		{
			name: "markTaggedPagesAsMinor",
			label: "रखरखाव टैगिंग को छोटा सम्पादन चिन्हित करें",
			type: "boolean"
		},
		{
			name: "markTaggedPagesAsPatrolled",
			label: "पृष्ठों को टैग करते समय उन्हें जाँचा हुआ (patrolled) चिन्हित करें (यदि संभव हो)",
			helptip: "पृष्ठ जाँचे हुए तभी चिन्हित किये जाएँगे यदि उनपर विशेष:नए_पृष्ठ द्वारा जाया गया हो।",
			type: "boolean"
		},
		{
			name: "groupByDefault",
			label: "\"यदि संभव  हो तो {{अनेक समस्याएँ}} द्वारा वर्गीकृत करें\" चेकबॉक्स को डिफ़ॉल्ट रूप से चेक करें",
			type: "boolean"
		},
		{
			name: "tagArticleSortOrder",
			label: "लेख रखरखाव साँचों के लिए डिफ़ॉल्ट दृश्यता",
			type: "enum",
			enumValues: { "cat": "वर्ग अनुसार", "alpha": "वर्णमाला अनुसार" }
		},
		{
			name: "customTagList",
			label: "लेख रखरखाव के लिए दिखाने हेतु विशिष्ट टैग",
			helptip: "ये टैग सूची के अंत में अतिरिक्त विकल्पों की तरह नज़र आते हैं। आप इसमें ऐसे रखरखाव साँचे जोड़ सकते हैं जो ट्विंकल में डिफ़ॉल्ट रूप से उपलब्ध नहीं हैं।",
			type: "customList",
			customListValueTitle: "साँचे का नाम (बिना ब्रैकेट के)",
			customListLabelTitle: "टैग विंडो में दिखाने हेतु पाठ"
		}
	]
},

{
	title: "सन्देश",
	inFriendlyConfig: true,
	preferences: [
		{
			name: "markTalkbackAsMinor",
			label: "सन्देशों को छोटा सम्पादन चिन्हित करें।",
			type: "boolean"
		},
		{
			name: "insertTalkbackSignature",
			label: "सन्देशों में हस्ताक्षर जोड़ें",
			helptip: "यदि यह सक्षम है तो केवल {{सन्देश}} साँचा जोड़ने पर भी उसके नीचे आपके हस्ताक्षर जोड़े जाएँगे।",
			type: "boolean"
		},
		{
			name: "talkbackHeading",
			label: "सन्देश के लिए प्रयोग किया जाने वाला अनुभाग शीर्षक",
			type: "string"
		}
	]
},

{
	title: "कड़ीतोड़",
	preferences: [
		// TwinkleConfig.unlinkNamespaces (array)
		// In what namespaces unlink should happen, default in 0 (article) and 100 (portal)
		{
			name: "unlinkNamespaces",
			label: "कड़ियाँ निम्न नामस्थानों से हटाएँ",
			helptip: "किसी भी चर्चा/वार्ता नामस्थान को चुनते समय याद रखें कि इससे पुरालेखों में से भी कड़ियाँ हट जाएँगी (जो नहीं किया जाना चाहिए)।",
			type: "set",
			setValues: Twinkle.config.commonSets.namespacesNoSpecial
		}
	]
},

{
	title: "Warn user",
	preferences: [
		// TwinkleConfig.defaultWarningGroup (int)
		// if true, watch the page which has been dispatched an warning or notice, if false, default applies
		{
			name: "defaultWarningGroup",
			label: "Default warning level",
			type: "enum",
			enumValues: { "1": "Level 1", "2": "Level 2", "3": "Level 3", "4": "Level 4", "5": "Level 4im", "6": "Single-issue notices", "7": "Single-issue warnings", "8": "Block (admin only)" }
		},

		// TwinkleConfig.showSharedIPNotice may take arguments:
		// true: to show shared ip notice if an IP address
		// false: to not print the notice
		{
			name: "showSharedIPNotice",
			label: "Add extra notice on shared IP talk pages",
			helptip: "Notice used is {{<a href='" + mw.util.wikiGetlink("Template:SharedIPAdvice") + "'>SharedIPAdvice</a>}}",
			type: "boolean"
		},

		// TwinkleConfig.watchWarnings (boolean)
		// if true, watch the page which has been dispatched an warning or notice, if false, default applies
		{
			name: "watchWarnings",
			label: "Add user talk page to watchlist when notifying",
			type: "boolean"
		},

		// TwinkleConfig.blankTalkpageOnIndefBlock (boolean)
		// if true, blank the talk page when issuing an indef block notice (per [[WP:UW#Indefinitely blocked users]])
		{
			name: "blankTalkpageOnIndefBlock",
			label: "Blank the talk page when indefinitely blocking users",
			helptip: "See <a href=\"" + mw.util.wikiGetlink("WP:UW#Indefinitely blocked users") + "\">WP:UW</a> for more information.",
			adminOnly: true,
			type: "boolean"
		}
	]
},

{
	title: "स्वागत",
	inFriendlyConfig: true,
	preferences: [
		{
			name: "topWelcomes",
			label: "स्वागत साँचे सदस्य वार्ता पृष्ठ पर ऊपर-ऊपर जोड़ें",
			type: "boolean"
		},
		{
			name: "watchWelcomes",
			label: "स्वागत करते समय सदस्य वार्ता पृष्ठ अपनी ध्यानसूची में जोड़ें",
			helptip: "इससे आप उस नए सदस्य का ध्यान रख सकेंगे, और आवश्यकता पड़ने पर उनकी मदद कर सकेंगे।",
			type: "boolean"
		},
		{
			name: "insertHeadings",
			label: "स्वागत से पहले अनुभाग शीर्षक जोड़ें",
			type: "boolean"
		},
		{
			name: "welcomeHeading",
			label: "स्वागत के लिए प्रयुक्त अनुभाग शीर्षक",
			helptip: "इससे तभी फ़र्क पड़ेगा यदि अनुभाग शीर्षक सक्षम है और साँचे में पहले से अनुभाग शीर्षक नहीं है।",
			type: "string"
		},
		{
			name: "insertUsername",
			label: "साँचों में अपना सदस्यनाम जोड़ें (जहाँ आवश्यक हो)",
			helptip: "कुछ स्वागत साँचों में स्वागत करने वाले सदस्य का नाम भी जुड़ता है। यदि आप इस विकल्प को अक्षम करते हैं तो ऐसे साँचों में आपका सदस्यनाम नहीं दिखाई देगा।",
			type: "boolean"
		},
		{
			name: "insertSignature",
			label: "स्वागत के बाद हस्ताक्षर जोड़ें",
			helptip: "Strongly recommended.",
			type: "boolean"
		},
		{
			name: "maskTemplateInSummary",
			label: "सम्पादन सारांश में साँचे का नाम णा जोड़ें",
			helptip: "नए सदस्यों को \"Welcomevandal\" जैसे नाम अटपटे या बुरे लग सकते हैं, इसलिए उन्हें सम्पादन सारांश में ना जोड़ा जाए तो अच्छा है।",
			type: "boolean"
		},
		{
			name: "quickWelcomeMode",
			label: "अवतरण अंतर पृष्ठ पर \"स्वागत\" पर क्लिक करने पर",
			helptip: "यदि आप अपने-आप स्वागत करने का चुनाव करते हाँ तो आप नीचे जिस साँचे का नाम देंगे, स्वागत के लिए उसका प्रयोग किया जाएगा।",
			type: "enum",
			enumValues: { auto: "अपने-आप स्वागत करे", norm: "आपसे साँचा चुनने को कहे" }
		},
		{
			name: "quickWelcomeTemplate",
			label: "अपने-आप स्वागत करते समय प्रयोग करने हेतु साँचा",
			helptip: "एक स्वागत साँचा का नाम जोड़ें, बिना ब्रैकेट के। उपयुक्त पृष्ठ की कड़ी अपने-आप जोड़ी जाएगी।",
			type: "string"
		},
		{
			name: "customWelcomeList",
			label: "विशिष्ट स्वागत साँचे",
			helptip: "आप अन्य विशिष्ट स्वागत साँचे (अथवा सदस्य नामस्थान के पृष्ठ जो साँचे हैं) यहाँ जोड़ सकते हैं। ये स्वागत विंडो में जोड़ के दिखाए जाएँगे और सदस्य वार्ता पृष्ठों पर substitute किये जाएँगे। सदस्य नामस्थान के पृष्ठ जोड़ते समय पृष्ठ का पूरा नाम (नाम्थान सहित) बताएँ। साँचों के लिए ऐसा करने की आवश्यकता नहीं है।",
			type: "customList",
			customListValueTitle: "साँचे का नाम (बिना ब्रैकेट के)",
			customListLabelTitle: "स्वागत विंडो में दिखाने हेतु पाठ"
		}
	]
},

{
	title: "पृष्ठ हटाने हेतु चर्चा (हहेच)",
	preferences: [
		// TwinkleConfig.xfdWatchPage (string)
		// The watchlist setting of the page being nominated for XfD. Either "yes" (add to watchlist), "no" (don't
		// add to watchlist), or "default" (use setting from preferences). Default is "default" (duh).
		{
			name: "xfdWatchPage",
			label: "नामांकित पृष्ठ को ध्यानसूची में जोड़ें",
			type: "enum",
			enumValues: Twinkle.config.commonEnums.watchlist
		},

		// TwinkleConfig.xfdWatchDiscussion (string)
		// The watchlist setting of the newly created XfD page (for those processes that create discussion pages for each nomination),
		// or the list page for the other processes.
		// Either "yes" (add to watchlist), "no" (don't add to watchlist), or "default" (use setting from preferences). Default is "default" (duh).
		{
			name: "xfdWatchDiscussion",
			label: "चर्चा पृष्ठ को ध्यानसूची में जोड़ें",
			helptip: "यहाँ चर्चा पृष्ठ से तात्पर्य विकिपीडिया:पृष्ठ हटाने हेतु चर्चा के उपयुक्त चर्चा उप-पृष्ठ से है।",
			type: "enum",
			enumValues: Twinkle.config.commonEnums.watchlist
		},

		// TwinkleConfig.xfdWatchList (string)
		// The watchlist setting of the XfD list page, *if* the discussion is on a separate page. Either "yes" (add to watchlist), "no" (don't
		// add to watchlist), or "default" (use setting from preferences). Default is "no" (Hehe. Seriously though, who wants to watch it?
		// Sorry in advance for any false positives.).
/*		{
			name: "xfdWatchList",
			label: "Add the daily log/list page to the watchlist (where applicable)",
			helptip: "This only applies for AfD and MfD, where the discussions are transcluded onto a daily log page (for AfD) or the main MfD page (for MfD).",
			type: "enum",
			enumValues: Twinkle.config.commonEnums.watchlist
		},
*/
		// TwinkleConfig.xfdWatchUser (string)
		// The watchlist setting of the user if he receives a notification. Either "yes" (add to watchlist), "no" (don't
		// add to watchlist), or "default" (use setting from preferences). Default is "default" (duh).
		{
			name: "xfdWatchUser",
			label: "पृष्ठ निर्माता के वार्ता पृष्ठ को ध्यानसूची में डालें",
			type: "enum",
			enumValues: Twinkle.config.commonEnums.watchlist
		}
	]
},

{
	title: "Hidden",
	hidden: true,
	preferences: [
		// twinkle.header.js: portlet setup
		{
			name: "portletArea",
			type: "string"
		},
		{
			name: "portletId",
			type: "string"
		},
		{
			name: "portletName",
			type: "string"
		},
		{
			name: "portletType",
			type: "string"
		},
		{
			name: "portletNext",
			type: "string"
		},
		// twinklefluff.js: defines how many revision to query maximum, maximum possible is 50, default is 50
		{
			name: "revertMaxRevisions",
			type: "integer"
		},
		// twinklebatchdelete.js: How many pages should be processed at a time
		{
			name: "batchdeleteChunks",
			type: "integer"
		},
		// twinklebatchdelete.js: How many pages left in the process of being completed should allow a new batch to be initialized
		{
			name: "batchDeleteMinCutOff",
			type: "integer"
		},
		// twinklebatchdelete.js: How many pages should be processed maximum
		{
			name: "batchMax",
			type: "integer"
		},
		// twinklebatchprotect.js: How many pages should be processed at a time
		{
			name: "batchProtectChunks",
			type: "integer"
		},
		// twinklebatchprotect.js: How many pages left in the process of being completed should allow a new batch to be initialized
		{
			name: "batchProtectMinCutOff",
			type: "integer"
		},
		// twinklebatchundelete.js: How many pages should be processed at a time
		{
			name: "batchundeleteChunks",
			type: "integer"
		},
		// twinklebatchundelete.js: How many pages left in the process of being completed should allow a new batch to be initialized
		{
			name: "batchUndeleteMinCutOff",
			type: "integer"
		},
		// twinkledelimages.js: How many files should be processed at a time
		{
			name: "deliChunks",
			type: "integer"
		},
		// twinkledelimages.js: How many files should be processed maximum
		{
			name: "deliMax",
			type: "integer"
		}
	]
}

]; // end of Twinkle.config.sections

//{
//			name: "",
//			label: "",
//			type: ""
//		},

Twinkle.config.init = function twinkleconfigInit() {

	if ((mw.config.get("wgNamespaceNumber") === mw.config.get("wgNamespaceIds").project && mw.config.get("wgTitle") === "Twinkle/Preferences" ||
	    (mw.config.get("wgNamespaceNumber") === mw.config.get("wgNamespaceIds").user && mw.config.get("wgTitle").lastIndexOf("/Twinkle preferences") === (mw.config.get("wgTitle").length - 20))) &&
	    mw.config.get("wgAction") === "view") {
		// create the config page at Wikipedia:Twinkle/Preferences, and at user subpages (for testing purposes)

		if (!document.getElementById("twinkle-config")) {
			return;  // maybe the page is misconfigured, or something - but any attempt to modify it will be pointless
		}

		// set style (the url() CSS function doesn't seem to work from wikicode - ?!)
		document.getElementById("twinkle-config-titlebar").style.backgroundImage = "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAkCAMAAAB%2FqqA%2BAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEhQTFRFr73ZobTPusjdsMHZp7nVwtDhzNbnwM3fu8jdq7vUt8nbxtDkw9DhpbfSvMrfssPZqLvVztbno7bRrr7W1d%2Fs1N7qydXk0NjpkW7Q%2BgAAADVJREFUeNoMwgESQCAAAMGLkEIi%2FP%2BnbnbpdB59app5Vdg0sXAoMZCpGoFbK6ciuy6FX4ABAEyoAef0BXOXAAAAAElFTkSuQmCC)";

		var contentdiv = document.getElementById("twinkle-config-content");
		contentdiv.textContent = "";  // clear children

		// let user know about possible conflict with monobook.js/vector.js file
		// (settings in that file will still work, but they will be overwritten by twinkleoptions.js settings)
		var contentnotice = document.createElement("p");
		// I hate innerHTML, but this is one thing it *is* good for...
		contentnotice.innerHTML = "<b>Before modifying your preferences here,</b> make sure you have removed any old <code>TwinkleConfig</code> and <code>FriendlyConfig</code> settings from your <a href=\"" + mw.util.wikiGetlink("Special:MyPage/skin.js") + "\" title=\"Special:MyPage/skin.js\">user JavaScript file</a>.";
		contentdiv.appendChild(contentnotice);

		// look and see if the user does in fact have any old settings in their skin JS file
		var skinjs = new Morebits.wiki.page("User:" + mw.config.get("wgUserName") + "/" + mw.config.get("skin") + ".js");
		skinjs.setCallbackParameters(contentnotice);
		skinjs.load(Twinkle.config.legacyPrefsNotice);

		// start a table of contents
		var toctable = document.createElement("table");
		toctable.className = "toc";
		toctable.style.marginLeft = "0.4em";
		var toctr = document.createElement("tr");
		var toctd = document.createElement("td");
		// create TOC title
		var toctitle = document.createElement("div");
		toctitle.id = "toctitle";
		var toch2 = document.createElement("h2");
		toch2.textContent = "अनुक्रम ";
		toctitle.appendChild(toch2);
		// add TOC show/hide link
		var toctoggle = document.createElement("span");
		toctoggle.className = "toctoggle";
		toctoggle.appendChild(document.createTextNode("["));
		var toctogglelink = document.createElement("a");
		toctogglelink.className = "internal";
		toctogglelink.setAttribute("href", "#tw-tocshowhide");
		toctogglelink.textContent = "छुपाएँ";
		toctoggle.appendChild(toctogglelink);
		toctoggle.appendChild(document.createTextNode("]"));
		toctitle.appendChild(toctoggle);
		toctd.appendChild(toctitle);
		// create item container: this is what we add stuff to
		var tocul = document.createElement("ul");
		toctogglelink.addEventListener("click", function twinkleconfigTocToggle() {
			var $tocul = $(tocul);
			$tocul.toggle();
			if ($tocul.find(":visible").length) {
				toctogglelink.textContent = "छुपाएँ";
			} else {
				toctogglelink.textContent = "दिखाएँ";
			}
		}, false);
		toctd.appendChild(tocul);
		toctr.appendChild(toctd);
		toctable.appendChild(toctr);
		contentdiv.appendChild(toctable);

		var tocnumber = 1;

		var contentform = document.createElement("form");
		contentform.setAttribute("action", "javascript:void(0)");  // was #tw-save - changed to void(0) to work around Chrome issue
		contentform.addEventListener("submit", Twinkle.config.save, true);
		contentdiv.appendChild(contentform);

		var container = document.createElement("table");
		container.style.width = "100%";
		contentform.appendChild(container);

		$(Twinkle.config.sections).each(function(sectionkey, section) {
			if (section.hidden || (section.adminOnly && !Morebits.userIsInGroup("sysop"))) {
				return true;  // i.e. "continue" in this context
			}

			var configgetter;  // retrieve the live config values
			if (section.inFriendlyConfig) {
				configgetter = Twinkle.getFriendlyPref;
			} else {
				configgetter = Twinkle.getPref;
			}

			// add to TOC
			var tocli = document.createElement("li");
			tocli.className = "toclevel-1";
			var toca = document.createElement("a");
			toca.setAttribute("href", "#twinkle-config-section-" + tocnumber.toString());
			toca.appendChild(document.createTextNode(section.title));
			tocli.appendChild(toca);
			tocul.appendChild(tocli);

			var row = document.createElement("tr");
			var cell = document.createElement("td");
			cell.setAttribute("colspan", "3");
			var heading = document.createElement("h4");
			heading.style.borderBottom = "1px solid gray";
			heading.style.marginTop = "0.2em";
			heading.id = "twinkle-config-section-" + (tocnumber++).toString();
			heading.appendChild(document.createTextNode(section.title));
			cell.appendChild(heading);
			row.appendChild(cell);
			container.appendChild(row);

			var rowcount = 1;  // for row banding

			// add each of the preferences to the form
			$(section.preferences).each(function(prefkey, pref) {
				if (pref.adminOnly && !Morebits.userIsInGroup("sysop")) {
					return true;  // i.e. "continue" in this context
				}

				row = document.createElement("tr");
				row.style.marginBottom = "0.2em";
				// create odd row banding
				if (rowcount++ % 2 === 0) {
					row.style.backgroundColor = "rgba(128, 128, 128, 0.1)";
				}
				cell = document.createElement("td");

				var label, input;
				switch (pref.type) {

					case "boolean":  // create a checkbox
						cell.setAttribute("colspan", "2");

						label = document.createElement("label");
						input = document.createElement("input");
						input.setAttribute("type", "checkbox");
						input.setAttribute("id", pref.name);
						input.setAttribute("name", pref.name);
						if (configgetter(pref.name) === true) {
							input.setAttribute("checked", "checked");
						}
						label.appendChild(input);
						label.appendChild(document.createTextNode(" " + pref.label));
						cell.appendChild(label);
						break;

					case "string":  // create an input box
					case "integer":
						// add label to first column
						cell.style.textAlign = "right";
						cell.style.paddingRight = "0.5em";
						label = document.createElement("label");
						label.setAttribute("for", pref.name);
						label.appendChild(document.createTextNode(pref.label + ":"));
						cell.appendChild(label);
						row.appendChild(cell);

						// add input box to second column
						cell = document.createElement("td");
						cell.style.paddingRight = "1em";
						input = document.createElement("input");
						input.setAttribute("type", "text");
						input.setAttribute("id", pref.name);
						input.setAttribute("name", pref.name);
						if (pref.type === "integer") {
							input.setAttribute("size", 6);
							input.setAttribute("type", "number");
							input.setAttribute("step", "1");  // integers only
						}
						if (configgetter(pref.name)) {
							input.setAttribute("value", configgetter(pref.name));
						}
						cell.appendChild(input);
						break;

					case "enum":  // create a combo box
						// add label to first column
						// note: duplicates the code above, under string/integer
						cell.style.textAlign = "right";
						cell.style.paddingRight = "0.5em";
						label = document.createElement("label");
						label.setAttribute("for", pref.name);
						label.appendChild(document.createTextNode(pref.label + ":"));
						cell.appendChild(label);
						row.appendChild(cell);

						// add input box to second column
						cell = document.createElement("td");
						cell.style.paddingRight = "1em";
						input = document.createElement("select");
						input.setAttribute("id", pref.name);
						input.setAttribute("name", pref.name);
						$.each(pref.enumValues, function(enumvalue, enumdisplay) {
							var option = document.createElement("option");
							option.setAttribute("value", enumvalue);
							if (configgetter(pref.name) === enumvalue) {
								option.setAttribute("selected", "selected");
							}
							option.appendChild(document.createTextNode(enumdisplay));
							input.appendChild(option);
						});
						cell.appendChild(input);
						break;

					case "set":  // create a set of check boxes
						// add label first of all
						cell.setAttribute("colspan", "2");
						label = document.createElement("label");  // not really necessary to use a label element here, but we do it for consistency of styling
						label.appendChild(document.createTextNode(pref.label + ":"));
						cell.appendChild(label);

						var checkdiv = document.createElement("div");
						checkdiv.style.paddingLeft = "1em";
						var worker = function(itemkey, itemvalue) {
							var checklabel = document.createElement("label");
							checklabel.style.marginRight = "0.7em";
							checklabel.style.display = "inline-block";
							var check = document.createElement("input");
							check.setAttribute("type", "checkbox");
							check.setAttribute("id", pref.name + "_" + itemkey);
							check.setAttribute("name", pref.name + "_" + itemkey);
							if (configgetter(pref.name) && configgetter(pref.name).indexOf(itemkey) !== -1) {
								check.setAttribute("checked", "checked");
							}
							// cater for legacy integer array values for unlinkNamespaces (this can be removed a few years down the track...)
							if (pref.name === "unlinkNamespaces") {
								if (configgetter(pref.name) && configgetter(pref.name).indexOf(parseInt(itemkey, 10)) !== -1) {
									check.setAttribute("checked", "checked");
								}
							}
							checklabel.appendChild(check);
							checklabel.appendChild(document.createTextNode(itemvalue));
							checkdiv.appendChild(checklabel);
						};
						if (pref.setDisplayOrder) {
							// add check boxes according to the given display order
							$.each(pref.setDisplayOrder, function(itemkey, item) {
								worker(item, pref.setValues[item]);
							});
						} else {
							// add check boxes according to the order it gets fed to us (probably strict alphabetical)
							$.each(pref.setValues, worker);
						}
						cell.appendChild(checkdiv);
						break;

					case "customList":
						// add label to first column
						cell.style.textAlign = "right";
						cell.style.paddingRight = "0.5em";
						label = document.createElement("label");
						label.setAttribute("for", pref.name);
						label.appendChild(document.createTextNode(pref.label + ":"));
						cell.appendChild(label);
						row.appendChild(cell);

						// add button to second column
						cell = document.createElement("td");
						cell.style.paddingRight = "1em";
						var button = document.createElement("button");
						button.setAttribute("id", pref.name);
						button.setAttribute("name", pref.name);
						button.setAttribute("type", "button");
						button.addEventListener("click", Twinkle.config.listDialog.display, false);
						// use jQuery data on the button to store the current config value
						$(button).data({
							value: configgetter(pref.name),
							pref: pref,
							inFriendlyConfig: section.inFriendlyConfig
						});
						button.appendChild(document.createTextNode("Edit items"));
						cell.appendChild(button);
						break;

					default:
						alert("twinkleconfig: unknown data type for preference " + pref.name);
						break;
				}
				row.appendChild(cell);

				// add help tip
				cell = document.createElement("td");
				cell.style.fontSize = "90%";

				cell.style.color = "gray";
				if (pref.helptip) {
					cell.innerHTML = pref.helptip;
				}
				// add reset link (custom lists don't need this, as their config value isn't displayed on the form)
				if (pref.type !== "customList") {
					var resetlink = document.createElement("a");
					resetlink.setAttribute("href", "#tw-reset");
					resetlink.setAttribute("id", "twinkle-config-reset-" + pref.name);
					resetlink.addEventListener("click", Twinkle.config.resetPrefLink, false);
					if (resetlink.style.styleFloat) {  // IE (inc. IE9)
						resetlink.style.styleFloat = "right";
					} else {  // standards
						resetlink.style.cssFloat = "right";
					}
					resetlink.style.margin = "0 0.6em";
					resetlink.appendChild(document.createTextNode("रीसेट"));
					cell.appendChild(resetlink);
				}
				row.appendChild(cell);

				container.appendChild(row);
				return true;
			});
			return true;
		});

		var footerbox = document.createElement("div");
		footerbox.setAttribute("id", "twinkle-config-buttonpane");
		footerbox.style.backgroundColor = "#BCCADF";
		footerbox.style.padding = "0.5em";
		var button = document.createElement("button");
		button.setAttribute("id", "twinkle-config-submit");
		button.setAttribute("type", "submit");
		button.appendChild(document.createTextNode("बदलाव सहेजें"));
		footerbox.appendChild(button);
		var footerspan = document.createElement("span");
		footerspan.className = "plainlinks";
		footerspan.style.marginLeft = "2.4em";
		footerspan.style.fontSize = "90%";
		var footera = document.createElement("a");
		footera.setAttribute("href", "#tw-reset-all");
		footera.setAttribute("id", "twinkle-config-resetall");
		footera.addEventListener("click", Twinkle.config.resetAllPrefs, false);
		footera.appendChild(document.createTextNode("Restore defaults"));
		footerspan.appendChild(footera);
		footerbox.appendChild(footerspan);
		contentform.appendChild(footerbox);

		// since all the section headers exist now, we can try going to the requested anchor
		if (location.hash) {
			location.hash = location.hash;
		}

	} else if (mw.config.get("wgNamespaceNumber") === mw.config.get("wgNamespaceIds").user) {

		var box = document.createElement("div");
		box.setAttribute("id", "twinkle-config-headerbox");
		box.style.border = "1px #f60 solid";
		box.style.background = "#fed";
		box.style.padding = "0.6em";
		box.style.margin = "0.5em auto";
		box.style.textAlign = "center";

		var link;
		if (mw.config.get("wgTitle") === mw.config.get("wgUserName") + "/twinkleoptions.js") {
			// place "why not try the preference panel" notice
			box.style.fontWeight = "bold";
			box.style.width = "80%";
			box.style.borderWidth = "2px";

			if (mw.config.get("wgArticleId") > 0) {  // page exists
				box.appendChild(document.createTextNode("इस पृष्ठ पर आपकी ट्विंकल वरीयताएँ हैं। आप निम्न लिंक पर क्लिक कर के अपनी ट्विंकल वरीयताएँ आसानी से बदल सकते हैं: "));
			} else {  // page does not exist
				box.appendChild(document.createTextNode(" आप निम्न लिंक पर क्लिक कर के अपनी ट्विंकल वरीयताएँ आसानी से बदल सकते हैं: "));
			}
			link = document.createElement("a");
			link.setAttribute("href", mw.util.wikiGetlink(mw.config.get("wgFormattedNamespaces")[mw.config.get("wgNamespaceIds").project] + ":Twinkle/Preferences") );
			link.appendChild(document.createTextNode("Twinkle preferences panel"));
			box.appendChild(link);
			box.appendChild(document.createTextNode("\nयदि आप चाहें तो आप इस पृष्ठ को सीधे भी संपादित कर सकते हैं, परन्तु कृपया ऐसा तभी करें यदि आपको जावास्क्रिप्ट एवं ट्विंकल की उपयुक्त जानकारी हो।"));
			$(box).insertAfter($("#contentSub"));

		} else if (mw.config.get("wgTitle").indexOf(mw.config.get("wgUserName")) === 0 &&
				mw.config.get("wgPageName").lastIndexOf(".js") === mw.config.get("wgPageName").length - 3) {
			// place "Looking for Twinkle options?" notice
			box.style.width = "60%";

			box.appendChild(document.createTextNode("यदि आप अपनी ट्विंकल वरीयताएँ परिवर्तित करना चाहते हैं तो निम्न लिंक पर क्लिक करें: "));
			link = document.createElement("a");
			link.setAttribute("href", mw.util.wikiGetlink(mw.config.get("wgFormattedNamespaces")[mw.config.get("wgNamespaceIds").project] + ":Twinkle/Preferences") );
			link.appendChild(document.createTextNode("Twinkle preferences panel"));
			box.appendChild(link);
			box.appendChild(document.createTextNode("."));
			$(box).insertAfter($("#contentSub"));
		}
	}
};

// Morebits.wiki.page callback from init code
Twinkle.config.legacyPrefsNotice = function twinkleconfigLegacyPrefsNotice(pageobj) {
	var text = pageobj.getPageText();
	var contentnotice = pageobj.getCallbackParameters();
	if (text.indexOf("TwinkleConfig") !== -1 || text.indexOf("FriendlyConfig") !== -1) {
		contentnotice.innerHTML = '<table class="plainlinks ombox ombox-content"><tr><td class="mbox-image">' +
			'<img alt="" src="http://upload.wikimedia.org/wikipedia/en/3/38/Imbox_content.png" /></td>' +
			'<td class="mbox-text"><p><big><b>Before modifying your settings here,</b> you must remove your old Twinkle and Friendly settings from your personal skin JavaScript.</big></p>' +
			'<p>To do this, you can <a href="' + mw.config.get("wgScript") + '?title=User:' + encodeURIComponent(mw.config.get("wgUserName")) + '/' + mw.config.get("skin") + '.js&action=edit" target="_tab"><b>edit your personal JavaScript</b></a>, removing all lines of code that refer to <code>TwinkleConfig</code> and <code>FriendlyConfig</code>.</p>' +
			'</td></tr></table>';
	} else {
		$(contentnotice).remove();
	}
};

// custom list-related stuff

Twinkle.config.listDialog = {};

Twinkle.config.listDialog.addRow = function twinkleconfigListDialogAddRow(dlgtable, value, label) {
	var contenttr = document.createElement("tr");
	// "remove" button
	var contenttd = document.createElement("td");
	var removeButton = document.createElement("button");
	removeButton.setAttribute("type", "button");
	removeButton.addEventListener("click", function() { $(contenttr).remove(); }, false);
	removeButton.textContent = "Remove";
	contenttd.appendChild(removeButton);
	contenttr.appendChild(contenttd);

	// value input box
	contenttd = document.createElement("td");
	var input = document.createElement("input");
	input.setAttribute("type", "text");
	input.className = "twinkle-config-customlist-value";
	input.style.width = "97%";
	if (value) {
		input.setAttribute("value", value);
	}
	contenttd.appendChild(input);
	contenttr.appendChild(contenttd);

	// label input box
	contenttd = document.createElement("td");
	input = document.createElement("input");
	input.setAttribute("type", "text");
	input.className = "twinkle-config-customlist-label";
	input.style.width = "98%";
	if (label) {
		input.setAttribute("value", label);
	}
	contenttd.appendChild(input);
	contenttr.appendChild(contenttd);

	dlgtable.appendChild(contenttr);
};

Twinkle.config.listDialog.display = function twinkleconfigListDialogDisplay(e) {
	var $prefbutton = $(e.target);
	var curvalue = $prefbutton.data("value");
	var curpref = $prefbutton.data("pref");

	var dialog = new Morebits.simpleWindow(720, 400);
	dialog.setTitle(curpref.label);
	dialog.setScriptName("Twinkle preferences");

	var dialogcontent = document.createElement("div");
	var dlgtable = document.createElement("table");
	dlgtable.className = "wikitable";
	dlgtable.style.margin = "1.4em 1em";
	dlgtable.style.width = "auto";

	var dlgtbody = document.createElement("tbody");

	// header row
	var dlgtr = document.createElement("tr");
	// top-left cell
	var dlgth = document.createElement("th");
	dlgth.style.width = "5%";
	dlgtr.appendChild(dlgth);
	// value column header
	dlgth = document.createElement("th");
	dlgth.style.width = "35%";
	dlgth.textContent = (curpref.customListValueTitle ? curpref.customListValueTitle : "Value");
	dlgtr.appendChild(dlgth);
	// label column header
	dlgth = document.createElement("th");
	dlgth.style.width = "60%";
	dlgth.textContent = (curpref.customListLabelTitle ? curpref.customListLabelTitle : "Label");
	dlgtr.appendChild(dlgth);
	dlgtbody.appendChild(dlgtr);

	// content rows
	var gotRow = false;
	$.each(curvalue, function(k, v) {
		gotRow = true;
		Twinkle.config.listDialog.addRow(dlgtbody, v.value, v.label);
	});
	// if there are no values present, add a blank row to start the user off
	if (!gotRow) {
		Twinkle.config.listDialog.addRow(dlgtbody);
	}

	// final "add" button
	var dlgtfoot = document.createElement("tfoot");
	dlgtr = document.createElement("tr");
	var dlgtd = document.createElement("td");
	dlgtd.setAttribute("colspan", "3");
	var addButton = document.createElement("button");
	addButton.style.minWidth = "8em";
	addButton.setAttribute("type", "button");
	addButton.addEventListener("click", function(e) {
		Twinkle.config.listDialog.addRow(dlgtbody);
	}, false);
	addButton.textContent = "Add";
	dlgtd.appendChild(addButton);
	dlgtr.appendChild(dlgtd);
	dlgtfoot.appendChild(dlgtr);

	dlgtable.appendChild(dlgtbody);
	dlgtable.appendChild(dlgtfoot);
	dialogcontent.appendChild(dlgtable);

	// buttonpane buttons: [Save changes] [Reset] [Cancel]
	var button = document.createElement("button");
	button.setAttribute("type", "submit");  // so Morebits.simpleWindow puts the button in the button pane
	button.addEventListener("click", function(e) {
		Twinkle.config.listDialog.save($prefbutton, dlgtbody);
		dialog.close();
	}, false);
	button.textContent = "Save changes";
	dialogcontent.appendChild(button);
	button = document.createElement("button");
	button.setAttribute("type", "submit");  // so Morebits.simpleWindow puts the button in the button pane
	button.addEventListener("click", function(e) {
		Twinkle.config.listDialog.reset($prefbutton, dlgtbody);
	}, false);
	button.textContent = "Reset";
	dialogcontent.appendChild(button);
	button = document.createElement("button");
	button.setAttribute("type", "submit");  // so Morebits.simpleWindow puts the button in the button pane
	button.addEventListener("click", function(e) {
		dialog.close();  // the event parameter on this function seems to be broken
	}, false);
	button.textContent = "Cancel";
	dialogcontent.appendChild(button);

	dialog.setContent(dialogcontent);
	dialog.display();
};

// Resets the data value, re-populates based on the new (default) value, then saves the
// old data value again (less surprising behaviour)
Twinkle.config.listDialog.reset = function twinkleconfigListDialogReset(button, tbody) {
	// reset value on button
	var $button = $(button);
	var curpref = $button.data("pref");
	var oldvalue = $button.data("value");
	Twinkle.config.resetPref(curpref, $button.data("inFriendlyConfig"));

	// reset form
	var $tbody = $(tbody);
	$tbody.find("tr").slice(1).remove();  // all rows except the first (header) row
	// add the new values
	var curvalue = $button.data("value");
	$.each(curvalue, function(k, v) {
		Twinkle.config.listDialog.addRow(tbody, v.value, v.label);
	});

	// save the old value
	$button.data("value", oldvalue);
};

Twinkle.config.listDialog.save = function twinkleconfigListDialogSave(button, tbody) {
	var result = [];
	var current = {};
	$(tbody).find('input[type="text"]').each(function(inputkey, input) {
		if ($(input).hasClass("twinkle-config-customlist-value")) {
			current = { value: input.value };
		} else {
			current.label = input.value;
			// exclude totally empty rows
			if (current.value || current.label) {
				result.push(current);
			}
		}
	});
	$(button).data("value", result);
};

// reset/restore defaults

Twinkle.config.resetPrefLink = function twinkleconfigResetPrefLink(e) {
	var wantedpref = e.target.id.substring(21); // "twinkle-config-reset-" prefix is stripped

	// search tactics
	$(Twinkle.config.sections).each(function(sectionkey, section) {
		if (section.hidden || (section.adminOnly && !Morebits.userIsInGroup("sysop"))) {
			return true;  // continue: skip impossibilities
		}

		var foundit = false;

		$(section.preferences).each(function(prefkey, pref) {
			if (pref.name !== wantedpref) {
				return true;  // continue
			}
			Twinkle.config.resetPref(pref, section.inFriendlyConfig);
			foundit = true;
			return false;  // break
		});

		if (foundit) {
			return false;  // break
		}
	});
	return false;  // stop link from scrolling page
};

Twinkle.config.resetPref = function twinkleconfigResetPref(pref, inFriendlyConfig) {
	switch (pref.type) {

		case "boolean":
			document.getElementById(pref.name).checked = (inFriendlyConfig ?
				Twinkle.defaultConfig.friendly[pref.name] : Twinkle.defaultConfig.twinkle[pref.name]);
			break;

		case "string":
		case "integer":
		case "enum":
			document.getElementById(pref.name).value = (inFriendlyConfig ?
				Twinkle.defaultConfig.friendly[pref.name] : Twinkle.defaultConfig.twinkle[pref.name]);
			break;

		case "set":
			$.each(pref.setValues, function(itemkey, itemvalue) {
				if (document.getElementById(pref.name + "_" + itemkey)) {
					document.getElementById(pref.name + "_" + itemkey).checked = ((inFriendlyConfig ?
						Twinkle.defaultConfig.friendly[pref.name] : Twinkle.defaultConfig.twinkle[pref.name]).indexOf(itemkey) !== -1);
				}
			});
			break;

		case "customList":
			$(document.getElementById(pref.name)).data("value", (inFriendlyConfig ?
				Twinkle.defaultConfig.friendly[pref.name] : Twinkle.defaultConfig.twinkle[pref.name]));
			break;

		default:
			alert("twinkleconfig: unknown data type for preference " + pref.name);
			break;
	}
};

Twinkle.config.resetAllPrefs = function twinkleconfigResetAllPrefs() {
	// no confirmation message - the user can just refresh/close the page to abort
	$(Twinkle.config.sections).each(function(sectionkey, section) {
		if (section.hidden || (section.adminOnly && !Morebits.userIsInGroup("sysop"))) {
			return true;  // continue: skip impossibilities
		}
		$(section.preferences).each(function(prefkey, pref) {
			if (!pref.adminOnly || Morebits.userIsInGroup("sysop")) {
				Twinkle.config.resetPref(pref, section.inFriendlyConfig);
			}
		});
		return true;
	});
	return false;  // stop link from scrolling page
};

Twinkle.config.save = function twinkleconfigSave(e) {
	Morebits.status.init( document.getElementById("twinkle-config-content") );

	Morebits.wiki.actionCompleted.notice = "Save";

	var userjs = mw.config.get("wgFormattedNamespaces")[mw.config.get("wgNamespaceIds").user] + ":" + mw.config.get("wgUserName") + "/twinkleoptions.js";
	var wikipedia_page = new Morebits.wiki.page(userjs, "आपकी ट्विंकल वरीयताएँ निम्न पृष्ठ पर सहेजी जा रही हैं: " + userjs);
	wikipedia_page.setCallbackParameters(e.target);
	wikipedia_page.load(Twinkle.config.writePrefs);

	return false;
};

// The JSON stringify method in the following code was excerpted from
// http://www.JSON.org/json2.js
// version of 2011-02-23

// Douglas Crockford, the code's author, has released it into the Public Domain.
// See http://www.JSON.org/js.html

var JSON;
if (!JSON) {
	JSON = {};
}

(function() {
	var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
		gap,
		indent = '  ',  // hardcoded indent
		meta = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' };

	function quote(string) {
		escapable.lastIndex = 0;
		return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
			var c = meta[a];
			return typeof c === 'string' ? c :	'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
		}) + '"' : '"' + string + '"';
	}

	function str(key, holder) {
		var i, k, v, length, mind = gap, partial, value = holder[key];

		if (value && typeof value === 'object' && $.isFunction(value.toJSON)) {
			value = value.toJSON(key);
		}

		switch (typeof value) {
		case 'string':
			return quote(value);
		case 'number':
			return isFinite(value) ? String(value) : 'null';
		case 'boolean':
		case 'null':
			return String(value);
		case 'object':
			if (!value) {
				return 'null';
			}
			gap += indent;
			partial = [];
			if ($.isArray(value)) {
				length = value.length;
				for (i = 0; i < length; ++i) {
					partial[i] = str(i, value) || 'null';
				}
				v = partial.length === 0 ? '[]' : gap ?
					'[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
					'[' + partial.join(',') + ']';
				gap = mind;
				return v;
			}
			for (k in value) {
				if (Object.prototype.hasOwnProperty.call(value, k)) {
					v = str(k, value);
					if (v) {
						partial.push(quote(k) + (gap ? ': ' : ':') + v);
					}
				}
			}
			v = partial.length === 0 ? '{}' : gap ?
				'{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
				'{' + partial.join(',') + '}';
			gap = mind;
			return v;
		default:
			throw new Error( "JSON.stringify: unknown data type" );
		}
	}

	if (!$.isFunction(JSON.stringify)) {
		JSON.stringify = function (value, ignoredParam1, ignoredParam2) {
			ignoredParam1 = ignoredParam2;  // boredom
			gap = '';
			return str('', {'': value});
		};
	}
}());

Twinkle.config.writePrefs = function twinkleconfigWritePrefs(pageobj) {
	var form = pageobj.getCallbackParameters();
	var statelem = pageobj.getStatusElement();

	// this is the object which gets serialized into JSON
	var newConfig = {
		twinkle: {},
		friendly: {}
	};

	// keeping track of all preferences that we encounter
	// any others that are set in the user's current config are kept
	// this way, preferences that this script doesn't know about are not lost
	// (it does mean obsolete prefs will never go away, but... ah well...)
	var foundTwinklePrefs = [], foundFriendlyPrefs = [];

	// a comparison function is needed later on
	// it is just enough for our purposes (i.e. comparing strings, numbers, booleans,
	// arrays of strings, and arrays of { value, label })
	// and it is not very robust: e.g. compare([2], ["2"]) === true, and
	// compare({}, {}) === false, but it's good enough for our purposes here
	var compare = function(a, b) {
		if ($.isArray(a)) {
			if (a.length !== b.length) {
				return false;
			}
			var asort = a.sort(), bsort = b.sort();
			for (var i = 0; asort[i]; ++i) {
				// comparison of the two properties of custom lists
				if ((typeof asort[i] === "object") && (asort[i].label !== bsort[i].label ||
					asort[i].value !== bsort[i].value)) {
					return false;
				} else if (asort[i].toString() !== bsort[i].toString()) { 
					return false;
				}
			}
			return true;
		} else {
			return a === b;
		}
	};

	$(Twinkle.config.sections).each(function(sectionkey, section) {
		if (section.adminOnly && !Morebits.userIsInGroup("sysop")) {
			return;  // i.e. "continue" in this context
		}

		// reach each of the preferences from the form
		$(section.preferences).each(function(prefkey, pref) {
			var userValue;  // = undefined

			// only read form values for those prefs that have them
			if (!section.hidden && (!pref.adminOnly || Morebits.userIsInGroup("sysop"))) {
				switch (pref.type) {

					case "boolean":  // read from the checkbox
						userValue = form[pref.name].checked;
						break;

					case "string":  // read from the input box or combo box
					case "enum":
						userValue = form[pref.name].value;
						break;

					case "integer":  // read from the input box
						userValue = parseInt(form[pref.name].value, 10);
						if (isNaN(userValue)) {
							Morebits.status.warn("Saving", "The value you specified for " + pref.name + " (" + pref.value + ") was invalid.  The save will continue, but the invalid data value will be skipped.");
							userValue = null;
						}
						break;

					case "set":  // read from the set of check boxes
						userValue = [];
						if (pref.setDisplayOrder) {
							// read only those keys specified in the display order
							$.each(pref.setDisplayOrder, function(itemkey, item) {
								if (form[pref.name + "_" + item].checked) {
									userValue.push(item);
								}
							});
						} else {
							// read all the keys in the list of values
							$.each(pref.setValues, function(itemkey, itemvalue) {
								if (form[pref.name + "_" + itemkey].checked) {
									userValue.push(itemkey);
								}
							});
						}
						break;

					case "customList":  // read from the jQuery data stored on the button object
						userValue = $(form[pref.name]).data("value");
						break;

					default:
						alert("twinkleconfig: unknown data type for preference " + pref.name);
						break;
				}
			}

			// only save those preferences that are *different* from the default
			if (section.inFriendlyConfig) {
				if (userValue !== undefined && !compare(userValue, Twinkle.defaultConfig.friendly[pref.name])) {
					newConfig.friendly[pref.name] = userValue;
				}
				foundFriendlyPrefs.push(pref.name);
			} else {
				if (userValue !== undefined && !compare(userValue, Twinkle.defaultConfig.twinkle[pref.name])) {
					newConfig.twinkle[pref.name] = userValue;
				}
				foundTwinklePrefs.push(pref.name);
			}
		});
	});

	if (Twinkle.prefs) {
		$.each(Twinkle.prefs.twinkle, function(tkey, tvalue) {
			if (foundTwinklePrefs.indexOf(tkey) === -1) {
				newConfig.twinkle[tkey] = tvalue;
			}
		});
		$.each(Twinkle.prefs.friendly, function(fkey, fvalue) {
			if (foundFriendlyPrefs.indexOf(fkey) === -1) {
				newConfig.friendly[fkey] = fvalue;
			}
		});
	}

	var text =
		"// twinkleoptions.js: व्यक्तिगत ट्विंकल वरीयता फ़ाइल\n" +
		"//\n" +
		"// नोट: ट्विंकल वरीयताएँ परिवर्तित करने का सबसे आसान तरीका है \n//[[" +
		mw.config.get("wgPageName") + "|Twinkle preferences panel]] का प्रयोग करना।\n" +
		"// यह फ़ाइल स्वचालित रूप से बनाई गई है।\n" +
		"// मान्य जावास्क्रिप्ट के अतिरिक्त\n" +
		"// आप जो भी बदलाव यहाँ करेंगे,\n" +
		"// वे अगली बारी ट्विंकल वरीयताएँ सहेजने पर\n" +
		"// अपने-आप हटा दिए जाएँगे।\n" +
		"// इस फ़ाइल को संपादित करते समय मान्य जावास्क्रिप्ट का ही प्रयोग करें।\n" +
		"\n" +
		"window.Twinkle.prefs = ";
	text += JSON.stringify(newConfig, null, 2);
	text +=
		";\n" +
		"\n" +
		"// End of twinkleoptions.js\n";

	pageobj.setPageText(text);
	pageobj.setEditSummary("ट्विंकल वरीयताएँ संजोयी जा रही हैं। स्वचालित सम्पादन: [[" + mw.config.get("wgPageName") + "]] से। ([[WP:TW|TW]])");
	pageobj.setCreateOption("recreate");
	pageobj.save(Twinkle.config.saveSuccess);
};

Twinkle.config.saveSuccess = function twinkleconfigSaveSuccess(pageobj) {
	pageobj.getStatusElement().info("successful");

	var noticebox = document.createElement("div");
	noticebox.className = "successbox";
	noticebox.style.fontSize = "100%";
	noticebox.style.marginTop = "2em";
	noticebox.innerHTML = "<p><b>आपकी ट्विंकल वरीयताएँ संजो दी गई हैं।</b></p><p>बदलाव देखने के लिए आपको <b>अपने ब्राउज़र की कैश मेमोरी खाली करनी होगी</b> (देखें <a href=\"" + mw.util.wikiGetlink("WP:BYPASS") + "\" title=\"WP:BYPASS\">WP:BYPASS</a>).</p>";
	Morebits.status.root.appendChild(noticebox);
	var noticeclear = document.createElement("br");
	noticeclear.style.clear = "both";
	Morebits.status.root.appendChild(noticeclear);
};

/*
****************************************
*** twinkledelimages.js: Batch deletion of images (sysops only)
****************************************
* Mode of invocation:     Tab ("Deli-batch")
* Active on:              Existing non-special pages
* Config directives in:   TwinkleConfig
*/

Twinkle.delimages = function twinkledeli() {
	if( mw.config.get( 'wgNamespaceNumber' ) < 0 || !mw.config.get( 'wgCurRevisionId' ) ) {
		return;
	}
	if( Morebits.userIsInGroup( 'sysop' ) ) {
		twAddPortletLink( Twinkle.delimages.callback, "Deli-batch", "tw-deli", "Delete files found on page" );
	}
};

Twinkle.delimages.unlinkCache = {};
Twinkle.delimages.callback = function twinkledeliCallback() {
	var Window = new Morebits.simpleWindow( 800, 400 );
	Window.setTitle( "Batch file deletion" );
	Window.setScriptName( "Twinkle" );
	Window.addFooterLink( "Twinkle help", "WP:TW/DOC#delimages" );

	var form = new Morebits.quickForm( Twinkle.delimages.callback.evaluate );
	form.append( {
		type: 'checkbox',
		list: [
			{
				label: 'Delete files',
				name: 'delete_image',
				value: 'delete',
				checked: true
			},
			{
				label: 'Unlink uses of this file',
				name: 'unlink_image',
				value: 'unlink',
				checked: true
			}
		]
	} );
	form.append( {
		type: 'textarea',
		name: 'reason',
		label: 'Reason: '
	} );
	var query;
	if( mw.config.get( 'wgNamespaceNumber' ) === 14 ) {  // Category:
		query = {
			'action': 'query',
			'generator': 'categorymembers',
			'gcmtitle': mw.config.get( 'wgPageName' ),
			'gcmnamespace': 6,  // File:
			'gcmlimit' : Twinkle.getPref('deliMax'), 
			'prop': [ 'imageinfo', 'categories', 'revisions' ],
			'grvlimit': 1,
			'grvprop': [ 'user' ]
		};
	} else {
		query = {
			'action': 'query',
			'generator': 'images',
			'titles': mw.config.get( 'wgPageName' ),
			'prop': [ 'imageinfo', 'categories', 'revisions' ],
			'gimlimit': 'max'
		};
	}
	var wikipedia_api = new Morebits.wiki.api( 'Grabbing files', query, function( self ) {
		var xmlDoc = self.responseXML;
		var images = $(xmlDoc).find('page[imagerepository="local"]');
		var list = [];

		$.each(images, function() {
			var $self = $(this);
			var image = $self.attr('title');
			var user = $self.find('imageinfo ii').attr('user');
			var last_edit = $self.find('revisions rev').attr('user');
			var disputed = $self.find('categories cl[title="Category:Contested candidates for speedy deletion"]').size() > 0;
			list.push( {
				'label': image + ' - author: ' + user + ', last edit from: ' + last_edit + ( disputed ? ' DISPUTED' : '' ),
				'value': image,
				'checked': !disputed
			});
		});

		self.params.form.append( {
			type: 'checkbox',
			name: 'images',
			list: list
		}
	);
	self.params.form.append( { type:'submit' } );

	var result = self.params.form.render();
	self.params.Window.setContent( result );


}  );

wikipedia_api.params = { form:form, Window:Window };
wikipedia_api.post();
var root = document.createElement( 'div' );
Morebits.status.init( root );
Window.setContent( root );
Window.display();
};

Twinkle.delimages.currentDeleteCounter = 0;
Twinkle.delimages.currentUnlinkCounter = 0;
Twinkle.delimages.currentdeletor = 0;
Twinkle.delimages.callback.evaluate = function twinkledeliCallbackEvaluate(event) {
	mw.config.set('wgPageName', mw.config.get('wgPageName').replace(/_/g, ' '));  // for queen/king/whatever and country!
	var images = event.target.getChecked( 'images' );
	var reason = event.target.reason.value;
	var delete_image = event.target.delete_image.checked;
	var unlink_image = event.target.unlink_image.checked;
	if( ! reason ) {
		return;
	}

	Morebits.simpleWindow.setButtonsEnabled( false );
	Morebits.status.init( event.target );

	function toCall( work ) {
		if( work.length === 0 && Twinkle.delimages.currentDeleteCounter <= 0 && Twinkle.delimages.currentUnlinkCounter <= 0 ) {
			window.clearInterval( Twinkle.delimages.currentdeletor );
			Morebits.wiki.removeCheckpoint();
			return;
		} else if( work.length !== 0 && Twinkle.delimages.currentDeleteCounter <= Twinkle.getPref('batchDeleteMinCutOff') && Twinkle.delimages.currentUnlinkCounter <= Twinkle.getPref('batchDeleteMinCutOff') ) {
			Twinkle.delimages.unlinkCache = []; // Clear the cache
			var images = work.shift();
			Twinkle.delimages.currentDeleteCounter = images.length;
			Twinkle.delimages.currentUnlinkCounter = images.length;
			var i;
			for( i = 0; i < images.length; ++i ) {
				var image = images[i];
				var query = {
					'action': 'query',
					'titles': image
				};
				var wikipedia_api = new Morebits.wiki.api( 'Checking if file ' + image + ' exists', query, Twinkle.delimages.callbacks.main );
				wikipedia_api.params = { image:image, reason:reason, unlink_image:unlink_image, delete_image:delete_image };
				wikipedia_api.post();
			}
		}
	}
	var work = Morebits.array.chunk( images, Twinkle.getPref('deliChunks') );
	Morebits.wiki.addCheckpoint();
	Twinkle.delimages.currentdeletor = window.setInterval( toCall, 1000, work );
};
Twinkle.delimages.callbacks = {
	main: function( self ) {
		var xmlDoc = self.responseXML;
		var $data = $(xmlDoc);

		var normal = $data.find('normalized n').attr('to');

		if( normal ) {
			self.params.image = normal;
		}

		var exists = $data.find('pages page[title="'+self.params.image.replace( /"/g, '\\"')+'"]:not([missing])').size() > 0;

		if( ! exists ) {
			self.statelem.error( "It seems that the page doesn't exists, perhaps it has already been deleted" );
			return;
		}
		if( self.params.unlink_image ) {
			var query = {
				'action': 'query',
				'list': 'imageusage',
				'iutitle': self.params.image,
				'iulimit': Morebits.userIsInGroup( 'sysop' ) ? 5000 : 500 // 500 is max for normal users, 5000 for bots and sysops
			};
			var wikipedia_api = new Morebits.wiki.api( 'Grabbing file links', query, Twinkle.delimages.callbacks.unlinkImageInstancesMain );
			wikipedia_api.params = self.params;
			wikipedia_api.post();
		}
		if( self.params.delete_image ) {

			var imagepage = new Morebits.wiki.page( self.params.image, 'Deleting image');
			imagepage.setEditSummary( "Deleted because \"" + self.params.reason + "\"." + Twinkle.getPref('deletionSummaryAd'));
			imagepage.deletePage();
		}
	},
	unlinkImageInstancesMain: function( self ) {
		var xmlDoc = self.responseXML;
		var instances = [];
		$(xmlDoc).find('imageusage iu').each(function(){
			instances.push($(this).attr('title'));
		});
		if( instances.length === 0 ) {
			--twinklebatchdelete.currentUnlinkCounter;
			return;
		}

		$.each( instances, function(k,title) {
			page = new Morebits.wiki.page(title, "Unlinking instances on " + title);
			page.setFollowRedirect(true);
			page.setCallbackParameters({'image': self.params.image, 'reason': self.params.reason});
			page.load(Twinkle.delimages.callbacks.unlinkImageInstances);

		});
	},
	unlinkImageInstances: function( self ) {
		var params = self.getCallbackParameters();
		var statelem = self.getStatusElement();

		var image = params.image.replace( /^(?:Image|File):/, '' );
		var old_text = self.getPageText();
		var wikiPage = new Morebits.wikitext.page( old_text );
		wikiPage.commentOutImage( image , 'Commented out because image was deleted' );
		var text = wikiPage.getText();

		if( text === old_text ) {
			statelem.error( 'failed to unlink image ' + image +' from ' + self.getPageName() );
			return;
		}
		self.setPageText(text);
		self.setEditSummary('Removing instance of file ' + image + " that has been deleted because \"" + params.reason + "\")" + "; " + Twinkle.getPref('deletionSummaryAd'));
		self.setCreateOption('nocreate');
		self.save();
	}
};

/*
 ****************************************
 *** twinklediff.js: Diff module
 ****************************************
 * Mode of invocation:     Tab on non-diff pages ("Last"); tabs on diff pages ("Since", "Since mine", "Current")
 * Active on:              Existing non-special pages
 * Config directives in:   TwinkleConfig
 */

Twinkle.diff = function twinklediff() { 
	if( mw.config.get('wgNamespaceNumber') < 0 || !mw.config.get('wgArticleId') ) {
		return;
	}

	var query = {
		'title': mw.config.get('wgPageName'),
		'diff': 'cur',
		'oldid': 'prev'
	};

	twAddPortletLink( mw.util.wikiScript("index")+ "?" + $.param( query ), 'Last', 'tw-lastdiff', 'Show most recent diff' );

	// Show additional tabs only on diff pages
	if (Morebits.queryString.exists('diff')) {
		twAddPortletLink(function(){ Twinkle.diff.evaluate(false); }, 'Since', 'tw-since', 'Show difference between last diff and the revision made by previous user' );
		twAddPortletLink( function(){ Twinkle.diff.evaluate(true); }, 'Since mine', 'tw-sincemine', 'Show difference between last diff and my last revision' );

		var oldid = /oldid=(.+)/.exec($('#mw-diff-ntitle1').find('strong a').first().attr("href"))[1];
		query = {
			'title': mw.config.get('wgPageName'),
			'diff': 'cur',
			'oldid' : oldid
		};
		twAddPortletLink( mw.util.wikiScript("index")+ "?" + $.param( query ), 'Current', 'tw-curdiff', 'Show difference to current revision' );
	}
};

Twinkle.diff.evaluate = function twinklediffEvaluate(me) {

	var user;
	if( me ) {
		user = mw.config.get('wgUserName');
	} else {
		var node = document.getElementById( 'mw-diff-ntitle2' );
		if( ! node ) {
			// nothing to do?
			return;
		}
		user = $(node).find('a').first().text();
	}
	var query = {
		'prop': 'revisions',
		'action': 'query',
		'titles': mw.config.get('wgPageName'),
		'rvlimit': 1, 
		'rvprop': [ 'ids', 'user' ],
		'rvstartid': mw.config.get('wgCurRevisionId') - 1, // i.e. not the current one
		'rvuser': user
	};
	Morebits.status.init( document.getElementById('mw-content-text') );
	var wikipedia_api = new Morebits.wiki.api( 'Grabbing data of initial contributor', query, Twinkle.diff.callbacks.main );
	wikipedia_api.params = { user: user };
	wikipedia_api.post();
};

Twinkle.diff.callbacks = {
	main: function( self ) {
		var xmlDoc = self.responseXML;
		var revid = $(xmlDoc).find('rev').attr('revid');

		if( ! revid ) {
			self.statelem.error( 'no suitable earlier revision found, or ' + self.params.user + ' is the only contributor. Aborting.' );
			return;
		}
		var query = {
			'title': mw.config.get('wgPageName'),
			'oldid': revid,
			'diff': mw.config.get('wgCurRevisionId')
		};
		window.location = mw.util.wikiScript('index') + '?' + Morebits.queryString.create( query );
	}
};

/*
 ****************************************
 *** twinklefluff.js: Revert/rollback module
 ****************************************
 * Mode of invocation:     Links on history, contributions, and diff pages
 * Active on:              Diff pages, history pages, contributions pages
 * Config directives in:   TwinkleConfig
 */

/**
 Twinklefluff revert and antivandalism utility
 */

Twinkle.fluff = {
	auto: function() {
		if( parseInt( Morebits.queryString.get('oldid'), 10) !== mw.config.get('wgCurRevisionId') ) {
			// not latest revision
			alert("रोलबैक नहीं किया जा सकता। पृष्ठ बदला जा चुका है।");
			return;
		}

		var vandal = $("#mw-diff-ntitle2").find("a.mw-userlink").text();

		Twinkle.fluff.revert( Morebits.queryString.get( 'twinklerevert' ), vandal, true );
	},
	normal: function() {

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

		if( mw.config.get('wgNamespaceNumber') === -1 && mw.config.get('wgCanonicalSpecialPageName') === "Contributions" ) {
			//Get the username these contributions are for
			username = decodeURIComponent(/wiki\/Special:Log\/(.+)$/.exec($('#contentSub').find('a[title^="Special:Log"]').last().attr("href").replace(/_/g, "%20"))[1]);
			if( Twinkle.getPref('showRollbackLinks').indexOf('contribs') !== -1 || 
				( mw.config.get('wgUserName') !== username && Twinkle.getPref('showRollbackLinks').indexOf('others') !== -1 ) || 
				( mw.config.get('wgUserName') === username && Twinkle.getPref('showRollbackLinks').indexOf('mine') !== -1 ) ) {
				var list = $("#mw-content-text").find("ul li:has(span.mw-uctop)");

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

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

				list.each(function(key, current) {
					var href = $(current).children("a:eq(1)").attr("href");
					current.appendChild( document.createTextNode(' ') );
					var tmpNode = revNode.cloneNode( true );
					tmpNode.firstChild.setAttribute( 'href', href + '&' + Morebits.queryString.create( { 'twinklerevert': 'norm' } ) );
					current.appendChild( tmpNode );
					current.appendChild( document.createTextNode(' ') );
					tmpNode = revVandNode.cloneNode( true );
					tmpNode.firstChild.setAttribute( 'href', href + '&' + Morebits.queryString.create( { 'twinklerevert': 'vand' } ) );
					current.appendChild( tmpNode );
				});
			}
		} else {
                        
			if( mw.config.get('wgCanonicalSpecialPageName') === "Undelete" ) {
				//You can't rollback deleted pages!
				return;
			}

			var body = document.getElementById('mw-content-text');

			var firstRev = $("div.firstrevisionheader").length;
			if( firstRev ) {
				// we have first revision here, nothing to do.
				return;
			}

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

			var old_rev_url = $("#mw-diff-otitle1").find("strong a").attr("href");

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

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

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

			var revertToRevisionLink = revertToRevision.appendChild( document.createElement('a') );
			revertToRevisionLink.href = "#";
			$(revertToRevisionLink).click(function(){
				Twinkle.fluff.revertToRevision(oldrev);
			});
			revertToRevisionLink.appendChild( spanTag( 'Black', '[' ) );
			revertToRevisionLink.appendChild( spanTag( 'SaddleBrown', 'restore this version' ) );
			revertToRevisionLink.appendChild( spanTag( 'Black', ']' ) );

			otitle.insertBefore( revertToRevision, otitle.firstChild );

			if( document.getElementById('differences-nextlink') ) {
				// Not latest revision
				curVersion = false;

				var new_rev_url = $("#mw-diff-ntitle1").find("strong a").attr("href");
				query = new Morebits.queryString( new_rev_url.split( '?', 2 )[1] );
				var newrev = query.get('oldid');
				revertToRevision = document.createElement('div');
				revertToRevision.setAttribute( 'id', 'tw-revert-to-nrevision' );
				revertToRevision.style.fontWeight = 'bold';
				revertToRevisionLink = revertToRevision.appendChild( document.createElement('a') );
				revertToRevisionLink.href = "#";
				$(revertToRevisionLink).click(function(){
					Twinkle.fluff.revertToRevision(newrev);
				});
				revertToRevisionLink.appendChild( spanTag( 'Black', '[' ) );
				revertToRevisionLink.appendChild( spanTag( 'SaddleBrown', 'restore this version' ) );
				revertToRevisionLink.appendChild( spanTag( 'Black', ']' ) );
				ntitle.insertBefore( revertToRevision, ntitle.firstChild );

				return;
			}
			if( Twinkle.getPref('showRollbackLinks').indexOf('diff') !== -1 ) {
				var vandal = $("#mw-diff-ntitle2").find("a").first().text();

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

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

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

				agfLink.href = "#"; 
				vandLink.href = "#"; 
				normLink.href = "#"; 
				$(agfLink).click(function(){
					Twinkle.fluff.revert('agf', vandal);
				});
				$(vandLink).click(function(){
					Twinkle.fluff.revert('vand', vandal);
				});
				$(normLink).click(function(){
					Twinkle.fluff.revert('norm', vandal);
				});

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

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

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

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

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

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

Twinkle.fluff.revert = function revertPage( type, vandal, autoRevert, rev, page ) {

	var pagename = page || mw.config.get('wgPageName');
	var revid = rev || mw.config.get('wgCurRevisionId');

	Morebits.status.init( document.getElementById('mw-content-text') );
	$( '#catlinks' ).remove();

	var params = {
		type: type,
		user: vandal,
		pagename: pagename,
		revid: revid,
		autoRevert: !!autoRevert
	};
	var query = {
		'action': 'query',
		'prop': ['info', 'revisions'],
		'titles': pagename,
		'rvlimit': 50, // max possible
		'rvprop': [ 'ids', 'timestamp', 'user', 'comment' ],
		'intoken': 'edit'
	};
	var wikipedia_api = new Morebits.wiki.api( 'Grabbing data of earlier revisions', query, Twinkle.fluff.callbacks.main );
	wikipedia_api.params = params;
	wikipedia_api.post();
};

Twinkle.fluff.revertToRevision = function revertToRevision( oldrev ) {

	Morebits.status.init( document.getElementById('mw-content-text') );

	var query = {
		'action': 'query',
		'prop': ['info',  'revisions'],
		'titles': mw.config.get('wgPageName'),
		'rvlimit': 1,
		'rvstartid': oldrev,
		'rvprop': [ 'ids', 'timestamp', 'user', 'comment' ],
		'intoken': 'edit',
		'format': 'xml'
	};
	var wikipedia_api = new Morebits.wiki.api( 'Grabbing data of the earlier revision', query, Twinkle.fluff.callbacks.toRevision.main );
	wikipedia_api.params = { rev: oldrev };
	wikipedia_api.post();
};

Twinkle.fluff.userIpLink = function( user ) {
	return (Morebits.isIPAddress(user) ? "[[Special:Contributions/" : "[[User:" ) + user + "|" + user + "]]";
};

Twinkle.fluff.callbacks = {
	toRevision: {
		main: function( self ) {
			var xmlDoc = self.responseXML;

			var lastrevid = parseInt( $(xmlDoc).find('page').attr('lastrevid'), 10);
			var touched = $(xmlDoc).find('page').attr('touched');
			var starttimestamp = $(xmlDoc).find('page').attr('starttimestamp');
			var edittoken = $(xmlDoc).find('page').attr('edittoken');
			var revertToRevID = $(xmlDoc).find('rev').attr('revid');
			var revertToUser = $(xmlDoc).find('rev').attr('user');

			if (revertToRevID !== self.params.rev) {
				self.statitem.error( 'The retrieved revision does not match the requested revision.  Aborting.' );
				return;
			}

			var optional_summary = prompt( "वापस लेने के लिये कोई कारण बताएँ:", "" );
			if (optional_summary === null)
			{
				self.statelem.error( 'Aborted by user.' );
				return;
			}
			var summary = "Reverted to revision " + revertToRevID + " by " + revertToUser + (optional_summary ? ": " + optional_summary : '') + "." +
				Twinkle.getPref('summaryAd');
		
			var query = { 
				'action': 'edit',
				'title': mw.config.get('wgPageName'),
				'summary': summary,
				'token': edittoken,
				'undo': lastrevid,
				'undoafter': revertToRevID,
				'basetimestamp': touched,
				'starttimestamp': starttimestamp,
				'watchlist': Twinkle.getPref('watchRevertedPages').indexOf( self.params.type ) !== -1 ? 'watch' : undefined,
				'minor': Twinkle.getPref('markRevertedPagesAsMinor').indexOf( self.params.type ) !== -1  ? true : undefined
			};

			Morebits.wiki.actionCompleted.redirect = mw.config.get('wgPageName');
			Morebits.wiki.actionCompleted.notice = "Reversion completed";

			var wikipedia_api = new Morebits.wiki.api( 'Saving reverted contents', query, Twinkle.fluff.callbacks.complete, self.statelem);
			wikipedia_api.params = self.params;
			wikipedia_api.post();

		}
	},
	main: function( self ) {
		var xmlDoc = self.responseXML;

		var lastrevid = parseInt( $(xmlDoc).find('page').attr('lastrevid'), 10);
		var touched = $(xmlDoc).find('page').attr('touched');
		var starttimestamp = $(xmlDoc).find('page').attr('starttimestamp');
		var edittoken = $(xmlDoc).find('page').attr('edittoken');
		var lastuser = $(xmlDoc).find('rev').attr('user');

		var revs = $(xmlDoc).find('rev');

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

		}

		if( Twinkle.fluff.whiteList.indexOf( self.params.user ) !== -1  ) {
			switch( self.params.type ) {
			case 'vand':
				Morebits.status.info( 'Info', [ 'Vandalism revert was chosen on ', Morebits.htmlNode( 'strong', self.params.user ), '. As this is a whitelisted bot, we assume you wanted to revert vandalism made by the previous user instead.' ] );
				index = 2;
				vandal = revs[1].getAttribute( 'user' );
				self.params.user = revs[1].getAttribute( 'user' );
				break;
			case 'agf':
				Morebits.status.warn( 'Notice', [ 'Good faith revert was chosen on ', Morebits.htmlNode( 'strong', self.params.user ), '. This is a whitelisted bot, it makes no sense at all to revert it as a good faith edit, will stop reverting.' ] );
				return;
			case 'norm':
				/* falls through */
			default:
				var cont = confirm( 'Normal revert was chosen, but the most recent edit was made by a whitelisted bot (' + self.params.user + '). Do you want to revert the revision before instead?' );
				if( cont ) {
					Morebits.status.info( 'Info', [ 'Normal revert was chosen on ', Morebits.htmlNode( 'strong', self.params.user ), '. This is a whitelisted bot, and per confirmation, we\'ll revert the previous revision instead.' ] );
					index = 2;
					self.params.user = revs[1].getAttribute( 'user' );
				} else {
					Morebits.status.warn( 'Notice', [ 'Normal revert was chosen on ', Morebits.htmlNode( 'strong', self.params.user ), '. This is a whitelisted bot, but per confirmation, revert on top revision will proceed.' ] );
				}
				break;
			}
		}
		var found = false;
		var count = 0;

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

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

		if( ! count ) {
			Morebits.status.error( 'Error', "We were to revert zero revisions. As that makes no sense, we'll stop reverting this time. It could be that the edit has already been reverted, but the revision ID was still the same." );
			return;
		}

		var good_revision = revs[ found ];
		var userHasAlreadyConfirmedAction = false;
		if (self.params.type !== 'vand' && count > 1) {
			if ( !confirm( self.params.user + ' has made ' + count + ' edits in a row. Are you sure you want to revert them all?') ) {
				Morebits.status.info( 'Notice', 'Stopping reverting per user input' );
				return;
			}
			userHasAlreadyConfirmedAction = true;
		}

		self.params.count = count;

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

		self.statelem.status( [ ' revision ', Morebits.htmlNode( 'strong', self.params.goodid ), ' that was made ', Morebits.htmlNode( 'strong', count ), ' revisions ago by ', Morebits.htmlNode( 'strong', self.params.gooduser ) ] );

		var summary, extra_summary, userstr, gooduserstr;
		switch( self.params.type ) {
		case 'agf':
			extra_summary = prompt( "An optional comment for the edit summary:", "" );
			if (extra_summary === null)
			{
				self.statelem.error( 'Aborted by user.' );
				return;
			}
			userHasAlreadyConfirmedAction = true;

			userstr = self.params.user;
			summary = "Reverted [[WP:AGF|good faith]] edits by [[Special:Contributions/" + userstr + "|" + userstr + "]] ([[User talk:" + 
				userstr + "|talk]])" + Twinkle.fluff.formatSummaryPostfix(extra_summary) + Twinkle.getPref('summaryAd');
			break;

		case 'vand':

			userstr = self.params.user;
			gooduserstr = self.params.gooduser;
			summary = "Reverted " + self.params.count + (self.params.count > 1 ? ' edits' : ' edit') + " by [[Special:Contributions/" +
				userstr + "|" + userstr + "]] ([[User talk:" + userstr + "|talk]]) identified as [[WP:VAND|vandalism]] to last revision by " +
				gooduserstr + "." + Twinkle.getPref('summaryAd');
			break;

		case 'norm':
			/* falls through */
		default:
			if( Twinkle.getPref('offerReasonOnNormalRevert') ) {
				extra_summary = prompt( "An optional comment for the edit summary:", "" );
				if (extra_summary === null)
				{
					self.statelem.error( 'Aborted by user.' );
					return;
				}
				userHasAlreadyConfirmedAction = true;
			}

			userstr = self.params.user;
			summary = "Reverted " + self.params.count + (self.params.count > 1 ? ' edits' : ' edit') + " by [[Special:Contributions/" + 
				userstr + "|" + userstr + "]] ([[User talk:" + userstr + "|talk]])" + Twinkle.fluff.formatSummaryPostfix(extra_summary) +
				Twinkle.getPref('summaryAd');
			break;
		}

		if (Twinkle.getPref('confirmOnFluff') && !userHasAlreadyConfirmedAction && !confirm("Reverting page: are you sure?")) {
			self.statelem.error( 'Aborted by user.' );
			return;
		}

		var query;
		if( (!self.params.autoRevert || Twinkle.getPref('openTalkPageOnAutoRevert')) && 
				Twinkle.getPref('openTalkPage').indexOf( self.params.type ) !== -1 &&
				mw.config.get('wgUserName') !== self.params.user ) {
			Morebits.status.info( 'Info', [ 'Opening user talk page edit form for user ', Morebits.htmlNode( 'strong', self.params.user ) ] );
			
			query = {
				'title': 'User talk:' + self.params.user,
				'action': 'edit',
				'preview': 'yes',
				'vanarticle': self.params.pagename.replace(/_/g, ' '),
				'vanarticlerevid': self.params.revid,
				'vanarticlegoodrevid': self.params.goodid,
				'type': self.params.type,
				'count': self.params.count
			};

			switch( Twinkle.getPref('userTalkPageMode') ) {
			case 'tab':
				window.open( mw.util.wikiScript('index') + '?' + Morebits.queryString.create( query ), '_tab' );
				break;
			case 'blank':
				window.open( mw.util.wikiScript('index') + '?' + Morebits.queryString.create( query ), '_blank', 'location=no,toolbar=no,status=no,directories=no,scrollbars=yes,width=1200,height=800' );
				break;
			case 'window':
				/* falls through */
			default:
				window.open( mw.util.wikiScript('index') + '?' + Morebits.queryString.create( query ), 
					( window.name === 'twinklewarnwindow' ? '_blank' : 'twinklewarnwindow' ),
					'location=no,toolbar=no,status=no,directories=no,scrollbars=yes,width=1200,height=800' );
				break;
			}
		}
		
		query = {
			'action': 'edit',
			'title': self.params.pagename,
			'summary': summary,
			'token': edittoken,
			'undo': lastrevid,
			'undoafter': self.params.goodid,
			'basetimestamp': touched,
			'starttimestamp': starttimestamp,
			'watchlist' :  Twinkle.getPref('watchRevertedPages').indexOf( self.params.type ) !== -1 ? 'watch' : undefined,
			'minor': Twinkle.getPref('markRevertedPagesAsMinor').indexOf( self.params.type ) !== -1 ? true : undefined
		};

		Morebits.wiki.actionCompleted.redirect = self.params.pagename;
		Morebits.wiki.actionCompleted.notice = "Reversion completed";

		var wikipedia_api = new Morebits.wiki.api( 'Saving reverted contents', query, Twinkle.fluff.callbacks.complete, self.statelem);
		wikipedia_api.params = self.params;
		wikipedia_api.post();

	},
	complete: function (apiobj) {
		var blacklist = $(apiobj.getXML()).find('edit').attr('spamblacklist');
		if (blacklist) {
			var code = document.createElement('code');
			code.style.fontFamily = "monospace";
			code.appendChild(document.createTextNode(blacklist));
			apiobj.statelem.error(['Could not rollback because the URL ', code, ' is on the spam blacklist.']);
		} else {
			apiobj.statelem.info("done");
		}
	}
};

Twinkle.fluff.formatSummaryPostfix = function(stringToAdd) {
	if (stringToAdd) {
		stringToAdd = ': ' + Morebits.string.toUpperCaseFirstChar(stringToAdd);
		if (stringToAdd.search(/[.?!;]$/) === -1) {
			stringToAdd = stringToAdd + '.';
		}
		return stringToAdd;
	}
	else {
		return '.';
	}
};

Twinkle.fluff.init = function twinklefluffinit() {
	if (twinkleUserAuthorized)
	{
		// a list of usernames, usually only bots, that vandalism revert is jumped over, that is
		// if vandalism revert was chosen on such username, then it's target is on the revision before.
		// This is for handeling quick bots that makes edits seconds after the original edit is made.
		// This only affect vandalism rollback, for good faith rollback, it will stop, indicating a bot 
		// has no faith, and for normal rollback, it will rollback that edit.
		Twinkle.fluff.whiteList = [
		];

		if ( Morebits.queryString.exists( 'twinklerevert' ) ) {
			Twinkle.fluff.auto();
		} else {
			Twinkle.fluff.normal();
		}
	}
};

/*
 ****************************************
 *** twinkleprotect.js: Protect/RPP module
 ****************************************
 * Mode of invocation:     Tab ("PP"/"RPP")
 * Active on:              Non-special pages
 * Config directives in:   TwinkleConfig
 */

// Note: a lot of code in this module is re-used/called by batchprotect.

Twinkle.protect = function twinkleprotect() {
	if ( mw.config.get('wgNamespaceNumber') < 0 ) {
		return;
	}

	twAddPortletLink(Twinkle.protect.callback, Morebits.userIsInGroup('sysop') ? "सुरक्षा" : "सुरक्षा", "tw-rpp",
		Morebits.userIsInGroup('sysop') ? "पृष्ठ सुरक्षित करें" : "पृष्ठ को सुरक्षित करने का अनुरोध करें" );
};

Twinkle.protect.callback = function twinkleprotectCallback() {
	if (!twinkleUserAuthorized) {
		alert("आपका अकाउंट ट्विंकल प्रयोग करने के लिये बहुत नया है।");
		return;
	}
	var Window = new Morebits.simpleWindow( 620, 530 );
	Window.setTitle( Morebits.userIsInGroup( 'sysop' ) ? "Apply, request or tag page protection" : "Request or tag page protection" );
	Window.setScriptName( "Twinkle" );
	Window.addFooterLink( "Protection templates", "Template:Protection templates" );
	Window.addFooterLink( "Protection policy", "WP:PROT" );
	Window.addFooterLink( "Twinkle help", "WP:TW/DOC#protect" );

	var form = new Morebits.quickForm( Twinkle.protect.callback.evaluate );
	var actionfield = form.append( {
			type: 'field',
			label: 'Type of action'
		} );
	if( Morebits.userIsInGroup( 'sysop' ) ) {
		actionfield.append( {
				type: 'radio',
				name: 'actiontype',
				event: Twinkle.protect.callback.changeAction,
				list: [
					{
						label: 'Protect page',
						value: 'protect',
						tooltip: 'Apply actual protection to the page.',
						checked: true
					}
				]
			} );
	}
	actionfield.append( {
			type: 'radio',
			name: 'actiontype',
			event: Twinkle.protect.callback.changeAction,
			list: [
				{
					label: 'Request page protection',
					value: 'request',
					tooltip: 'If you want to request protection via WP:RPP' + (Morebits.userIsInGroup('sysop') ? ' instead of doing the protection by yourself.' : '.'),
					checked: !Morebits.userIsInGroup('sysop')
				},
				{
					label: 'Tag page with protection template',
					value: 'tag',
					tooltip: 'If the protecting admin forgot to apply a protection template, or you have just protected the page without tagging, you can use this to apply the appropriate protection tag.',
					disabled: mw.config.get('wgArticleId') === 0
				}
			]
		} );

	form.append({ type: 'field', label: 'Preset', name: 'field_preset' });
	form.append({ type: 'field', label: '1', name: 'field1' });
	form.append({ type: 'field', label: '2', name: 'field2' });

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

	var result = form.render();
	Window.setContent( result );
	Window.display();

	// We must init the controls
	var evt = document.createEvent( "Event" );
	evt.initEvent( 'change', true, true );
	result.actiontype[0].dispatchEvent( evt );

	// get current protection level asynchronously
	Morebits.wiki.actionCompleted.postfix = false;  // avoid Action: completed notice
	if (Morebits.userIsInGroup('sysop')) {
		var query = {
			action: 'query',
			prop: 'info|flagged',
			inprop: 'protection',
			titles: mw.config.get('wgPageName')
		};
		Morebits.status.init($('div[name="currentprot"] span').last()[0]);
		var statelem = new Morebits.status("Current protection level");
		var wpapi = new Morebits.wiki.api("retrieving...", query, Twinkle.protect.callback.protectionLevel, statelem);
		wpapi.post();
	}
};

Twinkle.protect.protectionLevel = null;
Twinkle.protect.oldEditProtection = null;
Twinkle.protect.oldMoveProtection = null;

Twinkle.protect.callback.protectionLevel = function twinkleprotectCallbackProtectionLevel(apiobj) {
	var xml = apiobj.getXML();
	var result = [];

	Twinkle.protect.oldEditProtection = {level: 'all', expiry: 'infinity'};
	Twinkle.protect.oldMoveProtection = {level: 'all', expiry: 'infinity'};

	$(xml).find('pr').each(function(index, pr) {
		var $pr = $(pr);
		var boldnode = document.createElement('b');
		boldnode.textContent = Morebits.string.toUpperCaseFirstChar($pr.attr('type')) + ": " + $pr.attr('level');
		result.push(boldnode);
		if ($pr.attr('expiry') === 'infinity') {
			result.push(" (indefinite) ");
		} else {
			result.push(" (expires " + new Date($pr.attr('expiry')).toUTCString() + ") ");
		}
		if ($pr.attr('cascade') === '') {
			result.push("(cascading) ");
		}

		if ($pr.attr('type') === "edit") {
			Twinkle.protect.oldEditProtection = { 
				level: $pr.attr('level'),
				expiry: $pr.attr('expiry')
			};
		} else if ($pr.attr('type') === "move") {
			Twinkle.protect.oldMoveProtection = { 
				level: $pr.attr('level'),
				expiry: $pr.attr('expiry')
			};
		}
	});

	var $flagged = $(xml).find('flagged');
	if ($flagged.length) {
		var boldnode = document.createElement('b');
		// impossible for now to determine the PC level/expiry using API; bug 24068
		boldnode.textContent = "Pending changes: enabled";
		result.push(boldnode);
	}

	if (!result.length) {
		var boldnode = document.createElement('b');
		boldnode.textContent = "no protection";
		result.push(boldnode);
	}
	Twinkle.protect.protectionLevel = result;
	apiobj.statelem.info(result);
	window.setTimeout(function() { Morebits.wiki.actionCompleted.postfix = "completed"; }, 500);  // restore actionCompleted message
};

Twinkle.protect.callback.changeAction = function twinkleprotectCallbackChangeAction(e) {
	var field_preset;
	var field1;
	var field2;

	switch (e.target.values) {
		case 'protect':
			field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' });
			field_preset.append({
					type: 'select',
					name: 'category',
					label: 'Choose a preset:',
					event: Twinkle.protect.callback.changePreset,
					list: (mw.config.get('wgArticleId') ? Twinkle.protect.protectionTypes : Twinkle.protect.protectionTypesCreate)
				});

			field2 = new Morebits.quickForm.element({ type: 'field', label: 'Protection options', name: 'field2' });
			field2.append({ type: 'div', name: 'currentprot', label: ' ' });  // holds the current protection level, as filled out by the async callback
			// for existing pages
			if (mw.config.get('wgArticleId')) {
				field2.append({
						type: 'checkbox',
						name: 'editmodify',
						event: Twinkle.protect.formevents.editmodify,
						list: [
							{
								label: 'Modify edit protection',
								value: 'editmodify',
								tooltip: 'If this is turned off, the edit protection level, and expiry time, will be left as is.',
								checked: true
							}
						]
					});
				var editlevel = field2.append({
						type: 'select',
						name: 'editlevel',
						label: 'Edit protection:',
						event: Twinkle.protect.formevents.editlevel
					});
				editlevel.append({
						type: 'option',
						label: 'All',
						value: 'all'
					});
				editlevel.append({
						type: 'option',
						label: 'Autoconfirmed',
						value: 'autoconfirmed'
					});
				editlevel.append({
						type: 'option',
						label: 'Sysop',
						value: 'sysop',
						selected: true
					});
				field2.append({
						type: 'select',
						name: 'editexpiry',
						label: 'Expires:',
						event: function(e) {
							if (e.target.value === 'custom') {
								Twinkle.protect.doCustomExpiry(e.target);
							}
						},
						list: [
							{ label: '1 hour', value: '1 hour' },
							{ label: '2 hours', value: '2 hours' },
							{ label: '3 hours', value: '3 hours' },
							{ label: '6 hours', value: '6 hours' },
							{ label: '12 hours', value: '12 hours' },
							{ label: '1 day', value: '1 day' },
							{ label: '2 days', selected: true, value: '2 days' },
							{ label: '3 days', value: '3 days' },
							{ label: '4 days', value: '4 days' },
							{ label: '1 week', value: '1 week' },
							{ label: '2 weeks', value: '2 weeks' },
							{ label: '1 month', value: '1 month' },
							{ label: '2 months', value: '2 months' },
							{ label: '3 months', value: '3 months' },
							{ label: '1 year', value: '1 year' },
							{ label: 'indefinite', value:'indefinite' },
							{ label: 'Custom...', value: 'custom' }
						]
					});
				field2.append({
						type: 'checkbox',
						name: 'movemodify',
						event: Twinkle.protect.formevents.movemodify,
						list: [
							{
								label: 'Modify move protection',
								value: 'movemodify',
								tooltip: 'If this is turned off, the move protection level, and expiry time, will be left as is.',
								checked: true
							}
						]
					});
				var movelevel = field2.append({
						type: 'select',
						name: 'movelevel',
						label: 'Move protection:',
						event: Twinkle.protect.formevents.movelevel
					});
				movelevel.append({
						type: 'option',
						label: 'All',
						value: 'all'
					});
				movelevel.append({
						type: 'option',
						label: 'Autoconfirmed',
						value: 'autoconfirmed'
					});
				movelevel.append({
						type: 'option',
						label: 'Sysop',
						value: 'sysop',
						selected: true
					});
				field2.append({
						type: 'select',
						name: 'moveexpiry',
						label: 'Expires:',
						event: function(e) {
							if (e.target.value === 'custom') {
								Twinkle.protect.doCustomExpiry(e.target);
							}
						},
						list: [
							{ label: '1 hour', value: '1 hour' },
							{ label: '2 hours', value: '2 hours' },
							{ label: '3 hours', value: '3 hours' },
							{ label: '6 hours', value: '6 hours' },
							{ label: '12 hours', value: '12 hours' },
							{ label: '1 day', value: '1 day' },
							{ label: '2 days', value: '2 days' },
							{ label: '3 days', value: '3 days' },
							{ label: '4 days', value: '4 days' },
							{ label: '1 week', value: '1 week' },
							{ label: '2 weeks', value: '2 weeks' },
							{ label: '1 month', value: '1 month' },
							{ label: '2 months', value: '2 months' },
							{ label: '3 months', value: '3 months' },
							{ label: '1 year', value: '1 year' },
							{ label: 'indefinite', selected: true, value:'indefinite' },
							{ label: 'Custom...', value: 'custom' }
						]
					});
				field2.append({
						type: 'checkbox',
						name: 'pcmodify',
						event: Twinkle.protect.formevents.pcmodify,
						list: [
							{
								label: 'Modify pending changes protection',
								value: 'pcmodify',
								tooltip: 'If this is turned off, the pending changes level, and expiry time, will be left as is.',
								checked: true
							}
						]
					});
				var pclevel = field2.append({
						type: 'select',
						name: 'pclevel',
						label: 'Pending changes:',
						event: Twinkle.protect.formevents.pclevel
					});
				pclevel.append({
						type: 'option',
						label: 'None',
						value: 'none'
					});
				pclevel.append({
						type: 'option',
						label: 'Level 1',
						value: 'autoconfirmed',
						selected: true
					});
				pclevel.append({
						type: 'option',
						label: 'Level 2 (do not use)',
						value: 'review'
					});
				field2.append({
						type: 'select',
						name: 'pcexpiry',
						label: 'Expires:',
						event: function(e) {
							if (e.target.value === 'custom') {
								Twinkle.protect.doCustomExpiry(e.target);
							}
						},
						list: [
							{ label: '1 hour', value: '1 hour' },
							{ label: '2 hours', value: '2 hours' },
							{ label: '3 hours', value: '3 hours' },
							{ label: '6 hours', value: '6 hours' },
							{ label: '12 hours', value: '12 hours' },
							{ label: '1 day', value: '1 day' },
							{ label: '2 days', value: '2 days' },
							{ label: '3 days', value: '3 days' },
							{ label: '4 days', value: '4 days' },
							{ label: '1 week', value: '1 week' },
							{ label: '2 weeks', value: '2 weeks' },
							{ label: '1 month', selected: true, value: '1 month' },
							{ label: '2 months', value: '2 months' },
							{ label: '3 months', value: '3 months' },
							{ label: '1 year', value: '1 year' },
							{ label: 'indefinite', value:'indefinite' },
							{ label: 'Custom...', value: 'custom' }
						]
					});
			} else {  // for non-existing pages
				var createlevel = field2.append({
						type: 'select',
						name: 'createlevel',
						label: 'Create protection:',
						event: Twinkle.protect.formevents.createlevel
					});
				createlevel.append({
						type: 'option',
						label: 'All',
						value: 'all'
					});
				createlevel.append({
						type: 'option',
						label: 'Autoconfirmed',
						value: 'autoconfirmed'
					});
				createlevel.append({
						type: 'option',
						label: 'Sysop',
						value: 'sysop',
						selected: true
					});
				field2.append({
						type: 'select',
						name: 'createexpiry',
						label: 'Expires:',
						event: function(e) {
							if (e.target.value === 'custom') {
								Twinkle.protect.doCustomExpiry(e.target);
							}
						},
						list: [
							{ label: '1 hour', value: '1 hour' },
							{ label: '2 hours', value: '2 hours' },
							{ label: '3 hours', value: '3 hours' },
							{ label: '6 hours', value: '6 hours' },
							{ label: '12 hours', value: '12 hours' },
							{ label: '1 day', value: '1 day' },
							{ label: '2 days', value: '2 days' },
							{ label: '3 days', value: '3 days' },
							{ label: '4 days', value: '4 days' },
							{ label: '1 week', value: '1 week' },
							{ label: '2 weeks', value: '2 weeks' },
							{ label: '1 month', value: '1 month' },
							{ label: '2 months', value: '2 months' },
							{ label: '3 months', value: '3 months' },
							{ label: '1 year', value: '1 year' },
							{ label: 'indefinite', selected: true, value: 'indefinite' },
							{ label: 'Custom...', value: 'custom' }
						]
					});
			}
			field2.append({
					type: 'textarea',
					name: 'protectReason',
					label: 'Protection reason (for log):'
				});
			if (!mw.config.get('wgArticleId')) {  // tagging isn't relevant for non-existing pages
				break;
			}
			/* falls through */
		case 'tag':
			field1 = new Morebits.quickForm.element({ type: 'field', label: 'Tagging options', name: 'field1' });
			field1.append( {
					type: 'select',
					name: 'tagtype',
					label: 'Choose protection template:',
					list: Twinkle.protect.protectionTags,
					event: Twinkle.protect.formevents.tagtype
				} );
			field1.append( {
					type: 'checkbox',
					list: [
						{
							name: 'small',
							label: 'Iconify (small=yes)',
							tooltip: 'Will use the |small=yes feature of the template, and only render it as a keylock',
							checked: true
						},
						{
							name: 'noinclude',
							label: 'Wrap protection template with <noinclude>',
							tooltip: 'Will wrap the protection template in &lt;noinclude&gt; tags, so that it won\'t transclude',
							checked: (mw.config.get('wgNamespaceNumber') === 10)
						}
					]
				} );
			break;

		case 'request':
			field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Type of protection', name: 'field_preset' });
			field_preset.append({
					type: 'select',
					name: 'category',
					label: 'Type and reason:',
					event: Twinkle.protect.callback.changePreset,
					list: (mw.config.get('wgArticleId') ? Twinkle.protect.protectionTypes : Twinkle.protect.protectionTypesCreate)
				});

			field1 = new Morebits.quickForm.element({ type: 'field', label: 'Options', name: 'field1' });
			field1.append( {
					type: 'select',
					name: 'expiry',
					label: 'Duration: ',
					list: [
						{ label: 'Temporary', value: 'temporary' },
						{ label: 'Indefinite', value: 'indefinite' },
						{ label: '', selected: true, value: '' }
					]
				} );
			field1.append({
					type: 'textarea',
					name: 'reason',
					label: 'Reason: '
				});
			break;
		default:
			alert("Something's afoot in twinkleprotect");
			break;
	}

	var oldfield;
	if (field_preset) {
		oldfield = $(e.target.form).find('fieldset[name="field_preset"]')[0];
		oldfield.parentNode.replaceChild(field_preset.render(), oldfield);
	} else {
		$(e.target.form).find('fieldset[name="field_preset"]').css('display', 'none');
	}
	if (field1) {
		oldfield = $(e.target.form).find('fieldset[name="field1"]')[0];
		oldfield.parentNode.replaceChild(field1.render(), oldfield);
	} else {
		$(e.target.form).find('fieldset[name="field1"]').css('display', 'none');
	}
	if (field2) {
		oldfield = $(e.target.form).find('fieldset[name="field2"]')[0];
		oldfield.parentNode.replaceChild(field2.render(), oldfield);
	} else {
		$(e.target.form).find('fieldset[name="field2"]').css('display', 'none');
	}

	if (e.target.values === 'protect') {
		// fake a change event on the preset dropdown
		var evt = document.createEvent( "Event" );
		evt.initEvent( 'change', true, true );
		e.target.form.category.dispatchEvent( evt );

		// re-add protection level text, if it's available
		if (Twinkle.protect.protectionLevel) {
			Morebits.status.init($('div[name="currentprot"] span').last()[0]);
			// seems unneeded
			//Morebits.status.info("Current protection level", Twinkle.protect.protectionLevel);
		}

		// reduce vertical height of dialog
		$(e.target.form).find('fieldset[name="field2"] select').parent().css({ display: 'inline-block', marginRight: '0.5em' });
	}
};

Twinkle.protect.formevents = {
	editmodify: function twinkleprotectFormEditmodifyEvent(e) {
		e.target.form.editlevel.disabled = !e.target.checked;
		e.target.form.editexpiry.disabled = !e.target.checked || (e.target.form.editlevel.value === 'all');
		e.target.form.editlevel.style.color = e.target.form.editexpiry.style.color = (e.target.checked ? "" : "transparent");
	},
	editlevel: function twinkleprotectFormEditlevelEvent(e) {
		e.target.form.editexpiry.disabled = (e.target.value === 'all');
	},
	movemodify: function twinkleprotectFormMovemodifyEvent(e) {
		e.target.form.movelevel.disabled = !e.target.checked;
		e.target.form.moveexpiry.disabled = !e.target.checked || (e.target.form.movelevel.value === 'all');
		e.target.form.movelevel.style.color = e.target.form.moveexpiry.style.color = (e.target.checked ? "" : "transparent");
	},
	movelevel: function twinkleprotectFormMovelevelEvent(e) {
		e.target.form.moveexpiry.disabled = (e.target.value === 'all');
	},
	pcmodify: function twinkleprotectFormPcmodifyEvent(e) {
		e.target.form.pclevel.disabled = !e.target.checked;
		e.target.form.pcexpiry.disabled = !e.target.checked || (e.target.form.pclevel.value === 'none');
		e.target.form.pclevel.style.color = e.target.form.pcexpiry.style.color = (e.target.checked ? "" : "transparent");
	},
	pclevel: function twinkleprotectFormPclevelEvent(e) {
		e.target.form.pcexpiry.disabled = (e.target.value === 'none');
	},
	createlevel: function twinkleprotectFormCreatelevelEvent(e) {
		e.target.form.createexpiry.disabled = (e.target.value === 'all');
	},
	tagtype: function twinkleprotectFormTagtypeEvent(e) {
		e.target.form.small.disabled = e.target.form.noinclude.disabled = (e.target.value === 'none') || (e.target.value === 'noop');
	}
};

Twinkle.protect.doCustomExpiry = function twinkleprotectDoCustomExpiry(target) {
	var custom = prompt('Enter a custom expiry time.  \nYou can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', '');
	if (custom) {
		var option = document.createElement('option');
		option.setAttribute('value', custom);
		option.textContent = custom;
		target.appendChild(option);
		target.value = custom;
	}
};

Twinkle.protect.protectionTypes = [
	{
		label: 'Full protection',
		list: [
			{ label: 'Generic (full)', value: 'pp-protected' },
			{ label: 'Content dispute/edit warring (full)', value: 'pp-dispute' },
			{ label: 'Persistent vandalism (full)', value: 'pp-vandalism' },
			{ label: 'Highly visible template (full)', value: 'pp-template' },
			{ label: 'User talk of blocked user (full)', value: 'pp-usertalk' }
		]
	},
	{
		label: 'Semi-protection',
		list: [
			{ label: 'Generic (semi)', value: 'pp-semi-protected' },
			{ label: 'Persistent vandalism (semi)', selected: true, value: 'pp-semi-vandalism' },
			{ label: 'BLP policy violations (semi)', value: 'pp-semi-blp' },
			{ label: 'Sockpuppetry (semi)', value: 'pp-semi-sock' },
			{ label: 'User talk of blocked user (semi)', value: 'pp-semi-usertalk' }
		]
	},
	{
		label: 'Pending changes',
		list: [
			{ label: 'Generic (PC)', value: 'pp-pc-protected' },
			{ label: 'Persistent vandalism (PC)', value: 'pp-pc-vandalism' },
			{ label: 'BLP policy violations (PC)', value: 'pp-pc-blp' }
		]
	},
	{
		label: 'Move protection',
		list: [
			{ label: 'Generic (move)', value: 'pp-move' },
			{ label: 'Dispute/move warring (move)', value: 'pp-move-dispute' },
			{ label: 'Page-move vandalism (move)', value: 'pp-move-vandalism' },
			{ label: 'Highly visible page (move)', value: 'pp-move-indef' }
		]
	},
	{ label: 'Unprotection', value: 'unprotect' }
];

Twinkle.protect.protectionTypesCreate = [
	{
		label: 'Create protection',
		list: [
			{ label: 'Generic ({{pp-create}})', value: 'pp-create' },
			{ label: 'Offensive name', value: 'pp-create-offensive' },
			{ label: 'Repeatedly recreated', selected: true, value: 'pp-create-salt' },
			{ label: 'Recently deleted BLP', value: 'pp-create-blp' }
		]
	},
	{ label: 'Unprotection', value: 'unprotect' }
];

// NOTICE: keep this synched with [[MediaWiki:Protect-dropdown]]
// Also note: stabilize = Pending Changes level
Twinkle.protect.protectionPresetsInfo = {
	'pp-protected': {
		edit: 'sysop',
		move: 'sysop',
		reason: null
	},
	'pp-dispute': {
		edit: 'sysop',
		move: 'sysop',
		reason: '[[WP:PP#Content disputes|Edit warring / Content dispute]]'
	},
	'pp-vandalism': {
		edit: 'sysop',
		move: 'sysop',
		reason: 'Persistent [[WP:Vandalism|vandalism]]'
	},
	'pp-template': {
		edit: 'sysop',
		move: 'sysop',
		reason: '[[WP:High-risk templates|Highly visible template]]'
	},
	'pp-usertalk': {
		edit: 'sysop',
		move: 'sysop',
		reason: '[[WP:PP#Talk-page protection|Inappropriate use of user talk page while blocked]]'
	},
	'pp-semi-vandalism': {
		edit: 'autoconfirmed',
		reason: 'Persistent [[WP:Vandalism|vandalism]]',
		template: 'pp-vandalism'
	},
	'pp-semi-blp': {
		edit: 'autoconfirmed',
		reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons policy]]'
	},
	'pp-semi-usertalk': {
		edit: 'autoconfirmed',
		move: 'sysop',
		reason: '[[WP:PP#Talk-page protection|Inappropriate use of user talk page while blocked]]'
	},
	'pp-semi-template': {  // removed for now
		edit: 'autoconfirmed',
		move: 'sysop',
		reason: '[[WP:High-risk templates|Highly visible template]]',
		template: 'pp-template'
	},
	'pp-semi-sock': {
		edit: 'autoconfirmed',
		reason: 'Persistent [[WP:Sock puppetry|sock puppetry]]'
	},
	'pp-semi-protected': {
		edit: 'autoconfirmed',
		reason: null,
		template: 'pp-protected'
	},
	'pp-pc-vandalism': {
		stabilize: 'autoconfirmed',  // stabilize = Pending Changes
		reason: 'Persistent [[WP:Vandalism|vandalism]]',
		template: 'pp-pc1'
	},
	'pp-pc-blp': {
		stabilize: 'autoconfirmed',
		reason: 'Violations of the [[WP:BLP|biographies of living persons policy]]',
		template: 'pp-pc1'
	},
	'pp-pc-protected': {
		stabilize: 'autoconfirmed',
		reason: null,
		template: 'pp-pc1'
	},
	'pp-move': {
		move: 'sysop',
		reason: null
	},
	'pp-move-dispute': {
		move: 'sysop',
		reason: '[[WP:MOVP|Move warring]]'
	},
	'pp-move-vandalism': {
		move: 'sysop',
		reason: '[[WP:MOVP|Page-move vandalism]]'
	},
	'pp-move-indef': {
		move: 'sysop',
		reason: '[[WP:MOVP|Highly visible page]]'
	},
	'unprotect': {
		edit: 'all',
		move: 'all',
		stabilize: 'none',
		create: 'all',
		reason: null,
		template: 'none'
	},
	'pp-create-offensive': {
		create: 'sysop',
		reason: '[[WP:SALT|Offensive name]]'
	},
	'pp-create-salt': {
		create: 'sysop',
		reason: '[[WP:SALT|Repeatedly recreated]]'
	},
	'pp-create-blp': {
		create: 'sysop',
		reason: '[[WP:BLPDEL|Recently deleted BLP]]'
	},
	'pp-create': {
		create: 'sysop',
		reason: '{{pp-create}}'
	}
};

Twinkle.protect.protectionTags = [
	{
		label: 'None (remove existing protection templates)',
		value: 'none'
	},
	{
		label: 'None (do not remove existing protection templates)',
		value: 'noop'
	},
	{
		label: 'Full protection templates',
		list: [
			{ label: '{{pp-dispute}}: dispute/edit war', value: 'pp-dispute', selected: true },
			{ label: '{{pp-usertalk}}: blocked user talk', value: 'pp-usertalk' }
		]
	},
	{
		label: 'Full/semi-protection templates',
		list: [
			{ label: '{{pp-vandalism}}: vandalism', value: 'pp-vandalism' },
			{ label: '{{pp-template}}: high-risk template', value: 'pp-template' },
			{ label: '{{pp-protected}}: general protection', value: 'pp-protected' }
		]
	},
	{
		label: 'Semi-protection templates',
		list: [
			{ label: '{{pp-semi-usertalk}}: blocked user talk', value: 'pp-semi-usertalk' },
			{ label: '{{pp-semi-sock}}: sockpuppetry', value: 'pp-semi-sock' },
			{ label: '{{pp-semi-blp}}: BLP violations', value: 'pp-semi-blp' },
			{ label: '{{pp-semi-indef}}: general long-term', value: 'pp-semi-indef' }
		]
	},
	{
		label: 'Pending changes templates',
		list: [
			{ label: '{{pp-pc1}}: pending changes level 1', value: 'pp-pc1' }
		]
	},
	{
		label: 'Move protection templates',
		list: [
			{ label: '{{pp-move-dispute}}: dispute/move war', value: 'pp-move-dispute' },
			{ label: '{{pp-move-vandalism}}: page-move vandalism', value: 'pp-move-vandalism' },
			{ label: '{{pp-move-indef}}: general long-term', value: 'pp-move-indef' },
			{ label: '{{pp-move}}: other', value: 'pp-move' }
		]
	}
];

Twinkle.protect.callback.changePreset = function twinkleprotectCallbackChangePreset(e) {
	var form = e.target.form;

	var actiontypes = form.actiontype;
	var actiontype;
	for( var i = 0; i < actiontypes.length; i++ )
	{
		if( !actiontypes[i].checked ) {
			continue;
		}
		actiontype = actiontypes[i].values;
		break;
	}

	if (actiontype === 'protect') {  // actually protecting the page
		var item = Twinkle.protect.protectionPresetsInfo[form.category.value];
		if (mw.config.get('wgArticleId')) {
			if (item.edit) {
				form.editmodify.checked = true;
				Twinkle.protect.formevents.editmodify({ target: form.editmodify });
				form.editlevel.value = item.edit;
				Twinkle.protect.formevents.editlevel({ target: form.editlevel });
			} else {
				form.editmodify.checked = false;
				Twinkle.protect.formevents.editmodify({ target: form.editmodify });
			}

			if (item.move) {
				form.movemodify.checked = true;
				Twinkle.protect.formevents.movemodify({ target: form.movemodify });
				form.movelevel.value = item.move;
				Twinkle.protect.formevents.movelevel({ target: form.movelevel });
			} else {
				form.movemodify.checked = false;
				Twinkle.protect.formevents.movemodify({ target: form.movemodify });
			}
			
			if (item.stabilize) {
				form.pcmodify.checked = true;
				Twinkle.protect.formevents.pcmodify({ target: form.pcmodify });
				form.pclevel.value = item.stabilize;
				Twinkle.protect.formevents.pclevel({ target: form.pclevel });
			} else {
				form.pcmodify.checked = false;
				Twinkle.protect.formevents.pcmodify({ target: form.pcmodify });
			}
		} else {
			if (item.create) {
				form.createlevel.value = item.create;
				Twinkle.protect.formevents.createlevel({ target: form.createlevel });
			}
		}

		var reasonField = (actiontype === "protect" ? form.protectReason : form.reason);
		if (item.reason) {
			reasonField.value = item.reason;
		} else {
			reasonField.value = '';
		}

		// sort out tagging options
		if (mw.config.get('wgArticleId')) {
			if( form.category.value === 'unprotect' ) {
				form.tagtype.value = 'none';
			} else {
				form.tagtype.value = (item.template ? item.template : form.category.value);
			}
			Twinkle.protect.formevents.tagtype({ target: form.tagtype });

			if( /template/.test( form.category.value ) ) {
				form.noinclude.checked = true;
				form.editexpiry.value = form.moveexpiry.value = form.pcexpiry.value = "indefinite";
			} else {
				form.noinclude.checked = false;
			}
		}

	} else {  // RPP request
		if( form.category.value === 'unprotect' ) {
			form.expiry.value = '';
			form.expiry.disabled = true;
		} else {
			form.expiry.disabled = false;
		}
	}
};

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

	var actiontypes = form.actiontype;
	var actiontype;
	for( var i = 0; i < actiontypes.length; i++ )
	{
		if( !actiontypes[i].checked ) {
			continue;
		}
		actiontype = actiontypes[i].values;
		break;
	}

	if( actiontype === 'tag' || (actiontype === 'protect' && mw.config.get('wgArticleId')) ) {
		tagparams = {
			tag: form.tagtype.value,
			reason: ((form.tagtype.value === 'pp-protected' || form.tagtype.value === 'pp-semi-protected' || form.tagtype.value === 'pp-move') && form.protectReason) ? form.protectReason.value : null,
			expiry: (actiontype === 'protect') ? (form.editmodify.checked ? form.editexpiry.value : (form.movemodify.checked ?
				form.moveexpiry.value : null)) : null,
			small: form.small.checked,
			noinclude: form.noinclude.checked
		};
	}

	switch (actiontype) {
		case 'protect':
			// protect the page

			Morebits.wiki.actionCompleted.redirect = mw.config.get('wgPageName');
			Morebits.wiki.actionCompleted.notice = "Protection complete";

			var statusInited = false;
			var thispage;
			
			var allDone = function twinkleprotectCallbackAllDone() { 
				if (thispage) {
					thispage.getStatusElement().info("done");
				}
				if (tagparams) {
					Twinkle.protect.callbacks.taggingPageInitial(tagparams);
				}
			};

			var protectIt = function twinkleprotectCallbackProtectIt(next) {
				thispage = new Morebits.wiki.page(mw.config.get('wgPageName'), "Protecting page");
				if (mw.config.get('wgArticleId')) {
					if (form.editmodify.checked) {
						thispage.setEditProtection(form.editlevel.value, form.editexpiry.value);
					} else if (form.movemodify.checked && Twinkle.protect.oldEditProtection) {
						if (Twinkle.protect.oldEditProtection.expiry === "infinity") {
							Twinkle.protect.oldEditProtection.expiry = "indefinite";  // stupid API bug
						}
						thispage.setEditProtection(Twinkle.protect.oldEditProtection.level,
							Twinkle.protect.oldEditProtection.expiry);
					} else {
						alert("Twinkle couldn't retrieve the existing protection settings.");
						return;
					}

					if (form.movemodify.checked) {
						thispage.setMoveProtection(form.movelevel.value, form.moveexpiry.value);
					} else if (form.editmodify.checked && Twinkle.protect.oldMoveProtection) {
						if (Twinkle.protect.oldMoveProtection.expiry === "infinity") {
							Twinkle.protect.oldMoveProtection.expiry = "indefinite";  // stupid API bug
						}
						thispage.setMoveProtection(Twinkle.protect.oldMoveProtection.level,
							Twinkle.protect.oldMoveProtection.expiry);
					} else {
						alert("Twinkle couldn't retrieve the existing protection settings.");
						return;
					}
				} else {
					thispage.setCreateProtection(form.createlevel.value, form.createexpiry.value);
				}
				
				if (form.protectReason.value) {
					thispage.setEditSummary(form.protectReason.value);
				} else {
					alert("You must enter a protect reason, which will be inscribed into the protection log.");
					return;
				}

				if (!statusInited) {
					Morebits.simpleWindow.setButtonsEnabled( false );
					Morebits.status.init( form );
					statusInited = true;
				}

				thispage.protect(next);
			};
			
			var stabilizeIt = function twinkleprotectCallbackStabilizeIt() {
				if (thispage) {
					thispage.getStatusElement().info("done");
				}

				thispage = new Morebits.wiki.page(mw.config.get('wgPageName'), "Applying pending changes protection");
				thispage.setFlaggedRevs(form.pclevel.value, form.pcexpiry.value);
				
				if (form.protectReason.value) {
					thispage.setEditSummary(form.protectReason.value);
				} else {
					alert("You must enter a protect reason, which will be inscribed into the protection log.");
					return;
				}

				if (!statusInited) {
					Morebits.simpleWindow.setButtonsEnabled(false);
					Morebits.status.init(form);
					statusInited = true;
				}

				thispage.stabilize(allDone);
			};
			
			if ((form.editmodify && form.editmodify.checked) || (form.movemodify && form.movemodify.checked) || 
				!mw.config.get('wgArticleId')) {
				if (form.pcmodify && form.pcmodify.checked) {
					protectIt(stabilizeIt);
				} else {
					protectIt(allDone);
				}
			} else if (form.pcmodify && form.pcmodify.checked) {
				stabilizeIt();
			} else {
				alert("Please give Twinkle something to do! \nIf you just want to tag the page, you can choose the 'Tag page with protection template' option at the top.");
			}
			
			break;

		case 'tag':
			// apply a protection template

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

			Morebits.wiki.actionCompleted.redirect = mw.config.get('wgPageName');
			Morebits.wiki.actionCompleted.followRedirect = false;
			Morebits.wiki.actionCompleted.notice = "टैगिंग संपूर्ण";

			Twinkle.protect.callbacks.taggingPageInitial(tagparams);
			break;

		case 'request':
			// file request at RPP
			var typename, typereason;
			switch( form.category.value ) {
				case 'pp-dispute':
				case 'pp-vandalism':
				case 'pp-template':
				case 'pp-usertalk':
				case 'pp-protected':
					typename = 'full protection';
					break;
				case 'pp-semi-vandalism':
				case 'pp-semi-usertalk':
				case 'pp-semi-template':  // removed for now
				case 'pp-semi-sock':
				case 'pp-semi-blp':
				case 'pp-semi-protected':
					typename = 'semi-protection';
					break;
				case 'pp-pc-vandalism':
				case 'pp-pc-blp':
				case 'pp-pc-protected':
					typename = 'pending changes';
					break;
				case 'pp-move':
				case 'pp-move-dispute':
				case 'pp-move-indef':
				case 'pp-move-vandalism':
					typename = 'move protection';
					break;
				case 'pp-create':
				case 'pp-create-offensive':
				case 'pp-create-blp':
				case 'pp-create-salt':
					typename = 'create protection';
					break;
				case 'unprotect':
					/* falls through */
				default:
					typename = 'unprotection';
					break;
			}
			switch (form.category.value) {
				case 'pp-dispute':
					typereason = 'Content dispute/edit warring';
					break;
				case 'pp-vandalism':
				case 'pp-semi-vandalism':
				case 'pp-pc-vandalism':
					typereason = 'Persistent vandalism';
					break;
				case 'pp-template':
				case 'pp-semi-template':  // removed for now
					typereason = 'Highly visible template';
					break;
				case 'pp-usertalk':
				case 'pp-semi-usertalk':
					typereason = 'Inappropriate use of user talk page while blocked';
					break;
				case 'pp-semi-sock':
					typereason = 'Persistent sockpuppetry';
					break;
				case 'pp-semi-blp':
				case 'pp-pc-blp':
					typereason = '[[WP:BLP|BLP]] policy violations';
					break;
				case 'pp-move-dispute':
					typereason = 'Page title dispute/move warring';
					break;
				case 'pp-move-vandalism':
					typereason = 'Page-move vandalism';
					break;
				case 'pp-move-indef':
					typereason = 'Highly visible page';
					break;
				case 'pp-create-offensive':
					typereason = 'Offensive name';
					break;
				case 'pp-create-blp':
					typereason = 'Recently deleted [[WP:BLP|BLP]]';
					break;
				case 'pp-create-salt':
					typereason = 'Repeatedly recreated';
					break;
				default:
					typereason = '';
					break;
			}

			var reason = typereason;
			if( form.reason.value !== '') {
				if ( typereason !== '' ) {
					reason += "\u00A0\u2013 ";  // U+00A0 NO-BREAK SPACE; U+2013 EN RULE
				}
				reason += form.reason.value;
			}
			if( reason !== '' && reason.charAt( reason.length - 1 ) !== '.' ) {
				reason += '.';
			}

			var rppparams = {
				reason: reason,
				typename: typename,
				category: form.category.value,
				expiry: form.expiry.value
			};

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

			rppName = 'विकिपीडिया:पृष्ठ सुरक्षा अनुरोध';

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

			var rppPage = new Morebits.wiki.page( rppName, 'Requesting protection of page');
			rppPage.setFollowRedirect( true );
			rppPage.setCallbackParameters( rppparams );
			rppPage.load( Twinkle.protect.callbacks.fileRequest );
			break;
		default:
			alert("twinkleprotect: unknown kind of action");
			break;
	}
};

Twinkle.protect.callbacks = {
	taggingPageInitial: function( tagparams ) {
		if (tagparams.tag === 'noop') {
			Morebits.status.info("Applying protection template", "nothing to do");
			return;
		}

		var protectedPage = new Morebits.wiki.page( mw.config.get('wgPageName'), 'Tagging page');
		protectedPage.setCallbackParameters( tagparams );
		protectedPage.load( Twinkle.protect.callbacks.taggingPage );
	},
	taggingPage: function( protectedPage ) {
		var params = protectedPage.getCallbackParameters();
		var text = protectedPage.getPageText();
		var tag, summary;

		var oldtag_re = /\s*(?:<noinclude>)?\s*\{\{\s*(pp-[^{}]*?|protected|(?:t|v|s|p-|usertalk-v|usertalk-s|sb|move)protected(?:2)?|protected template|privacy protection)\s*?\}\}\s*(?:<\/noinclude>)?\s*/gi;
		var re_result = oldtag_re.exec(text);
		if (re_result) {
			if (confirm("{{" + re_result[1] + "}} was found on the page. \nClick OK to remove it, or click Cancel to leave it there.")) {
				text = text.replace( oldtag_re, '' );
			}
		}

		if ( params.tag !== 'none' ) {
			tag = params.tag;
			if( params.reason ) {
				tag += '|reason=' + params.reason;
			}
			if( ['indefinite', 'infinite', 'never', null].indexOf(params.expiry) === -1 ) {
				tag += '|expiry={{subst:#time:j F Y|' + (/^\s*\d+\s*$/.exec(params.expiry) ? params.expiry : '+' + params.expiry) + '}}';
			}
			if( params.small ) {
				tag += '|small=yes';
			}
		}

		if( params.tag === 'none' ) {
			summary = 'Removing protection template' + Twinkle.getPref('summaryAd');
		} else {
			if( params.noinclude ) {
				text = "<noinclude>{{" + tag + "}}</noinclude>" + text;
			} else if( Morebits.wiki.isPageRedirect() ) {
				text = text + "\n{{" + tag + "}}";
			} else {
				text = "{{" + tag + "}}\n" + text;
			}
			summary = "Adding {{" + params.tag + "}}" + Twinkle.getPref('summaryAd');
		}

		protectedPage.setEditSummary( summary );
		protectedPage.setPageText( text );
		protectedPage.setCreateOption( 'nocreate' );
		protectedPage.save();
	},

	fileRequest: function( rppPage ) {

		var params = rppPage.getCallbackParameters();
		var text = rppPage.getPageText();
		var statusElement = rppPage.getStatusElement();

		var ns2tag = {
			'0': 'la',
			'1': 'lat',
			'2': 'lu',
			'3': 'lut',
			'4': 'lw',
			'5': 'lwt',
			'6': 'lf',
			'7': 'lft',
			'8': 'lm',
			'9': 'lmt',
			'10': 'lt',
			'11': 'ltt',
			'12': 'lh',
			'13': 'lht',
			'14': 'lc',
			'15': 'lct',
			'100': 'lp',
			'101': 'lpt',
			'108': 'lb',
			'109': 'lbt'
		};

		var rppRe = new RegExp( '====\\s*\\{\\{\\s*' + ns2tag[ mw.config.get('wgNamespaceNumber') ] + '\\s*\\|\\s*' + RegExp.escape( mw.config.get('wgTitle'), true ) + '\\s*\\}\\}\\s*====', 'm' );
		var tag = rppRe.exec( text );

		var rppLink = document.createElement('a');
		rppLink.setAttribute('href', mw.util.wikiGetlink(rppPage.getPageName()) );
		rppLink.appendChild(document.createTextNode(rppPage.getPageName()));

		if ( tag ) {
			statusElement.error( [ 'There is already a protection request for this page at ', rppLink, ', aborting.' ] );
			return;
		}

		var newtag = '==== {{' + ns2tag[ mw.config.get('wgNamespaceNumber') ] + '|' + mw.config.get('wgTitle') +  '}} ====' + "\n";
		if( ( new RegExp( '^' + RegExp.escape( newtag ).replace( /\s+/g, '\\s*' ), 'm' ) ).test( text ) ) {
			statusElement.error( [ 'There is already a protection request for this page at ', rppLink, ', aborting.' ] );
			return;
		}

		var words;
		switch( params.expiry ) {
		case 'temporary':
			words = "Temporary ";
			break;
		case 'indefinite':
			words = "Indefinite ";
			break;
		default:
			words = "";
			break;
		}

		words += params.typename;

		newtag += "'''" + Morebits.string.toUpperCaseFirstChar(words) + ( params.reason !== '' ? ":''' " + params.reason : ".'''" ) + " ~~~~";

		var reg;
		if ( params.category === 'unprotect' ) {
			reg = /(\n==\s*सुरक्षा हटाने हेतु वर्तमान अनुरोध\s*==\s*)/;
		} else {
			reg = /(\n==\s*सुरक्षित करने हेतु वर्तमान अनुरोध\s*==\s*)/;
		}
		var originalTextLength = text.length;
		text = text.replace( reg, "$1" + newtag + "\n");
		if (text.length === originalTextLength)
		{
			var linknode = document.createElement('a');
			linknode.setAttribute("href", mw.util.wikiGetlink("Wikipedia:Twinkle/Fixing RPP") );
			linknode.appendChild(document.createTextNode('How to fix RPP'));
			statusElement.error( [ 'Could not find relevant heading on WP:RPP. To fix this problem, please see ', linknode, '.' ] );
			return;
		}
		statusElement.status( 'Adding new request...' );
		rppPage.setEditSummary( "Requesting " + params.typename + ' of [[' + mw.config.get('wgPageName').replace(/_/g, ' ') + ']].' + Twinkle.getPref('summaryAd') );
		rppPage.setPageText( text );
		rppPage.setCreateOption( 'recreate' );
		rppPage.save();
	}
};

/*
 ****************************************
 *** twinklespeedy.js: CSD module
 ****************************************
 * Mode of invocation:     Tab ("CSD")
 * Active on:              Non-special, existing pages
 * Config directives in:   TwinkleConfig
 *
 * NOTE FOR DEVELOPERS:
 *   If adding a new criterion, check out the default values of the CSD preferences
 *   in twinkle.header.js, and add your new criterion to those if you think it would
 *   be good. 
 */

Twinkle.speedy = function twinklespeedy() {
	// Disable on:
	// * special pages
	// * non-existent pages
	if (mw.config.get('wgNamespaceNumber') < 0 || !mw.config.get('wgArticleId')) {
		return;
	}

	twAddPortletLink( Twinkle.speedy.callback, "शीह", "tw-csd", Morebits.userIsInGroup('sysop') ? "शीघ्र हटाने के मापदंडों अनुसार पृष्ठ को हटाएँ" : "शीघ्र हटाने का नामांकन करें" );
};

// This function is run when the CSD tab/header link is clicked
Twinkle.speedy.callback = function twinklespeedyCallback() {
	if ( !twinkleUserAuthorized ) {
		alert("आपका अकाउंट ट्विंकल प्रयोग करने के लिये बहुत नया है।");
		return;
	}

	Twinkle.speedy.initDialog(Morebits.userIsInGroup( 'sysop' ) ? Twinkle.speedy.callback.evaluateSysop : Twinkle.speedy.callback.evaluateUser, true);
};

Twinkle.speedy.dialog = null;
// Prepares the speedy deletion dialog and displays it
// Parameters:
//  - callbackfunc: the function to call when the dialog box is submitted
//  - firstTime: is this the first time? (false during a db-multiple run, true otherwise)
//  - content: (optional) a div element in which the form content should be rendered - allows
//    for placing content in an existing dialog box
Twinkle.speedy.initDialog = function twinklespeedyInitDialog(callbackfunc, firstTime, content) {
	var dialog;
	if (!content)
	{
		Twinkle.speedy.dialog = new Morebits.simpleWindow( Twinkle.getPref('speedyWindowWidth'), Twinkle.getPref('speedyWindowHeight') );
		dialog = Twinkle.speedy.dialog;
		dialog.setTitle( "शीघ्र हटाने के लिये मापदंड चुनें" );
		dialog.setScriptName( "Twinkle" );
		dialog.addFooterLink( "पृष्ठ हटाने की नीति", "वि:हटाना" );
		dialog.addFooterLink( "Twinkle help", "WP:TW/DOC#speedy" );
	}

	var form = new Morebits.quickForm( callbackfunc, 'change' );
	if( firstTime && Morebits.userIsInGroup( 'sysop' ) ) {
		form.append( {
				type: 'checkbox',
				list: [
					{
						label: 'केवल टैग करें',
						value: 'tag_only',
						name: 'tag_only',
						tooltip: 'यदि आप पृष्ठ को हटाने के बजाए सिर्फ़ टैग करना चाहते हैं',
						checked : Twinkle.getPref('deleteSysopDefaultToTag'),
						event: function( event ) {
							// enable/disable notify checkbox
							event.target.form.notify.disabled = !event.target.checked;
							event.target.form.notify.checked = event.target.checked;
							// enable/disable talk page checkbox
							if (event.target.form.talkpage) {
								event.target.form.talkpage.disabled = event.target.checked;
								event.target.form.talkpage.checked = !event.target.checked && Twinkle.getPref('deleteTalkPageOnDelete');
							}
							// enable/disable redirects checkbox
							event.target.form.redirects.disabled = event.target.checked;
							event.target.form.redirects.checked = !event.target.checked;
							// enable/disable multiple
							$(event.target.form).find('input[name="csd"][value="अनेक"]')[0].disabled = !event.target.checked;
							event.stopPropagation();
						}
					}
				]
			} );
		form.append( { type: 'header', label: 'हटाने सम्बंधित विकल्प' } );
		if (mw.config.get('wgNamespaceNumber') % 2 === 0 && (mw.config.get('wgNamespaceNumber') !== 2 || (/\//).test(mw.config.get('wgTitle')))) {  // hide option for user pages, to avoid accidentally deleting user talk page
			form.append( {
				type: 'checkbox',
				list: [
					{
						label: 'वार्ता पृष्ठ भी हटाएँ',
						value: 'talkpage',
						name: 'talkpage',
						tooltip: "यह विकल्प पृष्ठ के साथ-साथ उसके वार्ता पृष्ठ को भी हटाता है।",
						checked: Twinkle.getPref('deleteTalkPageOnDelete'),
						disabled: Twinkle.getPref('deleteSysopDefaultToTag'),
						event: function( event ) {
							event.stopPropagation();
						}
					}
				]
			} );
		}
		form.append( {
				type: 'checkbox',
				list: [
					{
						label: 'सभी पुनर्निर्देश भी हटाएँ',
						value: 'redirects',
						name: 'redirects',
						tooltip: "यह विकल्प पृष्ठ को आ रहे सभी पुनार्निर्देशों को भी हटाता है। यदि लेख का विषय ज्ञानकोशीय हो तो आम तौर पर ऐसा नहीं किया जाना चाहिये।",
						checked: true,
						disabled: Twinkle.getPref('deleteSysopDefaultToTag'),
						event: function( event ) {
							event.stopPropagation();
						}
					}
				]
			} );
		form.append( { type: 'header', label: 'टैग संबंधी विकल्प' } );
	}

	// don't show this notification checkbox for db-multiple, as the value is ignored
	// XXX currently not possible to turn off notification when using db-multiple
	if (firstTime) {
		form.append( {
				type: 'checkbox',
				list: [
					{
						label: 'यदि संभव हो तो पृष्ठ निर्माता को सूचित करें',
						value: 'notify',
						name: 'notify',
						tooltip: "यदि यह विकल्प सक्षम है, और आपके Twinkle Preferences में सूचना देना सक्षम है, तो पृष्ठ निर्माता के वार्ता पृष्ठ पर एक सूचना साँचा जोड़ दिया जाएगा। " +
							"यदि आपके Twinkle Preferences में आपके द्वारा चुने मापदंड के लिये स्वागत सक्षम है, तो सदस्य का स्वागत भी किया जाएगा।",
						checked: !Morebits.userIsInGroup( 'sysop' ) || Twinkle.getPref('deleteSysopDefaultToTag'),
						disabled: Morebits.userIsInGroup( 'sysop' ) && !Twinkle.getPref('deleteSysopDefaultToTag'),
						event: function( event ) {
							event.stopPropagation();
						}
					}
				]
			}
		);
	} else {
		form.append( { type:'header', label: '{{शीह-अनेक}} के साथ टैगिंग' } );
	}

	if (firstTime) {
		form.append( { type: 'radio', name: 'csd',
			list: [
				{
					label: 'अनेक मापदंडों के साथ टैग करें',
					value: 'अनेक',
					tooltip: 'Twinkle की विंडो की एक श्रृंखला को खोलता है, जिससे आप उन मापदंडों को निर्दिष्ट कर सकते हैं जिनसे आप इस पृष्ठ को टैग करना चाहते हैं।',
					disabled: Morebits.userIsInGroup('sysop') && !Twinkle.getPref('deleteSysopDefaultToTag')
				}
			]
		} );
	} else if (Twinkle.speedy.dbmultipleparams.length > 0) {
		form.append( { type: 'radio', name: 'csd',
			list: [
				{
					label: 'कोई और मापदंड लागू नहीं होते - टैगिंग समाप्त करें',
					value: 'multiple-finish'
				}
			]
		} );
	}

	var namespace = mw.config.get('wgNamespaceNumber');
	if (namespace % 2 === 1 && namespace !== 3) {  // talk pages, but not user talk pages
		form.append( { type: 'header', label: 'वार्ता पृष्ठ' } );
		form.append( { type: 'radio', name: 'csd', list: Twinkle.speedy.talkList } );
	}

	switch (namespace) {
		case 0:  // article
		case 1:  // talk
			form.append( { type: 'header', label: 'लेख' } );
			form.append( { type: 'radio', name: 'csd', list: Twinkle.speedy.getArticleList(!firstTime) } );
			break;

		case 2:  // user
		case 3:  // user talk
			form.append( { type: 'header', label: 'सदस्य पृष्ठ' } );
			form.append( { type: 'radio', name: 'csd', list: Twinkle.speedy.getUserList(!firstTime) } );
			break;

		case 6:  // file
		case 7:  // file talk
			form.append( { type: 'header', label: 'फ़ाइलें' } );
			form.append( { type: 'radio', name: 'csd', list: Twinkle.speedy.getFileList(!firstTime) } );
			break;

		case 10:  // template
		case 11:  // template talk
			form.append( { type: 'header', label: 'साँचे' } );
			form.append( { type: 'radio', name: 'csd', list: Twinkle.speedy.templateList } );
			break;

		default:
			break;
	}

	form.append( { type: 'header', label: 'वैश्विक मापदंड' } );
	form.append( { type: 'radio', name: 'csd', list: Twinkle.speedy.getGeneralList(!firstTime) });

	var result = form.render();
	if (dialog)
	{
		// render new dialog
		dialog.setContent( result );
		dialog.display();
	}
	else
	{
		// place the form content into the existing dialog box
		content.textContent = ''; // clear children
		content.appendChild(result);
	}
};

Twinkle.speedy.talkList = [
	{
		label: 'हटाए गए पृष्ठों के वार्ता पृष्ठ',
		value: 'talk',
		tooltip: 'इसमें ऐसे कोई भी वार्ता पृष्ठ नहीं आते जिनसे विकिपीडिया को कोई फ़ायदा हो - खासकर सदस्य वार्ता पृष्ठ और वार्ता पुरालेख।'
	}
];

// this is a function to allow for db-multiple filtering
Twinkle.speedy.getFileList = function twinklespeedyGetFileList(multiple) {
	var result = [];
	result.push({
		label: 'फ़1. 14 दिन से अधिक समय तक कोई लाइसेंस न होना',
		value: 'लाइसेंस',
		tooltip: 'इसमें वे सभी फाइलें आती हैं जिनमें अपलोड होने से दो सप्ताह के बाद तक भी कोई लाइसेंस नहीं दिया गया है। ऐसा होने पर यदि फ़ाइल पुरानी होने के कारण सार्वजनिक क्षेत्र(पब्लिक डोमेन) में नहीं होगी, तो उसे शीघ्र हटा दिया जाएगा।'
	});
	if (!multiple) {
	result.push({
		label: 'फ़2. चित्र का विकिमीडिया कॉमन्स पर स्रोत और लाइसेंस जानकारी सहित उपलब्ध होना',
		value: 'कॉमन्स',
		tooltip: 'ऐसी फ़ाइलों को हटाने से पहले जाँच लें  कि कॉमन्स पर स्रोत और लाइसेंस जानकारी सही हो, और यदि कॉमन्स पर फ़ाइल का नाम विकिपीडिया पर फ़ाइल के नाम से भिन्न है तो विकिपीडिया की फ़ाइल की जगह सभी जगह कॉमन्स की फ़ाइल का प्रयोग करें।'
	});
	}
		result.push({
			label: 'फ़3. अप्रयुक्त ग़ैर मुक्त उचित उपयोग फ़ाइल',
			value: 'अप्रयुक्त ग़ैर मुक्त',
			tooltip: 'इस मापदंड के अंतर्गत वे फ़ाइलें आती हैं जो कॉपीराइट सुरक्षित हैं और उचित उपयोग हेतु विकिपीडिया पर डाली गई हैं, परंतु जिनका कोई उपयोग न किया जा रहा है और न ही होने की संभावना है।'
		});
	result.push({
		label: 'फ़4. ग़ैर मुक्त उचित उपयोग उपयोग फ़ाइल जिसपर कोई उचित उपयोग औचित्य न दिया हो',
		value: 'औचित्य',
		tooltip: 'ऐसी कॉपीराइट सुरक्षित फ़ाइलें जिनपर 7 दिन तक कोई उचित उपयोग औचित्य न दिया हो, उन्हें इस मापदंड के अंतर्गत हटाया जा सकता है।'
	});
	result.push({
		label: 'फ़5. ग़ैर मुक्त फ़ाइलें जिनका मुक्त विकल्प उपलब्ध हो',
		value: 'मुक्त विकल्प',
		tooltip: 'इस मापदंड के अंतर्गत वे फ़ाइलें आती हैं जो ग़ैर मुक्त हैं और जिनका कोई मुक्त विकल्प उपलब्ध है। यह आवश्यक नहीं कि मुक्त विकल्प हूबहू वही फ़ाइल हो।'
	});
	result.push({
		label: 'फ़6. फ़ालतू फ़ाइलें',
		value: 'फ़ालतू',
		tooltip: 'इस मापदंड के अंतर्गत वे फ़ाइलें आती हैं जिनका कोई प्रयोग नहीं हो रहा है और जिनका कोई ज्ञानकोशीय प्रयोग नहीं किया जा सकता है। इसमें चित्र, ध्वनियाँ एवं वीडियो फ़ाइलें नहीं आती हैं।'
	});
	if (!multiple) {
	result.push({
		label: 'व6फ़. साफ़ कॉपीराइट उल्लंघन - फ़ाइलें',
		value: 'कॉपीराइट फ़ाइल',
		tooltip: 'वे सभी फ़ाइलें जो अंतरजाल पर किसी ऐसी वेबसाइट से लिये गए हैं जो साफ़-साफ़ फ़ाइल को मुक्त लाइसेंस के अंतर्गत नहीं देती है। इसमें वे फ़ाइलें भी आती हैं जिनका कॉपीराइट स्वयं अपलोडर के पास है और सदस्य ने उसका पहला प्रकाशन किसी मुक्त लाइसेंस के अंतर्गत नहीं किया है।'
	});
	}
	return result;
};

Twinkle.speedy.getArticleList = function twinklespeedyGetArticleList(multiple) {
	var result = [];
	result.push({
		label: 'ल1. पूर्णतया अन्य भाषा में लिखे लेख',
		value: 'अन्य भाषा',
		tooltip: 'इसमें वे लेख आते हैं जो पूर्णतया हिन्दी के अलावा किसी और भाषा में लिखे हुए हैं, चाहे उनका नाम हिन्दी में हो या किसी और भाषा में।'
	});
	result.push({
		label: 'ल2. साफ़ प्रचार',
		value: 'प्रचार',
		tooltip: 'इसमें वे सभी पृष्ठ आते हैं जिनमें केवल प्रचार है, चाहे वह किसी व्यक्ति-विशेष का हो, किसी समूह का, किसी प्रोडक्ट का, अथवा किसी कंपनी का। इसमें प्रचार वाले केवल वही लेख आते हैं जिन्हें ज्ञानकोष के अनुरूप बनाने के लिये शुरू से दोबारा लिखना पड़ेगा।'
	});
	result.push({
		label: 'ल4. प्रतिलिपि लेख',
		value: 'प्रतिलिपि',
		tooltip: 'इस मापदंड के अंतर्गत वो लेख आते हैं जो किसी पुराने लेख की प्रतिलिपि हैं। इसमें वे लेख भी आते हैं जो किसी ऐसे विषय पर बनाए गए हैं जिनपर पहले से लेख मौजूद है और पुराना लेख नए लेख से बेहतर है।'
	});
	if (!multiple) {
		result.push({
			label: 'व6ल. साफ़ कॉपीराइट उल्लंघन - लेख',
			value: 'कॉपीराइट लेख',
			tooltip: 'इस मापदंड में वे सभी पृष्ठ आते हैं जो साफ़ तौर पर कॉपीराइट उल्लंघन हैं और जिनके इतिहास में उल्लंघन से मुक्त कोई भी अवतरण नहीं है।'
		});
	}
	return result;
};

Twinkle.speedy.getUserList = function twinklespeedyGetUserList(multiple) {
	var result = [];
	result.push({
		label: 'स1. सदस्य अनुरोध',
		value: 'सदस्य अनुरोध',
		tooltip: 'यदि सदस्य अपने सदस्य पृष्ठ, वार्ता पृष्ठ अथवा किसी उपपृष्ठ को हटाने का स्वयं अनुरोध करता है तो उस पृष्ठ को शीघ्र हटाया जा सकता है।'
	});
	result.push({
		label: 'स2. अस्तित्वहीन सदस्यों के सदस्य पृष्ठ अथवा उपपृष्ठ',
		value: 'अस्तित्वहीन',
		tooltip: 'ऐसे सदस्यों के पृष्ठ, वार्ता पृष्ठ अथवा उपपृष्ठ जो विकिपीडिया पर पंजीकृत नहीं हैं; इस मापदंड के अंतर्गत शाघ्र हटाए जा सकते हैं।'
	});
	if (!multiple) {
		result.push({
			label: 'व6स. साफ़ कॉपीराइट उल्लंघन - सदस्य पृष्ठ',
			value: 'कॉपीराइट सदस्य',
			tooltip: 'सदस्य अपने सदस्य पृष्ठ, वार्ता पृष्ठ अथवा किसी उपपृष्ठ पर कॉपीराइट सामग्री नहीं रख सकते और ऐसे पृष्ठों को शीघ्र हटाया जा सकता है। इसमें ऐसे पृष्ठ भी आते हैं जिनमें मुख्य रूप से "ग़ैर मुक्त उचित उपयोग चित्रों" की दीर्घा(गैलरी) हो, क्योंकि ऐसे चित्रों का सदस्य नामस्थान में प्रयोग विकिपीडिया की नीतियों के विरुद्ध है।'
		});
	}
	return result;
};

Twinkle.speedy.templateList = [
	{
		label: 'सा1. अप्रयुक्त साँचे जिनकी जगह किसी बेहतर साँचे ने ले ली है',
		value: 'पुराना साँचा',
		tooltip: 'इसके अंतर्गत वे सभी साँचे आते हैं जो अब प्रयोग में नहीं हैं और जिनकी जगह उनसे बेहतर किसी साँचे ने ले ली है। यदि नए साँचे के बेहतर होने पर विवाद हो, अथवा साँचा प्रयोग में हो तो हटाने हेतु चर्चा प्रक्रिया का प्रयोग करें।'
	}
];

Twinkle.speedy.getGeneralList = function twinklespeedyGetGeneralList(multiple) {
	var result = [];
	if (!multiple) {
		result.push({
			label: 'विशिष्ट कारण' + (Morebits.userIsInGroup('sysop') ? ' (हटाने का विशेष कारण)' : ' {'+'{शीह}} साँचे का प्रयोग करते हुए'),
			value: 'कारण',
			tooltip: '{'+'{शीह}} "शीघ्र हटाएँ" का लघु रूप है। ऐसे नामांकन में भी शीघ्र हटाने का कोई मापदंड लागू होना चाहिये। यदि कोई मापदंड लागू नहीं होता, तो पृष्ठ हटाने हेतु चर्चा का प्रयोग करें।'
		});
	}
	result.push({
		label: 'व1. अर्थहीन नाम अथवा सम्पूर्णतया अर्थहीन सामग्री वाले पृष्ठ',
		value: 'अर्थहीन',
		tooltip: 'इसमें वे पृष्ठ आते हैं जिनका नाम अर्थहीन है; अथवा जिनमें सामग्री अर्थहीन है, चाहे उसका नाम अर्थहीन न हो।'
	});
	result.push({
		label: 'व2. परीक्षण पृष्ठ',
		value: 'परीक्षण',
		tooltip: 'इसमें वे पृष्ठ आते हैं जिन्हें परीक्षण के लिये बनाया गया है, अर्थात यह जानने के लिये कि सचमुच सदस्य वहाँ बदलाव कर सकता है या नहीं। इस मापदंड के अंतर्गत सदस्यों के उपपृष्ठ नहीं आते।'
	});
	result.push({
		label: 'व3. साफ़ बर्बरता',
		value: 'बर्बरता',
		tooltip: 'इस मापदंड के अंतर्गत ऐसे पृष्ठ आते हैं जिनपर केवल बर्बरता हो। इसमें केवल वही पृष्ठ आते हैं जिनके इतिहास में बर्बरता मुक्त कोई भी अवतरण न हो।'
	});
	result.push({
		label: 'व4. साफ़ धोखा',
		value: 'धोखा',
		tooltip: 'इस मापदंड के अंतर्गत वे पृष्ठ आते हैं जिनपर साफ़ दिखाई दे रहा धोखा हो।'
	});
	result.push({
		label: 'व5. ख़ाली पृष्ठ',
		value: 'खाली',
		tooltip: 'इसमें वे सभी पृष्ठ आते हैं जिनमें कोई सामग्री नहीं है, और न ही किसी पुराने अवतरण में थी।'
	});
	if (!multiple) {
		result.push({
			label: 'व6. साफ़ कॉपीराइट उल्लंघन',
			value: 'कॉपीराइट',
			tooltip: 'इस मापदंड में वे सभी पृष्ठ आते हैं जो साफ़ तौर पर कॉपीराइट उल्लंघन हैं और जिनके इतिहास में उल्लंघन से मुक्त कोई भी अवतरण नहीं है। इसमें वे पृष्ठ भी आते हैं जिनपर डाली गई सामग्री का कॉपीराइट स्वयं उसी सदस्य के पास है और सदस्य ने उसका पहला प्रकाशन किसी मुक्त लाइसेंस के अंतर्गत नहीं किया है। इस मापदंड का प्रयोग तभी किया जाना चाहिये यदि पृष्ठ व6ल, व6फ़, अथवा व6स के अंतर्गत न आता हो।'
		});
	}
	return result;
};

Twinkle.speedy.normalizeHash = {
	'कारण': 'शीह',
	'अनेक': 'अनेक',
	'multiple-finish': 'multiple-finish',
	'अर्थहीन': 'व1',
	'परीक्षण': 'व2',
	'बर्बरता': 'व3',
	'धोखा': 'व4',
	'खाली': 'व5',
	'कॉपीराइट': 'व6',
	'कॉपीराइट लेख': 'व6ल',
	'कॉपीराइट फ़ाइल': 'व6फ़',
	'कॉपीराइट सदस्य': 'व6स',
	'अन्य भाषा': 'ल1',
	'प्रचार': 'ल2',
	'प्रतिलिपि': 'ल4',
	'लाइसेंस': 'फ़1',
	'कॉमन्स': 'फ़2',
	'अप्रयुक्त ग़ैर मुक्त': 'फ़3',
	'औचित्य': 'फ़4',
	'मुक्त विकल्प': 'फ़5',
	'फ़ालतू': 'फ़6',
	'पुराना साँचा': 'सा1',
	'सदस्य अनुरोध': 'स1',
	'अस्तित्वहीन': 'स2',
	'talk': ''
};

// keep this synched with [[MediaWiki:Deletereason-dropdown]]
Twinkle.speedy.reasonHash = {
	'कारण': '',
// General
	'अर्थहीन': 'अर्थहीन नाम अथवा सम्पूर्णतया अर्थहीन सामग्री वाले पृष्ठ',
	'परीक्षण': 'परीक्षण पृष्ठ',
	'बर्बरता': 'साफ़ बर्बरता',
	'धोखा': 'साफ़ धोखा',
	'खाली': 'ख़ाली पृष्ठ',
	'कॉपीराइट': 'साफ़ कॉपीराइट उल्लंघन',
	'कॉपीराइट लेख': 'साफ़ कॉपीराइट उल्लंघन - लेख',
	'कॉपीराइट फ़ाइल': 'साफ़ कॉपीराइट उल्लंघन - फ़ाइलें',
	'कॉपीराइट सदस्य': 'साफ़ कॉपीराइट उल्लंघन - सदस्य पृष्ठ',
// Articles
	'अन्य भाषा': 'पूर्णतया अन्य भाषा में लिखे पृष्ठ',
	'प्रचार': 'साफ़ प्रचार',
	'प्रतिलिपि': 'प्रतिलिपि लेख',
// Images and media
	'लाइसेंस': '14 दिन से अधिक समय तक कोई लाइसेंस न होना',
	'कॉमन्स': 'चित्र का विकिमीडिया कॉमन्स पर स्रोत और लाइसेंस जानकारी सहित उपलब्ध होना',
	'अप्रयुक्त ग़ैर मुक्त': 'अप्रयुक्त ग़ैर मुक्त उचित उपयोग फ़ाइल',
	'औचित्य': 'ग़ैर मुक्त उचित उपयोग उपयोग फ़ाइल जिसपर कोई उचित उपयोग औचित्य न दिया हो',
	'मुक्त विकल्प': 'ग़ैर मुक्त फ़ाइलें जिनका मुक्त विकल्प उपलब्ध हो',
	'फ़ालतू': 'फ़ालतू फ़ाइलें',
// Templates
	'पुराना साँचा': 'अप्रयुक्त साँचे जिनकी जगह किसी बेहतर साँचे ने ले ली है',
// User pages
	'सदस्य अनुरोध': 'सदस्य अनुरोध',
	'अस्तित्वहीन': 'अस्तित्वहीन सदस्यों के सदस्य पृष्ठ अथवा उपपृष्ठ',
//other
	'talk': 'हटाए गए पृष्ठ का वार्ता पृष्ठ'
};

Twinkle.speedy.callbacks = {
	sysop: {
		main: function( params ) {

			var thispage = new Morebits.wiki.page( mw.config.get('wgPageName'), "पृष्ठ हटाया जा रहा है" );
			var presetreason = "[[वि:हटाना#" + params.normalized + "|" + params.normalized + "]]." + params.reason;
			var statelem = thispage.getStatusElement();

			params.input = Twinkle.speedy.getParameters(params.value, params.normalized, statelem);	
			
			if(!Twinkle.speedy.cont) {
			return;
			}
			
			// delete page
			var reason;
			switch(params.normalized) {
				case 'शीह':
					reason = params.input.name + params.dbreason;
					break;
				case 'talk':
					reason = params.reason;
					break;
				default:
					reason = presetreason;
					params.input.val = '';
					$.each(params.input, function(prop, val){
					if (typeof val === 'string' && prop!== 'name' && prop!== 'val' && val!=="") {
						params.input.val += " " + val + " ";
						}
					});
					if (params.input.val!=='') {
					reason+=params.input.val;
					}
					break;
			}

			thispage.setEditSummary( reason + Twinkle.getPref('deletionSummaryAd') );
			thispage.deletePage();

			// delete talk page
			if (params.deleteTalkPage &&
			    document.getElementById( 'ca-talk' ).className !== 'new') {
				var talkpage = new Morebits.wiki.page( Morebits.wikipedia.namespaces[ mw.config.get('wgNamespaceNumber') + 1 ] + ':' + mw.config.get('wgTitle'), "वार्ता पृष्ठ हटाया जा रहा है" );
				talkpage.setEditSummary('हटाए गए पृष्ठ [[' + mw.config.get('wgPageName') + "]] का वार्ता पृष्ठ। " + Twinkle.getPref('deletionSummaryAd'));
				talkpage.deletePage();
			}

			// promote Unlink tool
			var $link, $bigtext;
			if( mw.config.get('wgNamespaceNumber') === 6) {
				$link = $('<a/>', {
					'href': '#',
					'text': 'click here to go to the Unlink tool',
					'css': { 'fontSize': '130%', 'fontWeight': 'bold' },
					'click': function(){
						Morebits.wiki.actionCompleted.redirect = null;
						Twinkle.speedy.dialog.close();
						Twinkle.unlink.callback("Removing usages of and/or links to deleted file " + mw.config.get('wgPageName'));
					}
				});
				$bigtext = $('<span/>', {
					'text': 'To orphan backlinks and remove instances of file usage',
					'css': { 'fontSize': '130%', 'fontWeight': 'bold' }
				});
				Morebits.status.info($bigtext[0], $link[0]);
			} else {
				$link = $('<a/>', {
					'href': '#',
					'text': 'click here to go to the Unlink tool',
					'css': { 'fontSize': '130%', 'fontWeight': 'bold' },
					'click': function(){
						Morebits.wiki.actionCompleted.redirect = null;
						Twinkle.speedy.dialog.close();
						Twinkle.unlink.callback("Removing links to deleted page " + mw.config.get('wgPageName'));
					}
				});
				$bigtext = $('<span/>', {
					'text': 'To orphan backlinks',
					'css': { 'fontSize': '130%', 'fontWeight': 'bold' }
				});
				Morebits.status.info($bigtext[0], $link[0]);
			}

			// open talk page of first contributor
			if( params.openusertalk ) {
				thispage = new Morebits.wiki.page( mw.config.get('wgPageName') );  // a necessary evil, in order to clear incorrect status text
				thispage.setCallbackParameters( params );
				thispage.lookupCreator( Twinkle.speedy.callbacks.sysop.openUserTalkPage );
			}

			// delete redirects
			if (params.deleteRedirects) {
				var query = {
					'action': 'query',
					'list': 'backlinks',
					'blfilterredir': 'redirects',
					'bltitle': mw.config.get('wgPageName'),
					'bllimit': 5000  // 500 is max for normal users, 5000 for bots and sysops
				};
				var wikipedia_api = new Morebits.wiki.api( 'getting list of redirects...', query, Twinkle.speedy.callbacks.sysop.deleteRedirectsMain,
					new Morebits.status( 'Deleting redirects' ) );
				wikipedia_api.params = params;
				wikipedia_api.post();
			}
		},
		openUserTalkPage: function( pageobj ) {
			pageobj.getStatusElement().unlink();  // don't need it anymore
			var user = pageobj.getCreator();
			var statusIndicator = new Morebits.status('Opening user talk page edit form for ' + user, 'opening...');

			var query = {
				'title': 'User talk:' + user,
				'action': 'edit',
				'preview': 'yes',
				'vanarticle': mw.config.get('wgPageName').replace(/_/g, ' ')
			};
			switch( Twinkle.getPref('userTalkPageMode') ) {
			case 'tab':
				window.open( mw.util.wikiScript('index') + '?' + Morebits.queryString.create( query ), '_tab' );
				break;
			case 'blank':
				window.open( mw.util.wikiScript('index') + '?' + Morebits.queryString.create( query ), '_blank', 'location=no,toolbar=no,status=no,directories=no,scrollbars=yes,width=1200,height=800' );
				break;
			case 'window':
				/* falls through */
			default:
				window.open( mw.util.wikiScript('index') + '?' + Morebits.queryString.create( query ),
					( window.name === 'twinklewarnwindow' ? '_blank' : 'twinklewarnwindow' ),
					'location=no,toolbar=no,status=no,directories=no,scrollbars=yes,width=1200,height=800' );
				break;
			}

			statusIndicator.info( 'complete' );
		},
		deleteRedirectsMain: function( apiobj ) {
			var xmlDoc = apiobj.getXML();
			var $snapshot = $(xmlDoc).find('backlinks bl');

			var total = $snapshot.length;

			if( !total ) {
				return;
			}

			var statusIndicator = apiobj.statelem;
			statusIndicator.status("0%");

			var onsuccess = function( apiobj ) {
				var obj = apiobj.params.obj;
				var total = apiobj.params.total;
				var now = parseInt( 100 * ++(apiobj.params.current)/total, 10 ) + '%';
				obj.update( now );
				apiobj.statelem.unlink();
				if( apiobj.params.current >= total ) {
					obj.info( now + ' (completed)' );
					Morebits.wiki.removeCheckpoint();
				}
			};

			Morebits.wiki.addCheckpoint();

			var params = $.extend( {}, apiobj.params );
			params.current = 0;
			params.total = total;
			params.obj = statusIndicator;

			$snapshot.each(function(key, value) {
				var title = $(value).attr('title');
				var page = new Morebits.wiki.page(title, 'Deleting redirect "' + title + '"');
				page.setEditSummary('हटाए गए पृष्ठ [[' + mw.config.get('wgPageName') + "]] को पुनर्निर्देश। " + Twinkle.getPref('deletionSummaryAd'));
				page.deletePage(onsuccess);
			});
		}
	},

	user: {
		main: function(pageobj) {
			var statelem = pageobj.getStatusElement();

			if (!pageobj.exists()) {
				statelem.error( "लगता है पृष्ठ अस्तित्व में नहीं है। इसे शायद पहले ही कोई हटा चुका है।" );
				return;
			}

			var text = pageobj.getPageText();
			var params = pageobj.getCallbackParameters();

			if(params.value!=='अनेक') {
			params.input = Twinkle.speedy.getParameters(params.value, params.normalized, statelem);
			}

			if(!Twinkle.speedy.cont) {
			return;
			}
			
			if(params.value!=='सदस्य अनुरोध') {
				if(Twinkle.speedy.self && Twinkle.getPref('NotifySelfSpeedy')) {
					if(params.value==='अनेक') {
						if(Twinkle.speedy.dbmultipleparams.indexOf('स1')=== -1) {
							if(!confirm('इस पृष्ठ के निर्माता आप ही हैं। क्या आप इसे शीघ्र हटाने हेतु नामांकित करना चाहते हैं?')) {
								statelem.error("नामांकन रद्द कर दिया गया है।");
								return;
							}
						}
					}
					if(!confirm('इस पृष्ठ के निर्माता आप ही हैं। क्या आप इसे शीघ्र हटाने हेतु नामांकित करना चाहते हैं?')) {
						statelem.error("नामांकन रद्द कर दिया गया है।");
						return;
					}
				}
			}
			
			statelem.status( 'Checking for tags on the page...' );
			
			// check for existing deletion tags
			var tag = /(\{\{(शीह|हटाएँ)-[a-zA-Z0-9\u0900-\u097F]*\}\})/.exec( text );
			if( tag ) {
				statelem.error( [ Morebits.htmlNode( 'strong', tag[1] ) , " पहले से पृष्ठ पर है।" ] );
				return;
			}

			var xfd = /(?:\{\{(हहेच (लेख|साँचा|श्रेणी|फ़ाइल|अन्य))[^{}]*?\}\})/i.exec( text );
			if( xfd && !confirm( "पृष्ठ पर हहेच साँचा {{" + xfd[1] + "}} पाया गया है। क्या आप अब भी शीघ्र हटाने का नामांकन जोड़ना चाहते हैं?" ) ) {
				statelem.error("नामांकन रद्द कर दिया गया है।");
				return;
			}

			var code, parameters, i;
			if (params.normalized === 'अनेक') {
				code = "{{शीह-अनेक";
				for (i in Twinkle.speedy.dbmultipleparams) {
					if (typeof Twinkle.speedy.dbmultipleparams[i] === 'string') {
						code += "|" + Twinkle.speedy.dbmultipleparams[i];
					}
				}
			}
			else {
				code = "{{शीह-";
				if (params.value === 'talk') {
					code+= "कारण|हटाए गए पृष्ठ का वार्ता पृष्ठ";
				}
				else {
				code+= params.value;
				}
				for(var i in params.input) {
					if (typeof params.input[i] === 'string' && i!== 'name' && i!== 'val' && params.input[i]!=="") {
						code += "|" + params.input[i];
					}
				}
			}
			if (Twinkle.speedy.self) {
				code += "|स्वयं=हाँ";
			}
			code += "}}";

			var thispage = new Morebits.wiki.page(mw.config.get('wgPageName'));
			// patrol the page, if reached from Special:NewPages
			if( Twinkle.getPref('markSpeedyPagesAsPatrolled') ) {
				thispage.patrol();
			}

			// Notification to first contributor			
			if (params.usertalk) {
				Twinkle.speedy.callbacks.user.notifyuser (params);
			}
			// or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name
			else if (params.lognomination) {
				Twinkle.speedy.callbacks.user.addToLog(params, null);
			}

			// Wrap SD template in noinclude tags if we are in template space.
			// Won't work with userboxes in userspace, or any other transcluded page outside template space
			if (mw.config.get('wgNamespaceNumber') === 10) {  // Template:
				code = "<noinclude>" + code + "</noinclude>";
			}

			// Remove tags that become superfluous with this action
			text = text.replace(/\{\{\s*(New unreviewed article|नया असमीक्षित लेख|Userspace draft)\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\}\s*/ig, "");
			if (mw.config.get('wgNamespaceNumber') === 6) {
				// remove "move to Commons" tag - deletion-tagged files cannot be moved to Commons
				text = text.replace(/\{\{(mtc|(copy |move )?to ?commons|move to wikimedia commons|copy to wikimedia commons)[^}]*}}/gi, "");
			}

			// Generate edit summary for edit
			var editsummary;
			switch (params.normalized)
			{
				case 'शीह':
					editsummary = '[[वि:हटाना#शीघ्र हटाना|शीघ्र हटाने]] का नामांकन। कारण: \"' + params.input.dbreason + '\"।';
					break;
				case 'अनेक':
					editsummary = 'शीघ्र हटाने का नामांकन (';
					for (i in Twinkle.speedy.dbmultipleparams) {
						if (typeof Twinkle.speedy.dbmultipleparams[i] === 'string' && Twinkle.speedy.dbmultipleparams[i].length <= 3 && Twinkle.speedy.dbmultipleparams[i].length >= 2 && !isNaN(Twinkle.speedy.dbmultipleparams[i].charAt(1))) {
							editsummary += '[[वि:हटाना#' + Twinkle.speedy.dbmultipleparams[i] + '|शीह ' + Twinkle.speedy.dbmultipleparams[i] + ']], ';
						}
					}
					editsummary = editsummary.substr(0, editsummary.length - 2); // remove trailing comma
					editsummary += ')।';
					break;
				case 'talk':
					editsummary = 'शीघ्र हटाने का नामांकन (हटाए गए पृष्ठ का वार्ता पृष्ठ)';
					break;
				default:
					editsummary = "शीघ्र हटाने का नामांकन ([[वि:हटाना#" + params.normalized + "|शीह " + params.normalized + "]]).";
					break;
			}

			pageobj.setPageText(code + "\n" + text);
			pageobj.setEditSummary(editsummary + Twinkle.getPref('summaryAd'));
			pageobj.setWatchlist(params.watch);
			pageobj.setCreateOption('nocreate');
			if (!params.usertalk && !params.lognomination && !Twinkle.speedy.cont) {
			return;}
			pageobj.save();
		},
		notifyuser: function (params) {
			// don't notify users when their user talk page is nominated
			if (Twinkle.speedy.initialContrib === mw.config.get('wgTitle') && mw.config.get('wgNamespaceNumber') === 3) {
				Status.warn("सूचना साँचा नहीं जोड़ा जाएगा।"); 
				return;
			}
			
			if (Twinkle.speedy.self && Twinkle.getPref('NotifySelfSpeedy')) {
				alert('आपको सूचित किया जाता है कि आपके बनाए इस पृष्ठ को शीघ्र हटाने हेतु नामांकित किया गया है। आपके वार्ता पृष्ठ पर सूचना साँचा नहीं जोड़ा जाएगा।');
				return;
			}
			
			var usertalkpage = new Morebits.wiki.page('सदस्य वार्ता:' + Twinkle.speedy.initialContrib, "पृष्ठ निर्माता को सूचित किया जा रहा है (" + Twinkle.speedy.initialContrib + ")");
			var notifytext = "\n\n{{subst:शीह सूचना-";

			// specialcase "db" and "talk"
			switch (params.value)
			{
				case 'कारण':
					notifytext += "कारण|" + mw.config.get('wgPageName');
					break;
				case 'talk':
					notifytext += "कारण|" + mw.config.get('wgPageName') + "|हटाए गए पृष्ठ का वार्ता पृष्ठ";
					break;
				default:
					notifytext += params.normalized + "|" + mw.config.get('wgPageName');
					break;
			}
			if (params.normalized!== 'अनेक') {
				for (var i in params.input) {
					if (typeof params.input[i] === 'string' && i!=='name' && params.normalized!==('व6' || 'व6ल' || 'व6फ़' || 'व6स') && params.input[i]!=='') {
						notifytext += '|' + params.input[i];
					}
				}
			}
			else {
				for (var i in Twinkle.speedy.dbmultipleparams) {
					if (typeof Twinkle.speedy.dbmultipleparams[i] === 'string') {
					notifytext+= '|' + Twinkle.speedy.dbmultipleparams[i];
					}
				}
			}
			notifytext +="}}~~~~";

			usertalkpage.setAppendText(notifytext);
			usertalkpage.setEditSummary("सूचना: [[" + mw.config.get('wgPageName') + "]] को शीघ्र हटाने का नामांकन।" + Twinkle.getPref('summaryAd'));
			usertalkpage.setCreateOption('recreate');
			usertalkpage.setFollowRedirect(true);

			if(!Twinkle.speedy.cont) {
				return;
			}

			usertalkpage.append();

			// add this nomination to the user's userspace log, if the user has enabled it
			if (params.lognomination) {
				Twinkle.speedy.callbacks.user.addToLog(params);
			}
		},

		// the params used are:
		//   for all: params.normalized
		//   for CSD: params.value
		addToLog: function(params) {
			var wikipedia_page = new Morebits.wiki.page("सदस्य:" + mw.config.get('wgUserName') + "/" + Twinkle.getPref('speedyLogPageName'), "Adding entry to userspace log");
			wikipedia_page.setCallbackParameters(params);
			wikipedia_page.load(Twinkle.speedy.callbacks.user.saveLog);
		},

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

			// add blurb if log page doesn't exist
			if (!pageobj.exists()) {
				text =
					"ये इस सदस्य द्वारा ट्विंकल के प्रयोग से किये गए सभी [[वि:हटाना#शीघ्र हटाना|शीघ्र हटाने]] के नामांकनों का लॉग है।\n\n" +
					"यदि आप यह लॉग अब नहीं रखना चाहते, तो आप [[वि:Twinkle/Preferences|preferences panel]] का प्रयोग कर के इसमें अद्यतन बंद कर सकते हैं, और " +
					"[[वि:हटाना#स1|स1]] के अंतर्गत इसे शीघ्र हटाने के लिये नामांकित कर सकते हैं।\n";
				if (Morebits.userIsInGroup("sysop") ) {
					text += "\nयह लॉग ट्विंकल के प्रयोग से सीधे हटाए गए पृष्ठों को नहीं दिखाता।\n";
				}
			}

			// create monthly header
			var date = new Date();
			var headerRe = new RegExp("^==+\\s*" + date.getUTCMonthName() + "\\s+" + date.getUTCFullYear() + "\\s*==+", "m");
			if (!headerRe.exec(text)) {
				text += "\n\n=== " + date.getUTCMonthName() + " " + date.getUTCFullYear() + " ===";
			}

			text += "\n# [[:" + mw.config.get('wgPageName') + "]]: ";
			switch (params.normalized)
			{
				case 'शीह':
					text += "{{tl|शीह-कारण}}";
					break;
				case 'अनेक':
					text += "अनेक मापदंड (";
					for (var i in Twinkle.speedy.dbmultipleparams) {
						if (typeof Twinkle.speedy.dbmultipleparams[i] === 'string' && Twinkle.speedy.dbmultipleparams[i].length <= 3 && Twinkle.speedy.dbmultipleparams[i].length >= 2 && !isNaN(Twinkle.speedy.dbmultipleparams[i].charAt(1))) {
							text += '[[वि:हटाना#' + Twinkle.speedy.dbmultipleparams[i] + '|' + Twinkle.speedy.dbmultipleparams[i] + ']], ';
						}
					}
					text = text.substr(0, text.length - 2);  // remove trailing comma
					text += ')';
					break;
				default:
					text += "[[वि:हटाना#" + params.normalized + "|शीह " + params.normalized + "]] ({{tl|शीह-" + params.value + "}})";
					break;
			}

			if (Twinkle.speedy.initialContrib) {
				text += "; {{सदस्य|1=" + Twinkle.speedy.initialContrib + "}} को सूचित किया";
			}
			text += " ~~~~~\n";

			pageobj.setPageText(text);
			pageobj.setEditSummary("[[" + mw.config.get('wgPageName') + "]] के शीघ्र हटाने के नामांकन का लॉग।" + Twinkle.getPref('summaryAd'));
			pageobj.setCreateOption("recreate");
			if (!Twinkle.speedy.cont) {
				return;
			}
			pageobj.save();
		}
	}
};

// prompts user for parameters to be passed into the speedy deletion tag
Twinkle.speedy.getParameters = function twinklespeedyGetParameters(value, normalized, statelem)
{
	var parameters = {};
	Twinkle.speedy.cont = true;
	switch( normalized ) {
		case 'शीह':
			var dbrationale = prompt('कृपया शीघ्र हटाने के लिये कारण दें।   \n\"यह पृष्ठ शीघ्र हटाने योग्य है क्योंकि:\"', "");
			if (!dbrationale || !dbrationale.replace(/^\s*/, "").replace(/\s*$/, ""))
			{
				statelem.error( 'कारण बताना आवश्यक है।  नामांकन रोक दिया गया है।' );
				Twinkle.speedy.cont = false;
			}
			parameters.name = "कारण";
			parameters.dbreason = dbrationale;
			break;
		case 'व6':
		case 'व6ल':
		case 'व6फ़':
		case 'व6स':
			var url = prompt( 'कृपया स्रोत यू॰आर॰एल बताएँ, http समेत', "" );
			
			if (url === "")
			{
				statelem.error( 'आपने स्रोत यू॰आर॰एल नहीं दिया है। नामांकन रोक दिया गया है।' );
				Twinkle.speedy.cont = false;
			}
			else if (url.indexOf("http")!==0)
			{
				statelem.error( 'आपने जो स्रोत यू॰आर॰एल दिया है, वह http से नहीं शुरू होता। नामांकन रोक दिया गया है।' );
				Twinkle.speedy.cont = false;
			}
			parameters.name = "स्रोत यू॰आर॰एल";
			parameters.source = url;
			break;
		case 'ल4':
			var article = prompt( 'कृपया मूल लेख का नाम बताएँ', "");
			var oarticle = new Morebits.wiki.page(article);
			if (article === "")
			{
				statelem.error( 'आपने मूल लेख का नाम नहीं दिया है। नामांकन रोक दिया गया है।' );
				Twinkle.speedy.cont = false;
			}
			oarticle.load(function loadsuccess() {
			if (!oarticle.exists())
				{
					statelem.error( 'आपने जो नाम दिया है, इस नाम का कोई लेख नहीं है। नामांकन रोक दिया गया है।' );
					Twinkle.speedy.cont = false;
				}
			});
			parameters.name = "मूल लेख";
			parameters.art = article;
			break;
		case 'फ़2':
			var cfile = prompt( 'कृपया कॉमन्स पर फ़ाइल का नाम बताएँ', "");
			
			if (cfile === "")
			{
				statelem.error( 'आपने कॉमन्स पर फ़ाइल का नाम नहीं दिया है। नामांकन रोक दिया गया है।' );
				Twinkle.speedy.cont = false;
			}
			parameters.name = "कॉमन्स पर फ़ाइल";
			parameters.cfile = cfile;
			break;
		case 'फ़5':
			var alternative = prompt( 'कृपया मुक्त विकल्प का नाम बताएँ।', "");
			
			if (alternative === "")
			{
				statelem.error( 'आपने मुक्त विकल्प का नाम नहीं दिया है। नामांकन रोक दिया गया है।' );
				Twinkle.speedy.cont = false;
			}
			parameters.name = "मुक्त विकल्प";
			parameters.altfile = alternative;
			break;
		case 'सा1':
			var bettertemplate = prompt( 'कृपया बेहतर साँचे का नाम बताएँ:', "" );
			if (bettertemplate === "")
			{
				statelem.error( 'आपने बेहतर साँचे का नाम नहीं दिया है। नामांकन रोक दिया गया है।' );
				Twinkle.speedy.cont = false;
			}
			parameters.name = "बेहतर साँचा";
			parameters.template = bettertemplate;
			break;
		default:
			break;
	}
	return parameters;
};

Twinkle.speedy.callback.evaluateSysop = function twinklespeedyCallbackEvaluateSysop(e)
{
	mw.config.set('wgPageName', mw.config.get('wgPageName').replace(/_/g, ' ')); // for queen/king/whatever and country!

	var tag_only = e.target.form.tag_only;
	if( tag_only && tag_only.checked ) {
		Twinkle.speedy.callback.evaluateUser(e);
		return;
	}

	var value = e.target.values;
	var normalized = Twinkle.speedy.normalizeHash[ value ];
	var params = {
		value: value,
		normalized: normalized,
		watch: Twinkle.getPref('watchSpeedyPages').indexOf( normalized ) !== -1,
		reason: Twinkle.speedy.reasonHash[ value ],
		openusertalk: Twinkle.getPref('openUserTalkPageOnSpeedyDelete').indexOf( normalized ) !== -1,
		deleteTalkPage: e.target.form.talkpage && e.target.form.talkpage.checked,
		deleteRedirects: e.target.form.redirects.checked
	};
	Morebits.status.init( e.target.form );

	Twinkle.speedy.callbacks.sysop.main( params );
};

Twinkle.speedy.callback.evaluateUser = function twinklespeedyCallbackEvaluateUser(e) {
	mw.config.set('wgPageName', mw.config.get('wgPageName').replace(/_/g, ' '));  // for queen/king/whatever and country!
	var value = e.target.values;

	if (value === 'अनेक')
	{
		e.target.form.style.display = "none"; // give the user a cue that the dialog is being changed
		setTimeout(function() {
			Twinkle.speedy.initDialog(Twinkle.speedy.callback.doMultiple, false, e.target.form.parentNode);
		}, 150);
		return;
	}

	if (value === 'multiple-finish') {
		value = 'अनेक';
	}
	else
	{
		// clear these out, whatever the case, to avoid errors
		Twinkle.speedy.dbmultipleparams = [];
	}

	var normalized = Twinkle.speedy.normalizeHash[ value ];
	var i;

	// analyse each db-multiple criterion to determine whether to watch the page/notify the creator
	var watchPage = false;
	if (value === 'अनेक')
	{
		for (i in Twinkle.speedy.dbmultipleparams)
		{
			if (typeof Twinkle.speedy.dbmultipleparams[i] === 'string' &&
				Twinkle.getPref('watchSpeedyPages').indexOf(Twinkle.speedy.dbmultipleparams[i]) !== -1)
			{
				watchPage = true;
				break;
			}
		}
	}
	else
	{
		watchPage = Twinkle.getPref('watchSpeedyPages').indexOf(normalized) !== -1;
	}

	var notifyuser = false;
	if (value === 'अनेक')
	{
		for (i in Twinkle.speedy.dbmultipleparams)
		{
			if (typeof Twinkle.speedy.dbmultipleparams[i] === 'string' &&
				Twinkle.getPref('notifyUserOnSpeedyDeletionNomination').indexOf(Twinkle.speedy.dbmultipleparams[i]) !== -1)
			{
				notifyuser = true;
				break;
			}
		}
	}
	else
	{
		notifyuser = (Twinkle.getPref('notifyUserOnSpeedyDeletionNomination').indexOf(normalized) !== -1) && e.target.form.notify.checked;
	}

/*
	var welcomeuser = false;
	if (notifyuser)
	{
		if (value === 'अनेक')
		{
			for (i in Twinkle.speedy.dbmultipleparams)
			{
				if (typeof Twinkle.speedy.dbmultipleparams[i] === 'string' &&
					Twinkle.getPref('welcomeUserOnSpeedyDeletionNotification').indexOf(Twinkle.speedy.dbmultipleparams[i]) !== -1)
				{
					welcomeuser = true;
					break;
				}
			}
		}
		else
		{
			welcomeuser = Twinkle.getPref('welcomeUserOnSpeedyDeletionNotification').indexOf(normalized) !== -1;
		}
	}
*/

	var csdlog = false;
	if (Twinkle.getPref('logSpeedyNominations') && value === 'अनेक')
	{
		for (i in Twinkle.speedy.dbmultipleparams)
		{
			if (typeof Twinkle.speedy.dbmultipleparams[i] === 'string' &&
				Twinkle.getPref('noLogOnSpeedyNomination').indexOf(Twinkle.speedy.dbmultipleparams[i]) === -1)
			{
				csdlog = true;
				break;
			}
		}
	}
	else
	{
		csdlog = Twinkle.getPref('logSpeedyNominations') && Twinkle.getPref('noLogOnSpeedyNomination').indexOf(normalized) === -1;
	}

	var params = {
		value: value,
		normalized: normalized,
		watch: watchPage,
		usertalk: notifyuser,
//		welcomeuser: welcomeuser,
		lognomination: csdlog
	};
	
	Morebits.status.init( e.target.form );

	Morebits.wiki.actionCompleted.redirect = mw.config.get('wgPageName');
	Morebits.wiki.actionCompleted.notice = "टैगिंग सम्पूर्ण, पृष्ठ कुछ ही क्षणों में रीलोड होगा";

	var wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), "पृष्ठ टैग हो रहा है");
	wikipedia_page.setCallbackParameters(params);
	wikipedia_page.load(function (params) {
		wikipedia_page.lookupCreator(function() {
			Twinkle.speedy.initialContrib = wikipedia_page.getCreator();
			Twinkle.speedy.self = (Twinkle.speedy.initialContrib === mw.config.get('wgUserName')) ? true : false;
			Twinkle.speedy.callbacks.user.main(params);
		});
	});
};

Twinkle.speedy.dbmultipleparams = [];
Twinkle.speedy.callback.doMultiple = function twinklespeedyCallbackDoMultiple(e)
{
	var value = e.target.values;
	var normalized = Twinkle.speedy.normalizeHash[value];
	if (value !== 'multiple-finish')
	{
		if (Twinkle.speedy.dbmultipleparams.indexOf(normalized) !== -1)
		{
			alert('आप यह मापदंड पहले ही चुन चुके हैं। कृपया कोई अन्य मापदंड चुनें।');
		}
		else
		{
			var parameters = Twinkle.speedy.getParameters(value, normalized, Morebits.status);
			Twinkle.speedy.dbmultipleparams.push(normalized);
			$.each(parameters, function addparams(prop, val) {
				if (typeof val === 'string' && prop!== 'name') {
					Twinkle.speedy.dbmultipleparams.push(val);
				}
			});
		}
		e.target.form.style.display = "none"; // give the user a cue that the dialog is being changed
		setTimeout(function() {
			Twinkle.speedy.initDialog(Twinkle.speedy.callback.doMultiple, false, e.target.form.parentNode);
		}, 150);
	}
	else
	{
		Twinkle.speedy.callback.evaluateUser(e);
	}
};

/*
 ****************************************
 *** twinkleunlink.js: Unlink module
 ****************************************
 * Mode of invocation:     Tab ("Unlink")
 * Active on:              Non-special pages
 * Config directives in:   TwinkleConfig
 */

Twinkle.unlink = function twinkleunlink() {
	if( mw.config.get('wgNamespaceNumber') < 0 ) {
		return;
	}
	twAddPortletLink( Twinkle.unlink.callback, "कड़ीतोड़", "tw-unlink", "अन्य पृष्ठों से इस पृष्ठ की कड़ियाँ हटाएँ" );
};

Twinkle.unlink.getChecked2 = function twinkleunlinkGetChecked2( nodelist ) {
	if( !( nodelist instanceof NodeList ) && !( nodelist instanceof HTMLCollection ) ) {
		return nodelist.checked ? [ nodelist.values ] : [];
	}
	var result = [];
	for(var i  = 0; i < nodelist.length; ++i ) {
		if( nodelist[i].checked ) {
			result.push( nodelist[i].values );
		}
	}
	return result;
};

// the parameter is used when invoking unlink from admin speedy
Twinkle.unlink.callback = function(presetReason) {
	var Window = new Morebits.simpleWindow( 800, 400 );
	Window.setTitle( "कड़ियाँ हटाएँ" );
	Window.setScriptName( "Twinkle" );
	Window.addFooterLink( "Twinkle help", "WP:TW/DOC#unlink" );

	var form = new Morebits.quickForm( Twinkle.unlink.callback.evaluate );
	form.append( {
		type: 'textarea',
		name: 'reason',
		label: 'कारण: ',
		value: (presetReason ? presetReason : '')
	} );

	var query;
	if(mw.config.get('wgNamespaceNumber') === 6) {  // File:
		query = {
			'action': 'query',
			'list': [ 'backlinks', 'imageusage' ],
			'bltitle': mw.config.get('wgPageName'),
			'iutitle': mw.config.get('wgPageName'),
			'bllimit': Morebits.userIsInGroup( 'sysop' ) ? 5000 : 500, // 500 is max for normal users, 5000 for bots and sysops
			'iulimit': Morebits.userIsInGroup( 'sysop' ) ? 5000 : 500, // 500 is max for normal users, 5000 for bots and sysops
			'blnamespace': Twinkle.getPref('unlinkNamespaces'),
			'iunamespace': Twinkle.getPref('unlinkNamespaces')
		};
	} else {
		query = {
			'action': 'query',
			'list': 'backlinks',
			'bltitle': mw.config.get('wgPageName'),
			'blfilterredir': 'nonredirects',
			'bllimit': Morebits.userIsInGroup( 'sysop' ) ? 5000 : 500, // 500 is max for normal users, 5000 for bots and sysops
			'blnamespace': Twinkle.getPref('unlinkNamespaces')
		};
	}
	var wikipedia_api = new Morebits.wiki.api( 'कड़ियाँ खोजी जा रही हैं', query, Twinkle.unlink.callbacks.display.backlinks );
	wikipedia_api.params = { form: form, Window: Window, image: mw.config.get('wgNamespaceNumber') === 6 };
	wikipedia_api.post();

	var root = document.createElement( 'div' );
	root.style.padding = '15px';  // just so it doesn't look broken
	Morebits.status.init( root );
	wikipedia_api.statelem.status( "कड़ियाँ लोड हो रही हैं..." );
	Window.setContent( root );
	Window.display();
};

Twinkle.unlink.callback.evaluate = function twinkleunlinkCallbackEvaluate(event) {
	mw.config.set('wgPageName', mw.config.get('wgPageName').replace(/_/g, ' '));  // for queen/king/whatever and country!

	Twinkle.unlink.backlinksdone = 0;
	Twinkle.unlink.imageusagedone = 0;

	function processunlink(pages, imageusage) {
		var statusIndicator = new Morebits.status((imageusage ? 'फ़ाइल प्रयोग हटाया जा रहा है' : 'कड़ियाँ हटाई जा रही हैं'), '0%');
		var total = pages.length;  // removing doubling of this number - no apparent reason for it

		Morebits.wiki.addCheckpoint();

		if( !pages.length ) {
			statusIndicator.info( '100% (सम्पूर्ण)' );
			Morebits.wiki.removeCheckpoint();
			return;
		}

		// get an edit token
		var params = { reason: reason, imageusage: imageusage, globalstatus: statusIndicator, current: 0, total: total };
		for (var i = 0; i < pages.length; ++i)
		{
			var myparams = $.extend({}, params);
			var articlepage = new Morebits.wiki.page(pages[i], '"' + pages[i] + '"' + ' पृष्ठ से कड़ियाँ हटाई जा रही हैं');
			articlepage.setCallbackParameters(myparams);
			articlepage.load(imageusage ? Twinkle.unlink.callbacks.unlinkImageInstances : Twinkle.unlink.callbacks.unlinkBacklinks);
		}
	}

	var reason = event.target.reason.value;
	var backlinks, imageusage;
	if( event.target.backlinks ) {
		backlinks = Twinkle.unlink.getChecked2(event.target.backlinks);
	}
	if( event.target.imageusage ) {
		imageusage = Twinkle.unlink.getChecked2(event.target.imageusage);
	}

	Morebits.simpleWindow.setButtonsEnabled( false );
	Morebits.status.init( event.target );
	Morebits.wiki.addCheckpoint();
	if (backlinks) {
		processunlink(backlinks, false);
	}
	if (imageusage) {
		processunlink(imageusage, true);
	}
	Morebits.wiki.removeCheckpoint();
};

Twinkle.unlink.backlinksdone = 0;
Twinkle.unlink.imageusagedone = 0;

Twinkle.unlink.callbacks = {
	display: {
		backlinks: function twinkleunlinkCallbackDisplayBacklinks(apiobj) {
			var xmlDoc = apiobj.responseXML;
			var havecontent = false;
			var list, namespaces, i;

			if( apiobj.params.image ) {
				var imageusage = $(xmlDoc).find('query imageusage iu');
				list = [];
				for ( i = 0; i < imageusage.length; ++i ) {
					var usagetitle = imageusage[i].getAttribute('title');
					list.push( { label: usagetitle, value: usagetitle, checked: true } );
				}
				if (!list.length)
				{
					apiobj.params.form.append( { type: 'div', label: 'फ़ाइल का प्रयोग कहीं नहीं मिला।' } );
				}
				else
				{
					apiobj.params.form.append( { type:'header', label: 'फ़ाइल प्रयोग' } );
					namespaces = [];
					$.each(Twinkle.getPref('unlinkNamespaces'), function(k, v) {
						namespaces.push(Morebits.wikipedia.namespacesFriendly[v]);
					});
					apiobj.params.form.append( {
						type: 'div',
						label: "चुने हुए नामस्थान: " + namespaces.join(', '),
						tooltip: "आप ये नामस्थान अपनी ट्विंकल वरीयताओं में बदल सकते हैं, [[वि:Twinkle/Preferences]] पर।"
					});
					if ($(xmlDoc).find('query-continue').length) {
						apiobj.params.form.append( {
							type: 'div',
							label: "पहले " + list.length.toString() + " फ़ाइल प्रयोग नीचे सूचीबद्ध हैं।"
						});
					}
					apiobj.params.form.append( {
						type: 'checkbox',
						name: 'imageusage',
						list: list
					} );
					havecontent = true;
				}
			}

			var backlinks = $(xmlDoc).find('query backlinks bl');
			if( backlinks.length > 0 ) {
				list = [];
				for ( i = 0; i < backlinks.length; ++i ) {
					var title = backlinks[i].getAttribute('title');
					list.push( { label: title, value: title, checked: true } );
				}
				apiobj.params.form.append( { type:'header', label: 'Backlinks' } );
				namespaces = [];
				$.each(Twinkle.getPref('unlinkNamespaces'), function(k, v) {
					namespaces.push(Morebits.wikipedia.namespacesFriendly[v]);
				});
				apiobj.params.form.append( {
					type: 'div',
					label: "चुने हुए नामस्थान: " + namespaces.join(', '),
					tooltip: "आप ये नामस्थान अपनी ट्विंकल वरीयताओं में बदल सकते हैं, [[वि:Twinkle/Preferences]] पर।"
				});
				if ($(xmlDoc).find('query-continue').length) {
					apiobj.params.form.append( {
						type: 'div',
						label: "यहाँ की कड़ियों वाले पहले" + list.length.toString() + "पृष्ठ नीचे सूचीबद्ध हैं।"
					});
				}
				apiobj.params.form.append( {
					type: 'checkbox',
					name: 'backlinks',
					list: list
				});
				havecontent = true;
			}
			else
			{
				apiobj.params.form.append( { type: 'div', label: "कोई कड़ियाँ नहीं मिली।" } );
			}

			if (havecontent) {
				apiobj.params.form.append( { type:'submit' } );
			}

			var result = apiobj.params.form.render();
			apiobj.params.Window.setContent( result );
		}
	},
	unlinkBacklinks: function twinkleunlinkCallbackUnlinkBacklinks(pageobj) {
		var text, oldtext;
		text = oldtext = pageobj.getPageText();
		var params = pageobj.getCallbackParameters();

		var wikiPage = new Morebits.wikitext.page(text);
		wikiPage.removeLink(mw.config.get('wgPageName'));
		text = wikiPage.getText();
		if (text === oldtext) {
			// Nothing to do, return
			Twinkle.unlink.callbacks.success(pageobj);
			Morebits.wiki.actionCompleted();
			return;
		}

		pageobj.setPageText(text);
		pageobj.setEditSummary("\"" + mw.config.get('wgPageName') + "\" पृष्ठ की कड़ियाँ हटाई जा रही हैं। कारण: " + params.reason + "।" + Twinkle.getPref('summaryAd'));
		pageobj.setCreateOption('nocreate');
		pageobj.save(Twinkle.unlink.callbacks.success);
	},
	unlinkImageInstances: function twinkleunlinkCallbackUnlinkImageInstances(pageobj) {
		var text, oldtext;
		text = oldtext = pageobj.getPageText();
		var params = pageobj.getCallbackParameters();

		var wikiPage = new Morebits.wikitext.page(text);
		wikiPage.commentOutImage(mw.config.get('wgTitle'), 'Commented out');
		text = wikiPage.getText();
		if (text === oldtext) {
			// Nothing to do, return
			Twinkle.unlink.callbacks.success(pageobj);
			Morebits.wiki.actionCompleted();
			return;
		}

		pageobj.setPageText(text);
		pageobj.setEditSummary("\"" + mw.config.get('wgPageName') + "\" फ़ाइल का प्रयोग हटाया जा रहा है। कारण: " + params.reason + "।" + Twinkle.getPref('summaryAd'));
		pageobj.setCreateOption('nocreate');
		pageobj.save(Twinkle.unlink.callbacks.success);
	},
	success: function twinkleunlinkCallbackSuccess(pageobj) {
		var params = pageobj.getCallbackParameters();
		var total = params.total;
		var now = parseInt( 100 * (params.imageusage ? ++(Twinkle.unlink.imageusagedone) : ++(Twinkle.unlink.backlinksdone))/total, 10 ) + '%';
		params.globalstatus.update( now );
		if((params.imageusage ? Twinkle.unlink.imageusagedone : Twinkle.unlink.backlinksdone) >= total) {
			params.globalstatus.info( now + ' (सम्पूर्ण)' );
			Morebits.wiki.removeCheckpoint();
		}
	}
};

/*
 ****************************************
 *** twinklewarn.js: Warn module
 ****************************************
 * Mode of invocation:     Tab ("Warn")
 * Active on:              User talk pages
 * Config directives in:   TwinkleConfig
 */

Twinkle.warn = function twinklewarn() {
	if( mw.config.get('wgNamespaceNumber') === 3 ) {
			twAddPortletLink( Twinkle.warn.callback, "Warn", "tw-warn", "Warn/notify user" );
	}

	// modify URL of talk page on rollback success pages
	if( mw.config.get('wgAction') === 'rollback' ) {
		var $vandalTalkLink = $("#mw-rollback-success").find(".mw-usertoollinks a").first();
		$vandalTalkLink.css("font-weight", "bold");
		$vandalTalkLink.wrapInner($("<span/>").attr("title", "If appropriate, you can use Twinkle to warn the user about their edits to this page."));

		var extraParam = "vanarticle=" + mw.util.rawurlencode(mw.config.get("wgPageName").replace(/_/g, " "));
		var href = $vandalTalkLink.attr("href");
		if (href.indexOf("?") === -1) {
			$vandalTalkLink.attr("href", href + "?" + extraParam);
		} else {
			$vandalTalkLink.attr("href", href + "&" + extraParam);
		}
	}
};

Twinkle.warn.callback = function twinklewarnCallback() {
	if ( !twinkleUserAuthorized ) {
		alert("Your account is too new to use Twinkle.");
		return;
	}
	if( mw.config.get('wgTitle').split( '/' )[0] === mw.config.get('wgUserName') &&
			!confirm( 'Warning yourself can be seen as a sign of mental instability! Are you sure you want to proceed?' ) ) {
		return;
	}
	
	var Window = new Morebits.simpleWindow( 600, 440 );
	Window.setTitle( "Warn/notify user" );
	Window.setScriptName( "Twinkle" );
	Window.addFooterLink( "Choosing a warning level", "WP:UWUL#Levels" );
	Window.addFooterLink( "Twinkle help", "WP:TW/DOC#warn" );

	var form = new Morebits.quickForm( Twinkle.warn.callback.evaluate );
	var main_select = form.append( {
			type:'field',
			label:'Choose type of warning/notice to issue',
			tooltip:'First choose a main warning group, then the specific warning to issue.'
		} );

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

	var defaultGroup = parseInt(Twinkle.getPref('defaultWarningGroup'), 10);
	main_group.append( { type:'option', label:'General note (1)', value:'level1', selected: ( defaultGroup === 1 || defaultGroup < 1 || ( Morebits.userIsInGroup( 'sysop' ) ? defaultGroup > 8 : defaultGroup > 7 ) ) } );
	main_group.append( { type:'option', label:'Caution (2)', value:'level2', selected: ( defaultGroup === 2 ) } );
	main_group.append( { type:'option', label:'Warning (3)', value:'level3', selected: ( defaultGroup === 3 ) } );
	main_group.append( { type:'option', label:'Final warning (4)', value:'level4', selected: ( defaultGroup === 4 ) } );
	main_group.append( { type:'option', label:'Only warning (4im)', value:'level4im', selected: ( defaultGroup === 5 ) } );
	main_group.append( { type:'option', label:'Single issue notices', value:'singlenotice', selected: ( defaultGroup === 6 ) } );
	main_group.append( { type:'option', label:'Single issue warnings', value:'singlewarn', selected: ( defaultGroup === 7 ) } );
	if( Morebits.userIsInGroup( 'sysop' ) ) {
		main_group.append( { type:'option', label:'Blocking', value:'block', selected: ( defaultGroup === 8 ) } );
	}

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

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

	var more = form.append( { type: 'field', name: 'reasonGroup', label: 'Warning information' } );
	more.append( { type:'textarea', label:'Optional message:', name:'reason', tooltip:'Perhaps a reason, or that a more detailed notice must be appended' } );

	var previewlink = document.createElement( 'a' );
	$(previewlink).click(function(){
		Twinkle.warn.callbacks.preview(result);  // |result| is defined below
	});
	previewlink.style.cursor = "pointer";
	previewlink.textContent = 'Preview';
	more.append( { type: 'div', id: 'warningpreview', label: [ previewlink ] } );
	more.append( { type: 'div', id: 'twinklewarn-previewbox', style: 'display: none' } );

	more.append( { type:'submit', label:'Submit' } );

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

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

// This is all the messages that might be dispatched by the code
// Each of the individual templates require the following information:
//   label (required): A short description displayed in the dialog
//   summary (required): The edit summary used. If an article name is entered, the summary is postfixed with "on [[article]]", and it is always postfixed with ". $summaryAd"
//   suppressArticleInSummary (optional): Set to true to suppress showing the article name in the edit summary. Useful if the warning relates to attack pages, or some such.
Twinkle.warn.messages = {
	level1: {
		"uw-vandalism1": {
			label:"Vandalism",
			summary:"General note: Nonconstructive editing"
		},
		"uw-test1": {
			label:"Editing tests",
			summary:"General note: Editing tests"
		},
		"uw-delete1": {
			label:"Removal of content, blanking",
			summary:"General note: Removal of content, blanking"
		},
		"uw-redirect1": { 
			label:"Creating malicious redirects", 
			summary:"General note: Creating malicious redirects" 
		},
		"uw-tdel1": { 
			label:"Removal of maintenance templates", 
			summary:"General note: Removal of maintenance templates" 
		},
		"uw-joke1": { 
			label:"Using improper humor", 
			summary:"General note: Using improper humor" 
		},
		"uw-create1": { 
			label:"Creating inappropriate pages", 
			summary:"General note: Creating inappropriate pages" 
		},
		"uw-upload1": { 
			label:"Uploading unencyclopedic images", 
			summary:"General note: Uploading unencyclopedic images" 
		},
		"uw-image1": { 
			label:"Image-related vandalism", 
			summary:"General note: Image-related vandalism" 
		},
		"uw-ics1": { 
			label:"Uploading files missing copyright status", 
			summary:"General note: Uploading files missing copyright status" 
		},
		"uw-idt1": { 
			label:"Removing file deletion tags", 
			summary:"General note: Removing file deletion tags" 
		},
		"uw-spam1": { 
			label:"Adding spam links", 
			summary:"General note: Adding spam links" 
		},
		"uw-advert1": { 
			label:"Using Wikipedia for advertising or promotion", 
			summary:"General note: Using Wikipedia for advertising or promotion" 
		},
		"uw-npov1": { 
			label:"Not adhering to neutral point of view", 
			summary:"General note: Not adhering to neutral point of view" 
		},
		"uw-unsourced1": { 
			label:"Addition of unsourced or improperly cited material", 
			summary:"General note: Addition of unsourced or improperly cited material" 
		},
		"uw-error1": { 
			label:"Introducing deliberate factual errors", 
			summary:"General note: Introducing factual errors" 
		},
		"uw-nor1": { 
			label:"Adding original research, including unpublished syntheses of source material", 
			summary:"General note: Adding original research, including unpublished syntheses of source material" 
		},
		"uw-biog1": { 
			label:"Adding unreferenced controversial information about living persons", 
			summary:"General note: Adding unreferenced controversial information about living persons" 
		},
		"uw-defam1": { 
			label:"Addition of defamatory content", 
			summary:"General note: Addition of defamatory content" 
		},
		"uw-uncen1": { 
			label:"Censorship of material", 
			summary:"General note: Censorship of material" 
		},
		"uw-mos1": { 
			label:"Manual of style", 
			summary:"General note: Formatting, date, language, etc (Manual of style)" 
		},
		"uw-move1": { 
			label:"Page moves against naming conventions or consensus", 
			summary:"General note: Page moves against naming conventions or consensus" 
		},
		"uw-chat1": { 
			label:"Using talk page as forum", 
			summary:"General note: Using talk page as forum" 
		},
		"uw-tpv1": { 
			label:"Refactoring others' talk page comments", 
			summary:"General note: Refactoring others' talk page comments" 
		},
		"uw-afd1": { 
			label:"Removing {{afd}} templates",
			summary:"General note: Removing {{afd}} templates"
		},
		"uw-speedy1": { 
			label:"Removing {{speedy deletion}} templates",
			summary:"General note: Removing {{speedy deletion}} templates"
		},
		"uw-blpprod1": { 
			label:"Removing {{blp prod}} templates",
			summary:"General note: Removing {{blp prod}} templates"
		},
		"uw-npa1": { 
			label:"Personal attack directed at a specific editor", 
			summary:"General note: Personal attack directed at a specific editor" 
		},
		"uw-agf1": { 
			label:"Not assuming good faith", 
			summary:"General note: Not assuming good faith" 
		},
		"uw-own1": { 
			label:"Ownership of articles", 
			summary:"General note: Ownership of articles"
		},
		"uw-tempabuse1": { 
			label:"Improper use of warning or blocking template", 
			summary:"General note: Improper use of warning or blocking template"
		},
		"uw-genre1": { 
			label:"Frequent or mass changes to genres without consensus or references", 
			summary:"General note: Frequent or mass changes to genres without consensus or references"
		}
	},
	level2: {
		"uw-vandalism2": { 
			label:"Vandalism", 
			summary:"Caution: Vandalism" 
		},
		"uw-test2": { 
			label:"Editing tests", 
			summary:"Caution: Editing tests" 
		},
		"uw-delete2": { 
			label:"Removal of content, blanking",
			summary:"Caution: Removal of content, blanking"
		},
		"uw-redirect2": { 
			label:"Creating malicious redirects", 
			summary:"Caution: Creating malicious redirects" 
		},
		"uw-tdel2": { 
			label:"Removal of maintenance templates", 
			summary:"Caution: Removal of maintenance templates" 
		},
		"uw-joke2": { 
			label:"Using improper humor", 
			summary:"Caution: Using improper humor" 
		},
		"uw-create2": { 
			label:"Creating inappropriate pages", 
			summary:"Caution: Creating inappropriate pages" 
		},
		"uw-upload2": { 
			label:"Uploading unencyclopedic images", 
			summary:"Caution: Uploading unencyclopedic images" 
		},
		"uw-image2": { 
			label:"Image-related vandalism", 
			summary:"Caution: Image-related vandalism" 
		},
		"uw-ics2": { 
			label:"Uploading files missing copyright status", 
			summary:"Caution: Uploading files missing copyright status" 
		},
		"uw-idt2": { 
			label:"Removing file deletion tags", 
			summary:"Caution: Removing file deletion tags" 
		},
		"uw-spam2": { 
			label:"Adding spam links", 
			summary:"Caution: Adding spam links" 
		},
		"uw-advert2": { 
			label:"Using Wikipedia for advertising or promotion", 
			summary:"Caution: Using Wikipedia for advertising or promotion" 
		},
		"uw-npov2": { 
			label:"Not adhering to neutral point of view", 
			summary:"Caution: Not adhering to neutral point of view" 
		},
		"uw-unsourced2": { 
			label:"Addition of unsourced or improperly cited material", 
			summary:"Caution: Addition of unsourced or improperly cited material" 
		},
		"uw-error2": { 
			label:"Introducing deliberate factual errors", 
			summary:"Caution: Introducing factual errors" 
		},
		"uw-nor2": { 
			label:"Adding original research, including unpublished syntheses of sourced material", 
			summary:"Caution: Adding original research, including unpublished syntheses of sourced material"
		},
		"uw-biog2": { 
			label:"Adding unreferenced controversial information about living persons", 
			summary:"Caution: Adding unreferenced controversial information about living persons" 
		},
		"uw-defam2": { 
			label:"Addition of defamatory content", 
			summary:"Caution: Addition of defamatory content" 
		},
		"uw-uncen2": { 
			label:"Censorship of material", 
			summary:"Caution: Censorship of material" 
		},
		"uw-mos2": { 
			label:"Manual of style", 
			summary:"Caution: Formatting, date, language, etc (Manual of style)" 
		},
		"uw-move2": { 
			label:"Page moves against naming conventions or consensus", 
			summary:"Caution: Page moves against naming conventions or consensus" 
		},
		"uw-chat2": { 
			label:"Using talk page as forum", 
			summary:"Caution: Using talk page as forum" 
		},
		"uw-tpv2": { 
			label:"Refactoring others' talk page comments", 
			summary:"Caution: Refactoring others' talk page comments" 
		},
		"uw-afd2": { 
			label:"Removing {{afd}} templates",
			summary:"Caution: Removing {{afd}} templates"
		},
		"uw-speedy2": { 
			label:"Removing {{speedy deletion}} templates",
			summary:"Caution: Removing {{speedy deletion}} templates"
		},
		"uw-blpprod2": { 
			label:"Removing {{blp prod}} templates",
			summary:"Caution: Removing {{blp prod}} templates"
		},
		"uw-npa2": { 
			label:"Personal attack directed at a specific editor", 
			summary:"Caution: Personal attack directed at a specific editor" 
		},
		"uw-agf2": { 
			label:"Not assuming good faith", 
			summary:"Caution: Not assuming good faith" 
		},
		"uw-own2": { 
			label:"Ownership of articles", 
			summary:"Caution: Ownership of articles"
		},
		"uw-tempabuse2": { 
			label:"Improper use of warning or blocking template", 
			summary:"Caution: Improper use of warning or blocking template"
		},
		"uw-genre2": { 
			label:"Frequent or mass changes to genres without consensus or references", 
			summary:"Caution: Frequent or mass changes to genres without consensus or references"
		}
	},
	level3: {
		"uw-vandalism3": { 
			label:"Vandalism", 
			summary:"Warning: Vandalism" 
		},
		"uw-test3": { 
			label:"Editing tests", 
			summary:"Warning: Editing tests" 
		},
		"uw-delete3": { 
			label:"Removal of content, blanking", 
			summary:"Warning: Removal of content, blanking"
		},
		"uw-redirect3": { 
			label:"Creating malicious redirects", 
			summary:"Warning: Creating malicious redirects" 
		},
		"uw-tdel3": { 
			label:"Removal of maintenance templates", 
			summary:"Warning: Removal of maintenance templates" 
		},
		"uw-joke3": { 
			label:"Using improper humor", 
			summary:"Warning: Using improper humor" 
		},
		"uw-create3": { 
			label:"Creating inappropriate pages", 
			summary:"Warning: Creating inappropriate pages" 
		},
		"uw-upload3": { 
			label:"Uploading unencyclopedic images", 
			summary:"Warning: Uploading unencyclopedic images" 
		},
		"uw-image3": { 
			label:"Image-related vandalism", 
			summary:"Warning: Image-related vandalism" 
		},
		"uw-ics3": { 
			label:"Uploading files missing copyright status", 
			summary:"Warning: Uploading files missing copyright status" 
		},
		"uw-idt3": { 
			label:"Removing file deletion tags", 
			summary:"Warning: Removing file deletion tags" 
		},
		"uw-spam3": { 
			label:"Adding spam links", 
			summary:"Warning: Adding spam links" 
		},
		"uw-advert3": { 
			label:"Using Wikipedia for advertising or promotion", 
			summary:"Warning: Using Wikipedia for advertising or promotion" 
		},
		"uw-npov3": { 
			label:"Not adhering to neutral point of view", 
			summary:"Warning: Not adhering to neutral point of view" 
		},
		"uw-unsourced3": { 
			label:"Addition of unsourced or improperly cited material", 
			summary:"Warning: Addition of unsourced or improperly cited material" 
		},
		"uw-error3": { 
			label:"Introducing deliberate factual errors", 
			summary:"Warning: Introducing deliberate factual errors" 
		},
		"uw-nor3": { 
			label:"Adding original research, including unpublished syntheses of sourced material", 
			summary:"Warning: Adding original research, including unpublished syntheses of sourced material"
		},
		"uw-biog3": { 
			label:"Adding unreferenced controversial or defamatory information about living persons", 
			summary:"Warning: Adding unreferenced controversial information about living persons" 
		},
		"uw-defam3": { 
			label:"Addition of defamatory content", 
			summary:"Warning: Addition of defamatory content" 
		},
		"uw-uncen3": { 
			label:"Censorship of material", 
			summary:"Warning: Censorship of material" 
		},
		"uw-mos3": { 
			label:"Manual of style", 
			summary:"Warning: Formatting, date, language, etc (Manual of style)" 
		},
		"uw-move3": { 
			label:"Page moves against naming conventions or consensus", 
			summary:"Warning: Page moves against naming conventions or consensus" 
		},
		"uw-chat3": { 
			label:"Using talk page as forum", 
			summary:"Warning: Using talk page as forum" 
		},
		"uw-tpv3": { 
			label:"Refactoring others' talk page comments", 
			summary:"Warning: Refactoring others' talk page comments" 
		},
		"uw-afd3": { 
			label:"Removing {{afd}} templates",
			summary:"Warning: Removing {{afd}} templates"
		},
		"uw-speedy3": { 
			label:"Removing {{speedy deletion}} templates",
			summary:"Warning: Removing {{speedy deletion}} templates"
		},
		"uw-blpprod3": { 
			label:"Removing {{blpprod}} templates",
			summary:"Warning: Removing {{blpprod}} templates"
		},
		"uw-npa3": { 
			label:"Personal attack directed at a specific editor", 
			summary:"Warning: Personal attack directed at a specific editor" 
		},
		"uw-agf3": { 
			label:"Not assuming good faith", 
			summary:"Warning: Not assuming good faith" 
		},
		"uw-own3": { 
			label:"Ownership of articles", 
			summary:"Warning: Ownership of articles"
		},
		"uw-genre3": { 
			label:"Frequent or mass changes to genres without consensus or reference", 
			summary:"Warning: Frequent or mass changes to genres without consensus or reference"
		}

	},
	level4: {
		"uw-generic4": { 
			label:"Generic warning (for template series missing level 4)", 
			summary:"Final warning notice" 
		},
		"uw-vandalism4": { 
			label:"Vandalism", 
			summary:"Final warning: Vandalism" 
		},
		"uw-test4": { 
			label:"Editing tests", 
			summary:"Final warning: Editing tests" 
		},
		"uw-delete4": { 
			label:"Removal of content, blanking", 
			summary:"Final warning: Removal of content, blanking" 
		},
		"uw-redirect4": { 
			label:"Creating malicious redirects", 
			summary:"Final warning: Creating malicious redirects" 
		},
		"uw-tdel4": { 
			label:"Removal of maintenance templates", 
			summary:"Final warning: Removal of maintenance templates" 
		},
		"uw-joke4": { 
			label:"Using improper humor", 
			summary:"Final warning: Using improper humor" 
		},
		"uw-create4": { 
			label:"Creating inappropriate pages", 
			summary:"Final warning: Creating inappropriate pages" 
		},
		"uw-upload4": { 
			label:"Uploading unencyclopedic images", 
			summary:"Final warning: Uploading unencyclopedic images" 
		},
		"uw-image4": { 
			label:"Image-related vandalism", 
			summary:"Final warning: Image-related vandalism" 
		},
		"uw-ics4": { 
			label:"Uploading files missing copyright status", 
			summary:"Final warning: Uploading files missing copyright status" 
		},
		"uw-idt4": { 
			label:"Removing file deletion tags", 
			summary:"Final warning: Removing file deletion tags" 
		},
		"uw-spam4": { 
			label:"Adding spam links", 
			summary:"Final warning: Adding spam links" 
		},
		"uw-advert4": { 
			label:"Using Wikipedia for advertising or promotion", 
			summary:"Final warning: Using Wikipedia for advertising or promotion" 
		},
		"uw-npov4": { 
			label:"Not adhering to neutral point of view", 
			summary:"Final warning: Not adhering to neutral point of view" 
		},
		"uw-error4": { 
			label:"Introducing deliberate factual errors", 
			summary:"Final Warning: Introducing deliberate factual errors"
		},
		"uw-nor4": { 
			label:"Adding original research, including unpublished syntheses of sourced material", 
			summary:"Final Warning: Adding original research, including unpublished syntheses of sourced material"
		},
		"uw-biog4": { 
			label:"Adding unreferenced defamatory information about living persons", 
			summary:"Final warning: Adding unreferenced controversial information about living persons" 
		},
		"uw-defam4": { 
			label:"Addition of defamatory content", 
			summary:"Final warning: Addition of defamatory content" 
		},
		"uw-uncen4": { 
			label:"Censorship of material", 
			summary:"Final warning: Censorship of material" 
		},
		"uw-mos4": { 
			label:"Manual of style", 
			summary:"Final warning: Formatting, date, language, etc (Manual of style)" 
		},
		"uw-move4": { 
			label:"Page moves against naming conventions or consensus", 
			summary:"Final warning: Page moves against naming conventions or consensus" 
		},
		"uw-chat4": { 
			label:"Using talk page as forum", 
			summary:"Final warning: Using talk page as forum" 
		},
		"uw-tpv4": { 
			label:"Refactoring others' talk page comments", 
			summary:"Final warning: Refactoring others' talk page comments" 
		},
		"uw-afd4": { 
			label:"Removing {{afd}} templates",
			summary:"Final warning: Removing {{afd}} templates"
		},
		"uw-speedy4": { 
			label:"Removing {{speedy deletion}} templates",
			summary:"Final warning: Removing {{speedy deletion}} templates"
		},
		"uw-blpprod4": { 
			label:"Removing {{blpprod}} templates",
			summary:"Final warning: Removing {{blpprod}} templates"
		},
		"uw-npa4": { 
			label:"Personal attack directed at a specific editor", 
			summary:"Final warning: Personal attack directed at a specific editor"
		}

	},
	level4im: {
		"uw-vandalism4im": { 
			label:"Vandalism", 
			summary:"Only warning: Vandalism" 
		},
		"uw-delete4im": { 
			label:"Removal of content, blanking", 
			summary:"Only warning: Removal of content, blanking" 
		},
		"uw-redirect4im": { 
			label:"Creating malicious redirects", 
			summary:"Only warning: Creating malicious redirects" 
		},
		"uw-joke4im": { 
			label:"Using improper humor", 
			summary:"Only warning: Using improper humor" 
		},
		"uw-create4im": { 
			label:"Creating inappropriate pages", 
			summary:"Only warning: Creating inappropriate pages" 
		},
		"uw-upload4im": { 
			label:"Uploading unencyclopedic images", 
			summary:"Only warning: Uploading unencyclopedic images" 
		},
		"uw-image4im": { 
			label:"Image-related vandalism", 
			summary:"Only warning: Image-related vandalism" 
		},
		"uw-spam4im": { 
			label:"Adding spam links", 
			summary:"Only warning: Adding spam links" 
		},
		"uw-advert4im": { 
			label:"Using Wikipedia for advertising or promotion", 
			summary:"Only warning: Using Wikipedia for advertising or promotion" 
		},
		"uw-biog4im": { 
			label:"Adding unreferenced defamatory information about living persons", 
			summary:"Only warning: Adding unreferenced controversial information about living persons" 
		},
		"uw-defam4im": { 
			label:"Addition of defamatory content", 
			summary:"Only warning: Addition of defamatory content" 
		},
		"uw-move4im": { 
			label:"Page moves against naming conventions or consensus", 
			summary:"Only warning: Page moves against naming conventions or consensus" 
		},
		"uw-npa4im": { 
			label:"Personal attack directed at a specific editor", 
			summary:"Only warning: Personal attack directed at a specific editor"
		}
	},
	singlenotice: {
		"uw-2redirect": { 
			label:"Creating double redirects through bad page moves", 
			summary:"Notice: Creating double redirects through bad page moves" 
		},
		"uw-aiv": { 
			label:"Bad AIV report", 
			summary:"Notice: Bad AIV report" 
		},
		"uw-articlesig": { 
			label:"Adding signatures to article space", 
			summary:"Notice: Adding signatures to article space" 
		},
		"uw-autobiography": { 
			label:"Creating autobiographies", 
			summary:"Notice: Creating autobiographies" 
		},
		"uw-badcat": { 
			label:"Adding incorrect categories", 
			summary:"Notice: Adding incorrect categories" 
		},
		"uw-badlistentry": {
			label:"Adding inappropriate entries to lists",
			summary:"Notice: Adding inappropriate entries to lists"
		},
		"uw-bite": { 
			label:"\"Biting\" newcomers", 
			summary:"Notice: \"Biting\" newcomers" 
		},
		"uw-coi": { 
			label:"Conflict of Interest", 
			summary:"Notice: Conflict of Interest" 
		},
		"uw-copying": {
			label:"Copying text to another page",
			summary:"Notice: Copying text to another page"
		},
		"uw-crystal": {
			label:"Adding speculative or unconfirmed information",
			summary:"Notice: Adding speculative or unconfirmed information"
		},
		"uw-csd": {
			label:"Speedy deletion declined",
			summary:"Notice: Speedy deletion declined"
		},
		"uw-c&pmove": { 
			label:"Cut and paste moves", 
			summary:"Notice: Cut and paste moves" 
		},
		"uw-dab": {
			label:"Incorrect edit to a disambiguation page",
			summary:"Notice: Incorrect edit to a disambiguation page"
		},
		"uw-date": { 
			label:"Unnecessarily changing date formats", 
			summary:"Notice: Unnecessarily changing date formats" 
		},
		"uw-deadlink": { 
			label:"Removing proper sources containing dead links", 
			summary:"Notice: Removing proper sources containing dead links" 
		},
		"uw-directcat": { 
			label:"Applying stub categories manually", 
			summary:"Notice: Applying stub categories manually" 
		},
		"uw-draftfirst": { 
			label:"User should draft in userspace without the risk of speedy deletion", 
			summary:"Notice: Consider drafting your article in [[Help:Userspace draft|userspace]]"
		},
		"uw-editsummary": { 
			label:"Not using edit summary", 
			summary:"Notice: Not using edit summary" 
		},
		"uw-english": { 
			label:"Not communicating in English", 
			summary:"Notice: Not communicating in English" 
		},
		"uw-fuir": { 
			label:"Fair use image has been removed from your userpage", 
			summary:"Notice: A fair use image has been removed from your userpage" 
		},
		"uw-hangon": { 
			label:"User forgets to place a hang-on reasoning to the talk page", 
			summary:"Notice: User forgets to place a hang-on reasoning to the talk page" 
		},
		"uw-hasty": { 
			label:"Hasty addition of speedy deletion tags", 
			summary:"Notice: Allow creators time to improve their articles before tagging them for deletion"
		},
		"uw-imageuse": {
			label:"Incorrect image linking",
			summary:"Notice: Incorrect image linking"
		},
		"uw-incompleteAFD": {
			label:"Incomplete AFD",
			summary:"Notice: Incomplete AFD"
		},
		"uw-italicize": { 
			label:"Italicize books, films, albums, magazines, TV series, etc within articles", 
			summary:"Notice: Italicize books, films, albums, magazines, TV series, etc within articles" 
		},
		"uw-lang": { 
			label:"Unnecessarily changing between British and American English", 
			summary:"Notice: Unnecessarily changing between British and American English" 
		},
		"uw-linking": { 
			label:"Excessive addition of redlinks or repeated blue links", 
			summary:"Notice: Excessive addition of redlinks or repeated blue links" 
		},
		"uw-minor": { 
			label:"Incorrect use of minor edits check box", 
			summary:"Notice: Incorrect use of minor edits check box" 
		},
		"uw-nonfree": { 
			label:"Uploading replaceable non-free images", 
			summary:"Notice: Uploading replaceable non-free images" 
		},
		"uw-notaiv": { 
			label:"Do not report complex abuse to AIV", 
			summary:"Notice: Do not report complex abuse to AIV" 
		},
		"uw-notenglish": {
			label:"Creating non-English articles",
			summary:"Notice: Creating non-English articles"
		},
		"uw-notifysd": { 
			label:"Notify authors of speedy deletion tagged articles", 
			summary:"Notice: Please notify authors of articles tagged for speedy deletion"
		},
		"uw-notvote": {
			label:"We use consensus, not voting", 
			summary:"Notice: We use consensus, not voting" 
		},
		"uw-patrolled": { 
			label:"Mark newpages as patrolled when patrolling", 
			summary:"Notice: Mark newpages as patrolled when patrolling" 
		},
		"uw-plagiarism": { 
			label:"Copying from public domain sources without attribution", 
			summary:"Notice: Copying from public domain sources without attribution" 
		},
		"uw-preview": { 
			label:"Use preview button to avoid mistakes", 
			summary:"Notice: Use preview button to avoid mistakes" 
		},
		"uw-probation": { 
			label:"Article is on probation", 
			summary:"Notice: Article is on probation" 
		},
		"uw-refimprove": {
			label:"Creating unverifiable articles",
			summary:"Notice: Creating unverifiable articles"
		},
		"uw-removevandalism": {
			label:"Incorrect vandalism removal",
			summary:"Notice: Incorrect vandalism removal"
		},
		"uw-repost": { 
			label:"Recreating material previously deleted via XfD process", 
			summary:"Notice: Recreating previously deleted material" 
		},
		"uw-salt": { 
			label:"Recreating salted articles under a different title", 
			summary:"Notice: Recreating salted articles under a different title" 
		},
		"uw-samename": { 
			label:"Rename request impossible", 
			summary:"Notice: Rename request impossible"
		},
		"uw-selfrevert": { 
			label:"Reverting self tests", 
			summary:"Notice: Reverting self tests" 
		},
		"uw-skype": {
			label:"Skype interfering with editing",
			summary:"Notice: Skype interfering with editing"
		},
		"uw-socialnetwork": { 
			label:"Wikipedia is not a social network", 
			summary:"Notice: Wikipedia is not a social network" 
		},
		"uw-sofixit": { 
			label:"Be bold and fix things yourself",
			summary:"Notice: You can be bold and fix things yourself" 
		},
		"uw-spoiler": {
			label:"Adding spoiler alerts or removing spoilers from appropriate sections",
			summary:"Notice: Don't delete or flag potential 'spoilers' in Wikipedia articles"
		},
		"uw-subst": { 
			label:"Remember to subst: templates", 
			summary:"Notice: Remember to subst: templates" 
		},
		"uw-talkinarticle": { 
			label:"Talk in article", 
			summary:"Notice: Talk in article" 
		},
		"uw-tilde": { 
			label:"Not signing posts", 
			summary:"Notice: Not signing posts" 
		},
		"uw-toppost": { 
			label:"Posting at the top of talk pages", 
			summary:"Notice: Posting at the top of talk pages" 
		},
		"uw-uaa": { 
			label:"Reporting of username to WP:UAA not accepted", 
			summary:"Notice: Reporting of username to WP:UAA not accepted" 
		},
		"uw-userspacenoindex": { 
			label:"User page/subpage isn't appropriate for search engine indexing", 
			summary:"Notice: User (sub)page isn't appropriate for search engine indexing" 
		},
		"uw-vgscope": {
			label:"Adding video game walkthroughs, cheats or instructions",
			summary:"Notice: Adding video game walkthroughs, cheats or instructions"
		},
		"uw-warn": { 
			label:"Place user warning templates when reverting vandalism", 
			summary:"Notice: You can use user warning templates when reverting vandalism"
		}
	},
	singlewarn: {
		"uw-3rr": { 
			label:"Edit warring, and/or violating the three revert rule",
			summary:"Warning: Edit warring, and/or violating the three revert rule"		},
		"uw-affiliate": { 
			label:"Affiliate marketing", 
			summary:"Warning: Affiliate marketing"
		},
		"uw-agf-sock": { 
			label:"Use of multiple accounts (assuming good faith)", 
			summary:"Warning: Using multiple accounts"
		},
		"uw-attack": {
			label:"Creating attack pages",
			summary:"Warning: Creating attack pages",
			suppressArticleInSummary: true
		},
		"uw-attempt": {
			label:"Triggering the edit filter",
			summary:"Warning: Triggering the edit filter"
		},
		"uw-bizlist": {
			label:"Business promotion",
			summary:"Warning: Promoting a business"
		},
		"uw-botun": {
			label:"Bot username",
			summary:"Warning: Bot username"
		},
		"uw-canvass": {
			label:"Canvassing",
			summary:"Warning: Canvassing"
		},
		"uw-copyright": {
			label:"Copyright violation",
			summary:"Warning: Copyright violation"
		},
		"uw-copyright-link": { 
			label:"Linking to copyrighted works violation",
			summary:"Warning: Linking to copyrighted works violation" 
		},
		"uw-copyright-remove": {
			label:"Removing {{copyvio}} template from articles",
			summary:"Warning: Removing {{copyvio}} templates"
		},
		"uw-efsummary": {
			label:"Edit summary triggering the edit filter",
			summary:"Warning: Edit summary triggering the edit filter"
		},
		"uw-hoax": { 
			label:"Creating hoaxes", 
			summary:"Warning: Creating hoaxes" 
		},
		"uw-legal": { 
			label:"Making legal threats", 
			summary:"Warning: Making legal threats" 
		},
		"uw-longterm": { 
			label:"Long term pattern of vandalism", 
			summary:"Warning: Long term pattern of vandalism" 
		},
		"uw-multipleIPs": { 
			label:"Usage of multiple IPs", 
			summary:"Warning: Usage of multiple IPs" 
		},
		"uw-pinfo": { 
			label:"Personal info", 
			summary:"Warning: Personal info" 
		},
		"uw-socksuspect": {
			label:"Sockpuppetry",
			summary:"Warning: You are a suspected [[WP:SOCK|sockpuppet]]"  // of User:...
		},
		"uw-upv": { 
			label:"Userpage vandalism", 
			summary:"Warning: Userpage vandalism"
		},
		"uw-username": { 
			label:"Username is against policy", 
			summary:"Warning: Your username might be against policy",
			suppressArticleInSummary: true  // not relevant for this template
		},
		"uw-coi-username": { 
			label:"Username is against policy, and conflict of interest", 
			summary:"Warning: Username and conflict of interest policy"
		},
		"uw-userpage": { 
			label:"Userpage or subpage is against policy", 
			summary:"Warning: Userpage or subpage is against policy"
		},
		"uw-wrongsummary": { 
			label:"Using inaccurate or inappropriate edit summaries", 
			summary:"Warning: Using inaccurate or inappropriate edit summaries"
		}
	},
	block: {
		"uw-block": {
			label: "Block",
			summary: "You have been blocked from editing",
			pageParam: true,
			reasonParam: true  // allows editing of reason for generic templates
		},
		"uw-blocknotalk": {
			label: "Block - talk page disabled",
			summary: "You have been blocked from editing and your user talk page has been disabled",
			pageParam: true,
			reasonParam: true
		},
		"uw-blockindef": {
			label: "Block - indefinite",
			summary: "You have been indefinitely blocked from editing",
			indefinite: true,
			pageParam: true,
			reasonParam: true
		},
		"uw-ablock": {
			label: "Block - IP address",
			summary: "Your IP address has been blocked from editing",
			pageParam: true
		},
		"uw-vblock": {
			label: "Vandalism block",
			summary: "You have been blocked from editing for persistent [[WP:VAND|vandalism]]",
			pageParam: true
		},
		"uw-voablock": {
			label: "Vandalism-only account block (indefinite)",
			summary: "You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]",
			indefinite: true,
			pageParam: true
		},
		"uw-bioblock": {
			label: "BLP violations block",
			summary: "You have been blocked from editing for violations of Wikipedia's [[WP:BLP|biographies of living persons policy]]",
			pageParam: true
		},
		"uw-sblock": {
			label: "Spam block",
			summary: "You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes"
		},
		"uw-adblock": {
			label: "Advertising block",
			summary: "You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]",
			pageParam: true
		},
		"uw-soablock": {
			label: "Spam/advertising-only account block (indefinite)",
			summary: "You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]",
			indefinite: true,
			pageParam: true
		},
		"uw-npblock": {
			label: "Creating nonsense pages block",
			summary: "You have been blocked from editing for creating [[WP:PN|nonsense pages]]",
			pageParam: true
		},
		"uw-copyrightblock": {
			label: "Copyright violation block",
			summary: "You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]",
			pageParam: true
		},
		"uw-spoablock": {
			label: "Sockpuppet account block (indefinite)",
			summary: "You have been indefinitely blocked from editing because your account is being used only for [[WP:SOCK|sock puppetry]]",
			indefinite: true
		},
		"uw-hblock": {
			label: "Harassment block",
			summary: "You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users",
			pageParam: true
		},
		"uw-ewblock": {
			label: "Edit warring block",
			summary: "You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]",
			pageParam: true
		},
		"uw-3block": {
			label: "Three-revert rule violation block",
			summary: "You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]",
			pageParam: true
		},
		"uw-deoablock": {
			label: "Disruption/trolling-only account block (indefinite)",
			summary: "You have been indefinitely blocked from editing because your account is being used only for [[WP:DE|trolling, disruption or harassment]]",
			indefinite: true,
			pageParam: true
		},
		"uw-lblock": {
			label: "Legal threat block (indefinite)",
			summary: "You have been indefinitely blocked from editing for making [[WP:NLT|legal threats or taking legal action]]",
			indefinite: true
		},
		"uw-aeblock": {
			label: "Arbitration enforcement block",
			summary: "You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits",
			pageParam: true,
			reasonParam: true
		},
		"uw-efblock": {
			label: "Edit filter-related block",
			summary: "You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]"
		},
		"uw-myblock": {
			label: "Social networking block",
			summary: "You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]",
			pageParam: true
		},
		"uw-dblock": {
			label: "Deletion/removal of content block",
			summary: "You have been blocked from editing for continued [[WP:VAND|removal of material]]",
			pageParam: true
		},
		"uw-compblock": {
			label: "Possible compromised account block (indefinite)",
			summary: "You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]",
			indefinite: true
		},
		"uw-botblock": {
			label: "Unapproved bot block",
			summary: "You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]",
			pageParam: true
		},
		"uw-ublock": {
			label: "Username soft block (indefinite)",
			summary: "You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]",
			indefinite: true,
			reasonParam: true
		},
		"uw-uhblock": {
			label: "Username hard block (indefinite)",
			summary: "You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]",
			indefinite: true,
			reasonParam: true
		},
		"uw-softerblock": {
			label: "Promotional username soft block (indefinite)",
			summary: "You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website",
			indefinite: true
		},
		"uw-causeblock": {
			label: "Promotional username soft block, for charitable causes (indefinite)",
			summary: "You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website",
			indefinite: true
		},
		"uw-botublock": {
			label: "Bot username soft block (indefinite)",
			summary: "You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved",
			indefinite: true
		},
		"uw-memorialblock": {
			label: "Memorial username soft block (indefinite)",
			summary: "You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone",
			indefinite: true
		},
		"uw-ublock-famous": {
			label: "Famous username soft block (indefinite)",
			summary: "You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual",
			indefinite: true
		},
		"uw-ublock-double": {
			label: "Similar username soft block (indefinite)",
			summary: "You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user",
			indefinite: true
		},
		"uw-uhblock-double": {
			label: "Username impersonation hard block (indefinite)",
			summary: "You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user",
			indefinite: true
		},
		"uw-vaublock": {
			label: "Vandalism-only account and username hard block (indefinite)",
			summary: "You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]",
			indefinite: true,
			pageParam: true
		},
		"uw-spamublock": {
			label: "Spam-only account and promotional username hard block (indefinite)",
			summary: "You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]",
			indefinite: true
		}
	}
};

Twinkle.warn.prev_block_timer = null;
Twinkle.warn.prev_block_reason = null;
Twinkle.warn.prev_article = null;
Twinkle.warn.prev_reason = null;

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

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

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

	if( value === 'block' ) {
		// create the block-related fields
		var more = new Morebits.quickForm.element( { type: 'div', id: 'block_fields' } );
		more.append( {
			type: 'input',
			name: 'block_timer',
			label: 'Period of blocking: ',
			tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...'
		} );
		more.append( {
			type: 'input',
			name: 'block_reason',
			label: '"You have been blocked for ..." ',
			tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.'
		} );
		e.target.root.insertBefore( more.render(), e.target.root.lastChild );

		// restore saved values of fields
		if(Twinkle.warn.prev_block_timer !== null) {
			e.target.root.block_timer.value = Twinkle.warn.prev_block_timer;
			Twinkle.warn.prev_block_timer = null;
		}
		if(Twinkle.warn.prev_block_reason !== null) {
			e.target.root.block_reason.value = Twinkle.warn.prev_block_reason;
			Twinkle.warn.prev_block_reason = null;
		}
		if(Twinkle.warn.prev_article === null) {
			Twinkle.warn.prev_article = e.target.root.article.value;
		}
		e.target.root.article.disabled = false;

		$(e.target.root.reason).parent().hide();
		e.target.root.previewer.closePreview();
	} else if( e.target.root.block_timer ) {
		// hide the block-related fields
		if(!e.target.root.block_timer.disabled && Twinkle.warn.prev_block_timer === null) {
			Twinkle.warn.prev_block_timer = e.target.root.block_timer.value;
		}
		if(!e.target.root.block_reason.disabled && Twinkle.warn.prev_block_reason === null) {
			Twinkle.warn.prev_block_reason = e.target.root.block_reason.value;
		}
		$(e.target.root).find("#block_fields").remove();

		if(e.target.root.article.disabled && Twinkle.warn.prev_article !== null) {
			e.target.root.article.value = Twinkle.warn.prev_article;
			Twinkle.warn.prev_article = null;
		}
		e.target.root.article.disabled = false;

		$(e.target.root.reason).parent().show();
		e.target.root.previewer.closePreview();
	}

	// clear overridden label on article textbox
	Morebits.quickForm.setElementTooltipVisibility(e.target.root.article, true);
	Morebits.quickForm.resetElementLabel(e.target.root.article);
};

Twinkle.warn.callback.change_subcategory = function twinklewarnCallbackChangeSubcategory(e) {
	var main_group = e.target.form.main_group.value;
	var value = e.target.form.sub_group.value;

	if( main_group === 'singlewarn' ) {
		if( value === 'uw-username') {
			if(Twinkle.warn.prev_article === null) {
				Twinkle.warn.prev_article = e.target.form.article.value;
			}
			e.target.form.article.notArticle = true;
			e.target.form.article.value = '';
		} else if( e.target.form.article.notArticle ) {
			if(Twinkle.warn.prev_article !== null) {
				e.target.form.article.value = Twinkle.warn.prev_article;
				Twinkle.warn.prev_article = null;
			}
			e.target.form.article.notArticle = false;
		}
	} else if( main_group === 'block' ) {
		if( Twinkle.warn.messages.block[value].indefinite ) {
			if(Twinkle.warn.prev_block_timer === null) {
				Twinkle.warn.prev_block_timer = e.target.form.block_timer.value;
			}
			e.target.form.block_timer.disabled = true;
			e.target.form.block_timer.value = 'indefinite';
		} else if( e.target.form.block_timer.disabled ) {
			if(Twinkle.warn.prev_block_timer !== null) {
				e.target.form.block_timer.value = Twinkle.warn.prev_block_timer;
				Twinkle.warn.prev_block_timer = null;
			}
			e.target.form.block_timer.disabled = false;
		}

		if( Twinkle.warn.messages.block[value].pageParam ) {
			if(Twinkle.warn.prev_article !== null) {
				e.target.form.article.value = Twinkle.warn.prev_article;
				Twinkle.warn.prev_article = null;
			}
			e.target.form.article.disabled = false;
		} else if( !e.target.form.article.disabled ) {
			if(Twinkle.warn.prev_article === null) {
				Twinkle.warn.prev_article = e.target.form.article.value;
			}
			e.target.form.article.disabled = true;
			e.target.form.article.value = '';
		}

		if( Twinkle.warn.messages.block[value].reasonParam ) {
			if(Twinkle.warn.prev_block_reason !== null) {
				e.target.form.block_reason.value = Twinkle.warn.prev_block_reason;
				Twinkle.warn.prev_block_reason = null;
			}
			e.target.form.block_reason.disabled = false;
		} else if( !e.target.form.block_reason.disabled ) {
			if(Twinkle.warn.prev_block_reason === null) {
				Twinkle.warn.prev_block_reason = e.target.form.block_reason.value;
			}
			e.target.form.block_reason.disabled = true;
			e.target.form.block_reason.value = '';
		}
	}

	// change form labels according to the warning selected
	if (value === "uw-socksuspect") {
		Morebits.quickForm.setElementTooltipVisibility(e.target.form.article, false);
		Morebits.quickForm.overrideElementLabel(e.target.form.article, "Username of sock master, if known (without User:) ");
	} else if (value === "uw-username") {
		Morebits.quickForm.setElementTooltipVisibility(e.target.form.article, false);
		Morebits.quickForm.overrideElementLabel(e.target.form.article, "Username violates policy because... ");
	} else {
		Morebits.quickForm.setElementTooltipVisibility(e.target.form.article, true);
		Morebits.quickForm.resetElementLabel(e.target.form.article);
	}
};

Twinkle.warn.callbacks = {
	preview: function(form) {
		var templatename = form.sub_group.value;
		
		var templatetext = '{{subst:' + templatename;
		var linkedarticle = form.article.value;
		if (templatename in Twinkle.warn.messages.block) {
			if( linkedarticle && Twinkle.warn.messages.block[templatename].pageParam ) {
				templatetext += '|page=' + linkedarticle;
			}

			var blocktime = form.block_timer.value;
			if( /te?mp|^\s*$|min/.exec( blocktime ) || Twinkle.warn.messages.block[templatename].indefinite ) {
				; // nothing
			} else if( /indef|\*|max/.exec( blocktime ) ) {
				templatetext += '|indef=yes';
			} else {
				templatetext += '|time=' + blocktime;
			}

			var blockreason = form.block_reason.value;
			if( blockreason ) {
				templatetext += '|reason=' + blockreason;
			}

			templatetext += "|sig=true}}";
		} else {
			if (linkedarticle) {
				// add linked article for user warnings (non-block templates)
				templatetext += '|1=' + linkedarticle;
			}
			templatetext += '}}';

			// add extra message for non-block templates
			var reason = form.reason.value;
			if (reason) {
				templatetext += " ''" + reason + "''";
			}
		}

		form.previewer.beginRender(templatetext);
	},
	main: function( pageobj ) {
		var text = pageobj.getPageText();
		var params = pageobj.getCallbackParameters();
		var messageData = Twinkle.warn.messages[params.main_group][params.sub_group];

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

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

		var date = new Date();

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

			if( temp_time > date ) {
				if( !confirm( "An identical " + params.sub_group + " has been issued in the last 24 hours.  \nWould you still like to add this warning/notice?" ) ) {
					pageobj.statelem.info( 'aborted per user request' );
					return;
				}
			}
		}

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

		if( latest.date > date ) {
			if( !confirm( "A " + latest.type + " has been issued in the last minute.  \nWould you still like to add this warning/notice?" ) ) {
				pageobj.statelem.info( 'aborted per user request' );
				return;
			}
		}
		
		var mainheaderRe = new RegExp("==+\\s*Warnings\\s*==+");
		var headerRe = new RegExp( "^==+\\s*(?:" + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() +  ")\\s+" + date.getUTCFullYear() + "\\s*==+", 'm' );

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

		if( params.main_group === 'block' ) {
			var article = '', reason = '', time = null;
			
			if( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.sub_group !== 'uw-lblock' && ( Twinkle.warn.messages.block[params.sub_group].indefinite || (/indef|\*|max/).exec( params.block_timer ) ) ) {
				Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' );
				text = "== " + date.getUTCMonthName() + " " + date.getUTCFullYear() + " ==\n";
			} else if( !headerRe.exec( text ) ) {
				Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' );
				text += "== " + date.getUTCMonthName() + " " + date.getUTCFullYear() + " ==\n";
			}
			
			if( params.article && Twinkle.warn.messages.block[params.sub_group].pageParam ) {
				article = '|page=' + params.article;
			}
			
			if( params.reason && Twinkle.warn.messages.block[params.sub_group].reasonParam ) {
				reason = '|reason=' + params.reason;
			}
			
			if( /te?mp|^\s*$|min/.exec( params.block_timer ) || Twinkle.warn.messages.block[params.sub_group].indefinite ) {
				time = '';
			} else if( /indef|\*|max/.exec( params.block_timer ) ) {
				time = '|indef=yes';
			} else {
				time = '|time=' + params.block_timer;
			}

			text += "{{subst:" + params.sub_group + article + time + reason + "|sig=true|subst=subst:}}";
		} else {
			if( !headerRe.exec( text ) ) {
				Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' );
				text += "== " + date.getUTCMonthName() + " " + date.getUTCFullYear() + " ==\n";
			}
			text += "{{subst:" + params.sub_group + ( params.article ? '|1=' + params.article : '' ) + "|subst=subst:}}" + (params.reason ? " ''" + params.reason + "'' ": ' ' ) + "~~~~";
		}
		
		if ( Twinkle.getPref('showSharedIPNotice') && Morebits.isIPAddress( mw.config.get('wgTitle') ) ) {
			Morebits.status.info( 'Info', 'Adding a shared IP notice' );
			text +=  "\n{{subst:SharedIPAdvice}}";
		}

		var summary = messageData.summary;
		if ( messageData.suppressArticleInSummary !== true && params.article ) {
			if ( params.sub_group === "uw-socksuspect" ) {  // this template requires a username
				summary += " of [[User:" + params.article + "]]";
			} else {
				summary += " on [[" + params.article + "]]";
			}
		}
		summary += "." + Twinkle.getPref("summaryAd");

		pageobj.setPageText( text );
		pageobj.setEditSummary( summary );
		pageobj.setWatchlist( Twinkle.getPref('watchWarnings') );
		pageobj.save();
	}
};

Twinkle.warn.callback.evaluate = function twinklewarnCallbackEvaluate(e) {

	// First, check to make sure a reason was filled in if uw-username was selected
	
	if(e.target.sub_group.value === 'uw-username' && e.target.article.value.trim() === '') {
		alert("You must supply a reason for the {{uw-username}} template.");
		return;
	}

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

	Morebits.simpleWindow.setButtonsEnabled( false );
	Morebits.status.init( e.target );

	Morebits.wiki.actionCompleted.redirect = mw.config.get('wgPageName');
	Morebits.wiki.actionCompleted.notice = "Warning complete, reloading talk page in a few seconds";

	var wikipedia_page = new Morebits.wiki.page( mw.config.get('wgPageName'), 'User talk page modification' );
	wikipedia_page.setCallbackParameters( params );
	wikipedia_page.setFollowRedirect( true );
	wikipedia_page.load( Twinkle.warn.callbacks.main );
};

/*
 ****************************************
 *** twinklexfd.js: XFD module
 ****************************************
 * Mode of invocation:     Tab ("XFD")
 * Active on:              Existing, non-special pages, except for file pages with no local (non-Commons) file which are not redirects
 * Config directives in:   TwinkleConfig
 */

Twinkle.xfd = function twinklexfd() {
	// Disable on:
	// * special pages
	// * non-existent pages
	// * files on Commons, whether there is a local page or not (unneeded local pages of files on Commons are eligible for CSD F2)
	// * file pages without actual files (these are eligible for CSD G8)
	if ( mw.config.get('wgNamespaceNumber') < 0 || !mw.config.get('wgArticleId') || (mw.config.get('wgNamespaceNumber') === 6 && (document.getElementById('mw-sharedupload') || (!document.getElementById('mw-imagepage-section-filehistory') && !Morebits.wiki.isPageRedirect()))) ) {
		return;
	}
	twAddPortletLink( Twinkle.xfd.callback, "हहेच", "tw-xfd", "हटाने हेतु चर्चा के लिये नामांकित करें" );
};

Twinkle.xfd.currentRationale = null;

// error callback on Morebits.status.object
Twinkle.xfd.printRationale = function twinklexfdPrintRationale() {
	if (Twinkle.xfd.currentRationale) {
		var p = document.createElement("p");
		p.textContent = "आपका दिया हटाने का कारण निम्नलिखित है। यदि आप चाहें तो ट्विंकल की नई ह॰हे॰च विंडो में कॉपी कर के पुनः प्रयास कर सकते हैं:";
		var pre = document.createElement("pre");
		pre.className = "toccolours";
		pre.style.marginTop = "0";
		pre.textContent = Twinkle.xfd.currentRationale;
		p.appendChild(pre);
		Morebits.status.root.appendChild(p);
		// only need to print the rationale once
		Twinkle.xfd.currentRationale = null;
	}
};

Twinkle.xfd.callback = function twinklexfdCallback() {
	if (!twinkleUserAuthorized) {
		alert("आपका अकाउंट ट्विंकल प्रयोग करने के लिये बहुत नया है।");
		return;
	}

	var Window = new Morebits.simpleWindow( 600, 350 );
	Window.setTitle( "हटाने हेतु चर्चा के लिये नामांकन" );
	Window.setScriptName( "Twinkle" );
	Window.addFooterLink( "पृष्ठ हटाने हेतु चर्चा", "वि:पृष्ठ_हटाने_हेतु_चर्चा" );
	Window.addFooterLink( "Twinkle help", "WP:TW/DOC#xfd" );

	var form = new Morebits.quickForm( Twinkle.xfd.callback.evaluate );
	var categories = form.append( {
			type: 'select',
			name: 'category',
			label: 'चर्चा पृष्ठ:',
			tooltip: 'आपके लिये अपने-आप सबसे उपयुक्त चर्चा पृष्ठ चुना जाता है, परंतु आप चाहें तो किसी अन्य विकल्प का प्रयोग भी कर सकते हैं (ऐसा ना किया जाए तो बेहतर है)',
			event: Twinkle.xfd.callback.change_category
		} );
	categories.append( {
			type: 'option',
			label: 'लेख हटाने हेतु चर्चा',
			selected: mw.config.get('wgNamespaceNumber') === 0,  // Main namespace
			value: 'लेख'
		} );
	categories.append( {
			type: 'option',
			label: 'श्रेणियाँ हटाने, विलय अथवा स्थानांतरित करने हेतु चर्चा',
			selected: mw.config.get('wgNamespaceNumber') === 14,  // Category namespace
			value: 'श्रेणियाँ'
		} );
	categories.append( {
			type: 'option',
			label: 'साँचे हटाने हेतु चर्चा',
			selected: mw.config.get('wgNamespaceNumber') === 10,  // Template namespace
			value: 'साँचे'
		} );
	categories.append( {
			type: 'option',
			label: 'फ़ाइलें हटाने हेतु चर्चा',
			selected: mw.config.get('wgNamespaceNumber') === 6,  // File namespace
			value: 'फ़ाइलें'
		} );
	categories.append( {
			type: 'option',
			label: 'अन्य पृष्ठ हटाने हेतु चर्चा',
			selected: mw.config.get('wgNamespaceNumber') !== 0 && 
						mw.config.get('wgNamespaceNumber') !== 6 && 
						mw.config.get('wgNamespaceNumber') !== 10 && 
						mw.config.get('wgNamespaceNumber') !== 14,  // Other namespaces
			value: 'अन्य'
		} );
	form.append( {
			type: 'checkbox',
			list: [
				{
					label: 'यदि संभव हो तो पृष्ठ निर्माता को सूचित करें',
					value: 'notify',
					name: 'notify',
					tooltip: "यदि यह सक्षम है तो पृष्ठ निर्माता के वार्ता पृष्ठ पर एक सूचना साँचा जोड़ दिया जाएगा।",
					checked: true
				}
			]
		}
	);
	form.append( {
			type: 'field',
			label:'Work area',
			name: 'work_area'
		} );
	form.append( { type:'submit' } );

	var result = form.render();
	Window.setContent( result );
	Window.display();

	// We must init the controls
	var evt = document.createEvent( "Event" );
	evt.initEvent( 'change', true, true );
	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(e.target.form, "work_area")[0];
	var work_area = null;

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

	switch( value ) {
	case 'लेख':
		work_area = new Morebits.quickForm.element( {
				type: 'field',
				label: 'लेख हटाने हेतु चर्चा',
				name: 'work_area'
			} );
		work_area.append( {
				type: 'textarea',
				name: 'xfdreason',
				label: 'कारण: ',
				value: oldreason
			} );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	case 'श्रेणियाँ':
		work_area = new Morebits.quickForm.element( {
				type: 'field',
				label: 'श्रेणियाँ हटाने, विलय अथवा स्थानांतरित करने हेतु चर्चा',
				name: 'work_area'
			} );
		var cfd_category = work_area.append( {
				type: 'select',
				label: 'कृपया कार्य चुनें: ',
				name: 'xfdcat',
				event: function(e) {
					var value = e.target.value;
					var target = e.target.form.xfdtarget;
					// update enabled status
					if( value === 'हटाना' ) {
						target.disabled = true;
					} else {
						target.disabled = false;
					}
					// update label
					if( value === 'विलय' ) {
						target.previousSibling.textContent = "दूसरी श्रेणी का नाम: ";
					} else if( value === 'स्थानान्तरण' ) {
						target.previousSibling.textContent = "श्रेणी के लिये नया नाम: ";
					}
				}
			} );
		cfd_category.append( { type: 'option', label: 'हटाना', value: 'हटाना', selected: true } );
		cfd_category.append( { type: 'option', label: 'विलय', value: 'विलय' } );
		cfd_category.append( { type: 'option', label: 'स्थानान्तरण', value: 'स्थानान्तरण' } );

		work_area.append( {
				type: 'input',
				name: 'xfdtarget',
				label: 'श्रेणी का नाम: ',
				disabled: true,
				value: ''
			} );
		work_area.append( {
				type: 'textarea',
				name: 'xfdreason',
				label: 'कारण: ',
				value: oldreason
			} );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	case 'साँचे':
		work_area = new Morebits.quickForm.element( {
				type: 'field',
				label: 'साँचे हटाने हेतु चर्चा',
				name: 'work_area'
			} );
		work_area.append( {
				type: 'textarea',
				name: 'xfdreason',
				label: 'कारण: ',
				value: oldreason
			} );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	case 'फ़ाइलें':
		work_area = new Morebits.quickForm.element( {
				type: 'field',
				label: 'फ़ाइलें हटाने हेतु चर्चा',
				name: 'work_area'
			} );
		work_area.append( {
				type: 'textarea',
				name: 'xfdreason',
				label: 'कारण: ',
				value: oldreason
			} );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	case 'अन्य':
		work_area = new Morebits.quickForm.element( {
				type: 'field',
				label: 'अन्य पृष्ठ हटाने हेतु चर्चा',
				name: 'work_area'
			} );
		work_area.append( {
				type: 'checkbox',
				list: [
						{
							label: 'नामांकन साँचे को <noinclude> में डालें',
							value: 'noinclude',
							name: 'noinclude',
							tooltip: 'इससे नामांकन साँचे को &lt;noinclude&gt; में लपेट दिया जाएगा, जिससे नामांकन साँचा पृष्ठ के साथ ट्रान्सक्लूड नहीं होगा।'
						}
					]
		} );
		work_area.append( {
				type: 'textarea',
				name: 'xfdreason',
				label: 'कारण: ',
				value: oldreason
			} );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		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( work_area, old_area );
		break;
	}

	form.notify.checked = true;
	form.notify.disabled = false;
};

Twinkle.xfd.callbacks = {
	afd: {
		// 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();

			// Check for existing AfD tag, for the benefit of new page patrollers
			var textNoAfd = text.replace(/\{\{\s*हहेच लेख\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\}\s*/g, "");
			if (text !== textNoAfd) {
				if (confirm("इस लेख पर पहले से एक नामांकन साँचा मौजूद है।  \nवर्तमान नामांकन साँचे को हटाकर नया नामांकन साँचा लगाने के लिये OK दबाएँ। नया नामांकन ख़ारिज करने के लिये Cancel दबाएँ।")) {
					text = textNoAfd;
				} else {
					statelem.error("लेख पहले से हटाने के लिये नामांकित है, और आपका नामांकन ख़ारिज कर दिया गया है।");
					window.location.reload();
					return;
				}
			}

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

			// Starting discussion page
			var wikipedia_page = new Morebits.wiki.page('विकिपीडिया:पृष्ठ हटाने हेतु चर्चा/लेख/' + mw.config.get('wgTitle'), "नामांकन चर्चा पृष्ठ पर नामांकन जोड़ा जा रहा है");
			wikipedia_page.setCallbackParameters(params);
			wikipedia_page.load(Twinkle.xfd.callbacks.afd.discussionPage);

			// Notification to first contributor
			if (params.usertalk) {
				var thispage = new Morebits.wiki.page(mw.config.get('wgPageName'));
				thispage.setCallbackParameters(params);
				thispage.lookupCreator(Twinkle.xfd.callbacks.afd.userNotification);
			}

			// Remove some tags that should always be removed on AfD.
			text = text.replace(/\{\{\s*(New unreviewed article|नया असमीक्षित लेख|Userspace draft)\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\}\s*/ig, "");
			// Then, test if there are speedy deletion-related templates on the article.
			var textNoSd = text.replace(/\{\{\s*((db|शीह|हटाएँ)(-[a-zA-Z0-9\u0900-\u097F]*)?|delete)\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\}\s*/ig, "");
			if (text !== textNoSd && confirm("इस लेख पर शीघ्र हटाने का नामांकन पाया गया है। क्या उस नामांकन को हटाया जाए?")) {
				text = textNoSd;
			}

			pageobj.setPageText('{{हहेच लेख|कारण=' + params.reason + "}}\n" + text);
			pageobj.setEditSummary("हटाने हेतु चर्चा के लिये नामांकन; देखें [[वि:पृष्ठ हटाने हेतु चर्चा/लेख/" + mw.config.get('wgTitle') + "|चर्चा पृष्ठ]]।" + Twinkle.getPref('summaryAd'));
			switch (Twinkle.getPref('xfdWatchPage')) {
				case 'yes':
					pageobj.setWatchlist(true);
					break;
				case 'no':
					pageobj.setWatchlistFromPreferences(false);
					break;
				default:
					pageobj.setWatchlistFromPreferences(true);
					break;
			}
			pageobj.setCreateOption('nocreate');
			pageobj.save();
		},
		discussionPage: function(pageobj) {
			var text = pageobj.getPageText();
			var params = pageobj.getCallbackParameters();

			pageobj.setPageText(text + "\n\n{{subst:हहेच लेख नामांकन|कारण=" + params.reason + "|पृष्ठ=" + mw.config.get('wgTitle') + "}}\n");
			pageobj.setEditSummary("[[" + mw.config.get('wgPageName') + "]] लेख को हटाने का नामांकन " + Twinkle.getPref('summaryAd'));
			switch (Twinkle.getPref('xfdWatchDiscussion')) {
				case 'yes':
					pageobj.setWatchlist(true);
					break;
				case 'no':
					pageobj.setWatchlistFromPreferences(false);
					break;
				default:
					pageobj.setWatchlistFromPreferences(true);
					break;
			}
			pageobj.setCreateOption('recreate');
			pageobj.save();
			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 params = pageobj.getCallbackParameters();
			var initialContrib = pageobj.getCreator();
			var usertalkpage = new Morebits.wiki.page('सदस्य वार्ता:' + initialContrib, "पृष्ठ निर्माता को सूचित किया जा रहा है (" + initialContrib + ")");
			var notifytext = "\n{{subst:हहेच लेख सूचना|पृष्ठ=" + mw.config.get('wgTitle') + "|कारण=" + params.reason + "}}~~~~";
			usertalkpage.setAppendText(notifytext);
			usertalkpage.setEditSummary("सूचना: [[" + mw.config.get('wgPageName') + "]] को हटाने हेतु चर्चा के लिये नामांकित किया गया है।" + Twinkle.getPref('summaryAd'));
			usertalkpage.setCreateOption('recreate');
			switch (Twinkle.getPref('xfdWatchUser')) {
				case 'yes':
					usertalkpage.setWatchlist(true);
					break;
				case 'no':
					usertalkpage.setWatchlistFromPreferences(false);
					break;
				default:
					usertalkpage.setWatchlistFromPreferences(true);
					break;
			}
			usertalkpage.setFollowRedirect(true);
			usertalkpage.append();
		}
	},


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

			var added_data = "{{हहेच श्रेणी|प्रकार=" + params.type + '|कारण=' + params.reason;
			var editsummary = "";
			switch( params.type ) {
			case 'हटाना':
				editsummary += "हटाने";
				break;
			case 'विलय':
				added_data += '|दूसरी श्रेणी=' + params.target;
				editsummary += "विलय";
				break;
			case 'स्थानान्तरण':
				added_data += '|नया नाम=' + params.target;
				editsummary += "स्थानान्तरण";
				break;
			default:
				alert("twinklexfd in taggingCategory(): unknown CFD action");
				break;
			}
			
			added_data += '}}';
			editsummary+= 'हेतु श्रेणी का नामांकन, देखें [[वि:पृष्ठ हटाने हेतु चर्चा/श्रेणियाँ/' + mw.config.get('wgTitle') + '|चर्चा पृष्ठ]]।';

			pageobj.setPageText(added_data + "\n" + text);
			pageobj.setEditSummary(editsummary + Twinkle.getPref('summaryAd'));
			switch (Twinkle.getPref('xfdWatchPage')) {
				case 'yes':
					pageobj.setWatchlist(true);
					break;
				case 'no':
					pageobj.setWatchlistFromPreferences(false);
					break;
				default:
					pageobj.setWatchlistFromPreferences(true);
					break;
			}
			pageobj.setCreateOption('recreate');  // since categories can be populated without an actual page at that title
			pageobj.save();
		},
		discussionPage: function(pageobj) {
			var text = pageobj.getPageText();
			var params = pageobj.getCallbackParameters();
			var editsummary = "[[" + mw.config.get('wgPageName') + "]] श्रेणी पृष्ठ को";
			
			var newtext = text + "\n\n{{subst:हहेच श्रेणी नामांकन|कारण=" + params.reason + "|पृष्ठ=" + mw.config.get('wgTitle') + "|प्रकार=" + params.type;

			switch (params.type) {
				case 'विलय':
					newtext += '|दूसरी श्रेणी=' + params.target;
					editsummary += 'विलय करने';
					break;
				case 'स्थानान्तरण':
					newtext += '|नया नाम=' + params.target;
					editsummary += 'स्थानांतरित करने';
					break;
				case 'हटाना': //falls through
				default:
					editsummary += 'हटाने';
					break;
			}
			editsummary += 'का नामांकन';
			newtext += "}}\n";

			pageobj.setEditSummary( editsummary + Twinkle.getPref('summaryAd'));
			pageobj.setPageText(newtext);

			switch (Twinkle.getPref('xfdWatchDiscussion')) {
				case 'yes':
					pageobj.setWatchlist(true);
					break;
				case 'no':
					pageobj.setWatchlistFromPreferences(false);
					break;
				default:
					pageobj.setWatchlistFromPreferences(true);
					break;
			}
			pageobj.setCreateOption('recreate');
			pageobj.save();
			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();
			var usertalkpage = new Morebits.wiki.page('सदस्य वार्ता:' + initialContrib, "पृष्ठ निर्माता को सूचित किया जा रहा है (" + initialContrib + ")");
			var notifytext = "\n{{subst:हहेच श्रेणी सूचना|पृष्ठ=" + mw.config.get('wgTitle') + "|प्रकार=" + params.type + '|कारण=' + params.reason;
			switch (params.type) {
				case 'विलय':
					notifytext += '|दूसरी श्रेणी=' + params.target;
					break;
				case 'स्थानान्तरण':
					notifytext += '|नया नाम=' + params.target;
					break;
				default:
					break;
			}
			
			notifytext += "}}~~~~";
			
			var editsummary = 'सूचना:' + mw.config.get('wgPageName') + "]] को";
			editsummary += (params.type === 'हटाना') ? 'हटाने' : params.type;
			editsummary+= 'हेतु चर्चा के लिये नामांकित किया गया है।';

			usertalkpage.setAppendText(notifytext);
			usertalkpage.setEditSummary(editsummary + Twinkle.getPref('summaryAd'));
			usertalkpage.setCreateOption('recreate');
			switch (Twinkle.getPref('xfdWatchUser')) {
				case 'yes':
					usertalkpage.setWatchlist(true);
					break;
				case 'no':
					usertalkpage.setWatchlistFromPreferences(false);
					break;
				default:
					usertalkpage.setWatchlistFromPreferences(true);
					break;
			}
			usertalkpage.setFollowRedirect(true);
			usertalkpage.append();
		}
	},


	tfd: {
		taggingTemplate: function(pageobj) {
			var text = pageobj.getPageText();
			var params = pageobj.getCallbackParameters();

			pageobj.setPageText((params.noinclude ? "<noinclude>{{हहेच साँचा" : "{{हहेच साँचा") + '|कारण=' + params.reason + (params.noinclude ? "}}</noinclude>" : "}}\n") + text);
			pageobj.setEditSummary("हटाने हेतु चर्चा के लिये नामांकन; देखें [[वि:पृष्ठ हटाने हेतु चर्चा/साँचे/" + mw.config.get('wgTitle') + "|नामांकन पृष्ठ]]।" + Twinkle.getPref('summaryAd'));
			switch (Twinkle.getPref('xfdWatchPage')) {
				case 'yes':
					pageobj.setWatchlist(true);
					break;
				case 'no':
					pageobj.setWatchlistFromPreferences(false);
					break;
				default:
					pageobj.setWatchlistFromPreferences(true);
					break;
			}
			pageobj.setCreateOption('nocreate');
			pageobj.save();
		},
		discussionPage: function(pageobj) {
			var text = pageobj.getPageText();
			var params = pageobj.getCallbackParameters();

			pageobj.setPageText(text + "\n\n{{subst:हहेच साँचा नामांकन|कारण=" + params.reason + "|पृष्ठ=" + mw.config.get('wgTitle') + "}}\n");
			pageobj.setEditSummary("[[" + mw.config.get('wgPageName') + "]] साँचे को हटाने का नामांकन " + Twinkle.getPref('summaryAd'));
			switch (Twinkle.getPref('xfdWatchDiscussion')) {
				case 'yes':
					pageobj.setWatchlist(true);
					break;
				case 'no':
					pageobj.setWatchlistFromPreferences(false);
					break;
				default:
					pageobj.setWatchlistFromPreferences(true);
					break;
			}
			pageobj.setCreateOption('recreate');
			pageobj.save();
			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();
			var usertalkpage = new Morebits.wiki.page('सदस्य वार्ता:' + initialContrib, "पृष्ठ निर्माता को सूचित किया जा रहा है (" + initialContrib + ")");
			var notifytext = "\n{{subst:हहेच साँचा सूचना|पृष्ठ=" + mw.config.get('wgTitle') + '|कारण=' + params.reason + "}}~~~~";

			usertalkpage.setAppendText(notifytext);
			usertalkpage.setEditSummary("सूचना: [[" + mw.config.get('wgPageName') + "]] को हटाने हेतु चर्चा के लिये नामांकित किया गया है।" + Twinkle.getPref('summaryAd'));
			usertalkpage.setCreateOption('recreate');
			switch (Twinkle.getPref('xfdWatchUser')) {
				case 'yes':
					usertalkpage.setWatchlist(true);
					break;
				case 'no':
					usertalkpage.setWatchlistFromPreferences(false);
					break;
				default:
					usertalkpage.setWatchlistFromPreferences(true);
					break;
			}
			usertalkpage.setFollowRedirect(true);
			usertalkpage.append();
		}
	},


	ffd: {
		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('{{हहेच फ़ाइल|कारण=' + params.reason + "}}\n" + text);
			pageobj.setEditSummary("हटाने हेतु चर्चा के लिये नामांकन; देखें [[वि:पृष्ठ हटाने हेतु चर्चा/फ़ाइलें/" + mw.config.get('wgTitle') + "|नामांकन पृष्ठ]]।" + Twinkle.getPref('summaryAd'));
			switch (Twinkle.getPref('xfdWatchPage')) {
				case 'yes':
					pageobj.setWatchlist(true);
					break;
				case 'no':
					pageobj.setWatchlistFromPreferences(false);
					break;
				default:
					pageobj.setWatchlistFromPreferences(true);
					break;
			}
			pageobj.setCreateOption('recreate');  // it might be possible for a file to exist without a description page
			pageobj.save();
		},
		discussionPage: function(pageobj) {
			var text = pageobj.getPageText();
			var params = pageobj.getCallbackParameters();

			pageobj.setPageText(text + "\n\n{{subst:हहेच फ़ाइल नामांकन|कारण=" + params.reason + "|पृष्ठ=" + mw.config.get('wgTitle') + "}}\n");
			pageobj.setEditSummary("[[" + mw.config.get('wgPageName') + "]] फ़ाइल को हटाने का नामांकन " + Twinkle.getPref('summaryAd'));
			switch (Twinkle.getPref('xfdWatchDiscussion')) {
				case 'yes':
					pageobj.setWatchlist(true);
					break;
				case 'no':
					pageobj.setWatchlistFromPreferences(false);
					break;
				default:
					pageobj.setWatchlistFromPreferences(true);
					break;
			}
			pageobj.setCreateOption('recreate');
			pageobj.save();
			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();
			var usertalkpage = new Morebits.wiki.page('सदस्य वार्ता:' + initialContrib, "पृष्ठ निर्माता को सूचित किया जा रहा है (" + initialContrib + ")");
			var notifytext = "\n{{subst:हहेच फ़ाइल सूचना|पृष्ठ=" + mw.config.get('wgTitle') + '|कारण=' + params.reason + "}}~~~~";

			usertalkpage.setAppendText(notifytext);
			usertalkpage.setEditSummary("सूचना: [[" + mw.config.get('wgPageName') + "]] को हटाने हेतु चर्चा के लिये नामांकित किया गया है।" + Twinkle.getPref('summaryAd'));
			usertalkpage.setCreateOption('recreate');
			switch (Twinkle.getPref('xfdWatchUser')) {
				case 'yes':
					usertalkpage.setWatchlist(true);
					break;
				case 'no':
					usertalkpage.setWatchlistFromPreferences(false);
					break;
				default:
					usertalkpage.setWatchlistFromPreferences(true);
					break;
			}
			usertalkpage.setFollowRedirect(true);
			usertalkpage.append();
		}
	},


	mfd: {
		taggingPage: function(pageobj) {
			var text = pageobj.getPageText();
			var params = pageobj.getCallbackParameters();

			pageobj.setPageText((params.noinclude ? "<noinclude>{{हहेच अन्य" : "{{हहेच अन्य") + '|कारण=' + params.reason + (params.noinclude ? "}}</noinclude>" : "}}\n") + text);
			pageobj.setEditSummary("हटाने हेतु चर्चा के लिये नामांकन; देखें [[वि:पृष्ठ हटाने हेतु चर्चा/अन्य/" + mw.config.get('wgPageName') + "|नामांकन पृष्ठ]]।" + Twinkle.getPref('summaryAd'));
			switch (Twinkle.getPref('xfdWatchPage')) {
				case 'yes':
					pageobj.setWatchlist(true);
					break;
				case 'no':
					pageobj.setWatchlistFromPreferences(false);
					break;
				default:
					pageobj.setWatchlistFromPreferences(true);
					break;
			}
			pageobj.setCreateOption('recreate');  // it might be possible for a file to exist without a description page
			pageobj.save();
		},
		discussionPage: function(pageobj) {
			var text = pageobj.getPageText();
			var params = pageobj.getCallbackParameters();

			pageobj.setPageText(text + "\n\n{{subst:हहेच अन्य नामांकन|कारण=" + params.reason + "|पृष्ठ=" + mw.config.get('wgPageName') + "}}\n");
			pageobj.setEditSummary("[[" + mw.config.get('wgPageName') + "]] पृष्ठ को हटाने का नामांकन " + Twinkle.getPref('summaryAd'));
			switch (Twinkle.getPref('xfdWatchDiscussion')) {
				case 'yes':
					pageobj.setWatchlist(true);
					break;
				case 'no':
					pageobj.setWatchlistFromPreferences(false);
					break;
				default:
					pageobj.setWatchlistFromPreferences(true);
					break;
			}
			pageobj.setCreateOption('recreate');
			pageobj.save();
			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();
			var usertalkpage = new Morebits.wiki.page('सदस्य वार्ता:' + initialContrib, "पृष्ठ निर्माता को सूचित किया जा रहा है (" + initialContrib + ")");
			var notifytext = "\n{{subst:हहेच अन्य सूचना|पृष्ठ=" + mw.config.get('wgPageName') + '|कारण=' + params.reason + "}}~~~~";

			usertalkpage.setAppendText(notifytext);
			usertalkpage.setEditSummary("सूचना: [[" + mw.config.get('wgPageName') + "]] को हटाने हेतु चर्चा के लिये नामांकित किया गया है।" + Twinkle.getPref('summaryAd'));
			usertalkpage.setCreateOption('recreate');
			switch (Twinkle.getPref('xfdWatchUser')) {
				case 'yes':
					usertalkpage.setWatchlist(true);
					break;
				case 'no':
					usertalkpage.setWatchlistFromPreferences(false);
					break;
				default:
					usertalkpage.setWatchlistFromPreferences(true);
					break;
			}
			usertalkpage.setFollowRedirect(true);
			usertalkpage.append();
		}
	}


};



Twinkle.xfd.callback.evaluate = function(e) {
	mw.config.set('wgPageName', mw.config.get('wgPageName').replace(/_/g, ' '));  // for queen/king/whatever and country!

	var type =  e.target.category.value;
	var usertalk = e.target.notify.checked;
	var reason = e.target.xfdreason.value;
	var xfdtarget, noinclude, xfdcat;
	if( type === "श्रेणियाँ" ) {
		xfdtarget = e.target.xfdtarget.value;
		xfdcat = e.target.xfdcat.value;
	}
	if( type === "अन्य" ) {
		noinclude = e.target.noinclude.checked;
	}
	else if (type === 'साँचे' ) {
		noinclude = true;
	}

	Morebits.simpleWindow.setButtonsEnabled( false );
	Morebits.status.init( e.target );

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

	if( !type ) {
		Morebits.status.error( 'Error', 'चर्चा पृष्ठ का नाम नहीं मिला!' );
		return;
	}

	var query, wikipedia_page, wikipedia_api, nompage, thispage, params;
	var date = new Date();
	switch( type ) {

	case 'लेख':
//		Morebits.wiki.addCheckpoint();
		params = { usertalk:usertalk, reason:reason };
		
		Morebits.wiki.actionCompleted.redirect = 'वि:पृष्ठ हटाने हेतु चर्चा/लेख/' + mw.config.get('wgTitle');
		Morebits.wiki.actionCompleted.notice = "नामांकन सम्पूर्ण, चर्चा पृष्ठ खोला जा रहा है";

		wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), "लेख पर नामांकन साँचा जोड़ा जा रहा है");
		wikipedia_page.setFollowRedirect(true);  // should never be needed, but if the article is moved, we would want to follow the redirect
		wikipedia_page.setCallbackParameters(params);
		wikipedia_page.load(Twinkle.xfd.callbacks.afd.taggingArticle);

//		Morebits.wiki.removeCheckpoint();
		break;

	case 'श्रेणियाँ':
		Morebits.wiki.addCheckpoint();

		if( xfdtarget ) {
			xfdtarget = xfdtarget.replace( /^\:?(Category|श्रेणी)\:/i, '' );
		} else {
			xfdtarget = '';
		}

		params = { reason: reason, target: xfdtarget, type: xfdcat };

		// Tagging category
		wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), "श्रेणी पृष्ठ पर नामांकन साँचा जोड़ा जा रहा है");
		wikipedia_page.setCallbackParameters(params);
		wikipedia_page.load(Twinkle.xfd.callbacks.cfd.taggingCategory);

		// Starting discussion page
		nompage = new Morebits.wiki.page('वि:पृष्ठ हटाने हेतु चर्चा/श्रेणियाँ/' + mw.config.get('wgTitle'), "नामांकन चर्चा पृष्ठ पर नामांकन जोड़ा जा रहा है");
		nompage.setCallbackParameters(params);
		nompage.load(Twinkle.xfd.callbacks.cfd.discussionPage);

		// Updating data for the action completed event
		Morebits.wiki.actionCompleted.redirect = 'वि:पृष्ठ हटाने हेतु चर्चा/श्रेणियाँ/' + mw.config.get('wgTitle');
		Morebits.wiki.actionCompleted.notice = "नामांकन सम्पूर्ण, चर्चा पृष्ठ खोला जा रहा है";

		// Notification to first contributor
		if (usertalk) {
			thispage = new Morebits.wiki.page(mw.config.get('wgPageName'));
			thispage.setCallbackParameters(params);
			thispage.lookupCreator(Twinkle.xfd.callbacks.cfd.userNotification);
		}

		Morebits.wiki.removeCheckpoint();
		break;

	case 'साँचे': // TFD
		Morebits.wiki.addCheckpoint();

		params = { reason: reason, noinclude: noinclude };
		// Tagging template
		wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), "साँचे पर नामांकन साँचा जोड़ा जा रहा है");
		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);

		// Starting discussion page
		nompage = new Morebits.wiki.page('वि:पृष्ठ हटाने हेतु चर्चा/साँचे/' + mw.config.get('wgTitle'), "नामांकन चर्चा पृष्ठ पर नामांकन जोड़ा जा रहा है");
		nompage.setCallbackParameters(params);
		nompage.load(Twinkle.xfd.callbacks.tfd.discussionPage);

		// Updating data for the action completed event
		Morebits.wiki.actionCompleted.redirect =  'वि:पृष्ठ हटाने हेतु चर्चा/साँचे/' + mw.config.get('wgTitle');
		Morebits.wiki.actionCompleted.notice = "नामांकन सम्पूर्ण, चर्चा पृष्ठ खोला जा रहा है";

		// Notification to first contributor
		if (usertalk) {
			thispage = new Morebits.wiki.page(mw.config.get('wgPageName'));
			thispage.setCallbackParameters(params);
			thispage.lookupCreator(Twinkle.xfd.callbacks.tfd.userNotification);
		}

		Morebits.wiki.removeCheckpoint();
		break;

	case 'फ़ाइलें': // FFD
		Morebits.wiki.addCheckpoint();

		params = { reason: reason };
		// Tagging file
		wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), "फ़ाइल विवरण पृष्ठ पर नामांकन साँचा जोड़ा जा रहा है");
		wikipedia_page.setFollowRedirect(true);
		wikipedia_page.setCallbackParameters(params);
		wikipedia_page.load(Twinkle.xfd.callbacks.ffd.taggingImage);

		// Adding discussion
		nompage = new Morebits.wiki.page('वि:पृष्ठ हटाने हेतु चर्चा/फ़ाइलें/' + mw.config.get('wgTitle'), "नामांकन चर्चा पृष्ठ पर नामांकन जोड़ा जा रहा है");
		nompage.setCallbackParameters(params);
		nompage.load(Twinkle.xfd.callbacks.ffd.discussionPage);

		// Updating data for the action completed event
		Morebits.wiki.actionCompleted.redirect = 'वि:पृष्ठ हटाने हेतु चर्चा/फ़ाइलें/' + mw.config.get('wgTitle');
		Morebits.wiki.actionCompleted.notice = "नामांकन सम्पूर्ण, चर्चा पृष्ठ खोला जा रहा है";

		// Notification to first contributor
		if (usertalk) {
			thispage = new Morebits.wiki.page(mw.config.get('wgPageName'));
			thispage.setCallbackParameters(params);
			thispage.lookupCreator(Twinkle.xfd.callbacks.ffd.userNotification);
		}

		Morebits.wiki.removeCheckpoint();
		break;

	case 'अन्य': // MFD
		Morebits.wiki.addCheckpoint();

		params = { noinclude: noinclude, reason: reason };
		// Tagging file
		wikipedia_page = new Morebits.wiki.page(mw.config.get('wgPageName'), "पृष्ठ पर नामांकन साँचा जोड़ा जा रहा है");
		wikipedia_page.setFollowRedirect(true);
		wikipedia_page.setCallbackParameters(params);
		wikipedia_page.load(Twinkle.xfd.callbacks.mfd.taggingPage);

		// Adding discussion
		nompage = new Morebits.wiki.page('वि:पृष्ठ हटाने हेतु चर्चा/अन्य/' + mw.config.get('wgPageName'), "नामांकन चर्चा पृष्ठ पर नामांकन जोड़ा जा रहा है");
		nompage.setCallbackParameters(params);
		nompage.load(Twinkle.xfd.callbacks.mfd.discussionPage);

		// Updating data for the action completed event
		Morebits.wiki.actionCompleted.redirect = 'वि:पृष्ठ हटाने हेतु चर्चा/अन्य/' + mw.config.get('wgPageName');
		Morebits.wiki.actionCompleted.notice = "नामांकन सम्पूर्ण, चर्चा पृष्ठ खोला जा रहा है";

		// Notification to first contributor
		if (usertalk) {
			thispage = new Morebits.wiki.page(mw.config.get('wgPageName'));
			thispage.setCallbackParameters(params);
			thispage.lookupCreator(Twinkle.xfd.callbacks.mfd.userNotification);
		}

		Morebits.wiki.removeCheckpoint();
		break;

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

/**
 * General initialization code
 */

var scriptpathbefore = mw.util.wikiScript( "index" ) + "?title=",
    scriptpathafter = "&action=raw&ctype=text/javascript&happy=yes";

// Retrieve the user's Twinkle preferences
$.ajax({
	url: scriptpathbefore + "User:" + encodeURIComponent( mw.config.get("wgUserName")) + "/twinkleoptions.js" + scriptpathafter,
	dataType: "text",
	error: function () { mw.util.jsMessage( "Could not load twinkleoptions.js" ); },
	success: function ( optionsText ) {

		// Quick pass if user has no options
		if ( optionsText === "" ) {
			return;
		}

		// Twinkle options are basically a JSON object with some comments. Strip those:
		optionsText = optionsText.replace( /(?:^(?:\/\/[^\n]*\n)*\n*|(?:\/\/[^\n]*(?:\n|$))*$)/g, "" );

		// First version of options had some boilerplate code to make it eval-able -- strip that too. This part may become obsolete down the line.
		if ( optionsText.lastIndexOf( "window.Twinkle.prefs = ", 0 ) === 0 ) {
			optionsText = optionsText.replace( /(?:^window.Twinkle.prefs = |;\n*$)/g, "" );
		}

		try {
			var options = $.parseJSON( optionsText );

			// Assuming that our options evolve, we will want to transform older versions:
			//if ( options.optionsVersion === undefined ) {
			// ...
			// options.optionsVersion = 1;
			//}
			//if ( options.optionsVersion === 1 ) {
			// ...
			// options.optionsVersion = 2;
			//}
			// At the same time, twinkleconfig.js needs to be adapted to write a higher version number into the options.

			if ( options ) {
				Twinkle.prefs = options;
			}
		}
		catch ( e ) {
			mw.util.jsMessage("Could not parse twinkleoptions.js");
		}
	},
	complete: function () {
		$( Twinkle.load );
	}
});

// Developers: you can import custom Twinkle modules here
// For example, mw.loader.load(scriptpathbefore + "User:UncleDouggie/morebits-test.js" + scriptpathafter);

Twinkle.load = function () {
	// Don't activate on special pages other than "Contributions" so that they load faster, especially the watchlist.
	// Also, Twinkle is incompatible with Internet Explorer versions 8 or lower, so don't load there either.
	if ( (mw.config.get('wgNamespaceNumber') === -1 && mw.config.get('wgCanonicalSpecialPageName') !== "Contributions" && mw.config.get('wgCanonicalSpecialPageName') !== "Prefixindex") ||
		($.client.profile().name === 'msie' && $.client.profile().versionNumber < 9) ) {
		return;
	}

	// Load the modules in the order that the tabs should appears
	// User/user talk-related
	Twinkle.arv();
	Twinkle.warn();
	Twinkle.welcome();
	Twinkle.shared();
	Twinkle.talkback();
	// Deletion
	Twinkle.speedy();
	Twinkle.xfd();
	// maintenance
	Twinkle.protect();
	Twinkle.tag();
	// Misc. ones last
	Twinkle.diff();
	Twinkle.unlink();
	Twinkle.config.init();
	Twinkle.fluff.init();
	if ( Morebits.userIsInGroup('sysop') ) {
		Twinkle.delimages();
		Twinkle.batchdelete();
		Twinkle.batchprotect();
		Twinkle.batchundelete();
	}
	// Run the initialization callbacks for any custom modules
	$( Twinkle.initCallbacks ).each(function ( k, v ) { v(); });
	Twinkle.addInitCallback = function ( func ) { func(); };

	// Increases text size in Twinkle dialogs, if so configured
	if ( Twinkle.getPref( "dialogLargeFont" ) ) {
		mw.util.addCSS( ".morebits-dialog-content, .morebits-dialog-footerlinks { font-size: 100% !important; } " +
			".morebits-dialog input, .morebits-dialog select, .morebits-dialog-content button { font-size: inherit !important; }" );
	}
};

} ( window, document, jQuery )); // End wrap with anonymous function

// </nowiki>