mirror of
https://github.com/coderaiser/cloudcmd.git
synced 2026-01-22 18:29:26 +00:00
feature: migrate to esbuild
This commit is contained in:
parent
ed9af6d7a6
commit
16862719b2
12 changed files with 331 additions and 271 deletions
51
build.mjs
Normal file
51
build.mjs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
âˆimport * as esbuild from 'esbuild'
|
||||
|
||||
const dir = './client';
|
||||
const dirModules = './client/modules';
|
||||
|
||||
await esbuild.build({
|
||||
entryPoints: [
|
||||
`${dir}/cloudcmd.js`,
|
||||
`${dirModules}/edit.js`,
|
||||
`${dirModules}/edit-file.js`,
|
||||
`${dirModules}/edit-file-vim.js`,
|
||||
`${dirModules}/edit-names.js`,
|
||||
`${dirModules}/edit-names-vim.js`,
|
||||
`${dirModules}/menu.js`,
|
||||
`${dirModules}/view/index.js`,
|
||||
`${dirModules}/help.js`,
|
||||
`${dirModules}/markdown.js`,
|
||||
`${dirModules}/config/index.js`,
|
||||
`${dirModules}/contact.js`,
|
||||
`${dirModules}/upload.js`,
|
||||
`${dirModules}/operation/index.js`,
|
||||
`${dirModules}/konsole.js`,
|
||||
`${dirModules}/terminal.js`,
|
||||
`${dirModules}/terminal-run.js`,
|
||||
`${dirModules}/cloud.js`,
|
||||
`${dirModules}/user-menu/index.js`,
|
||||
`${dirModules}/polyfill.js`,
|
||||
`${dirModules}/command-line.js`,
|
||||
|
||||
'css/columns/name-size-date.css',
|
||||
'css/columns/name-size.css',
|
||||
],
|
||||
entryNames: '[dir]',
|
||||
bundle: true,
|
||||
minify: true,
|
||||
sourcemap: true,
|
||||
target: ['chrome100'],
|
||||
alias: {
|
||||
path: 'path-browserify',
|
||||
},
|
||||
outdir: 'dist',
|
||||
loader: {
|
||||
'.png': 'dataurl',
|
||||
'.gif': 'dataurl',
|
||||
'.woff': 'dataurl',
|
||||
'.woff2': 'dataurl',
|
||||
'.ttf': 'dataurl',
|
||||
'.eot': 'dataurl',
|
||||
'.svg': 'text',
|
||||
}
|
||||
})
|
||||
202
client/client.js
202
client/client.js
|
|
@ -39,60 +39,60 @@ load.addErrorListener((e, src) => {
|
|||
|
||||
function CloudCmdProto(DOM) {
|
||||
let Listeners;
|
||||
|
||||
|
||||
Emitify.call(this);
|
||||
|
||||
|
||||
const CloudCmd = this;
|
||||
const Info = DOM.CurrentInfo;
|
||||
|
||||
|
||||
const {
|
||||
Storage,
|
||||
Files,
|
||||
} = DOM;
|
||||
|
||||
|
||||
this.log = (...a) => {
|
||||
if (!isDev)
|
||||
return;
|
||||
|
||||
|
||||
console.log(...a);
|
||||
};
|
||||
this.prefix = '';
|
||||
this.prefixSocket = '';
|
||||
this.prefixURL = '';
|
||||
this.DIRCLIENT = '/dist/';
|
||||
this.DIRCLIENT_MODULES = this.DIRCLIENT + 'modules/';
|
||||
|
||||
this.DIR_CLIENT = '/dist/client';
|
||||
this.DIR_CLIENT_MODULES = this.DIR_CLIENT + '/modules';
|
||||
|
||||
this.MIN_ONE_PANEL_WIDTH = 1155;
|
||||
this.HOST = location.origin || location.protocol + '//' + location.host;
|
||||
|
||||
|
||||
this.TITLE = 'Cloud Commander';
|
||||
|
||||
|
||||
this.sort = {
|
||||
left: 'name',
|
||||
right: 'name',
|
||||
};
|
||||
|
||||
|
||||
this.order = {
|
||||
left: 'asc',
|
||||
right: 'asc',
|
||||
};
|
||||
|
||||
|
||||
this.changeDir = async (path, {isRefresh, panel, history = true, noCurrent, currentName} = {}) => {
|
||||
const refresh = isRefresh;
|
||||
let panelChanged;
|
||||
|
||||
|
||||
if (!noCurrent && panel && panel !== Info.panel) {
|
||||
DOM.changePanel();
|
||||
panelChanged = true;
|
||||
}
|
||||
|
||||
|
||||
let imgPosition;
|
||||
|
||||
|
||||
if (panelChanged || refresh || !history)
|
||||
imgPosition = 'top';
|
||||
|
||||
|
||||
Images.show.load(imgPosition, panel);
|
||||
|
||||
|
||||
/* загружаем содержимое каталога */
|
||||
await ajaxLoad(addSlashToEnd(path), {
|
||||
refresh,
|
||||
|
|
@ -101,7 +101,7 @@ function CloudCmdProto(DOM) {
|
|||
currentName,
|
||||
}, panel);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Конструктор CloudClient, который
|
||||
* выполняет весь функционал по
|
||||
|
|
@ -111,7 +111,7 @@ function CloudCmdProto(DOM) {
|
|||
CloudCmd.prefix = prefix;
|
||||
CloudCmd.prefixURL = `${prefix}${apiURL}`;
|
||||
CloudCmd.prefixSocket = config.prefixSocket;
|
||||
|
||||
|
||||
CloudCmd.config = (key) => config[key];
|
||||
CloudCmd.config.if = currify((key, fn, a) => config[key] && fn(a));
|
||||
CloudCmd._config = (key, value) => {
|
||||
|
|
@ -119,61 +119,63 @@ function CloudCmdProto(DOM) {
|
|||
* should be called from config.js only
|
||||
* after successful update on server
|
||||
*/
|
||||
|
||||
|
||||
if (key === 'password')
|
||||
return;
|
||||
|
||||
|
||||
config[key] = value;
|
||||
};
|
||||
|
||||
|
||||
if (config.oneFilePanel)
|
||||
CloudCmd.MIN_ONE_PANEL_WIDTH = Infinity;
|
||||
|
||||
|
||||
if (!document.body.scrollIntoViewIfNeeded)
|
||||
await load.js(prefix + CloudCmd.DIRCLIENT_MODULES + 'polyfill.js');
|
||||
|
||||
await load.js(`${prefix}${CloudCmd.DIR_CLIENT_MODULES}/polyfill.js`);
|
||||
|
||||
await initModules();
|
||||
await baseInit();
|
||||
await loadStyle();
|
||||
|
||||
//await loadStyle();
|
||||
|
||||
CloudCmd.route(location.hash);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
async function loadStyle() {
|
||||
const {prefix} = CloudCmd;
|
||||
const name = prefix + '/dist/cloudcmd.common.css';
|
||||
|
||||
const name = prefix + '/build/cloudcmd.css';
|
||||
|
||||
await load.css(name);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
this.route = (path) => {
|
||||
const query = path.split('/');
|
||||
|
||||
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
|
||||
const [kebabModule] = query;
|
||||
const module = noJS(pascalCase(kebabModule.slice(1)));
|
||||
|
||||
|
||||
const file = query[1];
|
||||
const current = DOM.getCurrentByName(file);
|
||||
|
||||
|
||||
if (file && !current) {
|
||||
const msg = formatMsg('set current file', file, 'error');
|
||||
CloudCmd.log(msg);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
DOM.setCurrentFile(current);
|
||||
CloudCmd.execFromModule(module, 'show');
|
||||
};
|
||||
|
||||
|
||||
this.logOut = async () => {
|
||||
const url = CloudCmd.prefix + '/logout';
|
||||
const error = () => document.location.reload();
|
||||
const {prefix} = CloudCmd;
|
||||
|
||||
|
||||
await DOM.Storage.clear();
|
||||
unregisterSW(prefix);
|
||||
DOM.load.ajax({
|
||||
|
|
@ -181,19 +183,19 @@ function CloudCmdProto(DOM) {
|
|||
error,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const initModules = async () => {
|
||||
CloudCmd.Key = Key;
|
||||
CloudCmd.Key.bind();
|
||||
|
||||
|
||||
const [, modules] = await tryToCatch(Files.get, 'modules');
|
||||
const showLoad = Images.show.load;
|
||||
|
||||
|
||||
const doBefore = {
|
||||
edit: showLoad,
|
||||
menu: showLoad,
|
||||
};
|
||||
|
||||
|
||||
const load = (name, path, dobefore) => {
|
||||
loadModule({
|
||||
name,
|
||||
|
|
@ -201,28 +203,28 @@ function CloudCmdProto(DOM) {
|
|||
dobefore,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
if (!modules)
|
||||
return;
|
||||
|
||||
|
||||
for (const module of modules.local) {
|
||||
load(null, module, doBefore[module]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
async function saveCurrentName(currentName) {
|
||||
await Storage.set('current-name', currentName);
|
||||
}
|
||||
|
||||
|
||||
async function baseInit() {
|
||||
const files = DOM.getFiles();
|
||||
|
||||
|
||||
CloudCmd.on('current-file', DOM.updateCurrentInfo);
|
||||
CloudCmd.on('current-name', saveCurrentName);
|
||||
|
||||
|
||||
const name = await Storage.get('current-name');
|
||||
const currentFile = name && DOM.getCurrentByName(name) || files[0];
|
||||
|
||||
|
||||
/* выделяем строку с первым файлом */
|
||||
if (files)
|
||||
DOM.setCurrentFile(currentFile, {
|
||||
|
|
@ -231,56 +233,56 @@ function CloudCmdProto(DOM) {
|
|||
// overwre otherwise
|
||||
history: !location.hash,
|
||||
});
|
||||
|
||||
|
||||
const dirPath = DOM.getCurrentDirPath();
|
||||
Listeners = CloudCmd.Listeners;
|
||||
Listeners.init();
|
||||
|
||||
|
||||
const panels = getPanels();
|
||||
panels.forEach(Listeners.setOnPanel);
|
||||
|
||||
|
||||
Listeners.initKeysPanel();
|
||||
|
||||
|
||||
if (!CloudCmd.config('dirStorage'))
|
||||
return;
|
||||
|
||||
|
||||
const data = await Storage.get(dirPath);
|
||||
|
||||
|
||||
if (!data)
|
||||
await Storage.setJson(dirPath, getJsonFromFileTable());
|
||||
}
|
||||
|
||||
|
||||
function getPanels() {
|
||||
const panels = ['left'];
|
||||
|
||||
|
||||
if (CloudCmd.config('oneFilePanel'))
|
||||
return panels;
|
||||
|
||||
|
||||
return [
|
||||
...panels,
|
||||
'right',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
this.execFromModule = async (moduleName, funcName, ...args) => {
|
||||
await CloudCmd[moduleName]();
|
||||
|
||||
|
||||
const func = CloudCmd[moduleName][funcName];
|
||||
func(...args);
|
||||
};
|
||||
|
||||
|
||||
this.refresh = async (options = {}) => {
|
||||
const {
|
||||
panel = Info.panel,
|
||||
currentName,
|
||||
} = options;
|
||||
|
||||
|
||||
const path = DOM.getCurrentDirPath(panel);
|
||||
|
||||
|
||||
const isRefresh = true;
|
||||
const history = false;
|
||||
const noCurrent = options ? options.noCurrent : false;
|
||||
|
||||
|
||||
await CloudCmd.changeDir(path, {
|
||||
isRefresh,
|
||||
history,
|
||||
|
|
@ -289,7 +291,7 @@ function CloudCmdProto(DOM) {
|
|||
currentName,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Функция загружает json-данные о Файловой Системе
|
||||
* через ajax-запрос.
|
||||
|
|
@ -302,49 +304,49 @@ function CloudCmdProto(DOM) {
|
|||
*/
|
||||
async function ajaxLoad(path, options = {}, panel) {
|
||||
const {RESTful} = DOM;
|
||||
|
||||
|
||||
CloudCmd.log('reading dir: "' + path + '";');
|
||||
|
||||
|
||||
const dirStorage = CloudCmd.config('dirStorage');
|
||||
const json = dirStorage && await Storage.getJson(path);
|
||||
|
||||
|
||||
const name = options.currentName || Info.name;
|
||||
const {
|
||||
noCurrent,
|
||||
refresh,
|
||||
} = options;
|
||||
|
||||
|
||||
if (!refresh && json)
|
||||
return await createFileTable(json, panel, options);
|
||||
|
||||
|
||||
const position = DOM.getPanelPosition(panel);
|
||||
const sort = CloudCmd.sort[position];
|
||||
const order = CloudCmd.order[position];
|
||||
|
||||
|
||||
const query = rendy('?sort={{ sort }}&order={{ order }}', {
|
||||
sort,
|
||||
order,
|
||||
});
|
||||
|
||||
|
||||
const [, newObj] = await RESTful.read(path + query, 'json');
|
||||
|
||||
|
||||
if (!newObj)
|
||||
return; // that's OK, error handled by RESTful
|
||||
|
||||
|
||||
options.sort = sort;
|
||||
options.order = order;
|
||||
|
||||
|
||||
await createFileTable(newObj, panel, options);
|
||||
|
||||
|
||||
if (refresh && !noCurrent)
|
||||
DOM.setCurrentByName(name);
|
||||
|
||||
|
||||
if (!CloudCmd.config('dirStorage'))
|
||||
return;
|
||||
|
||||
|
||||
Storage.setJson(path, newObj);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Функция строит файловую таблицу
|
||||
* @param json - данные о файлах
|
||||
|
|
@ -357,36 +359,36 @@ function CloudCmdProto(DOM) {
|
|||
history,
|
||||
noCurrent,
|
||||
} = options;
|
||||
|
||||
|
||||
const names = [
|
||||
'file',
|
||||
'path',
|
||||
'link',
|
||||
'pathLink',
|
||||
];
|
||||
|
||||
|
||||
const [
|
||||
error,
|
||||
[file, path, link, pathLink],
|
||||
] = await tryToCatch(Files.get, names);
|
||||
|
||||
|
||||
if (error)
|
||||
return DOM.Dialog.alert(error.responseText);
|
||||
|
||||
|
||||
const panel = panelParam || DOM.getPanel();
|
||||
const {prefix} = CloudCmd;
|
||||
|
||||
|
||||
const {
|
||||
dir,
|
||||
name,
|
||||
} = Info;
|
||||
|
||||
|
||||
const {childNodes} = panel;
|
||||
let i = childNodes.length;
|
||||
|
||||
|
||||
while (i--)
|
||||
panel.removeChild(panel.lastChild);
|
||||
|
||||
|
||||
panel.innerHTML = buildFromJSON({
|
||||
sort : options.sort,
|
||||
order : options.order,
|
||||
|
|
@ -400,26 +402,26 @@ function CloudCmdProto(DOM) {
|
|||
link,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Listeners.setOnPanel(panel);
|
||||
|
||||
|
||||
if (!noCurrent) {
|
||||
let current;
|
||||
|
||||
|
||||
if (name === '..' && dir !== '/')
|
||||
current = DOM.getCurrentByName(dir);
|
||||
|
||||
|
||||
if (!current)
|
||||
[current] = DOM.getFiles(panel);
|
||||
|
||||
|
||||
DOM.setCurrentFile(current, {
|
||||
history,
|
||||
});
|
||||
|
||||
|
||||
CloudCmd.emit('active-dir', Info.dirPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.goToParentDir = async () => {
|
||||
const {
|
||||
dir,
|
||||
|
|
@ -427,17 +429,17 @@ function CloudCmdProto(DOM) {
|
|||
parentDirPath,
|
||||
panel,
|
||||
} = Info;
|
||||
|
||||
|
||||
if (dirPath === parentDirPath)
|
||||
return;
|
||||
|
||||
|
||||
const path = parentDirPath;
|
||||
|
||||
|
||||
await CloudCmd.changeDir(path);
|
||||
|
||||
|
||||
const current = DOM.getCurrentByName(dir);
|
||||
const [first] = DOM.getFiles(panel);
|
||||
|
||||
|
||||
DOM.setCurrentFile(current || first, {
|
||||
history,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -50,8 +50,7 @@ const onUpdateFound = wraptile(async (config) => {
|
|||
const {DOM} = window;
|
||||
const prefix = getPrefix(config.prefix);
|
||||
|
||||
await load.js(`${prefix}/dist/cloudcmd.common.js`);
|
||||
await load.js(`${prefix}/dist/cloudcmd.js`);
|
||||
await load.js(`${prefix}/dist/client/cloudcmd.js`);
|
||||
|
||||
console.log('cloudcmd: sw: updated');
|
||||
|
||||
|
|
|
|||
|
|
@ -16,45 +16,45 @@ const noJS = (a) => a.replace(/.js$/, '');
|
|||
module.exports = function loadModule(params) {
|
||||
if (!params)
|
||||
return;
|
||||
|
||||
|
||||
const {path} = params;
|
||||
|
||||
|
||||
const name = path && noJS(pascalCase(path));
|
||||
const doBefore = params.dobefore;
|
||||
|
||||
|
||||
if (CloudCmd[name])
|
||||
return;
|
||||
|
||||
|
||||
CloudCmd[name] = () => {
|
||||
exec(doBefore);
|
||||
|
||||
|
||||
const {prefix} = CloudCmd;
|
||||
const pathFull = prefix + CloudCmd.DIRCLIENT_MODULES + path + '.js';
|
||||
|
||||
const pathFull = `${prefix}${CloudCmd.DIR_CLIENT_MODULES}/${path}.js`;
|
||||
|
||||
return loadJS(pathFull).then(async () => {
|
||||
const newModule = async (f) => f && f();
|
||||
const module = CloudCmd[name];
|
||||
|
||||
|
||||
Object.assign(newModule, module);
|
||||
|
||||
|
||||
CloudCmd[name] = newModule;
|
||||
|
||||
|
||||
CloudCmd.log('init', name);
|
||||
await module.init();
|
||||
|
||||
|
||||
return newModule;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
CloudCmd[name].show = async (...args) => {
|
||||
CloudCmd.log('show', name, args);
|
||||
const m = CloudCmd[name];
|
||||
|
||||
|
||||
const [e, a] = await tryToCatch(m);
|
||||
|
||||
|
||||
if (e)
|
||||
return console.error(e);
|
||||
|
||||
|
||||
return await a.show(...args);
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,19 +1,21 @@
|
|||
/* global CloudCmd, DOM */
|
||||
|
||||
'use strict';
|
||||
|
||||
/* global CloudCmd, DOM */
|
||||
|
||||
const exec = require('execon');
|
||||
const wrap = require('wraptile');
|
||||
const supermenu = require('supermenu');
|
||||
const createElement = require('@cloudcmd/create-element');
|
||||
const load = require('load.js');
|
||||
|
||||
const {FS} = require('../../common/cloudfunc');
|
||||
const {getIdBySrc} = require('../dom/load');
|
||||
const RESTful = require('../dom/rest');
|
||||
|
||||
const {
|
||||
config,
|
||||
Key,
|
||||
prefix,
|
||||
DIR_CLIENT_MODULES,
|
||||
} = CloudCmd;
|
||||
|
||||
const {
|
||||
|
|
@ -35,23 +37,24 @@ module.exports.ENABLED = false;
|
|||
|
||||
CloudCmd.Menu = exports;
|
||||
|
||||
module.exports.init = () => {
|
||||
module.exports.init = async () => {
|
||||
await load.css(`${prefix}${DIR_CLIENT_MODULES}/menu.css`);
|
||||
const {
|
||||
isAuth,
|
||||
menuDataFile,
|
||||
} = getFileMenuData();
|
||||
|
||||
|
||||
const fm = DOM.getFM();
|
||||
const menuData = getMenuData(isAuth);
|
||||
const options = getOptions({type: 'context'});
|
||||
const optionsFile = getOptions({type: 'file'});
|
||||
|
||||
|
||||
MenuContext = supermenu(fm, options, menuData);
|
||||
MenuContextFile = supermenu(fm, optionsFile, menuDataFile);
|
||||
|
||||
|
||||
MenuContext.addContextMenuListener();
|
||||
MenuContextFile.addContextMenuListener();
|
||||
|
||||
|
||||
Events.addKey(listener);
|
||||
};
|
||||
|
||||
|
|
@ -64,10 +67,10 @@ function hide() {
|
|||
|
||||
module.exports.show = (position) => {
|
||||
const {x, y} = getPosition(position);
|
||||
|
||||
|
||||
MenuContext.show(x, y);
|
||||
MenuContextFile.show(x, y);
|
||||
|
||||
|
||||
Images.hide();
|
||||
};
|
||||
|
||||
|
|
@ -77,33 +80,33 @@ function getPosition(position) {
|
|||
x: position.x,
|
||||
y: position.y,
|
||||
};
|
||||
|
||||
|
||||
return getCurrentPosition();
|
||||
}
|
||||
|
||||
function getMenuNameByEl(el) {
|
||||
if (!el)
|
||||
return 'context';
|
||||
|
||||
|
||||
const name = DOM.getCurrentName(el);
|
||||
|
||||
|
||||
if (name === '..')
|
||||
return 'context';
|
||||
|
||||
|
||||
return 'contextFile';
|
||||
}
|
||||
|
||||
function getOptions({type}) {
|
||||
let name;
|
||||
let func;
|
||||
|
||||
|
||||
if (type === 'context') {
|
||||
name = 'context';
|
||||
func = Key.unsetBind;
|
||||
} else if (type === 'file') {
|
||||
name = 'contextFile';
|
||||
}
|
||||
|
||||
|
||||
const options = {
|
||||
icon : true,
|
||||
beforeClose : Key.setBind,
|
||||
|
|
@ -111,7 +114,7 @@ function getOptions({type}) {
|
|||
beforeClick,
|
||||
name,
|
||||
};
|
||||
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
@ -128,16 +131,16 @@ function getMenuData(isAuth) {
|
|||
'Upload From Cloud': uploadFromCloud,
|
||||
'(Un)Select All': DOM.toggleAllSelectedFiles,
|
||||
};
|
||||
|
||||
|
||||
if (isAuth)
|
||||
menu['Log Out'] = CloudCmd.logOut;
|
||||
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
function getFileMenuData() {
|
||||
const isAuth = CloudCmd.config('auth');
|
||||
|
||||
|
||||
const menuBottom = getMenuData(isAuth);
|
||||
const menuTop = {
|
||||
'View': () => {
|
||||
|
|
@ -168,12 +171,12 @@ function getFileMenuData() {
|
|||
isCurrent(Buffer.copy, alertNoFiles);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
const menuDataFile = {
|
||||
...menuTop,
|
||||
...menuBottom,
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
isAuth,
|
||||
menuDataFile,
|
||||
|
|
@ -183,21 +186,21 @@ function getFileMenuData() {
|
|||
function isCurrent(yesFn, noFn) {
|
||||
if (Info.name !== '..')
|
||||
return yesFn();
|
||||
|
||||
|
||||
noFn();
|
||||
}
|
||||
|
||||
function isPath(x, y) {
|
||||
const {panel} = Info;
|
||||
const isEmptyRoot = !panel;
|
||||
|
||||
|
||||
if (isEmptyRoot)
|
||||
return false;
|
||||
|
||||
|
||||
const el = document.elementFromPoint(x, y);
|
||||
const elements = panel.querySelectorAll('[data-name="js-path"] *');
|
||||
const is = ~[].indexOf.call(elements, el);
|
||||
|
||||
|
||||
return is;
|
||||
}
|
||||
|
||||
|
|
@ -207,22 +210,22 @@ function beforeShow(callback, params) {
|
|||
x: params.x,
|
||||
y: params.y,
|
||||
});
|
||||
|
||||
|
||||
const menuName = getMenuNameByEl(el);
|
||||
|
||||
|
||||
let isShow = menuName !== 'contextFile';
|
||||
|
||||
|
||||
if (params.name === 'contextFile')
|
||||
isShow = !isShow;
|
||||
|
||||
|
||||
if (isShow)
|
||||
MenuShowedName = name;
|
||||
|
||||
|
||||
exec(callback);
|
||||
|
||||
|
||||
if (isShow)
|
||||
isShow = isPath(params.x, params.y);
|
||||
|
||||
|
||||
return isShow;
|
||||
}
|
||||
|
||||
|
|
@ -232,26 +235,26 @@ function beforeClick(name) {
|
|||
|
||||
async function _uploadTo(nameModule) {
|
||||
const [error, data] = await Info.getData();
|
||||
|
||||
|
||||
if (error)
|
||||
return;
|
||||
|
||||
|
||||
const {name} = Info;
|
||||
|
||||
|
||||
CloudCmd.execFromModule(nameModule, 'uploadFile', name, data);
|
||||
CloudCmd.log('Uploading to ' + name + '...');
|
||||
}
|
||||
|
||||
function uploadFromCloud() {
|
||||
Images.show.load('top');
|
||||
|
||||
|
||||
CloudCmd.execFromModule('Cloud', 'saveFile', async (currentName, data) => {
|
||||
const path = DOM.getCurrentDirPath() + currentName;
|
||||
const [e] = await RESTful.write(path, data);
|
||||
|
||||
|
||||
if (e)
|
||||
return;
|
||||
|
||||
|
||||
await CloudCmd.refresh({currentName});
|
||||
});
|
||||
}
|
||||
|
|
@ -266,15 +269,15 @@ function download(type) {
|
|||
const PACK = '/pack';
|
||||
const date = Date.now();
|
||||
const files = DOM.getActiveFiles();
|
||||
|
||||
|
||||
if (!files.length)
|
||||
return alertNoFiles();
|
||||
|
||||
|
||||
for (const file of files) {
|
||||
const selected = DOM.isSelected(file);
|
||||
const isDir = DOM.isCurrentIsDir(file);
|
||||
const path = DOM.getCurrentPath(file);
|
||||
|
||||
|
||||
CloudCmd.log('downloading file ' + path + '...');
|
||||
/*
|
||||
* if we send ajax request -
|
||||
|
|
@ -283,26 +286,26 @@ function download(type) {
|
|||
*/
|
||||
const encodedPath = encodeURI(path).replace(/#/g, '%23');
|
||||
const id = getIdBySrc(path);
|
||||
|
||||
|
||||
let src;
|
||||
|
||||
|
||||
if (isDir)
|
||||
src = prefixURL + PACK + encodedPath + DOM.getPackerExt(type);
|
||||
else
|
||||
src = prefixURL + FS + encodedPath + '?download';
|
||||
|
||||
|
||||
const element = createElement('iframe', {
|
||||
id : id + '-' + date,
|
||||
async: false,
|
||||
className: 'hidden',
|
||||
src,
|
||||
});
|
||||
|
||||
|
||||
const {body} = document;
|
||||
const removeChild = body.removeChild.bind(body, element);
|
||||
|
||||
|
||||
setTimeout(removeChild, TIME);
|
||||
|
||||
|
||||
if (selected)
|
||||
DOM.toggleSelectedFile(file);
|
||||
}
|
||||
|
|
@ -311,12 +314,12 @@ function download(type) {
|
|||
function getCurrentPosition() {
|
||||
const current = Info.element;
|
||||
const rect = current.getBoundingClientRect();
|
||||
|
||||
|
||||
const position = {
|
||||
x: Math.round(rect.left + rect.width / 3),
|
||||
y: Math.round(rect.top),
|
||||
};
|
||||
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
|
|
@ -325,20 +328,21 @@ function listener(event) {
|
|||
F9,
|
||||
ESC,
|
||||
} = Key;
|
||||
|
||||
|
||||
const key = event.keyCode;
|
||||
const isBind = Key.isBind();
|
||||
|
||||
|
||||
if (!isBind)
|
||||
return;
|
||||
|
||||
|
||||
if (key === ESC)
|
||||
return hide();
|
||||
|
||||
|
||||
if (key === F9) {
|
||||
const position = getCurrentPosition();
|
||||
MenuContext.show(position.x, position.y);
|
||||
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,11 +27,15 @@ const sourceStore = fullstore();
|
|||
const Name = 'UserMenu';
|
||||
CloudCmd[Name] = module.exports;
|
||||
|
||||
const {Key} = CloudCmd;
|
||||
const {
|
||||
Key,
|
||||
prefix,
|
||||
DIR_CLIENT_MODULES,
|
||||
} = CloudCmd;
|
||||
|
||||
module.exports.init = async () => {
|
||||
await Promise.all([
|
||||
loadCSS(`${CloudCmd.prefix}/dist/user-menu.css`),
|
||||
loadCSS(`${prefix}${DIR_CLIENT_MODULES}/user-menu/index.css`),
|
||||
CloudCmd.View(),
|
||||
]);
|
||||
};
|
||||
|
|
@ -43,52 +47,52 @@ const {CurrentInfo} = DOM;
|
|||
|
||||
async function show() {
|
||||
Images.show.load('top');
|
||||
|
||||
|
||||
const {dirPath} = CurrentInfo;
|
||||
const res = await fetch(`${CloudCmd.prefix}/api/v1/user-menu?dir=${dirPath}`);
|
||||
const source = await res.text();
|
||||
const [error, userMenu] = tryCatch(getUserMenu, source);
|
||||
|
||||
|
||||
Images.hide();
|
||||
|
||||
|
||||
if (error)
|
||||
return Dialog.alert(getCodeFrame({error, source}));
|
||||
|
||||
|
||||
sourceStore(source);
|
||||
|
||||
|
||||
const {
|
||||
names,
|
||||
keys,
|
||||
items,
|
||||
settings,
|
||||
} = parseUserMenu(userMenu);
|
||||
|
||||
|
||||
if (settings.run)
|
||||
return runSelected(settings.select, items, runUserMenu);
|
||||
|
||||
|
||||
const button = createElement('button', {
|
||||
className: 'cloudcmd-user-menu-button',
|
||||
innerText: 'User Menu',
|
||||
notAppend: true,
|
||||
});
|
||||
|
||||
|
||||
const select = createElement('select', {
|
||||
className: 'cloudcmd-user-menu',
|
||||
innerHTML: fillTemplate(names),
|
||||
notAppend: true,
|
||||
size: 10,
|
||||
});
|
||||
|
||||
|
||||
button.addEventListener('click', onButtonClick(userMenu, select));
|
||||
select.addEventListener('dblclick', onDblClick(userMenu));
|
||||
select.addEventListener('keydown', onKeyDown({
|
||||
keys,
|
||||
userMenu,
|
||||
}));
|
||||
|
||||
|
||||
const afterShow = () => select.focus();
|
||||
const autoSize = true;
|
||||
|
||||
|
||||
CloudCmd.View.show([button, select], {
|
||||
autoSize,
|
||||
afterShow,
|
||||
|
|
@ -97,10 +101,10 @@ async function show() {
|
|||
|
||||
function fillTemplate(options) {
|
||||
const result = [];
|
||||
|
||||
|
||||
for (const option of options)
|
||||
result.push(`<option>${option}</option>`);
|
||||
|
||||
|
||||
return result.join('');
|
||||
}
|
||||
|
||||
|
|
@ -122,39 +126,39 @@ const onKeyDown = currify(async ({keys, userMenu}, e) => {
|
|||
keyCode,
|
||||
target,
|
||||
} = e;
|
||||
|
||||
|
||||
const keyName = e.key.toUpperCase();
|
||||
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
|
||||
let value;
|
||||
|
||||
|
||||
if (keyCode === Key.ESC)
|
||||
return hide();
|
||||
|
||||
|
||||
if (keyCode === Key.ENTER)
|
||||
value = userMenu[target.value];
|
||||
else if (keys[keyName])
|
||||
value = keys[keyName];
|
||||
else
|
||||
return navigate(target, e);
|
||||
|
||||
|
||||
await runUserMenu(value);
|
||||
});
|
||||
|
||||
const runUserMenu = async (fn) => {
|
||||
hide();
|
||||
|
||||
|
||||
const [error] = await tryToCatch(fn, {
|
||||
DOM,
|
||||
CloudCmd,
|
||||
tryToCatch,
|
||||
});
|
||||
|
||||
|
||||
if (!error)
|
||||
return;
|
||||
|
||||
|
||||
const source = sourceStore();
|
||||
return Dialog.alert(getCodeFrame({
|
||||
error,
|
||||
|
|
@ -164,25 +168,25 @@ const runUserMenu = async (fn) => {
|
|||
|
||||
function getCodeFrame({error, source}) {
|
||||
const {code} = error;
|
||||
|
||||
|
||||
if (!code || code === 'frame')
|
||||
return error.message;
|
||||
|
||||
|
||||
const [line, column] = parseError(error);
|
||||
const start = {
|
||||
line,
|
||||
column,
|
||||
};
|
||||
|
||||
|
||||
const location = {
|
||||
start,
|
||||
};
|
||||
|
||||
|
||||
const frame = codeFrameColumns(source, location, {
|
||||
message: error.message,
|
||||
highlightCode: false,
|
||||
});
|
||||
|
||||
|
||||
return `<pre>${frame}</pre>`;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,20 +64,20 @@ const Config = {
|
|||
Images.hide();
|
||||
Key.unsetBind();
|
||||
},
|
||||
|
||||
|
||||
beforeClose: () => {
|
||||
Events.rmKey(listener);
|
||||
Key.setBind();
|
||||
},
|
||||
|
||||
|
||||
afterShow: () => {
|
||||
El.focus();
|
||||
},
|
||||
|
||||
|
||||
onOverlayClick,
|
||||
afterClose: noop,
|
||||
autoSize: false,
|
||||
|
||||
|
||||
helpers: {
|
||||
title: {},
|
||||
},
|
||||
|
|
@ -86,62 +86,62 @@ module.exports._Config = Config;
|
|||
|
||||
module.exports.init = async () => {
|
||||
await loadAll();
|
||||
|
||||
|
||||
const events = [
|
||||
'click',
|
||||
'contextmenu',
|
||||
];
|
||||
|
||||
|
||||
events.forEach(addEvent(Overlay, onOverlayClick));
|
||||
};
|
||||
|
||||
async function show(data, options = {}) {
|
||||
const prefixURL = CloudCmd.prefixURL + FS;
|
||||
|
||||
|
||||
if (Loading)
|
||||
return;
|
||||
|
||||
|
||||
if (!options || options.bindKeys !== false)
|
||||
Events.addKey(listener);
|
||||
|
||||
|
||||
El = createElement('div', {
|
||||
className: 'view',
|
||||
notAppend: true,
|
||||
});
|
||||
|
||||
|
||||
El.tabIndex = 0;
|
||||
|
||||
|
||||
if (data) {
|
||||
if (isArray(data))
|
||||
El.append(...data);
|
||||
else
|
||||
El.append(data);
|
||||
|
||||
|
||||
modal.open(El, initConfig(options));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Images.show.load();
|
||||
|
||||
|
||||
const path = prefixURL + Info.path;
|
||||
const type = options.raw ? '' : await getType(path);
|
||||
|
||||
|
||||
switch(type) {
|
||||
default:
|
||||
return await viewFile();
|
||||
|
||||
|
||||
case 'markdown':
|
||||
return await CloudCmd.Markdown.show(Info.path);
|
||||
|
||||
|
||||
case 'html':
|
||||
return viewHtml(path);
|
||||
|
||||
|
||||
case 'image':
|
||||
return viewImage(Info.path, prefixURL);
|
||||
|
||||
|
||||
case 'media':
|
||||
return await viewMedia(path);
|
||||
|
||||
|
||||
case 'pdf':
|
||||
return viewPDF(path);
|
||||
}
|
||||
|
|
@ -154,11 +154,11 @@ function createIframe(src) {
|
|||
width: '100%',
|
||||
height: '100%',
|
||||
});
|
||||
|
||||
|
||||
element.addEventListener('load', () => {
|
||||
element.contentWindow.addEventListener('keydown', listener);
|
||||
});
|
||||
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
|
|
@ -169,21 +169,21 @@ function viewHtml(src) {
|
|||
|
||||
function viewPDF(src) {
|
||||
const element = createIframe(src);
|
||||
|
||||
|
||||
const options = assign({}, Config);
|
||||
|
||||
|
||||
if (CloudCmd.config('showFileName'))
|
||||
options.title = Info.name;
|
||||
|
||||
|
||||
modal.open(element, options);
|
||||
}
|
||||
|
||||
async function viewMedia(path) {
|
||||
const [e, element] = await getMediaElement(path);
|
||||
|
||||
|
||||
if (e)
|
||||
return alert(e);
|
||||
|
||||
|
||||
const allConfig = {
|
||||
...Config,
|
||||
...{
|
||||
|
|
@ -195,22 +195,22 @@ async function viewMedia(path) {
|
|||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
modal.open(element, allConfig);
|
||||
}
|
||||
|
||||
async function viewFile() {
|
||||
const [error, data] = await Info.getData();
|
||||
|
||||
|
||||
if (error)
|
||||
return Images.hide();
|
||||
|
||||
|
||||
const element = document.createTextNode(data);
|
||||
const options = Config;
|
||||
|
||||
|
||||
if (CloudCmd.config('showFileName'))
|
||||
options.title = Info.name;
|
||||
|
||||
|
||||
El.append(element);
|
||||
modal.open(El, options);
|
||||
}
|
||||
|
|
@ -220,25 +220,25 @@ const copy = (a) => assign({}, a);
|
|||
module.exports._initConfig = initConfig;
|
||||
function initConfig(options) {
|
||||
const config = copy(Config);
|
||||
|
||||
|
||||
if (!options)
|
||||
return config;
|
||||
|
||||
|
||||
const names = Object.keys(options);
|
||||
|
||||
|
||||
for (const name of names) {
|
||||
const isConfig = Boolean(config[name]);
|
||||
const item = options[name];
|
||||
|
||||
|
||||
if (!isFn(item) || !isConfig) {
|
||||
config[name] = options[name];
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
const fn = config[name];
|
||||
config[name] = series(fn, item);
|
||||
}
|
||||
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
|
|
@ -252,14 +252,14 @@ function viewImage(path, prefixURL) {
|
|||
href: `${prefixURL}${path}`,
|
||||
title: encode(basename(path)),
|
||||
});
|
||||
|
||||
|
||||
const names = Info.files
|
||||
.map(DOM.getCurrentPath)
|
||||
.filter(isSupportedImage);
|
||||
|
||||
|
||||
const titles = names
|
||||
.map(makeTitle);
|
||||
|
||||
|
||||
const index = names.indexOf(Info.path);
|
||||
const imageConfig = {
|
||||
index,
|
||||
|
|
@ -270,41 +270,41 @@ function viewImage(path, prefixURL) {
|
|||
title : {},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
const config = {
|
||||
...Config,
|
||||
...imageConfig,
|
||||
};
|
||||
|
||||
|
||||
modal.open(titles, config);
|
||||
}
|
||||
|
||||
async function getMediaElement(src) {
|
||||
check(src);
|
||||
|
||||
|
||||
const [error, template] = await tryToCatch(Files.get, 'view/media-tmpl');
|
||||
|
||||
|
||||
if (error)
|
||||
return [error];
|
||||
|
||||
|
||||
const {name} = Info;
|
||||
|
||||
|
||||
if (!TemplateAudio)
|
||||
TemplateAudio = template;
|
||||
|
||||
|
||||
const is = isAudio(name);
|
||||
const type = is ? 'audio' : 'video';
|
||||
|
||||
|
||||
const innerHTML = rendy(TemplateAudio, {
|
||||
src,
|
||||
type,
|
||||
name,
|
||||
});
|
||||
|
||||
|
||||
const element = createElement('div', {
|
||||
innerHTML,
|
||||
});
|
||||
|
||||
|
||||
return [null, element];
|
||||
}
|
||||
|
||||
|
|
@ -318,12 +318,12 @@ function check(src) {
|
|||
* @callback - executes, when everything loaded
|
||||
*/
|
||||
async function loadAll() {
|
||||
const {prefix} = CloudCmd;
|
||||
|
||||
const {DIRCLIENT_MODULES} = CloudCmd;
|
||||
|
||||
time(Name + ' load');
|
||||
|
||||
|
||||
Loading = true;
|
||||
await loadCSS(`${prefix}/dist/view.css`);
|
||||
await loadCSS(`${DIRCLIENT_MODULES}view/index.css`);
|
||||
Loading = false;
|
||||
}
|
||||
|
||||
|
|
@ -332,32 +332,32 @@ function onOverlayClick(event) {
|
|||
x: event.clientX,
|
||||
y: event.clientY,
|
||||
};
|
||||
|
||||
|
||||
setCurrentByPosition(position);
|
||||
}
|
||||
|
||||
function setCurrentByPosition(position) {
|
||||
const element = DOM.getCurrentByPosition(position);
|
||||
|
||||
|
||||
if (!element)
|
||||
return;
|
||||
|
||||
|
||||
const {
|
||||
files,
|
||||
filesPassive,
|
||||
} = Info;
|
||||
|
||||
|
||||
const isFiles = files.includes(element);
|
||||
const isFilesPassive = filesPassive.includes(element);
|
||||
|
||||
|
||||
if (!isFiles && !isFilesPassive)
|
||||
return;
|
||||
|
||||
|
||||
const isCurrent = DOM.isCurrentFile(element);
|
||||
|
||||
|
||||
if (isCurrent)
|
||||
return;
|
||||
|
||||
|
||||
DOM.setCurrentFile(element);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@
|
|||
content : '\e80c ';
|
||||
}
|
||||
|
||||
.icon-file::before, {
|
||||
.icon-file::before {
|
||||
font-family : 'Fontello';
|
||||
content : '\e80d ';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
|
||||
<link rel="icon" href="{{ prefix }}/favicon.ico">
|
||||
|
||||
<link rel=stylesheet href="{{ prefix }}/dist/cloudcmd.css">
|
||||
<link rel=stylesheet href="{{ prefix }}/dist/client/cloudcmd.css">
|
||||
<noscript>
|
||||
<link rel=stylesheet href="{{ prefix }}/dist/nojs.css">
|
||||
<link rel=stylesheet href="{{ prefix }}/dist/css/nojs.css">
|
||||
</noscript>
|
||||
<style data-name="columns">
|
||||
{{ columns }}
|
||||
|
|
@ -33,8 +33,7 @@
|
|||
<button id=shift~ class="cmd-button reduce-text icon-terminal" title="Terminal" >⇧ ~</button>
|
||||
<button id=contact class="cmd-button reduce-text icon-contact" title="Contact" ></button>
|
||||
</div>
|
||||
<script src="{{ prefix }}/dist/cloudcmd.common.js"></script>
|
||||
<script src="{{ prefix }}/dist/cloudcmd.js"></script>
|
||||
<script src="{{ prefix }}/dist/client/cloudcmd.js"></script>
|
||||
<script>
|
||||
CloudCmd({{ config }});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -164,6 +164,7 @@
|
|||
"css-modules-require-hook": "^4.2.3",
|
||||
"domtokenlist-shim": "^1.2.0",
|
||||
"emitify": "^4.0.1",
|
||||
"esbuild": "^0.17.5",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-plugin-n": "^15.2.4",
|
||||
"eslint-plugin-putout": "^16.0.0",
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ const fileop = require('@cloudcmd/fileop');
|
|||
const DIR_ROOT = DIR + '../';
|
||||
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
const getDist = (isDev) => isDev ? 'dist-dev' : 'dist';
|
||||
const getDist = () => 'html';//(isDev) => isDev ? 'dist-dev' : 'dist';
|
||||
|
||||
const getIndexPath = (isDev) => path.join(DIR, '..', `${getDist(isDev)}/index.html`);
|
||||
const html = fs.readFileSync(getIndexPath(isDev), 'utf8');
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const isDev = process.env.NODE_ENV === 'development';
|
|||
const getDist = (isDev) => isDev ? 'dist-dev' : 'dist';
|
||||
|
||||
const dist = getDist(isDev);
|
||||
const columnsDir = path.join(__dirname, '..', dist, 'columns');
|
||||
const columnsDir = path.join(__dirname, '..', 'dist', 'css', 'columns');
|
||||
|
||||
const names = fs.readdirSync(columnsDir)
|
||||
.filter(not(isMap));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue