Order of the Stick Keyboard Navigation

By Tom W.M. Last update Mar 15, 2007 — Installed 186 times.
// ==UserScript==
// @name            Order of the Stick Keyboard Navigation
// @namespace       http://freecog.net/2007/
// @description     Adds keyboard shortcuts for navigating between strips.
// @include         http://www.giantitp.com/comics/*
// ==/UserScript==

//GM_setValue("debug", true);

var debug = GM_getValue("debug", false);
var unsafe_debug = GM_getValue("unsafe_debug", false);

var debug_queue = [];

function log() {
    if (debug && unsafe_debug) {
        // Pass debug messages to Firebug
        unsafeWindow.console.log.apply(unsafeWindow.console, arguments);
    } else if (debug) {
        // Pass to Greasemonkey's icky log function
        GM_log(Array.join(arguments, ' '));
    } else {
        // Push them onto the queue so that they can be 
        // spit out if debugging is enabled.
        debug_queue.push(Array.slice(arguments));
    }
};

function pump_debug_messages() {
    var msg;
    while ((msg = debug_queue.shift())) log.apply(null, msg);
}

GM_registerMenuCommand("Enable/Disable Debugging", function() {
    GM_setValue("debug", (debug = !debug));
    alert("Debugging has been " + (debug ? "enabled" : "disabled") + ".");
    pump_debug_messages();
});

GM_registerMenuCommand("Enable/Disable Firebug Debugging", function() {
    var msg = '';
    if (!debug && !unsafe_debug) {
        GM_setValue("debug", (debug = true));
        msg = "Debugging has been enabled.";
    }
    GM_setValue("unsafe_debug", (unsafe_debug = !unsafe_debug));
    alert("Firebug debugging has been " + (unsafe_debug ? "enabled" : "disabled") + ".");
    pump_debug_messages();
});

// Maps image link titles to keys under which to place the link 
// URL in the `urls` mapping.
var title_to_key = {
    "Next Comic": "next",
    "Previous Comic": "prev",
};

// Maps key ordinals to the key of the URL in the `urls` mapping.
var ord_to_key = {
    '110,f': "next", // n
    '98,f': "prev", // b
    '112,f': "prev", // p
    '44,f': "prev", // <
    '46,f': "next", // >
    '37,t': "prev", // CTRL + Left Arrow
    '39,t': "next", // CTRL + Right Arrow
};

var urls = {}; // "prev" and "next" keys -> URLs
var comic = null;

function is_comic_image(img) {
    // Older comics have a rational naming scheme
    if (img.src.match(/\/oots\d+\.(gif|png|jpe?g)/i)) return true;
    
    // Newer comics use jibberish URLs, e.g.
    // /comics/images/E4IVQ7hQwmd9fuZnPsj.gif
    // So we do some sniffing.
    if (img.getAttribute("src").match(/^\/comics\/images\/.*\.(gif|png|jpe?g)$/) && 
            img.parentNode.nodeName == 'TD' &&
            img.parentNode.getAttribute("align").match(/center/i)) {
        return true;
    }
    
    return false;
}


Array.slice(document.getElementsByTagName("img")).forEach(function(img) {
    // Get link URLs
    var title = img.title;
    var parent = img.parentNode;
    var is_link = (parent.nodeName == 'A' && parent.href);
    if (title in title_to_key && is_link) {
        urls[title_to_key[title]] = parent.href;
        log("Got URL for %s (%o).", title, img);
    }
    
    // Is this the comic?
    if (is_comic_image(img)) {
        comic = img;
        log("Found comic: %o", img);
    }
});

window.addEventListener("keypress", function(evt) {
    var ord = (evt.charCode || evt.keyCode) + ',' + (evt.ctrlKey ? 't' : 'f');
    log("Key event: %s %o", ord, evt.wrappedJSObject);
    if (ord_to_key[ord]) {
        document.location.href = urls[ord_to_key[ord]];
        evt.stopPropagation();
        evt.preventDefault();
    }
}, false);

// Scroll the comic to the top of the viewport
if (comic && GM_getValue("scroll_to_strip", true)) {
    window.addEventListener("load", function() {
        var top = 0, node = comic;
        do {
            top += node.offsetTop;
        } while ((node = node.offsetParent));
        log("Scroll top calculated: " + top);
        document.body.scrollTop = top;
    }, false);
}