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

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

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

// <nowiki>


(function($) {


/*
 ****************************************
 *** twinkleconfig.js: Preferences module
 ****************************************
 * Mode of invocation:     Adds configuration form to Wikipedia:Twinkle/Preferences,
                           and adds an 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.

 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.watchlistEnums = {
	'yes': 'به فهرست پی‌گیر‌ی‌ها اضافه کن (بی‌پایان)',
	'no': "به فهرست پی‌گیری‌ها اضافه نکن",
	'default': 'از ترجیحات وبگاه تبعیت کن',
	'1 week': 'پی‌گیری برای ۱ هفته',
	'1 month': 'پی‌گیری برای ۱ ماه',
	'3 months': 'پی‌گیری برای ۳ ماه',
	'6 months': 'پی‌گیری برای ۶ ماه'
};

Twinkle.config.commonSets = {
	csdCriteria: {
		"حذف سریع": "دلیل دلخواه ({{حذف سریع}})",
		"ع۱": "ع۱", "ع۲": "ع۲", "ع۳": "ع۳", "ع۴": "ع۴", "ع۵": "ع۵", "ع۶": "ع۶", "ع۷": "ع۷", "ع۸": "ع۸", "ع۱۰": "ع۱۰", "ع۱۱": "ع۱۱", "ع۱۲": "ع۱۲", "ع۱۳": "ع۱۳", "ع۱۴": "ع۱۴",
		"م۱": "م۱", "م۲": "م۲", "م۳": "م۳", "م۵": "م۵", "م۷": "م۷", "م۹": "م۹", "م۱۰": "م۱۰", "م۱۱": "م۱۱",
		"ک۱": "ک۱", "ک۲": "ک۲", "ک۳": "ک۳", "ک۵": "ک۵",
		"پ۱": "پ۱", "پ۲": "پ۲", "پ۳": "پ۳", "پ۷": "پ۷", "پ۸": "پ۸", "پ۹": "پ۹", "پ۱۰": "پ۱۰",
		"ر۱": "ر۱",
		"ا۲": "ا۲", "ا۳": "ا۳",
		"ت۲": "ت۲", "ت۳": "ت۳", "ت۴": "ت۴",
		"د۱": "د۱", "د۲": "د۲"
	},
	csdCriteriaDisplayOrder: [
		'حذف سریع',
		"ع۱", "ع۲", "ع۳", "ع۴", "ع۵", "ع۶", "ع۷", "ع۸", "ع۱۰", "ع۱۱", "ع۱۲", "ع۱۳", "ع۱۴",
		"م۱", "م۲", "م۳", "م۵", "م۷", "م۹", "م۱۰", "م۱۱",
		"ک۱", "ک۲", "ک۳", "ک۵",
		"پ۱", "پ۲", "پ۳", "پ۷", "پ۸", "پ۹", "پ۱۰",
		"ر۱",
		"ا۲", "ا۳",
		"ت۲", "ت۳", "ت۴",
		"د۱", "د۲"
	],
	csdCriteriaNotification: {
		"حذف سریع": "دلیل دلخواه ({{حذف سریع}})",
		"ع۱": "ع۱", "ع۲": "ع۲", "ع۳": "ع۳", "ع۴": "ع۴", "ع۵": "ع۵", "ع۶": "ع۶", "ع۷": "ع۷", "ع۸": "ع۸", "ع۱۰": "ع۱۰", "ع۱۱": "ع۱۱", "ع۱۲": "ع۱۲", "ع۱۳": "ع۱۳", "ع۱۴": "ع۱۴",
		"م۱": "م۱", "م۲": "م۲", "م۳": "م۳", "م۵": "م۵", "م۷": "م۷", "م۹": "م۹", "م۱۰": "م۱۰", "م۱۱": "م۱۱",
		"ک۱": "ک۱", "ک۲": "ک۲", "ک۳": "ک۳", "ک۵": "ک۵",
		"پ۱": "پ۱", "پ۲": "پ۲", "پ۳": "پ۳", "پ۷": "پ۷", "پ۸": "پ۸", "پ۹": "پ۹", "پ۱۰": "پ۱۰",
		"ر۱": "ر۱",
		"ا۲": "ا۲", "ا۳": "ا۳",
		"ت۲": "ت۲", "ت۳": "ت۳", "ت۴": "ت۴",
		"د۱": "د۱", "د۲": "د۲"
	},
	csdCriteriaNotificationDisplayOrder: [
		'حذف سریع',
		"ع۱", "ع۲", "ع۳", "ع۴", "ع۵", "ع۶", "ع۷", "ع۸", "ع۱۰", "ع۱۱", "ع۱۲", "ع۱۳", "ع۱۴",
		"م۱", "م۲", "م۳", "م۵", "م۷", "م۹", "م۱۰", "م۱۱",
		"ک۱", "ک۲", "ک۳", "ک۵",
		"پ۱", "پ۲", "پ۳", "پ۷", "پ۸", "پ۹", "پ۱۰",
		"ر۱",
		"ا۲", "ا۳",
		"ت۲", "ت۳", "ت۴",
		"د۱", "د۲"
	],
	csdAndDICriteria: {
		"حذف سریع": "دلیل دلخواه ({{حذف سریع}})",
		"ع۱": "ع۱", "ع۲": "ع۲", "ع۳": "ع۳", "ع۴": "ع۴", "ع۵": "ع۵", "ع۶": "ع۶", "ع۷": "ع۷", "ع۸": "ع۸", "ع۱۰": "ع۱۰", "ع۱۱": "ع۱۱", "ع۱۲": "ع۱۲", "ع۱۳": "ع۱۳", "ع۱۴": "ع۱۴",
		"م۱": "م۱", "م۲": "م۲", "م۳": "م۳", "م۵": "م۵", "م۷": "م۷", "م۹": "م۹", "م۱۰": "م۱۰", "م۱۱": "م۱۱",
		"ک۱": "ک۱", "ک۲": "ک۲", "ک۳": "ک۳", "ک۵": "ک۵",
		"پ۱": "پ۱", "پ۲": "پ۲", "پ۳": "پ۳", "پ۷": "پ۷", "پ۸": "پ۸", "پ۹": "پ۹", "پ۱۰": "پ۱۰",
		"ر۱": "ر۱",
		"ا۲": "ا۲", "ا۳": "ا۳",
		"ت۲": "ت۲", "ت۳": "ت۳", "ت۴": "ت۴",
		"د۱": "د۱", "د۲": "د۲"
	},
	csdAndDICriteriaDisplayOrder: [
		'حذف سریع',
		"ع۱", "ع۲", "ع۳", "ع۴", "ع۵", "ع۶", "ع۷", "ع۸", "ع۱۰", "ع۱۱", "ع۱۲", "ع۱۳", "ع۱۴",
		"م۱", "م۲", "م۳", "م۵", "م۷", "م۹", "م۱۰", "م۱۱",
		"ک۱", "ک۲", "ک۳", "ک۵",
		"پ۱", "پ۲", "پ۳", "پ۷", "پ۸", "پ۹", "پ۱۰",
		"ر۱",
		"ا۲", "ا۳",
		"ت۲", "ت۳", "ت۴",
		"د۱", "د۲"
	],
		namespacesNoSpecial: {
		'0': 'مقاله', // localized
		'1': 'بحث (مقاله)', // localized
		'2': 'کاربر', // localized
		'3': 'بحث کاربر', // localized
		'4': 'ویکی‌پدیا', // localized
		'5': 'بحث ویکی‌پدیا', // localized
		'6': 'پرونده', // localized
		'7': 'بحث پرونده', // localized
		'8': 'مدیاویکی', // localized
		'9': 'بحث مدیاویکی', // localized
		'10': 'الگو', // localized
		'11': 'بحث الگو', // localized
		'12': 'راهنما', // localized
		'13': 'بحث راهنما', // localized
		'14': 'رده', // localized
		'15': 'بحث رده', // localized
		'100': 'درگاه', // localized
		'101': 'بحث درگاه', // localized
		'108': 'کتاب', // localized
		'109': 'بحث کتاب', // localized
		'118': 'پیش‌نویس', // localized
		'119': 'بحث پیش‌نویس', // localized
		// '710': 'TimedText', // localized
		// '711': 'TimedText talk', // localized
		'828': 'پودمان', // localized
		'829': 'بحث پودمان' // localized
	}
};

/**
 * Section entry format:
 *
 * {
 *   title: <human-readable section title>,
 *   module: <name of the associated module, used to link to sections>,
 *   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>,
 *   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: 'عمومی',
		module: 'general',
		preferences: [
			// 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: { window: 'یک پنجره، به‌جای بحث کاربران', tab: 'یک زبانهٔ جدید', blank: 'یک پنجرهٔ کاملاً جدید' }
			},

			// TwinkleConfig.dialogLargeFont (boolean)
			{
				name: 'dialogLargeFont',
				label: 'استفاده از متن بزرگتر در پیام‌های توینکل',
				type: 'boolean'
			},

			// Twinkle.config.disabledModules (array)
			{
				name: 'disabledModules',
				label: 'خاموش‌کردن پودمان‌های انتخاب‌شدهٔ توینکل',
				helptip: 'هر آنچه در اینجا انتخاب کنید، برای استفاده در دسترس نخواهد بود، پس با دقت عمل کنید. برای فعال‌کردن دوباره، گزینه‌ها را غیرفعال کنید.',
				type: 'set',
				setValues: { /*arv: 'ARV',*/ warn: 'هشدار', welcome: 'خوش آمدید', shared: 'نشانی آی‌پی مشترک', talkback: 'بازبحث', speedy: 'حذف سریع', prod: 'حذف زمان‌دار', xfd: 'نظرخواهی حذف', image: 'تصویر', protect: 'محافظت (دمص)', tag: 'برچسب', diff: 'تفاوت', unlink: 'پیوندزدایی', fluff: 'بازگردانی و واگردانی' }
			},

			// Twinkle.config.disabledSysopModules (array)
			{
				name: 'disabledSysopModules',
				label: 'خاموش کردن پودمان‌های ویژهٔ مدیران',
				helptip: 'هر آنچه در اینجا انتخاب کنید، برای استفاده در دسترس نخواهد بود، پس با دقت عمل کنید. برای فعال‌کردن دوباره، گزینه‌ها را غیرفعال کنید.',
				adminOnly: true,
				type: 'set',
				setValues: { block: 'قطع دسترسی', deprod: 'مخالفت با حذف زمان‌دار (مخزمان)', batchdelete: 'حذف دسته‌جمعی', batchprotect: 'محافظت دسته‌جمعی', batchundelete: 'احیای دسته‌جمعی' }
			}
		]
	},

	{
		title: 'گبپ',
		preferences: [
			{
				name: 'spiWatchReport',
				label: 'افزودن صفحه‌های گزارش زاپاس‌بازی به فهرست پیگیری', // localized
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			}
		]
	},
	
	{
		title: 'بستن کاربر',
		module: 'block',
		adminOnly: true,
		preferences: [
			// TwinkleConfig.defaultToBlock64 (boolean)
			// Whether to default to just blocking the /64 on or off
			{
				name: 'defaultToBlock64',
				label: 'برای نشانی‌های آی‌پی نسخه ۶ (IPv6)، جهت قطع دسترسی بازهٔ /۶۴ به‌طور پیش‌فرض، این گزینه را فعال کنید',
				type: 'boolean'
			},

			// TwinkleConfig.defaultToPartialBlocks (boolean)
			// Whether to default partial blocks on or off
			{
				name: 'defaultToPartialBlocks',
				label: 'به صورت پیش‌فرض در زمان بازکردن منوی بستن، قطع دسترسی موردی انتخاب شود',
				helptip: 'اگر کاربر از پیش بسته شده‌باشد، مقدار این تنظیم به‌نفع قرار گرفتن تنظیم پیش‌فرض روی نوع قطع دسترسی کنونی، نادیده گرفته خواهد شد',
				type: 'boolean'
			},

			// TwinkleConfig.blankTalkpageOnIndefBlock (boolean)
			// if true, blank the talk page when issuing an indef block notice (per [[WP:UWUL#Indefinitely blocked users]])
			{
				name: 'blankTalkpageOnIndefBlock',
				label: 'صفحه بحث را در زمان قطع دسترسی بی‌پایان پاک کن',
				helptip: 'برای اطلاعات بیشتر، <a href="' + mw.util.getUrl(':en:Wikipedia:WikiProject_User_warnings/Usage_and_layout#Indefinitely_blocked_users') + '">WP:UWUL</a> را ببینید.',
				type: 'boolean'
			}
		]
	},

	{
		title: 'حذف تصویر',
		module: 'image',
		preferences: [
			// TwinkleConfig.notifyUserOnDeli (boolean)
			// If the user should be notified after placing a file deletion tag
			{
				name: 'notifyUserOnDeli',
				label: 'گزینهٔ «اطلاع‌رسانی به بارگذار پرونده» به صورت پیش‌فرض فعال باشد',
				type: 'boolean'
			},

			// TwinkleConfig.deliWatchPage (string)
			// The watchlist setting of the page tagged for deletion.
			{
				name: 'deliWatchPage',
				label: 'تصویر در زمان برچسب‌زدن به فهرست پی‌گیری‌ها اضافه شود',
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			},

			// TwinkleConfig.deliWatchUser (string)
			// The watchlist setting of the user talk page if a notification is placed.
			{
				name: 'deliWatchUser',
				label: 'صفحهٔ بحث بارگذار پرونده در زمان اطلاع‌رسانی، به فهرست پی‌گیری‌ها اضافه شود',
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			}
		]
	},

	{
		title: 'محافظت صفحه ' + (Morebits.userIsSysop ? '(سحص)' : '(دمص)'),
		module: 'protect',
		preferences: [
			{
				name: 'watchRequestedPages',
				label: 'در زمان درخواست برای محافظت، صفحه به فهرست پی‌گیری‌ها افزوده شود',
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			},
			{
				name: 'watchPPTaggedPages',
				label: 'در زمان برچسب‌زدن صفحه با الگوی محافظت، صفحه به فهرست پی‌گیری‌ها افزوده شود',
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			},
			{
				name: 'watchProtectedPages',
				label: 'در زمان محافظت کردن، صفحه به فهرست پی‌گیری‌ها افزوده شود',
				helptip: 'اگر پس از محافظت صفحه را برچسب نیز می‌زنید، تنظیم مرتبط با برچسب‌زدن ارجحیت خواهد داشت.',
				adminOnly: true,
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			}
		]
	},

	{
		title: 'حذف زمان‌دار (حز)',
		module: 'prod',
		preferences: [
			// TwinkleConfig.watchProdPages (string)
			// Watchlist setting when applying prod template to page
			{
				name: 'watchProdPages',
				label: 'مقاله در زمان برچسب‌زدن به فهرست پیگیری‌ها افزوده شود',
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			},

			// TwinkleConfig.markProdPagesAsPatrolled (boolean)
			// If, when applying prod template to page, to mark the page as curated/patrolled (if the page was reached from NewPages)
			{
				name: 'markProdPagesAsPatrolled',
				label: 'علامت زدن صفحه به‌عنوان گشت‌خورده/بازبینی‌شده در زمان برچسب زدن (در صورت امکان)',
				helptip: 'از آنجا که این عمل بر خلاف روند مورد پذیرش اجتماع است، بهتر است که این گزینه فعال نشود',
				type: 'boolean'
			},

			// TwinkleConfig.prodReasonDefault (string)
			// The prefilled PROD reason.
			{
				name: 'prodReasonDefault',
				label: 'دلیل پیش‌فرض حذف زمان‌دار',
				type: 'string'
			},

			{
				name: 'logProdPages',
				label: 'نگهداری سیاهه‌ای از همهٔ صفحه‌هایی که نامزد حذف زمان‌دار کرده‌اید در زیرصفحه کاربری',
				helptip: 'از آنجا که کاربران غیرمدیر به مشارکت‌های حذف‌شدهٔ خود دسترسی ندارند، سیاههٔ نگهداری‌شده در زیرصفحهٔ کاربری می‌تواند برای پیگیری تمام صفحه‌هایی که با استفاده از توینکل نامزد حذف زمان‌دار کرده‌اید، مفید باشد.',
				type: 'boolean'
			},
			{
				name: 'prodLogPageName',
				label: 'نگهداری سیاههٔ حذف زمان‌دار در این زیرصفحهٔ کاربری',
				helptip: 'نام یک زیرصفحه را در این جعبه وارد کنید. می‌توانید در صفحهٔ کاربر:<i>نام کاربری</i>/<i>نام زیرصفحه</i> به سیاههٔ حذف زمان‌دار خود دسترسی داشته باشید. این قابلیت تنها در صورتی کار می‌کند که گزینهٔ سیاههٔ حذف زمان‌دار در زیرصفحه کاربری را فعال کرده باشید.',
				type: 'string'
			}
		]
	},

	{
		title: 'برگرداندن و واگردانی',  // twinklefluff module
		module: 'fluff',
		preferences: [
			// TwinkleConfig.autoMenuAfterRollback (bool)
			// Option to automatically open the warning menu if the user talk page is opened post-reversion
			{
				name: 'autoMenuAfterRollback',
				label: 'باز کردن خودکار منوی هشدار توینکل در صفحهٔ بحث کاربر پس از بازگردانی با توینکل',
				helptip: 'تنها در صورتی کار می‌کند که گزینهٔ مرتبط در پایین را فعال کرده باشید.',
				type: 'boolean'
			},

			// TwinkleConfig.openTalkPage (array)
			// What types of actions that should result in opening of talk page
			{
				name: 'openTalkPage',
				label: 'باز کردن بحث کاربر پس از انجام این گونه‌های بازبینی',
				type: 'set',
				setValues: { agf: 'برگردانی با فرض حسن نیت', norm: 'برگردانی عادی', vand: 'برگردانی خرابکاری' }
			},

			// TwinkleConfig.openTalkPageOnAutoRevert (bool)
			// Defines if talk page should be opened when calling revert from contribs or recent changes pages. If set to true, openTalkPage defines then if talk page will be opened.
			{
				name: 'openTalkPageOnAutoRevert',
				label: 'باز کردن بحث کاربر به‌هنگام فراخواندن برگردانی از طریق مشارکت‌های کاربر یا تغییرات اخیر',
				helptip: 'برای اینکه این قابلیت کار کند، در صورت فعال بودن این گزینه باید گزینه‌های مورد نظر در تنظیمات قبلی نیز فعال شده باشند.',
				type: 'boolean'
			},

			// TwinkleConfig.rollbackInPlace (bool)
			//
			{
				name: 'rollbackInPlace',
				label: "جلوگیری از بارگیری مجدد صفحه در زمان برگردانی از طریق مشارکت‌های کاربر یا تغییرات اخیر",
				helptip: "در صورت فعال بودن این گزینه، توینکل صفحهٔ مشارکت‌های کاربر یا تغییرات اخیر را پس از برگردانی مجدداً بارگیری نخواهد کرد. این به شما این امکان را می‌دهد که همزمان بیش از یک ویرایش را برگردانی کنید.",
				type: 'boolean'
			},

			// TwinkleConfig.markRevertedPagesAsMinor (array)
			// What types of actions that should result in marking edit as minor
			{
				name: 'markRevertedPagesAsMinor',
				label: 'علامت‌زدن این گونه‌های بازبینی به‌عنوان ویرایش جزئی',
				type: 'set',
				setValues: { agf: 'برگردانی با فرض حسن نیت', norm: 'برگردانی عادی', vand: 'برگردانی خرابکاری', torev: '«برگرداندن این نسخه»' }
			},

			// TwinkleConfig.watchRevertedPages (array)
			// What types of actions that should result in forced addition to watchlist
			{
				name: 'watchRevertedPages',
				label: 'برای این انواع واگردانی، صفحه به فهرست پی‌گیری‌ها افزوده شود',
				type: 'set',
				setValues: { agf: 'برگردانی با فرض حسن نیت', norm: 'برگردانی عادی', vand: 'برگردانی خرابکاری', torev: '«برگرداندن این نسخه»' } // localized
			},
			// TwinkleConfig.watchRevertedExpiry
			// If any of the above items are selected, whether to expire the watch
			{
				name: 'watchRevertedExpiry',
				label: 'مدت‌زمان پی‌گیری صفحه در صورت استفاده از برگردانی در آن صفحه',
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			},

			// TwinkleConfig.offerReasonOnNormalRevert (boolean)
			// If to offer a prompt for extra summary reason for normal reverts, default to true
			{
				name: 'offerReasonOnNormalRevert',
				label: 'درخواست دلیل برای برگردانی‌های عادی',
				helptip: 'برگردانی‌های «عادی» آن‌هایی هستند که از طریق پیوند [برگردانی] میانی فراخوانده شده‌اند.',
				type: 'boolean'
			},

			{
				name: 'confirmOnFluff',
				label: 'قبل از بازگرداندن پیغام تایید نمایش داده شود (تمام دستگاه‌ها)',
				helptip: 'برای کاربرانی که از دستگاه‌های لمسی یا قلمی استفاده می‌کنند، و افرادی که دچار تردید مزمن هستند.',
				type: 'boolean'
			},

			{
				name: 'confirmOnMobileFluff',
				label: 'درخواست تأیید پیش از برگردانی (تنها دستگاه‌های موبایل)',
				helptip: 'جلوگیری از برگردانی‌های تصادفی در زمان استفاده از دستگاه‌های تلفن همراه.',
				type: 'boolean'
			},

			// TwinkleConfig.showRollbackLinks (array)
			// Where Twinkle should show rollback links:
			// diff, others, mine, contribs, history, recent
			// Note from TTO: |contribs| seems to be equal to |others| + |mine|, i.e. redundant, so I left it out heres
			{
				name: 'showRollbackLinks',
				label: 'نمایش پیوندهای برگردانی در این صفحه‌ها',
				type: 'set',
				setValues: { diff: 'صفحه‌های تفاوت', others: 'صفحه‌های مشارکت‌های کاربران دیگر', mine: 'صفحهٔ مشارکت‌های من', recent: 'تغییرات اخیر و صفحه‌های ویژهٔ تغییرات مرتبط', history: 'صفحه‌های تاریخچه' }
			}
		]
	},

	{
		title: 'برچسب آی‌پی مشترک',
		module: 'shared',
		preferences: [
			{
				name: 'markSharedIPAsMinor',
				label: 'علامت‌گذاری برچسب‌زدن آی‌پی مشترک به‌عنوان ویرایش جزئی',
				type: 'boolean'
			}
		]
	},

	{
		title: 'حذف سریع (محس)',
		module: 'speedy',
		preferences: [
			{
				name: 'speedySelectionStyle',
				label: 'چه زمانی صفحه حذف شود یا برچسب زده شود؟',
				type: 'enum',
				enumValues: { buttonClick: 'وقتی روی دکمهٔ «ثبت» کلیک کردم', radioClick: 'به محض کلیک کردن روی یکی از گزینه‌ها' }
			},

			// TwinkleConfig.watchSpeedyPages (array)
			// Whether to add speedy tagged or deleted pages to watchlist
			{
				name: 'watchSpeedyPages',
				label: 'برای این معیارها، صفحه به فهرست پی‌گیری‌ها افزوده شود',
				type: 'set',
				setValues: Twinkle.config.commonSets.csdCriteria,
				setDisplayOrder: Twinkle.config.commonSets.csdCriteriaDisplayOrder
			},
			// TwinkleConfig.watchSpeedyExpiry
			// If any of the above items are selected, whether to expire the watch
			{
				name: 'watchSpeedyExpiry',
				label: 'مدت‌زمان پی‌گیری صفحه در زمان برچسب‌زدن',
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			},

			// TwinkleConfig.markSpeedyPagesAsPatrolled (boolean)
			// If, when applying speedy template to page, to mark the page as triaged/patrolled (if the page was reached from NewPages)
			{
				name: 'markSpeedyPagesAsPatrolled',
				label: 'در زمان نصب برچسب، صفحه را در صورت امکان گشت بزن یا بازبینی کن',
				helptip: 'این گزینه احتمالاً نباید استفاده شود چون برای آن اجماع وجود ندارد',
				type: 'boolean'
			},

			// TwinkleConfig.watchSpeedyUser (string)
			// The watchlist setting of the user talk page if they receive a notification.
			{
				name: 'watchSpeedyUser',
				label: 'صفحهٔ بحث نخستین مشارکت‌کننده (در زمان آگاه‌سازی) به فهرست پی‌گیری‌ها افزوده شود',
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			},

			// TwinkleConfig.welcomeUserOnSpeedyDeletionNotification (array of strings)
			// On what types of speedy deletion notifications shall the user be welcomed
			// with a "firstarticle" notice if their talk page has not yet been created.
			{
				name: 'welcomeUserOnSpeedyDeletionNotification',
				label: 'در زمان آگاه‌سازی ایجادکنندهٔ صفحه با این معیارها، به او خوش‌آمد هم گفته شود',
				helptip: 'پیام خوشامد فقط وقتی افزوده می‌شود که صفحهٔ بحث کاربر وجود نداشته باشد. الگوی {{نخستین نوشتار}} برای این منظور به کار خواهد رفت.',
				type: 'set',
				setValues: Twinkle.config.commonSets.csdCriteriaNotification,
				setDisplayOrder: Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder
			},

			// TwinkleConfig.notifyUserOnSpeedyDeletionNomination (array)
			// What types of actions should result in the author of the page being notified of nomination
			{
				name: 'notifyUserOnSpeedyDeletionNomination',
				label: 'برای این معیارها، سازندهٔ صفحه را آگاه کن',
				helptip: 'حتی وقتی گزینهٔ آگاه‌سازی را از صفحهٔ حذف سریع انتخاب کنید، اعلان تنها برای معیارهای منتخب در اینجا در صفحهٔ بحث کاربر قرار داده خواهد شد.',
				type: 'set',
				setValues: Twinkle.config.commonSets.csdCriteriaNotification,
				setDisplayOrder: Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder
			},

			// TwinkleConfig.warnUserOnSpeedyDelete (array)
			// What types of actions should result in the author of the page being notified of speedy deletion (admin only)
			{
				name: 'warnUserOnSpeedyDelete',
				label: 'آگاه‌سازی سازندهٔ صفحه در زمان حذف با این معیارها',
				helptip: 'حتی وقتی گزینهٔ آگاه‌سازی را از صفحهٔ حذف سریع انتخاب کنید، اعلان تنها برای معیارهای منتخب در اینجا در صفحهٔ بحث کاربر قرار داده خواهد شد.',
				adminOnly: true,
				type: 'set',
				setValues: Twinkle.config.commonSets.csdCriteriaNotification,
				setDisplayOrder: Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder
			},

			// TwinkleConfig.promptForSpeedyDeletionSummary (array of strings)
			{
				name: 'promptForSpeedyDeletionSummary',
				label: 'فراهم کردن امکان ویرایش خلاصهٔ حذف در صورت حذف با این معیارها',
				adminOnly: true,
				type: 'set',
				setValues: Twinkle.config.commonSets.csdAndDICriteria,
				setDisplayOrder: Twinkle.config.commonSets.csdAndDICriteriaDisplayOrder
			},

			// 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'
			},

			{
				name: 'deleteRedirectsOnDelete',
				label: 'انتخاب گزینهٔ «تغییرمسیرها نیز حذف شوند» به‌طور پیش‌فرض',
				adminOnly: true,
				type: 'boolean'
			},

			// TwinkleConfig.deleteSysopDefaultToDelete (boolean)
			// Make the CSD screen default to "delete" instead of "tag" (admin only)
			{
				name: 'deleteSysopDefaultToDelete',
				label: 'فعال‌سازی پیش‌فرض حذف بی‌درنگ به‌جای قرار دادن برچسب حذف سریع',
				helptip: 'در صورت از پیش موجود بودن برچسب حذف سریع، حالت پیش‌فرض توینکل همیشه حالت «حذف» خواهد بود',
				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: 'نام یک زیرصفحه را در این جعبه وارد کنید. می‌توانید در صفحهٔ کاربر:<i>نام کربری</i>/<i>نام زیرصفحه</i> به سیاههٔ حذف سریع خود دسترسی داشته باشید. این قابلیت تنها در صورتی کار می‌کند که گزینهٔ سیاههٔ حذف سریع در زیرصفحه کاربری را فعال کرده باشید.',
				type: 'string'
			},
			{
				name: 'noLogOnSpeedyNomination',
				label: 'عدم افزودن صفحه به سیاههٔ حذف در فضای کاربری در زمان استفاده از این معیارها',
				type: 'set',
				setValues: Twinkle.config.commonSets.csdAndDICriteria,
				setDisplayOrder: Twinkle.config.commonSets.csdAndDICriteriaDisplayOrder
			}
		]
	},

	{
		title: 'برچسب',
		module: 'tag',
		preferences: [
			{
				name: 'watchTaggedVenues',
				label: 'در زمان برچسب‌زدن به این نوع از صفحه‌ها، صفحه به فهرست پی‌گیری‌ها افزوده شود',
				type: 'set',
				setValues: { articles: 'مقاله‌ها', drafts: 'پیش‌نویس‌ها', redirects: 'تغییر مسیرها', files: 'پرونده‌ها' }
			},
			{
				name: 'watchTaggedPages',
				label: 'در زمان افزودن برچسب، صفحه به فهرست پی‌گیری‌ها افزوده شود',
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			},
			{
				name: 'watchMergeDiscussions',
				label: 'در زمان آغاز نظرخواهی برای ادغام، صفحه به فهرست پی‌گیری‌ها افزوده شود',
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			},
			{
				name: 'markTaggedPagesAsMinor',
				label: 'علامت‌زدن ویرایش‌ها برای افزودن برچسب به‌عنوان ویرایش جزئی',
				type: 'boolean'
			},
			{
				name: 'markTaggedPagesAsPatrolled',
				label: 'انتخاب گزینهٔ «علامت‌گذاری صفحه به‌عنوان گشت‌خورده/بازبینی‌شده» به‌صورت پیش‌فرض',
				type: 'boolean'
			},
			{
				name: 'groupByDefault',
				label: 'انتخاب گزینهٔ «گروه‌بندی در {{مشکلات متعدد}}» به‌صورت پیش‌فرض',
				type: 'boolean'
			},
			{
				name: 'tagArticleSortOrder',
				label: 'حالت پیش‌فرض برای مرتب‌سازی برچسب‌های مقاله‌ها',
				type: 'enum',
				enumValues: { cat: 'بر پایهٔ رده', alpha: 'به ترتیب الفبا' }
			},
			{
				name: 'customTagList',
				label: 'برچسب‌های نگهداری سفارشی مقاله/پیش‌نویس جهت نمایش',
				helptip: "این برچسب‌ها به‌عنوان گزینه‌های بیشتر در پایین فهرست برچسب‌ها نمایش می‌یابند. به‌عنوان مثال، می‌توانید برچسب‌های نگهداری اضافه‌ای که هنوز به برچسب‌های پیش‌فرض توینکل اضافه نشده‌اند را بیافزایید.",
				type: 'customList',
				customListValueTitle: 'نام الگو (بدون آکولاد)',
				customListLabelTitle: 'متنی برای نمایش در کادر برچسب'
			},
			{
				name: 'customFileTagList',
				label: 'برچسب‌های سفارشی نگهداری پرونده جهت نمایش',
				helptip: 'برچسب‌های بیشتری که می‌خواهید برای پرونده‌ها بیافزایید.',
				type: 'customList',
				customListValueTitle: 'نام الگو (بدون آکولاد)',
				customListLabelTitle: 'متنی برای نمایش در کادر برچسب'
			},
			{
				name: 'customRedirectTagList',
				label: 'برچسب‌های سفارشی ردهٔ تغییرمسیر جهت نمایش',
				helptip: 'برچسب‌های بیشتری که می‌خواهید برای تغییرمسیرها بیافزایید.',
				type: 'customList',
				customListValueTitle: 'نام الگو (بدون آکولاد)',
				customListLabelTitle: 'متنی برای نمایش در کادر برچسب'
			}
		]
	},

	{
		title: 'بازبحث',
		module: 'talkback',
		preferences: [
			{
				name: 'markTalkbackAsMinor',
				label: 'علامت‌گذاری بازبحث‌ها به‌عنوان ویرایش جزئی',
				type: 'boolean'
			},
			{
				name: 'insertTalkbackSignature',
				label: 'قرار دادن امضا در درون بازبحث‌ها',
				type: 'boolean'
			},
			{
				name: 'talkbackHeading',
				label: 'عنوان بخش برای استفاده در بازبحث‌ها و پیام‌های «لطفاً ببینید»',
				tooltip: 'نباید حاوی علامت مساوی («==»)، که برای قالب‌بندی ویکی‌متن استفاده می‌شود، باشد',
				type: 'string'
			},
			{
				name: 'mailHeading',
				label: "عنوان بخش برای استفاده در اعلان‌های «رایانامه دارید»",
				tooltip: 'نباید حاوی علامت مساوی («==»)، که برای قالب‌بندی ویکی‌متن استفاده می‌شود، باشد',
				type: 'string'
			}
		]
	},

	{
		title: 'پیوندزدایی',
		module: 'unlink',
		preferences: [
			// TwinkleConfig.unlinkNamespaces (array)
			// In what namespaces unlink should happen, default in 0 (article), 10 (template), 100 (portal), and 118 (draft)
			{
				name: 'unlinkNamespaces',
				label: 'پیوندهایی که از این فضاهای نام هستند زدوده شوند',
				helptip: 'از انتخاب فضاهای بحث پرهیز کنید، چون توینکل ممکن است پیوند را از بایگانی‌های بحث نیز حذف کند؛ کاری که به‌شدت نکوهیده است.',
				type: 'set',
				setValues: Twinkle.config.commonSets.namespacesNoSpecial
			}
		]
	},

	{
		title: 'هشدار به کاربر',
		module: 'warn',
		preferences: [
			// TwinkleConfig.defaultWarningGroup (int)
			// Which level warning should be the default selected group, default is 1
			{
				name: 'defaultWarningGroup',
				label: 'سطح پیش‌فرض هشدارها',
				type: 'enum',
				enumValues: {
					1: 'سطح ۱',
					2: 'سطح ۲',
					3: 'سطح ۳',
					4: 'سطح ۴',
					5: 'سطح ۵ (تنها هشدار)',
					6: 'اعلان راجع به یک مشکل واحد',
					7: 'هشدار راجع به یک مشکل واحد',
					// 8 was used for block templates before #260
					9: 'هشدار سفارشی',
					10: 'تمام الگوهای هشدار',
					11: 'انتخاب خودکار (سطح ۱-۴)'
				}
			},

			// TwinkleConfig.combinedSingletMenus (boolean)
			// if true, show one menu with both single-issue notices and warnings instead of two separately
			{
				name: 'combinedSingletMenus',
				label: 'جایگزینی دو منوی جداگانه برای اشکال‌های واحد با یک منوی ترکیب‌شده',
				helptip: 'انتخاب اعلان‌های تک‌اشکال یا هشدارهای تک‌اشکال به‌عنوان گزینهٔ پیش‌فرض شما، در صورت فعال بودن، این را به انتخاب پیش‌فرض شما تبدیل خواهد کرد.',
				type: 'boolean'
			},

			// TwinkleConfig.showSharedIPNotice may take arguments:
			// true: to show shared ip notice if an IP address
			// false: to not print the notice
			{
				name: 'showSharedIPNotice',
				label: 'افزودن اعلان اضافی در صفحه‌های بحث آی‌پی مشترک',
				helptip: 'اعلان مورد استفاده، {{آگاهی آی‌پی مشترک}} است',
				type: 'boolean'
			},

			// TwinkleConfig.watchWarnings (string)
			// Watchlist setting for the page which has been dispatched an warning or notice
			{
				name: 'watchWarnings',
				label: 'در زمان آگاه‌سازی، صفحهٔ بحث کاربر به فهرست پی‌گیری‌ها افزوده شود',
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			},

			// TwinkleConfig.oldSelect (boolean)
			// if true, use the native select menu rather the select2-based one
			{
				name: 'oldSelect',
				label: 'استفاده از منوی انتخاب کلاسیک غیر قابل جستجو',
				type: 'boolean'
			},

			{
				name: 'customWarningList',
				label: 'الگوهای هشدار سفارشی برای نمایش',
				helptip: 'می‌توانید الگوهای شخصی یا زیرصفحه‌های کاربری را بیافزایید. هشدارهای سفارشی در ردهٔ «هشدارهای سفارشی» در داخل کادر جعبهٔ هشدار نمایش می‌یابند.',
				type: 'customList',
				customListValueTitle: 'نام الگو (بدون آکولاد)', // localized
				customListLabelTitle: 'متنی برای نمایش در فهرست هشدار (برای خلاصه ویرایش نیز استفاده می‌شود)' // localized
			}
		]
	},

	{
		title: 'خوشامدگویی',
		module: 'welcome',
		preferences: [
			{
				name: 'topWelcomes',
				label: 'قرار دادن خوشامدها در بالای محتوای موجود در صفحه‌های بحث کاربران',
				type: 'boolean'
			},
			{
				name: 'watchWelcomes',
				label: 'در زمان خوشامدگویی، صفحهٔ بحث کاربر به فهرست پی‌گیری‌ها افزوده شود',
				helptip: 'انجام این کار، تجربهٔ شخصی شما از خوشامدگویی به یک کاربر را بهبود می‌بخشد - شما قادر خواهید بود تا واکنش کاربر به‌عنوان یک تازه‌کار را ببینید و احتمالاً به او کمک کنید.',
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			},
			{
				name: 'insertUsername',
				label: 'افزودن نام کاربری شما به الگو (در صورت امکان)',
				helptip: "برخی الگوهای خوشامد دارای جملهٔ اولیه‌ای مانند «سلام، من &lt;نام کاربری&gt; هستم. خوش آمدید» یا مشابه آن هستند. اگر این گزینه را غیرفعال کنید، این الگوها نام کاربری شما را به‌این نحو نمایش نخواهند داد.",
				type: 'boolean'
			},
			{
				name: 'quickWelcomeMode',
				label: 'عملکرد پیوند «خوشامد» در صفحهٔ تفاوت',
				helptip: 'اگر حالت خوشامدگویی خودکار را انتخاب کنید، الگویی که در زیر مشخص کرده‌اید مورد استفاده قرار خواهد گرفت.',
				type: 'enum',
				enumValues: { auto: 'خوشامدگویی خودکار', norm: 'الزام برای انتخاب یک الگو' }
			},
			{
				name: 'quickWelcomeTemplate',
				label: 'الگوی مورد استفاده در زمان خوشامدگویی خودکار',
				helptip: 'نام یک الگوی خوشامد را بدون استفاده از آکولاد وارد کنید. پیوندی به مقالهٔ مشخص‌شده افزوده خواهد شد.',
				type: 'string'
			},
			{
				name: 'customWelcomeList',
				label: 'الگوهای خوشامد سفارشی برای نمایش',
				helptip: "می‌توانید الگوهای خوشامد یا زیرصفحه‌های کاربری که الگوی خوشامدگویی هستند را اضافه کنید (همراه با پیشوند «کاربر:»). فراموش نکنید که این الگوها در صفحه‌های بحث کاربران جانشانی خواهند شد.",
				type: 'customList',
				customListValueTitle: 'نام الگو (بدون آکولاد)', // localized
				customListLabelTitle: 'متنی برای نمایش در کادر خوشامد' // localized
			},
			{
				name: 'customWelcomeSignature',
				label: 'امضای خودکار الگوهای خوشامد سفارشی',
				helptip: 'در صورتی که الگوهای خوشامد سفارشی شما شامل امضای یکپارچه در داخل الگو هستند، این گزینه را غیرفعال کنید.',
				type: 'boolean'
			}
		]
	},

	{
		title: 'نبح (نظرخواهی برای حذف)',
		module: 'xfd',
		preferences: [
			{
				name: 'logXfdNominations',
				label: 'نگهداری یک سیاهه از نامزدی‌ها در نظرخواهی برای حذف (نبح) توسط شما در زیرصفحهٔ کاربری',
				helptip: 'سیاههٔ نگهداری‌شده در زیرصفحهٔ کاربری می‌تواند برای پیگیری تمام صفحه‌هایی که با استفاده از توینکل نامزد نظرخواهی برای حذف کرده‌اید، مفید باشد.',
				type: 'boolean'
			},
			{
				name: 'xfdLogPageName',
				label: 'نگهداری سیاههٔ نظرخواهی برای حذف در این زیرصفحهٔ کاربری',
				helptip: 'نام یک زیرصفحه را در این جعبه وارد کنید. می‌توانید در صفحهٔ کاربر:<i>نام کربری</i>/<i>نام زیرصفحه</i> به سیاههٔ نظرخواهی برای حذف خود دسترسی داشته باشید. این قابلیت تنها در صورتی کار می‌کند که گزینهٔ سیاههٔ نظرخواهی برای حذف در زیرصفحه کاربری را فعال کرده باشید.',
				type: 'string'
			},
			{
				name: 'noLogOnXfdNomination',
				label: 'برای نامزدی‌های حذف در این دسته، سیاهه‌ای در زیرصفحهٔ کاربری‌ام ایجاد نکن',
				type: 'set',
				setValues: { afd: 'نبح' /*, tfd: 'TfD', ffd: 'FfD', cfd: 'CfD', cfds: 'CfD/S', mfd: 'MfD', rfd: 'RfD', rm: 'RM' */ }
			},

			// TwinkleConfig.xfdWatchPage (string)
			// The watchlist setting of the page being nominated for XfD.
			{
				name: 'xfdWatchPage',
				label: 'صفحهٔ نامزد حذف به فهرست پی‌گیری‌ها اضافه شود',
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			},

			// 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.
			{
				name: 'xfdWatchDiscussion',
				label: 'صفحهٔ نظرخواهی به فهرست پی‌گیری‌ها اضافه شود',
				helptip: 'این گزینه مربوط به صفحه‌ای است که در آن بحث راجع به حذف صورت می‌گیرد، یعنی صفحهٔ نظرخواهی',
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			},

			// TwinkleConfig.xfdWatchList (string)
			// The watchlist setting of the XfD list page, *if* the discussion is on a separate page.
			/* 
			{
				name: 'xfdWatchList',
				label: 'افزودن صفحهٔ سیاهه/فهرست روزانه به فهرست پیگیری (در صورت امکان)', // localized
				helptip: 'این گزینه تنها در مورد وپ:نبح اعمال خواهد شد که بحث‌های آن در یک سیاههٔ روزانه یا صفحهٔ اصلی نبح تراگنجایش می‌شوند.', // localized
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			},
			*/ // localized [removed]

			// TwinkleConfig.xfdWatchUser (string)
			// The watchlist setting of the user talk page if they receive a notification.
			{
				name: 'xfdWatchUser',
				label: 'صفحهٔ بحث کاربری نخستین مشارکت‌کننده به فهرست پی‌گیری‌ها اضافه شود (در زمان آگاه‌سازی)',
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			},

			// TwinkleConfig.xfdWatchRelated (string)
			// The watchlist setting of the target of a redirect being nominated for RfD.
			/*
			{
				name: 'xfdWatchRelated',
				label: "صفحهٔ هدف تغییر مسیر به فهرست پی‌گیری‌ها افزوده شود (در زمان آگاه‌سازی)",
				helptip: 'این فقط برای زمانی کاربرد دارد که شما در صفحه بحث هدف تغییرمسیر اطلاع‌رسانی کرده‌اید',
				type: 'enum',
				enumValues: Twinkle.config.watchlistEnums
			},
			*/
			// localized [removed]
			
			{
				name: 'markXfdPagesAsPatrolled',
				label: 'در صورت امکان صفحه را در زمان ایجاد نظرخواهی برای حذف گشت بزن',
				type: 'boolean'
			}
		]
	},

	{
		title: 'پنهان',
		hidden: true,
		preferences: [
			// twinkle.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'
			},
			// twinklewarn.js: When using the autolevel select option, how many days makes a prior warning stale
			// Huggle is three days ([[Special:Diff/918980316]] and [[Special:Diff/919417999]]) while ClueBotNG is two:
			// https://github.com/DamianZaremba/cluebotng/blob/4958e25d6874cba01c75f11debd2e511fd5a2ce5/bot/action_functions.php#L62
			{
				name: 'autolevelStaleDays',
				type: 'integer'
			},
			// How many pages should be queried by deprod and batchdelete/protect/undelete
			{
				name: 'batchMax',
				type: 'integer',
				adminOnly: true
			},
			// How many pages should be processed at a time by deprod and batchdelete/protect/undelete
			{
				name: 'batchChunks',
				type: 'integer',
				adminOnly: true
			}
		]
	}

]; // end of Twinkle.config.sections


Twinkle.config.init = function twinkleconfigInit() {

	// create the config page at Wikipedia:Twinkle/Preferences
	if ((mw.config.get('wgNamespaceNumber') === mw.config.get('wgNamespaceIds').project && mw.config.get('wgTitle') === 'توینکل/ترجیحات') &&
			mw.config.get('wgAction') === 'view') {

		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 skin js/common.js file
		// (settings in that file will still work, but they will be overwritten by twinkleoptions.js settings)
		if (window.TwinkleConfig || window.FriendlyConfig) {
			var contentnotice = document.createElement('p');
			contentnotice.innerHTML = '<table class="plainlinks ombox ombox-content"><tr><td class="mbox-image">' +
				'<img alt="" src="https://upload.wikimedia.org/wikipedia/commons/3/38/Imbox_content.png" /></td>' +
				'<td class="mbox-text"><p><big><b>پیش از تغییر تنظیمات خود در این صفحه،</b> باید تنظیمات قدیمی توینکل خود را از جاوااسکریپت شخصی پوستهٔ خود حذف کنید.</big></p>' +
				'<p>برای انجام این کار می‌توانید <a href="' + mw.util.getUrl('کاربر:' + mw.config.get('wgUserName') + '/' + mw.config.get('skin') +
				'.js', { action: 'edit' }) + '" target="_blank"><b>پروندهٔ جاوااسکریپت شخصی پوستهٔ خود</b></a> یا <a href="' +
				mw.util.getUrl('کاربر:' + mw.config.get('wgUserName') + '/common.js', { action: 'edit'}) + '" target="_blank"><b>پروندهٔ common.js خود</b></a> را ویرایش کنید و تمام کدهایی که به <code>TwinkleConfig</code> و <code>FriendlyConfig</code> اشاره دارند را حذف کنید.</p>' +
				'</td></tr></table>';
			contentdiv.appendChild(contentnotice);
		}

		// start a table of contents
		var toctable = document.createElement('div');
		toctable.className = 'toc';
		toctable.style.marginRight = '0.4em';
		// 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);
		toctable.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);
		toctable.appendChild(tocul);
		contentdiv.appendChild(toctable);

		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.userIsSysop || Morebits.userIsEliminator))) {
				return true;  // i.e. "continue" in this context
			}

			// add to TOC
			var tocli = document.createElement('li');
			tocli.className = 'toclevel-1';
			var toca = document.createElement('a');
			toca.setAttribute('href', '#' + section.module);
			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 = section.module;
			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.userIsSysop || Morebits.userIsEliminator)) {
					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, gotPref = Twinkle.getPref(pref.name);
				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 (gotPref === 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 = 'left';
						cell.style.paddingLeft = '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.paddingLeft = '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 (gotPref) {
							input.setAttribute('value', gotPref);
						}
						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 = 'left';
						cell.style.paddingLeft = '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.paddingLeft = '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 ((gotPref === enumvalue) ||
								// Hack to convert old boolean watchlist prefs
								// to corresponding enums (added in v2.1)
								(typeof gotPref === 'boolean' &&
								((gotPref && enumvalue === 'yes') ||
								(!gotPref && enumvalue === 'no')))) {
								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.paddingRight = '1em';
						var worker = function(itemkey, itemvalue) {
							var checklabel = document.createElement('label');
							checklabel.style.marginLeft = '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 (gotPref && gotPref.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 (gotPref && gotPref.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 = 'left';
						cell.style.paddingLeft = '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.paddingLeft = '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: gotPref,
							pref: pref
						});
						button.appendChild(document.createTextNode('ویرایش آیتم‌ها'));
						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) {
					// convert mentions of templates in the helptip to clickable links
					cell.innerHTML = pref.helptip.replace(/{{(.+?)}}/g,
						'{{<a href="' + mw.util.getUrl('الگو:') + '$1" target="_blank">$1</a>}}');
				}
				// 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);
					resetlink.style.cssFloat = 'left';
					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.marginRight = '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('بازنشانی پیش‌فرض‌ها'));
		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 (window.location.hash) {
			var loc = window.location.hash;
			window.location.hash = '';
			window.location.hash = loc;
		}

	} else if (mw.config.get('wgNamespaceNumber') === mw.config.get('wgNamespaceIds').user &&
			mw.config.get('wgTitle').indexOf(mw.config.get('wgUserName')) === 0 &&
			mw.config.get('wgPageName').slice(-3) === '.js') {

		var box = document.createElement('div');
		// Styled in twinkle.css
		box.setAttribute('id', 'twinkle-config-headerbox');

		var link,
			scriptPageName = mw.config.get('wgPageName').slice(mw.config.get('wgPageName').lastIndexOf('/') + 1,
				mw.config.get('wgPageName').lastIndexOf('.js'));

		if (scriptPageName === 'twinkleoptions') {
			// place "why not try the preference panel" notice
			box.setAttribute('class', 'config-twopt-box');

			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.getUrl(mw.config.get('wgFormattedNamespaces')[mw.config.get('wgNamespaceIds').project] + ':توینکل/ترجیحات'));
			link.appendChild(document.createTextNode('پنل ترجیحات توینکل'));
			box.appendChild(link);
			box.appendChild(document.createTextNode('، یا با ویرایش این صفحه تغییرات مورد نظر خود را اعمال کنید.'));
			$(box).insertAfter($('#contentSub'));

		} else if (['monobook', 'vector', 'cologneblue', 'modern', 'timeless', 'minerva', 'common'].indexOf(scriptPageName) !== -1) {
			// place "Looking for Twinkle options?" notice
			box.setAttribute('class', 'config-userskin-box');

			box.appendChild(document.createTextNode('اگر می‌خواهید ترجیحات توینکل را تنظیم کنید، می‌توانید از '));
			link = document.createElement('a');
			link.setAttribute('href', mw.util.getUrl(mw.config.get('wgFormattedNamespaces')[mw.config.get('wgNamespaceIds').project] + ':توینکل/ترجیحات'));
			link.appendChild(document.createTextNode('پنل ترجیحات توینکل'));
			box.appendChild(link);
			box.appendChild(document.createTextNode(' استفاده کنید.'));
			$(box).insertAfter($('#contentSub'));
		}
	}
};

// custom list-related stuff

Twinkle.config.listDialog = {};

Twinkle.config.listDialog.addRow = function twinkleconfigListDialogAddRow($dlgtable, value, label) {
	var $contenttr, $valueInput, $labelInput;

	$dlgtable.append(
		$contenttr = $('<tr>').append(
			$('<td>').append(
				$('<button>')
					.attr('type', 'button')
					.on('click', function () {
						$contenttr.remove();
					})
					.text('حذف')
			),
			$('<td>').append(
				$valueInput = $('<input>')
					.attr('type', 'text')
					.addClass('twinkle-config-customlist-value')
					.css('width', '97%')
			),
			$('<td>').append(
				$labelInput = $('<input>')
					.attr('type', 'text')
					.addClass('twinkle-config-customlist-label')
					.css('width', '98%')
			)
		)
	);

	if (value) {
		$valueInput.val(value);
	}
	if (label) {
		$labelInput.val(label);
	}

};

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('ترجیحات توینکل');

	var $dlgtbody;

	dialog.setContent(
		$('<div>').append(
			$('<table>')
				.addClass('wikitable')
				.css({
					margin: '1.4em 1em',
					width: 'auto'
				})
				.append(
					$dlgtbody = $('<tbody>').append(
						// header row
						$('<tr>').append(
							$('<th>') // top-left cell
								.css('width', '5%'),
							$('<th>') // value column header
								.css('width', '35%')
								.text(curpref.customListValueTitle ? curpref.customListValueTitle : 'مقدار'),
							$('<th>') // label column header
								.css('width', '60%')
								.text(curpref.customListLabelTitle ? curpref.customListLabelTitle : 'برچسب')
						)
					),
					$('<tfoot>').append(
						$('<tr>').append(
							$('<td>')
								.attr('colspan', '3')
								.append(
									$('<button>')
										.text('افزودن')
										.css('min-width', '8em')
										.attr('type', 'button')
										.on('click', function () {
											Twinkle.config.listDialog.addRow($dlgtbody);
										})
								)
						)
					)
				),
			$('<button>')
				.text('ذخیرهٔ تغییرات')
				.attr('type', 'submit') // so Morebits.simpleWindow puts the button in the button pane
				.on('click', function () {
					Twinkle.config.listDialog.save($prefbutton, $dlgtbody);
					dialog.close();
				}),
			$('<button>')
				.text('بازنشانی')
				.attr('type', 'submit')
				.on('click', function () {
					Twinkle.config.listDialog.reset($prefbutton, $dlgtbody);
				}),
			$('<button>')
				.text('لغو')
				.attr('type', 'submit')
				.on('click', function () {
					dialog.close();
				})
		)[0]
	);

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

	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 curpref = $button.data('pref');
	var oldvalue = $button.data('value');
	Twinkle.config.resetPref(curpref);

	// reset form
	$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.userIsSysop || Morebits.userIsEliminator))) {
			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);
			foundit = true;
			return false;  // break
		});

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

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

		case 'boolean':
			document.getElementById(pref.name).checked = Twinkle.defaultConfig[pref.name];
			break;

		case 'string':
		case 'integer':
		case 'enum':
			document.getElementById(pref.name).value = Twinkle.defaultConfig[pref.name];
			break;

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

		case 'customList':
			$(document.getElementById(pref.name)).data('value', Twinkle.defaultConfig[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.userIsSysop || Morebits.userIsEliminator))) {
			return true;  // continue: skip impossibilities
		}
		$(section.preferences).each(function(prefkey, pref) {
			if (!pref.adminOnly || Morebits.userIsSysop || Morebits.userIsEliminator) {
				Twinkle.config.resetPref(pref);
			}
		});
		return true;
	});
	return false;  // stop link from scrolling page
};

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

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

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

	// this is the object which gets serialized into JSON; only
	// preferences that this script knows about are kept
	var newConfig = {optionsVersion: 2.1};

	// 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 (Array.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;
		}
		return a === b;

	};

	$(Twinkle.config.sections).each(function(sectionkey, section) {
		if (section.adminOnly && !(Morebits.userIsSysop || Morebits.userIsEliminator)) {
			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 (!pref.adminOnly || Morebits.userIsSysop || Morebits.userIsEliminator) {
				if (!section.hidden) {
					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('ذخیره‌سازی', 'مقداری که برای ' + pref.name + ' وارد کرده‌اید (' + pref.value + ') نامعتبر است.  ذخیره‌سازی ادامه خواهد یافت، اما از ذخیره‌سازی مقدار دادهٔ نامعتبر چشم‌پوشی خواهد شد.');
								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) {
									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;
					}
				} else if (Twinkle.prefs) {
					// Retain the hidden preferences that may have customised by the user from twinkleoptions.js
					// undefined if not set
					userValue = Twinkle.prefs[pref.name];
				}
			}

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

	var text =
		'// twinkleoptions.js: personal Twinkle preferences file\n' +
		'//\n' +
		'// NOTE: The easiest way to change your Twinkle preferences is by using the\n' +
		'// Twinkle preferences panel, at [[' + Morebits.pageNameNorm + ']].\n' +
		'//\n' +
		'// This file is AUTOMATICALLY GENERATED.  Any changes you make (aside from\n' +
		'// changing the configuration parameters in a valid-JavaScript way) will be\n' +
		'// overwritten the next time you click "save" in the Twinkle preferences\n' +
		'// panel.  If modifying this file, make sure to use correct JavaScript.\n' +
		'// <no' + 'wiki>\n' +
		'\n' +
		'window.Twinkle.prefs = ';
	text += JSON.stringify(newConfig, null, 2);
	text +=
		';\n' +
		'\n' +
		'// </no' + 'wiki>\n' +
		'// End of twinkleoptions.js\n';

	pageobj.setPageText(text);
	pageobj.setEditSummary('ذخیره‌سازی ترجیحات توینکل: ویرایش خودکار از مبدأ [[:' + Morebits.pageNameNorm + ']]');
	pageobj.setChangeTags(Twinkle.changeTags);
	pageobj.setCreateOption('recreate');
	pageobj.save(Twinkle.config.saveSuccess);
};

Twinkle.config.saveSuccess = function twinkleconfigSaveSuccess(pageobj) {
	pageobj.getStatusElement().info('موفق');

	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.getUrl('وپ:میانگیر') + '" title="وپ:میانگیر">وپ:میانگیر</a> را ببینید).</p>';
	Morebits.status.root.appendChild(noticebox);
	var noticeclear = document.createElement('br');
	noticeclear.style.clear = 'both';
	Morebits.status.root.appendChild(noticeclear);
};

Twinkle.addInitCallback(Twinkle.config.init);
})(jQuery);


// </nowiki>