Reddit enhancements

Adds a few comment highlighting features

当前为 2014-05-30 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Reddit enhancements
  3. // @description Adds a few comment highlighting features
  4. // @namespace https://greasyfork.org/users/98-jonnyrobbie
  5. // @author JonnyRobbie
  6. // @include /https?:\/\/(www\.)?reddit\.com\/r\/[a-zA-Z]+\/comments\/.*/
  7. // @version 1.5
  8. // ==/UserScript==
  9.  
  10. /*-----settings-----*/
  11. highlightBGColorB = '7CB9F7'; //background color of a highlighted newest comment
  12. highlightBGColorA = 'CEE3F7'; //background color of a highlighted older comment
  13. highlightHalf = 2 //[hours]; when the algorithm should interpolate exactly 0.5 between A and B
  14. highlightBGBorder = '1px solid #425466'; //border style of a highlighted new comment
  15. expiration = 432000000; //expiraton time in millisesonds; default is 5 days (432000000ms)
  16. highlihhtBetterChild = true; //highlight child comment if it has better karma than its parent
  17. betterChildStyleGradientA = '99AAEE';
  18. betterChildStyle = '3px solid'; //border of said comment
  19. highlightNegative = true; //highlight a comment if it has negative karma
  20. betterChildStyleGradientC = 'ad3429';
  21. /*-----settings-----*/
  22.  
  23. /*
  24. Changelog:
  25. 1.4.2
  26. -tweaked some colors and timing
  27. 1.4.1
  28. -added highlighting comment with negative karma
  29. -added color shading dependent on the new comment age
  30. -added an option to manually edit the time of the last thread visit
  31. 1.3.1
  32. -Added expiration for localstorage, defaulted to 14 days. It won't grow indefinately now.
  33. -Reduced the amount of console.log clutter
  34. */
  35.  
  36. haveGold = false; //inicialization
  37.  
  38. function isNumber(n) {
  39. return !isNaN(parseFloat(n)) && isFinite(n);
  40. }
  41.  
  42.  
  43. function hasClass(element, cls) {
  44. return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1;
  45. }
  46.  
  47. function getThread() {
  48. console.log("Logging reddit comment highlight.");
  49. console.log("Fetching thread ID...");
  50. if (document.querySelector('[rel="shorturl"]') === null) {
  51. console.log("Not a comment thread. Aborting userscript...");
  52. return;
  53. }
  54. haveGold = hasClass(document.getElementsByTagName("body")[0], "gold");
  55. console.log("hasgold: " + haveGold);
  56. purgeOldStorage(expiration);
  57. var threadID = "redd_id_" + document.querySelector('[rel="shorturl"]').href.substr(-6);
  58. console.log("Thread id: " + threadID);
  59. var lastVisit = localStorage.getItem(threadID);
  60. if (lastVisit === null) {
  61. console.log("Thread has not been visited yet. Creating localStorage...");
  62. localStorage.setItem(threadID, Date.parse(Date()));
  63. }
  64. else {
  65. var postMenu;
  66. postMenu = document.getElementById("siteTable").getElementsByClassName("flat-list")[0];
  67. if (!haveGold) {
  68. /*var editTime = document.createElement("li");
  69. editTime.innerHTML = "<a>change highlight time</a>";
  70. editTime.onclick = function(){timeSetBack(threadID);}
  71. postMenu.appendChild(editTime);*/
  72. var timeDiff = Date.parse(Date()) - lastVisit;
  73. var goldBox = document.createElement("div");
  74. goldBox.className = "rounded gold-accent comment-visits-box";
  75. var goldBoxTitle = document.createElement("div");
  76. goldBoxTitle.className = "title";
  77. var goldBoxLabel = document.createElement("span");
  78. goldBoxLabel.innerHTML = "Highlight comments posted since previous visit [hh:mm] ago:";
  79. var goldBoxInput = document.createElement("input");
  80. goldBoxInput.type = "text";
  81. goldBoxInput.style.margin = "auto 5px";
  82. goldBoxInput.style.width = "64px";
  83. goldBoxInput.id = "timeInput"
  84. goldBoxInput.value = padToTwo(Math.floor(timeDiff/(1000*3600))) + ":" + padToTwo(Math.floor(60*(timeDiff/(1000*3600) - Math.floor(timeDiff/(1000*3600)))));
  85. var goldBoxOK = document.createElement("input");
  86. goldBoxOK.type = "button";
  87. goldBoxOK.value = "OK";
  88. goldBoxOK.addEventListener("click", function(){timeSetBack(threadID, goldBoxInput);}, false)
  89. goldBox.appendChild(goldBoxTitle);
  90. goldBoxTitle.appendChild(goldBoxLabel);
  91. goldBoxTitle.appendChild(goldBoxInput);
  92. goldBoxTitle.appendChild(goldBoxOK);
  93. insertNodeAfter(goldBox, document.getElementsByClassName("commentarea")[0].getElementsByClassName("usertext")[0]);
  94. }
  95. }
  96. /*var reHigh = document.createElement("li");
  97. reHigh.innerHTML = "<a>reHigh</a>";
  98. reHigh.onclick = function(){highlightNew();}
  99. postMenu.appendChild(reHigh);*/
  100. commentReg = /^https?:\/\/(www\.)reddit\.com\/r\/[a-zA-Z]+\/comments\/[0-9a-z]+\/[0-9a-z_]+\/$/;
  101. isCommentPage = commentReg.test(document.URL);
  102. highlightComments(lastVisit, isCommentPage);
  103. //if (true) {
  104. console.log("Match for full comment page(" + document.URL + "): " + isCommentPage);
  105. if (isCommentPage == true) {
  106. console.log("Setting localStorage to now...");
  107. localStorage.setItem(threadID, Date.parse(Date()));
  108. } else
  109. {
  110. console.log("not a comment page")
  111. }
  112. //}
  113. //highlightBetter();
  114. }
  115.  
  116. function padToTwo(number) {
  117. if (number<=99) { number = ("0"+number).slice(-2); }
  118. return number;
  119. }
  120.  
  121.  
  122. function timeSetBack(threadID, textbox){
  123. //var timeBack = prompt("How many hours do you want to set the thread timestamp back?", "0");
  124. console.log("setting time back");
  125. var timeBackArray = textbox.value.split(":")
  126. if (timeBackArray.length > 2) {
  127. alert("You have not entered a valid time");
  128. console.log("too many colons");
  129. return
  130. }
  131. var timeBack = parseInt(timeBackArray[0], 10) + (parseInt(timeBackArray[1], 10)/60)
  132. if (timeBack != null && isNumber(timeBack) == true) {
  133. var lastVisit = Date.parse(Date())-timeBack*3600000;
  134. 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.");
  135. localStorage.setItem(threadID, lastVisit);
  136. console.log("Setting localStorage " + threadID + " " + timeBack*3600000 + " ms back");
  137. highlightComments(lastVisit, true);
  138. }
  139. }
  140.  
  141. function purgeOldStorage(difference) {
  142. var thisTime = Date.parse(Date());
  143. var total=0;
  144. for (var i=0;i<localStorage.length;i++) {
  145. if (localStorage.key(i).substr(0,8)=="redd_id_" && parseInt(localStorage.getItem(localStorage.key(i)))+difference<thisTime) {
  146. localStorage.removeItem(localStorage.key(i));
  147. total++;
  148. i=0;
  149. }
  150. }
  151. console.log(total + " localStorage older than " + difference + "ms has been removed");
  152. }
  153.  
  154. function isProperThread() {
  155. return true;
  156. }
  157.  
  158. function insertNodeAfter(node, sibling) {
  159. sibling.parentNode.insertBefore(node, sibling.nextSibling);
  160. }
  161.  
  162. function highlightComments(lastVisit, isCommentPage) {
  163. console.log("Thread last visited in: " + lastVisit);
  164. comments = document.getElementsByClassName('comment');
  165. console.log("Comment count: " + comments.length);
  166. nowtime = Date.parse(Date());
  167. highlightHalf = Math.pow(0.5, (1/(-highlightHalf)));
  168. for(i=0; i<comments.length; i++) {
  169. if ((' ' + comments[i].className + ' ').indexOf(' deleted ') > -1) {continue;} //if the comment contains class 'deleted', skip this iteration
  170. var ctime = Date.parse(comments[i].childNodes[2].childNodes[0].getElementsByTagName('time')[0].getAttribute('title'));
  171. var scorechild = parseInt(comments[i].getAttribute('data-ups')) - parseInt(comments[i].getAttribute('data-downs'));
  172. var scoreparent = parseInt(comments[i].parentNode.parentNode.parentNode.getAttribute('data-ups')) - parseInt(comments[i].parentNode.parentNode.parentNode.getAttribute('data-downs'));
  173. if (parseInt(ctime) > parseInt(lastVisit) && haveGold == false) {
  174. //console.log("Highlighting comment...");
  175. /*usertextBody = comments[i].getElementsByClassName("usertext-body");
  176. for (var j = 0; j < usertextBody.length; j++) {
  177. usertextBody[j].style.setProperty('background-color', interpolateColor(highlightBGColorA, highlightBGColorB, getGradient(nowtime-ctime)), '!important');
  178. usertextBody[j].style.setProperty('border',highlightBGBorder , '!important');
  179. }*/
  180. usertextBody = comments[i].getElementsByClassName("usertext-body")[0];
  181. //for (var j = 0; j < usertextBody.length; j++) {
  182. //console.log(usertextBody.length);
  183. console.log("comment(" + i + "," + 0 + "): gradient(" + nowtime + "," + ctime + ") = " + getGradient(nowtime-ctime));
  184. usertextBody.style.backgroundColor = interpolateColor(highlightBGColorA, highlightBGColorB, getGradient(nowtime-ctime));
  185. usertextBody.style.sborder = highlightBGBorder;
  186. }
  187. if (scoreparent < scorechild && highlihhtBetterChild == true) {
  188. //console.log("Highlighting better comment " + i + "...");
  189. //console.log(betterChildStyle + " " + getGradient(scoreparent, scorechild));
  190. //comments[i].style.setProperty('border',betterChildStyle + " " + getGradient(scoreparent, scorechild), 'important');
  191. comments[i].style.setProperty('border-left',betterChildStyle + " #" + betterChildStyleGradientA, 'important');
  192. }
  193. if (scorechild < 0 && highlightNegative == true) {
  194. //console.log("Highlighting better comment " + i + "...");
  195. //console.log(betterChildStyle + " " + getGradient(scoreparent, scorechild));
  196. //comments[i].style.setProperty('border',betterChildStyle + " " + getGradient(scoreparent, scorechild), 'important');
  197. comments[i].style.setProperty('border-left','1px solid #' + betterChildStyleGradientC, 'important');
  198. }
  199. }
  200. if (haveGold == true && isCommentPage == true) {highlightNew();}
  201. var goldSelect = document.getElementById("comment-visits");
  202. if (goldSelect != null) goldSelect.addEventListener("change", function(){console.log("changed highlighting");highlightNew();;}, false);
  203. window.addEventListener("load", function(){console.log("window loaded, highlighting");highlightNew();;}, false)
  204. }
  205.  
  206. function highlightNew(){
  207. //console.log("has gold, highligting new...");
  208. comments = document.getElementsByClassName("new-comment");
  209. console.log("gold " + comments.length + " new comments");
  210. nowtime = Date.parse(Date());
  211. highlightHalf = Math.pow(0.5, (1/(-highlightHalf)));
  212. for(i=0; i<comments.length; i++) {
  213. var titleDate;
  214. titleDate = comments[i].childNodes[2].childNodes[0].getElementsByTagName('time')[0].getAttribute('datetime')
  215. //console.log(titleDate);
  216. var ctime = Date.parse(titleDate); //current break???WTF
  217. //console.log("Highlighting comment..." + ctime);
  218. usertextBody = comments[i].getElementsByClassName("usertext-body")[0];
  219. //for (var j = 0; j < usertextBody.length; j++) {
  220. //console.log(usertextBody.length);
  221. console.log("comment(" + i + "," + 0 + "): gradient(" + nowtime + "," + ctime + ") = " + getGradient(nowtime-ctime));
  222. usertextBody.style.backgroundColor = interpolateColor(highlightBGColorA, highlightBGColorB, getGradient(nowtime-ctime));
  223. usertextBody.style.sborder = highlightBGBorder;
  224. //usertextBody.style.color = "#FF0000";
  225. //}
  226. }
  227. }
  228.  
  229. function finalBGColor(ctime) {
  230. nowtime = Date.parse(Date());
  231. }
  232.  
  233. function getGradient(time) {
  234. return Math.pow(highlightHalf, -(time/3600000));
  235. }
  236.  
  237. function interpolateColor(minColor,maxColor,depth){
  238. function d2h(d) {return d.toString(16);}
  239. function h2d(h) {return parseInt(h,16);}
  240. if(depth == 0){
  241. return minColor;
  242. }
  243. if(depth == 1){
  244. return maxColor;
  245. }
  246. var color = "#";
  247. for(var i=0; i < 6; i+=2){
  248. var minVal = new Number(h2d(minColor.substr(i,2)));
  249. var maxVal = new Number(h2d(maxColor.substr(i,2)));
  250. var nVal = minVal + (maxVal-minVal) * depth;
  251. var val = d2h(Math.floor(nVal));
  252. while(val.length < 2){
  253. val = "0"+val;
  254. }
  255. color += val;
  256. }
  257. return color;
  258. }
  259.  
  260. function formatDate(unixtime) {
  261. var t = new Date(unixtime);
  262. //return t.toString();
  263. return t.getDate() + "." + (t.getMonth()+1) + "." + t.getFullYear() + " " + (t.getUTCHours()+UTCDifference) + ":" + pad2(t.getMinutes()) + ":" + pad2(t.getSeconds()) + " " + timeZoneDesc;
  264. }
  265. function dateDifference(unixtime) {
  266. var diff = new Date();
  267. var unixtime2 = new Date(unixtime);
  268. //var date1 = new Date(),
  269. //date2 = new Date(1981, 11, 18);
  270. //order = (date2.getTime() - date1.getTime()) < 0 ? -1 : 1; // work out which is the later date
  271. var secDiff = (diff.getSeconds() - unixtime2.getSeconds());
  272. var minDiff = (diff.getMinutes() - unixtime2.getMinutes());
  273. var hourDiff = (diff.getHours() - unixtime2.getHours());
  274. var dayDiff = (diff.getDate() - unixtime2.getDate());
  275. var monthDiff = (diff.getMonth() - unixtime2.getMonth());
  276. var yearDiff = (diff.getFullYear() - unixtime2.getFullYear());
  277. var months = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  278. if (secDiff < 0) {
  279. minDiff--;
  280. secDiff += 60;
  281. }
  282. if (minDiff < 0) {
  283. hourDiff--;
  284. minDiff += 60;
  285. }
  286. if (hourDiff < 0) {
  287. dayDiff--;
  288. hourDiff += 24;
  289. }
  290. if (dayDiff < 0) {
  291. monthDiff--;
  292. dayDiff += months[diff.getMonth()-2]; // -2 as the months array is zero-indexed too
  293. }
  294. if (monthDiff < 0) {
  295. yearDiff--;
  296. monthDiff += 12;
  297. }
  298. //console.log(yearDiff+' years, '+monthDiff+' months, '+dayDiff+' days');
  299. 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");
  300. //return yearDiff + " years " + monthDiff + " months " + dayDiff + " days " + hourDiff + " hours " + minDiff + " minutes " + secDiff + " seconds";
  301. }
  302. function addCss(cssCode) {
  303. //thanks for the function from this blog http://www.tomhoppe.com/index.php/2008/03/dynamically-adding-css-through-javascript/
  304. var styleElement = document.createElement("style");
  305. styleElement.type = "text/css";
  306. if (styleElement.styleSheet) {
  307. styleElement.styleSheet.cssText = cssCode;
  308. } else {
  309. styleElement.appendChild(document.createTextNode(cssCode));
  310. }
  311. document.getElementsByTagName("head")[0].appendChild(styleElement);
  312. }
  313. function pad2(number) {
  314. return (number < 10 ? '0' : '') + number
  315. }
  316. getThread();