Reddit highlight newest comments

Highlights new comments in a thread since your last visit

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name           Reddit highlight newest comments
// @description    Highlights new comments in a thread since your last visit 
// @namespace      https://greatest.deepsurf.us/users/98-jonnyrobbie
// @author         JonnyRobbie
// @include        /https?:\/\/((www|pay|[a-z]{2})\.)?reddit\.com\/r\/[a-zA-Z0-9_-]+\/comments\/.*/
// @version        1.5.7
// ==/UserScript==

/*-----settings-----*/
highlightBGColorB = 'AECDFF'; //background color of a highlighted newest comment
highlightBGColorA = 'E5EFFF'; //background color of a highlighted older comment
highlightHalf = 2 //[hours]; when the algorithm should interpolate exactly 0.5 between A and B
highlightBGBorder = '1px solid #CDDAF3'; //border style of a highlighted new comment
expiration = 432000000; //expiraton time in millisesonds; default is 5 days (432000000ms)
highlihhtBetterChild = true; //highlight child comment if it has better karma than its parent
highlightNegative = true;
    betterChildStyle = '3px solid'; //border of said comment
    betterChildStyleGradientA = '99AAEE';
    betterChildStyleGradientB = 'F55220';
	betterChildStyleGradientC = 'ad3429';
/*-----settings-----*/

/*
Changelog:
1.5.7
-expand subreddit names
1.5.6
-fix double UI when viewing thread as a mod
1.5.5
-now works on subdomains
-now works in subreddits with alphanumeric characters
-some minor style changes
1.5.4
-fixed not working
1.5.3
-fixed a bog which has caused the script to hkald when the comment was too young to display score
1.5.2
-some more fugs resulting from reddit changes
1.5.1
-fixed reddit changes
1.4.2
-tweaked some colors and timing
1.4.1
-added highlighting comment with negative karma
-added color shading dependent on the new comment age
-added an option to manually edit the time of the last thread visit
1.3.1
-Added expiration for localstorage, defaulted to 14 days. It won't grow indefinately now.
-Reduced the amount of console.log clutter
*/

haveGold = false; //inicialization

function isNumber(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}


function hasClass(element, cls) {
    return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1;
}

function getThread() {
    console.log("Logging reddit comment highlight.");
    console.log("Fetching thread ID...");
    if (document.querySelector('[rel="shorturl"]') === null) {
        console.log("Not a comment thread. Aborting userscript...");
        return;
    }
	haveGold = hasClass(document.getElementsByTagName("body")[0], "gold") || hasClass(document.getElementsByTagName("body")[0], "moderatord");
	console.log("hasgold or is a mod: " + haveGold);
	purgeOldStorage(expiration);
    var threadID = "redd_id_" + document.querySelector('[rel="shorturl"]').href.substr(-6);
    console.log("Thread id: " + threadID);
    var lastVisit = localStorage.getItem(threadID);
    if (lastVisit === null) {
        console.log("Thread has not been visited yet. Creating localStorage...");
        localStorage.setItem(threadID, Date.parse(Date()));
    }
	else {
		var postMenu;
		postMenu = document.getElementById("siteTable").getElementsByClassName("flat-list")[0];
		if (!haveGold) {
			var timeDiff = Date.parse(Date()) - lastVisit;
			var goldBox = document.createElement("div");
				goldBox.className = "rounded gold-accent comment-visits-box";
				goldBox.style.marginLeft = "10px";
			var goldBoxTitle = document.createElement("div");
				goldBoxTitle.className = "title";
			var goldBoxLabel = document.createElement("span");
				goldBoxLabel.innerHTML = "Highlight comments posted since previous visit [hh:mm] ago:";
			var goldBoxInput = document.createElement("input");
				goldBoxInput.type = "text";
				goldBoxInput.style.margin = "auto 5px";
				goldBoxInput.style.width = "64px";
				goldBoxInput.id = "timeInput"
				goldBoxInput.value = padToTwo(Math.floor(timeDiff/(1000*3600))) + ":" + padToTwo(Math.floor(60*(timeDiff/(1000*3600) - Math.floor(timeDiff/(1000*3600)))));
			var goldBoxOK = document.createElement("input");
				goldBoxOK.type = "button";
				goldBoxOK.value = "OK";
				goldBoxOK.addEventListener("click", function(){timeSetBack(threadID, goldBoxInput);}, false)
				goldBox.appendChild(goldBoxTitle);
				goldBoxTitle.appendChild(goldBoxLabel);
				goldBoxTitle.appendChild(goldBoxInput);
				goldBoxTitle.appendChild(goldBoxOK);
			insertNodeAfter(goldBox, document.getElementsByClassName("commentarea")[0].getElementsByClassName("usertext")[0]);
		}
	}
	commentReg = /^https?:\/\/(www\.)reddit\.com\/r\/[a-zA-Z0-9_-]+\/comments\/[0-9a-z]+\/[0-9a-z_]+\/$/;
	isCommentPage = commentReg.test(document.URL);
    highlightComments(lastVisit, isCommentPage);
	console.log("Match for full comment page(" + document.URL + "): " + isCommentPage);
	if (isCommentPage == true) {
		console.log("Setting localStorage to now...");
		localStorage.setItem(threadID, Date.parse(Date()));
	} else
	{
		console.log("not a comment page")
	}
	permalinkReg = /^(https?:\/\/(www\.)reddit\.com\/r\/[a-zA-Z0-9_-]+\/comments\/[0-9a-z]+\/[0-9a-z_]+\/[0-9a-z]+)((\?context=([0-9])+)|\/)?$/;
	ispermalinkPage = permalinkReg.test(document.URL);
	if (ispermalinkPage == true) {
		var context = permalinkReg.exec(document.URL)[5];
		var nocontext = permalinkReg.exec(document.URL)[1];
		console.log("perma RegEx: " + nocontext)
		if (typeof context == 'undefined') {
			console.log("context not set");
			context = 0;
		}
		if (context > 5) {
			console.log("context greater than 5");
			context = 5;
		}
		console.log("permalink page...context " + context);
		addContext(context, permalinkReg.exec(document.URL)[1]);
	} else
	{
		console.log("not a permalink page");
	}
}

function addContext(context, nocontext) {
	var newSelect = document.createElement("select");
	var infobar = document.getElementsByClassName("commentarea")[0].getElementsByClassName("infobar")[0];
	newSelect.addEventListener("change", function(){window.location=nocontext + '?context=' + this.selectedIndex;}, false);
	var newOption;
	for (var c=0; c<=5; c++) {
		var newOption = document.createElement("option");
		if (c == context) {
			newOption.selected = "selected";
		}
		newOption.value = nocontext + "?context=" + c;
		newOption.innerHTML = "Context: " + c;
		newSelect.appendChild(newOption);
	}
	infobar.appendChild(newSelect);
}

function padToTwo(number) {
  if (number<=99) { number = ("0"+number).slice(-2); }
  return number;
}


function timeSetBack(threadID, textbox){
	console.log("setting time back");
	var timeBackArray = textbox.value.split(":")
	if (timeBackArray.length > 2) {
		alert("You have not entered a valid time");
		console.log("too many colons");
		return
	}
	var timeBack = parseInt(timeBackArray[0], 10) + (parseInt(timeBackArray[1], 10)/60)
	if (timeBack != null && isNumber(timeBack) == true) {
		var lastVisit = Date.parse(Date())-timeBack*3600000;
		if (lastVisit > localStorage.getItem(threadID)) alert("The time has been set lower than it was before. Please, refresh the page to properly display the changes.");
		localStorage.setItem(threadID, lastVisit);
		console.log("Setting localStorage " + threadID + " " + timeBack*3600000 + " ms back");
		highlightComments(lastVisit, true);
	}
}

function purgeOldStorage(difference) {
	var thisTime = Date.parse(Date());
	var total=0;
    for (var i=0;i<localStorage.length;i++) {
	    if (localStorage.key(i).substr(0,8)=="redd_id_" && parseInt(localStorage.getItem(localStorage.key(i)))+difference<thisTime) {
		    localStorage.removeItem(localStorage.key(i));
			total++;
			i=0;
		}
	}
	console.log(total + " localStorage older than " + difference + "ms has been removed");
}

function isProperThread() {
	return true;
}

function insertNodeAfter(node, sibling) {
	sibling.parentNode.insertBefore(node, sibling.nextSibling);
}

function highlightComments(lastVisit, isCommentPage) {
    console.log("Thread last visited in: " + lastVisit);
    comments = document.getElementsByClassName('comment');
    console.log("Comment count: " + comments.length);
	nowtime = Date.parse(Date());
	highlightHalf = Math.pow(0.5, (1/(-highlightHalf)));
    for(i=0; i<comments.length; i++) {
		if ((' ' + comments[i].className + ' ').indexOf(' deleted ') > -1) {continue;} //if the comment contains class 'deleted', skip this iteration
        var ctime = Date.parse(comments[i].childNodes[2].childNodes[0].getElementsByTagName('time')[0].getAttribute('title'));
		var scoreTag = comments[i].getElementsByClassName("tagline")[0].getElementsByClassName("unvoted");
		if (scoreTag.length > 0) {
		    scorechild = parseInt(scoreTag[0].innerHTML);
		} else {
		    scorechild = 0;
		}
		var scoreTag = comments[i].parentNode.parentNode.parentNode.getElementsByClassName("tagline")[0].getElementsByClassName("unvoted");
		if (scoreTag.length > 0) {
		    scoreparent = parseInt(scoreTag[0].innerHTML);
		} else {
		    scoreparent = 0;
		}
		var voted = comments[i].getElementsByClassName("midcol")[0].className;
		if (voted == "midcol likes") {
			scorechild++;
		} else if (voted == "midcol dislikes") {
			scorechild--;
		}
		var voted = comments[i].parentNode.parentNode.parentNode.getElementsByClassName("midcol")[0].className
		if (voted == "midcol likes") {
			scoreparent++;
		} else if (voted == "midcol dislikes") {
			scoreparent--;
		}
        if (parseInt(ctime) > parseInt(lastVisit) && haveGold == false) {
			usertextBody = comments[i].getElementsByClassName("usertext-body")[0];
			console.log("comment(" + i + "," + 0 + "): gradient(" + nowtime + "," + ctime + ") = " + getGradient(nowtime-ctime));
			usertextBody.style.backgroundColor = interpolateColor(highlightBGColorA, highlightBGColorB, getGradient(nowtime-ctime));
			usertextBody.style.sborder = highlightBGBorder;
        }
		
		
        if (scoreparent < scorechild && highlihhtBetterChild == true && comments[i].parentNode.parentNode.parentNode.className != "content") {
            comments[i].style.setProperty('border-left',betterChildStyle + " #" + betterChildStyleGradientA, 'important');
        }
		if (scorechild < 0 && highlightNegative == true) {
            comments[i].style.setProperty('border-left','1px solid #' + betterChildStyleGradientC, 'important');
        }
    }
	if (haveGold == true && isCommentPage == true) {highlightNew();}
	var goldSelect = document.getElementById("comment-visits");
	if (goldSelect != null) goldSelect.addEventListener("change", function(){console.log("changed highlighting");highlightNew();}, false);
	window.addEventListener("load", function(){console.log("window loaded, highlighting");highlightNew();}, false);
}

function highlightNew(){
	comments = document.getElementsByClassName("new-comment");
	console.log("gold " + comments.length + " new comments");
	nowtime = Date.parse(Date());
	highlightHalf = Math.pow(0.5, (1/(-highlightHalf)));
	for(i=0; i<comments.length; i++) {
		var titleDate;
		titleDate = comments[i].childNodes[2].childNodes[0].getElementsByTagName('time')[0].getAttribute('datetime')
		var ctime = Date.parse(titleDate);
		usertextBody = comments[i].getElementsByClassName("usertext-body")[0];
		console.log("comment(" + i + "," + 0 + "): gradient(" + nowtime + "," + ctime + ") = " + getGradient(nowtime-ctime));
		usertextBody.style.backgroundColor = interpolateColor(highlightBGColorA, highlightBGColorB, getGradient(nowtime-ctime));
		usertextBody.style.sborder = highlightBGBorder;
	}
}

function finalBGColor(ctime) {
	nowtime = Date.parse(Date());
	
}

function getGradient(time) {
	return Math.pow(highlightHalf, -(time/3600000));
}

function interpolateColor(minColor,maxColor,depth){
    function d2h(d) {return d.toString(16);}
    function h2d(h) {return parseInt(h,16);}
    if(depth == 0){
        return minColor;
    }
    if(depth == 1){
        return maxColor;
    }
    var color = "#";
        for(var i=0; i < 6; i+=2){
        var minVal = new Number(h2d(minColor.substr(i,2)));
        var maxVal = new Number(h2d(maxColor.substr(i,2)));
        var nVal = minVal + (maxVal-minVal) * depth;
        var val = d2h(Math.floor(nVal));
        while(val.length < 2){
            val = "0"+val;
        }
        color += val;
    }
    return color;
}

function formatDate(unixtime) {
    var t = new Date(unixtime);
    //return t.toString();
    return t.getDate() + "." + (t.getMonth()+1) + "." + t.getFullYear() + " " + (t.getUTCHours()+UTCDifference) + ":" + pad2(t.getMinutes()) + ":" + pad2(t.getSeconds()) + " " + timeZoneDesc;
}
function dateDifference(unixtime) {
    var diff = new Date();
    var unixtime2 = new Date(unixtime);
    var secDiff = (diff.getSeconds() - unixtime2.getSeconds());
    var minDiff = (diff.getMinutes() - unixtime2.getMinutes());
    var hourDiff = (diff.getHours() - unixtime2.getHours());
    var dayDiff = (diff.getDate() - unixtime2.getDate());
    var monthDiff = (diff.getMonth() - unixtime2.getMonth());
    var yearDiff = (diff.getFullYear() - unixtime2.getFullYear());
    var months = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    if (secDiff < 0) {
        minDiff--;
        secDiff += 60;
    }
    if (minDiff < 0) {
        hourDiff--;
        minDiff += 60;
    }
    if (hourDiff < 0) {
        dayDiff--;
        hourDiff += 24;
    }
    if (dayDiff < 0) {
        monthDiff--;
        dayDiff += months[diff.getMonth()-2]; // -2 as the months array is zero-indexed too
    }
    if (monthDiff < 0) {
        yearDiff--;
        monthDiff += 12;
    }
    //console.log(yearDiff+' years, '+monthDiff+' months, '+dayDiff+' days');
    return (yearDiff == 0 ? "" : yearDiff + " years ") + (monthDiff == 0 ? "" : monthDiff + " months ") + (dayDiff == 0 ? "" : dayDiff + " days ") + (hourDiff == 0 ? "" : hourDiff + " hours ") + (minDiff == 0 ? "" : minDiff + " minutes ") + (secDiff == 0 ? "" : secDiff + " seconds");
    //return yearDiff + " years " + monthDiff + " months " + dayDiff + " days " + hourDiff + " hours " + minDiff + " minutes " + secDiff + " seconds";
}
function addCss(cssCode) {
//thanks for the function from this blog http://www.tomhoppe.com/index.php/2008/03/dynamically-adding-css-through-javascript/
    var styleElement = document.createElement("style");
    styleElement.type = "text/css";
    if (styleElement.styleSheet) {
        styleElement.styleSheet.cssText = cssCode;
    } else {
        styleElement.appendChild(document.createTextNode(cssCode));
    }
    document.getElementsByTagName("head")[0].appendChild(styleElement);
}
function pad2(number) {
     return (number < 10 ? '0' : '') + number
}
getThread();