/* Retired 2008 Jan 19 22:35:09 GMT */
/* http://greasemonkey.mozdev.org */
// ==UserScript==
// @name hystry comment tracker for news.yc
// @namespace http://akkartik.name/greasemonkey
// @description Highlight news.yc pages with new comments. Based on Waleed's bookmarklet at http://selfdebugging.com
// @include http://news.ycombinator.com/*
// ==/UserScript==
// http://www.selfdebugging.com/2007/10/09/yc-news-tracker-a-better-way-to-read-comments
// http://news.ycombinator.com/item?id=65307
/*
Copyright (c) 2007, Waleed Abdulla, Kartik Agaram. All rights reserved.
Code licensed under the MIT License:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
var tracker={};
tracker.divID = "divCommentTracker";
tracker.tracklistDivID = "tracklist";
tracker.title = function() {
var regTitle = /Hacker News \| *([^ ].*)/
var match = regTitle.exec(document.title)
if (match) return match[1];
else return "";
}
/* links on the current page => timestamp of newest comment in seconds. */
tracker.serverTimestamp = {}
tracker.updateDOM = function() {
var links = document.getElementsByTagName('a')
for (var i=0; i < links.length; ++i) {
if (links[i].innerHTML.search(/comments?|discuss/) == -1) continue
var link = links[i].href.replace(/http:\/\/news.ycombinator.com\//, '')
threadID = tracker.threadID(link)
cookieTime = tracker.cookieTimestamp(threadID)
if (!cookieTime) {
continue
}
cookieTime/=1000
var serverTime = tracker.serverTimestamp[link]
if (!serverTime) {
/* No recent comments. Bug if this page has comments and server
* crawler is in steady state. */
links[i].style.color = "#bbb"
continue
}
if (serverTime >= cookieTime) {
links[i].style.fontWeight="bold"
links[i].style.color = "#ff6600"
}
else {
links[i].style.color = "#bbb"
}
}
}
tracker.threadID = function(link) {
var regUrl = /\?id=(\d+)/;
var match = regUrl.exec(link);
if (match) {
return match[1]
}
return null
}
tracker.cookieTimestamp = function(threadID) {
if (!threadID) return null
var regCookie = new RegExp("TT"+threadID+"=(\\d+)")
var match = regCookie.exec(document.cookie)
if (match) return parseInt(match[1], 10)
return null
}
tracker.main = function()
{
xhr = new GM_xmlhttpRequest({
'method': 'get',
'url': 'http://hystry.com/newsyc/timestamps.cgi?'+window.location.href,
'onload': function(response) {
tracker.serverTimestamp = eval('('+response.responseText+')')
tracker.updateDOM()
tracker.decideToTrack()
}
})
}
tracker.decideToTrack = function() {
// Get thread id from the URL.
var threadID = tracker.threadID(window.location.href)
var elem = document.getElementsByTagName('td')
var topLevel = false
for (var i = 0; i < elem.length; ++i) {
if (elem[i].className == 'title') {
topLevel = true
break
}
}
if (topLevel) {
tracker.track(threadID)
}
}
tracker.track = function(threadID)
{
// Minutes since this thread was last visited. -1 => not visited before.
var minutesElapsed = -1
var now = new Date()
var newComments = 0
// Get the time when the bookmarklet was clicked last from the cookie (if available).
var cookieTime = tracker.cookieTimestamp(threadID)
if (cookieTime) {
var one_minute = 1000*60
minutesElapsed = Math.ceil((now.getTime() - cookieTime) / one_minute)
cookieTime/=1000
}
// Save the current time in the cookie.
var expire = new Date()
expire.setTime(now.getTime() + 1000 * 60 * 60 * 24 * 5) // 5 days
/*? if(minutesElapsed == -1) // For reproducible debugging ?*/
document.cookie = tracker.title()+"TT" + threadID + "=" + escape(now.getTime()) + ";path=/;expires=" + expire.toGMTString()
if (minutesElapsed == -1) {
tracker.message(threadID, "Started tracking.", "#9F3F00")
return
}
var link = window.location.href.replace(/http:\/\/news.ycombinator.com\//, '')
var serverTime = tracker.serverTimestamp[link]
if (
/* This should only ever happen in steady state if there are no
* comments. */
!serverTime
|| serverTime < cookieTime
) {
tracker.message(threadID, "Nothing new.", "#888");
return
}
// Find the comments that were added since last time.
var spans = document.getElementsByTagName("span");
var regClass = new RegExp("\\bcomhead\\b");
var regTime = /((\d+)\sminute[s]?\sago)|((\d+)\shour[s]?\sago)|((\d+)\sday[s]?\sago)/;
for (var i=0; i < spans.length; i++)
if(regClass.test(spans[i].className)) {
var match = regTime.exec(spans[i].innerHTML);
if (match) {
// Extract the time in minutes.
var minutes = parseInt("0" + match[2],10) + (60 * parseInt("0" + match[4],10)) + (24 * 60 * parseInt("0" + match[6],10));
if (minutes < minutesElapsed) {
spans[i].parentNode.style.fontWeight="bold";
newComments++;
}
}
}
tracker.message(threadID, newComments + " new comment(s).
", "#9F3F00");
}
tracker.tracklist = function() {
var cookies = document.cookie.split(";")
var uniq = new Array()
// This version disregards cookies created by previous versions without
// the title field.
//var regTitle = /([^ ].*)TT(\d+)=.*/;
var regTitle = /(.*)TT(\d+)=.*/;
for (var i=0; i < cookies.length; i++) {
var match = regTitle.exec(cookies[i])
if (match) {
if (!uniq[match[2]] || uniq[match[2]].length < cookies[i].length)
uniq[match[2]] = cookies[i]
}
}
var ans = "other pages tracked:"+
"