Skip to content

Instantly share code, notes, and snippets.

@kennethrapp
Created November 26, 2013 18:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kennethrapp/ccb392c399d72efd8b0d to your computer and use it in GitHub Desktop.
Save kennethrapp/ccb392c399d72efd8b0d to your computer and use it in GitHub Desktop.
Hacker News userscript that still doesn't entirely work
// ==UserScript==
// @name HackerNewsHacker
// @namespace kennethrapp1@gmail.com
// @description Hacker New Hacker Hacks Hacker News
// @include *news.ycombinator.com/*
// @version 1
// @grant none
// ==/UserScript==
//Returns true if it is a DOM node
function isNode(o){
return (
typeof Node === "object" ? o instanceof Node :
o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName==="string"
);
}
//Returns true if it is a DOM element
function isElement(o){
return (
typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2
o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName==="string"
);
}
function arrayContains(a, v) {
for(var i = 0; i < a.length; i++) {
if(a[i] === v){
return true;
}
}
return false;
}
function arrayRemove(arr) {
var what, a = arguments, L = a.length, ax;
while (L > 1 && arr.length) {
what = a[--L];
while ((ax= arr.indexOf(what)) !== -1) {
arr.splice(ax, 1);
}
}
return arr;
}
var LocalStorage = {
cache: {},
usage: function(){
var limit = 2000000;
var size = JSON.stringify( window.localStorage).length;
return parseFloat(((size/limit)*100).toPrecision(2)); // return usage
},
/* add a new value to an array in local storage. If the
array doesn't exist, create it when called. */
add: function(key, value){
if(window.localStorage.getItem(key) === null){
var obj =[value];
}
else if(obj = JSON.parse(window.localStorage.getItem(key))){
if(arrayContains(obj, value) === false){
obj.push(value);
}
}
window.localStorage.setItem(key, JSON.stringify(obj));
this.getAll();
},
/* remove a value from a group */
remove: function(key, value){
if(obj = window.localStorage.getItem(key)){
var json = JSON.parse(obj);
obj = arrayRemove(json, value);
window.localStorage.setItem(key, JSON.stringify(obj));
}
this.getAll();
},
get: function(key){
if((typeof this.cache == 'object') && (key in this.cache)){
//console.log("is cached");
return this.cache[key];
}
else if(obj = window.localStorage.getItem(key)){
//console.log("is not cached");
var json = JSON.parse(obj);
this.cache[key] = json;
return json;
}
},
getAll: function(){
var i = 0, //i is standing for int
oJson = {}, //o is standing for object
sKey; //s is standing for string
while ((sKey = window.localStorage.key(i))) {
oJson[sKey] = JSON.parse(window.localStorage.getItem(sKey));
i++;
}
this.cache = oJson;
return oJson;
}
}
var HNHacker = {}
HNHacker.Users = [];
HNHacker.Ignore = [];
HNHacker.States = {};
HNHacker.keys = {
"following" : "f",
"ignoring" : "i"
}
/* do something relative to the passed node and username, based on
the state, which will be one of the state keys. */
HNHacker.applyUserState = function(state, username, node){
// ignore user - hide their content
if(arrayContains(state, HNHacker.keys['ignoring'])){
var ignore = HNHacker.MakeElement("div", {
"id" : "ignore_"+username,
"class" : "dead"
}, "[ignored]");
var target = node.parentNode.parentNode.childNodes[3];
target.style.visibility = "hidden";
target.style.position = "absolute";
node.parentNode.parentNode.insertBefore(ignore, node.parentNode.parentNode.childNodes[2]);
}
}
// run a callback for nodes matching an xpath.
HNHacker.AffectDOMPath = function(expression, callback){
var result = document.evaluate(expression,document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
if(result.snapshotLength){
for(i = 0; i <= result.snapshotLength - 1; i++){
callback.call(this, result.snapshotItem(i));
}
}
}
/* build a generic html element. return the node.*/
HNHacker.MakeElement = function(tag, attrs, inner){
var element = document.createElement(tag);
if(typeof attrs == 'object'){
for(var a in attrs){
element.setAttribute(a, attrs[a]);
}
}
if(typeof inner == "string"){
var b = document.createTextNode(inner);
element.appendChild(b);
}
else if(isElement(inner)){
element.appendChild(inner);
}
return element;
}
/* build and return an html link element with a callback for the click event. easy peasy. */
HNHacker.BuildTextControl = function(textcontent, callback, args){
var control = this.MakeElement("a", {
"href" : "#",
"class" : "hn_top"
}, textcontent );
control.addEventListener('click', function(event){
event.preventDefault();
callback.apply(this, args);
}, false );
return control;
}
/* build a menu. obj is an xpath or node to which the menu will be appended.
menuItems is an array of arrays - each subarray looks like
[label, callback, [args]]
label is a string (the inner link text) for the control,
callback is a callback for the click event, and args is an
array of arguments to pass to the callback.
*/
HNHacker.BuildMenu = function(obj, menuItems){
/* hopefully now this works whether you send a node or xpath expression... */
if(isElement(obj)){
obj.appendChild(document.createTextNode(" [ "));
for (var i = menuItems.length - 1; i >= 0; i--) {
var control = this.BuildTextControl.apply(this, menuItems[ menuItems.length - (i+1) ]);
obj.appendChild(control);
if(i > 0){
obj.appendChild(document.createTextNode(" | "));
}
};
obj.appendChild(document.createTextNode(" ] "));
}
else{
this.AffectDOMPath(obj, function(item){
item.appendChild(document.createTextNode(" [ "));
for (var i = menuItems.length - 1; i >= 0; i--) {
var control = this.BuildTextControl.apply(this, menuItems[ menuItems.length - (i+1) ]);
item.appendChild(control);
if(i > 0){
item.appendChild(document.createTextNode(" | "));
}
};
item.appendChild(document.createTextNode(" ] "));
});
}
}
/********************************************** startup ***********************************************/
/* create the "ui panel" in the blank table row right after the hn menu.*/
HNHacker.AffectDOMPath("//center[1]/table[1]/tbody[1]/tr[2]", function(node){
var u = HNHacker.MakeElement("td", {
"id" : "HNHacker_UI",
"style" : "padding-top:0.5em;padding-bottom:0.5em"
});
node.appendChild(u);
});
HNHacker.BuildMenu('(//span[@class="pagetop"])[1]', [
// display the localstorage usage and localstorage object in the console
[" storage ", function(){
console.log(LocalStorage.usage());
console.log(LocalStorage.getAll());
}, [] ],
// DELETE EVERYTHING
[" clear ", function(){
window.localStorage.clear();
}, [] ],
// display following
[" following ", function(){
var amFollowing = LocalStorage.get(HNHacker.keys['following']);
for(i =0; i<amFollowing.length; i++){
// add each 'followed' user's profile link to the top menu
var u = HNHacker.MakeElement("a", {
"href" : "https://news.ycombinator.com/user?id="+encodeURIComponent(amFollowing[i])
}, " ["+encodeURIComponent(amFollowing[i])+"] ");
document.getElementById("HNHacker_UI").appendChild(u);
}
}, [] ]
]);
/* iterate each comment, get each username, generate the menu and apply
the state for that user. */
HNHacker.AffectDOMPath('//span[@class="comhead"]', function(item){
var links = item.getElementsByTagName("a");
var menu = [];
// count the links and get the user data
if(links.length > 0){
var user = links[0].href;
var userContext = user.split("=");
var userName = userContext[1];
if(arrayContains(this.Users, userName) === false){
var userState = LocalStorage.get(userName); // this doesn't cache.
if(typeof userState === 'object'){
HNHacker.applyUserState(userState, userName, item);
}
this.Users.push(userName); // keep a queue of username so we don't store multiples
/* build the menu items for each name.*/
HNHacker.BuildMenu(item, [
["ignore", function(i){
LocalStorage.add(i, HNHacker.keys['ignoring']);
HNHacker.applyUserState(HNHacker.keys['ignoring'], i, item);
}, [userName] ],
["unignore", function(i){
/* unignoring isn't added to applyUserState because
there will be nothing to 'apply' after a page
refresh, we just need to remove the ls element
and undo the restyling. */
LocalStorage.remove(i, HNHacker.keys['ignoring']);
var elem = document.getElementById("ignore_"+i);
elem.parentNode.removeChild(elem);
var target = item.parentNode.parentNode.childNodes[3];
target.style.visibility="visible";
target.style.position="relative";
}, [userName] ],
["follow", function(i){
LocalStorage.add(HNHacker.keys['following'], i);
//HNHacker.applyUserState(HNHacker.keys['following'], i, item);
}, [userName] ]
]);
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment