Key Binder

So I was playing around with keyboard interface possibilities a couple of years ago, and eventually got the go ahead at OkCupid to implement keyboard shortcuts for navigating the site. The condition was it had to be minimally invasive and not eat up overhead tracking keystrokes. I also wanted to be able to track modifier keys sensibly, and have a simple interface for setting shortcuts.

kbind’s sole purpose is to track key strokes and keep an internal representation. To add a keyboard shortcut, you just write:

kbind["CTRL+A"] = function () {
    alert("Hiya");
};

… and it adds it to the kbind object as a method. You can also add stacked combinations, like

kbind["CTRL+ABIDE"] = function () {
    alert("Yeah, well, you know, that's\ 
             just, like, your opinion, man");
};

… which I programmed largely so I could put easter eggs in the okcupid interface (never got around to it). It also includes a built in toggle mechanism, which you can set like this:

kbind["T"] = {
	toggle : {
		toggleState: false,
		on:function() {
			alert("I'm on!");
		},
		off:function() {
			alert("I'm off!");
		}
	}
}

Finally, you can switch it on and off with kbind.enable() and kbind.disable().

The code is below, do as you please with it. This one’s based on the jQuery library; you can find the original, Prototype.js version at OkCupid.

/*

kbind.js

	- because I hate reaching for my mouse

Quietly keeps track of key combinations and allows interface
for event binding.

*/

var kbind = {

	ctrl		: false,
	shift		: false,
	opt			: false,
	stack		: "",
	clamp		: false,
	extant		: false,
	hardclear	: false,
    
    translations:{
        //Enter
        _13:"ENTER",
        
        //Numpad
        _96:"0",
        _97:"1",
        _98:"2",
        _99:"3",
        _100:"4",
        _101:"5",
        _102:"6",
        _103:"7",
        _104:"8",
        _105:"9",
        _107:"+",_61:"+",_187:"+", //unify keys
        _109:"-",_189:"-"
    },

    init:function(hardclear) {
        
		this.hardclear = (hardclear?true:false);

		if(this.extant) return;
		
		this.extant = true;
		
		var pass = this;

		$(window).bind(
			"keyup",
			function(e) {
	   			if(pass.clamp) return;
	
				// Unlink modifier keys
	            if(e.keyCode == 17) {
					pass.ctrl = false;
					pass.stack = "";
				}
	            else if(e.keyCode == 16) {
					pass.shift  = false;
					pass.stack = "";
				}
	            else if(e.keyCode == 18) {
					pass.opt = false;
					pass.stack = "";
				}
	            else if(pass.hardclear) {
					pass.stack  = "";
				}
	            pass.state();
	            return;
        	}
		);
		$(window).bind(
			"keydown",
			function(e) {
				if(pass.clamp) return;
				
				// Add modifier keys
	            if(e.keyCode == 17) 
					pass.ctrl = true;
	            else if(e.keyCode == 16) 
					pass.shift  = true;
	            else if(e.keyCode == 18) 
					pass.opt    = true;
	            else {
					// Translate and add to the stack
	                if(pass.translations["_" + e.keyCode])
	                    pass.stack += pass.translations["_" + e.keyCode];                    
					else
	    	            pass.stack += String.fromCharCode(e.keyCode).toUpperCase();
	            }
	            pass.state();
	            return;
        	}
		);
		// This addition prevents the ctrl key from
		// getting stuck on ctrl+click
		$(window).bind(
			"mousedown",
			function(e) {
				if(pass.clamp) return;
				
				pass.ctrl = false;
				pass.state();
			}
		);
    },

	disable:function() {
		this.clamp = true;
	},
	
	enable:function() {
		this.clamp = false;
	},
	
    state:function() {

        if(this.ctrl)   ctr_text = "CTRL+";
        else            ctr_text = "";
        
        if(this.shift)  sft_text = "SHIFT+";
        else            sft_text = "";

        if(this.opt)    opt_text = "OPT+";
        else            opt_text = "";

        if(this.stack)  act_text = this.stack;
        else            act_text = "";

        pressed = ctr_text + sft_text + opt_text + act_text;

        if(this[pressed]) {
			if(this[pressed].toggle) {
				if(!this[pressed].toggleState)
				{
					this[pressed].toggleState = true;
					this[pressed].toggle.on();
				} else {
					this[pressed].toggleState = false;
					this[pressed].toggle.off();
				}
			} else {
				this[pressed]();
			}
			this.stack = "";
		}
    }
};
kbind.init();

Tags: , , , , ,

Leave a Reply