مدیاویکی:Gadget-twinklearv.js

از ایران پدیا
پرش به ناوبری پرش به جستجو

نکته: پس از انتشار ممکن است برای دیدن تغییرات نیاز باشد که حافظهٔ نهانی مرورگر خود را پاک کنید.

  • فایرفاکس / سافاری: کلید Shift را نگه دارید و روی دکمهٔ Reload کلیک کنید، یا کلید‌های Ctrl-F5 یا Ctrl-R را با هم فشار دهید (در رایانه‌های اپل مکینتاش کلید‌های ⌘-R)
  • گوگل کروم: کلیدهای Ctrl+Shift+R را با هم فشار دهید (در رایانه‌های اپل مکینتاش کلید‌های ⌘-Shift-R)
  • اینترنت اکسپلورر/ Edge: کلید Ctrl را نگه‌دارید و روی دکمهٔ Refresh کلیک کنید، یا کلید‌های Ctrl-F5 را با هم فشار دهید
  • اپرا: Ctrl-F5 را بفشارید.
//<nowiki>


(function($){


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

Twinkle.arv = function twinklearv() {
	var username = mw.config.get('wgRelevantUserName');
	if ( !username ) {
		return;
	}

	var title = Morebits.isIPAddress( username ) ? 'گزارش آی‌پی به مدیران' : 'گزارش کاربر به مدیران';

	Twinkle.addPortletLink( function(){ Twinkle.arv.callback(username); }, "گزارش", "tw-arv", title );
};

Twinkle.arv.callback = function ( uid ) {
	if ( uid === mw.config.get('wgUserName') ) {
		alert( 'می‌خواهید خودتان را گزارش دهید؟' );
		return;
	}

	var Window = new Morebits.simpleWindow( 600, 500 );
	Window.setTitle( "گزارش‌دهی و بازرسی پیشرفته" ); //Backronym
	Window.setScriptName( "توینکل" );
	Window.addFooterLink( "راهنمای درخواست میانجی‌گری مدیران", ":en:WP:GAIV" );
	Window.addFooterLink( "UAA instructions", "WP:UAAI" );
	Window.addFooterLink( "About SPI", "WP:SPI" );
	Window.addFooterLink( "راهنمای توینکل", "WP:TW/DOC#arv" );

	var form = new Morebits.quickForm( Twinkle.arv.callback.evaluate );
	var categories = form.append( {
			type: 'select',
			name: 'category',
			label: 'نوع گزارش را انتخاب کنید: ',
			event: Twinkle.arv.callback.changeCategory
		} );
	categories.append( {
			type: 'option',
			label: 'خرابکاری (اپ:تام)',
			value: 'aiv'
		} );
	categories.append( {
			type: 'option',
			label: 'نام کاربری (اپ:تامنام)',
			value: 'username'
		} );
	categories.append( {
			type: 'option',
			label: 'زاپاس‌باز (اپ:دبک)',
			value: 'sock'
		} );
	categories.append( {
			type: 'option',
			label: 'زاپاس (اپ:دبک)',
			value: 'puppet'
		} );
	categories.append( {
			type: 'option',
			label: 'جنگ ویرایشی (اپ:تام۳)',
			value: 'an3'
		} );
	form.append( {
			type: 'field',
			label: 'محل کار',
			name: 'work_area'
		} );
	form.append( { type: 'submit' } );
	form.append( {
			type: 'hidden',
			name: 'uid',
			value: uid
		} );
	
	var result = form.render();
	Window.setContent( result );
	Window.display();

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

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

	switch( value ) {
	case 'aiv':
		/* falls through */
	default:
		work_area = new Morebits.quickForm.element( {
				type: 'field',
				label: 'کاربر را به خاطر خرابکاری معرفی کن',
				name: 'work_area'
			} );
		work_area.append( {
				type: 'input',
				name: 'page',
				label: 'صفحه پیوندداده‌شدهٔ اولیه: ',
				tooltip: 'برای پیوند ندادن به صفحه در گزارش خالی بگذارید',
				value: Morebits.queryString.exists( 'vanarticle' ) ? Morebits.queryString.get( 'vanarticle' ) : '',
				event: function(e) {
					var value = e.target.value;
					var root = e.target.form;
					if( value === '' ) {
						root.badid.disabled = root.goodid.disabled = true;
					} else {
						root.badid.disabled = false;
						root.goodid.disabled = root.badid.value === '';
					}
				}
			} );
		work_area.append( {
				type: 'input',
				name: 'badid',
				label: 'شناسه نسخه صفحه مورد نظر در زمان خرابکاری: ',
				tooltip: 'برای نداشتن پیوند تفاوت خالی بگذارید',
				value: Morebits.queryString.exists( 'vanarticlerevid' ) ? Morebits.queryString.get( 'vanarticlerevid' ) : '',
				disabled: !Morebits.queryString.exists( 'vanarticle' ),
				event: function(e) {
					var value = e.target.value;
					var root = e.target.form;
					root.goodid.disabled = value === '';
				}
			} );
		work_area.append( {
				type: 'input',
				name: 'goodid',
				label: 'شناسهٔ آخرین نسخهٔ صحیح صفحه مورد نظر قبل از خرابکاری: ',
				tooltip: 'برای پیوند تفاوت به بازبینی پیشین خالی بگذارید',
				value: Morebits.queryString.exists( 'vanarticlegoodrevid' ) ? Morebits.queryString.get( 'vanarticlegoodrevid' ) : '',
				disabled: !Morebits.queryString.exists( 'vanarticle' ) || Morebits.queryString.exists( 'vanarticlerevid' )
			} );
		work_area.append( {
				type: 'checkbox',
				name: 'arvtype',
				list: [
					{
						label: 'خرابکاری پس از هشدارهای نهایی (تا سطح ۴ یا ۴ نهایی)',
						value: 'final'
					},
					{
						label: 'خرابکاری در پی بازشدن (کمتر از ۱ روز)',
						value: 'postblock'
					},
					{
						label: 'حساب مشخصاً ساخته‌شده برای خرابکاری',
						value: 'vandalonly',
						disabled: Morebits.isIPAddress( root.uid.value )
					},
					{
						label: 'حساب مشخصاً یک ربات هرزنویس یا حساب از کنترل خارج‌شده است',
						value: 'spambot'
					},
					{
						label: 'حسابی که کارش تنها تبلیغ است',
						value: 'promoonly'
					}
				]
			} );
		work_area.append( {
				type: 'textarea',
				name: 'reason',
				label: 'توضیح: '
			} );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	case 'username':
		work_area = new Morebits.quickForm.element( {
				type: 'field',
				label: 'سوءاستفاده از نام کاربری را گزارش کن',
				name: 'work_area'
			} );
		work_area.append ( {
				type: 'header',
				label: 'گونه(های) نام کاربری نامناسب',
				tooltip: 'ایران‌پدیا اجازهٔ نام‌های کاربری گمراه‌کننده، تبلیغی، توهین‌آمیز یا مختل‌کننده را نمی‌دهد. نام‌های دامنه و نشانی‌های رایانامه به همین ترتیب ممنوع است. این ضوابط روی نام‌های کاربری و امضاها اعمال می‌شود. نام‌های کاربری که در زبان دیگر نامناسب هستند، یا نام نامناسبی را با غلط املایی یا جایگزینی نویسه‌ها نشان می‌دهد، یا به طور مستقیم یا ضمنی این کار را انجام می‌دهد، همچنان نامناسب در نظر گرفته می‌شود.'
			} );
		work_area.append( {
				type: 'checkbox',
				name: 'arvtype',
				list: [
					{
						label: 'نام کاربری گمراه‌کننده',
						value: 'گمراه‌کننده',
						tooltip: 'نام گمراه‌کننده نامی است که حقایق را به شکل دیگر نمایش دهد .'
					},
					{
						label: 'نام کاربری تبلیغاتی',
						value: 'تبلیغاتی',
						tooltip: 'نام تبلیغاتی نامی است که برای یک گروه یا شرکت یا وبگاه تبلیغ باشد'
					},
					{
						label: 'نام کاربری‌ای که نشان‌گر استفادهٔ مشترک است',
						value: 'مشترک',
						tooltip: 'نام‌های کاربری‌ای که به استفادهٔ مشترک بین چند کاربر اشاره دارند (مانند شرکت‌ها، گروه‌ها یا عنوان جایگاه‌های اداری) مجاز نیستند. با این وجود اگر نام کاربری شامل نام شرکت یا گروه باشد ولی به طور مشخص تنها برای مشخص کردن یک فرد به کار رود مجاز است، مانند "آرش از بنیاد فلان"، "دوستدار شرکت فلان ۲۰۱۴" و ...'
					},
					{
						label: 'نام کاربری توهین‌آمیز',
						value: 'توهین‌آمیز',
						tooltip: 'نام توهین‌آمیز نامی‌است که به گروهی از ویرایشگران اهانت باشد.'
					},
					{
						label: 'نام کاربری اخلالگرانه',
						value: 'اخلالگرانه',
						tooltip: 'نام اخلالگرانه نامی‌است که باعث چند دستگی و به هم‌ریختن فضای ایران‌پدیا گردد.'
					}
				]
			} );
		work_area.append( {
				type: 'textarea',
				name: 'reason',
				label: 'توضیح:'
			} );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;

	case 'puppet':
		work_area = new Morebits.quickForm.element( {
				type: 'field',
				label: 'گزارش زاپاس مشکوک',
				name: 'work_area'
			} );
		work_area.append(
			{
				type: 'input',
				name: 'sockmaster',
				label: 'زاپاس‌باز',
				tooltip: 'نام کاربری زاپاس (صاحب زاپاس) بدون پیشوند «کاربر:»'
			}
		);
		work_area.append( {
				type: 'textarea',
				label: 'مدرک:',
				name: 'evidence',
				tooltip: 'مدرک خود را وارد کنید. مدرک باید شفاف سازد که هر کدام از این کاربران به احتمال زیاد از چندین حساب سوءاستفاده می‌کنند. معمولاً مدرک به معنی تفاوت‌ها، تاریخچه صفحات یا دیگر اطلاعاتی است که توجیه می‌کند چرا این کاربران الف) یکسان و ب) مختل‌کننده هستند. باید به طور کامل مدرک و اطلاعات مورد نیاز برای قضاوت دربارهٔ موضوع باشد. از دیگر بحث‌ها که مدرک زاپاس‌بازی نیستند یا دیگر سوءاستفادهٔ حساب چندتایی اجتناب کنید.'
			} );
		work_area.append( {
				type: 'checkbox',
				list: [
					{
						label: 'درخواست بازرسی کاربر',
						name: 'checkuser',
						tooltip: 'بازرسی کاربر ابزاری است که برای بدست‌آوردن مدرک فنی مرتبط با اتهام زاپاس‌بازی کاربرد دارد. این ابزار بدون دلیل خوب، که شما باید به طور شفاف ثابت کنید استفاده نخواهد شد. مطمئن شوید مدرک‌تان چرای مناسب‌بودن بازرسی کاربر را شرح می‌دهد.'
					},
					{
						label: 'اطلاع‌رسانی به کاربرهای مورد شکایت',
						name: 'notify',
						tooltip: 'اطلاعیه الزامی نیست. در بیشتر موارد، به ویژه صاحبان زاپاس مزمن، اطلاعیه زیان‌بار باشد. هرچند، به ویژه در موارد کمتر نمایان شامل کاربرانی که از پیش گزارش نشده‌اند، اطلاعیه ممکن است موارد را عادلانه‌تر کند و همچنین در چشم‌های متهم عادلانه‌تر پدیدار شود. خودتان قضاوت کنید.'
					}
				]
			} );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	case 'sock':
		work_area = new Morebits.quickForm.element( {
				type: 'field',
				label: 'گزارش کاربر مشکوک به زاپاس‌بازی',
				name: 'work_area'
			} );
		work_area.append(
			{
				type: 'dyninput',
				name: 'sockpuppet',
				label: 'سوءاستفاده‌کنندگان از حساب‌های کاربری زاپاس',
				sublabel: 'زاپاس: ',
				tooltip: 'نام کاربری زاپاس بدون پیشوند «کاربر:»',
				min: 2
			} );
		work_area.append( {
				type: 'textarea',
				label: 'مدرک:',
				name: 'evidence',
				tooltip: 'مدرک خود را وارد کنید. مدرک باید شفاف سازد که هر کدام از این کاربران به احتمال زیاد از چندین حساب سوءاستفاده می‌کنند. معمولاً مدرک به معنی تفاوت‌ها، تاریخچه صفحات یا دیگر اطلاعاتی است که توجیه می‌کند چرا این کاربران الف) یکسان و ب) مختل‌کننده هستند. باید به طور کامل مدرک و اطلاعات مورد نیاز برای قضاوت دربارهٔ موضوع باشد. از دیگر بحث‌ها که مدرک زاپاس‌بازی نیستند یا دیگر سوءاستفادهٔ حساب چندتایی اجتناب کنید.'
			} );
		work_area.append( {
				type: 'checkbox',
				list: [ {
					label: 'درخواست بازرسی کاربر',
					name: 'checkuser',
					tooltip: 'بازرسی کاربر ابزاری است که برای بدست‌آوردن مدرک فنی مرتبط با اتهام زاپاس‌بازی کاربرد دارد. این ابزار بدون دلیل خوب، که شما باید به طور شفاف ثابت کنید استفاده نخواهد شد. مطمئن شوید مدرک‌تان چرای مناسب‌بودن بازرسی کاربر را شرح می‌دهد.'
				}, {
					label: 'اطلاع‌رسانی به کاربرهای مورد شکایت',
					name: 'notify',
					tooltip: 'اطلاعیه الزامی نیست. در بیشتر موارد، به ویژه صاحبان زاپاس مزمن، اطلاعیه زیان‌بار باشد. هرچند، به ویژه در موارد کمتر نمایان شامل کاربرانی که از پیش گزارش نشده‌اند، اطلاعیه ممکن است موارد را عادلانه‌تر کند و همچنین در چشم‌های متهم عادلانه‌تر پدیدار شود. خودتان قضاوت کنید.'
				} ]
			} );
		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
        break;
	case 'an3':
		work_area = new Morebits.quickForm.element( {
			type: 'field',
			label: 'گزارش جنگ ویرایشی',
			name: 'work_area'
		} );

		work_area.append( {
			type: 'input',
			name: 'page',
			label: 'صفحه',
			tooltip: 'صفحه‌ای که قرار است گزارش شود'
		} );
		work_area.append( {
			type: 'button',
			name: 'load',
			label: 'بارگیری',
			event: function(e) {
				var root = e.target.form;
				var value = root.page.value;
				var uid = root.uid.value;
				var $diffs = $(root).find('[name=diffs]');
				$diffs.find('.entry').remove();

				var date = new Date();
				date.setHours(-36); // all since 36 hours

				var api = new mw.Api();
				api.get({
					action: 'query',
					prop: 'revisions',
					format: 'json',
					rvprop: 'sha1|ids|timestamp|parsedcomment|comment',
					rvlimit: 500,
					rvend: date.toISOString(),
					rvuser: uid,
					indexpageids: true,
					redirects: true,
					titles: value
				}).done(function(data){
					var pageid = data.query.pageids[0];
					var page = data.query.pages[pageid];
					if(!page.revisions) {
						return;
					}
					for(var i = 0; i < page.revisions.length; ++i) {
						var rev = page.revisions[i];
						var $entry = $('<div/>', {
							'class': 'entry'
						});
						var $input = $('<input/>', {
							'type': 'checkbox',
							'name': 's_diffs',
							'value': rev.revid
						});
						$input.data('revinfo',rev);
						$input.appendTo($entry);
						$entry.append('<span>"'+rev.parsedcomment+'" at <a href="'+mw.config.get('wgScript')+'?diff='+rev.revid+'">'+moment(rev.timestamp).calendar()+'</a></span>').appendTo($diffs);
					}
				}).fail(function(data){
					console.log( 'API failed :(', data );
				});
				var $warnings = $(root).find('[name=warnings]');
				$warnings.find('.entry').remove();

				api.get({
					action: 'query',
					prop: 'revisions',
					format: 'json',
					rvprop: 'sha1|ids|timestamp|parsedcomment|comment',
					rvlimit: 500,
					rvend: date.toISOString(),
					rvuser: mw.config.get('wgUserName'),
					indexpageids: true,
					redirects: true,
					titles: 'User talk:' + uid
				}).done(function(data){
					var pageid = data.query.pageids[0];
					var page = data.query.pages[pageid];
					if(!page.revisions) {
						return;
					}
					for(var i = 0; i < page.revisions.length; ++i) {
						var rev = page.revisions[i];
						var $entry = $('<div/>', {
							'class': 'entry'
						});
						var $input = $('<input/>', {
							'type': 'checkbox',
							'name': 's_warnings',
							'value': rev.revid
						});
						$input.data('revinfo',rev);
						$input.appendTo($entry);
						$entry.append('<span>"'+rev.parsedcomment+'" at <a href="'+mw.config.get('wgScript')+'?diff='+rev.revid+'">'+moment(rev.timestamp).calendar()+'</a></span>').appendTo($warnings);
					}
				}).fail(function(data){
					console.log( 'API failed :(', data );
				});

				var $resolves = $(root).find('[name=resolves]');
				$resolves.find('.entry').remove();

				var t = new mw.Title(value);
				var ns = t.getNamespaceId();
				var talk_page = (new mw.Title(t.getMain(), ns%2? ns : ns+1)).getPrefixedText();

				api.get({
					action: 'query',
					prop: 'revisions',
					format: 'json',
					rvprop: 'sha1|ids|timestamp|parsedcomment|comment',
					rvlimit: 500,
					rvend: date.toISOString(),
					rvuser: mw.config.get('wgUserName'),
					indexpageids: true,
					redirects: true,
					titles: talk_page
				}).done(function(data){
					var pageid = data.query.pageids[0];
					var page = data.query.pages[pageid];
					if(!page.revisions) {
						return;
					}
					for(var i = 0; i < page.revisions.length; ++i) {
						var rev = page.revisions[i];
						var $entry = $('<div/>', {
							'class': 'entry'
						});
						var $input = $('<input/>', {
							'type': 'checkbox',
							'name': 's_resolves',
							'value': rev.revid
						});
						$input.data('revinfo',rev);
						$input.appendTo($entry);
						$entry.append('<span>"'+rev.parsedcomment+'" at <a href="'+mw.config.get('wgScript')+'?diff='+rev.revid+'">'+moment(rev.timestamp).calendar()+'</a></span>').appendTo($resolves);
					}

					// add free form input
					var $free_entry = $('<div/>', {
						'class': 'entry'
					});
					var $free_input = $('<input/>', {
						'type': 'text',
						'name': 's_resolves_free'
					});

					var $free_label = $('<label/>', {
						'for': 's_resolves_free',
						'html': 'Diff to additional discussions: '
					});
					$free_entry.append($free_label).append($free_input).appendTo($resolves);

				}).fail(function(data){
					console.log( 'API failed :(', data );
				});
			}
		} );
		work_area.append( {
			type: 'field',
			name: 'diffs',
			label: 'واگردانی‌های کاربر',
			tooltip: 'ویرایش‌هایی را انتخاب کنید که باور دارید واگردانی هستند'
		} );
		work_area.append( {
			type: 'field',
			name: 'warnings',
			label: 'هشدارهای داده‌شده به موضوع',
			tooltip: 'شما باید موضوع را پیش از گزارش هشدار دهید'
		} );
		work_area.append( {
			type: 'field',
			name: 'resolves',
			label: 'اقدامات قطعنامه',
			tooltip: 'شما باید تلاش می‌کردید تا مشکل را نخست در صفحهٔ بحث حل کنید'
		} );

		work_area.append( {
			type: 'textarea',
			label: 'توضیح:',
			name: 'comment'
		} );

		work_area = work_area.render();
		old_area.parentNode.replaceChild( work_area, old_area );
		break;
	}
};

Twinkle.arv.callback.evaluate = function(e) {
	var form = e.target;
	var reason = "";
	var comment = "";
	if ( form.reason ) {
		comment = form.reason.value;
	}
	var uid = form.uid.value;

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

		// Report user for vandalism
		case 'aiv':
			/* falls through */
		default:
			types = form.getChecked( 'arvtype' );
			if( !types.length && comment === '' ) {
				alert( 'شما باید دلیلی مشخص کنید' );
				return;
			}

			types = types.map( function(v) {
					switch(v) {
						case 'final':
							return 'خرابکاری پس از هشدار نهایی';
						case 'postblock':
							return 'خرابکاری پس از رهایی اخیر از بندایش';
						case 'spambot':
							return 'حساب آشکارا ربات جفنگ‌نگار یا حساب در معرض خطر است';
						case 'vandalonly':
							return 'اقدامات آشکارا حاکی از یک حساب تماماً خرابکار است';
						case 'promoonly':
							return 'حساب فقط برای اهداف تبلیغی استفاده می‌شود';
						default:
							return 'دلیل نامعلوم';
					}
				} ).join( '؛ ' );


			if ( form.page.value !== '' ) {
			
				// add a leading : on linked page namespace to prevent transclusion
				reason = "== گزارش خرابکاری ==\nخرابکاری توسط [[کاربر:" + uid + "|" + uid + "]] ";
				reason += 'در [[' + form.page.value.replace( /^(Image|Category|File):/i, ':$1:' ) + ']]';

				if ( form.badid.value !== '' ) {
					reason += ' ({{diff|' + form.page.value + '|' + form.badid.value + '|' + form.goodid.value + '|پیوند تفاوت}})';
				}
				reason += ':';
			}

			if ( types ) {
				reason += " " + types;
			}
			if (comment !== "" ) {
				reason += (reason === "" ? "" : ". ") + comment;
			}
			reason = reason.trim();
			if (reason.search(/[.؟!؛،]$/) ===-1) {
				reason += ".";
			}
			reason += " ~~~~";
			reason = reason.replace(/\r?\n/g, "\n*:");  // indent newlines

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

			Morebits.wiki.actionCompleted.redirect = "ایران‌پدیا:تابلوی اعلانات مدیران";
			Morebits.wiki.actionCompleted.notice = "گزارش کامل شد";

			var aivPage = new Morebits.wiki.page( 'ایران‌پدیا:تابلوی اعلانات مدیران', 'در حال پردازش درخواست تام' );
			// aivPage.setPageSection( 1 );
			aivPage.setFollowRedirect( true );
			
			aivPage.load( function() {
				var text = aivPage.getPageText();

				// check if user has already been reported
				if (new RegExp( "\\{\\{\\s*(?:(?:[Ii][Pp])?[Vv]andal|[Uu]serlinks)\\s*\\|\\s*(?:1=)?\\s*" + window.RegExp.escape( uid, true ) + "\\s*\\}\\}" ).test(text)) {
					aivPage.getStatusElement().error( 'گزارش از پیش موجود است، گزارش تازه‌ای افزوده نخواهد شد' );
					Morebits.status.printUserText( reason, 'توضیحاتی که نوشتید در زیر ارائه شده‌اند، در صورتی که می‌خواهید به طور دستی آنها را زیر گزارش موجود برای این کاربر در تام ارسال کنید:' );
					return;
				}
				aivPage.getStatusElement().status( 'در حال افزودن گزارش تازه...' );
				aivPage.setEditSummary( 'گزارش [[ویژه:مشارکت‌ها/' + uid + '|' + uid + ']].' + Twinkle.getPref('summaryAd') );
				aivPage.setPageText( text + "\n" + reason);  // add at bottom
				aivPage.save();
			} );
			break;
			
		// Report inappropriate username
		case 'username':
			types = form.getChecked( 'arvtype' ).map( Morebits.string.toLowerCaseFirstChar );
			
			var hasShared = types.indexOf( 'shared' ) > -1;
			if ( hasShared ) {
				types.splice( types.indexOf( 'shared' ), 1 );
			}

			if ( types.length <= 2 ) {
				types = types.join( ' و ' );
			} else {
				types = [ types.slice( 0, -1 ).join( '، ' ), types.slice( -1 ) ].join( ' و ' );
			}
			/* خطوط زیر به دلیل کاهش فرایند کار و عدم نیاز آن در ایران‌پدیای فارسی غیرفعال شده‌است.
			var article = 'یک';
			if ( /[aeiouwyh]/.test( types[0] || '' ) ) { // non 100% correct, but whatever, including 'h' for Cockney
				article = 'یک';
			}
			*/
			reason = "== [[کاربر:"+ uid +"]] ==\n*{{user-uaa|1=" + uid + "}}\n";
			if ( types.length || hasShared ) {
				reason += "نقض سیاست نام کاربری همچون نام کاربری " + types +
					( hasShared ? " که بر استفادهٔ مشترک اشاره می‌کند. " : ". " );
			}
			if ( comment !== '' ) {
				reason += Morebits.string.toUpperCaseFirstChar(comment) + ". ";
			}
			reason += "~~~~";
			reason = reason.replace(/\r?\n/g, "\n");  // indent newlines

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

			Morebits.wiki.actionCompleted.redirect = "ایران‌پدیا:تابلوی اعلانات مدیران/نام‌های کاربری نامناسب";
			Morebits.wiki.actionCompleted.notice = "گزارش کامل شد";

			var uaaPage = new Morebits.wiki.page( 'ایران‌پدیا:تابلوی اعلانات مدیران/نام‌های کاربری نامناسب', 'در حال پردازش درخواست تامنام' );
			uaaPage.setFollowRedirect( true );

			uaaPage.load( function() {
				var text = uaaPage.getPageText();
				
				// check if user has already been reported
				if (new RegExp( "\\{\\{\\s*user-uaa\\s*\\|\\s*(1\\s*=\\s*)?" + window.RegExp.escape(uid, true) + "\\s*(\\||\\})" ).test(text)) {
					uaaPage.getStatusElement().error( 'کاربر از پیش فهرست شده‌است.' );
					Morebits.status.printUserText( reason, 'توضیحاتی که نوشتید در زیر ارائه شده‌اند، در صورتی که می‌خواهید به طور دستی آنها را زیر گزارش موجود برای این کاربر در تامنام ارسال کنید:' );
					return;
				}
				uaaPage.getStatusElement().status( 'در حال افزودن گزارش تازه...' );
				uaaPage.setEditSummary( 'گزارش [[Special:Contributions/' + uid + '|' + uid + ']].'+ Twinkle.getPref('summaryAd') );
				uaaPage.setPageText( text + "\n" + reason);  // add at top
				uaaPage.save();
			} );
			break;
			
		// WP:SPI
		case "sock":
			/* falls through */
		case "puppet":
			var sockParameters = {
				evidence: form.evidence.value.trim(),
				checkuser: form.checkuser.checked,
				notify: form.notify.checked
			};

			var puppetReport = form.category.value === "puppet";
			if (puppetReport && !(form.sockmaster.value.trim())) {
				if (!confirm("شما حساب دارندهٔ زاپاسی برای این زاپاس وارد نکرده‌اید. آیا می‌خواهید این حساب را به عنوان یک زاپاس‌باز گزارش دهید؟")) {
					return;
				}
				puppetReport = false;
			}

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

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

		case 'an3':
			var diffs = $.map( $('input:checkbox[name=s_diffs]:checked',form), function(o){ return $(o).data('revinfo'); });

			if (diffs.length < 3 && !confirm("You have selected fewer than three offending edits. Do you wish to make the report anyway?")) {
				return;
			}

			var warnings = $.map( $('input:checkbox[name=s_warnings]:checked',form), function(o){ return $(o).data('revinfo'); });

			if(!warnings.length && !confirm("شما هیچ ویرایش جایی که به متخلف هشدار دادید را انتخاب نکرده‌اید. آیا می‌خواهید به هر صورت گزارش کنید؟")) {
				return;
			}

			var resolves = $.map( $('input:checkbox[name=s_resolves]:checked',form), function(o){ return $(o).data('revinfo'); });
			var free_resolves = $('input[name=s_resolves_free]').val();

			var an3_next = function(free_resolves) {
				if(!resolves.length && !free_resolves && !confirm("شما هیچ ویرایش جایی که تلاش کردید مشکل را حل کنید انتخاب نکردید. آیا می‌خواهید به هر صورت گزارش کنید؟")) {
					return;
				}

				var an3Parameters = {
					'uid': uid,
					'page': form.page.value.trim(),
					'comment': form.comment.value.trim(),
					'diffs': diffs,
					'warnings': warnings,
					'resolves': resolves,
					'free_resolves': free_resolves
				};

				Morebits.simpleWindow.setButtonsEnabled( false );
				Morebits.status.init( form );
				Twinkle.arv.processAN3( an3Parameters );
			};

			if(free_resolves) {
				var oldid=mw.util.getParamValue('oldid',free_resolves);
				var api = new mw.Api();
				api.get({
					action: 'query',
					prop: 'revisions',
					format: 'json',
					rvprop: 'ids|timestamp|comment',
					indexpageids: true,
					revids: oldid
				}).done(function(data){
					var pageid = data.query.pageids[0];
					var page = data.query.pages[pageid];
					an3_next(page);
				}).fail(function(data){
					console.log( 'API failed :(', data );
				});
			} else {
				an3_next();
			}
			break;
	}
};

Twinkle.arv.processSock = function( params ) {
	Morebits.wiki.addCheckpoint(); // prevent notification events from causing an erronous "action completed"
	
	// notify all user accounts if requested
	if (params.notify && params.sockpuppets.length>0) {
	
		var notifyEditSummary = "در حال اطلاع‌دادن دربارهٔ بدگمانی زاپاس‌بازی." + Twinkle.getPref('summaryAd');
		var notifyText = "\n\n{{subst:socksuspectnotice|1=" + params.uid + "}} ~~~~";
		
		// notify user's master account
		var masterTalkPage = new Morebits.wiki.page( 'بحث کاربر:' + params.uid, 'آگاه‌ساختن زاپاس‌باز مظنون' );
		masterTalkPage.setFollowRedirect( true );
		masterTalkPage.setEditSummary( notifyEditSummary );
		masterTalkPage.setAppendText( notifyText );
		masterTalkPage.append();

		var statusIndicator = new Morebits.status( 'آگاه‌ساختن زاپاس‌های مظنون', '0%' );
		var total = params.sockpuppets.length;
		var current =   0;
		
		// display status of notifications as they progress
		var onSuccess = function( sockTalkPage ) {
			var now = parseInt( 100 * ++(current)/total, 10 ) + '%';
			statusIndicator.update( now );
			sockTalkPage.getStatusElement().unlink();
			if ( current >= total ) {
				statusIndicator.info( now + ' (کامل شد)' );
			}
		};
		
		var socks = params.sockpuppets;

		// notify each puppet account
		for( var i = 0; i < socks.length; ++i ) {
			var sockTalkPage = new Morebits.wiki.page( 'بحث کاربر:' + socks[i], "آگاه‌ساختن " +  socks[i] );
			sockTalkPage.setFollowRedirect( true );
			sockTalkPage.setEditSummary( notifyEditSummary );
			sockTalkPage.setAppendText( notifyText );
			sockTalkPage.append( onSuccess );
		}
	}

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

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

	Morebits.wiki.actionCompleted.redirect = reportpage;
	Morebits.wiki.actionCompleted.notice = "گزارش کامل شد";

	var spiPage = new Morebits.wiki.page( reportpage, 'بازیابی صفحهٔ گفتگو' );
	spiPage.setFollowRedirect( true );
	spiPage.setEditSummary( 'افزودن گزارش تازه برای [[Special:Contributions/' + params.uid + '|' + params.uid + ']].'+ Twinkle.getPref('summaryAd') );
	spiPage.setAppendText( text );
	switch( Twinkle.getPref( 'spiWatchReport' ) ) {
		case 'yes':
			spiPage.setWatchlist( true );
			break;
		case 'no':
			spiPage.setWatchlistFromPreferences( false );
			break;
		default:
			spiPage.setWatchlistFromPreferences( true );
			break;
	}
	spiPage.append();
	
	Morebits.wiki.removeCheckpoint();  // all page updates have been started
};

Twinkle.arv.processAN3 = function( params ) {
	// prepare the AN3 report
	var minid;
	for(var i = 0; i < params.diffs.length; ++i) {
		if( params.diffs[i].parentid && (!minid || params.diffs[i].parentid < minid)) {
			minid = params.diffs[i].parentid;
		}
	}

	var api = new mw.Api();
	api.get({
		action: 'query',
		prop: 'revisions',
		format: 'json',
		rvprop: 'sha1|ids|timestamp|comment',
		rvlimit: 100,
		rvstartid: minid,
		rvexcludeuser: params.uid,
		indexpageids: true,
		redirects: true,
		titles: params.page
	}).done(function(data){
		Morebits.wiki.addCheckpoint(); // prevent notification events from causing an erronous "action completed"
		var orig;
		if(data.length) {
			var sha1 = data[0].sha1;
			for(var i = 1; i < data.length; ++i) {
				if(data[i].sha1 == sha1) {
					orig = data[i];
					break;
				}
			}

			if(!orig) {
				orig = data[0];
			}
		}

		var origtext = "";
		if(orig) {
			origtext = '{{diff2|' + orig.revid + '|' + orig.timestamp + '}} "' + orig.comment + '"';
		}

		var grouped_diffs = {};

		var parentid, lastid;
		for(var j = 0; j < params.diffs.length; ++j) {
			var cur = params.diffs[j];
			if( cur.revid && cur.revid != parentid || lastid === null ) {
				lastid = cur.revid;
				grouped_diffs[lastid] = [];
			}
			parentid = cur.parentid;
			grouped_diffs[lastid].push(cur);
		}

		var difftext = $.map(grouped_diffs, function(sub, index){
			var ret = "";
			if(sub.length >= 2) {
				var last = sub[0];
				var first = sub.slice(-1)[0];
				var label = "ویرایش‌های متوالی صورت‌گرفته از " + moment(first.timestamp).utc().format('HH:mm, D MMMM YYYY [(UTC)]') + " تا " + moment(last.timestamp).utc().format('HH:mm, D MMMM YYYY [(UTC)]');
				ret = "# {{diff|oldid="+first.parentid+"|diff="+last.revid+"|label="+label+"}}\n";
			}
			ret += sub.reverse().map(function(v){
				return (sub.length >= 2 ? '#' : '') + '# {{diff2|' + v.revid + '|' + moment(v.timestamp).utc().format('HH:mm, D MMMM YYYY [(UTC)]') + '}} "' + v.comment + '"';
			}).join("\n");
			return ret;
		}).reverse().join("\n");
		var warningtext = params.warnings.reverse().map(function(v){
			return '# ' + ' {{diff2|' + v.revid + '|' + moment(v.timestamp).utc().format('HH:mm, D MMMM YYYY [(UTC)]') + '}} "' + v.comment + '"';
		}).join("\n");
		var resolvetext = params.resolves.reverse().map(function(v){
			return '# ' + ' {{diff2|' + v.revid + '|' + moment(v.timestamp).utc().format('HH:mm, D MMMM YYYY [(UTC)]') + '}} "' + v.comment + '"';
		}).join("\n");

		if(params.free_resolves) {
			var page = params.free_resolves;
			var rev = page.revisions[0];
			resolvetext += "\n# " + ' {{diff2|' + rev.revid + '|' + moment(rev.timestamp).utc().format('HH:mm, D MMMM YYYY [(UTC)]') + ' در ' + page.title +  '}} "' + rev.comment + '"';
		}

		var comment = params.comment.replace(/~*$/g, '').trim();

		if(comment) {
			comment += " ~~~~";
		}

		var text = "\n\n"+'{{subst:AN3 report|diffs='+difftext+'|warnings='+warningtext+'|resolves='+resolvetext+'|pagename='+params.page+'|orig='+origtext+'|comment='+comment+'|uid='+params.uid+'}}';

		var reportpage = 'ایران‌پدیا:تابلوی اعلانات مدیران/نقض ۳ برگردان';

		Morebits.wiki.actionCompleted.redirect = reportpage;
		Morebits.wiki.actionCompleted.notice = "گزارش کامل شد";

		var an3Page = new Morebits.wiki.page( reportpage, 'بازیابی صفحه گفتگو' );
		an3Page.setFollowRedirect( true );
		an3Page.setEditSummary( 'افزودن گزارش تازه برای [[Special:Contributions/' + params.uid + '|' + params.uid + ']].'+ Twinkle.getPref('summaryAd') );
		an3Page.setAppendText( text );
		an3Page.append();

		// notify user

		var notifyEditSummary = "Notifying about edit warring noticeboard discussion." + Twinkle.getPref('summaryAd');
		var notifyText = "\n\n{{subst:an3-notice|1=" + mw.util.wikiUrlencode(params.uid) + "|auto=1}} ~~~~";

		var talkPage = new Morebits.wiki.page( 'بحث کاربر:' + params.uid, 'آگاه‌ساختن از جنگ ویرایشی' );
		talkPage.setFollowRedirect( true );
		talkPage.setEditSummary( notifyEditSummary );
		talkPage.setAppendText( notifyText );
		talkPage.append();
		Morebits.wiki.removeCheckpoint();  // all page updates have been started
	}).fail(function(data){
		console.log( 'API failed :(', data );
	});
};
})(jQuery);


//</nowiki>