Benutzer:TMg/cleanDiff.js

Dies ist eine alte Version dieser Seite, zuletzt bearbeitet am 4. Januar 2013 um 21:15 Uhr durch TMg (Diskussion | Beiträge) (Eigene Kodierung durch mw.util.wikiGetlink() ersetzt, siehe Diskussionsseite). Sie kann sich erheblich von der aktuellen Version unterscheiden.

Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.

  • Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
  • Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
  • Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
/**
 * Säubert die Versionsvergleiche und entfernt Markierungen, wo sich nichts geändert hat. Siehe Diskussionsseite.
 */
(function($, mw)
{
	/* Quit as fast as possible if there is nothing to do. Doesn't use jQuery for performance reasons */
	if (!document.getElementsByClassName) return;
	var action = mw.config.get('wgAction');
	var live = mw.user.options.get('uselivepreview') && (action == 'edit' || action == 'submit');
	if (!live && document.getElementsByClassName('diff').length <= 0) return;

	/* Borders around single characters and sequences of whitespace, punctuation and other non-letters */
	var wsRegex = /^(?:.|[^\w\xC0-\xD6\xD8-\xF6\u00F8-\u02AF\u0370-\u1FFE\u2C00-\uFEFC]+)$/;
	var wsClass = 'diffchange-extra';
	mw.util.addCSS(mw.user.options.get('gadget-old-diff-style') ?
		'td.diff-deletedline .diffchange.' + wsClass + ', td.diff-addedline .diffchange.' + wsClass +
		'{ background: rgba(255, 0, 0, .1); border: 1px solid red; border-color: rgba(255, 0, 0, .5); border-radius: 2px; padding: 0 1px; }' :
		'.' + wsClass + ' { border: 0 solid #FFD366; border-width: 0 2px; }' +
		'.diff-addedline .' + wsClass + ' { border-color: #99CFFF; }');

	if (live) $(mw).on('LivePreviewDone', cleanDiff);
	cleanDiff();

	function cleanDiff()
	{
		var diffs = document.getElementsByClassName('diff');
		if (!diffs || diffs.length <= 0) return;
		var lines = diffs[0].getElementsByTagName('TR');
		if (!lines || lines.length < 3) return;
		cleanDisconnectedLines(lines);
		cleanChangedLines(lines);
		createLinks(lines);
	}

	function cleanDisconnectedLines(lines)
	{
		var moveUps = [], moveDowns = [], lastDels = [];
		/* Warning, don't pre-calculate the length. It changes due to deleted nodes */
		for (var i = 0; i < lines.length; i++)
		{
			var tr = lines[i];
			var dels = tr.getElementsByClassName('diff-deletedline');
			var adds = tr.getElementsByClassName('diff-addedline');
			var emps = tr.getElementsByClassName('diff-empty');
			var isDel    = dels.length == 1 && adds.length == 0 && emps.length == 1;
			var isAdd    = dels.length == 0 && adds.length == 1 && emps.length == 1;
			var isChange = dels.length == 1 && adds.length == 1 && emps.length == 0;

			/* Sonderfall, wenn angeblich eine Leerzeile durch eine gefüllte ersetzt
			   und danach eine gefüllte Zeile gelöscht wurde */
			if (moveUps.length > 0 && isDel && dels[0].firstChild)
			{
				/* Markierung der Löschung nachholen, die Ersetzung ist schon markiert */
				markAllChanged(dels[0]);
				var moveUp = moveUps.shift();
				var tds = moveUp.getElementsByTagName('TD');
				tr.appendChild(tds[2]);
				tr.appendChild(tds[2]);
				moveUp.appendChild(emps[0]);
				continue;
			}
			/* Angebliche Änderungen sammeln, wenn die linke Seite leer ist */
			else if (isChange && dels[0].firstChild.nodeType == 1 && !dels[0].firstChild.firstChild)
				moveUps.push(tr);
			else if (!isDel)
				moveUps = [];

			/* Sonderfall, wenn angeblich eine gefüllte durch eine Leerzeile ersetzt
			   und danach eine gefüllte Zeile eingefügt wurde */
			if (moveDowns.length > 0 && isAdd && adds[0].firstChild)
			{
				/* Markierung der Einfügung nachholen, die Ersetzung ist schon markiert */
				markAllChanged(adds[0]);
				var moveDown = moveDowns.shift();
				var tds = moveDown.getElementsByTagName('TD');
				tr.insertBefore(tds[1], tr.firstChild);
				tr.insertBefore(tds[0], tr.firstChild);
				moveDown.insertBefore(emps[0], moveDown.firstChild);
				continue;
			}
			/* Angebliche Änderungen sammeln, wenn die rechte Seite leer ist */
			else if (isChange && adds[0].firstChild.nodeType == 1 && !adds[0].firstChild.firstChild)
				moveDowns.push(tr);
			else if (!isAdd)
				moveDowns = [];

			var cons = tr.getElementsByClassName('diff-context');
			/* Einfügungen sowie leere Kontextzeilen nach oben schieben,
			   wenn zuvor passende Löschungen gefunden wurden */
			if (lastDels.length > 0 && (isAdd || (cons.length == 2 && !cons[0].firstChild && !cons[1].firstChild)))
			{
				var lastDel = lastDels.shift();

				/* Leere rechte Seite der Löschung wegwerfen */
				var tds = lastDel.getElementsByTagName('TD');
				while (tds.length > 2) lastDel.removeChild(tds[2]);

				var tds = tr.getElementsByTagName('TD');
				if (tds.length == 4)
				{
					/* Die beiden Hälften der Kontextzeile als Löschung und Einfügung markieren */
					tds[0].firstChild.data = '−';
					tds[1].className = 'diff-deletedline';
					tds[2].firstChild.data = '+';
					tds[3].className = 'diff-addedline';
					/* Rechte Hälfte der Kontextzeile nach oben zur vorher gefundenen Löschung schieben */
					lastDel.appendChild(tds[2]);
					lastDel.appendChild(tds[2]);
					/* Die unten verbliebene linke Hälfte der Kontextzeile vervollständigen */
					var td = document.createElement('TD');
					td.colSpan = 2;
					td.className = 'diff-empty';
					tr.appendChild(td);
				}
				else
				{
					/* Einfügung nach oben zur Löschung schieben */
					lastDel.appendChild(tds[1]);
					lastDel.appendChild(tds[1]);
					/* Leeren Rest der Einfügung wegwerfen */
					tr.parentNode.removeChild(tr);
					/* Korrektur, weil die iterierte NodeList soeben ihre Länge verändert hat */
					i--;
				}
				markAllChanged(lastDel);
			}
			else if (isDel)
				lastDels.push(tr);
			else
				lastDels = [];
		}
	}

	function markAllChanged(e)
	{
		/* Alles als Änderung markieren. Schrumpfung der Markierung passiert losgelöst davon sowieso noch */
		var divs = e.getElementsByTagName('DIV');
		for (var i = divs.length; i--; )
		{
			var div = divs[i];
			if (div.getElementsByTagName('SPAN').length > 0) continue;
			var span = document.createElement('SPAN');
			span.className = 'diffchange diffchange-inline';
			span.appendChild(div.firstChild);
			div.appendChild(span);
		}
	}

	function cleanChangedLines(lines)
	{
		for (var i = 0, length = lines.length; i < length; i++)
		{
			var del = lines[i].getElementsByClassName('diff-deletedline');
			var add = lines[i].getElementsByClassName('diff-addedline');
			if (del && add && del.length > 0 && add.length > 0)
			{
				var dels = del[0].getElementsByClassName('diffchange');
				var adds = add[0].getElementsByClassName('diffchange');
				/*
				 * Vor der Fehlerbehebung vom 20. Januar 2012 wurden solche
				 * Phantom-Änderungen durch Leerzeichen am Zeilenende ausgelöst.
				 */
				if (dels.length <= 0 && adds.length <= 0) cleanChangedLineEnding(del[0], add[0]);
				else cleanChangedLine(dels, adds);
				cleanWhitespaceChanges(dels);
				cleanWhitespaceChanges(adds);
			}
		}
	}

	function cleanChangedLine(dels, adds)
	{
		var l1 = Math.min(dels.length, adds.length), l2 = l1 + 1;
		for (var i = 0; i < l1; i++)
			if (cleanChange(dels[i], adds[i])) l2 = l1 - i;
		/* Zweite Suche nach zueinander passenden Änderungen vom Zeilenende */
		for (var i = 1; i < l2; i++)
			cleanChange(dels[dels.length - i], adds[adds.length - i]);
	}

	function cleanChange(d, a)
	{
		/* Keine Optimierung, wenn sich der Text davor/dahinter geändert hat, dann ist es eine Verschiebung */
		if (d.previousSibling && d.previousSibling.nodeType == 3 && a.previousSibling && a.previousSibling.nodeType == 3)
		{
			var t1 = d.previousSibling.data, t2 = a.previousSibling.data;
			var l = Math.min(t1.length, t2.length);
			if (t1.slice(t1.length - l) != t2.slice(t2.length - l)) return false;
		}
		if (d.nextSibling && d.nextSibling.nodeType == 3 && a.nextSibling && a.nextSibling.nodeType == 3)
		{
			var t1 = d.nextSibling.data, t2 = a.nextSibling.data;
			var l = Math.min(t1.length, t2.length);
			if (t1.slice(0, l) != t2.slice(0, l)) return false;
		}

		var t1 = d.firstChild.data, t2 = a.firstChild.data;
		/* Keine Optimierung versuchen, wenn beides gleich ist und nichts mehr übrig bleiben würde */
		if (t1 == t2) return true;

		var p1 = 0, p2 = 0, l = Math.min(t1.length, t2.length);
		while (p1 < l && t1.charAt(p1) == t2.charAt(p1)) p1++;
		l -= p1;
		while (p2 < l && t1.charAt(t1.length - 1 - p2) == t2.charAt(t2.length - 1 - p2)) p2++;
		/* Vorzeitig abbrechen, wenn keine Übereinstimmung gefunden wurde */
		if (p1 <= 0 && p2 <= 0) return false;

		if (p1 > 0)
		{
			d.parentNode.insertBefore(document.createTextNode(t1.substr(0, p1)), d);
			a.parentNode.insertBefore(document.createTextNode(t2.substr(0, p1)), a);
		}
		if (p2 > 0)
		{
			d.parentNode.insertBefore(document.createTextNode(t1.substr(t1.length - p2)), d.nextSibling);
			a.parentNode.insertBefore(document.createTextNode(t2.substr(t2.length - p2)), a.nextSibling);
		}
		d.firstChild.data = t1.substring(p1, t1.length - p2);
		a.firstChild.data = t2.substring(p1, t2.length - p2);
		return true;
	}

	function cleanWhitespaceChanges(a)
	{
		for (var i = 0, length = a.length; i < length; i++)
			if (wsRegex.test(a[i].firstChild.data)) a[i].className += ' ' + wsClass;
	}

	function cleanChangedLineEnding(del, add)
	{
		while (del && del.nodeType == 1) del = del.lastChild;
		while (add && add.nodeType == 1) add = add.lastChild;
		if (!del || !add || del.nodeType != 3 || add.nodeType != 3 || del.data == add.data) return;
		var p = 0, t1 = del.data, t2 = add.data, l = Math.min(t1.length, t2.length);
		while (p < l && t1.charAt(p) == t2.charAt(p)) p++;
		if (p > 0)
		{
			del.data = t1.substr(0, p);
			add.data = t2.substr(0, p);
			var span = document.createElement('SPAN');
			span.className = 'diffchange diffchange-inline ' + wsClass;
			span.appendChild(document.createTextNode(t1.substr(p).replace(/ /g, '\xA0')));
			if (span.firstChild.data.length > 0) del.parentNode.appendChild(span);
			span = span.cloneNode(true);
			span.firstChild.data = t2.substr(p).replace(/ /g, '\xA0');
			if (span.firstChild.data.length > 0) add.parentNode.appendChild(span);
		}
	}

	function createLinks(lines)
	{
		/* Die Funktion verbraucht drei Viertel der Zeit, deshalb bei langen Diffs abschalten */
		var limit = typeof window.cleanDiffLinkLimit != 'undefined' ? window.cleanDiffLinkLimit : 100;
		if (lines.length > limit) return;
		for (var i = lines.length; --i; )
			$('DIV', lines[i]).html(function(i, html)
			{
				return html.replace(/<[^>]*|(\[\[(?:<\/span>)?)((?:[:\w\xC0-\uD7FF]|<span[^>]*>[:\w\xC0-\uD7FF][^&<=>[\]{|}]*<\/span>)(?:[^&<=>[\]{|}]+|<span[^>]*>[^&<=>[\]{|}]*<\/span>)*)(?=[<\]|])/g,
					function($0, $1, $2)
					{
						return $2 ? $1 + '<a href="' + mw.util.wikiGetlink($2.replace(/<[^>]*>*/g, '')) +
							'">' + $2 + '</a>' : $0;
					});
			});
	}
})(jQuery, mediaWiki);