mirror of
https://github.com/coderaiser/cloudcmd.git
synced 2026-01-23 18:55:26 +00:00
565 lines
14 KiB
JavaScript
565 lines
14 KiB
JavaScript
/* global CloudCmd, DOM */
|
||
|
||
'use strict';
|
||
|
||
const Info = DOM.CurrentInfo;
|
||
|
||
const exec = require('execon');
|
||
|
||
const Events = require('./dom/events');
|
||
const Buffer = require('./dom/buffer');
|
||
const {escapeRegExp} = require('../common/util');
|
||
|
||
let Chars = [];
|
||
const KEY = {
|
||
BACKSPACE : 8,
|
||
TAB : 9,
|
||
ENTER : 13,
|
||
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,
|
||
|
||
ZERO : 48,
|
||
|
||
A : 65,
|
||
|
||
C : 67,
|
||
D : 68,
|
||
|
||
G : 71,
|
||
|
||
M : 77,
|
||
|
||
O : 79,
|
||
Q : 81,
|
||
R : 82,
|
||
S : 83,
|
||
T : 84,
|
||
U : 85,
|
||
|
||
V : 86,
|
||
|
||
X : 88,
|
||
|
||
Z : 90,
|
||
|
||
INSERT_MAC : 96,
|
||
|
||
ASTERISK : 106,
|
||
PLUS : 107,
|
||
MINUS : 109,
|
||
|
||
F1 : 112,
|
||
F2 : 113,
|
||
F3 : 114,
|
||
F4 : 115,
|
||
F5 : 116,
|
||
F6 : 117,
|
||
F7 : 118,
|
||
F8 : 119,
|
||
F9 : 120,
|
||
F10 : 121,
|
||
|
||
EQUAL : 187,
|
||
HYPHEN : 189,
|
||
DOT : 190,
|
||
SLASH : 191,
|
||
TRA : 192, /* Typewritten Reverse Apostrophe (`) */
|
||
BACKSLASH : 220,
|
||
|
||
BRACKET_CLOSE: 221
|
||
};
|
||
|
||
KeyProto.prototype = KEY;
|
||
CloudCmd.Key = KeyProto;
|
||
|
||
function KeyProto() {
|
||
const Key = this;
|
||
|
||
let Binded;
|
||
|
||
this.isBind = () => {
|
||
return Binded;
|
||
};
|
||
|
||
this.setBind = () => {
|
||
Binded = true;
|
||
};
|
||
|
||
this.unsetBind = () => {
|
||
Binded = false;
|
||
};
|
||
|
||
this.bind = () => {
|
||
Events.addKey(listener);
|
||
Binded = true;
|
||
};
|
||
|
||
function getChar(event) {
|
||
/*
|
||
* event.keyIdentifier deprecated in chrome v51
|
||
* but event.key is absent in chrome <= v51
|
||
*/
|
||
|
||
if (event.key)
|
||
return event.key;
|
||
|
||
return fromCharCode(event.keyIdentifier);
|
||
}
|
||
|
||
function listener(event) {
|
||
const keyCode = event.keyCode;
|
||
const alt = event.altKey;
|
||
const ctrl = event.ctrlKey;
|
||
const shift = event.shiftKey;
|
||
const meta = event.metaKey;
|
||
const isBetween = keyCode >= KEY.ZERO && keyCode <= KEY.Z;
|
||
const isNumpad = /Numpad/.test(event.code);
|
||
|
||
let char = getChar(event);
|
||
let isSymbol = ~['.', '_', '-', '+', '='].indexOf(char);
|
||
|
||
if (!isSymbol) {
|
||
isSymbol = getSymbol(shift, keyCode);
|
||
|
||
if (isSymbol)
|
||
char = isSymbol;
|
||
}
|
||
|
||
/* in case buttons can be processed */
|
||
if (Key.isBind())
|
||
if (!isNumpad && !alt && !ctrl && !meta && (isBetween || isSymbol))
|
||
setCurrentByChar(char);
|
||
else {
|
||
Chars = [];
|
||
switchKey(event);
|
||
}
|
||
}
|
||
|
||
function getSymbol(shift, keyCode) {
|
||
switch (keyCode) {
|
||
case KEY.DOT:
|
||
return '.';
|
||
|
||
case KEY.HYPHEN:
|
||
return shift ? '_' : '-';
|
||
|
||
case KEY.EQUAL:
|
||
return shift ? '+' : '=';
|
||
}
|
||
}
|
||
|
||
function fromCharCode(keyIdentifier) {
|
||
const code = keyIdentifier.substring(2);
|
||
const hex = parseInt(code, 16);
|
||
const char = String.fromCharCode(hex);
|
||
|
||
return char;
|
||
}
|
||
|
||
function setCurrentByChar(char) {
|
||
let firstByName;
|
||
let skipCount = 0;
|
||
let setted = false;
|
||
let i = 0;
|
||
|
||
const escapeChar = escapeRegExp(char);
|
||
const regExp = new RegExp('^' + escapeChar + '.*$', 'i');
|
||
const {files} = Info;
|
||
const n = Chars.length;
|
||
|
||
while(i < n && char === Chars[i]) {
|
||
i++;
|
||
}
|
||
|
||
if (!i)
|
||
Chars = [];
|
||
|
||
const skipN = skipCount = i;
|
||
Chars.push(char);
|
||
|
||
const names = DOM.getFilenames(files);
|
||
const isTest = (a) => regExp.test(a);
|
||
const isRoot = (a) => a === '..';
|
||
const not = (f) => (a) => !f(a);
|
||
const setCurrent = (name) => {
|
||
const byName = DOM.getCurrentByName(name);
|
||
|
||
if (!skipCount) {
|
||
setted = true;
|
||
DOM.setCurrentFile(byName);
|
||
return true;
|
||
} else {
|
||
if (skipN === skipCount)
|
||
firstByName = byName;
|
||
|
||
--skipCount;
|
||
}
|
||
};
|
||
|
||
names
|
||
.filter(isTest)
|
||
.filter(not(isRoot))
|
||
.some(setCurrent);
|
||
|
||
if (!setted) {
|
||
DOM.setCurrentFile(firstByName);
|
||
Chars = [char];
|
||
}
|
||
}
|
||
|
||
function switchKey(event) {
|
||
let i, isSelected, prev, next;
|
||
let current = Info.element;
|
||
let name = Info.name;
|
||
|
||
const {Operation} = CloudCmd;
|
||
const panel = Info.panel;
|
||
const path = Info.path;
|
||
const isDir = Info.isDir;
|
||
|
||
const keyCode = event.keyCode;
|
||
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:
|
||
DOM.renameCurrent(current);
|
||
break;
|
||
|
||
case Key.F3:
|
||
if (shift)
|
||
CloudCmd.Markdown.show(path);
|
||
else if (ctrlMeta)
|
||
CloudCmd.sortPanel('name');
|
||
else
|
||
CloudCmd.View.show();
|
||
|
||
event.preventDefault();
|
||
break;
|
||
|
||
case Key.F4:
|
||
CloudCmd.EditFile.show();
|
||
event.preventDefault();
|
||
break;
|
||
|
||
case Key.F5:
|
||
if (ctrlMeta)
|
||
CloudCmd.sortPanel('date');
|
||
else
|
||
Operation.show('copy');
|
||
|
||
event.preventDefault();
|
||
break;
|
||
|
||
case Key.F6:
|
||
if (ctrlMeta)
|
||
CloudCmd.sortPanel('size');
|
||
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:
|
||
CloudCmd.Menu.show();
|
||
event.preventDefault();
|
||
break;
|
||
|
||
case Key.F10:
|
||
CloudCmd.Config.show();
|
||
event.preventDefault();
|
||
break;
|
||
|
||
case Key.TRA:
|
||
CloudCmd.Konsole.show();
|
||
event.preventDefault();
|
||
break;
|
||
|
||
case KEY.BRACKET_CLOSE:
|
||
CloudCmd.Konsole.show();
|
||
event.preventDefault();
|
||
break;
|
||
|
||
case Key.SPACE:
|
||
if (!isDir || name === '..')
|
||
isSelected = true;
|
||
else
|
||
isSelected = DOM.isSelected(current);
|
||
|
||
exec.if(isSelected, () => {
|
||
DOM.toggleSelectedFile(current);
|
||
}, (callback) => {
|
||
DOM.loadCurrentSize(callback, current);
|
||
});
|
||
|
||
event.preventDefault();
|
||
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();
|
||
|
||
name = Info.panel.getAttribute('data-name');
|
||
|
||
if (name === 'js-right')
|
||
DOM.duplicatePanel();
|
||
|
||
break;
|
||
|
||
case Key.RIGHT:
|
||
if (!alt)
|
||
return;
|
||
|
||
event.preventDefault();
|
||
|
||
name = Info.panel.getAttribute('data-name');
|
||
|
||
if (name === '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;
|
||
|
||
/* open directory */
|
||
case Key.ENTER:
|
||
if (Info.isDir)
|
||
CloudCmd.loadDir({
|
||
path: path === '/' ? '/' : path + '/'
|
||
});
|
||
break;
|
||
|
||
case Key.BACKSPACE:
|
||
CloudCmd.goToParentDir();
|
||
event.preventDefault();
|
||
break;
|
||
|
||
case Key.BACKSLASH:
|
||
if (ctrlMeta)
|
||
CloudCmd.loadDir({
|
||
path: '/'
|
||
});
|
||
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) {
|
||
CloudCmd.EditNames.show();
|
||
event.preventDefault();
|
||
}
|
||
|
||
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.D:
|
||
if (ctrlMeta) {
|
||
CloudCmd.log('clearing storage...');
|
||
|
||
DOM.Storage.clear(() => {
|
||
CloudCmd.log('storage cleared');
|
||
});
|
||
|
||
event.preventDefault();
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|