/* global CloudCmd, DOM */ import clipboard from '@cloudcmd/clipboard'; import {fullstore} from 'fullstore'; import * as Events from '#dom/events'; import * as Buffer from '../dom/buffer.mjs'; import KEY from './key.js'; import _vim from './vim/index.js'; import setCurrentByChar from './set-current-by-char.js'; import {createBinder} from './binder.js'; const Chars = fullstore(); const toggleVim = (keyCode, overrides = {}) => { const {_config, config} = overrides; if (keyCode === KEY.ESC) _config('vim', !config('vim')); }; const isUndefined = (a) => typeof a === 'undefined'; Chars([]); const {assign} = Object; const binder = createBinder(); const bind = () => { Events.addKey(listener, true); binder.setBind(); }; export const Key = assign(binder, KEY, { bind, }); export const _listener = listener; function getChar(event) { /* * event.keyIdentifier deprecated in chrome v51 * but event.key is absent in chrome <= v51 */ const { key, shift, keyCode, keyIdentifier, } = event; const char = key || fromCharCode(keyIdentifier); const symbol = getSymbol(shift, keyCode); return [symbol, char]; } async function listener(event, overrides = {}) { const { config = CloudCmd.config, _config = CloudCmd._config, switchKey = _switchKey, vim = _vim, } = overrides; const {keyCode} = event; // strange chrome bug calles listener twice // in second time event misses a lot fields if (isUndefined(event.altKey)) return; const alt = event.altKey; const ctrl = event.ctrlKey; const meta = event.metaKey; const isBetween = keyCode >= KEY.ZERO && keyCode <= KEY.Z; const isNumpad = /Numpad/.test(event.code); const [symbol, char] = getChar(event); if (!binder.isBind()) return; toggleVim(keyCode, { config, _config, }); const isVim = config('vim'); if (!isVim && !isNumpad && !alt && !ctrl && !meta && (isBetween || symbol)) return setCurrentByChar(char, Chars); Chars([]); await switchKey(event); if (keyCode >= KEY.F1 && keyCode <= KEY.F10) return; if (isVim) vim(char, event); } function getSymbol(shift, keyCode) { switch(keyCode) { case KEY.DOT: return '.'; case KEY.HYPHEN: return shift ? '_' : '-'; case KEY.EQUAL: return shift ? '+' : '='; } return ''; } function fromCharCode(keyIdentifier) { const code = keyIdentifier.substring(2); const hex = parseInt(code, 16); return String.fromCharCode(hex); } async function _switchKey(event) { const Info = DOM.CurrentInfo; let i; let isSelected; let prev; let next; let current = Info.element; let dataName; const { name, panel, path, isDir, } = Info; const { Operation, changeDir, config, } = CloudCmd; const {keyCode} = event; const alt = event.altKey; const shift = event.shiftKey; const ctrl = event.ctrlKey; const meta = event.metaKey; const ctrlMeta = ctrl || meta; if (current) { prev = current.previousSibling; next = current.nextSibling; } switch(keyCode) { case KEY.TAB: DOM.changePanel(); event.preventDefault(); break; case KEY.INSERT: DOM .toggleSelectedFile(current) .setCurrentFile(next); break; case KEY.INSERT_MAC: DOM .toggleSelectedFile(current) .setCurrentFile(next); break; case KEY.DELETE: if (shift) Operation.show('delete:silent'); else Operation.show('delete'); break; case KEY.ASTERISK: DOM.toggleAllSelectedFiles(current); break; case KEY.PLUS: DOM.expandSelection(); event.preventDefault(); break; case KEY.MINUS: DOM.shrinkSelection(); event.preventDefault(); break; case KEY.F1: CloudCmd.Help.show(); event.preventDefault(); break; case KEY.F2: CloudCmd.UserMenu.show(); break; case KEY.F3: event.preventDefault(); if (Info.isDir) await changeDir(path); else if (shift) CloudCmd.View.show(null, { raw: true, }); else if (ctrlMeta) CloudCmd.sortPanel('name'); else CloudCmd.View.show(); break; case KEY.F4: if (config('vim')) CloudCmd.EditFileVim.show(); else CloudCmd.EditFile.show(); event.preventDefault(); break; case KEY.F5: if (ctrlMeta) CloudCmd.sortPanel('date'); else if (alt) Operation.show('pack'); else Operation.show('copy'); event.preventDefault(); break; case KEY.F6: if (ctrlMeta) CloudCmd.sortPanel('size'); else if (shift) DOM.renameCurrent(current); else Operation.show('move'); event.preventDefault(); break; case KEY.F7: if (shift) DOM.promptNewFile(); else DOM.promptNewDir(); event.preventDefault(); break; case KEY.F8: Operation.show('delete'); event.preventDefault(); break; case KEY.F9: if (alt) Operation.show('extract'); else CloudCmd.Menu.show(); event.preventDefault(); break; case KEY.F10: CloudCmd.Config.show(); event.preventDefault(); break; case KEY.TRA: event.preventDefault(); if (shift) return CloudCmd.Terminal.show(); CloudCmd.Konsole.show(); break; case KEY.BRACKET_CLOSE: CloudCmd.Konsole.show(); event.preventDefault(); break; case KEY.SPACE: event.preventDefault(); if (!isDir || name === '..') isSelected = true; else isSelected = DOM.isSelected(current); if (!isSelected) await DOM.loadCurrentSize(current); DOM.toggleSelectedFile(current); break; case KEY.U: if (ctrlMeta) { DOM.swapPanels(); event.preventDefault(); } break; /* navigation on file table: * * in case of pressing button 'up', * * select previous row */ case KEY.UP: if (shift) DOM.toggleSelectedFile(current); DOM.setCurrentFile(prev); event.preventDefault(); break; /* in case of pressing button 'down', * * select next row */ case KEY.DOWN: if (shift) DOM.toggleSelectedFile(current); DOM.setCurrentFile(next); event.preventDefault(); break; case KEY.LEFT: if (!alt) return; event.preventDefault(); dataName = Info.panel.getAttribute('data-name'); if (dataName === 'js-right') DOM.duplicatePanel(); break; case KEY.RIGHT: if (!alt) return; event.preventDefault(); dataName = Info.panel.getAttribute('data-name'); if (dataName === 'js-left') DOM.duplicatePanel(); break; /* in case of pressing button 'Home', * * go to top element */ case KEY.HOME: DOM.setCurrentFile(Info.first); event.preventDefault(); break; /* in case of pressing button 'End', select last element */ case KEY.END: DOM.setCurrentFile(Info.last); event.preventDefault(); break; /* если нажали клавишу page down проматываем экран */ case KEY.PAGE_DOWN: DOM.scrollByPages(panel, 1); for (i = 0; i < 30; i++) { if (!current.nextSibling) break; current = current.nextSibling; } DOM.setCurrentFile(current); event.preventDefault(); break; /* если нажали клавишу page up проматываем экран */ case KEY.PAGE_UP: DOM.scrollByPages(panel, -1); for (i = 0; i < 30; i++) { if (!current.previousSibling) break; current = current.previousSibling; } DOM.setCurrentFile(current); event.preventDefault(); break; case KEY.ENTER: if (Info.isDir) await changeDir(path); else CloudCmd.View.show(); break; case KEY.BACKSPACE: CloudCmd.goToParentDir(); event.preventDefault(); break; case KEY.BACKSLASH: if (ctrlMeta) await changeDir('/'); break; case KEY.A: if (ctrlMeta) { DOM.selectAllFiles(); event.preventDefault(); } break; case KEY.G: if (alt) { DOM.goToDirectory(); event.preventDefault(); } break; case KEY.M: if (ctrlMeta) { if (config('vim')) CloudCmd.EditNamesVim.show(); else CloudCmd.EditNames.show(); event.preventDefault(); } break; case KEY.P: if (!ctrlMeta) return; event.preventDefault(); clipboard .writeText(Info.dirPath) .catch(CloudCmd.log); break; /** * обновляем страницу, * загружаем содержимое каталога * при этом данные берём всегда с * сервера, а не из кэша * (обновляем кэш) */ case KEY.R: if (ctrlMeta) { CloudCmd.log('reloading page...\n'); CloudCmd.refresh(); event.preventDefault(); } break; case KEY.C: if (ctrlMeta) Buffer.copy(); break; case KEY.X: if (ctrlMeta) Buffer.cut(); break; case KEY.V: if (ctrlMeta) Buffer.paste(); break; case KEY.Z: if (ctrlMeta) Buffer.clear(); break; case KEY.COLON: CloudCmd.CommandLine.show(); event.preventDefault(); break; /* чистим хранилище */ case KEY.D: if (ctrlMeta) { CloudCmd.log('clearing storage...'); await DOM.Storage.clear(); CloudCmd.log('storage cleared'); event.preventDefault(); } break; case KEY.DOT: if (meta && shift) { const showDotFiles = !CloudCmd.config('showDotFiles'); CloudCmd._config('showDotFiles', showDotFiles); CloudCmd.refresh(); await DOM.RESTful.Config.write({ showDotFiles, }); } break; } }