User:Teester/HoverDiff.js

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
var hovering = false;
var domElement;
var domElementName;
var hoverdiffPointerInTopHalf = false;

function getUrlParameter(url, sParam) {
	var sURLVariables = url.split('&');
	for (var i = 0; i < sURLVariables.length; i++) {
		var sParameterName = sURLVariables[i].split('=');
		if (sParameterName[0] === sParam) {
			return sParameterName[1] === undefined ? true : sParameterName[1];
		}
	}
}

function writeIntoDocument(data) {
	title = data.compare["*"];
	if (title.match("<!-- diff cache key")) {
		var indexLocation = title.match("<!-- diff cache key").index;
		title = title.substring(0, indexLocation) + "<tr><td colspan='4'>{{Reflist}}</td></tr>";
	}
	
	this.pageTitle = data.compare.totitle;
	title = title.replace(/&lt;/g, "<");
	title = title.replace(/&gt;/g," >");
	title = title.replace(/<!--.*-- >/g, "");
	title = title.replace(/<ref name=([\x00-\xFF]*?)\/ >/g, "");
	title = title.replace(/href="https:\/\//g, 'href="bibbles');
	title = title.replace(/href="http:\/\//g, 'href="bibble');
	title = encodeURIComponent(title);
	title = title.replace(/#/g, "%23");
	if (title.match("%3Cdiv%3E%5B%5BCategory%3A")) {
		parsed(title);
	} else {
		$.ajax({
			type: "POST",
			datatype: "json",
			url: "/w/api.php",
			data: "action=parse&disablelimitreport=true&title=" + this.pageTitle + "&format=json&disabletidy=true&contentmodel=wikitext&prop=text&text=" + title
		})
		.done(function( data ) {
			title = data.parse.text["*"];
			parsed(title);
		})
		.fail(function() {
			parsed(title);
		});
	}
}

function parsed(data) {	
	data = data.substring(29, data.length-6);
	try {
		title = decodeURIComponent(data);
	} catch (err) {
		title = data;
	}
	title = title.replace(/&lt;/g, "<");
	title = title.replace(/&gt;/g, ">");
	title = title.replace(/&amp;/g, "&");
	
	//Headings
	title = title.replace(/<div>====/g, "<div><span class=\"mw-headline\"><h4>");
	title = title.replace(/====<\/div>/g, "<\/div><\/span><\/h4>");
	title = title.replace(/<div>===/g, "<div><span class=\"mw-headline\"><h3>");
	title = title.replace(/===<\/div>/g, "<\/div><\/span><\/h3>");
	title = title.replace(/<div>==/g, "<div><span class=\"mw-headline\"><h2>");
	title = title.replace(/==<\/div>/g, "<\/div><\/span><\/h2>");
	
	//For a wikidata parsing bug
	title = title.replace(/bibbles/g, "http://");
	title = title.replace(/bibble/g, "https://");
	
	addPopupToPage(title);
}

function addPopupToPage(content) {

	if (this.hoverdiffPointerInTopHalf === true) {
		divClass = "hoverdiff-diff hoverdiff hoverdiff-fade-in-up hoverdiff-standard-y";
	} else {
		divClass = "hoverdiff-diff hoverdiff hoverdiff-fade-in-down hoverdiff-flipped-y";
	}

	stylesheet = getStylesheet();
	$('html > head').append("<style>"+stylesheet+"</style>");

	var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0"><defs><clippath id="hoverdiff-mask"><polygon points="0 8, 10 8, 18 0, 26 8, 1000 8, 1000 1000, 0 1000"/></clippath><clippath id="hoverdiff-mask-flip"><polygon points="0 8, 274 8, 282 0, 290 8, 1000 8, 1000 1000, 0 1000"/></clippath><clippath id="hoverdiff-landscape-mask"><polygon points="0 8, 174 8, 182 0, 190 8, 1000 8, 1000 1000, 0 1000"/></clippath><clippath id="hoverdiff-landscape-mask-flip"><polygon points="0 0, 1000 0, 1000 242, 190 242, 182 250, 174 242, 0 242"/></clippath></defs></svg>';
	if (this.hoverdiffPointerInTopHalf === true) {
		hoverdiffLocation = "top:" + (this.domElement.position().top + 25) + "px;left:" + this.domElement.position().left + "px;";
	} else {
		height = this.domElement.closest(this.domElementName).height();
		if (this.domElementName == ".mw-body") {
			height = height-50;
		}
		if (this.domElementName == "#mw-content-text") {
			height = height + 35;
		}
		hoverdiffLocation = "bottom:" + (height - this.domElement.position().top + 10) + "px;left:" + this.domElement.position().left + "px;";
	}
	html = "<div id='hoverdiff-svg'>" + svg + "</div><div class='" + divClass + "' role='tooltip' style='" + hoverdiffLocation + ";max-width:"+ window.innerWidth*0.7 +"px;max-height:300px;'><div class='hoverdiff-container'><div class='hoverdiff-extract' style='max-height:280px;overflow:auto;'><table class='hoverdiff-table'></table></div></div></div>" ;
	this.domElement.append(html);
	
	$(".hoverdiff-table").append(content);
	$(".diff-addedline div").css("background-color", "#7fd7c4"); 
	$(".diff-deletedline div").css("background-color", "#e88e89");
	scrollToChanges();
}

function getStylesheet() {
	
	var stylesheet = "@keyframes hoverdiff-fade-in-up {0% {opacity:0;transform:translate(0,20px)} 100% {opacity:1;transform:translate(0,0)}} ";
	stylesheet += "@keyframes hoverdiff-fade-out-down { 0% {opacity:1; transform:translate(0,0)} 100% { opacity:0; transform:translate(0,20px)}}";
	stylesheet += "@keyframes hoverdiff-fade-in-down {0% {opacity:0;transform:translate(0,-20px)} 100% {opacity:1;transform:translate(0,0)}} ";
	stylesheet += "@keyframes hoverdiff-fade-out-up { 0% {opacity:1; transform:translate(0,0)} 100% { opacity:0; transform:translate(0,-20px)}}";
	stylesheet += ".hoverdiff-fade-in-up { animation:hoverdiff-fade-in-up 0.2s ease forwards}";
	stylesheet += ".hoverdiff-fade-out-down { animation:hoverdiff-fade-out-down 0.2s ease forwards}";
	stylesheet += ".hoverdiff-fade-in-down { animation:hoverdiff-fade-in-down 0.2s ease forwards}";
	stylesheet += ".hoverdiff-fade-out-up { animation:hoverdiff-fade-out-up 0.2s ease forwards}";

	stylesheet += ".hoverdiff .hoverdiff-extract { margin:16px; display:block; color:#222222; text-decoration:none; position:relative;}";
	stylesheet += ".hoverdiff {background:#fff; position:absolute; z-index:110; box-shadow:0 30px 90px -20px rgba(0,0,0,0.3),0 0 1px #a2a9b1; padding:10px; font-size:14px; line-height:20px; min-width:300px;border-radius:2px;}";
	stylesheet += ".hoverdiff .hoverdiff-container {color:#222222; margin-top:-9px; padding-top:9px; text-decoration:none}";
	stylesheet += ".hoverdiff.hoverdiff-standard-y:before { content:''; position:absolute; border:8px solid transparent; border-top:0; border-bottom:8px solid #a2a9b1; top:-8px; left:10px}";
	stylesheet += ".hoverdiff.hoverdiff-standard-y:after { content:''; position:absolute; border:11px solid transparent; border-top:0; border-bottom:11px solid #ffffff; top:-7px; left:7px}";
	stylesheet += ".hoverdiff.hoverdiff-flipped-y:before { content:''; position:absolute; border:8px solid transparent; border-bottom:0; border-top:8px solid #a2a9b1; bottom:-8px; left:10px}";
	stylesheet += ".hoverdiff.hoverdiff-flipped-y:after { content:''; position:absolute; border:11px solid transparent; border-bottom:0; border-top:11px solid #ffffff; bottom:-7px; left:7px}";
	stylesheet += "#hoverdiff-svg {position:absolute;top:-1000px}";
	stylesheet += ".hoverdiff-table { table-layout: fixed; }";
	stylesheet += ".hoverdiff-table td { max-width:" + window.innerWidth*0.375 + "px; }";
	return stylesheet;
}

function scrollToChanges() {
	var container = $(".hoverdiff-extract");

	var diffChange = $(".diffchange").offset().top;
	if (diffChange === 0) { diffChange = 100000; }
	var diffAdded = $(".diff-addedline").offset().top;
	if (diffAdded === 0) { diffAdded = 100000; }
	var diffDeleted = $(".diff-deletedline").offset().top;
	if (diffDeleted === 0) { diffDeleted = 100000; }
	
	var scrollTo = Math.min(diffChange, diffAdded, diffDeleted);
	if (scrollTo == 100000) { scrollTo = 0; }
	
	container.animate({scrollTop: scrollTo - container.offset().top + container.scrollTop() - 25, scrollLeft: 0}, 0); 
}

function getDiff(url) {
	oldid = getUrlParameter(url, "oldid");
	diff = getUrlParameter(url, "diff");
	if (diff == "prev") {
		diff = "torelative=" + diff;
	} else {
		diff = "torev=" + diff;
	}
	$.ajax({
		datatype: "json",
		url: "/w/api.php?action=compare&frompst=true&topst=true&fromrev=" + oldid + "&" + diff + "&format=json"
	})
	.done(function( data ) {
		writeIntoDocument(data);
	})
	.fail(function() {
		console.log( 'The ajax request failed.' );
	});
}

$( document ).ajaxComplete(function (){
	$('.mw-changeslist-diff').hover( function(event){
		if ($(".mw-changeslist").length) {
			domElementName = ".mw-changeslist";
		} else {
			domElementName = "#mw-content-text";
		}
		domElement = $(this);
		if (event.originalEvent.clientY < window.innerHeight / 2) {
			hoverdiffPointerInTopHalf = true;
		} else {
			hoverdiffPointerInTopHalf = false;
		}
		var e = $(this);
		addDiff(e);
	}, 
	function() {
		removeDiff();
	});

	$('.mw-changeslist-groupdiff').hover( function(event){
		if ($(".mw-changeslist").length) {
			domElementName = ".mw-changeslist";
		} else {
			domElementName = "#mw-content-text";
		}
		domElement = $(this);
		if (event.originalEvent.clientY < window.innerHeight / 2) {
			hoverdiffPointerInTopHalf = true;
		} else {
			hoverdiffPointerInTopHalf = false;
		}
		var e = $(this);
		addDiff(e);
	}, 
	function() {
		removeDiff();
	});

	$('.hoverdiff-diff').hover( function(){
		domElementName = ".hoverdiff-diff";
		removeTimer();
	}, 
	function() {
		removeDiff();
	});
}); 

$('.mw-history-histlinks a').hover( function(event){
	domElementName = ".mw-body";
	domElement = $(this);
	if (event.originalEvent.clientY < window.innerHeight / 2) {
		hoverdiffPointerInTopHalf = true;
	} else {
		hoverdiffPointerInTopHalf = false;
	}
	var e = $(this);
	addDiff(e);
}, 
function() {
	removeDiff();
});

$('.mw-changeslist-diff').hover( function(event){
	if ($(".mw-changeslist").length) {
		domElementName = ".mw-changeslist";
	} else {
		domElementName = "#mw-content-text";
	}
	domElement = $(this);
	if (event.originalEvent.clientY < window.innerHeight / 2) {
		hoverdiffPointerInTopHalf = true;
	} else {
		hoverdiffPointerInTopHalf = false;
	}
	var e = $(this);
	addDiff(e);
}, 
function() {
	removeDiff();
});

$('.mw-changeslist-groupdiff').hover( function(event){
	if ($(".mw-changeslist").length) {
		domElementName = ".mw-changeslist";
	} else {
		domElementName = "#mw-content-text";
	}
	domElement = $(this);
	if (event.originalEvent.clientY < window.innerHeight / 2) {
		hoverdiffPointerInTopHalf = true;
	} else {
		hoverdiffPointerInTopHalf = false;
	}
	var e = $(this);
	addDiff(e);
}, 
function() {
	removeDiff();
});


function addDiff(e) {
	if (this.hovering === false) {
		if ($(".hoverdiff-diff").length === 0) {
			this.hovering = true;
			this.timer = setTimeout(function() {
				e.removeAttr("title");
				getDiff(e.attr('href')); 
			}, 250);
		}
	}
}

function removeTimer() {
	if (this.timer){
		this.hovering = false;
		clearTimeout(this.timer);
	}
}

function removeDiff() {
	removeTimer();
	this.timer = setTimeout(function() {
		$(".hoverdiff-diff").remove();
	}, 250);
}