مدیاویکی:Gadget-twinkleprotect-2022.js

از ویکی‌پدیا، دانشنامهٔ آزاد

نکته: برای دیدن تغییرات، ممکن است نیاز باشد که حافظهٔ نهانی مرورگر خود را پس از انتشار پاک‌سازی کنید. گوگل کروم، فایرفاکس، مایکروسافت اج و سافاری: کلید Shift را نگه دارید و روی دکمهٔ Reload در نوار ابزار مرورگر کلیک کنید. برای آگاهی از جزئیات و نحوهٔ پاک‌سازی حافظهٔ نهانی سایر مرورگرها، صفحهٔ ویکی‌پدیا:میانگیر مرورگرتان را خالی کنید را ببینید.

// <nowiki>


(function($) {


/*
 ****************************************
 *** twinkleprotect.js: Protect/RPP module
 ****************************************
 * Mode of invocation:     Tab ("PP"/"RPP")
 * Active on:              Non-special, non-MediaWiki pages
 */

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

var localizedTypeNames = {
		edit: 'ویرایش',
		move: 'انتقال',
		create: 'ایجاد',
		stabilize: 'پایدار',
		cascading: 'آبشاری'
	},
	localizedLevelNames = {
		autoconfirmed: 'کاربران تأییدشده',
		extendedconfirmed: 'کاربران تأییدشده پایدار',
		sysop: 'مدیران'
	};

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

	Twinkle.addPortletLink(Twinkle.protect.callback, (Morebits.userIsSysop || Morebits.userIsEliminator) ? 'حفاظت' : 'درخواست حفاظت', 'tw-rpp',
		(Morebits.userIsSysop || Morebits.userIsEliminator) ? 'حفاظت کردن صفحه' : 'درخواست برای حفاظت صفحه');
};

Twinkle.protect.callback = function twinkleprotectCallback() {
	var Window = new Morebits.simpleWindow(620, 530);
	Window.setTitle((Morebits.userIsSysop || Morebits.userIsEliminator) ? 'اعمال، درخواست یا برچسب‌گذاری محافظت صفحه' : 'درخواست یا برچسب‌گذاری محافظت صفحه');
	Window.setScriptName('توینکل');
	Window.addFooterLink('الگوهای محافظت', 'الگو:الگوهای حفاظت');
	Window.addFooterLink('سیاست حفاظت', 'وپ:حفاظت');
	Window.addFooterLink('راهنمای توینکل', 'وپ:توینکل/توضیحات#protect');
	Window.addFooterLink('ارائهٔ بازخورد', 'بوپ:توینکل');

	var form = new Morebits.quickForm(Twinkle.protect.callback.evaluate);
	var actionfield = form.append({
		type: 'field',
		label: 'نوع کنش'
	});
	if (Morebits.userIsSysop || Morebits.userIsEliminator) {
		actionfield.append({
			type: 'radio',
			name: 'actiontype',
			event: Twinkle.protect.callback.changeAction,
			list: [
				{
					label: 'حفاظت صفحه',
					value: 'protect',
					tooltip: 'اعمال حفاظت بر صفحه.',
					checked: true
				}
			]
		});
	}
	actionfield.append({
		type: 'radio',
		name: 'actiontype',
		event: Twinkle.protect.callback.changeAction,
		list: [
			{
				label: 'درخواست حفاظت صفحه',
				value: 'request',
				tooltip: 'در صورتی که می‌خواهید ' + ((Morebits.userIsSysop || Morebits.userIsEliminator) ? 'به‌جای اعمال حفاظت، ' : '') + 'در وپ:دمص برای حفاظت صفحه درخواست دهید.',
				checked: (!Morebits.userIsSysop && !Morebits.userIsEliminator)
			},
			{
				label: 'برچسب‌زدن صفحه با الگوی حفاظت',
				value: 'tag',
				tooltip: ((Morebits.userIsSysop || Morebits.userIsEliminator) ? 'در صورتی که صفحه را حفاظت کرده‌اید، اما هنوز به آن برچسب نزده‌اید، یا ' : '') + 'در صورتی که مدیر اعمال‌کنندهٔ حفاظت فراموش کرده که به صفحه برچسب بزند، می‌توانید از این گزینه برای افزودن برچسب حفاظت مناسب استفاده کنید.',
				disabled: mw.config.get('wgArticleId') === 0 || mw.config.get('wgPageContentModel') === 'Scribunto'
			}
		]
	});

	form.append({ type: 'field', label: 'پیش‌تنظیم', name: 'field_preset' });
	form.append({ type: 'field', label: '۱', name: 'field1' });
	form.append({ type: 'field', label: '۲', 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
	Twinkle.protect.fetchProtectionLevel();
};


// A list of bots who may be the protecting sysop, for whom we shouldn't
// remind the user contact before requesting unprotection (evaluate)
Twinkle.protect.trustedBots = ['Dexbot', 'Rezabot', 'HujiBot'];

// Customizable namespace and FlaggedRevs settings
// In theory it'd be nice to have restrictionlevels defined here,
// but those are only available via a siteinfo query

// mw.loader.getState('ext.flaggedRevs.review') returns null if the
// FlaggedRevs extension is not registered.  Previously, this was done with
// wgFlaggedRevsParams, but after 1.34-wmf4 it is no longer exported if empty
// (https://gerrit.wikimedia.org/r/c/mediawiki/extensions/FlaggedRevs/+/508427)
var hasFlaggedRevs = mw.loader.getState('ext.flaggedRevs.review') &&
// FlaggedRevs only valid in some namespaces, hardcoded until [[phab:T218479]]
(mw.config.get('wgNamespaceNumber') === 0 || mw.config.get('wgNamespaceNumber') === 4);
// Limit template editor; a Twinkle restriction, not a site setting
var isTemplate = mw.config.get('wgNamespaceNumber') === 10 || mw.config.get('wgNamespaceNumber') === 828;


// Contains the current protection level in an object
// Once filled, it will look something like:
// { edit: { level: "sysop", expiry: <some date>, cascade: true }, ... }
Twinkle.protect.currentProtectionLevels = {};

// returns a jQuery Deferred object, usage:
//   Twinkle.protect.fetchProtectingAdmin(apiObject, pageName, protect/stable).done(function(admin_username) { ...code... });
Twinkle.protect.fetchProtectingAdmin = function twinkleprotectFetchProtectingAdmin(api, pageName, protType, logIds) {
	logIds = logIds || [];

	return api.get({
		format: 'json',
		action: 'query',
		list: 'logevents',
		letitle: pageName,
		letype: protType
	}).then(function(data) {
		// don't check log entries that have already been checked (e.g. don't go into an infinite loop!)
		var event = data.query ? $.grep(data.query.logevents, function(le) {
			return $.inArray(le.logid, logIds);
		})[0] : null;
		if (!event) {
			// fail gracefully
			return null;
		} else if (event.action === 'move_prot' || event.action === 'move_stable') {
			return twinkleprotectFetchProtectingAdmin(api, protType === 'protect' ? event.params.oldtitle_title : event.params.oldtitle, protType, logIds.concat(event.logid));
		}
		return event.user;
	});
};

Twinkle.protect.fetchProtectionLevel = function twinkleprotectFetchProtectionLevel() {

	var api = new mw.Api();
	var protectDeferred = api.get({
		format: 'json',
		indexpageids: true,
		action: 'query',
		list: 'logevents',
		letype: 'protect',
		letitle: mw.config.get('wgPageName'),
		prop: hasFlaggedRevs ? 'info|flagged' : 'info',
		inprop: 'protection|watched',
		titles: mw.config.get('wgPageName')
	});
	var stableDeferred = api.get({
		format: 'json',
		action: 'query',
		list: 'logevents',
		letype: 'stable',
		letitle: mw.config.get('wgPageName')
	});

	var earlyDecision = [protectDeferred];
	if (hasFlaggedRevs) {
		earlyDecision.push(stableDeferred);
	}

	$.when.apply($, earlyDecision).done(function(protectData, stableData) {
		// $.when.apply is supposed to take an unknown number of promises
		// via an array, which it does, but the type of data returned varies.
		// If there are two or more deferreds, it returns an array (of objects),
		// but if there's just one deferred, it retuns a simple object.
		// This is annoying.
		protectData = $(protectData).toArray();

		var pageid = protectData[0].query.pageids[0];
		var page = protectData[0].query.pages[pageid];
		var current = {}, adminEditDeferred;

		// Save requested page's watched status for later in case needed when filing request
		Twinkle.protect.watched = page.watchlistexpiry || page.watched === '';

		$.each(page.protection, function(index, protection) {
			// Don't overwrite actual page protection with cascading protection
			if (!protection.source) {
				current[protection.type] = {
					level: protection.level,
					expiry: protection.expiry,
					cascade: protection.cascade === ''
				};
				// logs report last admin who made changes to either edit/move/create protection, regardless if they only modified one of them
				if (!adminEditDeferred) {
					adminEditDeferred = Twinkle.protect.fetchProtectingAdmin(api, mw.config.get('wgPageName'), 'protect');
				}
			} else {
				// Account for the page being covered by cascading protection
				current.cascading = {
					expiry: protection.expiry,
					source: protection.source,
					level: protection.level // should always be sysop, unused
				};
			}
		});

		if (page.flagged) {
			current.stabilize = {
				level: page.flagged.protection_level,
				expiry: page.flagged.protection_expiry
			};
			adminEditDeferred = Twinkle.protect.fetchProtectingAdmin(api, mw.config.get('wgPageName'), 'stable');
		}

		// show the protection level and log info
		Twinkle.protect.hasProtectLog = !!protectData[0].query.logevents.length;
		Twinkle.protect.protectLog = Twinkle.protect.hasProtectLog && protectData[0].query.logevents;
		Twinkle.protect.hasStableLog = hasFlaggedRevs ? !!stableData[0].query.logevents.length : false;
		Twinkle.protect.stableLog = Twinkle.protect.hasStableLog && stableData[0].query.logevents;
		Twinkle.protect.currentProtectionLevels = current;

		if (adminEditDeferred) {
			adminEditDeferred.done(function(admin) {
				if (admin) {
					$.each(['edit', 'move', 'create', 'stabilize', 'cascading'], function(i, type) {
						if (Twinkle.protect.currentProtectionLevels[type]) {
							Twinkle.protect.currentProtectionLevels[type].admin = admin;
						}
					});
				}
				Twinkle.protect.callback.showLogAndCurrentProtectInfo();
			});
		} else {
			Twinkle.protect.callback.showLogAndCurrentProtectInfo();
		}
	});
};

Twinkle.protect.callback.showLogAndCurrentProtectInfo = function twinkleprotectCallbackShowLogAndCurrentProtectInfo() {
	var currentlyProtected = !$.isEmptyObject(Twinkle.protect.currentProtectionLevels);

	if (Twinkle.protect.hasProtectLog || Twinkle.protect.hasStableLog) {
		var $linkMarkup = $('<span>');

		if (Twinkle.protect.hasProtectLog) {
			$linkMarkup.append(
				$('<a target="_blank" href="' + mw.util.getUrl('ویژه:سیاهه‌ها', {action: 'view', page: mw.config.get('wgPageName'), type: 'protect'}) + '">سیاههٔ حفاظت</a>'));
			if (!currentlyProtected || (!Twinkle.protect.currentProtectionLevels.edit && !Twinkle.protect.currentProtectionLevels.move)) {
				var lastProtectAction = Twinkle.protect.protectLog[0];
				if (lastProtectAction.action === 'unprotect') {
					$linkMarkup.append(' (خارج شده از حفاظت در ' + new Morebits.date(lastProtectAction.timestamp).calendar('utc') + ')');
				} else { // protect or modify
					$linkMarkup.append(' (منقضی‌شده در ' + new Morebits.date(lastProtectAction.params.details[0].expiry).calendar('utc') + ')');
				}
			}
			$linkMarkup.append(Twinkle.protect.hasStableLog ? $('<span> &bull; </span>') : null);
		}

		if (Twinkle.protect.hasStableLog) {
			$linkMarkup.append($('<a target="_blank" href="' + mw.util.getUrl('ویژه:سیاهه‌ها', {action: 'view', page: mw.config.get('wgPageName'), type: 'stable'}) + '">سیاههٔ تغییرات نیازمند بازبینی</a>)'));
			if (!currentlyProtected || !Twinkle.protect.currentProtectionLevels.stabilize) {
				var lastStabilizeAction = Twinkle.protect.stableLog[0];
				if (lastStabilizeAction.action === 'reset') {
					$linkMarkup.append(' (بازنشانی‌شده در ' + new Morebits.date(lastStabilizeAction.timestamp).calendar('utc') + ')');
				} else { // config or modify
					$linkMarkup.append(' (منقضی‌شده در ' + new Morebits.date(lastStabilizeAction.params.expiry).calendar('utc') + ')');
				}
			}
		}

		Morebits.status.init($('div[name="hasprotectlog"] span')[0]);
		Morebits.status.warn(
			currentlyProtected ? 'حفاظت‌های پشین' : 'این صفحه در گذشته حفاظت شده‌است',
			$linkMarkup[0]
		);
	}

	Morebits.status.init($('div[name="currentprot"] span')[0]);
	var protectionNode = [], statusLevel = 'info';

	if (currentlyProtected) {
		$.each(Twinkle.protect.currentProtectionLevels, function(type, settings) {
			var label = type === 'stabilize' ? 'تغییرات نیازمند بازبینی' : localizedTypeNames[type];

			if (type === 'cascading') { // Covered by another page
				label = 'حفاظت آبشاری ';
				protectionNode.push($('<b>' + label + '</b>')[0]);
				if (settings.source) { // Should by definition exist
					var sourceLink = '<a target="_blank" href="' + mw.util.getUrl(settings.source) + '">' + settings.source + '</a>';
					protectionNode.push($('<span>از ' + sourceLink + '</span>')[0]);
				}
			} else {
				var level = localizedLevelNames[settings.level];
				// Make cascading protection more prominent
				if (settings.cascade) {
					level += ' (آبشاری)';
				}
				protectionNode.push($('<b>' + label + ': ' + level + '</b>')[0]);
			}

			if (settings.expiry === 'infinity') {
				protectionNode.push(' (بی‌پایان) ');
			} else {
				protectionNode.push(' (در ' + new Morebits.date(settings.expiry).calendar('utc') + ' منقضی خواهد شد) ');
			}
			if (settings.admin) {
				var adminLink = '<a target="_blank" href="' + mw.util.getUrl('بحث کاربر:' + settings.admin) + '">' + settings.admin + '</a>';
				protectionNode.push($('<span>توسط ' + adminLink + '</span>')[0]);
			}
			protectionNode.push($('<span> \u2022 </span>')[0]);
		});
		protectionNode = protectionNode.slice(0, -1); // remove the trailing bullet
		statusLevel = 'warn';
	} else {
		protectionNode.push($('<b>فاقد حفاظت فعلی</b>')[0]);
	}

	Morebits.status[statusLevel]('سطح حفاظت فعلی', protectionNode);
};

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: 'پیش‌تنظیم', name: 'field_preset' });
			field_preset.append({
				type: 'select',
				name: 'category',
				label: 'انتخاب پیش‌تنظیم:',
				event: Twinkle.protect.callback.changePreset,
				list: mw.config.get('wgArticleId') ? Twinkle.protect.protectionTypes : Twinkle.protect.protectionTypesCreate
			});

			field2 = new Morebits.quickForm.element({ type: 'field', label: 'گزینه‌های حفاظت', name: 'field2' });
			field2.append({ type: 'div', name: 'currentprot', label: ' ' });  // holds the current protection level, as filled out by the async callback
			field2.append({ type: 'div', name: 'hasprotectlog', label: ' ' });
			// for existing pages
			if (mw.config.get('wgArticleId')) {
				field2.append({
					type: 'checkbox',
					event: Twinkle.protect.formevents.editmodify,
					list: [
						{
							label: 'تغییر حفاظت در برابر ویرایش',
							name: 'editmodify',
							tooltip: 'در صورت غیرفعال بودن این گزینه، سطح حفاظت در برابر ویرایش و زمان سرآمدن آن دست‌نخورده باقی خواهد ماند.',
							checked: true
						}
					]
				});
				field2.append({
					type: 'select',
					name: 'editlevel',
					label: 'حفاظت در برابر ویرایش:',
					event: Twinkle.protect.formevents.editlevel,
					list: Twinkle.protect.protectionLevels.filter(function(level) {
						// Filter TE outside of templates and modules
						return isTemplate || level.value !== 'sysop';
					})
				});
				field2.append({
					type: 'select',
					name: 'editexpiry',
					label: 'سرآمدن:',
					event: function(e) {
						if (e.target.value === 'custom') {
							Twinkle.protect.doCustomExpiry(e.target);
						}
					},
					// default expiry selection (2 days) is conditionally set in Twinkle.protect.callback.changePreset
					list: Twinkle.protect.protectionLengths
				});
				field2.append({
					type: 'checkbox',
					event: Twinkle.protect.formevents.movemodify,
					list: [
						{
							label: 'تغییر حفاظت در برابر انتقال',
							name: 'movemodify',
							tooltip: 'در صورت غیرفعال بودن این گزینه، سطح حفاظت در برابر انتقال و زمان سرآمدن آن دست‌نخورده باقی خواهد ماند.',
							checked: true
						}
					]
				});
				field2.append({
					type: 'select',
					name: 'movelevel',
					label: 'حفاظت در برابر انتقال:',
					event: Twinkle.protect.formevents.movelevel,
					list: Twinkle.protect.protectionLevels.filter(function(level) {
						// Autoconfirmed is required for a move, redundant
						return level.value !== 'autoconfirmed' && (isTemplate || level.value !== 'sysop');
					})
				});
				field2.append({
					type: 'select',
					name: 'moveexpiry',
					label: 'سرآمدن:',
					event: function(e) {
						if (e.target.value === 'custom') {
							Twinkle.protect.doCustomExpiry(e.target);
						}
					},
					// default expiry selection (2 days) is conditionally set in Twinkle.protect.callback.changePreset
					list: Twinkle.protect.protectionLengths
				});
				if (hasFlaggedRevs) {
					field2.append({
						type: 'checkbox',
						event: Twinkle.protect.formevents.pcmodify,
						list: [
							{
								label: 'تغییر حفاظت نیازمند بازبینی',
								name: 'pcmodify',
								tooltip: 'در صورت غیرفعال بودن این گزینه، سطح حفاظت نیازمند بازبینی و زمان سرآمدن آن دست‌نخورده باقی خواهد ماند.',
								checked: true
							}
						]
					});
					field2.append({
						type: 'select',
						name: 'pclevel',
						label: 'تغییرات نیازمند بازبینی:',
						event: Twinkle.protect.formevents.pclevel,
						list: [
							{ label: 'هیچ', value: 'none' },
							{ label: 'تغییرات نیازمند بازبینی', value: 'autoconfirmed', selected: true }
						]
					});
					field2.append({
						type: 'select',
						name: 'pcexpiry',
						label: 'سرآمدن:',
						event: function(e) {
							if (e.target.value === 'custom') {
								Twinkle.protect.doCustomExpiry(e.target);
							}
						},
						// default expiry selection (1 month) is conditionally set in Twinkle.protect.callback.changePreset
						list: Twinkle.protect.protectionLengths
					});
				}
			} else {  // for non-existing pages
				field2.append({
					type: 'select',
					name: 'createlevel',
					label: 'حفاظت در برابر ایجاد:',
					event: Twinkle.protect.formevents.createlevel,
					list: Twinkle.protect.protectionLevels.filter(function(level) {
						// Filter TE always, and autoconfirmed in mainspace, redundant since WP:ACPERM
						return level.value !== 'sysop' && (mw.config.get('wgNamespaceNumber') !== 0 || level.value !== 'autoconfirmed');
					})
				});
				field2.append({
					type: 'select',
					name: 'createexpiry',
					label: 'سرآمدن:',
					event: function(e) {
						if (e.target.value === 'custom') {
							Twinkle.protect.doCustomExpiry(e.target);
						}
					},
					// default expiry selection (indefinite) is conditionally set in Twinkle.protect.callback.changePreset
					list: Twinkle.protect.protectionLengths
				});
			}
			field2.append({
				type: 'textarea',
				name: 'protectReason',
				label: 'دلیل (برای ثبت در سیاههٔ حفاظت):'
			});
			field2.append({
				type: 'div',
				name: 'protectReason_notes',
				label: 'یادداشت:',
				style: 'display:inline-block; margin-top:4px;',
				tooltip: 'درج یادداشتی در سیاههٔ حفاظت برای اشاره به اعمال این حفاظت بر پایهٔ درخواست در دمص.'
			});
			field2.append({
				type: 'checkbox',
				event: Twinkle.protect.callback.annotateProtectReason,
				style: 'display:inline-block; margin-top:4px;',
				list: [
					{
						label: 'درخواست دمص',
						name: 'protectReason_notes_rfpp',
						checked: false,
						value: 'درخواست‌شده در [[وپ:دمص]]'
					}
				]
			});
			field2.append({
				type: 'input',
				event: Twinkle.protect.callback.annotateProtectReason,
				label: 'شناسهٔ نسخهٔ دمص',
				name: 'protectReason_notes_rfppRevid',
				value: '',
				tooltip: 'شناسهٔ نسخهٔ اختیاری برای نسخه‌ای از دمص که درخواست حفاظت در آن موجود است.'
			});
			if (!mw.config.get('wgArticleId') || mw.config.get('wgPageContentModel') === 'Scribunto') {  // tagging isn't relevant for non-existing or module pages
				break;
			}
			/* falls through */
		case 'tag':
			field1 = new Morebits.quickForm.element({ type: 'field', label: 'گزینه‌های برچسب زدن', name: 'field1' });
			field1.append({ type: 'div', name: 'currentprot', label: ' ' });  // holds the current protection level, as filled out by the async callback
			field1.append({ type: 'div', name: 'hasprotectlog', label: ' ' });
			field1.append({
				type: 'select',
				name: 'tagtype',
				label: 'انتخاب الگوی حفاظت:',
				list: Twinkle.protect.protectionTags,
				event: Twinkle.protect.formevents.tagtype
			});
			field1.append({
				type: 'checkbox',
				list: [
					{
						name: 'small',
						label: 'کوچک (کوچک=بله)',
						tooltip: 'استفاده از ویژگی |کوچک=بله در الگو، به شکلی که فقط قفل نمایش داده شود',
						checked: true
					},
					{
						name: 'noinclude',
						label: 'قرار دادن الگوی حفاظت درون برچسب &lt;noinclude&gt;',
						tooltip: 'الگوی حفاظت را درون &lt;noinclude&gt; قرار می‌دهد تا تراگنجانش نشود',
						checked: mw.config.get('wgNamespaceNumber') === 10 || (mw.config.get('wgNamespaceNumber') === mw.config.get('wgNamespaceIds').project && mw.config.get('wgTitle').indexOf('نظرخواهی برای حذف/') === 0)
					}
				]
			});
			break;

		case 'request':
			field_preset = new Morebits.quickForm.element({ type: 'field', label: 'نوع حفاظت', name: 'field_preset' });
			field_preset.append({
				type: 'select',
				name: 'category',
				label: 'نوع و دلیل:',
				event: Twinkle.protect.callback.changePreset,
				list: mw.config.get('wgArticleId') ? Twinkle.protect.protectionTypes : Twinkle.protect.protectionTypesCreate
			});

			field1 = new Morebits.quickForm.element({ type: 'field', label: 'گزینه‌ها', name: 'field1' });
			field1.append({ type: 'div', name: 'currentprot', label: ' ' });  // holds the current protection level, as filled out by the async callback
			field1.append({ type: 'div', name: 'hasprotectlog', label: ' ' });
			field1.append({
				type: 'select',
				name: 'expiry',
				label: 'مدت:',
				list: [
					{ label: '', selected: true, value: '' },
					{ label: 'موقت', value: 'temporary' },
					{ label: 'بی‌پایان', value: 'infinity' }
				]
			});
			field1.append({
				type: 'textarea',
				name: 'reason',
				label: 'دلیل:'
			});
			break;
		default:
			alert("یک جای کار پودمان حفاظت توینکل می‌لنگد!");
			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);

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

	// re-add protection level and log info, if it's available
	Twinkle.protect.callback.showLogAndCurrentProtectInfo();
};

// NOTE: This function is used by batchprotect as well
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) {
		// sync move settings with edit settings if applicable
		if (e.target.form.movelevel.disabled && !e.target.form.editlevel.disabled) {
			e.target.form.movelevel.value = e.target.form.editlevel.value;
			e.target.form.moveexpiry.value = e.target.form.editexpiry.value;
		} else if (e.target.form.editlevel.disabled) {
			e.target.form.movelevel.value = 'sysop';
			e.target.form.moveexpiry.value = 'infinity';
		}
		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('یک زمان سرآمدن سفارشی وارد کنید. \n می‌توانید زمان‌های نسبی مثل «۱ دقیقه» یا «۱۹ روز» یا زمان‌مهر مطلق به شکل yyyymmddhhmm وارد کنید (به عنوان مثال "200602011405" نشان دهندهٔ اول فوریهٔ ۲۰۰۶، ساعت ۱۴:۰۵ جهانی است).', '');
	if (custom) {
		var option = document.createElement('option');
		option.setAttribute('value', custom);
		option.textContent = custom;
		target.appendChild(option);
		target.value = custom;
	} else {
		target.selectedIndex = 0;
	}
};

// NOTE: This list is used by batchprotect as well
Twinkle.protect.protectionLevels = [
	{ label: 'همه', value: 'all' },
	{ label: 'تأییدشده', value: 'autoconfirmed' },
	{ label: 'تأییدشده پایدار', value: 'extendedconfirmed' },
	{ label: 'ویرایشگران الگو', value: 'sysop' },
	{ label: 'مدیران', value: 'sysop', selected: true }
];

// default expiry selection is conditionally set in Twinkle.protect.callback.changePreset
// NOTE: This list is used by batchprotect as well
Twinkle.protect.protectionLengths = [
	{ label: '۱ ساعت', value: '1 hour' },
	{ label: '۲ ساعت', value: '2 hours' },
	{ label: '۳ ساعت', value: '3 hours' },
	{ label: '۶ ساعت', value: '6 hours' },
	{ label: '۱۲ ساعت', value: '12 hours' },
	{ label: '۱ روز', value: '1 day' },
	{ label: '۲ روز', value: '2 days' },
	{ label: '۳ روز', value: '3 days' },
	{ label: '۴ روز', value: '4 days' },
	{ label: '۱ هفته', value: '1 week' },
	{ label: '۲ هفته', value: '2 weeks' },
	{ label: '۱ ماه', value: '1 month' },
	{ label: '۲ ماه', value: '2 months' },
	{ label: '۳ ماه', value: '3 months' },
	{ label: '۱ سال', value: '1 year' },
	{ label: '۲ سال', value: '2 years' },
	{ label: 'بی‌پایان', value: 'infinity' },
	{ label: 'دلخواه...', value: 'custom' }
];

Twinkle.protect.protectionTypes = [
	{ label: 'خروج از حفاظت', value: 'unprotect' },
	{
		label: 'حفاظت کامل',
		list: [
			{ label: 'عمومی (طلایی)', value: 'حص-حفاظت‌شده' },
			{ label: 'اختلاف محتوایی/جنگ ویرایشی (طلایی)', value: 'حص-مناقشه' },
			{ label: 'خرابکاری ادامه‌دار (طلایی)', value: 'حص-خرابکاری' },
			{ label: 'صفحهٔ بحث کاربر بسته‌شده (طلایی)', value: 'حص-بحث کاربر' }
		]
	},
	{
		label: 'حفاظت الگو',
		list: [
			{ label: 'الگوی حساس (صورتی)', value: 'حص-الگو' }
		]
	},
	{
		label: 'نیمه‌حفاظت ویژه',
		list: [
			//{ label: 'Arbitration enforcement (ECP)', selected: true, value: 'pp-30-500-arb' },
			{ label: 'خرابکاری ادامه‌دار (آبی)', value: 'حص-۳۰-۵۰۰-خرابکاری' },
			{ label: 'ویرایش اخلالگرانه (آبی)', value: 'حص-۳۰-۵۰۰-اخلالگرانه' },
			{ label: 'نقض سیاست زندگی‌نامهٔ زندگان (آبی)', value: 'حص-۳۰-۵۰۰-زنده' },
			{ label: 'زاپاس‌بازی (آبی)', value: 'حص-۳۰-۵۰۰-زاپاس' }
		]
	},
	{
		label: 'نیمه‌حفاظت',
		list: [
			{ label: 'عمومی (نقره‌ای)', value: 'حص-نیمه-حفاظت‌شده' },
			{ label: 'خرابکاری ادامه‌دار (نقره‌ای)', selected: true, value: 'حص-نیمه-خرابکاری' },
			{ label: 'ویرایش اخلالگرانه (نقره‌ای)', value: 'حص-نیمه-اخلالگرانه' },
			{ label: 'افزودن مطالب بدون منبع (نقره‌ای)', value: 'حص-نیمه-بدون منبع' },
			{ label: 'نقض سیاست زندگی‌نامهٔ زندگان (نقره‌ای)', value: 'حص-نیمه-زنده' },
			{ label: 'زاپاس‌بازی (نقره‌ای)', value: 'حص-نیمه-زاپاس' },
			{ label: 'صفحهٔ بحث کاربر بسته‌شده (نقره‌ای)', value: 'حص-نیمه-بحث کاربر' }
		]
	},
	{
		label: 'تغییرات نیازمند بازبینی',
		list: [
			{ label: 'عمومی (سفید)', value: 'حص-حنب-حفاظت‌شده' },
			{ label: 'خرابکاری ادامه‌دار (سفید)', value: 'حص-حنب-خرابکاری' },
			{ label: 'ویرایش اخلالگرانه (سفید)', value: 'حص-حنب-اخلالگرانه' },
			{ label: 'افزودن مطالب بدون منبع (سفید)', value: 'حص-حنب-بدون منبع' },
			{ label: 'نقص سیاست زندگی‌نامهٔ زندگان (سفید)', value: 'حص-حنب-زنده' }
		]
	},
	{
		label: 'حفاظت در برابر انتقال',
		list: [
			{ label: 'عمومی (سبز)', value: 'حص-انتقال' },
			{ label: 'اختلاف نظر/جنگ بر سر عنوان (سبز)', value: 'حص-انتقال-مناقشه' },
			{ label: 'خرابکاری از طریق انتقال (سبز)', value: 'حص-انتقال-خرابکاری' },
			{ label: 'صفحهٔ پربازدید (سبز)', value: 'حص-انتقال-بی‌پایان' }
		]
	}
].filter(function(type) {
	// Filter for templates and flaggedrevs
	return (isTemplate || type.label !== 'حفاظت الگو') && (hasFlaggedRevs || type.label !== 'تغییرات نیازمند بازبینی');
});

Twinkle.protect.protectionTypesCreate = [
	{ label: 'خروج از حفاظت', value: 'unprotect' },
	{
		label: 'حفاظت در برابر ایجاد',
		list: [
			{ label: 'عنوان توهین‌آمیز', value: 'حص-ایجاد-توهین‌آمیز' },
			{ label: 'ایجادهای پی‌درپی', selected: true, value: 'حص-ایجاد-نمک' },
			{ label: 'زندگی‌نامهٔ زندگان که به‌تازگی حذف شده', value: 'حس-ایجاد-زنده' }
		]
	}
];

// A page with both regular and PC protection will be assigned its regular
// protection weight plus 2
Twinkle.protect.protectionWeight = {
	sysop: 40,
	extendedconfirmed: 20,
	autoconfirmed: 10,
	flaggedrevs_autoconfirmed: 5,  // Pending Changes protection alone
	all: 0,
	flaggedrevs_none: 0  // just in case
};

// NOTICE: keep this synched with [[MediaWiki:Protect-dropdown]]
// Also note: stabilize = Pending Changes level
// expiry will override any defaults
Twinkle.protect.protectionPresetsInfo = {
	'حص-حفاظت‌شده': {
		edit: 'sysop',
		move: 'sysop',
		reason: null
	},
	'حص-مناقشه': {
		edit: 'sysop',
		move: 'sysop',
		reason: '[[وپ:حفاظت#مناقشه بر سر محتوا|مناقشه بر روی مطالب/جنگ ویرایشی غیرسازنده]]'
	},
	'حص-خرابکاری': {
		edit: 'sysop',
		move: 'sysop',
		reason: '[[وپ:خرابکاری|خرابکاری گسترده]]'
	},
	'حص-بحث کاربر': {
		edit: 'sysop',
		move: 'sysop',
		expiry: 'infinity',
		reason: '[[وپ:حفاظت#فضای نام بحث کاربر|استفادهٔ نادرست از صفحهٔ بحث در زمان قطع دسترسی]]'
	},
	'حص-الگو': {
		edit: 'sysop',
		move: 'sysop',
		expiry: 'infinity',
		reason: '[[وپ:حساس|الگو یا پودمان حساس]]'
	},
	'حص-۳۰-۵۰۰-هیئت': {
		edit: 'extendedconfirmed',
		move: 'extendedconfirmed',
		expiry: 'infinity',
		reason: '[[وپ:قفل آبی|حکم هیئت داوری یا هیئت نظارت]]',
		template: 'حص-۳۰-۵۰۰'
	},
	'حص-۳۰-۵۰۰-خرابکاری': {
		edit: 'extendedconfirmed',
		move: 'extendedconfirmed',
		reason: '[[وپ:خرابکاری|خرابکاری]] گسترده توسط کاربران تأییدشده یا تأییدشدهٔ پایدار',
		template: 'حص-۳۰-۵۰۰'
	},
	'حص-۳۰-۵۰۰-اخلالگرانه': {
		edit: 'extendedconfirmed',
		move: 'extendedconfirmed',
		reason: '[[وپ:ویرایش اخلالگرانه|ویرایش‌های اخلالگرانه]] توسط کاربران تأییدشده یا تأییدشدهٔ پایدار',
		template: 'حص-۳۰-۵۰۰'
	},
	'حص-۳۰-۵۰۰-زنده': {
		edit: 'extendedconfirmed',
		move: 'extendedconfirmed',
		reason: 'نقض گستردهٔ [[وپ:زنده|سیاست زندگی‌نامهٔ افراد زنده]] توسط کاربران تأییدشده یا تأییدشدهٔ پایدار',
		template: 'حص-۳۰-۵۰۰'
	},
	'حص-۳۰-۵۰۰-زاپاس': {
		edit: 'extendedconfirmed',
		move: 'extendedconfirmed',
		reason: '[[وپ:زاپاس|زاپاس‌بازی]] گسترده',
		template: 'حص-۳۰-۵۰۰'
	},
	'حص-نیمه-خرابکاری': {
		edit: 'autoconfirmed',
		reason: '[[وپ:خرابکاری|خرابکاری]] گسترده',
		template: 'حص-خرابکاری'
	},
	'حص-نیمه-اخلالگرانه': {
		edit: 'autoconfirmed',
		reason: '[[وپ:ویرایش اخلالگرانه|ویرایش‌های اخلالگرانهٔ]] ادامه‌دار',
		template: 'حص-حفاظت‌شده'
	},
	'حص-نیمه-بدون منبع': {
		edit: 'autoconfirmed',
		reason: 'افزودن مداوم [[وپ:مقدارج|مطالب بدون منبع یا با منبع ضعیف]]',
		template: 'حص-حفاظت‌شده'
	},
	'حص-نیمه-زنده': {
		edit: 'autoconfirmed',
		reason: 'نقض [[وپ:زنده|سیاست زندگی‌نامهٔ افراد زنده]]',
		template: 'حص-زنده'
	},
	'حص-نیمه-بحث کاربر': {
		edit: 'autoconfirmed',
		move: 'autoconfirmed',
		expiry: 'infinity',
		reason: '[[وپ:حفاظت#فضای نام بحث کاربر|استفادهٔ نادرست از صفحهٔ بحث در زمان قطع دسترسی]]',
		template: 'حص-بحث کاربر'
	},
	'حص-نیمه-الگو': {  // removed for now
		edit: 'autoconfirmed',
		move: 'autoconfirmed',
		expiry: 'infinity',
		reason: '[[وپ:حساس|الگو یا پودمان حساس]]',
		template: 'حص-الگو'
	},
	'حص-نیمه-زاپاس': {
		edit: 'autoconfirmed',
		reason: '[[وپ:زاپاس|زاپاس‌بازی گسترده]]',
		template: 'حص-زاپاس'
	},
	'حص-نیمه-حفاظت‌شده': {
		edit: 'autoconfirmed',
		reason: null,
		template: 'حص-حفاظت‌شده'
	},
	'حص-حنب-خرابکاری': {
		stabilize: 'autoconfirmed',  // stabilize = Pending Changes
		reason: '[[وپ:خرابکاری|خرابکاری]] گسترده',
		template: 'حص-حنب'
	},
	'حص-حنب-اخلالگرانه': {
		stabilize: 'autoconfirmed',
		reason: '[[وپ:ویرایش اخلالگرانه|ویرایش‌های اخلالگرانهٔ]] ادامه‌دار',
		template: 'حص-حنب'
	},
	'حص-حنب-بدون منبع': {
		stabilize: 'autoconfirmed',
		reason: 'افزودن مداوم [[وپ:مقدارج|مطالب بدون منبع یا با منبع ضعیف]]',
		template: 'حص-حنب'
	},
	'حص-حنب-زنده': {
		stabilize: 'autoconfirmed',
		reason: 'نقض [[وپ:زنده|سیاست زندگی‌نامهٔ افراد زنده]]',
		template: 'حص-حنب'
	},
	'حص-حنب-حفاظت‌شده': {
		stabilize: 'autoconfirmed',
		reason: null,
		template: 'حص-حنب'
	},
	'حص-انتقال': {
		move: 'sysop',
		reason: null
	},
	'حص-انتقال-مناقشه': {
		move: 'sysop',
		reason: '[[وپ:قفل انتقال|مناقشه بر سر نام صفحه]]'
	},
	'حص-انتقال-خرابکاری': {
		move: 'sysop',
		reason: '[[وپ:قفل انتقال|خرابکاری به وسیلهٔ انتقال]]'
	},
	'حص-انتقال-بی‌پایان': {
		move: 'sysop',
		expiry: 'infinity',
		reason: '[[وپ:قفل انتقال|صفحهٔ پربازدید]]'
	},
	'خروج از حفاظت': {
		edit: 'all',
		move: 'all',
		stabilize: 'none',
		create: 'all',
		reason: null,
		template: 'none'
	},
	'حص-ایجاد-توهین‌آمیز': {
		create: 'sysop',
		reason: '[[وپ:نمک|عنوان توهین‌آمیز]]'
	},
	'حص-ایجاد-نمک': {
		create: 'extendedconfirmed',
		reason: '[[وپ:نمک|ایجادهای پی‌درپی]]'
	},
	'حص-ایجاد-زنده': {
		create: 'extendedconfirmed',
		reason: '[[وپ:حذف زنده|زندگی‌نامهٔ زندگان که به‌تازگی حذف شده]]'
	}
};

Twinkle.protect.protectionTags = [
	{
		label: 'هیچ (حذف الگوهای حفاظت موجود)',
		value: 'none'
	},
	{
		label: 'هیچ (عدم حذف الگوهای حفاظت موجود)',
		value: 'noop'
	},
	{
		label: 'الگوهای حفاظت در برابر ویرایش',
		list: [
			{ label: '{{حص-خرابکاری}}: خرابکاری', value: 'حص-خرابکاری' },
			{ label: '{{حص-مناقشه}}: مناقشه/جنگ ویرایشی', value: 'حص-مناقشه' },
			{ label: '{{حص-زنده}}: نقض سیاست زندگی‌نامه زندگان', value: 'حص-زنده' },
			{ label: '{{حص-زاپاس}}: زاپاس‌بازی', value: 'حص-زاپاس' },
			{ label: '{{حص-الگو}}: الگوی حساس', value: 'حص-الگو' },
			{ label: '{{حص-بحث کاربر}}: بحث کاربر بسته‌شده', value: 'حص-بحث کاربر' },
			{ label: '{{حص-حفاظت‌شده}}: حفاظت عمومی', value: 'حص-حفاظت‌شده' },
			{ label: '{{حص-نیمه-بی‌پایان}}: نیمه‌حفاظت کلی و طولانی‌مدت', value: 'حص-حفاظت‌شده' },
			{ label: '{{حص-۳۰-۵۰۰}}: نیمه‌حفاظت ویژه', value: 'حص-۳۰-۵۰۰' }
		]
	},
	{
		label: 'الگوهای حفاظت نیازمند بازبینی',
		list: [
			{ label: '{{حص-حنب}}: تغییرات نیازمند بازبینی', value: 'حص-حنب' }
		]
	},
	{
		label: 'الگوهای حفاظت در برابر انتقال',
		list: [
			{ label: '{{حص-انتقال-مناقشه}}: مناقشه/جنگ بر سر عنوان', value: 'حص-انتقال-مناقشه' },
			{ label: '{{حص-انتقال-خرابکاری}}: خرابکاری از طریق انتقال', value: 'حص-انتقال-خرابکاری' },
			{ label: '{{حص-انتقال-بی‌پایان}}: کلی و طولانی‌مدت', value: 'حص-انتقال-بی‌پایان' },
			{ label: '{{حص-انتقال}}: سایر', value: 'حص-انتقال' }
		]
	}
].filter(function(type) {
	// Filter FlaggedRevs
	return hasFlaggedRevs || type.label !== 'الگوهای حفاظت نیازمند بازبینی';
});

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 });
			}

			form.editexpiry.value = form.moveexpiry.value = item.expiry || '2 days';


			if (form.pcmodify) {
				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 });
				}
				form.pcexpiry.value = item.expiry || '1 month';
			}
		} else {
			if (item.create) {
				form.createlevel.value = item.create;
				Twinkle.protect.formevents.createlevel({ target: form.createlevel });
			}
			form.createexpiry.value = item.expiry || 'infinity';
		}

		var reasonField = actiontype === 'protect' ? form.protectReason : form.reason;
		if (item.reason) {
			reasonField.value = item.reason;
		} else {
			reasonField.value = '';
		}
		// Add any annotations
		Twinkle.protect.callback.annotateProtectReason(e);

		// sort out tagging options, disabled if nonexistent or lua
		if (mw.config.get('wgArticleId') && mw.config.get('wgPageContentModel') !== 'Scribunto') {
			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 });

			// Default settings for adding <noinclude> tags to protection templates
			var isTemplateEditorProtection = form.category.value === 'حص-الگو';
			var isAFD = mw.config.get('wgNamespaceNumber') === mw.config.get('wgNamespaceIds').project && mw.config.get('wgTitle').indexOf('نظرخواهی برای حذف/') === 0;
			var isNotTemplateNamespace = mw.config.get('wgNamespaceNumber') !== 10;
			if (isTemplateEditorProtection || isAFD) {
				form.noinclude.checked = true;
			} else if (isNotTemplateNamespace) {
				form.noinclude.checked = false;
			}
		}

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

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

	var tagparams;
	if (input.actiontype === 'tag' || (input.actiontype === 'protect' && mw.config.get('wgArticleId') && mw.config.get('wgPageContentModel') !== 'Scribunto')) {
		tagparams = {
			tag: input.tagtype,
			reason: false,
			small: input.small,
			noinclude: input.noinclude
		};
	}

	switch (input.actiontype) {
		case 'protect':
			// protect the page
			Morebits.wiki.actionCompleted.redirect = mw.config.get('wgPageName');
			Morebits.wiki.actionCompleted.notice = 'محافظت کامل شد';

			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'), 'در حال محافظت صفحه');
				if (mw.config.get('wgArticleId')) {
					if (input.editmodify) {
						thispage.setEditProtection(input.editlevel, input.editexpiry);
					}
					if (input.movemodify) {
						// Ensure a level has actually been chosen
						if (input.movelevel) {
							thispage.setMoveProtection(input.movelevel, input.moveexpiry);
						} else {
							alert('باید سطحی برای حفاظت در برابر انتقال انتخاب کنید!');
							return;
						}
					}
					thispage.setWatchlist(Twinkle.getPref('watchProtectedPages'));
				} else {
					thispage.setCreateProtection(input.createlevel, input.createexpiry);
					thispage.setWatchlist(false);
				}

				if (input.protectReason) {
					thispage.setEditSummary(input.protectReason);
				} else {
					alert('باید دلیلی برای حفاظت ارائه دهید؛ این دلیل در سیاههٔ حفاظت ثبت خواهد.');
					return;
				}

				if (input.protectReason_notes_rfppRevid && !/^\d+$/.test(input.protectReason_notes_rfppRevid)) {
					alert('شناسهٔ نسخهٔ وارد شده بدشکل است. لطفاً برای آگاهی از چگونگی یافتن شناسهٔ صحیح، صفحهٔ «وپ:پیوند دائمی» را ببینید.');
					return;
				}

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

				thispage.setChangeTags(Twinkle.changeTags);
				thispage.protect(next);
			};

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

				thispage = new Morebits.wiki.page(mw.config.get('wgPageName'), 'در حال اعمال حفاظت نیازمند بازبینی');
				thispage.setFlaggedRevs(input.pclevel, input.pcexpiry);

				if (input.protectReason) {
					thispage.setEditSummary(input.protectReason + Twinkle.summaryAd); // flaggedrevs tag support: [[phab:T247721]]
				} else {
					alert('باید دلیلی برای حفاظت وارد کنید؛ این دلیل در سیاههٔ حفاظت ثبت خواهد شد.');
					return;
				}

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

				thispage.setWatchlist(Twinkle.getPref('watchProtectedPages'));
				thispage.stabilize(allDone, function(error) {
					if (error.errorCode === 'stabilize_denied') { // [[phab:T234743]]
						thispage.getStatusElement().error('تلاش برای تغییر تنظیمات حفاظت نیازمند بازبینی ناموفق بود. این احتمالاً به‌دلیل وجود مشکل در نرم‌افزار مدیاویکی است. سایر کنش‌ها (برچسب‌زدن یا حفاظت معمولی) ممکن است اعمال شده باشند. لطفاً صفحه را دوباره باگیری کرده و دوباره تلاش کنید.');
					}
				});
			};

			if (input.editmodify || input.movemodify || !mw.config.get('wgArticleId')) {
				if (input.pcmodify) {
					protectIt(stabilizeIt);
				} else {
					protectIt(allDone);
				}
			} else if (input.pcmodify) {
				stabilizeIt();
			} else {
				alert("از توینکل چه می‌خواهید؟ \nاگر می‌خواهید به صفحه برچسب بزنید، می‌توانید گزینهٔ «برچسب‌زدن صفحه با الگوی حفاظت» در بالا را انتخاب کنید.");
			}

			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 RFPP
			var typename, typereason;
			switch (input.category) {
				case 'حص-مناقشه':
				case 'حص-خرابکاری':
				case 'حص-بحث کاربر':
				case 'حص-حفاظت‌شده':
					typename = 'حفاظت کامل';
					break;
				case 'حص-الگو':
					typename = 'حفاظت الگو';
					break;
				case 'حص-۳۰-۵۰۰-هیئت':
				case 'حص-۳۰-۵۰۰-خرابکاری':
				case 'حص-۳۰-۵۰۰-اخلالگرانه':
				case 'حص-۳۰-۵۰۰-زنده':
				case 'حص-۳۰-۵۰۰-زاپاس':
					typename = 'نیمه‌حفاظت ویژه';
					break;
				case 'حص-نیمه-خرابکاری':
				case 'حص-نیمه-اخلالگرانه':
				case 'حص-نیمه-بدون منبع':
				case 'حص-نیمه-بحث کاربر':
				case 'حص-نیمه-زاپاس':
				case 'حص-نیمه-زنده':
				case 'حص-نیمه-حفاظت‌شده':
					typename = 'حفاظت نیمه';
					break;
				case 'حص-حنب-خرابکاری':
				case 'حص-حنب-زنده':
				case 'حص-حنب-حفاظت‌شده':
				case 'حص-حنب-بدون منبع':
				case 'حص-حنب-اخلالگرانه':
					typename = 'تغییرات نیازمند بازبینی';
					break;
				case 'حص-انتقال':
				case 'حص-انتقال-مناقشه':
				case 'حص-انتقال-بی‌پایان':
				case 'حص-انتقال-خرابکاری':
					typename = 'حفاظت در برابر انتقال';
					break;
				case 'حص-ایجاد-توهین‌آمیز':
				case 'حص-ایجاد-زنده':
				case 'حص-ایجاد-نمک':
					typename = 'حفاظت در برابر ایجاد';
					break;
				case 'خروج از حفاظت':
					var admins = $.map(Twinkle.protect.currentProtectionLevels, function(pl) {
						if (!pl.admin || Twinkle.protect.trustedBots.indexOf(pl.admin) !== -1) {
							return null;
						}
						return 'کاربر:' + pl.admin;
					});
					if (admins.length && !confirm('آیا پیش از هر چیز نسبت به تماس با مدیران حفاظت‌کننده (' + Morebits.array.uniq(admins).join('، ') + ') اقدام کرده‌اید؟')) {
						return false;
					}
					// otherwise falls through
				default:
					typename = 'خروج از حفاظت';
					break;
			}
			switch (input.category) {
				case 'حص-مناقشه':
					typereason = 'مناقشه بر سر محتوا/جنگ ویرایشی';
					break;
				case 'حص-خرابکاری':
				case 'حص-نیمه-خرابکاری':
				case 'حص-حنب-خرابکاری':
				case 'حص-۳۰-۵۰۰-خرابکاری':
					typereason = '[[وپ:خ|خرابکاری]] گسترده';
					break;
				case 'حص-نیمه-اخلالگرانه':
				case 'حص-حنب-اخلالگرانه':
				case 'حص-۳۰-۵۰۰-اخلالگرانه':
					typereason = '[[ویکی‌پدیا:ویرایش اخلالگرانه|ویرایش‌های اخلالگرانهٔ]] ادامه‌دار';
					break;
				case 'حص-نیمه-بدون منبع':
				case 'حص-حنب-بدون منبع':
					typereason = 'افزودن مداوم [[وپ:مقدارج|مطالب بدون منبع یا با منبع ضعیف]]';
					break;
				case 'حص-الگو':
					typereason = '[[وپ:حساس|الگوی حساس]]';
					break;
				case 'حص-۳۰-۵۰۰-هیئت':
					typereason = '[[وپ:۳۰/۵۰۰|تصمیم هیئت داوری یا هیئت نظارت]]';
					break;
				case 'حص-بحث کاربر':
				case 'حص-نیمه-بحث کاربر':
					typereason = 'استفادهٔ نادرست از صفحهٔ بحث در زمان قطع دسترسی';
					break;
				case 'حص-نیمه-زاپاس':
				case 'حص-۳۰-۵۰۰-زاپاس':
					typereason = '[[وپ:زاپاس|زاپاس‌بازی]] گسترده';
					break;
				case 'حص-نیمه-زنده':
				case 'خص-حنب-زنده':
				case 'حص-۳۰-۵۰۰-زنده':
					typereason = 'نقض [[وپ:زنده|سیاست زندگی‌نامهٔ زندگان]]';
					break;
				case 'حص-انتقال-مناقشه':
					typereason = 'مناقشه بر سر عنوان/جنگ بر سر نام صفحه';
					break;
				case 'حص-انتقال-خرابکاری':
					typereason = 'خرابکاری از طریق انتقال';
					break;
				case 'حص-انتقال-بی‌پایان':
					typereason = 'صفحهٔ پربازدید';
					break;
				case 'حص-ایجاد-توهین‌آمیز':
					typereason = 'عنوان توهین‌آمیز';
					break;
				case 'حص-ایجاد-زنده':
					typereason = '[[وپ:زنده|زندگی‌نامهٔ زندگان]] که اخیراً حذف شده';
					break;
				case 'حص-ایجاد-نمک':
					typereason = 'ایجادهای پی‌درپی';
					break;
				default:
					typereason = '';
					break;
			}

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

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

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

			var rppName = 'ویکی‌پدیا:درخواست محافظت صفحه';

			// Updating data for the action completed event
			Morebits.wiki.actionCompleted.redirect = 'ویکی‌پدیا: درخواست محافظت صفحه';
			Morebits.wiki.actionCompleted.notice = 'درخواست کامل شد، درحال تغییر مسیر به صفحهٔ بحث';

			var rppPage = new Morebits.wiki.page(rppName, 'در حال ثبت درخواست برای حفاظت صفحه');
			rppPage.setFollowRedirect(true);
			rppPage.setCallbackParameters(rppparams);
			rppPage.load(Twinkle.protect.callbacks.fileRequest);
			break;
		default:
			alert('پودمان حفاظت توینکل: نوع کنش ناشناخته');
			break;
	}
};

Twinkle.protect.protectReasonAnnotations = [];
Twinkle.protect.callback.annotateProtectReason = function twinkleprotectCallbackAnnotateProtectReason(e) {
	var form = e.target.form;
	var protectReason = form.protectReason.value.replace(new RegExp('(?:; |؛ )?' + mw.util.escapeRegExp(Twinkle.protect.protectReasonAnnotations.join(': '))), '');

	if (this.name === 'protectReason_notes_rfpp') {
		if (this.checked) {
			Twinkle.protect.protectReasonAnnotations.push(this.value);
			$(form.protectReason_notes_rfppRevid).parent().show();
		} else {
			Twinkle.protect.protectReasonAnnotations = [];
			form.protectReason_notes_rfppRevid.value = '';
			$(form.protectReason_notes_rfppRevid).parent().hide();
		}
	} else if (this.name === 'protectReason_notes_rfppRevid') {
		Twinkle.protect.protectReasonAnnotations = Twinkle.protect.protectReasonAnnotations.filter(function(el) {
			return el.indexOf('[[Special:Permalink') === -1 || el.indexOf('[[ویژه:پیوند دائمی') === -1;
		});
		if (e.target.value.length) {
			var permalink = '[[ویژه:پیوند دائمی/' + e.target.value + '#' + Morebits.pageNameNorm + ']]';
			Twinkle.protect.protectReasonAnnotations.push(permalink);
		}
	}

	if (!Twinkle.protect.protectReasonAnnotations.length) {
		form.protectReason.value = protectReason;
	} else {
		form.protectReason.value = (protectReason ? protectReason + '؛ ' : '') + Twinkle.protect.protectReasonAnnotations.join(': ');
	}
};

Twinkle.protect.callbacks = {
	taggingPageInitial: function(tagparams) {
		if (tagparams.tag === 'noop') {
			Morebits.status.info('در حال قرار دادن برچسب حفاظت در صفحه', 'کاری برای انجام نیست');
			return;
		}

		var protectedPage = new Morebits.wiki.page(mw.config.get('wgPageName'), 'در حال برچسب زدن به صفحه');
		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*(?:\*\/)?\s*/gi;
		var re_result = oldtag_re.exec(text);
		if (re_result) {
			if (params.tag === 'none' || confirm('برچسب {{' + re_result[1] + '}} در صفحه یافت شد. \nبرای حذف آن روی OK، و برای نگه‌داشتن آن روی Cancel کلیک کنید.')) {
				text = text.replace(oldtag_re, '');
			}
		}

		if (params.tag === 'none') {
			summary = 'حذف الگوی حفاظت';
		} else {
			tag = params.tag;
			if (params.reason) {
				tag += '|1=' + params.reason;
			}
			if (params.small) {
				tag += '|کوچک=بله';
			}

			if (/^\s*#(redirect|تغییر\_?مسیر)/i.test(text)) { // redirect page
				// Only tag if no {{rcat shell}} is found
				if (!text.match(/{{(?:redr|this is a redirect|r(?:edirect)?(?:.?cat.*)?[ _]?sh|پوسته رده تغییرمسیر)/i)) {
					text = text.replace(/#(REDIRECT|تغییر\_?مسیر) ?(\[\[.*?\]\])(.*)/i, '#تغییرمسیر $1$2\n\n{{' + tag + '}}');
				} else {
					Morebits.status.info('پوستهٔ ردهٔ تغییرمسیر در صفحه موجود است', 'کاری برای انجام نیست');
					return;
				}
			} else {
				var needsTagToBeCommentedOut = ['javascript', 'css', 'sanitized-css'].indexOf(protectedPage.getContentModel()) !== -1;
				if (needsTagToBeCommentedOut) {
					if (params.noinclude) {
						tag = '/* <noinclude>{{' + tag + '}}</noinclude> */';
					} else {
						tag = '/* {{' + tag + '}} */\n';
					}

					// Prepend tag at very top
					text = tag + text;
				} else {
					if (params.noinclude) {
						tag = '<noinclude>{{' + tag + '}}</noinclude>';
					} else {
						tag = '{{' + tag + '}}\n';
					}

					// Insert tag after short description or any hatnotes
					var wikipage = new Morebits.wikitext.page(text);
					text = wikipage.insertAfterTemplates(tag, Twinkle.hatnoteRegex).getText();
				}
			}
			summary = 'افزودن برچسب {{' + params.tag + '}}';
		}

		protectedPage.setEditSummary(summary);
		protectedPage.setChangeTags(Twinkle.changeTags);
		protectedPage.setWatchlist(Twinkle.getPref('watchPPTaggedPages'));
		protectedPage.setPageText(text);
		protectedPage.setCreateOption('nocreate');
		protectedPage.suppressProtectWarning(); // no need to let admins know they are editing through protection
		protectedPage.save();
	},

	fileRequest: function(rppPage) {

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

		var rppRe = new RegExp('===\\s*(\\[\\[)?\\s*:?\\s*' + Morebits.string.escapeRegExp(Morebits.pageNameNorm) + '\\s*(\\]\\])?\\s*===', 'm');
		var tag = rppRe.exec(text);

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

		if (tag) {
			statusElement.error([ 'در حال حاضر درخواست حفاظتی برای این صفحه در ', rppLink, ' موجود است، ادامه نمی‌دهیم' ]); // localized
			return;
		}

		var newtag = '=== [[:' + Morebits.pageNameNorm + ']] ===\n';
		if (new RegExp('^' + Morebits.string.escapeRegExp(newtag).replace(/\s+/g, '\\s*'), 'm').test(text)) {
			statusElement.error([ 'در حال حاضر درخواست حفاظتی برای این صفحه در ', rppLink, ' موجود است، ادامه نمی‌دهیم' ]); // localized
			return;
		}
		newtag += '* {{پیوندهای دمص|1=' + Morebits.pageNameNorm + '}}\n\n';

		var words;
		switch (params.expiry) {
			case 'temporary':
				words = ' موقت'; // localized
				break;
			case 'indefinite':
				words = ' بی‌پایان'; // localized
				break;
			default:
				words = '';
				break;
		}

		words = params.typename + words;

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

		// If either protection type results in a increased status, then post it under increase
		// else we post it under decrease
		var increase = false;
		var protInfo = Twinkle.protect.protectionPresetsInfo[params.category];

		// function to compute protection weights (see comment at Twinkle.protect.protectionWeight)
		var computeWeight = function(mainLevel, stabilizeLevel) {
			var result = Twinkle.protect.protectionWeight[mainLevel || 'all'];
			if (stabilizeLevel) {
				if (result) {
					if (stabilizeLevel.level === 'autoconfirmed') {
						result += 2;
					}
				} else {
					result = Twinkle.protect.protectionWeight['flaggedrevs_' + stabilizeLevel];
				}
			}
			return result;
		};

		// compare the page's current protection weights with the protection we are requesting
		var editWeight = computeWeight(Twinkle.protect.currentProtectionLevels.edit &&
			Twinkle.protect.currentProtectionLevels.edit.level,
		Twinkle.protect.currentProtectionLevels.stabilize &&
			Twinkle.protect.currentProtectionLevels.stabilize.level);
		if (computeWeight(protInfo.edit, protInfo.stabilize) > editWeight ||
			computeWeight(protInfo.move) > computeWeight(Twinkle.protect.currentProtectionLevels.move &&
			Twinkle.protect.currentProtectionLevels.move.level) ||
			computeWeight(protInfo.create) > computeWeight(Twinkle.protect.currentProtectionLevels.create &&
			Twinkle.protect.currentProtectionLevels.create.level)) {
			increase = true;
		}

		var reg;
		if (increase) {
			reg = /(\n==\s*درخواست‌های خروج از محافظت\s*==)/; // localized
		} else {
			reg = /(\n==\s*درخواست ویرایش صفحه‌های محافظت‌شده\s*==)/; // localized
		}

		var originalTextLength = text.length;
		text = text.replace(reg, '\n' + newtag + '\n$1');
		if (text.length === originalTextLength) {
			var linknode = document.createElement('a');
			linknode.setAttribute('href', mw.util.getUrl('ویکی‌پدیا:توینکل/تعمیر')); // localized
			linknode.appendChild(document.createTextNode('چگونگی تعمیر دمص'));
			statusElement.error([ 'عنوانی مناسب در وپ:دمص یافت نشد. برای حل این مشکل ', linknode, ' را ببینید' ]); // localized
			return;
		}
		statusElement.status('افزودن درخواست جدید...'); // localized
		rppPage.setEditSummary('/* ' + Morebits.pageNameNorm + ' */ درخواست ' + params.typename + (params.typename === 'نیازمند بازبینی' ? ' برای [[:' : ' برای [[:') + // localized
			Morebits.pageNameNorm + ']].');
		rppPage.setChangeTags(Twinkle.changeTags);
		rppPage.setPageText(text);
		rppPage.setCreateOption('recreate');
		rppPage.save(function() {
			// Watch the page being requested
			var watchPref = Twinkle.getPref('watchRequestedPages');
			// action=watch has no way to rely on user preferences (T262912), so we do it manually.
			// The watchdefault pref appears to reliably return '1' (string),
			// but that's not consistent among prefs so might as well be "correct"
			var watch = watchPref !== 'no' && (watchPref !== 'default' || !!parseInt(mw.user.options.get('watchdefault'), 10));
			if (watch) {
				var watch_query = {
					action: 'watch',
					titles: mw.config.get('wgPageName'),
					token: mw.user.tokens.get('watchToken')
				};
				// Only add the expiry if page is unwatched or already temporarily watched
				if (Twinkle.protect.watched !== true && watchPref !== 'default' && watchPref !== 'yes') {
					watch_query.expiry = watchPref;
				}
				new Morebits.wiki.api('در حال افزودن صفحهٔ موضوع درخواست به فهرست پی‌گیری‌ها', watch_query).post();
			}
		});
	}
};

Twinkle.addInitCallback(Twinkle.protect, 'protect');
})(jQuery);


// </nowiki>