Posts Tagged ‘javascript’

Polygon Boundary

Thursday, March 25th, 2010

One of the many fun things about being in charge of advertising implementations is your boss will say things like, “Hey, I need you to do the design and markup for this ad and only show it to users living in a polygonal region of California. Can you get that up in a couple of hours?”

I assumed this would be readily available with a little googling, but had trouble finding a Javascript cut-and-paste example, so I ended up adapting a point-in-polygon algorithm written in C.

function in_polygon(polygon,point)
{
	var X = [],
	    Y = [],
	    j=X.length-1,
	    odd=false,
	    x=point[0],
	    y=point[1];

	for(i=0; i < polygon.length; i++) {
		X[X.length] = polygon[i][0];
		Y[Y.length] = polygon[i][1];
	}

	for (i=0; i < X.length; i++) {
		if(Y[i] < y && Y[j] >= y
		|| Y[j] < y && Y[i] >= y)
			if (X[i]+(y-Y[i])/(Y[j]-Y[i])*(X[j]-X[i]) < x)
				odd=!odd;
		j=i;
	}
	return odd;
}

The original program took two arrays, one of X coordinates and one of Y coordinates, so the first for loop in the function just translates a more human readable set of values into the X and Y arrays used by the algorithm. So you define the inputs like so:

var polygon = [
	[38,-123],
	[38,-121],
	[36,-121],
	[36,-123]
];

var point = [37,-125];

... and feed them into the function to get a boolean answer.

Hope this saves somebody some time.

Key Binder

Wednesday, March 24th, 2010

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();