├── README.md ├── hackem.js ├── hackemtimer.js ├── hackemup.css ├── hackemup.js └── index.html /README.md: -------------------------------------------------------------------------------- 1 | # Hack'em Up: A Hacker News bookmarklet 2 | 3 | Welcome to a Hacker News Bookmarklet... 4 | "Hack'em Up" by Mr Speaker 5 | v1.1 6 | 7 | Screen play: 8 | Drag bookmarklet to a tab that is opened to 9 | http://www.ycombinator.com/news 10 | Every 2 minutes the page will be refreshed and 11 | changes (to ranks, comments, votes, karma) 12 | will be highlighted. 13 | 14 | Written and directed by: 15 | var _ = mrspeaker 16 | twitter = @_ 17 | mail = _@gmail.com, 18 | tubes = http://_.net; 19 | 20 | Also staring: 21 | jQuery Bookmarklet - version 1.0 22 | Originally written by: Brett Barros 23 | With modifications by: Paul Irish 24 | 25 | Change Log: 26 | 1.1 27 | Refactored away some local state. 28 | General clean ups. 29 | 30 | -------------------------------------------------------------------------------- /hackem.js: -------------------------------------------------------------------------------- 1 | /* 2 | Welcome to a Hacker News Bookmarklet... 3 | "Hack'em Up" by Mr Speaker 4 | v1.1 5 | 6 | jQuery Bookmarklet loader/initialiser 7 | */ 8 | 9 | // Loadem Up! 10 | (function(opts){fullFunc(opts)})({ 11 | css : [hnuBase + "hackemup.css?v=2"], 12 | js : [ 13 | hnuBase + "hackemup.js", 14 | hnuBase + "hackemtimer.js" 15 | ], 16 | ready : function() { 17 | 18 | // Only works on the main page 19 | var loc = window.document.location; 20 | if(loc.hostname !== "news.ycombinator.com" || (loc.pathname !== "/" && loc.pathname !== "/news") ){ 21 | alert("Only works on Hacker News front page:\nhttp://news.ycombinator.com/"); 22 | return; 23 | }; 24 | 25 | // Start the show. 26 | hackemup.init(); 27 | hnutimer.init(function() { 28 | // When the timer's done... 29 | hackemup.fetch(); 30 | }); 31 | 32 | // Open all links in a new tab. 33 | // ... I don't usually like to do such a thing, but by public demand... 34 | $("body a").live("click", function(){ 35 | $(this).attr("target", "_blank"); 36 | }); 37 | } 38 | }); 39 | 40 | // jQuery bookmarklet magic... 41 | // ... by Brett Barros (& Paul Irish) 42 | // ... http://www.latentmotion.com/downloads/blank-bookmarklet-v1.js 43 | function fullFunc(a){function d(b){if(b.length===0){a.ready();return false} 44 | $.getScript(b[0],function(){d(b.slice(1))})}function e(b){$.each(b,function(c,f){$("") 45 | .attr({href:f,rel:"stylesheet",type:'text/css'}).appendTo("head")})}a.jqpath=a. 46 | jqpath||"https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"; 47 | (function(b){var c=document.createElement("script");c.type="text/javascript";c.src=b; 48 | c.onload=function(){e(a.css);d(a.js)};document.body.appendChild(c)})(a.jqpath)}; 49 | -------------------------------------------------------------------------------- /hackemtimer.js: -------------------------------------------------------------------------------- 1 | /* 2 | Welcome to a Hacker News Bookmarklet... 3 | "Hack'em Up" by Mr Speaker 4 | v1.1 5 | 6 | Timer module: Will only update when tab is focused. 7 | */ 8 | var hnutimer = { 9 | refreshTime: 2 * (60 * 1000), 10 | waitOnFocusTime: 1500, 11 | 12 | timerId: null, 13 | lastCheck: null, 14 | focusedTime: null, 15 | 16 | init: function(onTimerExpire) { 17 | $(window).bind({ 18 | "focus": function(){ hnutimer.onFocus(); }, 19 | "blur": function(){ hnutimer.onBlur(); } 20 | }); 21 | this.onTimerExpire = onTimerExpire; 22 | // Init onfocus to avoid first time load delay 23 | this.focusedTime = new Date().getTime() - this.waitOnFocusTime; 24 | this.update(); 25 | }, 26 | update: function() { 27 | var doFetch = true, 28 | refreshTime = this.refreshTime, 29 | previous = this.lastCheck, 30 | rightNow = new Date().getTime(), 31 | elapsed = previous ? rightNow - previous : refreshTime; 32 | 33 | if (elapsed < refreshTime) { 34 | doFetch = false; 35 | refreshTime -= elapsed; 36 | } 37 | 38 | if(doFetch) { 39 | if(rightNow - this.focusedTime >= this.waitOnFocusTime){ 40 | this.onTimerExpire && this.onTimerExpire(); 41 | this.lastCheck = rightNow; 42 | } else { 43 | refreshTime = this.waitOnFocusTime; 44 | } 45 | } 46 | this.timerId = setTimeout(function(){ hnutimer.update(); }, refreshTime); 47 | }, 48 | onFocus: function() { 49 | this.focusedTime = new Date().getTime(); 50 | this.update(); 51 | }, 52 | onBlur: function() { 53 | clearTimeout(this.timerId); 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /hackemup.css: -------------------------------------------------------------------------------- 1 | del.hnu { 2 | text-decoration: none; 3 | } 4 | .hnu { 5 | font-size:0.9em; 6 | background-color: #f60; 7 | } 8 | .hnu-col { 9 | min-width: 35px; 10 | } 11 | .hnu-up, .hnu-down, .hnu-new { 12 | -webkit-border-radius: 6px; 13 | -moz-border-radius: 6px; 14 | border-radius: 6px; 15 | padding: 1px 3px; 16 | color: #fff; 17 | float:left; 18 | margin: 3px 1px 0 2px; 19 | font-size: 0.6em; 20 | min-width: 6px; 21 | text-align: center; 22 | } 23 | .hnu-up { background-color: #86c444; } 24 | .hnu-down { background-color:#e76363; opacity: 0.6; } 25 | .hnu-votes { 26 | padding: 0 2px; 27 | color: #f60; 28 | background-color: transparent; 29 | } 30 | .hnu-karma { 31 | color: #f6f6f6; 32 | padding: 0 2px; 33 | font-size: 0.7em; 34 | } 35 | 36 | .hnu-spin { 37 | -o-transform: rotate(1440deg); 38 | -o-transition: all 6s linear; 39 | 40 | -moz-transform: rotate(1440deg); 41 | -moz-transition: all 6s linear; 42 | 43 | -webkit-transform: rotate(1440deg); 44 | -webkit-transition: all 6s linear; 45 | 46 | transform: rotate(1440deg); 47 | transition: transform 6s linear; 48 | } -------------------------------------------------------------------------------- /hackemup.js: -------------------------------------------------------------------------------- 1 | /* 2 | Welcome to a Hacker News Bookmarklet... 3 | "Hack'em Up" by Mr Speaker 4 | v1.1 5 | 6 | DOM wranglin' a go-go. 7 | */ 8 | var hackemup = { 9 | 10 | selecta: { 11 | body: "body table:first", 12 | logo: "body table:first table:first tbody tr:first td:first a:first img", 13 | firstColumn: "body > center > table tbody tr:eq(3) td > table > tbody > tr > td:first" 14 | }, 15 | 16 | init: function() { 17 | // Animate the first column 18 | $(this.selecta.firstColumn) 19 | .animate({ width: 35 }, 400); 20 | }, 21 | 22 | fetch: function(isRetry) { 23 | var _this = this, 24 | logo = $(this.selecta.logo).addClass("hnu-spin"); 25 | 26 | // Fetch the new HTML 27 | $("
").load("/ " + this.selecta.body, function(response, status) { 28 | logo.removeClass("hnu-spin"); 29 | 30 | if(status == "success") { 31 | _this.update($(response)); 32 | return; 33 | } 34 | 35 | // Retry once 36 | !isRetry && setTimeout(function(){ 37 | _this.fetch(true); 38 | }, 1500); 39 | return; 40 | }); 41 | }, 42 | 43 | update: function(fetched) { 44 | // Remove added changes from last round 45 | // (because we re-parse the doc. TODO: just store the last doc 46 | // then we don't have to be careful about how we add new elements) 47 | $(this.selecta.body).find(".hnu").remove(); 48 | 49 | // Extract some infoz 50 | var lastDoc = new hndoc($(this.selecta.body)), 51 | newDoc = new hndoc(fetched.children()), 52 | _this = this; 53 | 54 | // Replace the current page DOM with the latest DOM 55 | lastDoc.$.replaceWith(newDoc.$); 56 | 57 | // Stretch the first column 58 | $(this.selecta.firstColumn).addClass("hnu-col"); 59 | 60 | // Check if articles have changed 61 | newDoc 62 | .articleList 63 | .each(function() { 64 | var newVersion = this, 65 | oldVersion = lastDoc 66 | .articleList 67 | .filter(function(){ 68 | return newVersion.id === this.id; 69 | }); 70 | 71 | if(oldVersion.length) { 72 | _this.updateArticle(newVersion, oldVersion[0]); 73 | } 74 | else { 75 | _this.newArticle(newVersion); 76 | } 77 | }); 78 | 79 | // Hide runs of rises (probably means another story tanked) 80 | this.removeRuns(newDoc.$.find(".hnu-up")); 81 | this.removeRuns(newDoc.$.find(".hnu-down")); 82 | 83 | // Check if karma has changed 84 | if(newDoc.karma !== lastDoc.karma) { 85 | this.setKarma(newDoc, lastDoc.karma); 86 | } 87 | }, 88 | 89 | // Update the DOM to show last karma 90 | setKarma: function(doc, oldKarma) { 91 | var end = $(document.createTextNode(") | ")), 92 | old = $("93 | Keep track of what's changed on HackerNews main page since the last time you looked. 94 |
95 |
96 | 1▲ Show HN: This is what a rising article looks like
97 | 4▲ Netcraft confirms that this article is dying fast.
98 | +▲ New article released. Underneath you can see the updated stats.
99 |
Highlights and updates new articles, articles that are rising or falling fast (drop more than 3), as well as updating comment counts, 118 | points, and your karma. Updates every couple of minutes - but if you move to another tab (and leave HN open) no updates will happen until you get back.
119 |Directions 120 |
128 | More info at Mr Speaker's Hompage. 129 |
130 |131 |