feature(load-module) add

This commit is contained in:
coderaiser 2018-06-16 00:29:02 +03:00
parent 146b32b215
commit 4b528280ba
24 changed files with 1352 additions and 1491 deletions

View file

@ -9,6 +9,7 @@
"plugins": [
"@babel/plugin-transform-object-assign",
"@babel/plugin-proposal-object-rest-spread",
"@babel/plugin-syntax-dynamic-import",
"module:fast-async",
"module:babel-plugin-macros",
]

View file

@ -1,4 +1,5 @@
{
"parser": "babel-eslint",
"env": {
"es6": true,
"node": true,
@ -14,7 +15,7 @@
"indent": ["error", 4],
"semi": "error",
"no-console": 0,
"no-use-before-define": ["error", "nofunc"]
"no-use-before-define": 0,
},
"extends": [
"eslint:recommended"

View file

@ -11,6 +11,8 @@ yarn-error.log
yarn.lock
now.json
dist*/modules
modules/jquery-mouse-wheel
modules/jquery/dist

View file

@ -27,6 +27,8 @@ const devtool = isDev ? 'eval' : 'source-map';
const notEmpty = (a) => a;
const clean = (array) => array.filter(notEmpty);
const noParse = (a) => /\.spec\.js$/.test(a);
const babelDev = {
babelrc: false,
plugins: [
@ -55,8 +57,8 @@ const plugins = [
];
const splitChunks = {
chunks: 'all',
name: 'cloudcmd.common',
chunks: 'async',
};
module.exports = {
@ -96,6 +98,7 @@ module.exports = {
],
module: {
rules,
noParse,
},
plugins,
};

View file

@ -1,14 +1,16 @@
'use strict';
/* global Util, DOM */
/* global DOM */
const itype = require('itype/legacy');
const Emitify = require('emitify/legacy');
const inherits = require('inherits');
const rendy = require('rendy/legacy');
const wraptile = require('wraptile/legacy');
const exec = require('execon');
const {kebabToCamelCase} = require('../common/util');
const isDev = process.env.NODE_ENV === 'development';
const Images = require('./dom/images');
const {
unregisterSW,
@ -27,22 +29,21 @@ const {
buildFromJSON,
} = require('../common/cloudfunc');
/* global Util, DOM */
const loadModule = require('./load-module');
inherits(CloudCmdProto, Emitify);
module.exports = new CloudCmdProto(Util, DOM);
module.exports = new CloudCmdProto(DOM);
function CloudCmdProto(Util, DOM) {
function CloudCmdProto(DOM) {
let Key;
let Debug;
let Listeners;
const log = (str) => {
if (!Debug)
const log = (...a) => {
if (!isDev )
return;
console.log(str);
console.log(...a);
};
Emitify.call(this);
@ -75,16 +76,6 @@ function CloudCmdProto(Util, DOM) {
right: 'asc',
};
log.enable = () => {
Debug = true;
};
log.disable = () => {
Debug = false;
};
const kebabToCamelCase = Util.kebabToCamelCase;
/**
* Функция привязываеться ко всем ссылкам и
* загружает содержимое каталогов
@ -127,49 +118,6 @@ function CloudCmdProto(Util, DOM) {
}, panel, callback);
};
/**
* function load modules
* @params = {name, path, func, dobefore, arg}
*/
function loadModule(params) {
if (!params)
return;
let path = params.path;
const name = params.name || path && kebabToCamelCase(path);
const func = params.func;
const funcName = params.funcName;
const doBefore = params.dobefore;
const isContain = /\.js/.test(path);
if (!isContain)
path += '.js';
if (CloudCmd[name])
return;
CloudCmd[name] = (...args) => {
const prefix = CloudCmd.PREFIX;
const pathFull = prefix + CloudCmd.DIRCLIENT_MODULES + path;
exec(doBefore);
const done = (error) => {
const Proto = CloudCmd[name];
if (error || !itype.function(Proto))
return;
CloudCmd[name] = new Proto(...args);
};
return DOM.load.js(pathFull, func || done);
};
CloudCmd[name][funcName] = CloudCmd[name];
}
/**
* Конструктор CloudClient, который
* выполняет весь функционал по
@ -180,7 +128,6 @@ function CloudCmdProto(Util, DOM) {
initModules,
baseInit,
loadPlugins,
loadStyle,
exec.with(CloudCmd.route, location.hash),
], noop);
@ -217,13 +164,6 @@ function CloudCmdProto(Util, DOM) {
exec.if(document.body.scrollIntoViewIfNeeded, func, funcBefore);
};
function loadStyle(callback) {
const prefix = CloudCmd.PREFIX;
const name = prefix + '/dist/cloudcmd.common.css';
DOM.load.css(name, callback);
}
function loadPlugins(callback) {
const prefix = CloudCmd.PREFIX;
const plugins = prefix + '/plugins.js';
@ -263,7 +203,6 @@ function CloudCmdProto(Util, DOM) {
}
DOM.setCurrentFile(current);
CloudCmd.execFromModule(module, 'show');
};
@ -303,14 +242,10 @@ function CloudCmdProto(Util, DOM) {
};
const load = (name, path, dobefore) => {
const isTmpl = path === 'template';
const funcName = isTmpl ? 'get' : 'show';
loadModule({
name,
path,
dobefore,
funcName,
});
};
@ -372,16 +307,11 @@ function CloudCmdProto(Util, DOM) {
];
}
this.execFromModule = (moduleName, funcName, ...args) => {
const obj = CloudCmd[moduleName];
const isObj = itype.object(obj);
this.execFromModule = async (moduleName, funcName, ...args) => {
await CloudCmd[moduleName]();
exec.if(isObj, () => {
const obj = CloudCmd[moduleName];
const func = obj[funcName];
func(...args);
}, obj);
const func = CloudCmd[moduleName][funcName];
func(...args);
};
this.refresh = (options = {}, callback) => {

53
client/load-module.js Normal file
View file

@ -0,0 +1,53 @@
'use strict';
/* global CloudCmd */
const exec = require('execon');
const tryToCatch = require('try-to-catch/legacy');
const {
kebabToCamelCase,
} = require('../common/util');
/**
* function load modules
* @params = {name, path, func, dobefore, arg}
*/
module.exports = function loadModule(params) {
if (!params)
return;
let path = params.path;
const name = params.name || path && kebabToCamelCase(path);
const doBefore = params.dobefore;
if (CloudCmd[name])
return;
CloudCmd[name] = () => {
exec(doBefore);
return import(`./modules/${path}` /* webpackChunkName: "cloudcmd-" */).then(async (module) => {
const newModule = async (f) => f && f();
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);
a.show(...args);
};
};

View file

@ -2,10 +2,9 @@
'use strict';
CloudCmd.Cloud = CloudProto;
const exec = require('execon');
const currify = require('currify/legacy');
const {promisify} = require('es6-promisify');
const {log} = CloudCmd;
@ -15,11 +14,9 @@ const Images = require('../dom/images');
const upload = currify(_upload);
function CloudProto(callback) {
loadFiles(callback);
return module.exports;
}
module.exports.init = async () => {
await loadFiles();
};
module.exports.uploadFile = (filename, data) => {
const mimetype = '';
@ -52,7 +49,7 @@ function _upload(callback, file) {
});
}
function loadFiles(callback) {
const loadFiles = promisify((callback) => {
const js = '//api.filepicker.io/v2/filepicker.js';
load.js(js, () => {
@ -65,5 +62,5 @@ function loadFiles(callback) {
exec(callback);
});
});
}
});

View file

@ -7,21 +7,22 @@ require('../../css/config.css');
const rendy = require('rendy/legacy');
const exec = require('execon');
const currify = require('currify/legacy');
const wraptile = require('wraptile/legacy');
const squad = require('squad/legacy');
const input = require('../input');
const {promisify} = require('es6-promisify');
const input = require('../input');
const Images = require('../dom/images');
const Events = require('../dom/events');
const Files = require('../dom/files');
const {getTitle} = require('../../common/cloudfunc');
const {Dialog, setTitle} = DOM;
const TITLE = 'Config';
const alert = currify(Dialog.alert, TITLE);
const Config = module.exports;
const loadSocket = promisify(DOM.loadSocket);
const showLoad = () => {
Images.show.load('top');
@ -37,33 +38,18 @@ const addChange = currify((fn, input) => {
return input;
});
CloudCmd.Config = ConfigProto;
const Config = {};
let Loading = true;
function ConfigProto() {
const noop = () => {};
if (!CloudCmd.config('configDialog'))
return {
show: noop
};
Loading = true;
module.exports.init = async () => {
showLoad();
exec.series([
CloudCmd.View,
(callback) => {
Loading = false;
exec(callback);
DOM.loadSocket(initSocket);
},
show
]);
return module.exports;
}
await CloudCmd.View();
await loadSocket();
initSocket();
Loading = false;
};
const config = CloudCmd.config;
@ -113,10 +99,7 @@ function initSocket() {
function authCheck(socket) {
socket.emit('auth', config('username'), config('password'));
socket.on('reject', () => {
alert('Wrong credentials!');
});
socket.on('reject', wraptile(alert, 'Wrong credentials!'));
}
Config.save = saveHttp;
@ -124,6 +107,9 @@ Config.save = saveHttp;
module.exports.show = show;
function show() {
if (!CloudCmd.config('configDialog'))
return;
const prefix = CloudCmd.PREFIX;
const funcs = [
exec.with(Files.get, 'config-tmpl'),
@ -193,9 +179,11 @@ function fillTemplate(error, template) {
});
}
module.exports.hide = () => {
module.exports.hide = hide;
function hide() {
CloudCmd.View.hide();
};
}
function onChange(el) {
const obj = {};
@ -249,7 +237,7 @@ function onNameChange(name) {
function onKey({keyCode, target}) {
switch (keyCode) {
case Key.ESC:
Config.hide();
hide();
break;
case Key.ENTER:

View file

@ -2,54 +2,46 @@
/* global CloudCmd */
const exec = require('execon');
const Events = require('../dom/events');
const {Key} = CloudCmd;
CloudCmd.EditFileVim = function EditFileVimProto(callback) {
const EditFileVim = this;
const ConfigView = {
bindKeys: false,
beforeClose: () => {
Events.rmKey(listener);
CloudCmd.EditFile.isChanged();
}
};
function init(callback) {
exec.series([
CloudCmd.EditFile,
callback || EditFileVim.show,
]);
const ConfigView = {
bindKeys: false,
beforeClose: () => {
Events.rmKey(listener);
CloudCmd.EditFile.isChanged();
}
this.show = () => {
Events.addKey(listener);
CloudCmd.EditFile
.show(ConfigView)
.getEditor()
.setKeyMap('vim');
};
this.hide = () => {
CloudCmd.Edit.hide();
};
function listener(event) {
const {
keyCode,
shiftKey,
} = event;
if (shiftKey && keyCode === Key.ESC) {
event.preventDefault();
EditFileVim.hide();
}
}
init(callback);
};
module.exports.init = async () => {
await CloudCmd.EditFile();
};
module.exports.show = () => {
Events.addKey(listener);
CloudCmd.EditFile
.show(ConfigView)
.getEditor()
.setKeyMap('vim');
};
module.exports.hide = hide;
function hide() {
CloudCmd.Edit.hide();
}
function listener(event) {
const {
keyCode,
shiftKey,
} = event;
if (shiftKey && keyCode === Key.ESC) {
event.preventDefault();
hide();
}
}

View file

@ -3,204 +3,183 @@
/* global CloudCmd, DOM*/
const Format = require('format-io/legacy');
const currify = require('currify/legacy');
const store = require('fullstore/legacy');
const squad = require('squad/legacy');
const exec = require('execon');
const supermenu = require('supermenu');
const call = currify((fn, callback) => {
fn();
callback();
});
const Info = DOM.CurrentInfo;
const Dialog = DOM.Dialog;
const config = CloudCmd.config;
CloudCmd.EditFile = function EditFileProto(callback) {
const Info = DOM.CurrentInfo;
const Dialog = DOM.Dialog;
const EditFile = exec.bind();
const config = CloudCmd.config;
let Menu;
const TITLE = 'Edit';
const Images = DOM.Images;
let MSG_CHANGED;
const ConfigView = {
beforeClose: () => {
exec.ifExist(Menu, 'hide');
isChanged();
}
};
module.exports.init = async () => {
await CloudCmd.Edit();
let Menu;
const editor = CloudCmd.Edit.getEditor();
authCheck(editor);
setListeners(editor);
};
function getName() {
const {name, isDir} = Info;
const TITLE = 'Edit';
const Images = DOM.Images;
if (isDir)
return `${name}.json`;
let MSG_CHANGED;
const ConfigView = {
beforeClose: () => {
exec.ifExist(Menu, 'hide');
EditFile.isChanged();
}
return name;
}
module.exports.show = (options) => {
const config = {
...ConfigView,
...options,
};
function init(callback) {
const editor = store();
const getMainEditor = () => CloudCmd.Edit.getEditor();
const getEditor = squad(editor, getMainEditor);
const auth = squad(authCheck, editor);
const listeners = squad(setListeners, editor);
const show = callback ? exec : EditFile.show;
exec.series([
CloudCmd.Edit,
call(getEditor),
call(auth),
call(listeners),
show,
], callback);
}
Images.show.load();
function getName() {
const {name, isDir} = Info;
if (isDir)
return `${name}.json`;
return name;
}
CloudCmd.Edit
.getEditor()
.setOption('keyMap', 'default');
EditFile.show = (options) => {
const config = {
...ConfigView,
...options,
};
Info.getData((error, data) => {
const path = Info.path;
const name = getName();
Images.show.load();
if (error)
return Images.hide();
setMsgChanged(name);
CloudCmd.Edit
.getEditor()
.setOption('keyMap', 'default');
.setValueFirst(path, data)
.setModeForPath(name)
.enableKey();
Info.getData((error, data) => {
const path = Info.path;
const name = getName();
if (error)
return Images.hide();
setMsgChanged(name);
CloudCmd.Edit
.getEditor()
.setValueFirst(path, data)
.setModeForPath(name)
.enableKey();
CloudCmd.Edit.show(config);
});
return CloudCmd.Edit;
};
CloudCmd.Edit.show(config);
});
EditFile.hide = () => {
CloudCmd.Edit.hide();
};
function setListeners(editor) {
const element = CloudCmd.Edit.getElement();
DOM.Events.addOnce('contextmenu', element, setMenu);
editor.on('save', (value) => {
DOM.setCurrentSize(Format.size(value));
});
}
function authCheck(spawn) {
spawn.emit('auth', config('username'), config('password'));
spawn.on('reject', () => {
Dialog.alert(TITLE, 'Wrong credentials!');
});
}
function setMenu(event) {
const position = {
x: event.clientX,
y: event.clientY
};
event.preventDefault();
if (Menu)
return;
const options = {
beforeShow: (params) => {
params.x -= 18;
params.y -= 27;
},
afterClick: () => {
const editor = CloudCmd.Edit.getEditor();
editor.focus();
}
};
const element = CloudCmd.Edit.getElement();
Menu = supermenu(element, options, getMenuData());
Menu.show(position.x, position.y);
}
function getMenuData() {
const editor = CloudCmd.Edit.getEditor();
return {
'Save Ctrl+S' : () => {
editor.save();
},
'Go To Line Ctrl+G' : () => {
editor.goToLine();
},
'Cut Ctrl+X' : () => {
editor.cutToClipboard();
},
'Copy Ctrl+C' : () => {
editor.copyToClipboard();
},
'Paste Ctrl+V' : () => {
editor.pasteFromClipboard();
},
'Delete Del' : () => {
editor.remove('right');
},
'Select All Ctrl+A' : () => {
editor.selectAll();
},
'Beautify Ctrl+B' : () => {
editor.beautify();
},
'Minify Ctrl+M' : () => {
editor.minify();
},
'Close Esc' : () => {
EditFile.hide();
}
};
}
function setMsgChanged(name) {
MSG_CHANGED = 'Do you want to save changes to ' + name + '?';
}
EditFile.isChanged = () => {
const editor = CloudCmd.Edit.getEditor();
const is = editor.isChanged();
if (!is)
return;
const cancel = false;
Dialog.confirm(TITLE, MSG_CHANGED, {cancel})
.then(() => {
editor.save();
});
};
init(callback);
return EditFile;
return CloudCmd.Edit;
};
module.exports.hide = hide;
function hide() {
CloudCmd.Edit.hide();
}
function setListeners(editor) {
const element = CloudCmd.Edit.getElement();
DOM.Events.addOnce('contextmenu', element, setMenu);
editor.on('save', (value) => {
DOM.setCurrentSize(Format.size(value));
});
}
function authCheck(spawn) {
spawn.emit('auth', config('username'), config('password'));
spawn.on('reject', () => {
Dialog.alert(TITLE, 'Wrong credentials!');
});
}
function setMenu(event) {
const position = {
x: event.clientX,
y: event.clientY
};
event.preventDefault();
if (Menu)
return;
const options = {
beforeShow: (params) => {
params.x -= 18;
params.y -= 27;
},
afterClick: () => {
CloudCmd.Edit
.getEditor()
.focus();
}
};
const element = CloudCmd.Edit.getElement();
Menu = supermenu(element, options, getMenuData());
Menu.show(position.x, position.y);
}
function getMenuData() {
const editor = CloudCmd.Edit.getEditor();
return {
'Save Ctrl+S' : () => {
editor.save();
},
'Go To Line Ctrl+G' : () => {
editor.goToLine();
},
'Cut Ctrl+X' : () => {
editor.cutToClipboard();
},
'Copy Ctrl+C' : () => {
editor.copyToClipboard();
},
'Paste Ctrl+V' : () => {
editor.pasteFromClipboard();
},
'Delete Del' : () => {
editor.remove('right');
},
'Select All Ctrl+A' : () => {
editor.selectAll();
},
'Beautify Ctrl+B' : () => {
editor.beautify();
},
'Minify Ctrl+M' : () => {
editor.minify();
},
'Close Esc' : () => {
hide();
}
};
}
function setMsgChanged(name) {
MSG_CHANGED = 'Do you want to save changes to ' + name + '?';
}
module.exports.isChanged = isChanged;
function isChanged() {
const editor = CloudCmd.Edit.getEditor();
const is = editor.isChanged();
if (!is)
return;
const cancel = false;
Dialog.confirm(TITLE, MSG_CHANGED, {cancel})
.then(() => {
editor.save();
});
}

View file

@ -2,137 +2,122 @@
'use strict';
const exec = require('execon');
const currify = require('currify/legacy');
const {promisify} = require('es6-promisify');
const load = require('../dom/load');
const {MAX_FILE_SIZE: maxSize} = require('../../common/cloudfunc');
const {time, timeEnd} = require('../../common/util');
CloudCmd.Edit = EditProto;
const Name = 'Edit';
const EditorName = CloudCmd.config('editor');
function EditProto(callback) {
const Name = 'Edit';
const EditorName = CloudCmd.config('editor');
const loadFiles = currify(_loadFiles);
let Loading = true;
let Element;
let editor;
const ConfigView = {
afterShow: () => {
editor
.moveCursorTo(0, 0)
.focus();
}
};
const Edit = exec.bind();
function init(callback) {
const element = createElement();
exec.series([
CloudCmd.View,
loadFiles(element)
], callback);
let Loading = true;
let Element;
let editor;
const ConfigView = {
afterShow: () => {
editor
.moveCursorTo(0, 0)
.focus();
}
};
module.exports.init = async () => {
const element = createElement();
function createElement() {
const element = load({
name: 'div',
style:
'width : 100%;' +
'height : 100%;' +
'font-family: "Droid Sans Mono";' +
'position : absolute;',
notAppend: true
});
Element = element;
return element;
}
await CloudCmd.View();
await loadFiles(element);
};
function createElement() {
const element = load({
name: 'div',
style:
'width : 100%;' +
'height : 100%;' +
'font-family: "Droid Sans Mono";' +
'position : absolute;',
notAppend: true
});
function checkFn(name, fn) {
if (typeof fn !== 'function')
throw Error(name + ' should be a function!');
}
Element = element;
function initConfig(options = {}) {
const config = Object.assign({}, options, ConfigView);
if (!options.afterShow)
return config;
checkFn('options.afterShow', options.afterShow);
const afterShow = {config};
config.afterShow = () => {
afterShow();
options.afterShow();
};
return config;
}
Edit.show = (options) => {
if (Loading)
return;
CloudCmd.View.show(Element, initConfig(options));
Edit.getEditor()
.setOptions({
fontSize: 16,
});
return Edit;
};
Edit.getEditor = () => {
return editor;
};
Edit.getElement = () => {
return Element;
};
Edit.hide = () => {
CloudCmd.View.hide();
return Edit;
};
function _loadFiles(element, callback) {
const socketPath = CloudCmd.PREFIX;
const prefix = socketPath + '/' + EditorName;
const url = prefix + '/' + EditorName + '.js';
time(Name + ' load');
load.js(url, () => {
const word = window[EditorName];
const options = {
maxSize,
prefix,
socketPath,
};
word(element, options, (ed) => {
timeEnd(Name + ' load');
editor = ed;
Loading = false;
exec(callback);
});
});
}
init(callback);
return Edit;
return element;
}
function checkFn(name, fn) {
if (typeof fn !== 'function')
throw Error(name + ' should be a function!');
}
function initConfig(options = {}) {
const config = Object.assign({}, options, ConfigView);
if (!options.afterShow)
return config;
checkFn('options.afterShow', options.afterShow);
const afterShow = {config};
config.afterShow = () => {
afterShow();
options.afterShow();
};
return config;
}
module.exports.show = (options) => {
if (Loading)
return;
CloudCmd.View.show(Element, initConfig(options));
getEditor()
.setOptions({
fontSize: 16,
});
};
module.exports.getEditor = getEditor;
function getEditor() {
return editor;
}
module.exports.getElement = () => {
return Element;
};
module.exports.hide = () => {
CloudCmd.View.hide();
};
const loadFiles = promisify((element, callback) => {
const socketPath = CloudCmd.PREFIX;
const prefix = socketPath + '/' + EditorName;
const url = prefix + '/' + EditorName + '.js';
time(Name + ' load');
load.js(url, () => {
const word = window[EditorName];
const options = {
maxSize,
prefix,
socketPath,
};
word(element, options, (ed) => {
timeEnd(Name + ' load');
editor = ed;
Loading = false;
callback();
});
});
});

View file

@ -2,16 +2,11 @@
/* global CloudCmd */
CloudCmd.Help = HelpProto;
const Images = require('../dom/images');
function HelpProto() {
module.exports.init = () => {
Images.show.load('top');
show();
return exports;
}
};
module.exports.show = show;
module.exports.hide = hide;

View file

@ -6,6 +6,7 @@
/* global Console */
const exec = require('execon');
const {promisify} = require('es6-promisify');
const currify = require('currify/legacy');
const Images = require('../dom/images');
const {
@ -15,144 +16,130 @@ const {
const rmLastSlash = (a) => a.replace(/\/$/, '') || '/';
CloudCmd.Konsole = ConsoleProto;
let konsole;
const {config} = CloudCmd;
function ConsoleProto() {
let konsole;
const {config} = CloudCmd;
const cd = currify((fn, dir) => fn(`cd ${rmLastSlash(dir)}`));
const Name = 'Konsole';
const TITLE = 'Console';
let Element;
let Loaded;
module.exports.init = async () => {
Images.show.load('top');
const noop = () => {};
const cd = currify((fn, dir) => fn(`cd ${rmLastSlash(dir)}`));
if (!config('console'))
return {
show: noop
};
const Name = 'Konsole';
const TITLE = 'Console';
let Element;
let Loaded;
const Konsole = this;
function init() {
Images.show.load('top');
exec.series([
CloudCmd.View,
load,
create,
Konsole.show,
]);
Element = DOM.load({
name : 'div',
className : 'console'
});
}
this.hide = () => {
CloudCmd.View.hide();
};
this.clear = () => {
konsole.clear();
};
function getPrefix() {
return CloudCmd.PREFIX + '/console';
}
function getEnv() {
return {
ACTIVE_DIR: DOM.getCurrentDirPath.bind(DOM),
PASSIVE_DIR: DOM.getNotCurrentDirPath.bind(DOM),
CURRENT_NAME: DOM.getCurrentName.bind(DOM),
CURRENT_PATH: () => {
return Info.path;
}
};
}
function onPath(path) {
if (Info.dirPath === path)
return;
CloudCmd.loadDir({
path,
});
}
const getDirPath = () => {
if (config('syncConsolePath'))
return Info.dirPath;
};
function create(callback) {
const options = {
cwd: getDirPath(),
env: getEnv(),
prefix: getPrefix(),
socketPath: CloudCmd.PREFIX,
};
konsole = Console(Element, options, (spawn) => {
spawn.on('connect', exec.with(authCheck, spawn));
spawn.on('path', config.if('syncConsolePath', onPath));
CloudCmd.on('active-dir', config.if('syncConsolePath', cd(spawn.handler)));
exec(callback);
});
konsole.addShortCuts({
'P': () => {
const command = konsole.getPromptText();
const path = DOM.getCurrentDirPath();
konsole.setPromptText(command + path);
}
});
}
function authCheck(spawn) {
spawn.emit('auth', config('username'), config('password'));
spawn.on('reject', () => {
Dialog.alert(TITLE, 'Wrong credentials!');
});
}
this.show = (callback) => {
if (!Loaded)
return;
CloudCmd.View.show(Element, {
afterShow: () => {
konsole.focus();
exec(callback);
}
});
};
function load(callback) {
const prefix = getPrefix();
const url = prefix + '/console.js';
DOM.load.js(url, (error) => {
if (error)
return Dialog.alert(TITLE, error.message);
Loaded = true;
Util.timeEnd(Name + ' load');
exec(callback);
});
Util.time(Name + ' load');
}
init();
await CloudCmd.View();
await load();
await create();
};
module.exports.hide = () => {
CloudCmd.View.hide();
};
module.exports.clear = () => {
konsole.clear();
};
function getPrefix() {
return CloudCmd.PREFIX + '/console';
}
function getEnv() {
return {
ACTIVE_DIR: DOM.getCurrentDirPath.bind(DOM),
PASSIVE_DIR: DOM.getNotCurrentDirPath.bind(DOM),
CURRENT_NAME: DOM.getCurrentName.bind(DOM),
CURRENT_PATH: () => {
return Info.path;
}
};
}
function onPath(path) {
if (Info.dirPath === path)
return;
CloudCmd.loadDir({
path,
});
}
const getDirPath = () => {
if (config('syncConsolePath'))
return Info.dirPath;
};
const create = promisify((callback) => {
const options = {
cwd: getDirPath(),
env: getEnv(),
prefix: getPrefix(),
socketPath: CloudCmd.PREFIX,
};
Element = DOM.load({
name : 'div',
className : 'console'
});
konsole = Console(Element, options, (spawn) => {
spawn.on('connect', exec.with(authCheck, spawn));
spawn.on('path', config.if('syncConsolePath', onPath));
CloudCmd.on('active-dir', config.if('syncConsolePath', cd(spawn.handler)));
exec(callback);
});
konsole.addShortCuts({
'P': () => {
const command = konsole.getPromptText();
const path = DOM.getCurrentDirPath();
konsole.setPromptText(command + path);
}
});
});
function authCheck(spawn) {
spawn.emit('auth', config('username'), config('password'));
spawn.on('reject', () => {
Dialog.alert(TITLE, 'Wrong credentials!');
});
}
module.exports.show = (callback) => {
if (!Loaded)
return;
if (!config('console'))
return;
CloudCmd.View.show(Element, {
afterShow: () => {
konsole.focus();
exec(callback);
}
});
};
const load = promisify((callback) => {
const prefix = getPrefix();
const url = prefix + '/console.js';
DOM.load.js(url, (error) => {
if (error)
return Dialog.alert(TITLE, error.message);
Loaded = true;
Util.timeEnd(Name + ' load');
exec(callback);
});
Util.time(Name + ' load');
});

View file

@ -2,24 +2,14 @@
/*global CloudCmd */
CloudCmd.Markdown = MarkdownProto;
const exec = require('execon');
const Images = require('../dom/images');
const load = require('../dom/load');
const {Markdown} = require('../dom/rest');
function MarkdownProto(name, options) {
module.exports.init = async () => {
Images.show.load('top');
exec.series([
CloudCmd.View,
exec.with(show, name, options),
]);
return module.exports;
}
await CloudCmd.View();
};
module.exports.show = show;

View file

@ -2,8 +2,6 @@
'use strict';
CloudCmd.Menu = MenuProto;
const exec = require('execon');
const wrap = require('wraptile/legacy');
const supermenu = require('supermenu');
@ -12,324 +10,320 @@ const {FS} = require('../../common/cloudfunc');
const load = require('../dom/load');
const RESTful = require('../dom/rest');
function MenuProto(position) {
const config = CloudCmd.config;
const Buffer = DOM.Buffer;
const Info = DOM.CurrentInfo;
const Key = CloudCmd.Key;
const Events = DOM.Events;
const Dialog = DOM.Dialog;
const Images = DOM.Images;
const Menu = this;
const TITLE = 'Cloud Commander';
const alertNoFiles = wrap(Dialog.alert.noFiles)(TITLE);
const uploadTo = wrap(_uploadTo);
const config = CloudCmd.config;
const Buffer = DOM.Buffer;
const Info = DOM.CurrentInfo;
const Key = CloudCmd.Key;
const Events = DOM.Events;
const Dialog = DOM.Dialog;
const Images = DOM.Images;
const TITLE = 'Cloud Commander';
const alertNoFiles = wrap(Dialog.alert.noFiles)(TITLE);
const uploadTo = wrap(_uploadTo);
let MenuShowedName;
let MenuContext;
let MenuContextFile;
module.exports.ENABLED = false;
module.exports.init = () => {
const {isAuth, menuDataFile} = getFileMenuData();
let MenuShowedName;
let MenuContext;
let MenuContextFile;
const NOT_FILE = true;
const fm = DOM.getFM();
const menuData = getMenuData(isAuth);
const options = getOptions(NOT_FILE);
const optionsFile = getOptions();
this.ENABLED = false;
MenuContext = supermenu(fm, options, menuData);
MenuContextFile = supermenu(fm, optionsFile, menuDataFile);
function init(position) {
const {isAuth, menuDataFile} = getFileMenuData();
const NOT_FILE = true;
const fm = DOM.getFM();
const menuData = getMenuData(isAuth);
const options = getOptions(NOT_FILE);
const optionsFile = getOptions();
MenuContext = supermenu(fm, options, menuData);
MenuContextFile = supermenu(fm, optionsFile, menuDataFile);
Menu.show(position);
Events.addKey(listener);
}
Events.addKey(listener);
};
module.exports.hide = hide;
function hide() {
MenuContext.hide();
MenuContextFile.hide();
}
module.exports.show = (position) => {
const {x, y} = getPosition(position);
this.hide = () => {
MenuContext.hide();
MenuContextFile.hide();
};
MenuContext.show(x, y);
MenuContextFile.show(x, y);
this.show = (position) => {
const {x, y} = getPosition(position);
MenuContext.show(x, y);
MenuContextFile.show(x, y);
Images.hide();
};
function getPosition(position) {
if (position)
return {
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(notFile) {
let name, func;
if (notFile) {
name = 'context';
func = Key.unsetBind;
} else {
name = 'contextFile';
}
const options = {
icon : true,
beforeClose : Key.setBind,
beforeShow : exec.with(beforeShow, func),
beforeClick,
name,
};
return options;
}
function getMenuData(isAuth) {
const menu = {
'Paste': Buffer.paste,
'New': {
'File': DOM.promptNewFile,
'Directory': DOM.promptNewDir
},
'Upload': () => {
CloudCmd.Upload.show();
},
'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 show = wrap((name) => {
CloudCmd[name].show();
});
const menuBottom = getMenuData(isAuth);
const menuTop = {
'View': show('View'),
'Edit': show('EditFile'),
'Rename': () => {
setTimeout(DOM.renameCurrent, 100);
},
'Delete': () => {
CloudCmd.Operation.show('delete');
},
'Pack': () => {
CloudCmd.Operation.show('pack');
},
'Extract': () => {
CloudCmd.Operation.show('extract');
},
'Download': preDownload,
'Upload To Cloud': uploadTo('Cloud'),
'Cut': () => {
isCurrent(Buffer.cut, alertNoFiles);
},
'Copy': () => {
isCurrent(Buffer.copy, alertNoFiles);
},
};
const menuDataFile = {
...menuTop,
...menuBottom,
};
Images.hide();
};
function getPosition(position) {
if (position)
return {
isAuth,
menuDataFile,
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(notFile) {
let name, func;
if (notFile) {
name = 'context';
func = Key.unsetBind;
} else {
name = 'contextFile';
}
function isCurrent(yesFn, noFn) {
if (Info.name !== '..')
return yesFn();
noFn();
const options = {
icon : true,
beforeClose : Key.setBind,
beforeShow : exec.with(beforeShow, func),
beforeClick,
name,
};
return options;
}
function getMenuData(isAuth) {
const menu = {
'Paste': Buffer.paste,
'New': {
'File': DOM.promptNewFile,
'Directory': DOM.promptNewDir
},
'Upload': () => {
CloudCmd.Upload.show();
},
'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 show = wrap((name) => {
CloudCmd[name].show();
});
const menuBottom = getMenuData(isAuth);
const menuTop = {
'View': show('View'),
'Edit': show('EditFile'),
'Rename': () => {
setTimeout(DOM.renameCurrent, 100);
},
'Delete': () => {
CloudCmd.Operation.show('delete');
},
'Pack': () => {
CloudCmd.Operation.show('pack');
},
'Extract': () => {
CloudCmd.Operation.show('extract');
},
'Download': preDownload,
'Upload To Cloud': uploadTo('Cloud'),
'Cut': () => {
isCurrent(Buffer.cut, alertNoFiles);
},
'Copy': () => {
isCurrent(Buffer.copy, alertNoFiles);
},
};
const menuDataFile = {
...menuTop,
...menuBottom,
};
return {
isAuth,
menuDataFile,
};
}
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;
}
function beforeShow(callback, params) {
const name = params.name;
let el = DOM.getCurrentByPosition({
x: params.x,
y: params.y
});
const menuName = getMenuNameByEl(el);
let notShow = menuName === 'contextFile';
if (params.name === 'contextFile') {
notShow = !notShow;
}
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;
}
if (!notShow)
MenuShowedName = name;
function beforeShow(callback, params) {
const name = params.name;
let el = DOM.getCurrentByPosition({
x: params.x,
y: params.y
});
const menuName = getMenuNameByEl(el);
let notShow = menuName === 'contextFile';
if (params.name === 'contextFile') {
notShow = !notShow;
}
if (!notShow)
MenuShowedName = name;
exec(callback);
if (!notShow)
notShow = isPath(params.x, params.y);
return notShow;
}
exec(callback);
function beforeClick(name) {
return MenuShowedName !== name;
}
if (!notShow)
notShow = isPath(params.x, params.y);
function _uploadTo(nameModule) {
Info.getData((error, data) => {
if (error)
return;
const name = Info.name;
const execFrom = CloudCmd.execFromModule;
execFrom(nameModule, 'uploadFile', name, data);
});
CloudCmd.log('Uploading to ' + name + '...');
}
function uploadFromCloud() {
Images.show.load('top');
CloudCmd.execFromModule('Cloud', 'saveFile', (currentName, data) => {
const path = DOM.getCurrentDirPath() + currentName;
RESTful.write(path, data, (error) => {
if (error)
return;
CloudCmd.refresh({currentName});
});
});
}
function preDownload() {
download(config('packer'));
}
function download(type) {
const TIME = 30 * 1000;
const prefixUr = CloudCmd.PREFIX_URL;
const PACK = '/pack';
const date = Date.now();
const files = DOM.getActiveFiles();
if (!files.length)
return alertNoFiles();
files.forEach((file) => {
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 -
* no need in hash so we escape #
* and all other characters, like "%"
*/
const encodedPath = encodeURI(path).replace(/#/g, '%23');
const id = load.getIdBySrc(path);
let src;
if (isDir)
src = prefixUr + PACK + encodedPath + DOM.getPackerExt(type);
else
src = prefixUr + FS + encodedPath + '?download';
const element = load({
id : id + '-' + date,
name : 'iframe',
async : false,
className : 'hidden',
src,
});
const {body} = document;
const removeChild = body.removeChild.bind(body, element);
setTimeout(removeChild, TIME);
if (selected)
DOM.toggleSelectedFile(file);
});
}
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;
}
function listener(event) {
const F9 = Key.F9;
const ESC = Key.ESC;
const key = event.keyCode;
const isBind = Key.isBind();
if (!isBind)
return notShow;
}
function beforeClick(name) {
return MenuShowedName !== name;
}
function _uploadTo(nameModule) {
Info.getData((error, data) => {
if (error)
return;
if (key === ESC)
return Menu.hide();
if (key === F9) {
const position = getCurrentPosition();
MenuContext.show(position.x, position.y);
event.preventDefault();
}
}
const name = Info.name;
const execFrom = CloudCmd.execFromModule;
execFrom(nameModule, 'uploadFile', name, data);
});
init(position);
CloudCmd.log('Uploading to ' + name + '...');
}
function uploadFromCloud() {
Images.show.load('top');
CloudCmd.execFromModule('Cloud', 'saveFile', (currentName, data) => {
const path = DOM.getCurrentDirPath() + currentName;
RESTful.write(path, data, (error) => {
if (error)
return;
CloudCmd.refresh({currentName});
});
});
}
function preDownload() {
download(config('packer'));
}
function download(type) {
const TIME = 30 * 1000;
const prefixUr = CloudCmd.PREFIX_URL;
const PACK = '/pack';
const date = Date.now();
const files = DOM.getActiveFiles();
if (!files.length)
return alertNoFiles();
files.forEach((file) => {
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 -
* no need in hash so we escape #
* and all other characters, like "%"
*/
const encodedPath = encodeURI(path).replace(/#/g, '%23');
const id = load.getIdBySrc(path);
let src;
if (isDir)
src = prefixUr + PACK + encodedPath + DOM.getPackerExt(type);
else
src = prefixUr + FS + encodedPath + '?download';
const element = load({
id : id + '-' + date,
name : 'iframe',
async : false,
className : 'hidden',
src,
});
const {body} = document;
const removeChild = body.removeChild.bind(body, element);
setTimeout(removeChild, TIME);
if (selected)
DOM.toggleSelectedFile(file);
});
}
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;
}
function listener(event) {
const F9 = Key.F9;
const ESC = Key.ESC;
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();
}
}

View file

@ -1,43 +0,0 @@
'use strict';
const test = require('tape');
const getNextCurrentName = require('./get-next-current-name');
test('get-next-current-name', (t) => {
const names = [
'..',
'hello',
'1',
'2',
];
const removedNames = [
'1'
];
const name = getNextCurrentName('hello', names, removedNames);
t.equal(name, 'hello', 'should equal');
t.end();
});
test('get-next-current-name: all files removed', (t) => {
const names = [
'..',
'hello',
'1',
'2',
];
const removedNames = [
'1',
'2',
'hello',
];
const name = getNextCurrentName('2', names, removedNames);
t.equal(name, '..', 'should equal');
t.end();
});

View file

@ -5,10 +5,10 @@
'use strict';
CloudCmd.Operation = OperationProto;
const currify = require('currify/legacy');
const wraptile = require('wraptile/legacy');
const {promisify} = require('es6-promisify');
const exec = require('execon');
const {
@ -22,455 +22,453 @@ const getNextCurrentName = require('./get-next-current-name');
const removeQuery = (a) => a.replace(/\?.*/, '');
function OperationProto(operation, data) {
const Name = 'Operation';
const {
TITLE,
config,
} = CloudCmd;
const {Dialog, Images} = DOM;
const initOperations = wraptile(_initOperations);
const authCheck = wraptile(_authCheck);
const Name = 'Operation';
const {
TITLE,
config,
} = CloudCmd;
const {Dialog, Images} = DOM;
const initOperations = wraptile(_initOperations);
const authCheck = wraptile(_authCheck);
const Operation = {};
let Loaded;
let {
cp: copyFn,
mv: moveFn,
delete: deleteFn,
extract: extractFn,
} = RESTful;
let packZipFn = RESTful.pack;
let packTarFn = RESTful.pack;
const Info = DOM.CurrentInfo;
const showLoad = Images.show.load.bind(null, 'top');
const processFiles = currify(_processFiles);
const noFilesCheck = () => {
const {length} = DOM.getActiveFiles();
const is = Boolean(!length);
let Loaded;
if (is)
return Dialog.alert.noFiles(TITLE);
let {
cp: copyFn,
mv: moveFn,
delete: deleteFn,
extract: extractFn,
} = RESTful;
return is;
};
let packZipFn = RESTful.pack;
let packTarFn = RESTful.pack;
module.exports.init = promisify((callback) => {
showLoad();
const Info = DOM.CurrentInfo;
const showLoad = Images.show.load.bind(null, 'top');
const Operation = this;
const processFiles = currify(_processFiles);
const noFilesCheck = () => {
const {length} = DOM.getActiveFiles();
const is = Boolean(!length);
if (is)
return Dialog.alert.noFiles(TITLE);
return is;
};
function init() {
showLoad();
exec.series([
DOM.loadSocket,
(callback) => {
if (!config('progress'))
return callback();
load(initOperations(CloudCmd.PREFIX, callback));
},
() => {
Loaded = true;
Images.hide();
Operation.show(operation, data);
}
]);
}
function _authCheck(spawn, ok) {
const accept = wraptile(ok);
const alertDialog = wraptile(Dialog.alert);
spawn.on('accept', accept(spawn));
spawn.on('reject', alertDialog (TITLE, 'Wrong credentials!'));
spawn.emit('auth', config('username'), config('password'));
}
function _initOperations(socketPrefix, fn) {
const prefix = `${socketPrefix}/fileop`;
fileop({prefix, socketPrefix}, (e, operator) => {
fn();
operator.on('connect', authCheck(operator, onConnect));
operator.on('disconnect', onDisconnect);
});
}
function onConnect(operator) {
packTarFn = (data, callback) => {
operator.tar(data.from, data.to, data.names)
.then(setListeners({noContinue: true}, callback));
};
packZipFn = (data, callback) => {
operator.zip(data.from, data.to, data.names)
.then(setListeners({noContinue: true}, callback));
};
deleteFn = (from, files, callback) => {
from = removeQuery(from);
operator.remove(from, files)
.then(setListeners(callback));
};
copyFn = (data, callback) => {
operator.copy(data.from, data.to, data.names)
.then(setListeners(callback));
};
moveFn = (data, callback) => {
operator.move(data.from, data.to, data.names)
.then(setListeners(callback));
};
extractFn = (data, callback) => {
operator.extract(data.from, data.to)
.then(setListeners({noContinue: true}, callback));
};
}
function onDisconnect() {
packZipFn = RESTful.pack;
packTarFn = RESTful.pack;
deleteFn = RESTful.delete;
copyFn = RESTful.cp;
moveFn = RESTful.mv;
extractFn = RESTful.extract;
}
function getPacker(type) {
if (type === 'zip')
return packZipFn;
return packTarFn;
}
this.hide = () => {
CloudCmd.View.hide();
};
this.show = (operation, data) => {
if (!Loaded)
return;
if (operation === 'copy')
return Operation.copy(data);
if (operation === 'move')
return Operation.move(data);
if (operation === 'delete')
return Operation.delete();
if (operation === 'delete:silent')
return Operation.deleteSilent();
if (operation === 'pack')
return Operation.pack();
if (operation === 'extract')
return Operation.extract();
};
this.copy = processFiles({
type: 'copy',
});
this.move = processFiles({
type: 'move'
});
this.delete = () => {
promptDelete();
};
this.deleteSilent = () => {
deleteSilent();
};
this.pack = () => {
const isZip = config('packer') === 'zip';
twopack('pack', isZip ? 'zip' : 'tar');
};
this.extract = () => {
twopack('extract');
};
/**
* prompt and delete current file or selected files
*
* @currentFile
*/
function promptDelete() {
if (noFilesCheck())
return;
const msgAsk = 'Do you really want to delete the ';
const msgSel = 'selected ';
const files = DOM.getActiveFiles();
const names = DOM.getFilenames(files);
const n = names.length;
let msg;
if (n) {
let name = '';
for (let i = 0; i < 5 && i < n; i++)
name += '\n' + names[i];
if (n >= 5)
name += '\n...';
msg = msgAsk + msgSel + n + ' files/directories?\n' + encode(name);
} else {
const current = DOM.getCurrentFile();
const isDir = DOM.isCurrentIsDir(current);
const getType = (isDir) => {
return isDir ? 'directory' : 'file';
};
const type = getType(isDir) + ' ';
const name = DOM.getCurrentName(current);
msg = msgAsk + msgSel + type + name + '?';
}
const cancel = false;
Dialog.confirm(TITLE, msg, {cancel}).then(() => {
deleteSilent(files);
});
}
/**
* delete current or selected files
*
* @files
*/
function deleteSilent(files = DOM.getActiveFiles()) {
const query = '?files';
const path = Info.dirPath;
if (noFilesCheck())
return;
showLoad();
const removedNames = DOM.getFilenames(files);
const names = DOM.CurrentInfo.files.map(DOM.getCurrentName);
const prevCurrent = DOM.getCurrentName();
const currentName = getNextCurrentName(prevCurrent, names, removedNames);
deleteFn(path + query, removedNames, (e) => {
const Storage = DOM.Storage;
const dirPath = Info.dirPath;
!e && CloudCmd.refresh({
currentName
});
Storage.removeMatch(dirPath);
});
}
/*
* process files (copy or move)
* @param data
* @param operation
*/
function _processFiles(options, data) {
let selFiles, files;
let panel;
let shouldAsk;
let sameName;
let ok;
let from = '';
let to = '';
let names = [];
if (data) {
from = data.from;
to = data.to;
names = data.names;
panel = Info.panel;
} else {
from = Info.dirPath;
to = DOM.getNotCurrentDirPath();
selFiles = DOM.getSelectedFiles();
names = DOM.getFilenames(selFiles);
data = {};
shouldAsk = true;
panel = Info.panelPassive;
}
if (!names.length)
names.push(DOM.getCurrentName());
const name = names[0];
sameName = DOM.getCurrentByName(name, panel);
if (!data && noFilesCheck())
return;
const {type} = options;
const isCopy = type === 'copy';
const option = isCopy ? 'confirmCopy' : 'confirmMove';
const title = isCopy ? 'Copy' : 'Rename/Move';
const operation = isCopy ? copyFn : moveFn;
if (shouldAsk && config(option))
return message(title, to, names.map(encode))
.then(ask);
ask(to);
function ask(to) {
ok = from !== to && to;
if (ok && !shouldAsk || !sameName)
return go();
const str = `"${ name }" already exist. Overwrite?`;
const cancel = false;
Dialog.confirm(TITLE, str, {cancel}).then(go);
function go() {
showLoad();
files = {
from,
to,
names,
};
operation(files, () => {
DOM.Storage.remove(from, () => {
const {
panel,
panelPassive,
} = Info;
const setCurrent = () => {
const currentName = name || data.names[0];
DOM.setCurrentByName(currentName);
};
if (!Info.isOnePanel)
CloudCmd.refresh({
panel: panelPassive,
noCurrent: true,
});
CloudCmd.refresh({panel}, setCurrent);
});
});
}
}
}
function checkEmpty(name, operation) {
if (!operation)
throw Error(name + ' could not be empty!');
}
function twopack(operation, type) {
let op;
let fileFrom;
let currentName = Info.name;
const Images = DOM.Images;
const path = Info.path;
const dirPath = Info.dirPath;
const activeFiles = DOM.getActiveFiles();
const names = DOM.getFilenames(activeFiles);
checkEmpty('operation', operation);
if (!names.length)
return Dialog.alert.noFiles(TITLE);
switch(operation) {
case 'extract':
op = extractFn;
fileFrom = {
from: path,
to: dirPath
};
currentName = removeExtension(currentName);
break;
case 'pack':
op = getPacker(type);
if (names.length > 1)
currentName = Info.dir;
currentName += DOM.getPackerExt(type);
fileFrom = {
from: dirPath,
to: dirPath + currentName,
names,
};
break;
}
Images.show.load('top');
op(fileFrom, (error) => {
!error && CloudCmd.refresh({
currentName
});
});
}
function message(msg, to, names) {
const n = names.length;
const name = names[0];
msg += ' ';
if (names.length > 1)
msg += n + ' file(s)';
else
msg += '"' + name + '"';
msg += ' to';
const cancel = false;
return Dialog.prompt(TITLE, msg, to, {cancel});
}
function load(callback) {
const prefix = CloudCmd.PREFIX;
const file = `${prefix}/fileop/fileop.js`;
DOM.load.js(file, (error) => {
if (error) {
Dialog.alert(TITLE, error.message);
return exec(callback);
}
exec.series([
DOM.loadSocket,
(callback) => {
if (!config('progress'))
return callback();
load(initOperations(CloudCmd.PREFIX, callback));
},
(callback) => {
Loaded = true;
Util.timeEnd(Name + ' load');
exec(callback);
});
Util.time(Name + ' load');
}
Images.hide();
callback();
}
], callback);
});
function _authCheck(spawn, ok) {
const accept = wraptile(ok);
const alertDialog = wraptile(Dialog.alert);
init();
spawn.on('accept', accept(spawn));
spawn.on('reject', alertDialog (TITLE, 'Wrong credentials!'));
spawn.emit('auth', config('username'), config('password'));
}
function _initOperations(socketPrefix, fn) {
const prefix = `${socketPrefix}/fileop`;
fileop({prefix, socketPrefix}, (e, operator) => {
fn();
operator.on('connect', authCheck(operator, onConnect));
operator.on('disconnect', onDisconnect);
});
}
function onConnect(operator) {
packTarFn = (data, callback) => {
operator.tar(data.from, data.to, data.names)
.then(setListeners({noContinue: true}, callback));
};
packZipFn = (data, callback) => {
operator.zip(data.from, data.to, data.names)
.then(setListeners({noContinue: true}, callback));
};
deleteFn = (from, files, callback) => {
from = removeQuery(from);
operator.remove(from, files)
.then(setListeners(callback));
};
copyFn = (data, callback) => {
operator.copy(data.from, data.to, data.names)
.then(setListeners(callback));
};
moveFn = (data, callback) => {
operator.move(data.from, data.to, data.names)
.then(setListeners(callback));
};
extractFn = (data, callback) => {
operator.extract(data.from, data.to)
.then(setListeners({noContinue: true}, callback));
};
}
function onDisconnect() {
packZipFn = RESTful.pack;
packTarFn = RESTful.pack;
deleteFn = RESTful.delete;
copyFn = RESTful.cp;
moveFn = RESTful.mv;
extractFn = RESTful.extract;
}
function getPacker(type) {
if (type === 'zip')
return packZipFn;
return packTarFn;
}
module.exports.hide = () => {
CloudCmd.View.hide();
};
module.exports.show = (operation, data) => {
if (!Loaded)
return;
if (operation === 'copy')
return Operation.copy(data);
if (operation === 'move')
return Operation.move(data);
if (operation === 'delete')
return Operation.delete();
if (operation === 'delete:silent')
return Operation.deleteSilent();
if (operation === 'pack')
return Operation.pack();
if (operation === 'extract')
return Operation.extract();
};
Operation.copy = processFiles({
type: 'copy',
});
Operation.move = processFiles({
type: 'move'
});
Operation.delete = () => {
promptDelete();
};
Operation.deleteSilent = () => {
deleteSilent();
};
Operation.pack = () => {
const isZip = config('packer') === 'zip';
twopack('pack', isZip ? 'zip' : 'tar');
};
Operation.extract = () => {
twopack('extract');
};
/**
* prompt and delete current file or selected files
*
* @currentFile
*/
function promptDelete() {
if (noFilesCheck())
return;
const msgAsk = 'Do you really want to delete the ';
const msgSel = 'selected ';
const files = DOM.getActiveFiles();
const names = DOM.getFilenames(files);
const n = names.length;
let msg;
if (n) {
let name = '';
for (let i = 0; i < 5 && i < n; i++)
name += '\n' + names[i];
if (n >= 5)
name += '\n...';
msg = msgAsk + msgSel + n + ' files/directories?\n' + encode(name);
} else {
const current = DOM.getCurrentFile();
const isDir = DOM.isCurrentIsDir(current);
const getType = (isDir) => {
return isDir ? 'directory' : 'file';
};
const type = getType(isDir) + ' ';
const name = DOM.getCurrentName(current);
msg = msgAsk + msgSel + type + name + '?';
}
const cancel = false;
Dialog.confirm(TITLE, msg, {cancel}).then(() => {
deleteSilent(files);
});
}
/**
* delete current or selected files
*
* @files
*/
function deleteSilent(files = DOM.getActiveFiles()) {
const query = '?files';
const path = Info.dirPath;
if (noFilesCheck())
return;
showLoad();
const removedNames = DOM.getFilenames(files);
const names = DOM.CurrentInfo.files.map(DOM.getCurrentName);
const prevCurrent = DOM.getCurrentName();
const currentName = getNextCurrentName(prevCurrent, names, removedNames);
deleteFn(path + query, removedNames, (e) => {
const Storage = DOM.Storage;
const dirPath = Info.dirPath;
!e && CloudCmd.refresh({
currentName
});
Storage.removeMatch(dirPath);
});
}
/*
* process files (copy or move)
* @param data
* @param operation
*/
function _processFiles(options, data) {
let selFiles, files;
let panel;
let shouldAsk;
let sameName;
let ok;
let from = '';
let to = '';
let names = [];
if (data) {
from = data.from;
to = data.to;
names = data.names;
panel = Info.panel;
} else {
from = Info.dirPath;
to = DOM.getNotCurrentDirPath();
selFiles = DOM.getSelectedFiles();
names = DOM.getFilenames(selFiles);
data = {};
shouldAsk = true;
panel = Info.panelPassive;
}
if (!names.length)
names.push(DOM.getCurrentName());
const name = names[0];
sameName = DOM.getCurrentByName(name, panel);
if (!data && noFilesCheck())
return;
const {type} = options;
const isCopy = type === 'copy';
const option = isCopy ? 'confirmCopy' : 'confirmMove';
const title = isCopy ? 'Copy' : 'Rename/Move';
const operation = isCopy ? copyFn : moveFn;
if (shouldAsk && config(option))
return message(title, to, names.map(encode))
.then(ask);
ask(to);
function ask(to) {
ok = from !== to && to;
if (ok && !shouldAsk || !sameName)
return go();
const str = `"${ name }" already exist. Overwrite?`;
const cancel = false;
Dialog.confirm(TITLE, str, {cancel}).then(go);
function go() {
showLoad();
files = {
from,
to,
names,
};
operation(files, () => {
DOM.Storage.remove(from, () => {
const {
panel,
panelPassive,
} = Info;
const setCurrent = () => {
const currentName = name || data.names[0];
DOM.setCurrentByName(currentName);
};
if (!Info.isOnePanel)
CloudCmd.refresh({
panel: panelPassive,
noCurrent: true,
});
CloudCmd.refresh({panel}, setCurrent);
});
});
}
}
}
function checkEmpty(name, operation) {
if (!operation)
throw Error(name + ' could not be empty!');
}
function twopack(operation, type) {
let op;
let fileFrom;
let currentName = Info.name;
const Images = DOM.Images;
const path = Info.path;
const dirPath = Info.dirPath;
const activeFiles = DOM.getActiveFiles();
const names = DOM.getFilenames(activeFiles);
checkEmpty('operation', operation);
if (!names.length)
return Dialog.alert.noFiles(TITLE);
switch(operation) {
case 'extract':
op = extractFn;
fileFrom = {
from: path,
to: dirPath
};
currentName = removeExtension(currentName);
break;
case 'pack':
op = getPacker(type);
if (names.length > 1)
currentName = Info.dir;
currentName += DOM.getPackerExt(type);
fileFrom = {
from: dirPath,
to: dirPath + currentName,
names,
};
break;
}
Images.show.load('top');
op(fileFrom, (error) => {
!error && CloudCmd.refresh({
currentName
});
});
}
function message(msg, to, names) {
const n = names.length;
const name = names[0];
msg += ' ';
if (names.length > 1)
msg += n + ' file(s)';
else
msg += '"' + name + '"';
msg += ' to';
const cancel = false;
return Dialog.prompt(TITLE, msg, to, {cancel});
}
function load(callback) {
const prefix = CloudCmd.PREFIX;
const file = `${prefix}/fileop/fileop.js`;
DOM.load.js(file, (error) => {
if (error) {
Dialog.alert(TITLE, error.message);
return exec(callback);
}
Loaded = true;
Util.timeEnd(Name + ' load');
exec(callback);
});
Util.time(Name + ' load');
}

View file

@ -2,18 +2,18 @@
/* global CloudCmd, gritty */
const {promisify} = require('es6-promisify');
require('../../css/terminal.css');
const exec = require('execon');
const load = require('../dom/load');
const DOM = require('../dom');
const Images = require('../dom/images');
const {Dialog} = DOM;
const TITLE = 'Terminal';
CloudCmd.Terminal = TerminalProto;
const {Dialog} = DOM;
const {Key} = CloudCmd;
let Element;
@ -22,30 +22,26 @@ let Terminal;
const {config} = CloudCmd;
function TerminalProto() {
const noop = () => {};
if (!config('terminal'))
return {
show: noop
};
const loadAll = promisify((callback) => {
const prefix = getPrefix();
const url = prefix + '/gritty.js';
DOM.load.js(url, (error) => {
if (error)
return Dialog.alert(TITLE, error.message);
Loaded = true;
exec(callback);
});
});
module.exports.init = async () => {
Images.show.load('top');
exec.series([
CloudCmd.View,
loadAll,
create,
show,
]);
Element = load({
name: 'div',
className : 'terminal',
});
return module.exports;
}
await CloudCmd.View();
await loadAll();
await create();
};
module.exports.show = show;
module.exports.hide = hide;
@ -67,7 +63,12 @@ function getEnv() {
};
}
function create(callback) {
function create() {
Element = load({
name: 'div',
className : 'terminal',
});
const options = {
env: getEnv(),
prefix: getPrefix(),
@ -86,12 +87,11 @@ function create(callback) {
});
socket.on('connect', exec.with(authCheck, socket));
exec(callback);
}
function authCheck(spawn) {
spawn.emit('auth', config('username'), config('password'));
spawn.on('reject', () => {
Dialog.alert(TITLE, 'Wrong credentials!');
});
@ -101,6 +101,9 @@ function show(callback) {
if (!Loaded)
return;
if (!config('terminal'))
return;
CloudCmd.View.show(Element, {
afterShow: () => {
if (Terminal)
@ -111,16 +114,3 @@ function show(callback) {
});
}
function loadAll(callback) {
const prefix = getPrefix();
const url = prefix + '/gritty.js';
DOM.load.js(url, (error) => {
if (error)
return Dialog.alert(TITLE, error.message);
Loaded = true;
exec(callback);
});
}

View file

@ -2,25 +2,15 @@
'use strict';
const exec = require('execon');
const load = require('../dom/load');
const Files = require('../dom/files');
const Images = require('../dom/images');
const uploadFiles = require('../dom/upload-files');
CloudCmd.Upload = UploadProto;
function UploadProto() {
module.exports.init = async () => {
Images.show.load('top');
exec.series([
CloudCmd.View,
show
]);
return exports;
}
await CloudCmd.View();
};
module.exports.show = show;
module.exports.hide = hide;

View file

@ -8,6 +8,7 @@ const itype = require('itype/legacy');
const rendy = require('rendy/legacy');
const exec = require('execon');
const currify = require('currify/legacy');
const {promisify} = require('es6-promisify');
const {time} = require('../../common/util');
const {FS} = require('../../common/cloudfunc');
@ -23,10 +24,7 @@ const lifo = currify((fn, el, cb, name) => fn(name, el, cb));
const addEvent = lifo(Events.add);
const getRegExp = (ext) => RegExp(`\\.${ext}$`, 'i');
CloudCmd.View = ViewProto;
module.exports = exec.bind();
//module.exports = exec.bind();
module.exports.show = show;
module.exports.hide = hide;
@ -77,9 +75,7 @@ const Config = {
}
};
function ViewProto(callback) {
const func = callback || exec.with(show, null);
module.exports.init = promisify((fn) => {
Loading = true;
exec.series([
@ -87,9 +83,9 @@ function ViewProto(callback) {
loadAll,
(callback) => {
Loading = false;
exec(callback);
callback();
}
], func);
], fn);
Config.parent = Overlay = load({
id : 'js-view',
@ -103,9 +99,7 @@ function ViewProto(callback) {
];
events.forEach(addEvent(Overlay, onOverLayClick));
return module.exports;
}
});
function show(data, options) {
const prefixUrl = CloudCmd.PREFIX_URL + FS;

View file

@ -5,6 +5,7 @@ module.exports.unregisterSW = unregisterSW;
async function registerSW(prefix) {
prefix = prefix ? `/${prefix}/` : `/`;
if (!navigator.serviceWorker)
return;

View file

@ -4,6 +4,10 @@ const preval = require('preval.macro');
const tryToCatch = require('try-to-catch/legacy');
const currify = require('currify/legacy');
const isDev = process.env.NODE_ENV === 'development';
const tryWrap = (a) => (...b) => tryToCatch(a, ...b);
const wait = currify((f, e) => e.waitUntil(f()));
const respondWith = currify((f, e) => e.respondWith(f(e)));
const getPathName = (url) => new URL(url).pathname;
@ -62,10 +66,10 @@ async function onFetch(event) {
const cache = await caches.open(NAME);
const response = await cache.match(request);
if (response)
if (!isDev && response)
return response;
const [e, resp] = await tryToCatch(fetch, request.clone());
const [e, resp] = await tryToRequest(request);
if (e)
return console.error(e, response, pathname);
@ -92,3 +96,28 @@ async function addToCache(request, response) {
return cache.put(request, response);
}
async function tryToRequest(req) {
const {url} = req;
const path = url.replace(`${location.origin}/`, '');
const get = tryWrap(fetch);
const prefix = getPrefix();
if (prefix && /^dist/.test(path))
return await get(createRequest(`${prefix}${path}`));
return await get(req.clone());
}
function getPrefix() {
const {
href,
origin,
} = location;
const prefix = href
.replace(origin, '')
.replace('sw.js', '');
return prefix;
}

View file

@ -158,9 +158,11 @@
"devDependencies": {
"@babel/core": "^7.0.0-beta.49",
"@babel/plugin-proposal-object-rest-spread": "^7.0.0-beta.49",
"@babel/plugin-syntax-dynamic-import": "^7.0.0-beta.51",
"@babel/plugin-transform-object-assign": "^7.0.0-beta.49",
"@babel/preset-env": "^7.0.0-beta.49",
"@cloudcmd/clipboard": "^1.0.0",
"babel-eslint": "^8.2.3",
"babel-loader": "^8.0.0-beta",
"babel-plugin-macros": "^2.2.1",
"clean-css-loader": "^1.0.1",

View file

@ -1,8 +1,10 @@
'use strict';
const version = require('../../package').version;
const format = require('format-io');
const version = require('../../package').version;
const config = require('../config');
const getMemory = () => {
const rss = process.memoryUsage().rss;
return format.size(rss);
@ -11,5 +13,6 @@ const getMemory = () => {
module.exports = () => ({
version,
memory: getMemory(),
prefix: config('prefix'),
});