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

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

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

  • فایرفاکس / سافاری: کلید Shift را نگه دارید و روی دکمهٔ Reload کلیک کنید، یا کلید‌های Ctrl-F5 یا Ctrl-R را با هم فشار دهید (در رایانه‌های اپل مکینتاش کلید‌های ⌘-R)
  • گوگل کروم: کلیدهای Ctrl+Shift+R را با هم فشار دهید (در رایانه‌های اپل مکینتاش کلید‌های ⌘-Shift-R)
  • اینترنت اکسپلورر/ Edge: کلید Ctrl را نگه‌دارید و روی دکمهٔ Refresh کلیک کنید، یا کلید‌های Ctrl-F5 را با هم فشار دهید
  • اپرا: Ctrl-F5 را بفشارید.
$(function () {
	'use strict';

	var projectPageAllowedCats = [
		'رهنمودهای ویکی‌پدیا',
		'سیاست‌های ویکی‌پدیا',
		'راهنمای ویکی‌پدیا',
		'انشاهای ویکی‌پدیا',
		'ابزارهای ویکی‌پدیا',
		'خودآموز ویکی‌پدیا'
	];

	var projectPageAllowedTitles = [
		'ویکی‌پدیا:اشتباه‌یاب/تست',
		'ویکی‌پدیا:اشتباه‌یاب/تمرین',
		'ویکی‌پدیا:صفحه تمرین'
	];

	if (mw.config.get('wgAction') !== 'view' || [0, 4, 12].indexOf(mw.config.get('wgNamespaceNumber')) === -1 ||
			mw.util.getParamValue('diff') ||
			mw.util.getParamValue('oldid') ||
			$('.noarticletext').length) {
		return;
	}

	if (mw.config.get('wgNamespaceNumber') === 4 &&
			projectPageAllowedTitles.indexOf(mw.config.get('wgPageName')) === -1 &&
			mw.config.get("wgCategories").every(function (x) {
				return projectPageAllowedCats.indexOf(x) === -1;
			})) {
		return;
	}


	var pageContentCache;

	function getPageTextCache() {
		return pageContentCache ? $.Deferred().resolve(pageContentCache) : loadPage(mw.config.get('wgPageName'));
	}

	// Copyedited from https://de.wikipedia.org/wiki/MediaWiki:Gadget-Rechtschreibpruefung.js
	function markWord(node, text, word, hint, color, preparedId) {
		var pos, len, newnodes = 0;
		var newnode, middlenode, endnode;

		// textnode - search for word
		if (node.nodeType == 3) {
			pos = node.data.search(text);
			if (pos >= 0) {
				// create new span-element
				newnode = $('<span>', {
					title: hint,
					class: 'CheckDictation-marked CheckDictation-marked-' + word.replace(/ /g, '_'),
					id: preparedId
				}).css('background-color', color)[0];

				// get length of the matching part
				len = node.data.match(text)[0].length;

				// splits content in three parts: begin, middle and end
				middlenode = node.splitText(pos);
				endnode = middlenode.splitText(len);

				// appends a copy of the middle to the new span-node
				newnode.appendChild(middlenode.cloneNode(true));
				// replace middlenode with the new span-node
				middlenode.parentNode.replaceChild(newnode, middlenode);

				newnodes = 1;
			}
		} else if ((node.nodeType == 1) // element node
			&&
			(node.hasChildNodes()) // with child nodes
			&&
			(node.tagName.toLowerCase() != "script") // no script, style and form
			&&
			(node.tagName.toLowerCase() != "style") && (node.tagName.toLowerCase() != "form")) {
			var this_child;
			for (this_child = 0; this_child < node.childNodes.length; this_child++) {
				this_child = this_child + markWord(node.childNodes[this_child], text, word, hint, color, preparedId);
			}
		}
		return newnodes;
	}

	function loadPage(title) {
		return new mw.Api().get({
			action: 'query',
			prop: 'revisions',
			titles: title,
			rvprop: 'content',
			format: 'json'
		}).then(function (data) {
			return ((data.query.pages[Object.keys(data.query.pages)[0] || ''].revisions || '')[0] || '')['*'];
		});
	}

	function savePage(title, text, summary) {
		return new mw.Api().post({
			action: 'edit',
			title: title,
			text: text,
			summary: summary,
			minor: '',
			token: mw.user.tokens.get('editToken')
		}).then(function (data) {
			if (data.error && data.error.info) {
				mw.notify(data.error.info);
			}
		}, function (data) {
			mw.notify(data);
		});
	}

	function dictationReplaceDialog(title, diatext, word, suggestions) {
		var defer = $.Deferred();
		$('#CheckDictation-form, #CheckDictation-form-input').remove();
		$('<div>', {
			html: title
		}).append('<br><br><br>' + diatext).append(
			'<br><br>',
			$('<input>', {
				id: 'CheckDictation-form-input',
				value: word,
				style: 'line-height: 2;'
			}),
			' ',
			persianWikiTools && persianWikiTools.superTool ? $('<img>', {
				src: '//upload.wikimedia.org/wikipedia/fa/f/fc/Button_super_tool.png',
				alt: 'ابرابزار',
				title: 'ابرابزار'
			}).click(function retrySuperTool() {
				$('#CheckDictation-form-input').val((persianWikiTools.superTool(' ' + $('#CheckDictation-form-input').val() + ' ')).trim());
			}) : '<br><br>',
			$('<div>').append(suggestions.map(function (text) {
				return $('<button>', {
					text: text
				}).click(function () {
					$('#CheckDictation-form-input').val(text);
				});
			})),
			'<br>جایگزینی موارد بیشتر در متن:<br><br>',
			$('<input>', {
				id: 'CheckDictation-form-input1',
				value: '',
				style: 'line-height: 2;'
			}), ' > ',
			$('<input>', {
				id: 'CheckDictation-form-input2',
				value: '',
				style: 'line-height: 2;'
			})
		).dialog({
			width: 600,
			title: 'اصلاح یک کلمه به طور عمومی در متن',
			buttons: [{
				text: 'افزودن به فهرست کلمات درست و پرکاربرد',
				class: 'ui-button-red',
				click: function () {
					if (confirm('کلماتی که به فهرست افزوده می‌شوند باید درست و پرکاربرد باشند. اگر کلمه درست و پرکاربرد است دکمهٔ Ok را کلیک کنید در غیر این صورت دکمهٔ Cancel را کلیک کنید.')) {
						defer.reject();
					}
					$(this).dialog('close');
				}
			}, {
				text: 'اصلاح شود',
				class: 'ui-button-green',
				click: function () {
					var box1 = $('#CheckDictation-form-input1', this).val();
					var box2 = $('#CheckDictation-form-input2', this).val();
					if (box1 === box2) {
						box1 = '';
						box2 = '';
					}
					defer.resolve($('#CheckDictation-form-input', this).val(), box1, box2);
					$(this).dialog('close');
				}
			}],
			close: function () {
				$('#CheckDictation-form, #CheckDictation-form-input').remove();
			}
		}).parent().prop('id', 'CheckDictation-form');
		return defer;
	}

	var exceptionsPage = 'ویکی‌پدیا:اشتباه‌یاب/موارد درست/' + mw.config.get('wgPageName').replace(/_/g, ' ');
	var exceptionsUrl = mw.util.getUrl(exceptionsPage);
	$.when(
		$.getJSON('//tools.wmflabs.org/checkdictation-fa/check/' + mw.config.get('wgPageName')),
		loadPage(exceptionsPage)
	).then(function (result, exceptionsText) {

		result = result[0];
		if (!Array.isArray(result.result)) {
			console.error(result.error);
			return;
		}

		var issues = result.result;
		if (issues.length === 0) {
			$('#siteSub').append(' ', $('<span>', {
				text: 'اشتباه‌یاب: اشتباهی پیدا نشد!',
				class: 'CheckDictation-allgood-label',
			}));
			return;
		}

		// Turn exceptions into an array
		var exceptions = [];
		if (exceptionsText !== undefined) {
			var pattern = /(?:\* )([^\n\r]+)(?:[\r\n]|$)/g,
				matches;
			while (matches = pattern.exec(exceptionsText)) {
				exceptions.push(matches[1]);
			}
		}

		if (exceptions.length > 0) {
			var numberOfExceptions = exceptions.length.toLocaleString('fa');
			$('#siteSub').append(' ', $('<span>', {
				'class': 'CheckDictation-exceptions-label'
			}).append(
				'اشتباه‌یاب: ' + numberOfExceptions + ' کلمهٔ مشکوک به اشتباه ',
				$('<a>', {
					href: exceptionsUrl,
					text: 'مستثنی شده‌اند'
				})
			));
		}

		// Remove those words that are in the exceptions list from the issues list
		issues = issues.filter(function (issue) {
			return exceptions.indexOf(issue.word) === -1;
		});

		if (issues.length === 0) {
			return;
		}

		var availableTypes = {};
		issues.forEach(function (x) {
			availableTypes[x.type] = true;
		});
		var types = result.types;

		var legend = Object.keys(types).filter(function (x) {
			return availableTypes[x];
		}).map(function (x) {
			var type = types[x];
			return [
				$('<div>').css({
					width: '12px',
					height: '12px',
					display: 'inline-block',
					'background-color': type.color
				}),
				' ',
				$('<span>', {
					text: type.title
				}).css({
					'font-size': '10pt'
				})
			];
		}).reduce(function (x, y) {
			return x.concat('، ', y);
		});

		$('#CheckDictation-tool').remove();

		// رفع تداخل با provit.js
		var anchor1 = mw.config.get('wgAction') === 'edit' ?
			'#firstHeading' :
			'h1:first';
		var numberOfIssues = issues.length.toLocaleString('fa');
		$('#siteSub').append(' ', $('<span>', {
			text: 'اشتباه‌یاب: ' + numberOfIssues + ' مشکل نوشتاری یا شیوه‌نامه‌ای یافت شد؛ برای بررسی و اصلاح اینجا کلیک کنید',
			class: 'CheckDictation-issues-label'
		}).click(function () {
			$(this).hide();
			$('#CheckDictation-tool').show();
		}));
		$(anchor1).after($('<div>', {
			id: 'CheckDictation-tool'
		}).hide().append(
			$('<big>', {
				text: numberOfIssues + ' مورد مشکوک به اشتباه نوشتاری یا شیوه‌نامه‌ای یافت شد!'
			}).css('font-style', 'italic'),
			' (',
			legend,
			')',
			$('<div>', {
				id: 'spellmarkedwords',
				text: 'موارد مشکوک به اشتباه: '
			}),
			'<br><br>',
			$('<div>', {
				id: 'CheckDictation-button-place'
			}),
			$('<div>', {
				id: 'CheckDictation-button-place-after',
			}).css('font-size', '10pt').append(
				'در صورت کلیک بر روی دکمهٔ فوق موارد درست در ',
				$('<a>', {
					href: exceptionsUrl,
					text: exceptionsPage
				}),
				' ذخیره می‌شوند.',
				'<br>',
				$('<div>', {
					id: 'CheckDictation-button-place-after-2',
					text: 'استفادهٔ نادرست از این دکمه، باعث می‌شود دسترسی شما به ابزار بسته شود. پیش از کلیک بر روی دکمه، مطمئن شوید که همهٔ موارد پیشنهادی درست هستند. پیش از کلیک بر روی دکمه، حتماً از ابرابزار استفاده کنید. بسیاری از خطاهای مقاله توسط ابرابزار رفع می‌شوند.'
				}).css({
					'font-size': '10pt',
					'font-weight': 'bold',
					'color': 'red'
				}),
				'<br>',
				[
					'نکتهٔ ۱: بعضی از این موارد، فقط در حالت ویرایش دیده‌ می‌شوند!',
					'نکتهٔ ۲: با کلیک کردن بر روی کلمات در بالا، بدون رفتن به پنجرهٔ ویرایش به صورت خودکار می‌توانید کلمهٔ درست را جایگزین کنید.',
					'نکتهٔ ۳: می‌توانید چند مورد را پشت سر هم با کلیک انتخاب کنید و با زدن دکمه‌ای که ظاهر می‌شود همهٔ آنها را در یک ویرایش جایگزین کنید.'
				].map(function (x) {
					return $('<div>', {
						text: x
					}).css('font-size', '8pt');
				})
			)
		));
		if (mw.config.get('wgUserGroups').indexOf('sysop') > -1 || mw.config.get('wgUserGroups').indexOf('patroller') > -1) {
			$('<button>', {
				text: 'ذخیره در زیرصفحه: همهٔ موارد برای این مقاله درست است!',
				style: 'margin-right: 2em',
				id: 'CheckDictation-close'
			}).click(function () {
				var newExceptions = exceptions;
				var addToExceptions = issues.map(function (issue) {
					return issue.word;
				});
				addToExceptions.forEach(function (word) {
					newExceptions.push(word);
				});
				newExceptions = '* ' + newExceptions.join('\n* ');

				savePage(exceptionsPage, newExceptions, 'افزودن >' + addToExceptions.join('، ')).then(function () {
					location.href = location.href;
				});

			}).appendTo('#CheckDictation-button-place');
		} else {
			$('#CheckDictation-button-place-after').hide();
			$('#CheckDictation-button-place-after-2').hide();
		}
		var arabic_diacritics = "ًٌٍَُِّْٔ"
		var main_regex = '\[؛؟\\s\\n\\r\\•●⚫⬤\\„\\”\\‚\\’\\‘\\“\\[\\]\\{\\}\\t\\<\\>\\.\\,\\"' + "\\'\\+\\!\\?\\-\\/\\»«،\\:\\|\\(\\)\۰۱۲۳۴۵۶۷۸۹]";
		var start_regex = '\(\^\|' + main_regex + '\)';
		var end_regex = '\(\$\|[' + arabic_diacritics + ']*' + main_regex + '\)';
		var articleBody = $('#bodyContent, #article')[0];
		var replacements = [];
		var markedItems = [];

		issues.forEach(function (item, i) {
			var word = item.word,
				type = types[item.type],
				suggestions = item.suggestions.sort(),
				cleanedWord = item.cleaned_word,
				regexp, preparedId;
			if (i !== 0) {
				markedItems.push('، ');
			}
			regexp = new RegExp(type.syntax ?
				mw.RegExp.escape(word) :
				(start_regex + '(' +
					word.substring(0, word.length - 1).replace(/[ء-يٓ-ٕپچژگکكڪﻙﻚیﻱﻲكﮑﮐﮏﮎﻜﻛﻚﻙىﻯيہەھﻰ-ﻴ]/g, '$&\[ً-ِّْٰٔ\]\*') + word.slice(-1) +
					')' + end_regex),
				'g'
			);
			preparedId = 'tool-' + encodeURI(word).replace(/%/g, '.');
			markWord(articleBody, regexp, word, suggestions[0] || type.hint, type.color, preparedId);
			var element = $('<span>', {
				style: 'background-color: ' + type.color,
				text: word,
				title: suggestions[0] || type.hint
			});
			element.css('cursor', 'pointer');
			element.click(function () {
				if (!type.autofix) {
					location.hash = preparedId;
					return;
				}
				getPageTextCache().then(function (content) {
					var nearWordsRegex = new RegExp('((?:\\S+\\s+){0,5}|\^)(?:[«\\[\\(\\)\\|\\-۰۱۲۳۴۵۶۷۸۹0-9\\u200c\\]])?(' + word.substring(0, word.length - 1).replace(/[^ ]/g, '$&\[ً-ِّْٰٔ\]\*') + word.slice(-1) + ')(\\s*(?:\\S+\\s+){0,5}|\$)', 'g')

					var nearWordsList = [],
						found;
					while (found = nearWordsRegex.exec(content)) {
						nearWordsList.push(found[0]);
					}
					if (nearWordsList != null) {
						nearWordsList = '… ' + nearWordsList.join(' …<br>… ') + ' …'
						nearWordsList = nearWordsList.replace(/\t/g, '	')
						nearWordsList = nearWordsList.replace(/\n\n/g, '<br>')
						nearWordsList = nearWordsList.replace(/\n(\*|\#|\;|\=)/g, '<br>$1')
					} else {
						nearWordsList = ''
					}
					dictationReplaceDialog(
						('در متن یا متن‌های زیر عبارت «' + word + '» با چه چیزی جایگزین شود؟'),
						(nearWordsList.replace(new RegExp('(' + word.substring(0, word.length - 1).replace(/[^ ]/g, '$&\[ً-ِّْٰٔ\]\*') + word.slice(-1) + ')', 'g'), '<big><b> $1 </b></big>')),
						word,
						suggestions
					).then(function (toWord, toWord1, toWord2) {
						// Hide mark as valid button and comments, it confuses the users
						$('#CheckDictation-close, #CheckDictation-button-place-after').hide();

						element.css('background-color', '').css('color', 'grey').text(word + '⟸' + toWord).off();
						toWord = toWord.replace(/\200c /g, ' ').replace(/ \200c/g, ' ');
						if (mw.config.get('wgAction') === 'edit') {
							$('#wpTextbox1').val($('#wpTextbox1').val().replace(regexp, '$1' + toWord + '$3'));
							$('#wpTextbox1').val($('#wpTextbox1').val().replace(toWord1, toWord2));
						} else {
							if (toWord !== word) {
								replacements.push([regexp, '$1' + toWord + '$3', word + '⟸' + toWord]);
							}
							if (toWord1 !== toWord2) {
								replacements.push([toWord1, toWord2, toWord1 + '⟸' + toWord2]);
							}
							$('#CheckDictation-apply').show();
						}
					}, function () {
						if (/[كڪﻙﻚيىےۍېہەھﭖﭗﭘﭙﭺﭻﭼﭽﮊﮋﮎﮏﮐﮑﻙﻚﻛﻜﮒﮓﮔﮕﮤﮥﯼﯽﯾﯿﻯﻰﻱﻲﻳﻴﺁﺂﺄﺃﺅﺆﺇﺈﺉﺊﺋﺌﺎﺏﺐﺑﺒﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻫﻭﻮﻰﻲﻶﻸﻺﻼ]/.exec(word)) {
							alert('لغت پیشنهادی نویسهٔ غیرفارسی یا نویسهٔ نادرست دارد. قبل از گزارش لغت، لطفاً ابرابزار را در صفحه برانید!');
							return;
						}
						if (/((.*\u200c)$|^(\u200c.*))/.exec(word)) {
							alert('در انتها یا ابتدای لغت پیشنهادی نویسهٔ فاصلهٔ مجازی اضافی وجود دارد. لطفاً آن را به کمک ابزار جایگزینی یا ابرابزار حذف کنید');
							return;
						}
						$('CheckDictation-marked-' + word.replace(/ /g, '_')).css('background-color', '');
						element.css('background-color', '').css('color', 'grey').off();
						loadPage('ویکی‌پدیا:اشتباه‌یاب/فهرست موارد درست').then(function (text) {
							if ((text + '\n').indexOf('* ' + (cleanedWord || word) + '\n') !== -1) {
								return;
							}
							return savePage('ویکی‌پدیا:اشتباه‌یاب/فهرست موارد درست', text + '\n* ' + (cleanedWord || word), 'افزودن لغت درست و پرکاربرد «' + (cleanedWord || word) + '» به فهرست کلمات [[وپ:اشتباه|اشتباه‌یاب]]. موجود در مقالهٔ [[' + mw.config.get('wgPageName') + ']]');
						}).then(function () {
							mw.notify('کلمهٔ «' + word + '» به فهرست کلمات درست و پرکاربرد افزوده شد.');
						});
						$.get('//tools.wmflabs.org/checkdictation-fa/check/Botupdate').then(function () {}, function () {});
					});
				});
			});
			markedItems.push(element[0]);

		});

		$('#spellmarkedwords').append(markedItems);

		// reset page location to intended hash linked place
		var hash = location.hash;
		if (hash) {
			location.hash = '';
			location.hash = hash;
		}

		$('<button>', {
			text: 'جایگزینی مواردی که انتخاب کردید!',
			style: 'margin-right: 2em',
			id: 'CheckDictation-apply'
		}).click(function () {
			$(this).prop('disabled', 'disabled').text('در حال دریافت و جایگزینی صفحه...');
			loadPage(mw.config.get('wgPageName')).then(function (text) {
				replacements.forEach(function (x) {
					text = text.replace(x[0], x[1]);
				});
				return savePage(
					mw.config.get('wgPageName'),
					text,
					'/' + '*جایگزینی با [[وپ:اشتباه|اشتباه‌یاب]]*' + '/' + replacements.map(function (x) {
						return x[2];
					}).join('، ')
				);
			}).then(function () {
				mw.notify('انجام شد، لطفاً بررسی کنید');
				location.href = mw.util.getUrl(mw.config.get('wgPageName'), {
					diff: 'last'
				});
			});
		}).hide().appendTo('#spellmarkedwords');
	});
});