(function($){var down={};var handlers={};$.onKeyCombo=function(keys,handler){if(keys.length===0||typeof handler!="function"){return}if(!handlers[keys.length])handlers[keys.length]=[];handlers[keys.length].push({trigger:keys,handler:handler});$(document).keydown((function(event){down[event.which]=true})).keyup((function(event){var pressed=_.map(_.keys(down),(function(num){return parseInt(num,10)}));var count=pressed.length;var collection=handlers[count];var execute=[];if(collection){for(var j=0;j<collection.length;j++){var trigger=collection[j].trigger;var handler=collection[j].handler;if(pressed.length==trigger.length&&_.difference(pressed,trigger).length==0){execute.push(handler)}}}_.each(pressed,(function(p){delete down[p]}));_.each(execute,(function(handler){console.log("$.onKeyCombo: firing handler for keys "+trigger.toString());handler(event)}))}))};$.offKeyCombo=function(keys,handler){if(keys.length===0||typeof handler!="function"){return}var collection=handlers[keys.length];if(!collection)return;var index=_.findIndex(collection,(function(i){return i.trigger.length==keys.length&&_.difference(i.trigger,keys).length==0&&i.handler==handler}));if(index!=-1){collection.splice(index,1)}}})($);