// ==UserScript==
// @name        LifeHacker Category Viewer
// @namespace   http://blogs.sun.com/richb
// @description Generate a list of all LifeHacker posts by category
// @include     http://lifehacker.com/software/
// ==/UserScript==

// Version: 0.3 - 24th January 2008.
// Author:  Rich Burridge - Sun Microsystems Inc.
//          with lots of help from Tyler Trafford (thanks!)

if (typeof GM_xmlhttpRequest != 'function') {
    // do nothing
} else {
    if (window == top) {

        // Find the entries with the categories in it.
        var allCategories = document.evaluate(
            "//ul[@class='nobull']//*[@href]",
            document,
            null,
            XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
            null);

        GM_addStyle("body { text-align: center; }");
        GM_addStyle("h2 { margin: 10px 0px; }");

        // Clear out the original body of the web page.
        document.body = document.createElement("body");

        // Set up an event listener which runs whenever a div of some 
        // predefined class is added to the page.
        document.body.addEventListener("DOMNodeInserted", insert, false);

        // Add in the JavaScript that will expand a category.
        var scriptDiv = document.createElement("div");
        newHTML  = '<div>\n';
        newHTML += '</p>\n';
        newHTML += '<script type="text/javascript">\n';
        newHTML += '<!--\n';
        newHTML += 'function expandCategory(catName, link, text) {\n';
        newHTML += '    var existingDiv = document.getElementById(catName);\n';
        newHTML += '    existingDiv.innerHTML = ""\n';
        newHTML += '\n';
        newHTML += '    var newDiv = document.createElement("div");\n';
        newHTML += '    newDiv.id = \'cat\';\n';
        newHTML += '\n';
        newHTML += '    var newH2 = document.createElement("h2");\n';
        newHTML += '    newH2.innerHTML = \'<a href="\' + link + \'">\' + text + \'</a>\';\n';
        newHTML += '    newDiv.appendChild(newH2);\n';
        newHTML += '    existingDiv.appendChild(newDiv);\n';
        newHTML += '}\n';
        newHTML += '// -->\n';
        newHTML += '</script>\n';
        newHTML += '</div>\n';
        scriptDiv.innerHTML = newHTML;
        document.body.insertBefore(scriptDiv, document.body.firstChild);

        GM_log("allCategories.snapshotLength:");
        GM_log(allCategories.snapshotLength);
        for (var i = 0; i < allCategories.snapshotLength; i++) {
            thisLink = allCategories.snapshotItem(i);
            var categoryName = thisLink.textContent;

            // Get just the links that end in "/".
            if (thisLink.href[thisLink.href.length-1] == '/') {
                newDiv = document.createElement("div");
                newDiv.id = categoryName;
                newH2 = document.createElement("h2");
                var link = thisLink.href;
                newHTML  = '<a href="javascript:void(0);" onclick="expandCategory(\'' + categoryName + '\', \'' + link + '\', \'' + categoryName + '\');">' + categoryName + '</a>\n';

                newH2.innerHTML = newHTML;
                newDiv.appendChild(newH2);
                document.body.appendChild(newDiv);
            }
        }
    }
}

function insert(e) {
    GM_log("insert called.");
    var div = e.target;
    if (!div || !div.id || div.id != "cat") {
        return;
    }
    var href = document.evaluate(
        './/h2/a/@href',
        div,
        null,
        XPathResult.STRING_TYPE,
        null).stringValue;
    window.setTimeout(function() {
        processUrl(div, href);
    }, 0);
}

function processUrl(newDiv, pageUrl) {
    GM_xmlhttpRequest({
        method:'GET',
        url:pageUrl,
        onload:function(responseDetails){
            thisPage = document.createElement("div");
            thisPage.innerHTML = responseDetails.responseText

            // Find out the H2 headers on the page with an href attribute.
            var allEntries = document.evaluate(
            ".//h2",
            thisPage,
            null,
            XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
            null);

            var newHTML = newDiv.innerHTML + '<ul>\n';

            for (var k = 0; k < allEntries.snapshotLength; k++) {
                thisEntry = allEntries.snapshotItem(k);
                newHTML += '  <li>' + thisEntry.innerHTML + '</li>\n';
            }

            newHTML = newHTML + '</ul>\n';
            newDiv.innerHTML = newHTML;

            // Look for a "next »" link in the footer for the next page of
            // entries for this category. If found, process the new 
            // category sub-page, and append the generated HTML to the 
            // div element for this category.
            var footerAFields = document.evaluate(
                ".//div[@id='footer']/a",
                thisPage,
                null,
                XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
                null);

            if (footerAFields.snapshotLength == 1) {
                nextEntry =  footerAFields.snapshotItem(0);
                if (nextEntry.textContent == "next »") {
                    var catSubDiv = document.createElement("div");
                    newDiv.appendChild(catSubDiv);
                    processUrl(catSubDiv, nextEntry.href);
                }
            }
        }
    });
}

// ChangeLog
// 24th Jan 2008 - 0.3 - richb - From Tyler: adjusted to work with current
//                               LifeHacker web site layout and latest
//                               version of GreaseMonkey.
// 28th Nov 2007 - 0.2 - richb - Added dynamic category loading.
// 26th Nov 2007 - 0.1 - richb - Initial version.
