Skip to content

Instantly share code, notes, and snippets.

@jankuca
Created February 3, 2011 19:12
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jankuca/809982 to your computer and use it in GitHub Desktop.
Save jankuca/809982 to your computer and use it in GitHub Desktop.
Shortcut JavaScript library
/**
* Shortcut Library
* --
* @version 2.01.B
* @author Binny V A
* @modifier Jan Kuča <jan@jankuca.com>
* @license BSD
*/
(function (window) {
var document = window.document;
// work around for stupid Shift key bug created by using lowercase
// as a result the shift+num combination was broken
var shift_nums = {
'`': '~',
'1': '!',
'2': '@',
'3': '#',
'4': '$',
'5': '%',
'6': '^',
'7': '&',
'8': '*',
'9': '(',
'0': ')',
'-': '_',
'=': '+',
';': ':',
'\'': '"',
',': '<',
'.': '>',
'/': '?',
'\\': '|'
};
// special key codes
var special_keys = {
'esc': 27,
'escape': 27,
'tab': 9,
'space': 32,
'return': 13,
'enter': 13,
'backspace': 8,
'scrolllock': 145,
'scroll_lock': 145,
'scroll': 145,
'capslock': 20,
'caps_lock': 20,
'caps': 20,
'numlock': 144,
'num_lock': 144,
'num': 144,
'pause': 19,
'break': 19,
'insert': 45,
'home': 36,
'delete': 46,
'end': 35,
'pageup': 33,
'page_up': 33,
'pu': 33,
'pagedown': 34,
'page_down': 34,
'pd': 34,
'left': 37,
'up': 38,
'right': 39,
'down': 40,
'f1': 112,
'f2': 113,
'f3': 114,
'f4': 115,
'f5': 116,
'f6': 117,
'f7': 118,
'f8': 119,
'f9': 120,
'f10': 121,
'f11': 122,
'f12': 123
};
var Shortcut = {
'all_shortcuts': {}, // storage for all the shortcuts
'add': function (shortcut_combination, callback, opt) {
var default_options = {
'type': 'keydown',
'propagate': false,
'disable_in_input': false,
'target': document,
'keycode': false
};
if (!opt) {
opt = default_options;
} else {
var dfo;
for (dfo in default_options) {
if (default_options.hasOwnProperty(dfo) && opt[dfo] === undefined) {
opt[dfo] = default_options[dfo];
}
}
}
var el = opt.target;
if (typeof opt.target === 'string') {
el = document.getElementById(opt.target);
}
shortcut_combination = shortcut_combination.toLowerCase();
// keypress event listener
var fn = function (event) {
event = event || window.event;
if (opt.disable_in_input) { // don't enable shortcut keys in input, and textarea fields
var element;
if (event.target) {
element = event.target;
} else if (event.srcElement) {
element = event.srcElement;
}
if (element.nodeType === 3) {
element = element.parentNode;
}
if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
return;
}
}
// determine which key is pressed
var code;
if (event.keyCode) {
code = event.keyCode;
} else if (event.which) {
code = event.which;
}
var character = String.fromCharCode(code).toLowerCase();
if (code === 188) { // comma
character = ',';
}
if (code === 190) { // period
character = '.';
}
var keys = shortcut_combination.split('+');
//Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
var kp = 0;
var modifiers = {
'shift': { 'wanted': false, 'pressed': false },
'ctrl': { 'wanted': false, 'pressed': false },
'alt': { 'wanted': false, 'pressed': false },
'meta': { 'wanted': false, 'pressed': false } // meta is mac os-specific
};
if (event.ctrlKey) {
modifiers.ctrl.pressed = true;
}
if (event.shiftKey) {
modifiers.shift.pressed = true;
}
if (event.altKey) {
modifiers.alt.pressed = true;
}
if (event.metaKey) {
modifiers.meta.pressed = true;
}
var i, k;
for (i = 0; i < keys.length; ++i) {
k = keys[i];
++kp;
// modifiers
switch (k) {
case 'ctrl':
case 'control':
modifiers.ctrl.wanted = true;
break;
case 'shift':
modifiers.shift.wanted = true;
break;
case 'alt':
modifiers.alt.wanted = true;
break;
case 'meta':
modifiers.meta.wanted = true;
break;
default:
if (k.length > 1) { // special key
if (special_keys[k] === code) {
break;
} else if (opt.keycode && opt.keycode === code) {
break;
}
} else { // regular key
if (character === k) { // lowercase
break;
} else {
if (shift_nums[character] && event.shiftKey) { // uppercase
character = shift_nums[character];
}
if (character === k) {
break;
}
}
}
--kp;
}
}
if (kp === keys.length &&
modifiers.ctrl.pressed === modifiers.ctrl.wanted &&
modifiers.shift.pressed === modifiers.shift.wanted &&
modifiers.alt.pressed === modifiers.alt.wanted &&
modifiers.meta.pressed === modifiers.meta.wanted
) {
callback(event);
if (!opt.propagate) { // stop the event
if (event.stopPropagation) {
event.stopPropagation();
event.preventDefault();
}
return false;
}
}
};
if (this.all_shortcuts[shortcut_combination] === undefined) {
this.all_shortcuts[shortcut_combination] = [];
}
this.all_shortcuts[shortcut_combination].push({
'callback': fn,
'target': el,
'event': opt.type
});
// add the listener
if (el.addEventListener) {
el.addEventListener(opt.type, fn, false);
} else if (el.attachEvent) {
el.attachEvent('on' + opt.type, fn);
} else {
el['on' + opt.type] = fn;
}
},
'remove': function (shortcut_combination, callback) {
shortcut_combination = shortcut_combination.toLowerCase();
var bindings = this.all_shortcuts[shortcut_combination];
if (bindings === undefined) {
return;
}
if (callback === undefined) {
delete this.all_shortcuts[shortcut_combination];
} else {
var i, ii = bindings.length;
var binding, el;
for (i = 0; i < ii; ++i) {
binding = bindings[i];
el = binding.target;
if (binding.callback === callback) {
if (el.detachEvent) {
el.detachEvent('on' + binding.event, callback);
} else if (el.removeEventListener) {
el.removeEventListener(binding.event, callback, false);
} else {
el['on' + binding.event] = false;
}
}
}
}
}
};
window.Shortcut = Shortcut;
})(window);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment