Konami Code

I’m not sure why I keep making things difficult for myself; code that I wrote a year ago looks horribly convoluted now. Take the code I wrote a year ago to access a page by typing a word as a classic example. I thought it was great when I wrote it, but a bit more expierience and I can see the problems and how complex it was getting.

Important problems were:

  1. Script wasn’t self-contained, a lot of functionality was needed in the onload function
  2. Script could only re-direct the page and assumed that it was always supplied a valid URL
  3. Script was obtrusive, blindly replacing variables and handlers
  4. Relied on window.onload instead of DOMContentLoaded

I also missed the classic point: this is the Konami code and anyone who uses my script will want to be able to do anything they want when the correct sequence of keys has been typed.

Lets re-write this script in a much more concise and much easier to use. To start with, let’s wrap this in an annonymous function to make it easier to compress and a little faster:

(function(win) {

}(window));

Next, we’ll solve one of the problems we had with obtrusiveness with a simple addEvent function. I’m using Scott Andrew LePera’s addEvent function because of it’s simplicity and how little we need it for, even though it is considered harmful.

function addEvent(elem, evt, func) {
    if (elem.addEventListener) {
        elem.addEventListener(evt, func, false);
    } else if (elem.attachEvent) {
        elem.attachEvent('on' + evt, func);
    }
}

Now all we need to do is register the keys. To start, we’ll need the key registration function. It should accept a sequence of keys and a callback function. We should also do a degree of validating to prevent this function firing without everything it needs and create the index:

function registerCode(sequence, callback) {

    if ( !sequence || !callback ) { return; }

    var index = 0;

}

Now we need to check the keys being typed. In JavaScript, there are two ways to do this: either using the “onkeypress” event, or the “onkeyup” and “onkeydown” events. They look like they all do the same thing, but they don’t quite. The “onkeypress” event registers the key that was typed, making a distinction between upper and lowercase letters. By contrast, “onkeyup” and “onkeydown” register the button that was pressed making no distinction between upper and lowercase. They’re both useful in different circumstances, but for this script we don’t need to know the case, merely the button.

Detecting which key was typed requires slightly different code depending on whether or not you’re trying to use Internet Explorer (although I’ve heard rumours that IE9 uses W3C standards). Luckily that’s nothing more than a couple of lines of code.

    addEvent(win, 'keydown', function(e) {
        
        e = e || window.event;
        var keyCode = e.which || e.keyCode;
        
    });

The main functionality is the same as it was in my previous script: define an array of key codes and set an index to 0, increase the index if the key at position index of our array is the key that was pressed, re-set to 0 otherwise. If the index is the same as the array length, we know that the correct sequence of keys was typed as we can fire a callback function. Want to see the code that does that?

        if (keyCode == sequence[index]) {
            index += 1;
            
            if (index == sequence.length) {
                // callback to go here
            }
        
        } else {
            index = 0;
        }

Simple, huh? Firing the callback function is very easy, the following piece of code will do it: [code]callback();[/code]The only thing I don’t like about doing that is that in this case it will fail silently. If there is an error in the callback function, it simply won’t work, but won’t give many hints as to why. This is the perfect opportunity to use a try…catch statement, throwing a helpful error if the callback doesn’t work. At least something that lets the user know which callback isn’t working.

                try {
                    callback();
                } catch(err) {
                    throw new Error('Error in key-typing code for sequence ['
                        + sequence.join(',') + ']: ' + err.message);
                }

Finally, the key registration function needs exposing to the wider world.

if ( !win.type ) { win.type = registerCode; }

To use this function, all you need to so is call our new type function and define a callback. It’s possible to register multiple codes and callbacks:

type([38, 38, 40, 40, 37, 39, 37, 39, 66, 65], function() { alert('konami'); }); // Up, Up, Down, Down, Left, Right, Left, Right, B, A
type([72, 69, 76, 76, 79], function() { alert('world'); }); // H, E, L, L, O

If I’m honest, there are precious few reasons why you’d ever need this code, it’s more of a fun little easter egg than a serious script. But it can still be fun. It’s free to use for anyone who wants it. If you need the complete script, please find it below:

(function(win) {

// A very basic addEvent function
function addEvent(elem, evt, func) {
    if (elem.addEventListener) {
        elem.addEventListener(evt, func, false);
    } else if (elem.attachEvent) {
        elem.attachEvent('on' + evt, func);
    }
}

// The main bones of the opperation
function registerCode(sequence, callback) {

    // Quickly check to make sure we have what we need before continuing
    if ( !sequence || !callback ) { return; }
    
    // Make a reference to how far through the sequence we are
    var index = 0;
    
    // Add the function
    addEvent(win, 'keydown', function(e) {
        
        // Make allowances for IE's event registration and keycodes
        e = e || window.event;
        var keyCode = e.which || e.keyCode;
        
        // If the key pressed is the one in the sequence we want, increase the
        // index. Further down we'll reset the index if the code doesn't match
        if (keyCode == sequence[index]) {
            index += 1;
            
            // The index being the same as the sequence length means that it was
            // typed in correctly
            if (index == sequence.length) {
            
                // To stop a callback silently failing, we'll wrap it in a
                // try-catch method to expose any errors
                try {
                    callback();
                } catch(err) {
                    throw new Error('Error in key-typing code for sequence ['
                        + sequence.join(',') + ']: ' + err.message);
                }
            }
        
        // As I said earlier, if the code doesn't match we'll reset the index
        } else {
            index = 0;
        }
    });

}

// Expose the key typing code to the wider world but don't overwrite anything
if ( !win.type ) { win.type = registerCode; }

}(window));

Leave a Reply

Your email address will not be published. Required fields are marked *