UNPKG

hotkeyz

Version:

A tiny dev-friendly keyboard event listener.

320 lines (273 loc) 7.61 kB
function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var keycode = createCommonjsModule(function (module, exports) { // Source: http://jsfiddle.net/vWx8V/ // http://stackoverflow.com/questions/5603195/full-list-of-javascript-keycodes /** * Conenience method returns corresponding value for given keyName or keyCode. * * @param {Mixed} keyCode {Number} or keyName {String} * @return {Mixed} * @api public */ function keyCode(searchInput) { // Keyboard Events if (searchInput && 'object' === typeof searchInput) { var hasKeyCode = searchInput.which || searchInput.keyCode || searchInput.charCode; if (hasKeyCode) searchInput = hasKeyCode; } // Numbers if ('number' === typeof searchInput) return names[searchInput] // Everything else (cast to string) var search = String(searchInput); // check codes var foundNamedKey = codes[search.toLowerCase()]; if (foundNamedKey) return foundNamedKey // check aliases var foundNamedKey = aliases[search.toLowerCase()]; if (foundNamedKey) return foundNamedKey // weird character? if (search.length === 1) return search.charCodeAt(0) return undefined } /** * Compares a keyboard event with a given keyCode or keyName. * * @param {Event} event Keyboard event that should be tested * @param {Mixed} keyCode {Number} or keyName {String} * @return {Boolean} * @api public */ keyCode.isEventKey = function isEventKey(event, nameOrCode) { if (event && 'object' === typeof event) { var keyCode = event.which || event.keyCode || event.charCode; if (keyCode === null || keyCode === undefined) { return false; } if (typeof nameOrCode === 'string') { // check codes var foundNamedKey = codes[nameOrCode.toLowerCase()]; if (foundNamedKey) { return foundNamedKey === keyCode; } // check aliases var foundNamedKey = aliases[nameOrCode.toLowerCase()]; if (foundNamedKey) { return foundNamedKey === keyCode; } } else if (typeof nameOrCode === 'number') { return nameOrCode === keyCode; } return false; } }; exports = module.exports = keyCode; /** * Get by name * * exports.code['enter'] // => 13 */ var codes = exports.code = exports.codes = { 'backspace': 8, 'tab': 9, 'enter': 13, 'shift': 16, 'ctrl': 17, 'alt': 18, 'pause/break': 19, 'caps lock': 20, 'esc': 27, 'space': 32, 'page up': 33, 'page down': 34, 'end': 35, 'home': 36, 'left': 37, 'up': 38, 'right': 39, 'down': 40, 'insert': 45, 'delete': 46, 'command': 91, 'left command': 91, 'right command': 93, 'numpad *': 106, 'numpad +': 107, 'numpad -': 109, 'numpad .': 110, 'numpad /': 111, 'num lock': 144, 'scroll lock': 145, 'my computer': 182, 'my calculator': 183, ';': 186, '=': 187, ',': 188, '-': 189, '.': 190, '/': 191, '`': 192, '[': 219, '\\': 220, ']': 221, "'": 222 }; // Helper aliases var aliases = exports.aliases = { 'windows': 91, '⇧': 16, '⌥': 18, '⌃': 17, '⌘': 91, 'ctl': 17, 'control': 17, 'option': 18, 'pause': 19, 'break': 19, 'caps': 20, 'return': 13, 'escape': 27, 'spc': 32, 'spacebar': 32, 'pgup': 33, 'pgdn': 34, 'ins': 45, 'del': 46, 'cmd': 91 }; /*! * Programatically add the following */ // lower case chars for (i = 97; i < 123; i++) codes[String.fromCharCode(i)] = i - 32; // numbers for (var i = 48; i < 58; i++) codes[i - 48] = i; // function keys for (i = 1; i < 13; i++) codes['f'+i] = i + 111; // numpad keys for (i = 0; i < 10; i++) codes['numpad '+i] = i + 96; /** * Get by code * * exports.name[13] // => 'Enter' */ var names = exports.names = exports.title = {}; // title for backward compat // Create reverse mapping for (i in codes) names[codes[i]] = i; // Add aliases for (var alias in aliases) { codes[alias] = aliases[alias]; } }); var keycode_1 = keycode.code; var keycode_2 = keycode.codes; var keycode_3 = keycode.aliases; var keycode_4 = keycode.names; var keycode_5 = keycode.title; keycode.aliases.meta = keycode.codes.command; var MODS = ['meta', 'ctrl', 'alt', 'shift']; var MOD_KEYS = MODS.map(keycode); var COMBO_RX = /((([^ ]+(\s*\+\s*[^ ]+)*)\s*\-\s*)?[^ ]+)/g; var MEMBER_RX = /-\s*([^ ]+)$/; var ALIASES = { comma: ',', plus: '+', minus: '-' }; function isValid(combo) { return combo.includes('-') || !MODS.includes(combo); } function normalizeKey(input) { var key = input.trim(); return key in ALIASES ? ALIASES[key] : key; } function normalizeCombo(combo, _, parts) { if (!isValid(combo)) { throw new Error('Missing key in combo: ' + parts.join(' ')); } else if (!combo.includes('-') || combo === '-') { return normalizeKey(combo); } var members = combo.split(MEMBER_RX); // sort modifiers in standard order var mods = members[0].split('+').map(function (mod) { return mod.trim(); }).sort(function (a, b) { return MODS.indexOf(a) - MODS.indexOf(b); }).join(' + '); var key = normalizeKey(members[1]); return "".concat(mods, " - ").concat(key); } function normalizeHotkeys(hotkeys) { var hotkeyMap = new Map(); Object.keys(hotkeys).forEach(function (command) { command.split(',').forEach(function (cmd) { var hotkey = cmd.match(COMBO_RX).map(normalizeCombo); var callback = hotkeys[command]; hotkeyMap.set(hotkey, callback); }); }); return hotkeyMap; } function eventToCombos(event) { var key = event.key; var keyCode = keycode(event) ? keycode(event).replace(/\s+/g, '-') : key; var mods = MODS.filter(function (mod) { return event[mod + 'Key']; }).join(' + '); // mods are ignored when shifted key values are used, e.g: `A` shouldn't be `shift - A` var keyCombo = mods && key === keyCode ? "".concat(mods, " - ").concat(key) : key; var keyCodeCombo = mods ? "".concat(mods, " - ").concat(keyCode) : keyCode; return [keyCombo, keyCodeCombo]; } function initSequence(time) { var keys = []; var keyCodes = []; var timeout = null; function reset() { keys = []; keyCodes = []; } function add(event) { if (timeout) { clearTimeout(timeout); } var combos = eventToCombos(event); keys.push(combos[0]); keyCodes.push(combos[1]); timeout = setTimeout(reset, time); return [keys, keyCodes]; } return { add: add, reset: reset }; } function match(sequence, command) { return sequence.slice(-command.length).join(' ') === command.join(' '); } function findCommands(sequences, commands) { return commands.filter(function (command) { return sequences.some(function (sequence) { return match(sequence, command); }); }); } function hotkeyz(config) { var time = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1000; var hotkeys = normalizeHotkeys(config); var sequence = initSequence(time); var commands = Array.from(hotkeys.keys()); return function (event) { if (MOD_KEYS.includes(event.keyCode)) { return; } var sequences = sequence.add(event); var matches = findCommands(sequences, commands); // stop the event normal behavior only if it matches a hotkey if (matches.length > 0) { event.preventDefault(); event.stopPropagation(); matches.forEach(function (command) { return hotkeys.get(command)(event); }); // reset the sequence buffer if one was executed if (matches.some(function (command) { return command.length > 1; })) { sequence.reset(); } } }; } export default hotkeyz;