Kill those annoying threads (FireFox only)

gregorach

Bushcrafter (boy, I've got a lot to say!)
Sep 15, 2005
3,723
29
51
Edinburgh
I don't know about anybody else, but every now and then I would like to completely ignore the existence of a particular thread. Well, I now have a way! I have written (well, I've ripped-off somebody else's code and hacked it about to do what I want) a Greasemonkey script that will hide a thread from both the "New Posts" view and the normal forum view with a single click. At the moment, it won't hide a thread on the front page of the forum though.

Oh, I think it will work on the search results too... But I haven't tried it yet. ;)

This gets a bit technical, so bear with me...

Firstly, you'll need the Greasemonkey add-on for FireFox 3. Got it? Good.

Now, open Tools > Greasemonkey > New User Script...

Give it a name (say "BCUK KillFile"), a namespace (doesn't really matter what it is - "http://foo" should do OK), and a description.

Under "Includes (One per line)" enter: "http://www.bushcraftuk.com/forum/*"

Hit OK.

If you've used Greasemonkey before, it will now open your text editor. If you haven't, you'll need to set one - I suggest c:\windows\notepad.exe for convenience. (Assuming you're on Windows. If you're not, you probably don't need me to tell you what to do here.)

Then simply paste in the code below, and save it. From now on, whenever you view the "New Posts" list, or an individual forum, each thread title will have an [ignore] link next to it. Simply hit that to banish the thread (OK, you'll need to refresh the page first). You will also have an "Ignored Threads" option up in the menu bar (next to New Posts, Search, Log Out) - hit that to open a little window where you can see all the threads you're ignoring, remove a thread from the ignore list, or clear the list entirely.

I haven't tested this very thoroughly, and I have no real intention of supporting it properly, but I'm prepared to try and answer questions if need be. However, if it breaks your computer, it's your own lookout. I take no responsibility, express or implied, for anything at all. ;)

So, here's the code:

Code:
// ==UserScript==
// @name           vBulletin Kill Thread
// @namespace      http://killthread.gregorach.com/
// @description    Kill threads in vBulletin
// @include        http://www.bushcraftuk.com/forum/*
// ==/UserScript==

/*===================================================================================*\
| For vBulletin ???                                                                   |
| 2008 By Gregorach                                                                   |
| Ripped off from soemthing ripped off from Google Groups Killfile v3.5.1             |
|    2008 by Damian Penney, Tim & Jeff Ur                                             |
\*===================================================================================*/

// Start

var showing = false;

unsafeWindow.closeThreadthreadKillList = function() {
    var killDiv = document.getElementById("threadKillList");
    killDiv.style.visibility = "hidden";
}

unsafeWindow._ThreadReanimate = function(killspec) {
    window.setTimeout(Reanimate, 0, killspec);
}

unsafeWindow._ThreadKillFile_Set = function(killspec, title) {
    killspec = killspec.replace(/^\s+|\s+$/g, "");
    window.setTimeout(SetKillFile, 0, killspec, title);
}

unsafeWindow._Purge = function() {
    window.setTimeout(updateKillFile, 0, Array(), new Array());
    alert("KillFile Purged.\nRefresh page to see changes.");
}


function JavaScriptEscape(text) {
    if (!arguments.callee.sRE) {
        var specials = [
      '"', '\'', '\\'
    ];
        arguments.callee.sRE = new RegExp(
      '(\\' + specials.join('|\\') + ')', 'g'
    );
    }
    return escape(text.replace(arguments.callee.sRE, '\\$1'));
}

function QuoteEscape(text) {
    if (!arguments.callee.sRE) {
        var specials = [
      '\''
    ];
        arguments.callee.sRE = new RegExp(
      '(\\' + specials.join('|\\') + ')', 'g'
    );
    }
    return escape(text.replace(arguments.callee.sRE, '\\$1'));
}

function RegExpEscape(text) {
    if (!arguments.callee.sRE) {
        var specials = [
      '/', '.', '*', '+', '?', '|',
      '(', ')', '[', ']', '{', '}', '\\'
    ];
        arguments.callee.sRE = new RegExp(
      '(\\' + specials.join('|\\') + ')', 'g'
    );
    }
    return text.replace(arguments.callee.sRE, '\\$1');
}

function Reanimate(killspec) {
    var splitCh = String.fromCharCode(255);
    var data = "";
    var titleData = "";
    var list = new Array();
    var titleList = new Array();
    var newList = new Array();
    var newTitleList = new Array();
    var title = "";

    // initialize variables

    data = unescape(GM_getValue("ThreadKillFile", "-----"));
    list = data.split(splitCh);

    titleData = unescape(GM_getValue("ThreadTitleKillFile", "-----"));
    titleList = titleData.split(splitCh);

    for (var j = 0; j < list.length; j++) {
        if (list[j] != killspec) {
            newList.push(list[j]);
            newTitleList.push(titleList[j]);
        } else {
            title = titleList[j];
        }
    }
    updateKillFile(newList, newTitleList);
    alert('"' + title + '" has been removed from the KillFile.\nRefresh page to see changes.');
}

function SetKillFile(killspec, title) {
    var splitCh = String.fromCharCode(255);
    var data = "";
    var titleData = "";
    var list = new Array();
    var titleList = new Array();

    // initialize variables

    data = unescape(GM_getValue("ThreadKillFile", "-----"));
    list = data.split(splitCh);
    list.push(RegExpEscape(killspec));

    titleData = unescape(GM_getValue("ThreadTitleKillFile", "-----"));
    titleList = titleData.split(splitCh);
    titleList.push(title);

    updateKillFile(list, titleList);
    
    try {
        var targetLink = document.getElementById("thread_title_" + killspec);
        var wrappingTable = document.evaluate("ancestor::tr", targetLink, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        GM_log("stripping comment from thread view");

        var pNode = wrappingTable.snapshotItem(0);
        pNode.parentNode.removeChild(pNode);

        alert('"' + title + '" has been added to the KillFile.');
    } catch (ex) {
        alert('"' + title + '" has been added to the KillFile.\nRefresh page to see  changes.');
    }
}

unsafeWindow.ManageThreadKillFile = function() {
    var killDiv = document.getElementById("threadKillList");
    if (showing == false) {
        killDiv.style.visibility = "visible";
        showing = true;
    } else {
        killDiv.style.visibility = "hidden";
        showing = false;
    }
}

function GroupsBeta_Thread_List_View(list) {
    // Delete comments in thread comment view

    var threads = document.getElementById('threadslist');
    if (!threads) return;

    var candidates = document.evaluate(".//td[3]/div/a", threads, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

    var blocked = false;
    var threadLinkRE = new RegExp("showthread\.php.t=([0-9]+)")

    if (candidates.snapshotLength > 0) {
        for (var i = 0; i < candidates.snapshotLength; i++) {
            var cand = candidates.snapshotItem(i);
            if (threadLinkRE.test(cand.getAttribute("href"))) {
                var threadId = threadLinkRE.exec(cand.getAttribute("href"))[1];
                var title = cand.textContent;
                blocked = false;

                for (var j = 0; !blocked && j < list.length; j++) {

                    if (threadId == list[j]) {
                        wrappingTable = document.evaluate("ancestor::tr", cand, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
                        GM_log("stripping comment from thread view");

                        var pNode = wrappingTable.snapshotItem(0);
                        pNode.parentNode.removeChild(pNode);
                        blocked = true;
                    }
                }

                if (!blocked) {
                    var spacer = document.createTextNode(" ");
                    var link = document.createElement("a");
                    link.href = "javascript:_ThreadKillFile_Set('" + threadId + "', '" + QuoteEscape(title) + "');";

                    cand.parentNode.appendChild(spacer);
                    link.appendChild(document.createTextNode("[Ignore]"));

                    cand.parentNode.appendChild(link);
                    blocked = false;
                }
            }
        }
    }
}

function updateKillFile(list, titleList) {
    var splitCh = String.fromCharCode(255);
    data = list.join(splitCh);
    window.setTimeout(GM_setValue, 0, "ThreadKillFile", escape(data));

    titleData = titleList.join(splitCh);
    window.setTimeout(GM_setValue, 0, "ThreadTitleKillFile", escape(titleData));
    
    updateKillFileText(list, titleList);
}

function updateKillFileText(list, titleList) {
    var newDiv = document.getElementById("threadKillList");

    var divHtml = "<b>Ignored Thread List</b>";
    divHtml = divHtml + "<span style='float:right'><a href=\"javascript:closeThreadthreadKillList ();\">CLOSE</a></span>";
    divHtml += '<br/><br/>';
    divHtml += "X - remove from list<br/>"

    for (var j = 1; j < list.length; j++) {
        divHtml += "<a href=\"javascript:_ThreadReanimate('" + JavaScriptEscape(list[j]) + "');\">X</a> " 
	      + titleList[j] + "<br/>";
    }

    divHtml += "<br/><br/><a href=\"javascript:_Purge()\">Purge List</a>"
    + "<br/><br/><center><font size=0.5em>Brought to you by gregorach</font></center>"
    newDiv.innerHTML = divHtml;

    //document.getElementById('deleteOP').checked = GM_getValue('ThreadIgnoreListOptions', false);
}

function GroupsBeta_Run() {
    // Add the kill file menu option to the menu bar
    var splitCh = String.fromCharCode(255);
    var list = new Array();
    data = unescape(GM_getValue("ThreadKillFile", "-----"));
    list = data.split(splitCh);

    // get thread title list
    var titleList = new Array();
    titleData = unescape(GM_getValue("ThreadTitleKillFile", "-----"));
    titleData = titleData.split(splitCh);

    var titleView = false;

    // Determine what view mode we are in
    var candidates = document.evaluate("//DIV[@class='maincontboxhead']//td[2]//span//b", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    for (var i = 0; i < candidates.snapshotLength; i++) {
        var cand = candidates.snapshotItem(i);

        if (cand.textContent.indexOf("Topic list") >= 0) {
            titleView = true;
        }
    }

    var cand = document.getElementById('navbar_search');
    var candParent = cand.parentNode;
    //if (linkText.indexOf("Help") >= 0) {
    var newLink = document.createElement('a');
    newLink.setAttribute('href', 'javascript:ManageThreadKillFile()');
    var pipe = document.createTextNode(" | ");
    var linkText = document.createTextNode("Ignored Threads");

    var tdata = document.createElement('td');
    tdata.className = 'vbmenu_control';
    candParent.appendChild(tdata);
    tdata.appendChild(newLink);
    newLink.appendChild(linkText);

    // Creat the div that will show all the folks on the killfile
    var newDiv = document.createElement("div");
    newDiv.id = "allfolks";
    var divHtml = "The Morgue";
    newDiv.innerHTML = divHtml;

    cand.parentNode.insertBefore(newDiv, cand.nextSibling);

    newDiv.style.position = "absolute";
    newDiv.style.top = "10px";
    newDiv.style.left = "10px";
    newDiv.style.backgroundColor = "#fff";
    newDiv.style.fontSize = "12px";
    newDiv.style.fontFamily = "Verdana";
    newDiv.style.padding = "10px";
    newDiv.style.border = "solid 1px #000000";
    newDiv.style.visibility = "hidden";
    newDiv.id = "threadKillList";
    newDiv.style.zIndex = "300";
    newDiv.style.textAlign = 'left';
    updateKillFileText(list, titleData);

    GroupsBeta_Thread_List_View(list);
}

// Main routine
GM_log(location.href);
GroupsBeta_Run();
// End

[With thanks to whoever ripped off the code that I'm ripping off. I believe it originates with Damian Penney and Tim & Jeff Ur, who wrote the Google Groups KillFile. I dunno how many hands it's been through since. Sorry guys, but that's the internet for you. :D]
 

durulz

Need to contact Admin...
Jun 9, 2008
1,755
1
Elsewhere
Yeah. Wouldn't have just been easier to ignore the threads?
I kind of know what you mean though - sometimes something is so aggravating you just don't want to be reminded of it.
 

gregorach

Bushcrafter (boy, I've got a lot to say!)
Sep 15, 2005
3,723
29
51
Edinburgh
There are some things I find virtually impossible to ignore. Must be my ADD...

Besides, I've got sod-all to do at work today, I'm a programmer, and my JavaScript is rusty. Why not use the time to refresh my skillz and produce a feature that I've often wanted, both at the same time?

What's "easy" got to do with anything? ;)
 

Melonfish

Bushcrafter (boy, I've got a lot to say!)
Jan 8, 2009
2,460
2
Warrington, UK
liking it muchly i can see wide ranging forum uses!
does this work on the bushcraft bb system only though? what is bushcraft using?
 

gregorach

Bushcrafter (boy, I've got a lot to say!)
Sep 15, 2005
3,723
29
51
Edinburgh
It should work on pretty much anything that's using the standard vBulletin templates (at least, I think it's vBulletin - no idea which version we're running).

If you want to use it on other fora, all you should need to do is add the URL under "Includes". Although, be warned - it only matches on the thread ID, so you might find yourself missing threads on other fora which just happen to have the same ID as a thread you've ignored somewhere else. I'm not sure how Greasemonkey handles that sort of thing...
 

gregorach

Bushcrafter (boy, I've got a lot to say!)
Sep 15, 2005
3,723
29
51
Edinburgh
Hmmm... Looks like I've got a wee bug with the thread titles. Told you I didn't test it properly...

Now fixed in original code.
 

gregorach

Bushcrafter (boy, I've got a lot to say!)
Sep 15, 2005
3,723
29
51
Edinburgh
Not on your life. ;)

Melonfish - I've just checked the Greasemonkey docs, and it seems that it doesn't store preferences (such as the killfile that makes this work) separately by domain, so you would run into the problem of hidden threads on one forum also hiding threads with the same ID on other fora. To get around this, just create a new User Script (with a different name and namespace) for each site, using the same code.
 

Wayland

Hárbarðr
I would answer but I'm ignoring you all.....
10980.gif
 

gregorach

Bushcrafter (boy, I've got a lot to say!)
Sep 15, 2005
3,723
29
51
Edinburgh
I have just updated this slightly to make it more efficient, and to kill threads instantly, with no need to refresh the page.
 

Loenja

Settler
Apr 27, 2008
718
1
forest row
waking the dead her i know but,
i get a ignored thread list (blank) but no ignore this thread buttons
any help please
 

BCUK Shop

We have a a number of knives, T-Shirts and other items for sale.

SHOP HERE