Benutzer:TMg/cleanDiff.js

/**
 * 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' ),
		live = mw.user.options.get( 'uselivepreview' ) && ( action === 'edit' || action === 'submit' );
	if ( !live && !document.getElementsByClassName( 'diff' ).length ) {
		return;
	}

	/* Borders around single characters and sequences of whitespace, punctuation and other non-letters */
	var wsRegex = /[\wÀ-ÖØ-öø-\u02AF\u0370-\u1FFE\u2C00-\uFEFC]/,
		wsClass = 'diffchange-extra',
		wsCss = 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; }';

	mw.loader.using( 'mediawiki.util', function () {
		mw.util.addCSS( wsCss );
	} );

	mw.hook( 'wikipage.diff' ).add( function ( $diffs ) {
		if ( !$diffs.length ) {
			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],
				tds,
				dels = tr.getElementsByClassName( 'diff-deletedline' ),
				adds = tr.getElementsByClassName( 'diff-addedline' ),
				emps = tr.getElementsByClassName( 'diff-empty' ),
				isDel = dels.length === 1 && !adds.length && emps.length === 1,
				isAdd = !dels.length && adds.length === 1 && emps.length === 1,
				isChange = dels.length === 1 && adds.length === 1 && !emps.length;

			/* 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() ;
				tds = moveUp.getElementsByTagName( 'TD' );
				tr.appendChild( tds[2] );
				tr.appendChild( tds[2] );
				moveUp.appendChild( emps[0] );
				continue;
			} else if ( isChange && dels[0].firstChild.nodeType === 1 && !dels[0].firstChild.firstChild ) {
				/* Angebliche Änderungen sammeln, wenn die linke Seite leer ist */
				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();
				tds = moveDown.getElementsByTagName( 'TD' );
				tr.insertBefore( tds[1], tr.firstChild );
				tr.insertBefore( tds[0], tr.firstChild );
				moveDown.insertBefore( emps[0], moveDown.firstChild );
				continue;
			} else if ( isChange && adds[0].firstChild.nodeType === 1 && !adds[0].firstChild.firstChild ) {
				/* Angebliche Änderungen sammeln, wenn die rechte Seite leer ist */
				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 */
				tds = lastDel.getElementsByTagName( 'TD' );
				while ( tds.length > 2 ) {
					lastDel.removeChild( tds[2] );
				}

				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';
			/* Include possible <a> elements, e.g. because of [[User:Schnark/js/linkUnlinked.js]] */
			while ( div.firstChild ) 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' ),
				add = lines[i].getElementsByClassName( 'diff-addedline' );

			if ( del && add && del.length > 0 && add.length > 0 ) {
				var dels = del[0].getElementsByClassName( 'diffchange' ),
					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 && !adds.length ) {
					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 ( i = 1; i < l2; i++ ) {
			cleanChange( dels[dels.length - i], adds[adds.length - i] );
		}
	}

	function cleanChange( d, a ) {
		var e1, e2, t1, t2, l,
			p1 = 0,
			p2 = 0;

		/* 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 ) {
			t1 = d.previousSibling.data;
			t2 = a.previousSibling.data;
			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 ) {
			t1 = d.nextSibling.data;
			t2 = a.nextSibling.data;
			l = Math.min( t1.length, t2.length );
			if ( t1.slice( 0, l ) !== t2.slice( 0, l ) ) {
				return false;
			}
		}

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

		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 && !p2 ) {
			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 );
		}
		e1.data = t1.substring( p1, t1.length - p2 );
		e2.data = t2.substring( p1, t2.length - p2 );
		return true;
	}

	function cleanWhitespaceChanges( a ) {
		for ( var i = 0, length = a.length; i < length; i++ ) {
			var e = a[i].firstChild;
			if ( e.nodeType === 3 && ( e.data.length < 2 || !wsRegex.test( e.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 ) {
		/* Off by default because of a Firefox bug */
		var limit = typeof window.cleanDiffLinkLimit !== 'undefined' ? window.cleanDiffLinkLimit : -1;
		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>)*)(?=(?:<span[^>]*>)?[\]|])/g,
					function( $0, $1, $2 ) {
						return $2
							? $1 + '<a href="' + mw.util.getUrl( $2.replace( /<[^>]*>*/g, '' ) )
							+ '">' + $2 + '<\/a>'
							: $0;
					} );
			} );
		}
	}
} )( jQuery, mediaWiki );

Content Disclaimer

Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.

  1. The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
  2. There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
  3. It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
  4. Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
  5. Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.