jQuery.fn.shortkeys = jQuery.fn.keys = function (obj, settings) {
    var el = this;
    this.settings = jQuery.extend(
        {
            split: "+",
            moreKeys: {}
        }, settings || {});
    this.wackyKeys = {
        '.': 190,
        ',': 188,
        ';': 59,
        space: 32,
        larrow: 37,
        uarrow: 38,
        rarrow: 39,
        darrow: 40,
        escape: 27,
        enter: 13,
        tab: 9,
        shift: 16
    };
    this.formElements = new Array("input", "select", "textarea", "button");
    this.keys = new Array();
    this.onFormElement = false;
    this.keysDown = new Array();
    this.init = function (obj) {
        for(x in this.wackyKeys) {
            this.wackyKeys[x.toUpperCase()] = this.wackyKeys[x];
        }
        for(x in obj) {
            this.keys.push(x.split(this.settings.split));
        }
        for(i in this.keys) {
            var quickArr = new Array();
            for(j in this.keys[i]) {
                quickArr.push(this.convertToNumbers(this.keys[i][j].toUpperCase()));
            }
            quickArr.sort();
            this.keys[i] = quickArr;
        }
    };
    this.convertToNumbers = function (inp) {
        if (this.wackyKeys[inp] != undefined) {
            return this.wackyKeys[inp];
        }
        return inp.toUpperCase().charCodeAt(0);
    };
    this.keyAdd = function(keyCode) {
        this.keysDown.push(keyCode);
        this.keysDown.sort();
    };
    this.keyRemove = function (keyCode) {
        for(i in this.keysDown) {
            if(this.keysDown[i] == keyCode) {
                this.keysDown.splice(i,1);
            }
        };
        this.keysDown.sort();
    };
    this.keyTest = function (i) {
        if (this.keys[i].length != this.keysDown.length) return false;
        for(j in this.keys[i]) {
            if(this.keys[i][j] != this.keysDown[j]) {
                return false;
            }
        }
        return true;
    };
    this.keyRemoveAll = function () {
        this.keysDown = new Array();
    };
    this.focused = function (bool) {
        this.onFormElement = bool;
    };

    function handleKeyDown(e){
        el.keyAdd(e.keyCode);
        var i = 0;
        for(x in obj) {
            if(el.keyTest(i) && !el.onFormElement) {
                return obj[x]() || false;
                break;
            }
            i++;
        };
        return null;
    }
    function handleKeyUp(e){
        el.keyRemove(e.keyCode);
    }
    for(x in this.formElements) {
        var formEl = $(this.formElements[x]);
        if (formEl.livequery){
            formEl.livequery('focus', function () {
                              el.focused(true);
                          });
            formEl.livequery('blur', function () {
                             el.focused(false);
                         });
        } else {
            $(formEl).focus( function () {
                                 el.focused(true);
                             });
            $(formEl).blur( function () {
                                el.focused(false);
                            });
        }
    }
    function handleWindowFocus(){
        // this is incase someone Alt+tabbed out of the window.
        el.keyRemoveAll();
    }

    this.started = false;
    this.stop = function(){
        if (!this.started){
            return;
        }
        // stop processing keyboard events
        $(document).unbind("keydown",handleKeyDown);
        $(document).unbind("keyup",handleKeyUp);
        $(window).unbind("focus",handleWindowFocus);
        this.started = false;
    };
    this.start = function(){
        if (this.started){
            return;
        }
        // start processing keyboard events
        $(document).keydown(handleKeyDown);
        $(document).keyup(handleKeyUp);
        $(window).focus(handleWindowFocus);
        // in case stop gets called from a keyboard
        // event and the key gets stuck
        el.keyRemoveAll();
        this.started = true;
    };

    this.init(obj);
    this.start();
    jQuery.extend(this.wackyKeys, this.settings.moreKeys);

    return this;
};