מדיה ויקי:Gadget-Checkty.js

הערה: לאחר הפרסום, ייתכן שיהיה צורך לנקות את זיכרון המטמון (cache) של הדפדפן כדי להבחין בשינויים.

  • פיירפוקס / ספארי: להחזיק את המקש Shift בעת לחיצה על טעינה מחדש (Reload) או ללחוץ על צירוף המקשים Ctrl-F5 או Ctrl-R (במחשב מק: ⌘-R).
  • גוגל כרום: ללחוץ על צירוף המקשים Ctrl-Shift-R (במחשב מק: ⌘-Shift-R).
  • אינטרנט אקספלורר / אדג': להחזיק את המקש Ctrl בעת לחיצה על רענן (Refresh) או ללחוץ על צירוף המקשים Ctrl-F5.
  • אופרה: ללחוץ על Ctrl-F5.
// הוספת כפתור "בדיקה" שמבצע החלפות נפוצות של בוט ההחלפות וכן מתריע על בעיות סגנון ועיצוב שונות
// נכתב על ידי [[משתמש:ערן]] ו[[משתמש:קיפודנחש]]
// לעזרה ראו [[mediawiki:Gadget-Checkty.js/הוראות]]
mw.messages.set({
	'checkty-large-element': 'בערך קיים אלמנט גדול, רצוי להקטין כדי שיתאים לרזולוציות נמוכות',
	'checkty-long-list': 'נראה כי בערך רשימה של מעל 20 פריטים. כדאי לשקול לפצלה לשני טורים באמצעות תבנית:טורים',
	'checkty-waiting-disambig-query': 'ממתין לרשימת פירושונים מהשרת...',
	'checkty-warnings-title': 'הערות לבדיקה:',
	'checkty-no-replacements': 'הדף מכיל תבנית "ללא בוט" ולכן לא יבוצעו החלפות',
	'checkty-disambig-suffix': ' (פירושונים)',
	'checkty-disambig-no-links': 'בדיקת קישורים לדפי פירושונים הסתיימה. לא נמצאו קישורים לפירושונים',
	'checkty-disambig-success': 'בדיקת קישורים לדפי פירושונים הסתיימה בהצלחה.',
	'checkty-disambig-links-title': 'הגרסה השמורה האחרונה של הדף מקשרת לדפי פירושונים. אנא תקנו את הקישורים לדפים הבאים: ',
	'checkty-page-doesnt-exist': ' (הדף אינו קיים)',
	'checkty-dismabig-dialog-title': 'תיקון פירושונים',
	'checkty-remove-link': 'הסרת קישור',
	'checkty-disambig-meaning': 'מה הכוונה ב "$1" במשפט: ',
	'checkty-dismabig-fix-summary': 'תיקון קישור לפירושונים',
	'checkty-search': 'חיפוש',
	'checkty-fix-numberRangeDash': 'תיקון קווים מפרידים'
});

var chectTyTool = {
	textbox: null,
	formatReplacesConfig: [{
		from: /\[\[(.*?)\|\1([א-ת]*?)\]\]/g,
		to: '[[$1]]$2'
	}, {
		from: /\[\[(.+?)\|([במל])\1\]\]/g,
		to: "$2[[$1]]"
	}, {
		from: /([א-ת]\]?\]?) ?([,\.]) ?(\[?\[?[א-ת]{3})/g,
		to: "$1$2 $3"
	}, {
		from: /([א-ת])\( ?([א-ת])/g,
		to: "$1 ($2"
	}, {
		from: /\t/g,
		to: " "
	}, {
		from: /(\n\n)\n+/g,
		to: "$1"
	}, {
		from: /== ? ?\n\n==/g,
		to: "==\n=="
	}, {
		from: /^ ? ? \n/gm,
		to: "\n"
	}, {
		from: /(?!.{2}\|)[ \t]{2,}/g,
		to: ' '
	}, {
		from: /\[\[(File|Image|תמונה):/ig,
		to: '[[קובץ:'
	}, {
		from: /\|thumb(nail)?/ig,
		to: '|ממוזער'
	},{ //remove unseen character
		from: /‏/g,
		to: ""
	}],
	regexes: [],
	ignoreStrings: [],
	run: function () {
		if (this != chectTyTool) {
			chectTyTool.run();
			return;
		}
		var t = $('#wpTextbox1');
		this.textbox = t.length ? t[0] : null;
		if (!this.textbox || this.textbox.value.length === 0) return;
		if (!($('#checktyResults').length)) $('.editButtons').after('<div id="checktyResults"><div class="checktyResultsTitle">'+mw.msg( 'checkty-warnings-title' )+'</div></div>');
		//first call to remote functions than to local
		this.build_regexes();
		this.checkImages();
		this.disambigCheck();
		this.languageCheck();
		this.numberRangeDash(false);
		this.formatReplace();
		this.refsCheck();
		this.mergeRefs();
		this.titleOrderCheck();
		this.overlinkify();
	},
	writeMsg: function (msg) {
		if (msg instanceof Array) {
			if (msg.length === 0) return;
			msg = '<div>' + msg.join('<br/>') + '</div>';
		}
		var x = $(msg).css('display', 'none').addClass('checktyMsg');
		$('#checktyResults').append(x);
		x.show('slow');
	},
	build_regexes: function (data) {
		if (/\{\{\s*ללא_בוט\s*\}\}/.test(this.textbox.value)) {
			this.writeMsg('<div>'+mw.msg( 'checkty-no-replacements' )+'</div>');
			return;
		}
		if (data) {
			var lines = data.split(/\n/);
			var clear_nowiki = /\|<nowiki>(.*)<\/nowiki>/;
			var matches, regex;
			while (lines.length) {
				if (!(matches = lines.shift().match(/^\|(\d+)/))) continue;
				var num = parseInt(matches[1], 10);
				if (!(matches = lines.shift().match(clear_nowiki))) continue;
				try {
					regex = new RegExp(matches[1], 'g');
				} catch (e) {
					//ignore
					continue;
				}
				if (!(matches = lines.shift().match(clear_nowiki))) continue;
				this.regexes[num] = [regex, matches[1]];
				var ignore = lines.shift().replace(/\||<\/?nowiki>/g, '');
				ignore = $.trim(ignore);
				if (ignore) this.ignoreStrings.push(mw.RegExp.escape(ignore));
			}
			this.process_page();
		} else $.ajax({
			url: mw.util.getUrl( (window.replaceListPage || 'ויקימסע:בוט/בוט החלפות/רשימת החלפות נוכחית'), { action: 'raw', ctype: 'text/x-wiki' } ),
			dataType: 'html',
			success: function (data, status) {
				chectTyTool.build_regexes(data);
			}
		});
	},
	process_page: function () {
		var t = this.textbox.value,
			skip_dict = {},
			skip_ar = [],
			actual_replaced = [],
			skipmatch = t.match(/{{ללא[_ ]בוט\|\s*(\d+)\s*}}/g), i, match;
		if (skipmatch)
			for (i = 0; i < skipmatch.length; i++) {
				var matches = skipmatch[i].match(/{{ללא[_ ]בוט\|\s*(\d+)\s*}}/);
				skip_dict[parseInt(matches[1], 10)] = true;
				skip_ar.push(matches[1]);
			}
		var specials = [];
		var ignoreRegex = new RegExp('(' + this.ignoreStrings.join('|') + ')');
		while (true) { //extract inner links, inner templates and inner params - we don't want to sptit those.
			match = t.match(/(\{\{[^\{\}]*\}\}|(\n|\[\[)(?:File|קובץ|תמונה|Image):.*?[\|\n]|[^\[\0]\[[^\{\}\[]*\])/);
			if ((!match || !match.length) && this.ignoreStrings.length ) match = t.match(ignoreRegex);
			if (!match || !match.length) break;
			specials.push(match[0]);
			t = t.replace(match[0], "\0" + specials.length + "\0");
		}
		for (i in this.regexes)
			if (!skip_dict[i] && !isNaN(i))
				if (this.regexes[i][0].test(t)) {
					var before = t;
					t = t.replace(this.regexes[i][0], this.regexes[i][1]);
					if (t != before)
						actual_replaced.push($.trim(this.regexes[i][1].replace(/\$\d*/g, '')));
				}
		while (true) {
			match = t.match(/\0(\d+)\0/);
			if (!match || !match.length) break;
			t = t.replace(match[0], specials[parseInt(match[1], 10) - 1]);
		}
		this.textbox.value = t;
		var msg = ['ריצת סקריפט ההחלפות הסתיימה. אנא בצעו "הצגת שינויים" לפני שמירה, כדי לוודא שהסקריפט לא גרם נזק.'];
		if (skip_ar.length) msg.push('החלפות שלא התבצעו בגלל תבנית "ללא בוט": ' + skip_ar.join(', '));
		msg.push(actual_replaced.length ? 'התבצעו ההחלפות הבאות: ' + actual_replaced.join('‏, ') : 'לא התבצעו החלפות - הדף "נקי".');
		this.writeMsg(msg);

		if (actual_replaced.length) chectTyTool.addSummary('סקריפט החלפות (' + actual_replaced.join(', ') + ')');
	},
	fetchDisambigLinks: function (next) {
		var dfd = new jQuery.Deferred();
		var api = new mw.Api();
		var params = {
			action: 'query',
			generator: 'links',
			titles: mw.config.get('wgPageName'),
			prop: 'pageprops',
			ppprop: 'disambiguation',
			gpllimit: '500'
		};
		if (next !== undefined) {
			params.gplcontinue = next;
		}
		api.get(params).done(function (data) {
			//extract disambig pages
			if (!data.hasOwnProperty('query')) {
				dfd.reject();
				return;
			}
			var disambigs = [];
			for (var pid in data.query.pages) {
				var p = data.query.pages[pid];
				//list only real disambig links
				if (p.pageprops && p.title != mw.config.get('wgTitle') + mw.msg( 'checkty-disambig-suffix' )) {
					disambigs.push(p.title);
				}
			}
			if (data['query-continue'] !== undefined) {
				var nextReq = chectTyTool.fetchDisambigLinks(data['query-continue'].links.gplcontinue);
				nextReq.done(function (more) {
					dfd.resolve($.merge(disambigs, more));
				});
			} else {
				dfd.resolve(disambigs);
			}
		});
		return dfd.promise();
	},
	formatReplace: function () {
		var txt = this.textbox.value;
		// Format autofix
		$(this.formatReplacesConfig).each(function (i, o) {
			txt = txt.replace(o.from, o.to);
		});
		this.textbox.value = txt;
	},
	disambigCheck: function () {
		this.writeMsg($('<div>', {
			id: 'waitForDisambigs'
		}).text( mw.msg( 'checkty-waiting-disambig-query' ) ));

		this.fetchDisambigLinks().fail(function () {
			$('#waitForDisambigs').html( mw.msg( 'checkty-disambig-no-links' ) );
		}).done(function (res) {
			if (res.length === 0) {
				$('#waitForDisambigs').html( mw.msg( 'checkty-disambig-success' ) );
				return;
			}
			$('#waitForDisambigs').remove();
			var disambigs = $('<div id="disambigWarnning">'+mw.msg( 'checkty-disambig-links-title' )+'</div>').prepend('<img src="//upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Disambig_RTL.svg/15px-Disambig_RTL.svg.png">').css('padding', '0 5px');
			$.each(res, function (i, disTitle) {
				if (i > 0) {
					disambigs.append(', ');
				}
				disambigs.append($('<a href="' + mw.util.getUrl(disTitle) + '">' + disTitle + '</a>').click(function () {
					var disambigName = $(this).text();
					$.getJSON(
						mw.util.wikiScript('api'), {
							action: 'parse',
							page: disambigName,
							prop: 'text',
							format: 'json'
						}).done(function (data) {
							if (data && data.parse && data.parse.text) {
								chectTyTool.resolveDisambig(disambigName, data.parse.text['*']);
							} else {
								console.error(data);
							}
						});
					return false;
				}));
			});
			chectTyTool.writeMsg(disambigs);
		});
	},
	resolveDisambig: function (name, data) {
		var offset = 0,
			textbox = this.textbox,
			linkRgx = new RegExp('(?:\\.|^)([^\\.\n]*(\\[\\[' + mw.RegExp.escape(name) + '[\\|\\]]).*?)[\\.\\n]', 'm'),
			orgPos = $(textbox).textSelection('getCaretPosition'),
			cSentence = $('<div>');
		var options = $('<div>').append($('li', data).map(function () {
			var a = $(this).children('a').get(0);
			if (a) {
				var storeTitle = $(this).text();
				var anchor = '',
					h = a.href;
				if (h.indexOf('#') + 1) anchor = decodeURI(h.substr(h.indexOf('#')).replace(/\./g, '%').replace(/_/g, ' '));
				$(a).text( a.title.replace(mw.msg( 'checkty-page-doesnt-exist' ), "") + anchor);
				a.title = storeTitle;
			}
			return a || null;
		}).click(resolve))
			.append($('<a href="#">'+mw.msg( 'checkty-remove-link' )+'</a>').click(removeLink));
		var disambigDialog = $('<div>').append( mw.msg('checkty-disambig-meaning', name) + '<hr/>').append(cSentence).append(options.buttonset()).dialog({
			title: mw.msg( 'checkty-dismabig-dialog-title' ),
			close: function () {
				$(textbox).textSelection('setSelection', {
					start: orgPos
				});
			}
		});
		findSentence();
		function findSentence() {
			var text = textbox.value.substr(offset),
				m = text.match(linkRgx);
			if (!m) {
				disambigDialog.dialog('close');
				return;
			}
			offset += text.indexOf(m[1]) + m[1].indexOf(m[2]);
			var linkIndex = m[1].indexOf(m[2]) + 2,
				html = m[1].substr(0, linkIndex) + '<big>' + name + '</big>' + m[1].substr(linkIndex + name.length);
			cSentence.html(html);
		}
		function resolve() {
			var answer = $(this).text(),
				text = textbox.value,
				startLink = text.indexOf('[[' + name, offset);
			if (text.charAt(startLink + 2 + name.length) != '|') answer += '|' + name;
			offset += answer.length + 2;
			text = text.substr(0, startLink + 2) + answer + text.substr(startLink + 2 + name.length);
			textbox.value = text;
			findSentence();
			chectTyTool.addSummary( mw.msg( 'checkty-dismabig-fix-summary' ) );
			return false;
		}
		function removeLink() {
			var text = textbox.value,
				startLink = text.indexOf('[[' + name, offset),
				endLink = text.indexOf(']]', startLink),
				pipeChar = startLink + 2 + name.length,
				linkText = (text.charAt(pipeChar) != '|') ? name : text.substr(pipeChar + 1, endLink - pipeChar - 1);
			offset += (endLink - startLink) + linkText.length;
			text = text.substr(0, startLink) + linkText + text.substr(endLink + 2);
			textbox.value = text;
			findSentence();
			return false;
		}
	},
	addSummary: function (msg) {
		var editSummary = $('#wpSummary').val();
		if (editSummary.indexOf(msg) === -1) $('#wpSummary').val(editSummary + (editSummary.length === 0 ? '' : ', ') + msg);
		// tag the edit
		if ( $('#checktyTag').length === 0 ) {
			$('#editform').append('<input type="hidden" name="wpChangeTags" id="checktyTag" value="צ\'קטי">');
		}		
	},
	checkImages: function (data) {
		var fairUsageTemplates = ['תבנית:שימוש הוגן', 'תבנית:תמונת חבר כנסת'];
		if (!data) {
			if (!(/\{\{(ויקישיתוף בשורה|מיזמים)/.test(this.textbox.value))) {
				$.getJSON('//www.wikidata.org/w/api.php?callback=?', {
					languages: 'he',
					action: 'wbgetentities',
					sites: 'hewiki',
					titles: mw.config.get('wgTitle'),
					format: 'json',
					props: 'claims'
				}).done(function (data) {
					if (data.success === undefined || !data.success) return;
					for (var entityId in data.entities) {
						var claims = data.entities[entityId].claims;
						if (claims && claims.hasOwnProperty('P373')) {
							chectTyTool.writeMsg($('<div>', {
								text: 'בערך זה חסר קישור לוויקישיתוף. ניתן להוסיף קישור באמצעות בחירת המקום הנכון להוספתו בערך ולחיצה על הקישור הבא. '
							}).prepend('<img src="//upload.wikimedia.org/wikipedia/commons/thumb/4/4a/Commons-logo.svg/15px-Commons-logo.svg.png">').css('padding', '0 5px').append($('<a>', {
								href: '#',
								text: 'להוספה'
							}).click(function () {
								mw.toolbar.insertTags('{{ויקישיתוף בשורה}}')
								chectTyTool.addSummary('ויקישיתוף בשורה');
							})));
						}
					}
				});
			}
			//in case there are no images in page
			if (!mw.util.getParamValue('section') && !(/\[\[(תמונה|קובץ|File|Image):/i.test(this.textbox.value))) {
				var articleName = mw.config.get('wgPageName');
				var fistURL = this.fistURL({
					datatype: 'articles',
					data: articleName
				});
				var msg = $('<div>', {
					text: 'בדף זה אין תמונות. ניתן לחפש תמונות חופשיות ממקורות שונים. '
				}).append($('<a>', {
					href: decodeURI(fistURL),
					text: 'חיפוש תמונות',
					target: '_blank'
				}));
				this.writeMsg(msg);
				return;
			}
			$.getJSON(
				mw.util.wikiScript('api'), {
					action: 'query',
					generator: 'images',
					titles: mw.config.get('wgPageName'),
					prop: 'templates',
					tltemplates: fairUsageTemplates.join('|'),
					format: 'json'
				}).done(function (data) {
					if (data && data.query && data.query.pages) chectTyTool.checkImages(data.query.pages);
				});
		} else {
			var fairUseImgs = $.map(data, function (o) {
				if (!o.templates) return;
				var isFairUsage;
				$.each(o.templates, function (k, license) {
					if ($.inArray(license.title, fairUsageTemplates) != -1) {
						isFairUsage = true;
						return false;
					}
				});
				if (isFairUsage) return o.title;
			});
			if (fairUseImgs.length === 0) return;
			//add message with fair usage images
			var fistURL = this.fistURL({
				data: fairUseImgs.join('\r\n'),
				datatype: 'replaceimages'
			});
			var msg = $('<div>', {
				text: 'הדף מכיל תמונות בשימוש הוגן, שמומלץ להחליפן בחלופות חופשיות במידת האפשר. '
			}).append($('<a>', {
				href: fistURL,
				text: 'חיפוש חלופות חופשיות',
				target: '_blank'
			}));
			if (!(/\{\{תמונה להחלפה\}\}/.test($('#wpTextbox1').val())))
				msg.append(' - ').append($('<a>', {
					text: 'סימון להחלפה',
					href: '#'
				}).click(function () {
					var t = $('#wpTextbox1');
					$.each(fairUseImgs, function (i, fiImg) {
						var imgName = mw.RegExp.escape(/.:(.+)$/.exec(fiImg)[1]);
						var imgDescRE =  new RegExp(imgName.replace(' ', '[ _]') + '((?:[^\\[\\]]|\\[\\[[^\\[\\]]*?\\]\\])*?)\]\]', 'i');
						var matches;
						if (!(matches = imgDescRE.exec(t.val()))) {
							return;
						}
						//this is thumb img
						var imgDesc = matches[1].split('|');
						var isThumb = false;
						var imgCaption = '';
						for (var i in imgDesc) {
							if (/thumb|ממוזער/i.test(imgDesc[i])) {
								isThumb = true;
							} else if (!(/^(ימין|שמאל|מרכז|right|left|center|[0-9]+px)?$/i.test(imgDesc[i]))) {
								imgCaption = imgDesc[i]; //unknown parameter assumed to be description
							}
						}
						if (isThumb) {
							if (imgCaption) {
								imgDesc = matches[1].replace(imgCaption, imgCaption + '{{תמונה להחלפה}}');
							} else {
								imgDesc = matches[1] + '|{{תמונה להחלפה}}';
							}
							t.val(t.val().replace(matches[1], imgDesc));
							chectTyTool.addSummary('{{תמונה להחלפה}}');
						} else {
							//is in infobox heuristic: = before image tag
							var isInInfobox = new RegExp('=\s*\\[\\[(?:file|image|קובץ|תמונה):' + imgName.replace(' ', '[ _]') + '[^\\]]*?\]\]', 'i');
							if (isInInfobox.test(t.val())) {
								t.val(t.val().replace(matches[0], matches[0] + '{{תמונה להחלפה}}'));
								chectTyTool.addSummary('{{תמונה להחלפה}}');
							}
						}
					});
				}));
			this.writeMsg(msg);
		}
	},
	fistURL: function (p) {
		return 'https://tools.wmflabs.org/fist/fist.php?doit=1&language=he&project=wikipedia&params[catdepth]=&params[random]=&params[startat]=&params[ll_max]=5&params[free_only]=1&params[commons_max]=5&params[flickr_max]=5&params[include_flickr_id]=1&params[flickr_new_name_from_article]=1&params[picasa_max]=5&params[wts_max]=5&params[gimp_max]=&params[esp_max]=5&params[geograph_max]=5&params[geograph_max_de]=5&params[geograph_max_channel-islands]=5&params[freemages_max]=5&params[forarticles]=all&params[lessthan_images]=&params[default_thumbnail_size]=250&params[jpeg]=1&params[png]=1&params[gif]=1&params[svg]=1&params[output_format]=out_html&params[min_width]=80&params[min_height]=80&params[ab_max]=5&sources[languagelinks]=1&sources[commons]=1&sources[flickr]=1&' + $.param(p);
	},
	languageCheck: function (checks) { //style and language check
		var highlightStr = this.highlightString;
		var findFunc = function () {
			var toFind = eval(unescape($(this).attr('href').substr(1))).exec($('#wpTextbox1').val());
			if (toFind) highlightStr(toFind[0]);
			return false;
		};
		if (checks) {
			var txt = this.textbox.value;
			var checkWarnings = $('<div></div>');
			for (var x in checks) {
				if (checks[x]['test'].test(txt)) {
					checkWarnings.append($('<a href="#' + escape(checks[x]['test']) + '">'+mw.msg( 'checkty-search' )+'</a>').click(findFunc));
					checkWarnings.append('&nbsp;-&nbsp;' + checks[x]['remark'] + '<br/>');
				}
			}
 
			// Design checks: Elements width
			var largeElement = /[6789][0-9][0-9]px/;
			if (largeElement.test(txt)) checkWarnings.append(mw.msg( 'checkty-large-element' ) + '<br/>');
			var manyLi = RegExp('(?:\n\\*.*){20}');
			if (manyLi.test(txt)) checkWarnings.append(mw.msg( 'checkty-long-list' ) );
			if (checkWarnings.html().length) this.writeMsg(checkWarnings);
		} else {
			var api = new mw.Api();
			api.get({
				action:'parse',
				page: 'ויקימסע:בדיקה אוטומטית',
				prop: 'wikitext'
				}).done(function (data) {
					var DictionaryText = data.parse.wikitext['*'].split('-----')[1]
					var genrealWarningWords = DictionaryText.split('\n*');
					var checks = [];
					for (var i=0;i<genrealWarningWords.length;i++)
					{
						var splittedWarn = genrealWarningWords[i].split("//");
						if ( splittedWarn.length !== 2 ) continue;
						checks.push({
							'test': new RegExp( splittedWarn[0], 'i' ),
							'remark': splittedWarn[1]
						});
					}
					chectTyTool.languageCheck(checks);
			});

			var txt = $('#wpTextbox1').val(),
			countRgx = /(?:[^ ]+ ){2}.?(?:ש[נת]י|שלוש[תה]?|ארבע[הת]?|חמשת?|חמישה|ששת?|שבע[הת]?|תשע[הת]?|עשר[הת]?) [א-ת]+/g,
			gramCheck = [], m;
			while (m = countRgx.exec(txt)) { gramCheck.push(m) }
			if (gramCheck.length) $.post('//tools.wmflabs.org/eranbot/shtei_shekel/heb_check.py', { wikitext: gramCheck.join('\n') } ).done(function(d){
				if (!d.errs || d.errs.length === 0) return;
				var zaharNekevaWarns = $('<div></div>');
				for ( var i = 0; i < d.errs.length; i++ ) {
					var origRgx = new RegExp(d.errs[i].orig);
					zaharNekevaWarns.append($('<a href="#' + escape(origRgx) + '">'+mw.msg( 'checkty-search' )+'</a>').click(findFunc));
					zaharNekevaWarns.append('&nbsp;-&nbsp;ייתכן שיש אי התאמה במין ב"' + d.errs[i].orig + '" (תיקון: '+d.errs[i].suggested+') <br/>');
				}
				chectTyTool.writeMsg(zaharNekevaWarns);
			});

		}
	},
	refsCheck: function () { //בדיקת אחידות הערות שוליים
		function safeRegexFix(fixRegex, fixReplace, check){
			var txt = $('#wpTextbox1').val(), 
				m;
			//remove templates within templates
			var specials = [];
			while (m = /\{\{(?!הערה\|)[^\{]*?\}\}/g.exec(txt)) {
				txt = txt.replace(m[0], '\0' + specials.length + '\0')
				specials.push(m[0])
			}
			if (check) {
				var counterA = 0, counterB = 0;
				while (fixRegex.exec(txt)) counterA++;
				while (fixReplace.exec(txt)) counterB++;
				return [counterA, counterB];
			} else {
				txt = txt.replace(/(\{\{הערה\|.*?\}\}|<ref>.*?<\/ref>)\s+(?=(\{\{הערה\||<ref>))/g, '$1'); // remove spaces between refs
				txt = txt.replace(fixRegex, fixReplace);
				while (m = specials.pop()) txt = txt.replace('\0' + specials.length + '\0', m);
				$('#wpTextbox1').val(txt);
			} 
		}
		var refAfter = /([\.\,])\s*((\{\{הערה.*?\}\}|<ref>.*?<\/ref>)+)\.?/g,
			refBefore = /\.?((\{\{הערה\|([^\{]|\{(?!\{הערה\|))*\}\}|<ref>([^<]|<(?!\/ref>))*<\/ref>)+)\s*([\.\,])/g

		var refCounter = safeRegexFix(refAfter, refBefore, true);
		if (refCounter[0] > 0 && refCounter[1] > 0) {
			this.writeMsg($('<div>', {
				text: 'חוסר תאימות בהערות שוליים: ' + refCounter[0] + ' הערות אחרי סימן פיסוק, ' + refCounter[1] + ' הערות לפני סימן פיסוק [תיקון: '
			}).append($('<a>', {
				text: 'אחרי',
				href: '#'
			}).click(function () {
				safeRegexFix(refBefore, '$5$1', false);
				chectTyTool.addSummary('אחידות במיקום הערות שוליים ביחס לסימני פיסוק');
			})).append(' | ').append($('<a>', {
				text: 'לפני',
				href: '#'
			}).click(function () {
				safeRegexFix(refAfter, '$2$1', false);
				chectTyTool.addSummary('אחידות במיקום הערות שוליים ביחס לסימני פיסוק');
			})).append(']'));
		}
	},
	mergeRefs: function() { // merge refs with same content
		var references = {};// a dictionary where key is the ref content and the value is list of uses
		var refRE = /\{\{הערה\|(?! *שם *=)(?:1=)?((?:[^\{\}]|\{\{.*?\}\})+)(?!\|=שם=)\}\}/g;
		var wikitext = this.textbox.value;
		var m;
		while ( m = refRE.exec( wikitext ) ){
			references[m[1]] = references[m[1]] || [];
			references[m[1]].push(m[0]);
		}
		for (var refContet in references) {
			var refUses = references[refContet];
			if ( refUses.length === 1 ) continue;
			var commonRefStructure = /(.+?) .+ (?:עמ|p).? ?([0-9]+)/.exec(refContet);
			var defaultRefName = '';
			if (commonRefStructure) {
				defaultRefName = commonRefStructure[1]+commonRefStructure[2];
			}
			var refName = prompt('ישנה הערת שוליים החוזרת מספר פעמים. ניתן להפנות את כל המופעים אל הערה אחת באמצעות מתן שם להערה.\nאנא הזינו שם להערה הבאה:\n'+refContet, defaultRefName);
			if ( !refName ) continue;
			wikitext = wikitext.replace( refUses[0], '{{הערה|שם='+refName+'|1='+refContet+'}}');
			for (var refI = 1; refI<refUses.length; refI++){
				wikitext = wikitext.replace( refUses[refI], '{{הערה|שם='+refName+'}}');
			}
			this.textbox.value = wikitext;
		}
	},
	titleOrderCheck: function(){
		// validates the titles order is consistent
		var orderedTitles = ['ראו גם', 'לקריאה נוספת', 'קישורים חיצוניים', 'הערות שוליים'], 
			isSorted = 1,
			titles = $('#wpTextbox1').val().match('==\\s*'+orderedTitles.join('|')+'\\s*==','g'), i, indexes;
		if (!titles) return; // no such titles
		indexes = $.map( titles, function(e){return orderedTitles.indexOf(e.replace(/\s*==\s*/g,''))});
		for ( i=0; ( i < indexes.length-1 ) && isSorted; i++) {	isSorted &= (indexes[i] < indexes[i+1]) };
		if ( !isSorted ) {
			this.writeMsg($('<div>מומלץ לתקן את סדר הכותרות ל: ' + orderedTitles.join(', ') + '</div>') );
		}
	},
	overlinkify: function() {
		function removeOverlink(context, link){
			return context.replace(new RegExp(mw.RegExp.escape('[['+link+']]'), 'g'), link).replace(new RegExp(mw.RegExp.escape(link)),'[['+ link+']]')
		}
		var highlightStr = this.highlightString;
		var wikitext = $('#wpTextbox1').val();
		var overlinkingRgx = /\[\[([^\[\]\|]+?)\]\].+\[\[\1\]\]/g
		var m;
		
		while(m=overlinkingRgx.exec(wikitext))
		{
			var removeLink = $('<a href="#">'+mw.msg( 'checkty-remove-link' )+'</a>').data({'search': m[1]}).click(function(e){ 
				var specificOverlinkingRgx = new RegExp('\\[\\[('+$(this).data('search')+')\\]\\].+\\[\\[\\1\\]\\]', 'g');
				var wikitext = $('#wpTextbox1').val();
				var m;
				while( m = specificOverlinkingRgx.exec( wikitext ) )
				{
					wikitext = wikitext.replace(m[0], removeOverlink(m[0], m[1]));
				}
				$('#wpTextbox1').val(wikitext);
				chectTyTool.addSummary('הסרת קישורים עודפים');
				e.preventDefault();
			});
			var searchLink =$('<a href="#">'+mw.msg( 'checkty-search' )+'</a>').data({'search': m[1]}).click(function(e){ 
				highlightStr('[['+$(this).data('search')+']]'); e.preventDefault();
			});
			this.writeMsg($('<div>').append(['נמצאו קישורים עודפים ל"'+m[1]+'" [',searchLink, '&nbsp;-&nbsp;', removeLink, ']']));
			wikitext = wikitext.replace(m[0], removeOverlink(m[0], m[1]));
		}
	},
	numberRangeDash: function(fix) {
		// replace minus => dash foreach x-y s.t x<y
		var numberRangeReNoMinus = /([א-ת]'?[ -](?:\[\[|\()?[0-9]+(?:\]\])?)-((?:\[\[)?[0-9]+)((?:\]\]|\))?[.:,]?( |\n)(?!לפנה"ס))/g, 
			wikitext = this.textbox.value, replacements = [],  m, i;
		while(m = numberRangeReNoMinus.exec(wikitext))
		{
			if ( parseInt(m[1].replace(/[^0-9]/g, '')) <  parseInt(m[2].replace(/[^0-9]/g, '')) ) {
				replacements.push([m[1] + '-' + m[2] + m[3], m[1] + '–' + m[2] + m[3]]);
			}
		}
		if (replacements.length==0) return;
		if(fix) {
			for(i = 0; i<replacements.length; i++) {
				wikitext = wikitext.replace(replacements[i][0], replacements[i][1]);
			}
			this.textbox.value = wikitext;
			this.addSummary( 'קו מפריד בטווח מספרים' );
		} else {
			this.writeMsg($('<div><a href="#">'+mw.msg( 'checkty-fix-numberRangeDash' )+'</a></div>').click(function(e){  chectTyTool.numberRangeDash(true); e.preventDefault(); }));
		}
	},
	//original version from http://code.google.com/p/proveit-js/source/browse/ProveIt_Wikipedia.js#384
	//thanks to Georgia Tech Research Corporation. Atlanta, GA 30332-0415
	highlightString: function (toFind) {
		var txtArea = $('#wpTextbox1');
		var nextPlace = txtArea.val().indexOf(toFind, txtArea.textSelection('getCaretPosition') + 1);
		if (nextPlace === -1) nextPlace = txtArea.val().indexOf(toFind); //start from begining
		if (nextPlace === -1) return; //not found... nothing to do
		var origText = txtArea.val();
		txtArea.val(origText.substring(0, nextPlace));
		txtArea.focus();
		txtArea.scrollTop(1000000); //Larger than any real textarea (hopefully)
		var curScrollTop = txtArea.scrollTop();
		txtArea.val(origText);
		if (curScrollTop > 0) {
			var HALF_EDIT_BOX_HEIGHT = 200;
			txtArea.scrollTop(curScrollTop + HALF_EDIT_BOX_HEIGHT);
		}
		txtArea.focus().textSelection('setSelection', {
			start: nextPlace,
			end: nextPlace + toFind.length
		});
	}
};

if ($.inArray(mw.config.get('wgAction'), ['edit', 'submit']) + 1) $(document).ready(function () {
	$('#wpPreview').after($('<input type="button" id="btnCheckTool" title="צ\'קטי - כלי לבדיקת בעיות נפוצות ועוד" value="' + (window.checkToolName || 'בדיקה') + '" />').click(chectTyTool.run));
});