UNPKG

grapesjs-clot

Version:

Free and Open Source Web Builder Framework

328 lines (288 loc) 8.27 kB
// The initial version of this library was borrowed from https://github.com/madrobby/keymaster // and adapted to the GrapesJS's need var k, _handlers = {}, _mods = { 16: false, 18: false, 17: false, 91: false, }, _scope = 'all', // modifier keys _MODIFIERS = { '⇧': 16, shift: 16, '⌥': 18, alt: 18, option: 18, '⌃': 17, ctrl: 17, control: 17, '⌘': 91, command: 91, }, // special keys _MAP = { backspace: 8, tab: 9, clear: 12, enter: 13, return: 13, esc: 27, escape: 27, space: 32, left: 37, up: 38, right: 39, down: 40, del: 46, delete: 46, home: 36, end: 35, pageup: 33, pagedown: 34, ',': 188, '.': 190, '/': 191, '`': 192, '-': 189, '=': 187, ';': 186, "'": 222, '[': 219, ']': 221, '\\': 220, }, code = function (x) { return _MAP[x] || x.toUpperCase().charCodeAt(0); }, _downKeys = []; for (k = 1; k < 20; k++) _MAP['f' + k] = 111 + k; // IE doesn't support Array#indexOf, so have a simple replacement function index(array, item) { var i = array.length; while (i--) if (array[i] === item) return i; return -1; } // for comparing mods before unassignment function compareArray(a1, a2) { if (a1.length != a2.length) return false; for (var i = 0; i < a1.length; i++) { if (a1[i] !== a2[i]) return false; } return true; } var modifierMap = { 16: 'shiftKey', 18: 'altKey', 17: 'ctrlKey', 91: 'metaKey', }; function updateModifierKey(event) { for (k in _mods) _mods[k] = event[modifierMap[k]]; } // handle keydown event function dispatch(event) { var key, handler, k, i, modifiersMatch, scope; key = event.keyCode; if (index(_downKeys, key) == -1) { _downKeys.push(key); } // if a modifier key, set the key.<modifierkeyname> property to true and return if (key == 93 || key == 224) key = 91; // right command on webkit, command on Gecko if (key in _mods) { _mods[key] = true; // 'assignKey' from inside this closure is exported to window.key for (k in _MODIFIERS) if (_MODIFIERS[k] == key) assignKey[k] = true; return; } updateModifierKey(event); // see if we need to ignore the keypress (filter() can can be overridden) // by default ignore key presses if a select, textarea, or input is focused if (!assignKey.filter.call(this, event)) return; // abort if no potentially matching shortcuts found if (!(key in _handlers)) return; scope = getScope(); // for each potential shortcut for (i = 0; i < _handlers[key].length; i++) { handler = _handlers[key][i]; // see if it's in the current scope if (handler.scope == scope || handler.scope == 'all') { // check if modifiers match if any modifiersMatch = handler.mods.length > 0; for (k in _mods) if ((!_mods[k] && index(handler.mods, +k) > -1) || (_mods[k] && index(handler.mods, +k) == -1)) modifiersMatch = false; // call the handler and stop the event if neccessary if ((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch) { if (handler.method(event, handler) === false) { if (event.preventDefault) event.preventDefault(); else event.returnValue = false; if (event.stopPropagation) event.stopPropagation(); if (event.cancelBubble) event.cancelBubble = true; } } } } } // unset modifier keys on keyup function clearModifier(event) { var key = event.keyCode, k, i = index(_downKeys, key); // remove key from _downKeys if (i >= 0) { _downKeys.splice(i, 1); } if (key == 93 || key == 224) key = 91; if (key in _mods) { _mods[key] = false; for (k in _MODIFIERS) if (_MODIFIERS[k] == key) assignKey[k] = false; } } function resetModifiers() { for (k in _mods) _mods[k] = false; for (k in _MODIFIERS) assignKey[k] = false; } // parse and assign shortcut function assignKey(key, scope, method) { var keys, mods; keys = getKeys(key); if (method === undefined) { method = scope; scope = 'all'; } // for each shortcut for (var i = 0; i < keys.length; i++) { // set modifier keys if any mods = []; key = keys[i].split('+'); if (key.length > 1) { mods = getMods(key); key = [key[key.length - 1]]; } // convert to keycode and... key = key[0]; key = code(key); // ...store handler if (!(key in _handlers)) _handlers[key] = []; _handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods, }); } } // unbind all handlers for given key in current scope function unbindKey(key, scope) { var multipleKeys, keys, mods = [], i, j, obj; multipleKeys = getKeys(key); for (j = 0; j < multipleKeys.length; j++) { keys = multipleKeys[j].split('+'); if (keys.length > 1) { mods = getMods(keys); } key = keys[keys.length - 1]; key = code(key); if (scope === undefined) { scope = getScope(); } if (!_handlers[key]) { return; } for (i = 0; i < _handlers[key].length; i++) { obj = _handlers[key][i]; // only clear handlers if correct scope and mods match if (obj.scope === scope && compareArray(obj.mods, mods)) { _handlers[key][i] = {}; } } } } // Returns true if the key with code 'keyCode' is currently down // Converts strings into key codes. function isPressed(keyCode) { if (typeof keyCode == 'string') { keyCode = code(keyCode); } return index(_downKeys, keyCode) != -1; } function getPressedKeyCodes() { return _downKeys.slice(0); } function filter(event) { var tagName = (event.target || event.srcElement).tagName; // ignore keypressed in any elements that support keyboard data input return !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA'); } // initialize key.<modifier> to false for (k in _MODIFIERS) assignKey[k] = false; // set current scope (default 'all') function setScope(scope) { _scope = scope || 'all'; } function getScope() { return _scope || 'all'; } // delete all handlers for a given scope function deleteScope(scope) { var key, handlers, i; for (key in _handlers) { handlers = _handlers[key]; for (i = 0; i < handlers.length; ) { if (handlers[i].scope === scope) handlers.splice(i, 1); else i++; } } } // abstract key logic for assign and unassign function getKeys(key) { var keys; key = key.replace(/\s/g, ''); keys = key.split(','); if (keys[keys.length - 1] == '') { keys[keys.length - 2] += ','; } return keys; } // abstract mods logic for assign and unassign function getMods(key) { var mods = key.slice(0, key.length - 1); for (var mi = 0; mi < mods.length; mi++) mods[mi] = _MODIFIERS[mods[mi]]; return mods; } // cross-browser events function addEvent(object, event, method) { if (object.addEventListener) object.addEventListener(event, method, false); else if (object.attachEvent) object.attachEvent('on' + event, function () { method(window.event); }); } // set window.key and window.key.set/get/deleteScope, and the default filter assignKey.setScope = setScope; assignKey.getScope = getScope; assignKey.deleteScope = deleteScope; assignKey.filter = filter; assignKey.isPressed = isPressed; assignKey.getPressedKeyCodes = getPressedKeyCodes; assignKey.unbind = unbindKey; assignKey.handlers = _handlers; assignKey.init = win => { // set the handlers globally on document // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48 addEvent(win.document, 'keydown', function (event) { dispatch(event); }); addEvent(win.document, 'keyup', clearModifier); addEvent(win, 'focus', resetModifiers); }; export default assignKey;