From 3a48cc8b7fa6d000223ee273051ecad4c6e64545 Mon Sep 17 00:00:00 2001 From: djmaze Date: Sat, 26 Sep 2020 09:34:23 +0200 Subject: [PATCH] Probably resolved issue #14 Could get some better improvements --- dev/Common/Globals.js | 24 ++--------- dev/prototype.js | 2 +- dev/shortcuts.js | 92 ++++++++++++++++++++++++++----------------- 3 files changed, 60 insertions(+), 58 deletions(-) diff --git a/dev/Common/Globals.js b/dev/Common/Globals.js index ea1c399fb..6cb25843b 100644 --- a/dev/Common/Globals.js +++ b/dev/Common/Globals.js @@ -9,11 +9,6 @@ export const $htmlCL = $html.classList; */ export const dropdownVisibility = ko.observable(false).extend({ rateLimit: 0 }); -/** - * @type {boolean} - */ -export const useKeyboardShortcuts = ko.observable(true); - /** * @type {boolean} */ @@ -50,18 +45,8 @@ export const keyScopeFake = ko.observable(KeyState.All); export const keyScope = ko.computed({ read: () => keyScopeFake(), - write: (value) => { + write: value => { if (KeyState.Menu !== value) { - if (KeyState.Compose === value) { - // disableKeyFilter - shortcuts.filter = () => useKeyboardShortcuts(); - } else { - // restoreKeyFilter - shortcuts.filter = event => !(event.target.matches - && (event.target.matches('input,select,textarea') - || event.target.closest('[contenteditable]'))); - } - keyScopeFake(value); if (dropdownVisibility()) { value = KeyState.Menu; @@ -72,12 +57,9 @@ export const keyScope = ko.computed({ } }); -keyScopeReal.subscribe((value) => { - // console.log('keyScope=' + sValue); // DEBUG - shortcuts.setScope(value); -}); +keyScopeReal.subscribe(value => shortcuts.setScope(value)); -dropdownVisibility.subscribe((value) => { +dropdownVisibility.subscribe(value => { if (value) { keyScope(KeyState.Menu); } else if (KeyState.Menu === shortcuts.getScope()) { diff --git a/dev/prototype.js b/dev/prototype.js index 05bcf410e..0b2057486 100644 --- a/dev/prototype.js +++ b/dev/prototype.js @@ -1,7 +1,7 @@ (w=>{ Array.isNotEmpty = array => Array.isArray(array) && array.length; - Array.prototype.unique((v, i, a) => a.indexOf(v) === i); + Array.prototype.unique = function() { return this.filter((v, i, a) => a.indexOf(v) === i); }; // Import momentjs locales function w.moment = { diff --git a/dev/shortcuts.js b/dev/shortcuts.js index 9ff1805c7..54f46410d 100644 --- a/dev/shortcuts.js +++ b/dev/shortcuts.js @@ -5,61 +5,79 @@ const doc = document, meta = /Mac OS X/.test(navigator.userAgent) ? 'meta' : 'ctrl', - actions = {}, - toArray = v => Array.isArray(v) ? v : [v]; -let - _scope = 'all'; + _scopes = {}, + toArray = v => Array.isArray(v) ? v : [v], -doc.addEventListener('keydown', event => { - const key = (event.key||event.code||'').toLowerCase(); - if (actions[key] && win.shortcuts.filter(event)) { + fire = (actions, event) => { let modifiers = []; event.altKey && modifiers.push('alt'); event.ctrlKey && modifiers.push('ctrl'); event.metaKey && modifiers.push('meta'); event.shiftKey && modifiers.push('shift'); modifiers = modifiers.join('+'); - if (actions[key][modifiers]) { + if (actions[modifiers]) { // for each potential shortcut - actions[key][modifiers].forEach(cmd => { - // see if it's in the current scope - if (cmd.scope.includes(_scope) || cmd.scope == 'all') { - try { - // call the handler and stop the event if neccessary - if (cmd.method(event) === false) { - event.preventDefault(); - event.stopPropagation(); - } - } catch (e) { - console.error(e); + actions[modifiers].forEach(cmd => { + try { + // call the handler and stop the event if neccessary + if (!event.defaultPrevented && cmd(event) === false) { + event.preventDefault(); + event.stopPropagation(); } + } catch (e) { + console.error(e); } }); } - } -}); + }, + + keydown = event => { + const key = (event.key||event.code||'').toLowerCase(); + if (scope[key] && win.shortcuts.filter(event)) { + fire(scope[key], event); + } + if (!event.defaultPrevented && _scope !== 'all' && _scopes.all[key] && win.shortcuts.filter(event)) { + fire(_scopes.all[key], event); + } + }; + +let + scope = {}, + _scope = 'all'; + +doc.addEventListener('keydown', keydown); win.shortcuts = { - add: (keys, modifiers, scope, method) => { + on: () => doc.addEventListener('keydown', keydown), + off: () => doc.removeEventListener('keydown', keydown), + add: (keys, modifiers, scopes, method) => { if (method === undefined) { - method = scope; - scope = 'all'; + method = scopes; + scopes = 'all'; } - toArray(keys).forEach(key => { - key = key.toLowerCase(); - if (!actions[key]) { - actions[key] = {}; + toArray(scopes).forEach(scope => { + if (!_scopes[scope]) { + _scopes[scope] = {}; } - modifiers = toArray(modifiers) - .map(key => 'meta' == key ? meta : key) - .unique().sort().join('+'); - if (!actions[key][modifiers]) { - actions[key][modifiers] = []; - } - actions[key][modifiers].push({scope:toArray(scope), method:method}); + toArray(keys).forEach(key => { + key = key.toLowerCase(); + if (!_scopes[scope][key]) { + _scopes[scope][key] = {}; + } + modifiers = toArray(modifiers) + .map(key => 'meta' == key ? meta : key) + .unique().sort().join('+'); + if (!_scopes[scope][key][modifiers]) { + _scopes[scope][key][modifiers] = []; + } + _scopes[scope][key][modifiers].push(method); + }); }); }, - setScope: scope => _scope = scope || 'all', + setScope: value => { + _scope = value || 'all'; + scope = _scopes[_scope] || {}; + }, getScope: () => _scope, getMetaKey: () => meta, // ignore keydown in any element that supports keyboard input @@ -67,4 +85,6 @@ win.shortcuts = { && (event.target.matches('input,select,textarea') || event.target.closest('[contenteditable]'))) }; +win.shortcuts.on(); + })(this);