Compare commits

..

136 commits

Author SHA1 Message Date
coderiaser
78e87796df chore: cloudcmd: v19.1.9 2026-01-21 20:08:24 +02:00
coderiaser
75ad4415c4 feature: cloudcmd: @putout/eslint-flat v4.0.0 2026-01-21 20:06:53 +02:00
coderiaser
c5d9bd7c1f feature: client: key: vim: get rid of mock-require 2026-01-21 20:06:35 +02:00
coderiaser
f437a52ff0 feature: client: images: migrate to EMS 2026-01-21 19:59:27 +02:00
coderiaser
7192a56e94 feature: client: dom: current-file: migrate to ESM 2026-01-21 19:50:30 +02:00
coderiaser
b9dd4f2676 chore: cloudcmd: v19.1.8 2026-01-20 23:43:51 +02:00
coderiaser
e8cf3c92f9 chore: lint 2026-01-20 23:43:06 +02:00
coderiaser
d574a93d6d feature: client: key: migrate to ESM 2026-01-20 23:41:26 +02:00
coderiaser
8a769fd512 fix: client: modules: operation: no update after copy 2026-01-20 23:41:26 +02:00
coderiaser
3b409074c1 feature: client: modules: operation: migrate to ESM 2026-01-20 23:41:26 +02:00
coderiaser
3b6b0b5a5b feature: client: buffer: migrate to ESM 2026-01-20 23:41:26 +02:00
coderiaser
8876f050e0 feature: cloudcmd: eslint-plugin-putout v30.0.0 2026-01-20 23:41:26 +02:00
coderiaser
8507282d55 test: cloudcmd: client: key: rm skip 2026-01-20 23:41:26 +02:00
coderaiser
f61b21eecc chore: cloudcmd: actions: lint ☘️ 2026-01-17 12:36:49 +00:00
coderiaser
242820b7cf chore: cloudcmd: v19.1.7 2026-01-17 14:36:00 +02:00
coderiaser
dd240ba9b2 test: client: key: vim: rm skip 2026-01-17 14:36:00 +02:00
coderaiser
4b945c0047 chore: cloudcmd: actions: lint ☘️ 2026-01-17 12:23:41 +00:00
coderiaser
23a6a6981a feature: client: dom/events -> #dom/events 2026-01-17 14:22:49 +02:00
coderiaser
9cebb2416f feature: client: dom: events: migrate to ESM 2026-01-17 14:22:49 +02:00
coderiaser
a94fa0d465 feature: client: cloudcmd: migrate to ESM 2026-01-17 14:22:49 +02:00
coderiaser
3bdf47a5bb feature: client: migrate to ESM 2026-01-17 14:22:49 +02:00
coderaiser
0ccd109a50 chore: cloudcmd: actions: lint ☘️ 2026-01-16 22:25:24 +00:00
coderaiser
6b0bd2e1de
refactor: client: move out inner functions 2026-01-16 23:24:31 +01:00
coderiaser
c23a6a12c8 chore: cloudcmd: v19.1.6 2026-01-16 23:56:12 +02:00
coderiaser
a523ef65f5 fix: tests 2026-01-16 23:55:32 +02:00
coderaiser
0971ac4e94 chore: cloudcmd: actions: lint ☘️ 2026-01-16 21:49:12 +00:00
coderiaser
64654e8d5c feature: common: cloudfunc: migrate to ESM 2026-01-16 23:48:19 +02:00
coderaiser
a063353a27 chore: cloudcmd: actions: lint ☘️ 2026-01-16 21:21:49 +00:00
coderiaser
add31607f9 feature: common: cloudfunc: get rid of bas64 2026-01-16 23:20:54 +02:00
coderiaser
e36de00ce8 feature: modulas: migrate to ESM 2026-01-16 23:20:54 +02:00
coderaiser
2c1210bbb1 chore: cloudcmd: actions: lint ☘️ 2026-01-16 17:14:25 +00:00
coderiaser
85ebf21122 chore: cloudcmd: v19.1.5 2026-01-16 19:13:27 +02:00
coderiaser
450f14614d feature: client: improve testability 2026-01-16 19:12:44 +02:00
coderaiser
f75bf4a401 chore: cloudcmd: actions: lint ☘️ 2026-01-16 16:28:05 +00:00
coderiaser
d979e94927 feature: server: env: migrate to ESM 2026-01-16 18:27:01 +02:00
coderiaser
8de9bd0847 chore: webpack: migrate to ESM 2026-01-16 14:19:35 +02:00
coderiaser
e178321be9 chore: cloudcmd: v19.1.4 2026-01-15 22:14:35 +02:00
coderiaser
4b84d20bb0 chore: cloudcmd 2026-01-15 22:14:01 +02:00
coderiaser
6e778a35ba feature: client: sort: migrate to ESM 2026-01-15 18:03:09 +02:00
coderiaser
e27ef51d43 feature: client: sort: migrate to ESM 2026-01-15 18:02:54 +02:00
coderiaser
917f585137 feature: client: load-module: migrate to ESM 2026-01-15 17:55:17 +02:00
coderiaser
9950cacad9 feature: client: get-json-from-file-table: migrate to ESM 2026-01-15 17:53:45 +02:00
coderiaser
457e23f31d chore: cloudcmd: v19.1.3 2026-01-15 16:33:27 +02:00
coderiaser
cfa0b5e382 test: cloudcmd: client: key 2026-01-15 16:32:28 +02:00
coderiaser
f903c5c9c5 feature: cloudcmd: multi-rename v3.0.0 2026-01-15 16:22:47 +02:00
coderiaser
08dd5ac183 chore: cloudcmd: v19.1.2 2026-01-14 14:38:46 +02:00
coderiaser
9e2c5ac635 fix: client: edit-names: group rename not renaming (#453) 2026-01-14 14:37:29 +02:00
coderiaser
6856207d0d feature: server: env -> env.parse 2026-01-14 13:20:37 +02:00
coderiaser
f0dcbe946f fix: client: key: config 2026-01-14 13:18:53 +02:00
coderiaser
008279df57 chore: rm eslintrc 2026-01-13 16:03:49 +02:00
coderiaser
dc99417c27 feature: client: key: get rid of mock-require 2026-01-13 15:19:32 +02:00
coderiaser
12751646fe test: ployfill: get rid of mock-require 2026-01-13 13:47:14 +02:00
coderaiser
8234201a39 chore: cloudcmd: actions: lint ☘️ 2026-01-13 11:37:25 +00:00
coderiaser
4bb7d704b4 feature: client: modules: view: get rid of mock-require 2026-01-13 13:36:35 +02:00
coderiaser
feb5aad36b test: client: sw: register: get rid of mock-require 2026-01-13 13:28:31 +02:00
coderiaser
c231fca334 test: client: dom: operations: rename-current: get rid of mock-require 2026-01-12 21:48:18 +02:00
coderiaser
14452d05b6 test: client: dom: io: get rid of mock-require 2026-01-12 21:40:54 +02:00
coderiaser
5c1ad5f8ff chore: cloudcmd: v19.1.1 2026-01-12 13:33:21 +02:00
coderiaser
5b4bb90d61 test: client: dom: goTiDirectory: get rid of mock-require 2026-01-12 13:32:48 +02:00
coderiaser
5cc6f79d6a feature: cloudcmd: @cloudcmd/stub v5.0.0 2026-01-12 13:15:36 +02:00
coderiaser
024bc41345 feature: cloudcmd: fullstore v4.0.0 2026-01-12 13:15:36 +02:00
coderiaser
fb115c675e test: common: util: get rid of mock-require 2026-01-12 13:10:05 +02:00
coderiaser
53f6f9e76f feature: cloudcmd: globals v17.0.0 2026-01-04 22:43:24 +02:00
coderiaser
6d21c539d4 feature: cloudcmd: madrun v12.1.0 2026-01-04 22:43:21 +02:00
coderiaser
253389ea7b feature: cloudcmd: supertape v12.0.0 2026-01-04 22:43:17 +02:00
coderiaser
cdf11f7483 chore: cloudcmd: v19.1.0 2025-12-31 14:14:29 +02:00
coderiaser
0ff16314d7 feature: cloudcmd: redlint v5.0.0 2025-12-31 14:13:33 +02:00
coderiaser
cc889bda4f chore: lint 2025-12-31 14:13:10 +02:00
coderiaser
43edba8cb8 feature: cloudcmd: try-to-catch v4.0.0 2025-12-31 13:44:04 +02:00
coderiaser
06f3b78256 feature: cloudcmd: try-catch v4.0.4 2025-12-31 13:44:00 +02:00
coderiaser
dfcd655780 feature: deno config: add 2025-12-24 16:47:48 +02:00
coderiaser
ab20a462db feature: server: bun support (oven-sh/bun#25674) 2025-12-24 15:53:38 +02:00
coderiaser
2ec57132ed chore: cloudcmd: v19.0.17 2025-12-24 14:42:43 +02:00
coderiaser
0222d177fc feature: cloudcmd: gritty v9.0.0 2025-12-24 14:25:32 +02:00
coderiaser
db0e0aef73 chore: cloudcmd: v19.0.16 2025-12-05 19:32:16 +02:00
coderiaser
14ec19e80a feature: cloudcmd: find-up v8.0.0 2025-12-05 19:30:34 +02:00
coderiaser
e6a00979ff feature: cloudcmd: eslint-plugin-putout v29.0.2 2025-12-05 19:29:52 +02:00
coderiaser
5b5352c7d9 feature: cloudcmd: putout v41.0.0 2025-12-05 19:29:47 +02:00
coderiaser
78ddbd8a39 chore: cloudcmd: v19.0.15 2025-11-28 13:07:28 +02:00
coderiaser
0067653109 feature: cloudcmd: aleman v1.16.5 2025-11-28 13:06:31 +02:00
coderiaser
5a2ac765d4 chore: cloudcmd: v19.0.14 2025-11-27 20:17:40 +02:00
coderiaser
3ceb9a8c59 feature: cloudcmd: open v11.0.0 2025-11-27 20:16:41 +02:00
coderiaser
2a525e9bbb fix: aleman: copy paste in text editor (#449) 2025-11-27 20:16:05 +02:00
coderaiser
a49e963dc9 chore: lint 2025-10-26 15:17:50 +02:00
coderiaser
1037a1a264 chore: cloudcmd: v19.0.13 2025-09-26 13:21:16 +03:00
coderiaser
8477f3e466 feature: cloudcmd: aleman v1.16.3 (#446) 2025-09-26 13:20:41 +03:00
coderiaser
e5d004c0af chore: cloudcmd: v19.0.12 2025-09-25 20:26:41 +03:00
coderiaser
836e908e11 feature: cloudcmd: aleman v1.16.2 2025-09-25 20:25:54 +03:00
coderiaser
9fd4a451ab chore: cloudcmd: v19.0.11 2025-09-24 18:08:25 +03:00
coderiaser
f4386a6f0f feature: cloudcmd: aleman v1.16.1 2025-09-24 18:07:18 +03:00
coderaiser
91f9c0a1aa
chore: putout: client: disable nodejs 2025-09-24 08:13:59 +02:00
coderiaser
babeb9fb85 chore: cloudcmd: v19.0.10 2025-09-23 21:16:46 +03:00
coderiaser
2e667ba693 feature: cloudcmd: aleman v1.15.0 2025-09-23 21:16:21 +03:00
coderiaser
07d18cfe96 chore: cloudcmd: v19.0.9 2025-09-22 17:58:22 +03:00
coderiaser
60c56164b0 feature: cloudcmd: aleman v1.14.4 2025-09-22 17:57:42 +03:00
coderiaser
d8453236c2 chore: cloudcmd: v19.0.8 2025-09-20 13:14:00 +03:00
coderiaser
efe81320e1 feature: cloudcmd: aleman v1.14.3 2025-09-20 13:13:42 +03:00
coderaiser
14e6754be3 chore: cloudcmd: actions: lint ☘️ 2025-09-18 16:56:23 +00:00
coderiaser
3033aed2e3 chore: cloudcmd: v19.0.7 2025-09-18 19:55:21 +03:00
coderiaser
5b972e2e4a feature: cloudcmd: aleman v1.14.0 2025-09-18 19:54:51 +03:00
coderaiser
543e08a837 chore: cloudcmd: actions: lint ☘️ 2025-09-17 20:17:02 +00:00
coderiaser
2a8109f090 chore: cloudcmd: v19.0.6 2025-09-17 23:16:00 +03:00
coderiaser
39a24028ac feature: cloudcmd: aleman v1.13.0 2025-09-17 23:15:17 +03:00
coderiaser
3f3c644500 chore: cloudcmd: v19.0.5 2025-09-16 17:13:04 +03:00
coderiaser
38dd510158 feature: cloudcmd: aleman v1.12.4 2025-09-16 17:12:17 +03:00
coderiaser
64df81bc45 fix: cloudcmd: client: listeners: f9: stopPropagation 2025-09-16 17:11:32 +03:00
coderiaser
8366b3bbe2 chore: cloudcmd: v19.0.4 2025-09-15 22:11:21 +03:00
coderiaser
66db798c35 feature: cloudcmd: aleman v1.12.3 2025-09-15 22:10:56 +03:00
coderiaser
56ed1cd535 chore: cloudcmd: v19.0.3 2025-09-15 17:49:10 +03:00
coderiaser
c5aed16f63 feature: cloudcmd: aleman v1.12.2 2025-09-15 17:48:40 +03:00
coderiaser
1537fa73fc chore: cloudcmd: v19.0.2 2025-09-14 22:22:41 +03:00
coderiaser
511347d3ef feature: cloudcmd: aleman v1.11.0 2025-09-14 22:22:04 +03:00
coderiaser
c963ffefee chore: cloudcmd: v19.0.1 2025-09-14 22:10:54 +03:00
coderiaser
fc6304a1c6 fix: tmpl: config: aleman, supermenu 2025-09-14 22:10:30 +03:00
coderiaser
a05ecdb406 feature: cloudcmd: aleman v1.10.0 2025-09-14 22:07:48 +03:00
coderiaser
9e4d66df4d docs: help: menu: typos 2025-09-14 19:46:31 +03:00
coderiaser
3f7e17721e chore: cloudcmd: v19.0.0 2025-09-14 19:40:40 +03:00
coderiaser
50b19dcc44 feature: cloudcmd: menu: default: supermenu -> aleman 2025-09-14 19:38:46 +03:00
coderiaser
7fb130cdbd chore: actions: v24 2025-09-14 19:38:18 +03:00
coderiaser
5970f10a84 feature: cloudcmd: drop support of node < 22 2025-09-14 19:37:41 +03:00
coderiaser
84a51a932c chore: cloudcmd: v18.8.11 2025-09-14 19:12:26 +03:00
coderiaser
b0360d8ef4 feature: cloudcmd: aleman v1.9.1 2025-09-14 19:11:45 +03:00
coderiaser
00a20129de feature: cloudcmd: html: importsmap: add 2025-09-14 19:11:04 +03:00
coderiaser
5e657e9bd1 chore: cloudcmd: v18.8.10 2025-09-14 18:20:57 +03:00
coderiaser
2559343eef docs: help: menu: hot keys 2025-09-14 18:09:41 +03:00
coderiaser
ddf9e45567 feature: cloudcmd: aleman v1.9.0 2025-09-14 18:09:15 +03:00
coderiaser
84bfef562d chore: cloudcmd: v18.8.9 2025-09-14 12:13:49 +03:00
coderiaser
2e7bdb8aa0 feature: cloudcmd: aleman v1.8.0 2025-09-14 12:13:00 +03:00
coderiaser
a2f66951a8 chore: cloudcmd: v18.8.8 2025-09-13 16:56:52 +03:00
coderiaser
03631d95f7 feature: cloudcmd: aleman v1.7.0 2025-09-13 16:55:59 +03:00
coderiaser
1fc57fdb91 chore: cloudcmd: v18.8.7 2025-09-12 16:43:18 +03:00
coderiaser
09408af5dd feature: cloudcmd: aleman v1.6.1 2025-09-12 16:42:35 +03:00
coderiaser
4eb47f9d42 chore: cloudcmd: v18.8.6 2025-09-12 16:15:36 +03:00
coderiaser
4fcaf2885d feature: cloudcmd: aleman v1.6.0 2025-09-12 16:14:20 +03:00
coderiaser
eaea183536 chore: cloudcmd: v18.8.5 2025-09-10 19:56:40 +03:00
coderiaser
c69ec16ef0 feature: cloudcmd: aleman v1.5.0 2025-09-10 19:56:04 +03:00
129 changed files with 1534 additions and 1418 deletions

View file

@ -1,38 +0,0 @@
'use strict';
module.exports = {
extends: [
'plugin:putout/safe+align',
],
plugins: [
'putout',
'n',
],
rules: {
'key-spacing': 'off',
'n/prefer-node-protocol': 'error',
},
overrides: [{
files: ['bin/release.js'],
rules: {
'no-console': 'off',
'n/shebang': 'off',
},
}, {
files: ['client/dom/index.js'],
rules: {
'no-multi-spaces': 'off',
},
}, {
files: ['bin/cloudcmd.js'],
rules: {
'no-console': 'off',
},
}, {
files: ['{client,common,static}/**/*.js'],
env: {
browser: true,
},
}],
ignorePatterns: ['*.md{js}'],
};

View file

@ -11,12 +11,12 @@ jobs:
packages: write packages: write
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- uses: oven-sh/setup-bun@v1 - uses: oven-sh/setup-bun@v2
with: with:
bun-version: latest bun-version: latest
- name: Use Node.js 22.x - name: Use Node.js 22.x
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version: 22.x node-version: 22.x
- name: Install Redrun - name: Install Redrun

View file

@ -9,15 +9,16 @@ jobs:
strategy: strategy:
matrix: matrix:
node-version: node-version:
- 20.x
- 22.x - 22.x
- 24.x
- 25.x
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- uses: oven-sh/setup-bun@v1 - uses: oven-sh/setup-bun@v2
with: with:
bun-version: latest bun-version: latest
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- name: Install Redrun - name: Install Redrun

View file

@ -18,6 +18,7 @@ now.json
app.json app.json
bower.json bower.json
manifest.yml manifest.yml
deno.json
bin/release.mjs bin/release.mjs

View file

@ -7,6 +7,9 @@
"fontello.json", "fontello.json",
"*.md" "*.md"
], ],
"rules": {
"package-json/add-type": "off"
},
"match": { "match": {
"base64": { "base64": {
"types/convert-typeof-to-is-type": "off" "types/convert-typeof-to-is-type": "off"
@ -26,9 +29,12 @@
"server/{server,exit,terminal,distribute/log}.{js,mjs}": { "server/{server,exit,terminal,distribute/log}.{js,mjs}": {
"remove-console": "off" "remove-console": "off"
}, },
"client/{client,cloudcmd,load-module}.js": { "client/{client,cloudcmd,load-module}.{js,mjs}": {
"remove-console": "off" "remove-console": "off"
}, },
"client": {
"nodejs": "off"
},
"client/sw": { "client/sw": {
"remove-console": "off" "remove-console": "off"
}, },
@ -43,6 +49,9 @@
}, },
"vim.js": { "vim.js": {
"merge-duplicate-functions": "off" "merge-duplicate-functions": "off"
},
"common": {
"nodejs/declare": "off"
} }
} }
} }

View file

@ -1,9 +1,6 @@
'use strict'; import {env} from 'node:process';
import OptimizeCssAssetsPlugin from 'optimize-css-assets-webpack-plugin';
const {env} = require('node:process'); import MiniCssExtractPlugin from 'mini-css-extract-plugin';
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const isDev = env.NODE_ENV === 'development'; const isDev = env.NODE_ENV === 'development';
@ -29,7 +26,7 @@ const rules = [{
type: 'asset/inline', type: 'asset/inline',
}]; }];
module.exports = { export default {
plugins, plugins,
module: { module: {
rules, rules,

View file

@ -1,11 +1,9 @@
'use strict'; import {env} from 'node:process';
import HtmlWebpackPlugin from 'html-webpack-plugin';
const {env} = require('node:process');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const isDev = env.NODE_ENV === 'development'; const isDev = env.NODE_ENV === 'development';
const plugins = [ export const plugins = [
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
inject: false, inject: false,
template: 'html/index.html', template: 'html/index.html',
@ -13,10 +11,6 @@ const plugins = [
}), }),
]; ];
module.exports = {
plugins,
};
function getMinifyHtmlOptions() { function getMinifyHtmlOptions() {
return { return {
removeComments: true, removeComments: true,

View file

@ -1,18 +1,12 @@
'use strict'; import {resolve, sep} from 'node:path';
import {env} from 'node:process';
import webpack from 'webpack';
import WebpackBar from 'webpackbar';
const {
resolve,
sep,
join,
} = require('node:path');
const {env} = require('node:process');
const { const {
EnvironmentPlugin, EnvironmentPlugin,
NormalModuleReplacementPlugin, NormalModuleReplacementPlugin,
} = require('webpack'); } = webpack;
const WebpackBar = require('webpackbar');
const modules = './modules'; const modules = './modules';
const dirModules = './client/modules'; const dirModules = './client/modules';
@ -23,7 +17,7 @@ const dir = './client';
const {NODE_ENV} = env; const {NODE_ENV} = env;
const isDev = NODE_ENV === 'development'; const isDev = NODE_ENV === 'development';
const rootDir = join(__dirname, '..'); const rootDir = new URL('..', import.meta.url).pathname;
const dist = resolve(rootDir, 'dist'); const dist = resolve(rootDir, 'dist');
const distDev = resolve(rootDir, 'dist-dev'); const distDev = resolve(rootDir, 'dist-dev');
const devtool = isDev ? 'eval' : 'source-map'; const devtool = isDev ? 'eval' : 'source-map';
@ -31,7 +25,7 @@ const devtool = isDev ? 'eval' : 'source-map';
const notEmpty = (a) => a; const notEmpty = (a) => a;
const clean = (array) => array.filter(notEmpty); const clean = (array) => array.filter(notEmpty);
const noParse = (a) => /\.spec\.js$/.test(a); const noParse = (a) => a.endsWith('.spec.js');
const options = { const options = {
babelrc: true, babelrc: true,
}; };
@ -92,7 +86,7 @@ const splitChunks = {
}, },
}; };
module.exports = { export default {
resolve: { resolve: {
symlinks: false, symlinks: false,
alias: { alias: {
@ -100,8 +94,8 @@ module.exports = {
'node:path': 'path', 'node:path': 'path',
}, },
fallback: { fallback: {
path: require.resolve('path-browserify'), path: import.meta.resolve('path-browserify'),
process: require.resolve('process/browser'), process: import.meta.resolve('process/browser'),
}, },
}, },
devtool, devtool,
@ -120,7 +114,7 @@ module.exports = {
'terminal': `${dirCss}/terminal.css`, 'terminal': `${dirCss}/terminal.css`,
'user-menu': `${dirCss}/user-menu.css`, 'user-menu': `${dirCss}/user-menu.css`,
'sw': `${dir}/sw/sw.js`, 'sw': `${dir}/sw/sw.js`,
'cloudcmd': `${dir}/cloudcmd.js`, 'cloudcmd': `${dir}/cloudcmd.mjs`,
[`${modules}/edit`]: `${dirModules}/edit.js`, [`${modules}/edit`]: `${dirModules}/edit.js`,
[`${modules}/edit-file`]: `${dirModules}/edit-file.js`, [`${modules}/edit-file`]: `${dirModules}/edit-file.js`,
[`${modules}/edit-file-vim`]: `${dirModules}/edit-file-vim.js`, [`${modules}/edit-file-vim`]: `${dirModules}/edit-file-vim.js`,
@ -133,7 +127,7 @@ module.exports = {
[`${modules}/config`]: `${dirModules}/config/index.js`, [`${modules}/config`]: `${dirModules}/config/index.js`,
[`${modules}/contact`]: `${dirModules}/contact.js`, [`${modules}/contact`]: `${dirModules}/contact.js`,
[`${modules}/upload`]: `${dirModules}/upload.js`, [`${modules}/upload`]: `${dirModules}/upload.js`,
[`${modules}/operation`]: `${dirModules}/operation/index.js`, [`${modules}/operation`]: `${dirModules}/operation/index.mjs`,
[`${modules}/konsole`]: `${dirModules}/konsole.js`, [`${modules}/konsole`]: `${dirModules}/konsole.js`,
[`${modules}/terminal`]: `${dirModules}/terminal.js`, [`${modules}/terminal`]: `${dirModules}/terminal.js`,
[`${modules}/terminal-run`]: `${dirModules}/terminal-run.js`, [`${modules}/terminal-run`]: `${dirModules}/terminal-run.js`,

223
ChangeLog
View file

@ -1,3 +1,226 @@
2026.01.21, v19.1.9
feature:
- 75ad4415 cloudcmd: @putout/eslint-flat v4.0.0
- c5d9bd7c client: key: vim: get rid of mock-require
- f437a52f client: images: migrate to EMS
- 7192a56e client: dom: current-file: migrate to ESM
2026.01.20, v19.1.8
fix:
- 8a769fd5 client: modules: operation: no update after copy
feature:
- d574a93d client: key: migrate to ESM
- 3b409074 client: modules: operation: migrate to ESM
- 3b6b0b5a client: buffer: migrate to ESM
- 8876f050 cloudcmd: eslint-plugin-putout v30.0.0
2026.01.17, v19.1.7
feature:
- 23a6a698 client: dom/events -> #dom/events
- 9cebb241 client: dom: events: migrate to ESM
- a94fa0d4 client: cloudcmd: migrate to ESM
- 3bdf47a5 client: migrate to ESM
2026.01.16, v19.1.6
fix:
- a523ef65 tests
feature:
- 64654e8d common: cloudfunc: migrate to ESM
- add31607 common: cloudfunc: get rid of bas64
- e36de00c modulas: migrate to ESM
2026.01.16, v19.1.5
feature:
- 450f1461 client: improve testability
- d979e949 server: env: migrate to ESM
2026.01.15, v19.1.4
feature:
- 6e778a35 client: sort: migrate to ESM
- e27ef51d client: sort: migrate to ESM
- 917f5851 client: load-module: migrate to ESM
- 9950caca client: get-json-from-file-table: migrate to ESM
2026.01.15, v19.1.3
feature:
- f903c5c9 cloudcmd: multi-rename v3.0.0
2026.01.14, v19.1.2
fix:
- 9e2c5ac6 client: edit-names: group rename not renaming (#453)
- f0dcbe94 client: key: config
feature:
- 6856207d server: env -> env.parse
- dc99417c client: key: get rid of mock-require
- 4bb7d704 client: modules: view: get rid of mock-require
2026.01.12, v19.1.1
feature:
- 5cc6f79d cloudcmd: @cloudcmd/stub v5.0.0
- 024bc413 cloudcmd: fullstore v4.0.0
- 53f6f9e7 cloudcmd: globals v17.0.0
- 6d21c539 cloudcmd: madrun v12.1.0
- 253389ea cloudcmd: supertape v12.0.0
2025.12.31, v19.1.0
feature:
- 0ff16314 cloudcmd: redlint v5.0.0
- 43edba8c cloudcmd: try-to-catch v4.0.0
- 06f3b782 cloudcmd: try-catch v4.0.4
- dfcd6557 deno config: add
- ab20a462 server: bun support (oven-sh/bun#25674)
2025.12.24, v19.0.17
feature:
- 0222d177 cloudcmd: gritty v9.0.0
2025.12.05, v19.0.16
feature:
- 14ec19e8 cloudcmd: find-up v8.0.0
- e6a00979 cloudcmd: eslint-plugin-putout v29.0.2
- 5b5352c7 cloudcmd: putout v41.0.0
2025.11.28, v19.0.15
feature:
- 00676531 cloudcmd: aleman v1.16.5
2025.11.27, v19.0.14
fix:
- 2a525e9b aleman: copy paste in text editor (#449)
feature:
- 3ceb9a8c cloudcmd: open v11.0.0
2025.09.26, v19.0.13
feature:
- 8477f3e4 cloudcmd: aleman v1.16.3 (#446)
2025.09.25, v19.0.12
feature:
- 836e908e cloudcmd: aleman v1.16.2
2025.09.24, v19.0.11
feature:
- f4386a6f cloudcmd: aleman v1.16.1
2025.09.23, v19.0.10
feature:
- 2e667ba6 cloudcmd: aleman v1.15.0
2025.09.22, v19.0.9
feature:
- 60c56164 cloudcmd: aleman v1.14.4
2025.09.20, v19.0.8
feature:
- efe81320 cloudcmd: aleman v1.14.3
2025.09.18, v19.0.7
feature:
- 5b972e2e cloudcmd: aleman v1.14.0
2025.09.17, v19.0.6
feature:
- 39a24028 cloudcmd: aleman v1.13.0
2025.09.16, v19.0.5
fix:
- 64df81bc cloudcmd: client: listeners: f9: stopPropagation
feature:
- 38dd5101 cloudcmd: aleman v1.12.4
2025.09.15, v19.0.4
feature:
- 66db798c cloudcmd: aleman v1.12.3
2025.09.15, v19.0.3
feature:
- c5aed16f cloudcmd: aleman v1.12.2
2025.09.14, v19.0.2
feature:
- 511347d3 cloudcmd: aleman v1.11.0
2025.09.14, v19.0.1
fix:
- fc6304a1 tmpl: config: aleman, supermenu
feature:
- a05ecdb4 cloudcmd: aleman v1.10.0
2025.09.14, v19.0.0
feature:
- 50b19dcc cloudcmd: menu: default: supermenu -> aleman
- 5970f10a cloudcmd: drop support of node < 22
2025.09.14, v18.8.11
feature:
- b0360d8e cloudcmd: aleman v1.9.1
- 00a20129 cloudcmd: html: importsmap: add
2025.09.14, v18.8.10
feature:
- ddf9e455 cloudcmd: aleman v1.9.0
2025.09.14, v18.8.9
feature:
- 2e7bdb8a cloudcmd: aleman v1.8.0
2025.09.13, v18.8.8
feature:
- 03631d95 cloudcmd: aleman v1.7.0
2025.09.12, v18.8.7
feature:
- 09408af5 cloudcmd: aleman v1.6.1
2025.09.12, v18.8.6
feature:
- 4fcaf288 cloudcmd: aleman v1.6.0
2025.09.10, v18.8.5
feature:
- c69ec16e cloudcmd: aleman v1.5.0
2025.09.09, v18.8.4 2025.09.09, v18.8.4
feature: feature:

58
HELP.md
View file

@ -1,4 +1,4 @@
# Cloud Commander v18.8.4 # Cloud Commander v19.1.9
### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL] ### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL]
@ -190,7 +190,8 @@ Then, start the server again with `cloudcmd` and reload the page.
| `Ctrl + F5` | sort by date | `Ctrl + F5` | sort by date
| `Ctrl + F6` | sort by size | `Ctrl + F6` | sort by size
| `Ctrl + Command + .` | show/hide dot files | `Ctrl + Command + .` | show/hide dot files
| `Up`, `Down` | file system navigation | `Up` | move cursor up
| `Down` | move cursor down
| `Enter` | change directory/view file | `Enter` | change directory/view file
| `Alt + Left/Right` | show content of directory under cursor in target panel | `Alt + Left/Right` | show content of directory under cursor in target panel
| `Alt + G` | go to directory | `Alt + G` | go to directory
@ -646,10 +647,20 @@ Right-mouse click to show a context menu with these items:
### Hot keys ### Hot keys
|Key |Operation | Key | Operation |
|:----------------------|:-------------------------------------------- |:-------------|:------------------------|
| `F9` | open | `F9` | open |
| `Esc` | close | `Esc` | close |
| `Up`, `j` | move cursor up |
| `Down`, `k` | move cursor down |
| `Left`, `h` | close submenu |
| `Right`, `l` | open submenu |
| `G` or `$` | navigate to bottom |
| `gg` or `^` | navigate to top |
Commands can be joined, for example:
- `5j` will navigate **5** items below current;
## One file panel ## One file panel
@ -1100,6 +1111,41 @@ There are a lot of ways to be involved in `Cloud Commander` development:
## Version history ## Version history
- *2026.01.21*, **[v19.1.9](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.9)**
- *2026.01.20*, **[v19.1.8](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.8)**
- *2026.01.17*, **[v19.1.7](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.7)**
- *2026.01.16*, **[v19.1.6](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.6)**
- *2026.01.16*, **[v19.1.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.5)**
- *2026.01.15*, **[v19.1.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.4)**
- *2026.01.15*, **[v19.1.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.3)**
- *2026.01.14*, **[v19.1.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.2)**
- *2026.01.12*, **[v19.1.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.1)**
- *2025.12.31*, **[v19.1.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.1.0)**
- *2025.12.24*, **[v19.0.17](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.17)**
- *2025.12.05*, **[v19.0.16](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.16)**
- *2025.11.28*, **[v19.0.15](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.15)**
- *2025.11.27*, **[v19.0.14](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.14)**
- *2025.09.26*, **[v19.0.13](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.13)**
- *2025.09.25*, **[v19.0.12](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.12)**
- *2025.09.24*, **[v19.0.11](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.11)**
- *2025.09.23*, **[v19.0.10](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.10)**
- *2025.09.22*, **[v19.0.9](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.9)**
- *2025.09.20*, **[v19.0.8](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.8)**
- *2025.09.18*, **[v19.0.7](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.7)**
- *2025.09.17*, **[v19.0.6](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.6)**
- *2025.09.16*, **[v19.0.5](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.5)**
- *2025.09.15*, **[v19.0.4](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.4)**
- *2025.09.15*, **[v19.0.3](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.3)**
- *2025.09.14*, **[v19.0.2](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.2)**
- *2025.09.14*, **[v19.0.1](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.1)**
- *2025.09.14*, **[v19.0.0](//github.com/coderaiser/cloudcmd/releases/tag/v19.0.0)**
- *2025.09.14*, **[v18.8.11](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.11)**
- *2025.09.14*, **[v18.8.10](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.10)**
- *2025.09.14*, **[v18.8.9](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.9)**
- *2025.09.13*, **[v18.8.8](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.8)**
- *2025.09.12*, **[v18.8.7](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.7)**
- *2025.09.12*, **[v18.8.6](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.6)**
- *2025.09.10*, **[v18.8.5](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.5)**
- *2025.09.09*, **[v18.8.4](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.4)** - *2025.09.09*, **[v18.8.4](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.4)**
- *2025.09.04*, **[v18.8.3](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.3)** - *2025.09.04*, **[v18.8.3](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.3)**
- *2025.09.04*, **[v18.8.2](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.2)** - *2025.09.04*, **[v18.8.2](//github.com/coderaiser/cloudcmd/releases/tag/v18.8.2)**

View file

@ -1,4 +1,4 @@
# Cloud Commander v18.8.4 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL] # Cloud Commander v19.1.9 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL]
### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL] ### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL]

View file

@ -3,12 +3,12 @@
import process from 'node:process'; import process from 'node:process';
import {createRequire} from 'node:module'; import {createRequire} from 'node:module';
import {promisify} from 'node:util'; import {promisify} from 'node:util';
import tryToCatch from 'try-to-catch'; import {tryToCatch} from 'try-to-catch';
import {createSimport} from 'simport'; import {createSimport} from 'simport';
import parse from 'yargs-parser'; import parse from 'yargs-parser';
import exit from '../server/exit.js'; import exit from '../server/exit.js';
import {createConfig, configPath} from '../server/config.js'; import {createConfig, configPath} from '../server/config.mjs';
import env from '../server/env.js'; import * as env from '../server/env.mjs';
import prefixer from '../server/prefixer.js'; import prefixer from '../server/prefixer.js';
import * as validate from '../server/validate.mjs'; import * as validate from '../server/validate.mjs';
@ -101,27 +101,27 @@ const yargsOptions = {
], ],
default: { default: {
'server': true, 'server': true,
'name': choose(env('name'), config('name')), 'name': choose(env.parse('name'), config('name')),
'auth': choose(env.bool('auth'), config('auth')), 'auth': choose(env.bool('auth'), config('auth')),
'port': config('port'), 'port': config('port'),
'online': config('online'), 'online': config('online'),
'open': choose(env.bool('open'), config('open')), 'open': choose(env.bool('open'), config('open')),
'editor': env('editor') || config('editor'), 'editor': env.parse('editor') || config('editor'),
'menu': env('menu') || config('menu'), 'menu': env.parse('menu') || config('menu'),
'packer': config('packer') || 'tar', 'packer': config('packer') || 'tar',
'zip': config('zip'), 'zip': config('zip'),
'username': env('username') || config('username'), 'username': env.parse('username') || config('username'),
'root': choose(env('root'), config('root')), 'root': choose(env.parse('root'), config('root')),
'prefix': choose(env('prefix'), config('prefix')), 'prefix': choose(env.parse('prefix'), config('prefix')),
'console': choose(env.bool('console'), config('console')), 'console': choose(env.bool('console'), config('console')),
'contact': choose(env.bool('contact'), config('contact')), 'contact': choose(env.bool('contact'), config('contact')),
'terminal': choose(env.bool('terminal'), config('terminal')), 'terminal': choose(env.bool('terminal'), config('terminal')),
'columns': env('columns') || config('columns') || '', 'columns': env.parse('columns') || config('columns') || '',
'theme': env('theme') || config('theme') || '', 'theme': env.parse('theme') || config('theme') || '',
'vim': choose(env.bool('vim'), config('vim')), 'vim': choose(env.bool('vim'), config('vim')),
'log': config('log'), 'log': config('log'),
'import-url': env('import_url') || config('importUrl'), 'import-url': env.parse('import_url') || config('importUrl'),
'import-listen': choose(env.bool('import_listen'), config('importListen')), 'import-listen': choose(env.bool('import_listen'), config('importListen')),
'import': choose(env.bool('import'), config('import')), 'import': choose(env.bool('import'), config('import')),
'export': choose(env.bool('export'), config('export')), 'export': choose(env.bool('export'), config('export')),
@ -132,15 +132,15 @@ const yargsOptions = {
'sync-console-path': choose(env.bool('sync_console_path'), config('syncConsolePath')), 'sync-console-path': choose(env.bool('sync_console_path'), config('syncConsolePath')),
'config-dialog': choose(env.bool('config_dialog'), config('configDialog')), 'config-dialog': choose(env.bool('config_dialog'), config('configDialog')),
'config-auth': choose(env.bool('config_auth'), config('configAuth')), 'config-auth': choose(env.bool('config_auth'), config('configAuth')),
'terminal-path': env('terminal_path') || config('terminalPath'), 'terminal-path': env.parse('terminal_path') || config('terminalPath'),
'terminal-command': env('terminal_command') || config('terminalCommand'), 'terminal-command': env.parse('terminal_command') || config('terminalCommand'),
'terminal-auto-restart': choose(env.bool('terminal_auto_restart'), config('terminalAutoRestart')), 'terminal-auto-restart': choose(env.bool('terminal_auto_restart'), config('terminalAutoRestart')),
'one-file-panel': choose(env.bool('one_file_panel'), config('oneFilePanel')), 'one-file-panel': choose(env.bool('one_file_panel'), config('oneFilePanel')),
'confirm-copy': choose(env.bool('confirm_copy'), config('confirmCopy')), 'confirm-copy': choose(env.bool('confirm_copy'), config('confirmCopy')),
'confirm-move': choose(env.bool('confirm_move'), config('confirmMove')), 'confirm-move': choose(env.bool('confirm_move'), config('confirmMove')),
'keys-panel': env.bool('keys_panel') || config('keysPanel'), 'keys-panel': env.bool('keys_panel') || config('keysPanel'),
'import-token': env('import_token') || config('importToken'), 'import-token': env.parse('import_token') || config('importToken'),
'export-token': env('export_token') || config('exportToken'), 'export-token': env.parse('export_token') || config('exportToken'),
'dropbox': config('dropbox'), 'dropbox': config('dropbox'),
'dropbox-token': config('dropboxToken') || '', 'dropbox-token': config('dropboxToken') || '',
@ -238,7 +238,7 @@ async function main() {
menu: config('menu'), menu: config('menu'),
}; };
const password = env('password') || args.password; const password = env.parse('password') || args.password;
if (password) if (password)
config('password', await getPassword(password)); config('password', await getPassword(password));

View file

@ -2,7 +2,7 @@
import {promisify} from 'node:util'; import {promisify} from 'node:util';
import process from 'node:process'; import process from 'node:process';
import tryToCatch from 'try-to-catch'; import {tryToCatch} from 'try-to-catch';
import {createSimport} from 'simport'; import {createSimport} from 'simport';
import minor from 'minor'; import minor from 'minor';
import _place from 'place'; import _place from 'place';

View file

@ -1,30 +1,24 @@
'use strict'; import process from 'node:process';
const process = require('node:process');
/* global DOM */ /* global DOM */
const Emitify = require('emitify'); import Emitify from 'emitify';
const inherits = require('inherits'); import inherits from 'inherits';
const rendy = require('rendy'); import rendy from 'rendy';
const load = require('load.js'); import load from 'load.js';
const tryToCatch = require('try-to-catch'); import {tryToCatch} from 'try-to-catch';
const {addSlashToEnd} = require('format-io'); import {addSlashToEnd} from 'format-io';
const pascalCase = require('just-pascal-case'); import pascalCase from 'just-pascal-case';
const currify = require('currify'); import currify from 'currify';
import * as Images from './dom/images.mjs';
const Images = require('./dom/images'); import {unregisterSW} from './sw/register.js';
import {getJsonFromFileTable} from './get-json-from-file-table.mjs';
const {unregisterSW} = require('./sw/register'); import {Key} from './key/index.mjs';
const getJsonFromFileTable = require('./get-json-from-file-table'); import {
const Key = require('./key');
const {
apiURL, apiURL,
formatMsg, formatMsg,
buildFromJSON, buildFromJSON,
} = require('../common/cloudfunc'); } from '../common/cloudfunc.mjs';
import {loadModule} from './load-module.mjs';
const loadModule = require('./load-module');
const noJS = (a) => a.replace(/.js$/, ''); const noJS = (a) => a.replace(/.js$/, '');
@ -32,16 +26,19 @@ const isDev = process.env.NODE_ENV === 'development';
inherits(CloudCmdProto, Emitify); inherits(CloudCmdProto, Emitify);
module.exports = new CloudCmdProto(DOM); export const createCloudCmd = ({DOM, Listeners}) => {
return new CloudCmdProto({
DOM,
Listeners,
});
};
load.addErrorListener((e, src) => { load.addErrorListener((e, src) => {
const msg = `file ${src} could not be loaded`; const msg = `file ${src} could not be loaded`;
Images.show.error(msg); Images.show.error(msg);
}); });
function CloudCmdProto(DOM) { function CloudCmdProto({DOM, Listeners}) {
let Listeners;
Emitify.call(this); Emitify.call(this);
const CloudCmd = this; const CloudCmd = this;
@ -49,11 +46,9 @@ function CloudCmdProto(DOM) {
const {Storage, Files} = DOM; const {Storage, Files} = DOM;
this.log = (...a) => { this.log = () => {
if (!isDev) if (!isDev)
return; return;
console.log(...a);
}; };
this.prefix = ''; this.prefix = '';
this.prefixSocket = ''; this.prefixSocket = '';
@ -151,7 +146,7 @@ function CloudCmdProto(DOM) {
const [kebabModule] = query; const [kebabModule] = query;
const module = noJS(pascalCase(kebabModule.slice(1))); const module = noJS(pascalCase(kebabModule.slice(1)));
const file = query[1]; const [, file] = query;
const current = DOM.getCurrentByName(file); const current = DOM.getCurrentByName(file);
if (file && !current) { if (file && !current) {
@ -230,7 +225,6 @@ function CloudCmdProto(DOM) {
const dirPath = DOM.getCurrentDirPath(); const dirPath = DOM.getCurrentDirPath();
({Listeners} = CloudCmd);
Listeners.init(); Listeners.init();
const panels = getPanels(); const panels = getPanels();

View file

@ -1,39 +1,44 @@
'use strict'; import process from 'node:process';
import wraptile from 'wraptile';
const process = require('node:process'); import load from 'load.js';
require('../css/main.css'); import '../css/main.css';
import {registerSW, listenSW} from './sw/register.js';
const wraptile = require('wraptile'); import {initSortPanel, sortPanel} from './sort.mjs';
const load = require('load.js'); import Util from '../common/util.js';
import * as CloudFunc from '../common/cloudfunc.mjs';
const {registerSW, listenSW} = require('./sw/register'); import DOM from './dom/index.js';
import {createCloudCmd} from './client.mjs';
import * as Listeners from './listeners/index.js';
const isDev = process.env.NODE_ENV === 'development'; const isDev = process.env.NODE_ENV === 'development';
module.exports = async (config) => { export default init;
window.Util = require('../common/util');
window.CloudFunc = require('../common/cloudfunc'); globalThis.CloudCmd = init;
window.DOM = require('./dom'); async function init(config) {
window.CloudCmd = require('./client'); globalThis.CloudCmd = createCloudCmd({
DOM,
Listeners,
});
globalThis.DOM = DOM;
globalThis.Util = Util;
globalThis.CloudFunc = CloudFunc;
await register(config); await register(config);
require('./listeners'); initSortPanel();
require('./key'); globalThis.CloudCmd.sortPanel = sortPanel;
require('./sort');
const prefix = getPrefix(config.prefix); const prefix = getPrefix(config.prefix);
window.CloudCmd.init(prefix, config); globalThis.CloudCmd.init(prefix, config);
if (window.CloudCmd.config('menu') === 'aleman') if (globalThis.CloudCmd.config('menu') === 'aleman')
setTimeout(() => { setTimeout(() => {
import('https://esm.sh/@putout/processor-html'); import('https://esm.sh/@putout/processor-html');
import('https://esm.sh/@putout/bundle'); import('https://esm.sh/@putout/bundle');
}, 100); }, 100);
}; }
window.CloudCmd = module.exports;
function getPrefix(prefix) { function getPrefix(prefix) {
if (!prefix) if (!prefix)
@ -49,7 +54,7 @@ const onUpdateFound = wraptile(async (config) => {
if (isDev) if (isDev)
return; return;
const {DOM} = window; const {DOM} = globalThis;
const prefix = getPrefix(config.prefix); const prefix = getPrefix(config.prefix);
await load.js(`${prefix}/dist/cloudcmd.common.js`); await load.js(`${prefix}/dist/cloudcmd.common.js`);
@ -58,7 +63,7 @@ const onUpdateFound = wraptile(async (config) => {
console.log('cloudcmd: sw: updated'); console.log('cloudcmd: sw: updated');
DOM.Events.removeAll(); DOM.Events.removeAll();
window.CloudCmd(config); globalThis.CloudCmd(config);
}); });
async function register(config) { async function register(config) {

View file

@ -1,135 +0,0 @@
'use strict';
/* global CloudCmd */
const tryToPromiseAll = require('../../common/try-to-promise-all');
const Storage = require('./storage');
const DOM = require('./');
module.exports = new BufferProto();
function BufferProto() {
const Info = DOM.CurrentInfo;
const CLASS = 'cut-file';
const COPY = 'copy';
const CUT = 'cut';
const Buffer = {
cut: callIfEnabled.bind(null, cut),
copy: callIfEnabled.bind(null, copy),
clear: callIfEnabled.bind(null, clear),
paste: callIfEnabled.bind(null, paste),
};
function showMessage(msg) {
DOM.Dialog.alert(msg);
}
function getNames() {
const files = DOM.getActiveFiles();
return DOM.getFilenames(files);
}
function addCutClass() {
const files = DOM.getActiveFiles();
for (const element of files) {
element.classList.add(CLASS);
}
}
function rmCutClass() {
const files = DOM.getByClassAll(CLASS);
for (const element of files) {
element.classList.remove(CLASS);
}
}
function callIfEnabled(callback) {
const is = CloudCmd.config('buffer');
if (is)
return callback();
showMessage('Buffer disabled in config!');
}
async function readBuffer() {
const [e, cp, ct] = await tryToPromiseAll([
Storage.getJson(COPY),
Storage.getJson(CUT),
]);
return [
e,
cp,
ct,
];
}
async function copy() {
const names = getNames();
const from = Info.dirPath;
await clear();
if (!names.length)
return;
await Storage.remove(CUT);
await Storage.setJson(COPY, {
from,
names,
});
}
async function cut() {
const names = getNames();
const from = Info.dirPath;
await clear();
if (!names.length)
return;
addCutClass();
await Storage.setJson(CUT, {
from,
names,
});
}
async function clear() {
await Storage.remove(COPY);
await Storage.remove(CUT);
rmCutClass();
}
async function paste() {
const [error, cp, ct] = await readBuffer();
if (error || !cp && !ct)
return showMessage(error || 'Buffer is empty!');
const opStr = cp ? 'copy' : 'move';
const data = cp || ct;
const {Operation} = CloudCmd;
const msg = 'Path is same!';
const to = Info.dirPath;
if (data.from === to)
return showMessage(msg);
Operation.show(opStr, {
...data,
to,
});
await clear();
}
return Buffer;
}

124
client/dom/buffer.mjs Normal file
View file

@ -0,0 +1,124 @@
/* global CloudCmd*/
import tryToPromiseAll from '../../common/try-to-promise-all.js';
import Storage from './storage.js';
const CLASS = 'cut-file';
const COPY = 'copy';
const CUT = 'cut';
function showMessage(msg) {
globalThis.DOM.Dialog.alert(msg);
}
function getNames() {
const {DOM} = globalThis;
const files = DOM.getActiveFiles();
return DOM.getFilenames(files);
}
function addCutClass() {
const {DOM} = globalThis;
const files = DOM.getActiveFiles();
for (const element of files) {
element.classList.add(CLASS);
}
}
function rmCutClass() {
const {DOM} = globalThis;
const files = DOM.getByClassAll(CLASS);
for (const element of files) {
element.classList.remove(CLASS);
}
}
const checkEnabled = (fn) => () => {
const is = CloudCmd.config('buffer');
if (is)
return fn();
showMessage('Buffer disabled in config!');
};
async function readBuffer() {
const [e, cp, ct] = await tryToPromiseAll([
Storage.getJson(COPY),
Storage.getJson(CUT),
]);
return [
e,
cp,
ct,
];
}
export const copy = checkEnabled(async () => {
const Info = globalThis.DOM.CurrentInfo;
const names = getNames();
const from = Info.dirPath;
await clear();
if (!names.length)
return;
await Storage.remove(CUT);
await Storage.setJson(COPY, {
from,
names,
});
});
export const cut = checkEnabled(async () => {
const Info = globalThis.DOM.CurrentInfo;
const names = getNames();
const from = Info.dirPath;
await clear();
if (!names.length)
return;
addCutClass();
await Storage.setJson(CUT, {
from,
names,
});
});
export const clear = checkEnabled(async () => {
await Storage.remove(COPY);
await Storage.remove(CUT);
rmCutClass();
});
export const paste = checkEnabled(async () => {
const Info = globalThis.DOM.CurrentInfo;
const [error, cp, ct] = await readBuffer();
if (error || !cp && !ct)
return showMessage(error || 'Buffer is empty!');
const opStr = cp ? 'copy' : 'move';
const data = cp || ct;
const {Operation} = CloudCmd;
const msg = 'Path is same!';
const to = Info.dirPath;
if (data.from === to)
return showMessage(msg);
Operation.show(opStr, {
...data,
to,
});
await clear();
});

View file

@ -1,13 +1,8 @@
'use strict';
/* global DOM */ /* global DOM */
/* global CloudCmd */ /* global CloudCmd */
const createElement = require('@cloudcmd/create-element'); import createElement from '@cloudcmd/create-element';
const {atob, btoa} = require('../../common/base64'); import {encode, decode} from '../../common/entity.js';
import {getTitle, FS} from '../../common/cloudfunc.mjs';
const {encode, decode} = require('../../common/entity');
const {getTitle, FS} = require('../../common/cloudfunc');
let Title; let Title;
@ -15,14 +10,15 @@ const CURRENT_FILE = 'current-file';
const encodeNBSP = (a) => a?.replace('\xa0', '&nbsp;'); const encodeNBSP = (a) => a?.replace('\xa0', '&nbsp;');
const decodeNBSP = (a) => a?.replace('&nbsp;', '\xa0'); const decodeNBSP = (a) => a?.replace('&nbsp;', '\xa0');
module.exports._CURRENT_FILE = CURRENT_FILE; export const _CURRENT_FILE = CURRENT_FILE;
/** /**
* set name from current (or param) file * set name from current (or param) file
* *
* @param name * @param name
* @param current * @param current
*/ */
module.exports.setCurrentName = (name, current) => { export const setCurrentName = (name, current) => {
const Info = DOM.CurrentInfo; const Info = DOM.CurrentInfo;
const {link} = Info; const {link} = Info;
const {prefix} = CloudCmd; const {prefix} = CloudCmd;
@ -44,7 +40,7 @@ module.exports.setCurrentName = (name, current) => {
* *
* @param currentFile * @param currentFile
*/ */
module.exports.getCurrentName = (currentFile) => { export const getCurrentName = (currentFile) => {
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
if (!current) if (!current)
@ -71,18 +67,19 @@ const parseNameAttribute = (attribute) => {
return decodeNBSP(decodeURI(atob(attribute))); return decodeNBSP(decodeURI(atob(attribute)));
}; };
module.exports._parseNameAttribute = parseNameAttribute; export const _parseNameAttribute = parseNameAttribute;
const parseHrefAttribute = (prefix, attribute) => { const parseHrefAttribute = (prefix, attribute) => {
attribute = attribute.replace(RegExp('^' + prefix + FS), ''); attribute = attribute.replace(RegExp('^' + prefix + FS), '');
return decode(decodeNBSP(attribute)); return decode(decodeNBSP(attribute));
}; };
module.exports._parseHrefAttribute = parseHrefAttribute; export const _parseHrefAttribute = parseHrefAttribute;
/** /**
* get current direcotory path * get current direcotory path
*/ */
module.exports.getCurrentDirPath = (panel = DOM.getPanel()) => { export const getCurrentDirPath = (panel = DOM.getPanel()) => {
const path = DOM.getByDataName('js-path', panel); const path = DOM.getByDataName('js-path', panel);
return path.textContent; return path.textContent;
}; };
@ -92,7 +89,7 @@ module.exports.getCurrentDirPath = (panel = DOM.getPanel()) => {
* *
* @param currentFile - current file by default * @param currentFile - current file by default
*/ */
module.exports.getCurrentPath = (currentFile) => { export const getCurrentPath = (currentFile) => {
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
const [element] = DOM.getByTag('a', current); const [element] = DOM.getByTag('a', current);
const {prefix} = CloudCmd; const {prefix} = CloudCmd;
@ -103,7 +100,7 @@ module.exports.getCurrentPath = (currentFile) => {
/** /**
* get current direcotory name * get current direcotory name
*/ */
module.exports.getCurrentDirName = () => { export const getCurrentDirName = () => {
const href = DOM const href = DOM
.getCurrentDirPath() .getCurrentDirPath()
.replace(/\/$/, ''); .replace(/\/$/, '');
@ -116,7 +113,7 @@ module.exports.getCurrentDirName = () => {
/** /**
* get current direcotory path * get current direcotory path
*/ */
module.exports.getParentDirPath = (panel) => { export const getParentDirPath = (panel) => {
const path = DOM.getCurrentDirPath(panel); const path = DOM.getCurrentDirPath(panel);
const dirName = DOM.getCurrentDirName() + '/'; const dirName = DOM.getCurrentDirName() + '/';
const index = path.lastIndexOf(dirName); const index = path.lastIndexOf(dirName);
@ -130,7 +127,7 @@ module.exports.getParentDirPath = (panel) => {
/** /**
* get not current direcotory path * get not current direcotory path
*/ */
module.exports.getNotCurrentDirPath = () => { export const getNotCurrentDirPath = () => {
const panel = DOM.getPanel({ const panel = DOM.getPanel({
active: false, active: false,
}); });
@ -143,14 +140,14 @@ module.exports.getNotCurrentDirPath = () => {
* *
* @currentFile * @currentFile
*/ */
module.exports.getCurrentFile = () => { export const getCurrentFile = () => {
return DOM.getByClass(CURRENT_FILE); return DOM.getByClass(CURRENT_FILE);
}; };
/** /**
* get current file by name * get current file by name
*/ */
module.exports.getCurrentByName = (name, panel = DOM.CurrentInfo.panel) => { export const getCurrentByName = (name, panel = DOM.CurrentInfo.panel) => {
const dataName = 'js-file-' + btoa(encodeURI(encodeNBSP(name))); const dataName = 'js-file-' + btoa(encodeURI(encodeNBSP(name)));
return DOM.getByDataName(dataName, panel); return DOM.getByDataName(dataName, panel);
}; };
@ -172,7 +169,7 @@ function unsetCurrentFile(currentFile) {
/** /**
* unified way to set current file * unified way to set current file
*/ */
module.exports.setCurrentFile = (currentFile, options) => { export const setCurrentFile = (currentFile, options) => {
const o = options; const o = options;
const currentFileWas = DOM.getCurrentFile(); const currentFileWas = DOM.getCurrentFile();
@ -219,7 +216,7 @@ module.exports.setCurrentFile = (currentFile, options) => {
return DOM; return DOM;
}; };
this.setCurrentByName = (name) => { export const setCurrentByName = (name) => {
const current = DOM.getCurrentByName(name); const current = DOM.getCurrentByName(name);
return DOM.setCurrentFile(current); return DOM.setCurrentFile(current);
}; };
@ -230,7 +227,7 @@ this.setCurrentByName = (name) => {
* @param layer - element * @param layer - element
* @param - position {x, y} * @param - position {x, y}
*/ */
module.exports.getCurrentByPosition = ({x, y}) => { export const getCurrentByPosition = ({x, y}) => {
const element = document.elementFromPoint(x, y); const element = document.elementFromPoint(x, y);
const getEl = (el) => { const getEl = (el) => {
@ -262,7 +259,7 @@ module.exports.getCurrentByPosition = ({x, y}) => {
* *
* @param currentFile * @param currentFile
*/ */
module.exports.isCurrentFile = (currentFile) => { export const isCurrentFile = (currentFile) => {
if (!currentFile) if (!currentFile)
return false; return false;
@ -274,7 +271,7 @@ module.exports.isCurrentFile = (currentFile) => {
* *
* @param name * @param name
*/ */
module.exports.setTitle = (name) => { export const setTitle = (name) => {
if (!Title) if (!Title)
Title = DOM.getByTag('title')[0] || createElement('title', { Title = DOM.getByTag('title')[0] || createElement('title', {
innerHTML: name, innerHTML: name,
@ -291,18 +288,18 @@ module.exports.setTitle = (name) => {
* *
* @param currentFile * @param currentFile
*/ */
module.exports.isCurrentIsDir = (currentFile) => { export const isCurrentIsDir = (currentFile) => {
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
const path = DOM.getCurrentPath(current); const path = DOM.getCurrentPath(current);
const fileType = DOM.getCurrentType(current); const fileType = DOM.getCurrentType(current);
const isZip = /\.zip$/.test(path); const isZip = path.endsWith('.zip');
const isDir = /^directory(-link)?/.test(fileType); const isDir = /^directory(-link)?/.test(fileType);
return isDir || isZip; return isDir || isZip;
}; };
module.exports.getCurrentType = (currentFile) => { export const getCurrentType = (currentFile) => {
const current = currentFile || DOM.getCurrentFile(); const current = currentFile || DOM.getCurrentFile();
const el = DOM.getByDataName('js-type', current); const el = DOM.getByDataName('js-type', current);
const type = el.className const type = el.className

View file

@ -1,20 +1,18 @@
'use strict'; import {test, stub} from 'supertape';
import {create} from 'auto-globals';
import wraptile from 'wraptile';
import * as currentFile from './current-file.mjs';
const {test, stub} = require('supertape');
const {create} = require('auto-globals');
const wraptile = require('wraptile');
const currentFile = require('./current-file');
const id = (a) => a; const id = (a) => a;
const returns = wraptile(id); const returns = wraptile(id);
const {_CURRENT_FILE} = currentFile; const {_CURRENT_FILE} = currentFile;
test('current-file: setCurrentName: setAttribute', (t) => { test('current-file: setCurrentName: setAttribute', (t) => {
const {DOM, CloudCmd} = global; const {DOM, CloudCmd} = globalThis;
global.DOM = getDOM(); globalThis.DOM = getDOM();
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const current = create(); const current = create();
const {setAttribute} = current; const {setAttribute} = current;
@ -23,17 +21,17 @@ test('current-file: setCurrentName: setAttribute', (t) => {
t.calledWith(setAttribute, ['data-name', 'js-file-aGVsbG8='], 'should call setAttribute'); t.calledWith(setAttribute, ['data-name', 'js-file-aGVsbG8='], 'should call setAttribute');
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.end(); t.end();
}); });
test('current-file: setCurrentName: setAttribute: cyrillic', (t) => { test('current-file: setCurrentName: setAttribute: cyrillic', (t) => {
const {DOM, CloudCmd} = global; const {DOM, CloudCmd} = globalThis;
global.DOM = getDOM(); globalThis.DOM = getDOM();
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const current = create(); const current = create();
const {setAttribute} = current; const {setAttribute} = current;
@ -42,8 +40,8 @@ test('current-file: setCurrentName: setAttribute: cyrillic', (t) => {
t.calledWith(setAttribute, ['data-name', 'js-file-JUQwJUIwJUQwJUI5'], 'should call setAttribute'); t.calledWith(setAttribute, ['data-name', 'js-file-JUQwJUIwJUQwJUI5'], 'should call setAttribute');
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.end(); t.end();
}); });
@ -59,12 +57,12 @@ test('current-file: getCurrentName', (t) => {
}); });
test('current-file: emit', (t) => { test('current-file: emit', (t) => {
const {DOM, CloudCmd} = global; const {DOM, CloudCmd} = globalThis;
const emit = stub(); const emit = stub();
global.DOM = getDOM(); globalThis.DOM = getDOM();
global.CloudCmd = getCloudCmd({ globalThis.CloudCmd = getCloudCmd({
emit, emit,
}); });
@ -74,22 +72,22 @@ test('current-file: emit', (t) => {
t.calledWith(emit, ['current-file', current], 'should call emit'); t.calledWith(emit, ['current-file', current], 'should call emit');
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.end(); t.end();
}); });
test('current-file: setCurrentName: return', (t) => { test('current-file: setCurrentName: return', (t) => {
const {DOM, CloudCmd} = global; const {DOM, CloudCmd} = globalThis;
const link = {}; const link = {};
global.DOM = getDOM({ globalThis.DOM = getDOM({
link, link,
}); });
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const current = create(); const current = create();
@ -97,19 +95,19 @@ test('current-file: setCurrentName: return', (t) => {
t.equal(result, link, 'should return link'); t.equal(result, link, 'should return link');
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.end(); t.end();
}); });
test('current-file: getParentDirPath: result', (t) => { test('current-file: getParentDirPath: result', (t) => {
const {DOM} = global; const {DOM} = globalThis;
const getCurrentDirPath = returns('/D/Films/+++favorite films/'); const getCurrentDirPath = returns('/D/Films/+++favorite films/');
const getCurrentDirName = returns('+++favorite films'); const getCurrentDirName = returns('+++favorite films');
global.DOM = getDOM({ globalThis.DOM = getDOM({
getCurrentDirPath, getCurrentDirPath,
getCurrentDirName, getCurrentDirName,
}); });
@ -117,55 +115,55 @@ test('current-file: getParentDirPath: result', (t) => {
const result = currentFile.getParentDirPath(); const result = currentFile.getParentDirPath();
const expected = '/D/Films/'; const expected = '/D/Films/';
global.DOM = DOM; globalThis.DOM = DOM;
t.equal(result, expected, 'should return parent dir path'); t.equal(result, expected, 'should return parent dir path');
t.end(); t.end();
}); });
test('current-file: isCurrentFile: no', (t) => { test('current-file: isCurrentFile: no', (t) => {
const {DOM, CloudCmd} = global; const {DOM, CloudCmd} = globalThis;
global.DOM = getDOM(); globalThis.DOM = getDOM();
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const result = currentFile.isCurrentFile(); const result = currentFile.isCurrentFile();
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.notOk(result); t.notOk(result);
t.end(); t.end();
}); });
test('current-file: isCurrentFile', (t) => { test('current-file: isCurrentFile', (t) => {
const {DOM, CloudCmd} = global; const {DOM, CloudCmd} = globalThis;
const isContainClass = stub(); const isContainClass = stub();
global.DOM = getDOM({ globalThis.DOM = getDOM({
isContainClass, isContainClass,
}); });
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const current = {}; const current = {};
currentFile.isCurrentFile(current); currentFile.isCurrentFile(current);
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.calledWith(isContainClass, [current, _CURRENT_FILE], 'should call isContainClass'); t.calledWith(isContainClass, [current, _CURRENT_FILE], 'should call isContainClass');
t.end(); t.end();
}); });
test('current-file: getCurrentType', (t) => { test('current-file: getCurrentType', (t) => {
const {DOM, CloudCmd} = global; const {DOM, CloudCmd} = globalThis;
global.DOM = getDOM(); globalThis.DOM = getDOM();
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const {getByDataName} = global.DOM; const {getByDataName} = globalThis.DOM;
getByDataName.returns({ getByDataName.returns({
className: 'mini-icon directory', className: 'mini-icon directory',
@ -175,87 +173,87 @@ test('current-file: getCurrentType', (t) => {
currentFile.getCurrentType(current); currentFile.getCurrentType(current);
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.calledWith(getByDataName, ['js-type', current]); t.calledWith(getByDataName, ['js-type', current]);
t.end(); t.end();
}); });
test('current-file: isCurrentIsDir: getCurrentType', (t) => { test('current-file: isCurrentIsDir: getCurrentType', (t) => {
const {DOM, CloudCmd} = global; const {DOM, CloudCmd} = globalThis;
global.DOM = getDOM(); globalThis.DOM = getDOM();
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const {getCurrentType} = global.DOM; const {getCurrentType} = globalThis.DOM;
const current = create(); const current = create();
currentFile.isCurrentIsDir(current); currentFile.isCurrentIsDir(current);
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.calledWith(getCurrentType, [current]); t.calledWith(getCurrentType, [current]);
t.end(); t.end();
}); });
test('current-file: isCurrentIsDir: directory', (t) => { test('current-file: isCurrentIsDir: directory', (t) => {
const {DOM, CloudCmd} = global; const {DOM, CloudCmd} = globalThis;
global.DOM = getDOM({ globalThis.DOM = getDOM({
getCurrentType: stub().returns('directory'), getCurrentType: stub().returns('directory'),
}); });
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const current = create(); const current = create();
const result = currentFile.isCurrentIsDir(current); const result = currentFile.isCurrentIsDir(current);
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.ok(result); t.ok(result);
t.end(); t.end();
}); });
test('current-file: isCurrentIsDir: directory-link', (t) => { test('current-file: isCurrentIsDir: directory-link', (t) => {
const {DOM, CloudCmd} = global; const {DOM, CloudCmd} = globalThis;
global.DOM = getDOM({ globalThis.DOM = getDOM({
getCurrentType: stub().returns('directory-link'), getCurrentType: stub().returns('directory-link'),
}); });
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const current = create(); const current = create();
const result = currentFile.isCurrentIsDir(current); const result = currentFile.isCurrentIsDir(current);
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.ok(result); t.ok(result);
t.end(); t.end();
}); });
test('current-file: isCurrentIsDir: file', (t) => { test('current-file: isCurrentIsDir: file', (t) => {
const {DOM, CloudCmd} = global; const {DOM, CloudCmd} = globalThis;
global.DOM = getDOM({ globalThis.DOM = getDOM({
getCurrentType: stub().returns('file'), getCurrentType: stub().returns('file'),
}); });
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const current = create(); const current = create();
const result = currentFile.isCurrentIsDir(current); const result = currentFile.isCurrentIsDir(current);
global.DOM = DOM; globalThis.DOM = DOM;
global.CloudCmd = CloudCmd; globalThis.CloudCmd = CloudCmd;
t.notOk(result); t.notOk(result);
t.end(); t.end();
@ -291,7 +289,7 @@ function getDOM(overrides = {}) {
getByDataName = stub(), getByDataName = stub(),
isContainClass = stub(), isContainClass = stub(),
getCurrentType = stub(), getCurrentType = stub(),
getCurrentPath = stub(), getCurrentPath = stub().returns(''),
} = overrides; } = overrides;
return { return {

View file

@ -1,6 +1,6 @@
'use strict'; 'use strict';
const tryToCatch = require('try-to-catch'); const {tryToCatch} = require('try-to-catch');
const { const {
alert, alert,

View file

@ -3,8 +3,8 @@
/* global CloudCmd */ /* global CloudCmd */
const philip = require('philip'); const philip = require('philip');
const Images = require('./images'); const Images = require('./images.mjs');
const {FS} = require('../../common/cloudfunc'); const {FS} = require('../../common/cloudfunc.mjs');
const DOM = require('.'); const DOM = require('.');
const Dialog = require('./dialog'); const Dialog = require('./dialog');

View file

@ -2,7 +2,7 @@
const test = require('supertape'); const test = require('supertape');
const {create} = require('auto-globals'); const {create} = require('auto-globals');
const tryCatch = require('try-catch'); const {tryCatch} = require('try-catch');
const {isContainClass} = require('./dom-tree'); const {isContainClass} = require('./dom-tree');

View file

@ -1,198 +0,0 @@
'use strict';
const itype = require('itype');
const EventStore = require('./event-store');
module.exports = new EventsProto();
function EventsProto() {
const Events = this;
const getEventOptions = (eventName) => {
if (eventName !== 'touchstart')
return false;
return {
passive: true,
};
};
function parseArgs(eventName, element, listener, callback) {
let isFunc;
const args = [
eventName,
element,
listener,
callback,
];
const EVENT_NAME = 1;
const ELEMENT = 0;
const type = itype(eventName);
switch(type) {
default:
if (!type.endsWith('element'))
throw Error(`unknown eventName: ${type}`);
parseArgs(args[EVENT_NAME], args[ELEMENT], listener, callback);
break;
case 'string':
isFunc = itype.function(element);
if (isFunc) {
listener = element;
element = null;
}
if (!element)
element = window;
callback(element, [
eventName,
listener,
getEventOptions(eventName),
]);
break;
case 'array':
for (const name of eventName) {
parseArgs(name, element, listener, callback);
}
break;
case 'object':
for (const name of Object.keys(eventName)) {
const eventListener = eventName[name];
parseArgs(name, element, eventListener, callback);
}
break;
}
}
/**
* safe add event listener
*
* @param type
* @param element - document by default
* @param listener
*/
this.add = (type, element, listener) => {
checkType(type);
parseArgs(type, element, listener, (element, args) => {
const [name, fn, options] = args;
element.addEventListener(name, fn, options);
EventStore.add(element, name, fn);
});
return Events;
};
/**
* safe add event listener
*
* @param type
* @param listener
* @param element - document by default
*/
this.addOnce = (type, element, listener) => {
const once = (event) => {
Events.remove(type, element, once);
listener(event);
};
if (!listener) {
listener = element;
element = null;
}
this.add(type, element, once);
return Events;
};
/**
* safe remove event listener
*
* @param type
* @param listener
* @param element - document by default
*/
this.remove = (type, element, listener) => {
checkType(type);
parseArgs(type, element, listener, (element, args) => {
element.removeEventListener(...args);
});
return Events;
};
/**
* remove all added event listeners
*/
this.removeAll = () => {
const events = EventStore.get();
for (const [el, name, fn] of events)
el.removeEventListener(name, fn);
EventStore.clear();
};
/**
* safe add event keydown listener
*
* @param args
*/
this.addKey = function(...args) {
return Events.add('keydown', ...args);
};
/**
* safe remove event click listener
*
* @param args
*/
this.rmKey = function(...args) {
return Events.remove('keydown', ...args);
};
/**
* safe add event click listener
*/
this.addClick = function(...args) {
return Events.add('click', ...args);
};
/**
* safe remove event click listener
*/
this.rmClick = function(...args) {
return Events.remove('click', ...args);
};
this.addContextMenu = function(...args) {
return Events.add('contextmenu', ...args);
};
/**
* safe add load listener
*/
this.addLoad = function(...args) {
return Events.add('load', ...args);
};
function checkType(type) {
if (!type)
throw Error('type could not be empty!');
}
}

203
client/dom/events/index.mjs Normal file
View file

@ -0,0 +1,203 @@
import itype from 'itype';
import EventStore from './event-store.js';
/**
* safe add event listener
*
* @param type
* @param element - document by default
* @param listener
*/
export const add = (type, element, listener) => {
checkType(type);
parseArgs(type, element, listener, (element, args) => {
const [name, fn, options] = args;
element.addEventListener(name, fn, options);
EventStore.add(element, name, fn);
});
return Events;
};
/**
* safe add event listener
*
* @param type
* @param listener
* @param element - document by default
*/
export const addOnce = (type, element, listener) => {
const once = (event) => {
Events.remove(type, element, once);
listener(event);
};
if (!listener) {
listener = element;
element = null;
}
add(type, element, once);
return Events;
};
/**
* safe remove event listener
*
* @param type
* @param listener
* @param element - document by default
*/
export const remove = (type, element, listener) => {
checkType(type);
parseArgs(type, element, listener, (element, args) => {
element.removeEventListener(...args);
});
return Events;
};
/**
* remove all added event listeners
*/
export const removeAll = () => {
const events = EventStore.get();
for (const [el, name, fn] of events)
el.removeEventListener(name, fn);
EventStore.clear();
};
/**
* safe add event keydown listener
*
* @param args
*/
export const addKey = function(...args) {
return add('keydown', ...args);
};
/**
* safe remove event click listener
*
* @param args
*/
export const rmKey = function(...args) {
return Events.remove('keydown', ...args);
};
/**
* safe add event click listener
*/
export const addClick = function(...args) {
return Events.add('click', ...args);
};
/**
* safe remove event click listener
*/
export const rmClick = function(...args) {
return remove('click', ...args);
};
export const addContextMenu = function(...args) {
return add('contextmenu', ...args);
};
/**
* safe add load listener
*/
export const addLoad = function(...args) {
return add('load', ...args);
};
function checkType(type) {
if (!type)
throw Error('type could not be empty!');
}
const getEventOptions = (eventName) => {
if (eventName !== 'touchstart')
return false;
return {
passive: true,
};
};
function parseArgs(eventName, element, listener, callback) {
let isFunc;
const args = [
eventName,
element,
listener,
callback,
];
const EVENT_NAME = 1;
const ELEMENT = 0;
const type = itype(eventName);
switch(type) {
default:
if (!type.endsWith('element'))
throw Error(`unknown eventName: ${type}`);
parseArgs(args[EVENT_NAME], args[ELEMENT], listener, callback);
break;
case 'string':
isFunc = itype.function(element);
if (isFunc) {
listener = element;
element = null;
}
if (!element)
element = window;
callback(element, [
eventName,
listener,
getEventOptions(eventName),
]);
break;
case 'array':
for (const name of eventName) {
parseArgs(name, element, listener, callback);
}
break;
case 'object':
for (const name of Object.keys(eventName)) {
const eventListener = eventName[name];
parseArgs(name, element, eventListener, callback);
}
break;
}
}
const Events = {
add,
addClick,
addContextMenu,
addKey,
addLoad,
addOnce,
remove,
removeAll,
rmClick,
rmKey,
};

View file

@ -1,10 +1,5 @@
/* global DOM */ /* global DOM */
import createElement from '@cloudcmd/create-element';
'use strict';
const createElement = require('@cloudcmd/create-element');
const Images = module.exports;
const LOADING = 'loading'; const LOADING = 'loading';
const HIDDEN = 'hidden'; const HIDDEN = 'hidden';
@ -12,7 +7,8 @@ const ERROR = 'error';
const getLoadingType = () => isSVG() ? '-svg' : '-gif'; const getLoadingType = () => isSVG() ? '-svg' : '-gif';
module.exports.get = getElement; export const get = getElement;
/** /**
* check SVG SMIL animation support * check SVG SMIL animation support
*/ */
@ -40,7 +36,7 @@ function getElement() {
} }
/* Функция создаёт картинку загрузки */ /* Функция создаёт картинку загрузки */
module.exports.loading = () => { export const loading = () => {
const element = getElement(); const element = getElement();
const {classList} = element; const {classList} = element;
const loadingImage = LOADING + getLoadingType(); const loadingImage = LOADING + getLoadingType();
@ -52,7 +48,7 @@ module.exports.loading = () => {
}; };
/* Функция создаёт картинку ошибки загрузки */ /* Функция создаёт картинку ошибки загрузки */
module.exports.error = () => { export const error = () => {
const element = getElement(); const element = getElement();
const {classList} = element; const {classList} = element;
const loadingImage = LOADING + getLoadingType(); const loadingImage = LOADING + getLoadingType();
@ -63,14 +59,21 @@ module.exports.error = () => {
return element; return element;
}; };
module.exports.show = show; show.load = show;
module.exports.show.load = show; show.error = (text) => {
module.exports.show.error = error; const image = Images.error();
DOM.show(image);
image.title = text;
return image;
};
/** /**
* Function shows loading spinner * Function shows loading spinner
* position = {top: true}; * position = {top: true};
*/ */
function show(position, panel) { export function show(position, panel) {
const image = Images.loading(); const image = Images.loading();
const parent = image.parentElement; const parent = image.parentElement;
const refreshButton = DOM.getRefreshButton(panel); const refreshButton = DOM.getRefreshButton(panel);
@ -96,19 +99,10 @@ function show(position, panel) {
return image; return image;
} }
function error(text) {
const image = Images.error();
DOM.show(image);
image.title = text;
return image;
}
/** /**
* hide load image * hide load image
*/ */
module.exports.hide = () => { export const hide = () => {
const element = Images.get(); const element = Images.get();
DOM.hide(element); DOM.hide(element);
@ -116,7 +110,7 @@ module.exports.hide = () => {
return Images; return Images;
}; };
module.exports.setProgress = (value, title) => { export const setProgress = (value, title) => {
const DATA = 'data-progress'; const DATA = 'data-progress';
const element = Images.get(); const element = Images.get();
@ -131,7 +125,7 @@ module.exports.setProgress = (value, title) => {
return Images; return Images;
}; };
module.exports.clearProgress = () => { export const clearProgress = () => {
const DATA = 'data-progress'; const DATA = 'data-progress';
const element = Images.get(); const element = Images.get();
@ -143,3 +137,13 @@ module.exports.clearProgress = () => {
return Images; return Images;
}; };
const Images = {
clearProgress,
setProgress,
show,
hide,
get,
error,
loading,
};

View file

@ -3,12 +3,12 @@
/* global CloudCmd */ /* global CloudCmd */
const Util = require('../../common/util'); const Util = require('../../common/util');
const Images = require('./images'); const Images = require('./images.mjs');
const RESTful = require('./rest'); const RESTful = require('./rest');
const Storage = require('./storage'); const Storage = require('./storage');
const renameCurrent = require('./operations/rename-current'); const renameCurrent = require('./operations/rename-current');
const CurrentFile = require('./current-file'); const CurrentFile = require('./current-file.mjs');
const DOMTree = require('./dom-tree'); const DOMTree = require('./dom-tree');
const Cmd = module.exports; const Cmd = module.exports;
@ -32,8 +32,8 @@ DOM.CurrentInfo = CurrentInfo;
module.exports = DOM; module.exports = DOM;
DOM.uploadDirectory = require('./directory'); DOM.uploadDirectory = require('./directory');
DOM.Buffer = require('./buffer'); DOM.Buffer = require('./buffer.mjs');
DOM.Events = require('./events'); DOM.Events = require('#dom/events');
const loadRemote = require('./load-remote'); const loadRemote = require('./load-remote');
const selectByPattern = require('./select-by-pattern'); const selectByPattern = require('./select-by-pattern');
@ -416,7 +416,7 @@ module.exports.shrinkSelection = () => {
* setting history wrapper * setting history wrapper
*/ */
module.exports.setHistory = (data, title, url) => { module.exports.setHistory = (data, title, url) => {
const ret = window.history; const ret = globalThis.history;
const {prefix} = CloudCmd; const {prefix} = CloudCmd;
url = prefix + url; url = prefix + url;
@ -554,7 +554,7 @@ module.exports.getPanel = (options) => {
* then always work with passive * then always work with passive
* panel * panel
*/ */
if (window.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH) if (globalThis.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH)
panel = DOM.getByDataName('js-left'); panel = DOM.getByDataName('js-left');
if (!panel) if (!panel)
@ -729,17 +729,19 @@ module.exports.getPackerExt = (type) => {
return '.tar.gz'; return '.tar.gz';
}; };
module.exports.goToDirectory = async () => { module.exports.goToDirectory = async (overrides = {}) => {
const msg = 'Go to directory:';
const {Dialog} = DOM; const {Dialog} = DOM;
const {prompt = Dialog.prompt, changeDir = CloudCmd.changeDir} = overrides;
const msg = 'Go to directory:';
const {dirPath} = CurrentInfo; const {dirPath} = CurrentInfo;
const [cancel, path = dirPath] = await Dialog.prompt(msg, dirPath); const [cancel, path = dirPath] = await prompt(msg, dirPath);
if (cancel) if (cancel)
return; return;
await CloudCmd.changeDir(path); await changeDir(path);
}; };
module.exports.duplicatePanel = async () => { module.exports.duplicatePanel = async () => {

View file

@ -3,30 +3,20 @@
require('css-modules-require-hook/preset'); require('css-modules-require-hook/preset');
const {test, stub} = require('supertape'); const {test, stub} = require('supertape');
const mockRequire = require('mock-require'); const {getCSSVar, goToDirectory} = require('./index');
const {getCSSVar} = require('./index');
const {reRequire, stopAll} = mockRequire;
global.CloudCmd = {}; globalThis.CloudCmd = {};
test('cloudcmd: client: dom: goToDirectory', async (t) => { test('cloudcmd: client: dom: goToDirectory', async (t) => {
const path = ''; const path = '';
const {CloudCmd} = global;
const changeDir = stub(); const changeDir = stub();
const prompt = stub().returns([null, path]); const prompt = stub().returns([null, path]);
CloudCmd.changeDir = changeDir; await goToDirectory({
mockRequire('./dialog', {
prompt, prompt,
changeDir,
}); });
const {goToDirectory} = reRequire('.');
await goToDirectory();
stopAll();
t.calledWith(changeDir, [path]); t.calledWith(changeDir, [path]);
t.end(); t.end();
}); });
@ -35,14 +25,14 @@ test('cloudcmd: client: dom: getCSSVar', (t) => {
const body = {}; const body = {};
const getPropertyValue = stub().returns(0); const getPropertyValue = stub().returns(0);
global.getComputedStyle = stub().returns({ globalThis.getComputedStyle = stub().returns({
getPropertyValue, getPropertyValue,
}); });
const result = getCSSVar('hello', { const result = getCSSVar('hello', {
body, body,
}); });
delete global.getComputedStyle; delete globalThis.getComputedStyle;
t.notOk(result); t.notOk(result);
t.end(); t.end();
@ -52,14 +42,14 @@ test('cloudcmd: client: dom: getCSSVar: 1', (t) => {
const body = {}; const body = {};
const getPropertyValue = stub().returns(1); const getPropertyValue = stub().returns(1);
global.getComputedStyle = stub().returns({ globalThis.getComputedStyle = stub().returns({
getPropertyValue, getPropertyValue,
}); });
const result = getCSSVar('hello', { const result = getCSSVar('hello', {
body, body,
}); });
delete global.getComputedStyle; delete globalThis.getComputedStyle;
t.ok(result); t.ok(result);
t.end(); t.end();

View file

@ -1,14 +1,14 @@
'use strict'; 'use strict';
const {FS} = require('../../../common/cloudfunc'); const {FS} = require('../../../common/cloudfunc.mjs');
const sendRequest = require('./send-request'); const _sendRequest = require('./send-request');
const imgPosition = { const imgPosition = {
top: true, top: true,
}; };
module.exports.delete = async (url, data) => { module.exports.delete = async (url, data) => {
return await sendRequest({ return await _sendRequest({
method: 'DELETE', method: 'DELETE',
url: FS + url, url: FS + url,
data, data,
@ -19,7 +19,7 @@ module.exports.delete = async (url, data) => {
}; };
module.exports.patch = async (url, data) => { module.exports.patch = async (url, data) => {
return await sendRequest({ return await _sendRequest({
method: 'PATCH', method: 'PATCH',
url: FS + url, url: FS + url,
data, data,
@ -28,7 +28,7 @@ module.exports.patch = async (url, data) => {
}; };
module.exports.write = async (url, data) => { module.exports.write = async (url, data) => {
return await sendRequest({ return await _sendRequest({
method: 'PUT', method: 'PUT',
url: FS + url, url: FS + url,
data, data,
@ -36,7 +36,11 @@ module.exports.write = async (url, data) => {
}); });
}; };
module.exports.createDirectory = async (url) => { module.exports.createDirectory = async (url, overrides = {}) => {
const {
sendRequest = _sendRequest,
} = overrides;
return await sendRequest({ return await sendRequest({
method: 'PUT', method: 'PUT',
url: `${FS}${url}?dir`, url: `${FS}${url}?dir`,
@ -47,7 +51,7 @@ module.exports.createDirectory = async (url) => {
module.exports.read = async (url, dataType = 'text') => { module.exports.read = async (url, dataType = 'text') => {
const notLog = !url.includes('?'); const notLog = !url.includes('?');
return await sendRequest({ return await _sendRequest({
method: 'GET', method: 'GET',
url: FS + url, url: FS + url,
notLog, notLog,
@ -56,7 +60,7 @@ module.exports.read = async (url, dataType = 'text') => {
}; };
module.exports.copy = async (from, to, names) => { module.exports.copy = async (from, to, names) => {
return await sendRequest({ return await _sendRequest({
method: 'PUT', method: 'PUT',
url: '/copy', url: '/copy',
data: { data: {
@ -69,7 +73,7 @@ module.exports.copy = async (from, to, names) => {
}; };
module.exports.pack = async (data) => { module.exports.pack = async (data) => {
return await sendRequest({ return await _sendRequest({
method: 'PUT', method: 'PUT',
url: '/pack', url: '/pack',
data, data,
@ -77,7 +81,7 @@ module.exports.pack = async (data) => {
}; };
module.exports.extract = async (data) => { module.exports.extract = async (data) => {
return await sendRequest({ return await _sendRequest({
method: 'PUT', method: 'PUT',
url: '/extract', url: '/extract',
data, data,
@ -85,7 +89,7 @@ module.exports.extract = async (data) => {
}; };
module.exports.move = async (from, to, names) => { module.exports.move = async (from, to, names) => {
return await sendRequest({ return await _sendRequest({
method: 'PUT', method: 'PUT',
url: '/move', url: '/move',
data: { data: {
@ -98,7 +102,7 @@ module.exports.move = async (from, to, names) => {
}; };
module.exports.rename = async (from, to) => { module.exports.rename = async (from, to) => {
return await sendRequest({ return await _sendRequest({
method: 'PUT', method: 'PUT',
url: '/rename', url: '/rename',
data: { data: {
@ -111,7 +115,7 @@ module.exports.rename = async (from, to) => {
module.exports.Config = { module.exports.Config = {
read: async () => { read: async () => {
return await sendRequest({ return await _sendRequest({
method: 'GET', method: 'GET',
url: '/config', url: '/config',
imgPosition, imgPosition,
@ -120,7 +124,7 @@ module.exports.Config = {
}, },
write: async (data) => { write: async (data) => {
return await sendRequest({ return await _sendRequest({
method: 'PATCH', method: 'PATCH',
url: '/config', url: '/config',
data, data,
@ -131,7 +135,7 @@ module.exports.Config = {
module.exports.Markdown = { module.exports.Markdown = {
read: async (url) => { read: async (url) => {
return await sendRequest({ return await _sendRequest({
method: 'GET', method: 'GET',
url: `/markdown${url}`, url: `/markdown${url}`,
imgPosition, imgPosition,
@ -140,7 +144,7 @@ module.exports.Markdown = {
}, },
render: async (data) => { render: async (data) => {
return await sendRequest({ return await _sendRequest({
method: 'PUT', method: 'PUT',
url: '/markdown', url: '/markdown',
data, data,

View file

@ -1,18 +1,14 @@
'use strict'; 'use strict';
const {test, stub} = require('supertape'); const {test, stub} = require('supertape');
const io = require('.');
const mockRequire = require('mock-require');
const {reRequire, stopAll} = mockRequire;
test('client: dom: io', (t) => { test('client: dom: io', (t) => {
const sendRequest = stub(); const sendRequest = stub();
mockRequire('./send-request', sendRequest);
const io = reRequire('.'); io.createDirectory('/hello', {
sendRequest,
io.createDirectory('/hello'); });
const expected = { const expected = {
imgPosition: { imgPosition: {
@ -22,8 +18,6 @@ test('client: dom: io', (t) => {
url: '/fs/hello?dir', url: '/fs/hello?dir',
}; };
stopAll();
t.calledWith(sendRequest, [expected]); t.calledWith(sendRequest, [expected]);
t.end(); t.end();
}); });

View file

@ -3,7 +3,7 @@
/* global CloudCmd */ /* global CloudCmd */
const {promisify} = require('es6-promisify'); const {promisify} = require('es6-promisify');
const Images = require('../images'); const Images = require('../images.mjs');
const load = require('../load'); const load = require('../load');
module.exports = promisify((params, callback) => { module.exports = promisify((params, callback) => {

View file

@ -4,7 +4,7 @@
const rendy = require('rendy'); const rendy = require('rendy');
const itype = require('itype'); const itype = require('itype');
const load = require('load.js'); const load = require('load.js');
const tryToCatch = require('try-to-catch'); const {tryToCatch} = require('try-to-catch');
const {findObjByNameInArr} = require('../../common/util'); const {findObjByNameInArr} = require('../../common/util');

View file

@ -4,7 +4,7 @@ const itype = require('itype');
const jonny = require('jonny'); const jonny = require('jonny');
const Emitify = require('emitify'); const Emitify = require('emitify');
const exec = require('execon'); const exec = require('execon');
const Images = require('./images'); const Images = require('./images.mjs');
module.exports.getIdBySrc = getIdBySrc; module.exports.getIdBySrc = getIdBySrc;
/** /**

View file

@ -3,21 +3,29 @@
/* global CloudCmd */ /* global CloudCmd */
const capitalize = require('just-capitalize'); const capitalize = require('just-capitalize');
const Dialog = require('../dialog'); const _Dialog = require('../dialog');
const Storage = require('../storage'); const Storage = require('../storage');
const RESTful = require('../rest'); const RESTful = require('../rest');
const { const _currentFile = require('../current-file.mjs');
isCurrentFile,
getCurrentName,
getCurrentFile,
getCurrentByName,
getCurrentType,
getCurrentDirPath,
setCurrentName,
} = require('../current-file');
module.exports = async (current) => { module.exports = async (current, overrides = {}) => {
const {
refresh = CloudCmd.refresh,
Dialog = _Dialog,
currentFile = _currentFile,
} = overrides;
const {
isCurrentFile,
getCurrentName,
getCurrentFile,
getCurrentByName,
getCurrentType,
getCurrentDirPath,
setCurrentName,
} = currentFile;
if (!isCurrentFile(current)) if (!isCurrentFile(current))
current = getCurrentFile(); current = getCurrentFile();
@ -58,5 +66,5 @@ module.exports = async (current) => {
setCurrentName(to, current); setCurrentName(to, current);
Storage.remove(dirPath); Storage.remove(dirPath);
CloudCmd.refresh(); refresh();
}; };

View file

@ -2,23 +2,20 @@
const {test, stub} = require('supertape'); const {test, stub} = require('supertape');
const mockRequire = require('mock-require'); const renameCurrent = require('./rename-current');
const {reRequire, stopAll} = mockRequire;
test('cloudcmd: client: dom: renameCurrent: isCurrentFile', async (t) => { test('cloudcmd: client: dom: renameCurrent: isCurrentFile', async (t) => {
const current = {}; const current = {};
const isCurrentFile = stub(); const isCurrentFile = stub();
mockRequire('../dialog', stubDialog()); const currentFile = stubCurrentFile({
mockRequire('../current-file', stubCurrentFile({
isCurrentFile, isCurrentFile,
})); });
const renameCurrent = reRequire('./rename-current'); await renameCurrent(current, {
await renameCurrent(current); Dialog: stubDialog(),
currentFile,
stopAll(); });
t.calledWith(isCurrentFile, [current], 'should call isCurrentFile'); t.calledWith(isCurrentFile, [current], 'should call isCurrentFile');
t.end(); t.end();
@ -27,11 +24,6 @@ test('cloudcmd: client: dom: renameCurrent: isCurrentFile', async (t) => {
test('cloudcmd: client: dom: renameCurrent: file exist', async (t) => { test('cloudcmd: client: dom: renameCurrent: file exist', async (t) => {
const current = {}; const current = {};
const name = 'hello'; const name = 'hello';
const {CloudCmd} = global;
global.CloudCmd = {
refresh: stub(),
};
const prompt = stub().returns([null, name]); const prompt = stub().returns([null, name]);
const confirm = stub().returns([true]); const confirm = stub().returns([true]);
@ -39,25 +31,23 @@ test('cloudcmd: client: dom: renameCurrent: file exist', async (t) => {
const getCurrentByName = stub().returns(current); const getCurrentByName = stub().returns(current);
const getCurrentType = stub().returns('directory'); const getCurrentType = stub().returns('directory');
mockRequire('../dialog', stubDialog({ const Dialog = stubDialog({
confirm, confirm,
prompt, prompt,
})); });
mockRequire('../current-file', stubCurrentFile({ const currentFile = stubCurrentFile({
getCurrentByName, getCurrentByName,
getCurrentType, getCurrentType,
})); });
const renameCurrent = reRequire('./rename-current'); await renameCurrent(null, {
await renameCurrent(); Dialog,
currentFile,
});
const expected = 'Directory "hello" already exists. Proceed?'; const expected = 'Directory "hello" already exists. Proceed?';
global.CloudCmd = CloudCmd;
stopAll();
t.calledWith(confirm, [expected], 'should call confirm'); t.calledWith(confirm, [expected], 'should call confirm');
t.end(); t.end();
}); });

View file

@ -1,10 +1,10 @@
'use strict'; 'use strict';
const tryToCatch = require('try-to-catch'); const {tryToCatch} = require('try-to-catch');
const {encode} = require('../../common/entity'); const {encode} = require('../../common/entity');
const Images = require('./images'); const Images = require('./images.mjs');
const IO = require('./io'); const IO = require('./io');
const Dialog = require('./dialog'); const Dialog = require('./dialog');

View file

@ -7,58 +7,58 @@ const storage = require('./storage');
const {stringify} = JSON; const {stringify} = JSON;
test('cloudcmd: client: storage: set', async (t) => { test('cloudcmd: client: storage: set', async (t) => {
const {localStorage} = global; const {localStorage} = globalThis;
const setItem = stub(); const setItem = stub();
global.localStorage = { globalThis.localStorage = {
setItem, setItem,
}; };
await storage.set('hello', 'world'); await storage.set('hello', 'world');
global.localStorage = localStorage; globalThis.localStorage = localStorage;
t.calledWith(setItem, ['hello', 'world'], 'should call setItem'); t.calledWith(setItem, ['hello', 'world'], 'should call setItem');
t.end(); t.end();
}); });
test('cloudcmd: client: storage: get', async (t) => { test('cloudcmd: client: storage: get', async (t) => {
const {localStorage} = global; const {localStorage} = globalThis;
const getItem = stub().returns('world'); const getItem = stub().returns('world');
global.localStorage = { globalThis.localStorage = {
getItem, getItem,
}; };
const result = await storage.get('hello'); const result = await storage.get('hello');
global.localStorage = localStorage; globalThis.localStorage = localStorage;
t.equal(result, 'world'); t.equal(result, 'world');
t.end(); t.end();
}); });
test('cloudcmd: client: storage: getJson', async (t) => { test('cloudcmd: client: storage: getJson', async (t) => {
const {localStorage} = global; const {localStorage} = globalThis;
const expected = { const expected = {
hello: 'world', hello: 'world',
}; };
const getItem = stub().returns(stringify(expected)); const getItem = stub().returns(stringify(expected));
global.localStorage = { globalThis.localStorage = {
getItem, getItem,
}; };
const result = await storage.getJson('hello'); const result = await storage.getJson('hello');
global.localStorage = localStorage; globalThis.localStorage = localStorage;
t.deepEqual(result, expected); t.deepEqual(result, expected);
t.end(); t.end();
}); });
test('cloudcmd: client: storage: setJson', async (t) => { test('cloudcmd: client: storage: setJson', async (t) => {
const {localStorage} = global; const {localStorage} = globalThis;
const data = { const data = {
hello: 'world', hello: 'world',
}; };
@ -66,42 +66,42 @@ test('cloudcmd: client: storage: setJson', async (t) => {
const expected = stringify(data); const expected = stringify(data);
const setItem = stub(); const setItem = stub();
global.localStorage = { globalThis.localStorage = {
setItem, setItem,
}; };
await storage.setJson('hello', data); await storage.setJson('hello', data);
global.localStorage = localStorage; globalThis.localStorage = localStorage;
t.calledWith(setItem, ['hello', expected]); t.calledWith(setItem, ['hello', expected]);
t.end(); t.end();
}); });
test('cloudcmd: client: storage: remove', async (t) => { test('cloudcmd: client: storage: remove', async (t) => {
const {localStorage} = global; const {localStorage} = globalThis;
const removeItem = stub(); const removeItem = stub();
global.localStorage = { globalThis.localStorage = {
removeItem, removeItem,
}; };
await storage.remove('hello'); await storage.remove('hello');
global.localStorage = localStorage; globalThis.localStorage = localStorage;
t.calledWith(removeItem, ['hello'], 'should call removeItem'); t.calledWith(removeItem, ['hello'], 'should call removeItem');
t.end(); t.end();
}); });
test('cloudcmd: client: storage: clear', async (t) => { test('cloudcmd: client: storage: clear', async (t) => {
const {localStorage} = global; const {localStorage} = globalThis;
const clear = stub(); const clear = stub();
global.localStorage = { globalThis.localStorage = {
clear, clear,
}; };
await storage.clear(); await storage.clear();
global.localStorage = localStorage; globalThis.localStorage = localStorage;
t.calledWithNoArgs(clear, 'should call clear'); t.calledWithNoArgs(clear, 'should call clear');
t.end(); t.end();

View file

@ -5,10 +5,10 @@ const {eachSeries} = require('execon');
const wraptile = require('wraptile'); const wraptile = require('wraptile');
const load = require('./load'); const load = require('./load');
const Images = require('./images'); const Images = require('./images.mjs');
const {alert} = require('./dialog'); const {alert} = require('./dialog');
const {FS} = require('../../common/cloudfunc'); const {FS} = require('../../common/cloudfunc.mjs');
const {getCurrentDirPath: getPathWhenRootEmpty} = require('.'); const {getCurrentDirPath: getPathWhenRootEmpty} = require('.');
const loadFile = wraptile(_loadFile); const loadFile = wraptile(_loadFile);

View file

@ -1,47 +0,0 @@
'use strict';
/* global DOM */
const Info = DOM.CurrentInfo;
/**
* Функция генерирует JSON из html-таблицы файлов и
* используеться при первом заходе в корень
*/
module.exports = () => {
const path = DOM.getCurrentDirPath();
const infoFiles = Info.files || [];
const notParent = (current) => {
const name = DOM.getCurrentName(current);
return name !== '..';
};
const parse = (current) => {
const name = DOM.getCurrentName(current);
const size = DOM.getCurrentSize(current);
const owner = DOM.getCurrentOwner(current);
const mode = DOM.getCurrentMode(current);
const date = DOM.getCurrentDate(current);
const type = DOM.getCurrentType(current);
return {
name,
size,
mode,
owner,
date,
type,
};
};
const files = infoFiles
.filter(notParent)
.map(parse);
const fileTable = {
path,
files,
};
return fileTable;
};

View file

@ -0,0 +1,44 @@
/* global DOM */
/**
* Функция генерирует JSON из html-таблицы файлов и
* используеться при первом заходе в корень
*/
export const getJsonFromFileTable = () => {
const Info = DOM.CurrentInfo;
const path = DOM.getCurrentDirPath();
const infoFiles = Info.files || [];
const files = infoFiles
.filter(notParent)
.map(parse);
const fileTable = {
path,
files,
};
return fileTable;
};
const notParent = (current) => {
const name = DOM.getCurrentName(current);
return name !== '..';
};
const parse = (current) => {
const name = DOM.getCurrentName(current);
const size = DOM.getCurrentSize(current);
const owner = DOM.getCurrentOwner(current);
const mode = DOM.getCurrentMode(current);
const date = DOM.getCurrentDate(current);
const type = DOM.getCurrentType(current);
return {
name,
size,
mode,
owner,
date,
type,
};
};

View file

@ -1,22 +1,17 @@
'use strict';
/* global CloudCmd, DOM */ /* global CloudCmd, DOM */
const clipboard = require('@cloudcmd/clipboard'); import clipboard from '@cloudcmd/clipboard';
const fullstore = require('fullstore'); import {fullstore} from 'fullstore';
import * as Events from '#dom/events';
import * as Buffer from '../dom/buffer.mjs';
import KEY from './key.js';
import _vim from './vim/index.js';
import setCurrentByChar from './set-current-by-char.js';
import {createBinder} from './binder.js';
const Buffer = require('../dom/buffer');
const Events = require('../dom/events');
const KEY = require('./key');
const vim = require('./vim');
const setCurrentByChar = require('./set-current-by-char');
const {createBinder} = require('./binder');
const Info = DOM.CurrentInfo;
const Chars = fullstore(); const Chars = fullstore();
const toggleVim = (keyCode) => { const toggleVim = (keyCode, overrides = {}) => {
const {_config, config} = CloudCmd; const {_config, config} = overrides;
if (keyCode === KEY.ESC) if (keyCode === KEY.ESC)
_config('vim', !config('vim')); _config('vim', !config('vim'));
@ -29,13 +24,16 @@ Chars([]);
const {assign} = Object; const {assign} = Object;
const binder = createBinder(); const binder = createBinder();
module.exports = assign(binder, KEY); const bind = () => {
module.exports.bind = () => {
Events.addKey(listener, true); Events.addKey(listener, true);
binder.setBind(); binder.setBind();
}; };
module.exports._listener = listener; export const Key = assign(binder, KEY, {
bind,
});
export const _listener = listener;
function getChar(event) { function getChar(event) {
/* /*
@ -55,7 +53,14 @@ function getChar(event) {
return [symbol, char]; return [symbol, char];
} }
async function listener(event) { async function listener(event, overrides = {}) {
const {
config = CloudCmd.config,
_config = CloudCmd._config,
switchKey = _switchKey,
vim = _vim,
} = overrides;
const {keyCode} = event; const {keyCode} = event;
// strange chrome bug calles listener twice // strange chrome bug calles listener twice
@ -74,8 +79,12 @@ async function listener(event) {
if (!binder.isBind()) if (!binder.isBind())
return; return;
toggleVim(keyCode); toggleVim(keyCode, {
const isVim = CloudCmd.config('vim'); config,
_config,
});
const isVim = config('vim');
if (!isVim && !isNumpad && !alt && !ctrl && !meta && (isBetween || symbol)) if (!isVim && !isNumpad && !alt && !ctrl && !meta && (isBetween || symbol))
return setCurrentByChar(char, Chars); return setCurrentByChar(char, Chars);
@ -112,7 +121,8 @@ function fromCharCode(keyIdentifier) {
return String.fromCharCode(hex); return String.fromCharCode(hex);
} }
async function switchKey(event) { async function _switchKey(event) {
const Info = DOM.CurrentInfo;
let i; let i;
let isSelected; let isSelected;
let prev; let prev;

View file

@ -3,27 +3,23 @@
require('css-modules-require-hook/preset'); require('css-modules-require-hook/preset');
const autoGlobals = require('auto-globals'); const autoGlobals = require('auto-globals');
const mockRequire = require('mock-require');
const supertape = require('supertape'); const supertape = require('supertape');
const {ESC} = require('./key'); const {ESC} = require('./key');
const {Key, _listener} = require('./index.mjs');
const {getDOM, getCloudCmd} = require('./vim/globals.fixture'); const {getDOM, getCloudCmd} = require('./vim/globals.fixture');
const test = autoGlobals(supertape); const test = autoGlobals(supertape);
const {reRequire, stopAll} = mockRequire;
const {stub} = supertape; const {stub} = supertape;
global.DOM = getDOM(); globalThis.DOM = getDOM();
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
test('cloudcmd: client: key: enable vim', async (t) => { test('cloudcmd: client: key: enable vim', async (t) => {
const vim = stub(); const vim = stub();
const {CloudCmd} = global; const config = stub().returns(true);
const {config} = CloudCmd; const _config = stub();
CloudCmd.config = stub().returns(true);
CloudCmd._config = stub();
mockRequire('./vim', vim);
const {_listener, setBind} = reRequire('.');
const event = { const event = {
keyCode: ESC, keyCode: ESC,
@ -31,11 +27,14 @@ test('cloudcmd: client: key: enable vim', async (t) => {
altKey: false, altKey: false,
}; };
setBind(); Key.setBind();
await _listener(event);
CloudCmd.config = config; await _listener(event, {
stopAll(); vim,
config,
_config,
switchKey: stub(),
});
t.calledWith(vim, ['Escape', event]); t.calledWith(vim, ['Escape', event]);
t.end(); t.end();
@ -43,25 +42,20 @@ test('cloudcmd: client: key: enable vim', async (t) => {
test('cloudcmd: client: key: disable vim', async (t) => { test('cloudcmd: client: key: disable vim', async (t) => {
const _config = stub(); const _config = stub();
const config = stub();
const event = { const event = {
keyCode: ESC, keyCode: ESC,
key: 'Escape', key: 'Escape',
altKey: false, altKey: false,
}; };
const {CloudCmd} = global; Key.setBind();
const {config} = CloudCmd; await _listener(event, {
config,
_config,
switchKey: stub(),
});
global.CloudCmd.config = _config; t.calledWith(_config, ['vim', true]);
global.CloudCmd._config = _config;
const {_listener, setBind} = reRequire('.');
setBind();
await _listener(event);
CloudCmd.config = config;
t.calledWith(_config, ['vim']);
t.end(); t.end();
}); });

View file

@ -3,9 +3,9 @@
'use strict'; 'use strict';
const {escapeRegExp} = require('../../common/util'); const {escapeRegExp} = require('../../common/util');
const Info = DOM.CurrentInfo;
module.exports = function setCurrentByChar(char, charStore) { module.exports = function setCurrentByChar(char, charStore) {
const Info = DOM.CurrentInfo;
let firstByName; let firstByName;
let skipCount = 0; let skipCount = 0;
let setted = false; let setted = false;

View file

@ -1,6 +1,6 @@
'use strict'; 'use strict';
const fullstore = require('fullstore'); const {fullstore} = require('fullstore');
const limier = require('limier'); const limier = require('limier');
const searchStore = fullstore([]); const searchStore = fullstore([]);

View file

@ -5,7 +5,7 @@ const dir = './';
const {getDOM} = require('./globals.fixture'); const {getDOM} = require('./globals.fixture');
global.DOM = getDOM(); globalThis.DOM = getDOM();
const {_next, _previous} = require(`${dir}find`); const {_next, _previous} = require(`${dir}find`);

View file

@ -9,32 +9,43 @@ const {
selectFileNotParent, selectFileNotParent,
} = require('./set-current'); } = require('./set-current');
const {Dialog} = DOM; module.exports = (key, event, overrides = {}) => {
const defaults = {
const DEPS = { ...globalThis.DOM,
...DOM, ...globalThis.CloudCmd,
...CloudCmd, };
};
const deps = {
module.exports = async (key, event, deps = DEPS) => { ...defaults,
...overrides,
};
const operations = getOperations(event, deps); const operations = getOperations(event, deps);
await vim(key, operations);
vim(key, operations, deps);
}; };
const getOperations = (event, deps) => { const getOperations = (event, deps) => {
const { const {
Info = DOM.CurrentInfo, Info = globalThis.DOM.CurrentInfo,
CloudCmd = globalThis.CloudCmd,
Operation, Operation,
unselectFiles, unselectFiles,
setCurrentFile, setCurrentFile,
setCurrentByName, setCurrentByName,
getCurrentName, getCurrentName,
prompt = globalThis.DOM.Dialog.prompt,
preventDefault = event?.preventDefault?.bind(event),
toggleSelectedFile, toggleSelectedFile,
Buffer = {}, Buffer = {},
createFindNext = _createFindNext,
} = deps; } = deps;
return { return {
findNext: createFindNext({
setCurrentByName,
}),
escape: unselectFiles, escape: unselectFiles,
remove: () => { remove: () => {
@ -100,8 +111,8 @@ const getOperations = (event, deps) => {
}, },
find: async () => { find: async () => {
event.preventDefault(); preventDefault();
const [, value] = await Dialog.prompt('Find', ''); const [, value] = await prompt('Find', '');
if (!value) if (!value)
return; return;
@ -112,11 +123,6 @@ const getOperations = (event, deps) => {
setCurrentByName(result); setCurrentByName(result);
}, },
findNext: () => {
const name = finder.findNext();
setCurrentByName(name);
},
findPrevious: () => { findPrevious: () => {
const name = finder.findPrevious(); const name = finder.findPrevious();
setCurrentByName(name); setCurrentByName(name);
@ -125,3 +131,10 @@ const getOperations = (event, deps) => {
}; };
module.exports.selectFile = selectFileNotParent; module.exports.selectFile = selectFileNotParent;
const _createFindNext = (overrides = {}) => () => {
const {setCurrentByName} = overrides;
const name = finder.findNext();
setCurrentByName(name);
};

View file

@ -10,13 +10,13 @@ const pathVim = join(dir, 'vim');
const {getDOM, getCloudCmd} = require('./globals.fixture'); const {getDOM, getCloudCmd} = require('./globals.fixture');
global.DOM = getDOM(); globalThis.DOM = getDOM();
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const vim = require(pathVim); const vim = require('./index.js');
const {assign} = Object; const {assign} = Object;
const {DOM} = global; const {DOM} = globalThis;
const {Buffer} = DOM; const {Buffer} = DOM;
const pathFind = join(dir, 'vim', 'find'); const pathFind = join(dir, 'vim', 'find');
const {reRequire, stopAll} = mockRequire; const {reRequire, stopAll} = mockRequire;
@ -520,15 +520,26 @@ test('cloudcmd: client: key: Enter', async (t) => {
test('cloudcmd: client: key: /', (t) => { test('cloudcmd: client: key: /', (t) => {
const preventDefault = stub(); const preventDefault = stub();
const element = {}; const element = {};
const Info = {
element,
files: [],
};
DOM.CurrentInfo.element = element; const getCurrentName = stub().returns('');
DOM.getCurrentName = () => '';
vim('/', { const event = {
preventDefault, preventDefault,
};
const prompt = stub().returns([]);
vim('/', event, {
getCurrentName,
Info,
prompt,
}); });
t.calledWithNoArgs(preventDefault, 'should call preventDefault'); t.calledWithNoArgs(preventDefault);
t.end(); t.end();
}); });
@ -559,17 +570,13 @@ test('cloudcmd: client: find', (t) => {
test('cloudcmd: client: key: n', (t) => { test('cloudcmd: client: key: n', (t) => {
const findNext = stub(); const findNext = stub();
const createFindNext = stub().returns(findNext);
mockRequire(pathFind, {
findNext,
});
const vim = reRequire(pathVim);
const event = {}; const event = {};
vim('n', event); vim('n', event, {
createFindNext,
stopAll(); });
t.calledWithNoArgs(findNext, 'should call findNext'); t.calledWithNoArgs(findNext, 'should call findNext');
t.end(); t.end();
@ -595,7 +602,7 @@ test('cloudcmd: client: key: N', (t) => {
test('cloudcmd: client: key: make directory', async (t) => { test('cloudcmd: client: key: make directory', async (t) => {
const vim = reRequire(pathVim); const vim = reRequire(pathVim);
const {DOM} = global; const {DOM} = globalThis;
assign(DOM, { assign(DOM, {
promptNewDir: stub(), promptNewDir: stub(),
@ -615,7 +622,7 @@ test('cloudcmd: client: key: make directory', async (t) => {
test('cloudcmd: client: key: make file', (t) => { test('cloudcmd: client: key: make file', (t) => {
const vim = reRequire(pathVim); const vim = reRequire(pathVim);
const {DOM} = global; const {DOM} = globalThis;
assign(DOM, { assign(DOM, {
promptNewFile: stub(), promptNewFile: stub(),
@ -634,28 +641,30 @@ test('cloudcmd: client: key: make file', (t) => {
}); });
test('cloudcmd: client: vim: terminal', (t) => { test('cloudcmd: client: vim: terminal', (t) => {
const {CloudCmd} = global; const CloudCmd = {
assign(CloudCmd, {
Terminal: { Terminal: {
show: stub(), show: stub(),
}, },
}); };
const event = {}; const event = {};
vim('t', event); vim('t', event, {
vim('t', event); CloudCmd,
});
vim('t', event, {
CloudCmd,
});
t.calledWithNoArgs(CloudCmd.Terminal.show); t.calledWithNoArgs(CloudCmd.Terminal.show);
t.end(); t.end();
}); });
test('cloudcmd: client: vim: edit', async (t) => { test('cloudcmd: client: vim: edit', async (t) => {
global.DOM = getDOM(); globalThis.DOM = getDOM();
global.CloudCmd = getCloudCmd(); globalThis.CloudCmd = getCloudCmd();
const {CloudCmd} = global; const {CloudCmd} = globalThis;
assign(CloudCmd, { assign(CloudCmd, {
EditFileVim: { EditFileVim: {

View file

@ -1,6 +1,6 @@
'use strict'; 'use strict';
const fullstore = require('fullstore'); const {fullstore} = require('fullstore');
const store = fullstore(''); const store = fullstore('');
const visual = fullstore(false); const visual = fullstore(false);

View file

@ -5,12 +5,13 @@
const exec = require('execon'); const exec = require('execon');
const itype = require('itype'); const itype = require('itype');
const currify = require('currify'); const currify = require('currify');
const tryToCatch = require('try-to-catch'); const {tryToCatch} = require('try-to-catch');
const clipboard = require('@cloudcmd/clipboard'); const clipboard = require('@cloudcmd/clipboard');
const getRange = require('./get-range'); const getRange = require('./get-range');
const uploadFiles = require('../dom/upload-files'); const uploadFiles = require('../dom/upload-files');
const {FS} = require('../../common/cloudfunc'); const {FS} = require('../../common/cloudfunc.mjs');
const Events = require('#dom/events');
const getIndex = currify(require('./get-index')); const getIndex = currify(require('./get-index'));
@ -29,10 +30,8 @@ module.exports.init = async () => {
]); ]);
}; };
CloudCmd.Listeners = module.exports;
const unselect = (event) => { const unselect = (event) => {
const isMac = /Mac/.test(window.navigator.platform); const isMac = /Mac/.test(globalThis.navigator.platform);
const { const {
shiftKey, shiftKey,
metaKey, metaKey,
@ -50,9 +49,6 @@ const execAll = currify((funcs, event) => {
fn(event); fn(event);
}); });
const Info = DOM.CurrentInfo;
const {Events} = DOM;
const EventsFiles = { const EventsFiles = {
mousedown: exec.with(execIfNotUL, setCurrentFileByEvent), mousedown: exec.with(execIfNotUL, setCurrentFileByEvent),
click: execAll([onClick, exec.with(execIfNotMobile, unselect)]), click: execAll([onClick, exec.with(execIfNotMobile, unselect)]),
@ -112,8 +108,6 @@ module.exports.initKeysPanel = () => {
return; return;
Events.addClick(keysElement, (event) => { Events.addClick(keysElement, (event) => {
event.stopPropagation();
const {target} = event; const {target} = event;
const {id} = target; const {id} = target;
@ -132,7 +126,10 @@ module.exports.initKeysPanel = () => {
'f6': operation('move'), 'f6': operation('move'),
'f7': DOM.promptNewDir, 'f7': DOM.promptNewDir,
'f8': operation('delete'), 'f8': operation('delete'),
'f9': CloudCmd.Menu.show, 'f9': () => {
event.stopPropagation();
CloudCmd.Menu.show();
},
'f10': CloudCmd.Config.show, 'f10': CloudCmd.Config.show,
'~': CloudCmd.Konsole.show, '~': CloudCmd.Konsole.show,
'shift~': CloudCmd.Terminal.show, 'shift~': CloudCmd.Terminal.show,
@ -166,6 +163,7 @@ function getPathListener(panel) {
} }
function isNoCurrent(panel) { function isNoCurrent(panel) {
const Info = DOM.CurrentInfo;
const infoPanel = Info.panel; const infoPanel = Info.panel;
if (!infoPanel) if (!infoPanel)
@ -190,6 +188,7 @@ function decodePath(path) {
} }
async function onPathElementClick(panel, event) { async function onPathElementClick(panel, event) {
const Info = DOM.CurrentInfo;
event.preventDefault(); event.preventDefault();
const element = event.target; const element = event.target;
@ -245,7 +244,7 @@ function onClick(event) {
} }
function toggleSelect(key, files) { function toggleSelect(key, files) {
const isMac = /Mac/.test(window.navigator.platform); const isMac = /Mac/.test(globalThis.navigator.platform);
if (!key) if (!key)
throw Error('key should not be undefined!'); throw Error('key should not be undefined!');
@ -260,6 +259,7 @@ function toggleSelect(key, files) {
} }
function changePanel(element) { function changePanel(element) {
const Info = DOM.CurrentInfo;
const {panel} = Info; const {panel} = Info;
const files = DOM.getByDataName('js-files', panel); const files = DOM.getByDataName('js-files', panel);
const ul = getULElement(element); const ul = getULElement(element);
@ -301,6 +301,7 @@ async function onTouch(event) {
* in Chrome (HTML5) * in Chrome (HTML5)
*/ */
function onDragStart(event) { function onDragStart(event) {
const Info = DOM.CurrentInfo;
const {prefixURL} = CloudCmd; const {prefixURL} = CloudCmd;
const element = getLIElement(event.target); const element = getLIElement(event.target);
const {isDir} = Info; const {isDir} = Info;
@ -337,6 +338,7 @@ function getULElement(element) {
} }
function setCurrentFileByEvent(event) { function setCurrentFileByEvent(event) {
const Info = DOM.CurrentInfo;
const BUTTON_LEFT = 0; const BUTTON_LEFT = 0;
const key = { const key = {
@ -450,7 +452,7 @@ function dragndrop() {
} }
function unload() { function unload() {
DOM.Events.add(['unload', 'beforeunload'], (event) => { Events.add(['unload', 'beforeunload'], (event) => {
const {Key} = CloudCmd; const {Key} = CloudCmd;
const isBind = Key?.isBind(); const isBind = Key?.isBind();
@ -479,7 +481,8 @@ function pop() {
function resize() { function resize() {
Events.add('resize', () => { Events.add('resize', () => {
const is = window.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH; const Info = DOM.CurrentInfo;
const is = globalThis.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH;
if (!is) if (!is)
return; return;

View file

@ -1,18 +1,16 @@
'use strict';
/* global CloudCmd */ /* global CloudCmd */
const exec = require('execon'); import exec from 'execon';
const tryToCatch = require('try-to-catch'); import {tryToCatch} from 'try-to-catch';
const loadJS = require('load.js').js; import {js as loadJS} from 'load.js';
import pascalCase from 'just-pascal-case';
const pascalCase = require('just-pascal-case');
const noJS = (a) => a.replace(/.js$/, ''); const noJS = (a) => a.replace(/.js$/, '');
/** /**
* function load modules * function load modules
* @params = {name, path, func, dobefore, arg} * @params = {name, path, func, dobefore, arg}
*/ */
module.exports = function loadModule(params) { export const loadModule = (params) => {
if (!params) if (!params)
return; return;
@ -51,7 +49,7 @@ module.exports = function loadModule(params) {
const [e, a] = await tryToCatch(m); const [e, a] = await tryToCatch(m);
if (e) if (e)
return console.error(e); return;
return await a.show(...args); return await a.show(...args);
}; };

View file

@ -9,7 +9,7 @@ const load = require('load.js');
const {ajax} = require('../dom/load'); const {ajax} = require('../dom/load');
const Files = require('../dom/files'); const Files = require('../dom/files');
const Images = require('../dom/images'); const Images = require('../dom/images.mjs');
const {log} = CloudCmd; const {log} = CloudCmd;
const upload = currify(_upload); const upload = currify(_upload);

View file

@ -8,16 +8,16 @@ const currify = require('currify');
const wraptile = require('wraptile'); const wraptile = require('wraptile');
const squad = require('squad'); const squad = require('squad');
const {promisify} = require('es6-promisify'); const {promisify} = require('es6-promisify');
const tryToCatch = require('try-to-catch'); const {tryToCatch} = require('try-to-catch');
const load = require('load.js'); const load = require('load.js');
const createElement = require('@cloudcmd/create-element'); const createElement = require('@cloudcmd/create-element');
const input = require('./input'); const input = require('./input');
const Images = require('../../dom/images'); const Images = require('../../dom/images.mjs');
const Events = require('../../dom/events'); const Events = require('#dom/events');
const Files = require('../../dom/files'); const Files = require('../../dom/files');
const {getTitle} = require('../../../common/cloudfunc'); const {getTitle} = require('../../../common/cloudfunc.mjs');
const {Dialog, setTitle} = DOM; const {Dialog, setTitle} = DOM;
const Name = 'Config'; const Name = 'Config';

View file

@ -6,7 +6,7 @@
CloudCmd.Contact = exports; CloudCmd.Contact = exports;
const olark = require('@cloudcmd/olark'); const olark = require('@cloudcmd/olark');
const Images = require('../dom/images'); const Images = require('../dom/images.mjs');
const {Events} = DOM; const {Events} = DOM;
const {Key} = CloudCmd; const {Key} = CloudCmd;

View file

@ -3,7 +3,7 @@
/* global CloudCmd */ /* global CloudCmd */
CloudCmd.EditFileVim = exports; CloudCmd.EditFileVim = exports;
const Events = require('../dom/events'); const Events = require('#dom/events');
const {Key} = CloudCmd; const {Key} = CloudCmd;

View file

@ -4,7 +4,7 @@
CloudCmd.EditFile = exports; CloudCmd.EditFile = exports;
const Format = require('format-io'); const Format = require('format-io');
const fullstore = require('fullstore'); const {fullstore} = require('fullstore');
const exec = require('execon'); const exec = require('execon');
const supermenu = require('supermenu'); const supermenu = require('supermenu');

View file

@ -3,7 +3,7 @@
/* global CloudCmd */ /* global CloudCmd */
CloudCmd.EditNamesVim = exports; CloudCmd.EditNamesVim = exports;
const Events = require('../dom/events'); const Events = require('#dom/events');
const {Key} = CloudCmd; const {Key} = CloudCmd;
const ConfigView = { const ConfigView = {

View file

@ -1,21 +1,17 @@
'use strict'; 'use strict';
const {tryToCatch} = require('try-to-catch');
/* global CloudCmd, DOM */ /* global CloudCmd, DOM */
CloudCmd.EditNames = exports; CloudCmd.EditNames = exports;
const currify = require('currify');
const exec = require('execon'); const exec = require('execon');
const supermenu = require('supermenu'); const supermenu = require('supermenu');
const multiRename = require('multi-rename'); const {multiRename} = require('multi-rename');
const reject = Promise.reject.bind(Promise);
const Info = DOM.CurrentInfo; const Info = DOM.CurrentInfo;
const {Dialog} = DOM; const {Dialog} = DOM;
const refresh = currify(_refresh);
const rename = currify(_rename);
let Menu; let Menu;
const ConfigView = { const ConfigView = {
@ -93,7 +89,7 @@ function setListeners() {
DOM.Events.addOnce('contextmenu', element, setMenu); DOM.Events.addOnce('contextmenu', element, setMenu);
} }
function applyNames() { async function applyNames() {
const dir = Info.dirPath; const dir = Info.dirPath;
const from = getActiveNames(); const from = getActiveNames();
const nameIndex = from.indexOf(Info.name); const nameIndex = from.indexOf(Info.name);
@ -105,18 +101,18 @@ function applyNames() {
const root = CloudCmd.config('root'); const root = CloudCmd.config('root');
Promise const response = rename(dir, from, to, root);
.resolve(root) const [error] = await tryToCatch(refresh, to, nameIndex, response);
.then(rename(dir, from, to))
.then(refresh(to, nameIndex)) if (error)
.catch(alert); alert(error);
} }
function _refresh(to, nameIndex, res) { function refresh(to, nameIndex, res) {
if (res.status === 404) if (res.status === 404) {
return res const error = res.text();
.text() throw error;
.then(reject); }
const currentName = to[nameIndex]; const currentName = to[nameIndex];
@ -132,7 +128,7 @@ function getDir(root, dir) {
return root + dir; return root + dir;
} }
function _rename(path, from, to, root) { function rename(path, from, to, root) {
const dir = getDir(root, path); const dir = getDir(root, path);
const {prefix} = CloudCmd; const {prefix} = CloudCmd;
@ -172,8 +168,8 @@ function setMenu(event) {
}; };
const menuData = { const menuData = {
'Save Ctrl+S': () => { 'Save Ctrl+S': async () => {
applyNames(); await applyNames();
hide(); hide();
}, },
'Go To Line Ctrl+G': () => { 'Go To Line Ctrl+G': () => {
@ -214,6 +210,7 @@ async function isChanged() {
if (!editor.isChanged()) if (!editor.isChanged())
return; return;
const [, names] = await Dialog.confirm(msg); const [cancel] = await Dialog.confirm(msg);
names && applyNames();
!cancel && await applyNames();
} }

View file

@ -5,10 +5,10 @@
const montag = require('montag'); const montag = require('montag');
const {promisify} = require('es6-promisify'); const {promisify} = require('es6-promisify');
const tryToCatch = require('try-to-catch'); const {tryToCatch} = require('try-to-catch');
const createElement = require('@cloudcmd/create-element'); const createElement = require('@cloudcmd/create-element');
const load = require('load.js'); const load = require('load.js');
const {MAX_FILE_SIZE: maxSize} = require('../../common/cloudfunc'); const {MAX_FILE_SIZE: maxSize} = require('../../common/cloudfunc.mjs');
const {time, timeEnd} = require('../../common/util'); const {time, timeEnd} = require('../../common/util');
const getEditor = () => editor; const getEditor = () => editor;

View file

@ -3,7 +3,7 @@
/* global CloudCmd */ /* global CloudCmd */
CloudCmd.Help = exports; CloudCmd.Help = exports;
const Images = require('../dom/images'); const Images = require('../dom/images.mjs');
module.exports.init = () => { module.exports.init = () => {
Images.show.load('top'); Images.show.load('top');

View file

@ -8,11 +8,11 @@ CloudCmd.Konsole = exports;
const exec = require('execon'); const exec = require('execon');
const currify = require('currify'); const currify = require('currify');
const tryToCatch = require('try-to-catch'); const {tryToCatch} = require('try-to-catch');
const loadJS = require('load.js').js; const loadJS = require('load.js').js;
const createElement = require('@cloudcmd/create-element'); const createElement = require('@cloudcmd/create-element');
const Images = require('../dom/images'); const Images = require('../dom/images.mjs');
const {Dialog, CurrentInfo: Info} = DOM; const {Dialog, CurrentInfo: Info} = DOM;
const rmLastSlash = (a) => a.replace(/\/$/, '') || '/'; const rmLastSlash = (a) => a.replace(/\/$/, '') || '/';

View file

@ -5,7 +5,7 @@ CloudCmd.Markdown = exports;
const createElement = require('@cloudcmd/create-element'); const createElement = require('@cloudcmd/create-element');
const Images = require('../dom/images'); const Images = require('../dom/images.mjs');
const {Markdown} = require('../dom/rest'); const {Markdown} = require('../dom/rest');
const {alert} = require('../dom/dialog'); const {alert} = require('../dom/dialog');

View file

@ -14,7 +14,7 @@ export const createCloudMenu = async (fm, options, menuData) => {
async function loadMenu() { async function loadMenu() {
if (CloudCmd.config('menu') === 'aleman') { if (CloudCmd.config('menu') === 'aleman') {
const {host, protocol} = window.location; const {host, protocol} = globalThis.location;
const url = `${protocol}//${host}/node_modules/aleman/menu/menu.js`; const url = `${protocol}//${host}/node_modules/aleman/menu/menu.js`;
const {createMenu} = await import(/* webpackIgnore: true */url); const {createMenu} = await import(/* webpackIgnore: true */url);

View file

@ -6,7 +6,7 @@ const exec = require('execon');
const wrap = require('wraptile'); const wrap = require('wraptile');
const createElement = require('@cloudcmd/create-element'); const createElement = require('@cloudcmd/create-element');
const {FS} = require('../../../common/cloudfunc'); const {FS} = require('../../../common/cloudfunc.mjs');
const {getIdBySrc} = require('../../dom/load'); const {getIdBySrc} = require('../../dom/load');
const RESTful = require('../../dom/rest'); const RESTful = require('../../dom/rest');
@ -109,6 +109,7 @@ function getOptions({type}) {
const options = { const options = {
icon: true, icon: true,
infiniteScroll: false,
beforeClose: Key.setBind, beforeClose: Key.setBind,
beforeHide: Key.setBind, beforeHide: Key.setBind,
beforeShow: exec.with(beforeShow, func), beforeShow: exec.with(beforeShow, func),

View file

@ -1,28 +1,20 @@
/* global CloudCmd */ import currify from 'currify';
/* global Util */ import wraptile from 'wraptile';
/* global DOM */ import {promisify} from 'es6-promisify';
/* global fileop */ import exec from 'execon';
import load from 'load.js';
import {tryToCatch} from 'try-to-catch';
import {encode} from '../../../common/entity.js';
import removeExtension from './remove-extension.js';
import {setListeners} from './set-listeners.mjs';
import getNextCurrentName from './get-next-current-name.js';
'use strict'; const {DOM, CloudCmd} = globalThis;
const currify = require('currify');
const wraptile = require('wraptile');
const {promisify} = require('es6-promisify');
const exec = require('execon');
const load = require('load.js');
const tryToCatch = require('try-to-catch');
const {encode} = require('../../../common/entity');
const removeExtension = require('./remove-extension');
const setListeners = require('./set-listeners');
const getNextCurrentName = require('./get-next-current-name');
const removeQuery = (a) => a.replace(/\?.*/, ''); const removeQuery = (a) => a.replace(/\?.*/, '');
const Name = 'Operation'; const Name = 'Operation';
CloudCmd[Name] = exports;
const {config} = CloudCmd; const {config} = CloudCmd;
const {Dialog, Images} = DOM; const {Dialog, Images} = DOM;
@ -53,7 +45,7 @@ const noFilesCheck = () => {
return is; return is;
}; };
module.exports.init = promisify((callback) => { export const init = promisify((callback) => {
showLoad(); showLoad();
exec.series([ exec.series([
@ -92,7 +84,7 @@ const onConnect = currify((fn, operator) => {
async function initOperations(prefix, socketPrefix, fn) { async function initOperations(prefix, socketPrefix, fn) {
socketPrefix = `${socketPrefix}/fileop`; socketPrefix = `${socketPrefix}/fileop`;
const operator = await fileop({ const operator = await globalThis.fileop({
prefix, prefix,
socketPrefix, socketPrefix,
}); });
@ -198,11 +190,11 @@ function getPacker(type) {
return packTarFn; return packTarFn;
} }
module.exports.hide = () => { export const hide = () => {
CloudCmd.View.hide(); CloudCmd.View.hide();
}; };
module.exports.show = (operation, data) => { export const show = (operation, data) => {
if (!Loaded) if (!Loaded)
return; return;
@ -505,8 +497,14 @@ async function prompt(msg, to, names) {
return await Dialog.prompt(msg, to); return await Dialog.prompt(msg, to);
} }
globalThis.CloudCmd[Name] = {
init,
hide,
show,
};
async function loadAll() { async function loadAll() {
const {prefix} = CloudCmd; const {prefix} = globalThis.CloudCmd;
const file = `${prefix}/fileop/fileop.js`; const file = `${prefix}/fileop/fileop.js`;
const [error] = await tryToCatch(load.js, file); const [error] = await tryToCatch(load.js, file);

View file

@ -9,10 +9,10 @@ module.exports = (name) => {
}; };
function getExtension(name) { function getExtension(name) {
if (/\.tar\.gz$/.test(name)) if (name.endsWith('.tar.gz'))
return '.tar.gz'; return '.tar.gz';
if (/\.tar\.bz2$/.test(name)) if (name.endsWith('.tar.bz2'))
return '.tar.bz2'; return '.tar.bz2';
return getExt(name); return getExt(name);

View file

@ -1,14 +1,11 @@
'use strict';
/* global DOM */ /* global DOM */
const forEachKey = require('for-each-key'); import forEachKey from 'for-each-key';
import wraptile from 'wraptile';
const wraptile = require('wraptile'); import format from './format.js';
const format = require('./format');
const {Dialog, Images} = DOM; const {Dialog, Images} = DOM;
module.exports = (options) => (emitter) => { export const setListeners = (options) => (emitter) => {
const { const {
operation, operation,
callback, callback,
@ -43,10 +40,13 @@ module.exports = (options) => (emitter) => {
operation, operation,
})); }));
let noProgress = true;
const listeners = { const listeners = {
progress: (value) => { progress: (value) => {
done = value === 100; done = value === 100;
progress.setProgress(value); progress.setProgress(value);
noProgress = false;
}, },
end: () => { end: () => {
@ -54,7 +54,7 @@ module.exports = (options) => (emitter) => {
forEachKey(removeListener, listeners); forEachKey(removeListener, listeners);
progress.remove(); progress.remove();
if (lastError || done) if (lastError || done || noProgress)
callback(); callback();
}, },

View file

@ -1,10 +1,20 @@
'use strict'; 'use strict';
/* global DOM */
require('domtokenlist-shim'); require('domtokenlist-shim');
const scrollIntoViewIfNeeded = require('scroll-into-view-if-needed'); const _scrollIntoViewIfNeeded = require('scroll-into-view-if-needed');
DOM.scrollIntoViewIfNeeded = (el) => scrollIntoViewIfNeeded(el, { globalThis.DOM = globalThis.DOM || {};
block: 'nearest',
}); const scrollIntoViewIfNeeded = (el, overrides = {}) => {
const {
scroll = _scrollIntoViewIfNeeded,
} = overrides;
return scroll(el, {
block: 'nearest',
});
};
globalThis.DOM.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded;
module.exports.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded;

View file

@ -1,24 +1,15 @@
'use strict'; 'use strict';
const {test, stub} = require('supertape'); const {test, stub} = require('supertape');
const {scrollIntoViewIfNeeded} = require('./polyfill');
const mockRequire = require('mock-require');
const {stopAll} = mockRequire;
test('cloudcmd: client: polyfill: scrollIntoViewIfNeaded', (t) => { test('cloudcmd: client: polyfill: scrollIntoViewIfNeaded', (t) => {
const {DOM} = global;
const scroll = stub(); const scroll = stub();
const el = {}; const el = {};
global.DOM = {}; scrollIntoViewIfNeeded(el, {
scroll,
mockRequire('scroll-into-view-if-needed', scroll); });
mockRequire.reRequire('./polyfill');
global.DOM.scrollIntoViewIfNeeded(el);
mockRequire.stop('scroll-into-view-if-neaded');
global.DOM = DOM;
const args = [ const args = [
el, { el, {
@ -26,8 +17,6 @@ test('cloudcmd: client: polyfill: scrollIntoViewIfNeaded', (t) => {
}, },
]; ];
stopAll();
t.calledWith(scroll, args, 'should call scrollIntoViewIfNeaded'); t.calledWith(scroll, args, 'should call scrollIntoViewIfNeaded');
t.end(); t.end();
}); });

View file

@ -2,15 +2,15 @@
/* global CloudCmd, gritty */ /* global CloudCmd, gritty */
const {promisify} = require('es6-promisify'); const {promisify} = require('es6-promisify');
const tryToCatch = require('try-to-catch'); const {tryToCatch} = require('try-to-catch');
const fullstore = require('fullstore'); const {fullstore} = require('fullstore');
require('../../css/terminal.css'); require('../../css/terminal.css');
const exec = require('execon'); const exec = require('execon');
const load = require('load.js'); const load = require('load.js');
const DOM = require('../dom'); const DOM = require('../dom');
const Images = require('../dom/images'); const Images = require('../dom/images.mjs');
const {Dialog} = DOM; const {Dialog} = DOM;
const {Key, config} = CloudCmd; const {Key, config} = CloudCmd;
@ -33,7 +33,7 @@ const loadAll = async () => {
const [e] = await tryToCatch(load.parallel, [js, css]); const [e] = await tryToCatch(load.parallel, [js, css]);
if (e) { if (e) {
const src = e.target.src.replace(window.location.href, ''); const src = e.target.src.replace(globalThis.location.href, '');
return Dialog.alert(`file ${src} could not be loaded`); return Dialog.alert(`file ${src} could not be loaded`);
} }

View file

@ -2,14 +2,14 @@
/* global CloudCmd */ /* global CloudCmd */
/* global gritty */ /* global gritty */
const tryToCatch = require('try-to-catch'); const {tryToCatch} = require('try-to-catch');
require('../../css/terminal.css'); require('../../css/terminal.css');
const exec = require('execon'); const exec = require('execon');
const load = require('load.js'); const load = require('load.js');
const DOM = require('../dom'); const DOM = require('../dom');
const Images = require('../dom/images'); const Images = require('../dom/images.mjs');
const loadParallel = load.parallel; const loadParallel = load.parallel;
@ -32,7 +32,7 @@ const loadAll = async () => {
const [e] = await tryToCatch(loadParallel, [js, css]); const [e] = await tryToCatch(loadParallel, [js, css]);
if (e) { if (e) {
const src = e.target.src.replace(window.location.href, ''); const src = e.target.src.replace(globalThis.location.href, '');
return Dialog.alert(`file ${src} could not be loaded`); return Dialog.alert(`file ${src} could not be loaded`);
} }

View file

@ -6,7 +6,7 @@ CloudCmd.Upload = exports;
const createElement = require('@cloudcmd/create-element'); const createElement = require('@cloudcmd/create-element');
const Files = require('../dom/files'); const Files = require('../dom/files');
const Images = require('../dom/images'); const Images = require('../dom/images.mjs');
const uploadFiles = require('../dom/upload-files'); const uploadFiles = require('../dom/upload-files');
module.exports.init = async () => { module.exports.init = async () => {

View file

@ -5,14 +5,14 @@ require('../../../css/user-menu.css');
const currify = require('currify'); const currify = require('currify');
const wraptile = require('wraptile'); const wraptile = require('wraptile');
const fullstore = require('fullstore'); const {fullstore} = require('fullstore');
const load = require('load.js'); const load = require('load.js');
const createElement = require('@cloudcmd/create-element'); const createElement = require('@cloudcmd/create-element');
const tryCatch = require('try-catch'); const {tryCatch} = require('try-catch');
const tryToCatch = require('try-to-catch'); const {tryToCatch} = require('try-to-catch');
const {codeFrameColumns} = require('@babel/code-frame'); const {codeFrameColumns} = require('@babel/code-frame');
const Images = require('../../dom/images'); const Images = require('../../dom/images.mjs');
const Dialog = require('../../dom/dialog'); const Dialog = require('../../dom/dialog');
const getUserMenu = require('./get-user-menu'); const getUserMenu = require('./get-user-menu');
const navigate = require('./navigate'); const navigate = require('./navigate');

View file

@ -1,6 +1,6 @@
'use strict'; 'use strict';
const fullstore = require('fullstore'); const {fullstore} = require('fullstore');
const { const {
J, J,

View file

@ -5,7 +5,7 @@ const testRegExp = currify((name, reg) => reg.test(name));
const getRegExp = (ext) => RegExp(`\\.${ext}$`, 'i'); const getRegExp = (ext) => RegExp(`\\.${ext}$`, 'i');
const isPDF = (a) => /\.pdf$/i.test(a); const isPDF = (a) => /\.pdf$/i.test(a);
const isHTML = (a) => /\.html$/.test(a); const isHTML = (a) => a.endsWith('.html');
const isMarkdown = (a) => /.\.md$/.test(a); const isMarkdown = (a) => /.\.md$/.test(a);
module.exports = (name) => { module.exports = (name) => {

View file

@ -2,19 +2,22 @@
'use strict'; 'use strict';
const CloudCmd = globalThis.CloudCmd || {};
const DOM = globalThis.DOM || {};
require('../../../css/view.css'); require('../../../css/view.css');
const rendy = require('rendy'); const rendy = require('rendy');
const currify = require('currify'); const currify = require('currify');
const wraptile = require('wraptile'); const wraptile = require('wraptile');
const tryToCatch = require('try-to-catch'); const {tryToCatch} = require('try-to-catch');
const load = require('load.js'); const load = require('load.js');
const modal = require('@cloudcmd/modal'); const _modal = require('@cloudcmd/modal');
const createElement = require('@cloudcmd/create-element'); const _createElement = require('@cloudcmd/create-element');
const {time} = require('../../../common/util'); const {time} = require('../../../common/util');
const {FS} = require('../../../common/cloudfunc'); const {FS} = require('../../../common/cloudfunc.mjs');
const { const {
isImage, isImage,
@ -23,8 +26,8 @@ const {
} = require('./types'); } = require('./types');
const Files = require('../../dom/files'); const Files = require('../../dom/files');
const Events = require('../../dom/events'); const Events = require('#dom/events');
const Images = require('../../dom/images'); const Images = require('../../dom/images.mjs');
const {encode} = require('../../../common/entity'); const {encode} = require('../../../common/entity');
const isString = (a) => typeof a === 'string'; const isString = (a) => typeof a === 'string';
@ -113,7 +116,7 @@ async function show(data, options = {}) {
if (!options || options.bindKeys !== false) if (!options || options.bindKeys !== false)
Events.addKey(listener); Events.addKey(listener);
El = createElement('div', { El = _createElement('div', {
className: 'view', className: 'view',
notAppend: true, notAppend: true,
}); });
@ -126,7 +129,7 @@ async function show(data, options = {}) {
else else
El.append(data); El.append(data);
modal.open(El, initConfig(options)); _modal.open(El, initConfig(options));
return; return;
} }
@ -157,7 +160,11 @@ async function show(data, options = {}) {
} }
module.exports._createIframe = createIframe; module.exports._createIframe = createIframe;
function createIframe(src) { function createIframe(src, overrides = {}) {
const {
createElement = _createElement,
} = overrides;
const element = createElement('iframe', { const element = createElement('iframe', {
src, src,
width: '100%', width: '100%',
@ -172,7 +179,8 @@ function createIframe(src) {
} }
module.exports._viewHtml = viewHtml; module.exports._viewHtml = viewHtml;
function viewHtml(src) { function viewHtml(src, overrides = {}) {
const {modal = _modal} = overrides;
modal.open(createIframe(src), Config); modal.open(createIframe(src), Config);
} }
@ -184,7 +192,7 @@ function viewPDF(src) {
if (CloudCmd.config('showFileName')) if (CloudCmd.config('showFileName'))
options.title = Info.name; options.title = Info.name;
modal.open(element, options); _modal.open(element, options);
} }
async function viewMedia(path) { async function viewMedia(path) {
@ -205,7 +213,7 @@ async function viewMedia(path) {
}, },
}; };
modal.open(element, allConfig); _modal.open(element, allConfig);
} }
async function viewFile() { async function viewFile() {
@ -221,7 +229,7 @@ async function viewFile() {
options.title = Info.name; options.title = Info.name;
El.append(element); El.append(element);
modal.open(El, options); _modal.open(El, options);
} }
const copy = (a) => assign({}, a); const copy = (a) => assign({}, a);
@ -253,7 +261,7 @@ function initConfig(options) {
} }
function hide() { function hide() {
modal.close(); _modal.close();
} }
function viewImage(path, prefixURL) { function viewImage(path, prefixURL) {
@ -286,7 +294,7 @@ function viewImage(path, prefixURL) {
...imageConfig, ...imageConfig,
}; };
modal.open(titles, config); _modal.open(titles, config);
} }
async function getMediaElement(src) { async function getMediaElement(src) {
@ -311,7 +319,7 @@ async function getMediaElement(src) {
name, name,
}); });
const element = createElement('div', { const element = _createElement('div', {
innerHTML, innerHTML,
}); });

View file

@ -3,22 +3,20 @@
require('css-modules-require-hook/preset'); require('css-modules-require-hook/preset');
const autoGlobals = require('auto-globals'); const autoGlobals = require('auto-globals');
const stub = require('@cloudcmd/stub'); const {stub} = require('@cloudcmd/stub');
const mockRequire = require('mock-require');
const test = autoGlobals(require('supertape')); const test = autoGlobals(require('supertape'));
const {reRequire, stopAll} = mockRequire; const {
_initConfig,
_viewHtml,
_Config,
_createIframe,
} = require('.');
test('cloudcmd: client: view: initConfig', (t) => { test('cloudcmd: client: view: initConfig', (t) => {
let config; let config;
let i = 0; let i = 0;
const {CloudCmd, DOM} = global;
global.CloudCmd = {};
global.DOM = {};
const {_initConfig} = reRequire('.');
const afterClose = () => ++i; const afterClose = () => ++i;
const options = { const options = {
afterClose, afterClose,
@ -30,54 +28,32 @@ test('cloudcmd: client: view: initConfig', (t) => {
config = _initConfig(options); config = _initConfig(options);
config.afterClose(); config.afterClose();
global.CloudCmd = CloudCmd;
global.DOM = DOM;
t.equal(i, 2, 'should not change default config'); t.equal(i, 2, 'should not change default config');
t.end(); t.end();
}); });
test('cloudcmd: client: view: initConfig: no options', (t) => { test('cloudcmd: client: view: initConfig: no options', (t) => {
const {CloudCmd, DOM} = global;
global.CloudCmd = {};
global.DOM = {};
const {_initConfig} = reRequire('.');
const config = _initConfig(); const config = _initConfig();
global.CloudCmd = CloudCmd;
global.DOM = DOM;
t.equal(typeof config, 'object'); t.equal(typeof config, 'object');
t.end(); t.end();
}); });
test('cloudcmd: client: view: html', (t) => { test('cloudcmd: client: view: html', (t) => {
const {CloudCmd, DOM} = global;
global.CloudCmd = {};
global.DOM = {};
const open = stub(); const open = stub();
const modal = {
mockRequire('@cloudcmd/modal', {
open, open,
}); };
const {_viewHtml, _Config} = reRequire('.');
const src = '/hello.html'; const src = '/hello.html';
_viewHtml(src); _viewHtml(src, {
modal,
global.CloudCmd = CloudCmd; });
global.DOM = DOM;
const [first] = open.args; const [first] = open.args;
const [arg] = first; const [arg] = first;
stopAll();
t.deepEqual(first, [arg, _Config]); t.deepEqual(first, [arg, _Config]);
t.end(); t.end();
}); });
@ -89,12 +65,11 @@ test('cloudcmd: client: view: createIframe', (t) => {
}; };
const createElement = stub().returns(el); const createElement = stub().returns(el);
mockRequire('@cloudcmd/create-element', createElement);
const {_createIframe} = reRequire('.');
const src = '/hello.html'; const src = '/hello.html';
_createIframe(src);
_createIframe(src, {
createElement,
});
const expected = { const expected = {
src, src,
@ -102,8 +77,6 @@ test('cloudcmd: client: view: createIframe', (t) => {
width: '100%', width: '100%',
}; };
stopAll();
t.calledWith(createElement, ['iframe', expected]); t.calledWith(createElement, ['iframe', expected]);
t.end(); t.end();
}); });
@ -116,13 +89,10 @@ test('cloudcmd: client: view: createIframe: returns', (t) => {
const createElement = stub().returns(el); const createElement = stub().returns(el);
mockRequire('@cloudcmd/create-element', createElement);
const {_createIframe} = reRequire('.');
const src = '/hello.html'; const src = '/hello.html';
const result = _createIframe(src); const result = _createIframe(src, {
createElement,
stopAll(); });
t.equal(result, el); t.equal(result, el);
t.end(); t.end();

View file

@ -7,7 +7,7 @@ const testRegExp = currify((name, reg) => reg.test(name));
const getRegExp = (ext) => RegExp(`\\.${ext}$`, 'i'); const getRegExp = (ext) => RegExp(`\\.${ext}$`, 'i');
const isPDF = (a) => /\.pdf$/i.test(a); const isPDF = (a) => /\.pdf$/i.test(a);
const isHTML = (a) => /\.html$/.test(a); const isHTML = (a) => a.endsWith('.html');
const isMarkdown = (a) => /.\.md$/.test(a); const isMarkdown = (a) => /.\.md$/.test(a);
module.exports.getType = async (path) => { module.exports.getType = async (path) => {

View file

@ -22,12 +22,9 @@ test('cloudcmd: client: view: types: detectType', async (t) => {
headers: [], headers: [],
}); });
const originalFetch = global.fetch; globalThis.fetch = fetch;
global.fetch = fetch;
await _detectType('/hello'); await _detectType('/hello');
global.fetch = originalFetch;
const expected = ['/hello', { const expected = ['/hello', {
method: 'HEAD', method: 'HEAD',
}]; }];
@ -37,17 +34,13 @@ test('cloudcmd: client: view: types: detectType', async (t) => {
}); });
test('cloudcmd: client: view: types: detectType: found', async (t) => { test('cloudcmd: client: view: types: detectType: found', async (t) => {
const originalFetch = global.fetch; globalThis.fetch = stub().returns({
global.fetch = stub().returns({
headers: [ headers: [
['content-type', 'image/png'], ['content-type', 'image/png'],
], ],
}); });
const result = await _detectType('/hello'); const result = await _detectType('/hello');
global.fetch = originalFetch;
t.equal(result, '.png'); t.equal(result, '.png');
t.end(); t.end();
}); });

View file

@ -1,31 +0,0 @@
'use strict';
/* global CloudCmd */
const DOM = require('./dom');
const Info = DOM.CurrentInfo;
const {sort, order} = CloudCmd;
const position = DOM.getPanelPosition();
let sortPrevious = sort[position];
const {getPanel} = DOM;
CloudCmd.sortPanel = (name, panel = getPanel()) => {
const position = panel.dataset.name.replace('js-', '');
if (name !== sortPrevious)
order[position] = 'asc';
else if (order[position] === 'asc')
order[position] = 'desc';
else
order[position] = 'asc';
sortPrevious = name;
sort[position] = name;
const noCurrent = position !== Info.panelPosition;
CloudCmd.refresh({
panel,
noCurrent,
});
};

36
client/sort.mjs Normal file
View file

@ -0,0 +1,36 @@
/* global CloudCmd */
import {fullstore} from 'fullstore';
import DOM from './dom/index.js';
const sortPrevious = fullstore();
const {getPanel} = DOM;
export const initSortPanel = () => {
const {sort} = CloudCmd;
const position = DOM.getPanelPosition();
sortPrevious(sort[position]);
};
export const sortPanel = (name, panel = getPanel()) => {
const {sort, order} = CloudCmd;
const Info = DOM.CurrentInfo;
const position = panel.dataset.name.replace('js-', '');
if (name !== sortPrevious())
order[position] = 'asc';
else if (order[position] === 'asc')
order[position] = 'desc';
else
order[position] = 'asc';
sortPrevious(name);
sort[position] = name;
const noCurrent = position !== Info.panelPosition;
CloudCmd.refresh({
panel,
noCurrent,
});
};

View file

@ -1,6 +1,6 @@
'use strict'; 'use strict';
const tryToCatch = require('try-to-catch'); const {tryToCatch} = require('try-to-catch');
module.exports.registerSW = registerSW; module.exports.registerSW = registerSW;
module.exports.unregisterSW = unregisterSW; module.exports.unregisterSW = unregisterSW;

View file

@ -3,14 +3,18 @@
const autoGlobals = require('auto-globals'); const autoGlobals = require('auto-globals');
const tape = require('supertape'); const tape = require('supertape');
const stub = require('@cloudcmd/stub'); const {stub} = require('@cloudcmd/stub');
const {tryCatch} = require('try-catch');
const {
listenSW,
registerSW,
unregisterSW,
} = require('./register');
const tryCatch = require('try-catch');
const {reRequire} = require('mock-require');
const test = autoGlobals(tape); const test = autoGlobals(tape);
test('sw: listen', (t) => { test('sw: listen', (t) => {
const {listenSW} = reRequire('./register');
const addEventListener = stub(); const addEventListener = stub();
const sw = { const sw = {
addEventListener, addEventListener,
@ -23,7 +27,6 @@ test('sw: listen', (t) => {
}); });
test('sw: lesten: no sw', (t) => { test('sw: lesten: no sw', (t) => {
const {listenSW} = reRequire('./register');
const [e] = tryCatch(listenSW, null, 'hello', 'world'); const [e] = tryCatch(listenSW, null, 'hello', 'world');
t.notOk(e, 'should not throw'); t.notOk(e, 'should not throw');
@ -31,8 +34,6 @@ test('sw: lesten: no sw', (t) => {
}); });
test('sw: register: registerSW: no serviceWorker', async (t, {navigator}) => { test('sw: register: registerSW: no serviceWorker', async (t, {navigator}) => {
const {registerSW} = reRequire('./register');
delete navigator.serviceWorker; delete navigator.serviceWorker;
await registerSW(); await registerSW();
@ -46,8 +47,6 @@ test('sw: register: registerSW: no https', async (t, {location, navigator}) => {
location.protocol = 'http:'; location.protocol = 'http:';
const {registerSW} = reRequire('./register');
await registerSW(); await registerSW();
t.notCalled(register, 'should not call register'); t.notCalled(register, 'should not call register');
@ -62,8 +61,6 @@ test('sw: register: registerSW: http', async (t, {location, navigator}) => {
const {register} = navigator.serviceWorker; const {register} = navigator.serviceWorker;
const {registerSW} = reRequire('./register');
await registerSW(); await registerSW();
t.notCalled(register, 'should not call register'); t.notCalled(register, 'should not call register');
@ -79,8 +76,6 @@ test('sw: register: registerSW: https self-signed', async (t, {location, navigat
const {register} = navigator.serviceWorker; const {register} = navigator.serviceWorker;
register.throws(Error('Cannot register service worker!')); register.throws(Error('Cannot register service worker!'));
const {registerSW} = reRequire('./register');
const result = await registerSW(); const result = await registerSW();
t.notOk(result, 'should not throw'); t.notOk(result, 'should not throw');
@ -91,8 +86,6 @@ test('sw: register: registerSW', async (t, {location, navigator}) => {
location.hostname = 'localhost'; location.hostname = 'localhost';
const {register} = navigator.serviceWorker; const {register} = navigator.serviceWorker;
const {registerSW} = reRequire('./register');
await registerSW('/hello'); await registerSW('/hello');
t.calledWith(register, ['/hello/sw.js'], 'should call register'); t.calledWith(register, ['/hello/sw.js'], 'should call register');
@ -107,8 +100,6 @@ test('sw: register: unregisterSW', async (t, {location, navigator}) => {
register.returns(serviceWorker); register.returns(serviceWorker);
const {unregisterSW} = reRequire('./register');
await unregisterSW('/hello'); await unregisterSW('/hello');
t.calledWith(register, ['/hello/sw.js'], 'should call register'); t.calledWith(register, ['/hello/sw.js'], 'should call register');

View file

@ -2,7 +2,7 @@
const process = require('node:process'); const process = require('node:process');
const codegen = require('codegen.macro'); const codegen = require('codegen.macro');
const tryToCatch = require('try-to-catch'); const {tryToCatch} = require('try-to-catch');
const currify = require('currify'); const currify = require('currify');
const isDev = process.env.NODE_ENV === 'development'; const isDev = process.env.NODE_ENV === 'development';
@ -50,14 +50,14 @@ const getRequest = (a, request) => {
return createRequest('/'); return createRequest('/');
}; };
self.addEventListener('install', wait(onInstall)); globalThis.addEventListener('install', wait(onInstall));
self.addEventListener('fetch', respondWith(onFetch)); globalThis.addEventListener('fetch', respondWith(onFetch));
self.addEventListener('activate', wait(onActivate)); globalThis.addEventListener('activate', wait(onActivate));
async function onActivate() { async function onActivate() {
console.info(`cloudcmd: sw: activate: ${NAME}`); console.info(`cloudcmd: sw: activate: ${NAME}`);
await self.clients.claim(); await globalThis.clients.claim();
const keys = await caches.keys(); const keys = await caches.keys();
const deleteCache = caches.delete.bind(caches); const deleteCache = caches.delete.bind(caches);
@ -67,7 +67,7 @@ async function onActivate() {
async function onInstall() { async function onInstall() {
console.info(`cloudcmd: sw: install: ${NAME}`); console.info(`cloudcmd: sw: install: ${NAME}`);
await self.skipWaiting(); await globalThis.skipWaiting();
} }
async function onFetch(event) { async function onFetch(event) {

View file

@ -1,19 +0,0 @@
'use strict';
module.exports.btoa = (str) => {
if (typeof btoa === 'function')
return btoa(str);
return Buffer
.from(str)
.toString('base64');
};
module.exports.atob = (str) => {
if (typeof atob === 'function')
return atob(str);
return Buffer
.from(str, 'base64')
.toString('binary');
};

View file

@ -1,55 +0,0 @@
'use strict';
const {test, stub} = require('supertape');
const {btoa, atob} = require('./base64');
test('btoa: browser', (t) => {
const btoaOriginal = global.btoa;
const btoaStub = stub();
const str = 'hello';
global.btoa = btoaStub;
btoa(str);
global.btoa = btoaOriginal;
t.calledWith(btoaStub, [str], 'should call global.btoa');
t.end();
});
test('btoa: node', (t) => {
const str = 'hello';
const expected = 'aGVsbG8=';
const result = btoa(str);
t.equal(result, expected, 'should encode base64');
t.end();
});
test('atob: browser', (t) => {
const atobOriginal = global.atob;
const atobStub = stub();
const str = 'hello';
global.atob = atobStub;
atob(str);
global.atob = atobOriginal;
t.calledWith(atobStub, [str], 'should call global.btoa');
t.end();
});
test('atob: node', (t) => {
const str = 'aGVsbG8=';
const expected = 'hello';
const result = atob(str);
t.equal(result, expected, 'should encode base64');
t.end();
});

View file

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const {promisify} = require('node:util'); const {promisify} = require('node:util');
const tryToCatch = require('try-to-catch'); const {tryToCatch} = require('try-to-catch');
const {test, stub} = require('supertape'); const {test, stub} = require('supertape');

View file

@ -1,17 +1,16 @@
'use strict'; import rendy from 'rendy';
import currify from 'currify';
import store from 'fullstore';
import {encode} from './entity.js';
const rendy = require('rendy'); export const getHeaderField = currify(_getHeaderField);
const currify = require('currify');
const store = require('fullstore');
const {encode} = require('./entity');
const {btoa} = require('./base64');
const getHeaderField = currify(_getHeaderField);
/* КОНСТАНТЫ (общие для клиента и сервера)*/ /* КОНСТАНТЫ (общие для клиента и сервера)*/
/* название программы */ /* название программы */
const NAME = 'Cloud Commander'; const NAME = 'Cloud Commander';
const FS = '/fs';
export const FS = '/fs';
const Path = store(); const Path = store();
Path('/'); Path('/');
@ -23,14 +22,10 @@ const filterOutDotFiles = ({showDotFiles}) => ({name}) => {
return !name.startsWith('.'); return !name.startsWith('.');
}; };
module.exports.FS = FS; export const apiURL = '/api/v1';
module.exports.apiURL = '/api/v1'; export const MAX_FILE_SIZE = 500 * 1024;
module.exports.MAX_FILE_SIZE = 500 * 1024;
module.exports.getHeaderField = getHeaderField;
module.exports.getPathLink = getPathLink;
module.exports.getDotDot = getDotDot;
module.exports.formatMsg = (msg, name, status) => { export const formatMsg = (msg, name, status) => {
status = status || 'ok'; status = status || 'ok';
name = name || ''; name = name || '';
@ -44,7 +39,7 @@ module.exports.formatMsg = (msg, name, status) => {
* Функция возвращает заголовок веб страницы * Функция возвращает заголовок веб страницы
* @path * @path
*/ */
module.exports.getTitle = (options) => { export const getTitle = (options) => {
options = options || {}; options = options || {};
const {path = Path(), name} = options; const {path = Path(), name} = options;
@ -63,7 +58,7 @@ module.exports.getTitle = (options) => {
* возвращаеться массив каталогов * возвращаеться массив каталогов
* @param url - адрес каталога * @param url - адрес каталога
*/ */
function getPathLink(url, prefix, template) { export function getPathLink(url, prefix, template) {
if (!url) if (!url)
throw Error('url could not be empty!'); throw Error('url could not be empty!');
@ -109,17 +104,17 @@ function getPathLink(url, prefix, template) {
return lines.join(''); return lines.join('');
} }
const getDataName = (name) => { export function _getDataName(name) {
const encoded = btoa(encodeURI(name)); const encoded = btoa(encodeURI(name));
return `data-name="js-file-${encoded}" `; return `data-name="js-file-${encoded}" `;
}; }
/** /**
* Функция строит таблицу файлв из JSON-информации о файлах * Функция строит таблицу файлв из JSON-информации о файлах
* @param params - информация о файлах * @param params - информация о файлах
* *
*/ */
module.exports.buildFromJSON = (params) => { export const buildFromJSON = (params) => {
const { const {
prefix, prefix,
template, template,
@ -185,7 +180,7 @@ module.exports.buildFromJSON = (params) => {
name: '..', name: '..',
}); });
const dataName = getDataName('..'); const dataName = _getDataName('..');
const attribute = `draggable="true" ${dataName}`; const attribute = `draggable="true" ${dataName}`;
/* Сохраняем путь к каталогу верхнего уровня*/ /* Сохраняем путь к каталогу верхнего уровня*/
@ -226,7 +221,7 @@ module.exports.buildFromJSON = (params) => {
attribute: getAttribute(file.type), attribute: getAttribute(file.type),
}); });
const dataName = getDataName(file.name); const dataName = _getDataName(file.name);
const attribute = `draggable="true" ${dataName}`; const attribute = `draggable="true" ${dataName}`;
return rendy(templateFile, { return rendy(templateFile, {
@ -262,7 +257,8 @@ function getAttribute(type) {
return 'target="_blank" '; return 'target="_blank" ';
} }
module.exports._getSize = getSize; export const _getSize = getSize;
function getSize({size, type}) { function getSize({size, type}) {
if (type === 'directory') if (type === 'directory')
return '&lt;dir&gt;'; return '&lt;dir&gt;';
@ -285,7 +281,7 @@ function _getHeaderField(sort, order, name) {
return `${name}${arrow}`; return `${name}${arrow}`;
} }
function getDotDot(path) { export function getDotDot(path) {
// убираем последний слеш и каталог в котором мы сейчас находимся // убираем последний слеш и каталог в котором мы сейчас находимся
const lastSlash = path.substr(path, path.lastIndexOf('/')); const lastSlash = path.substr(path, path.lastIndexOf('/'));
const dotDot = lastSlash.substr(lastSlash, lastSlash.lastIndexOf('/')); const dotDot = lastSlash.substr(lastSlash, lastSlash.lastIndexOf('/'));

View file

@ -1,19 +1,15 @@
'use strict'; import {readFileSync} from 'node:fs';
import test from 'supertape';
const {join} = require('node:path'); import montag from 'montag';
const {readFileSync} = require('node:fs'); import * as cheerio from 'cheerio';
import {
const test = require('supertape');
const montag = require('montag');
const cheerio = require('cheerio');
const {
_getSize, _getSize,
getPathLink, getPathLink,
buildFromJSON, buildFromJSON,
} = require('./cloudfunc'); _getDataName,
} from './cloudfunc.mjs';
const templatePath = join(__dirname, '../tmpl/fs'); const templatePath = new URL('../tmpl/fs', import.meta.url).pathname;
const template = { const template = {
pathLink: readFileSync(`${templatePath}/pathLink.hbs`, 'utf8'), pathLink: readFileSync(`${templatePath}/pathLink.hbs`, 'utf8'),
@ -175,3 +171,11 @@ test('cloudfunc: buildFromJSON: showDotFiles: false', (t) => {
t.equal(result, expected); t.equal(result, expected);
t.end(); t.end();
}); });
test('cloudfunc: _getDataName', (t) => {
const result = _getDataName('s');
const expected = 'data-name="js-file-cw==" ';
t.equal(result, expected);
t.end();
});

View file

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const test = require('supertape'); const test = require('supertape');
const tryCatch = require('try-catch'); const {tryCatch} = require('try-catch');
const datetime = require('./datetime'); const datetime = require('./datetime');
@ -16,11 +16,11 @@ test('common: datetime', (t) => {
}); });
test('common: datetime: no arg', (t) => { test('common: datetime: no arg', (t) => {
const {Date} = global; const {Date} = globalThis;
let called = false; let called = false;
global.Date = class extends Date { globalThis.Date = class extends Date {
constructor() { constructor() {
super(); super();
called = true; called = true;
@ -29,7 +29,7 @@ test('common: datetime: no arg', (t) => {
datetime(); datetime();
global.Date = Date; globalThis.Date = Date;
t.ok(called, 'should call new Date'); t.ok(called, 'should call new Date');
t.end(); t.end();

View file

@ -1,6 +1,6 @@
'use strict'; 'use strict';
const tryToCatch = require('try-to-catch'); const {tryToCatch} = require('try-to-catch');
const all = Promise.all.bind(Promise); const all = Promise.all.bind(Promise);
module.exports = async (a) => { module.exports = async (a) => {

View file

@ -1,8 +1,7 @@
'use strict'; 'use strict';
const test = require('supertape'); const test = require('supertape');
const {reRequire} = require('mock-require'); const {tryCatch} = require('try-catch');
const tryCatch = require('try-catch');
const Util = require('./util'); const Util = require('./util');
const { const {
@ -117,15 +116,3 @@ test('util: escapeRegExp', (t) => {
t.equal(escapeRegExp('#hello'), '\\#hello'); t.equal(escapeRegExp('#hello'), '\\#hello');
t.end(); t.end();
}); });
test('util: scope', (t) => {
global.window = {};
reRequire('./util');
t.pass('should set window in scope');
delete global.window;
t.end();
});

View file

@ -1,9 +1,7 @@
'use strict';
// used by OptimizeCssAssetsPlugin // used by OptimizeCssAssetsPlugin
const defaultPreset = require('cssnano-preset-default'); import defaultPreset from 'cssnano-preset-default';
module.exports = defaultPreset({ export default defaultPreset({
svgo: { svgo: {
plugins: [{ plugins: [{
convertPathData: false, convertPathData: false,

14
deno.json Normal file
View file

@ -0,0 +1,14 @@
{
"tasks": {
"start": "deno run -P=cloudcmd bin/cloudcmd.mjs"
},
"permissions": {
"cloudcmd": {
"env": true,
"read": true,
"sys": true,
"net": true,
"run": true
}
}
}

View file

@ -19,9 +19,6 @@ export const match = {
'bin/cloudcmd.js': { 'bin/cloudcmd.js': {
'no-console': 'off', 'no-console': 'off',
}, },
'cssnano.config.js': {
'n/no-extraneous-require': 'off',
},
}; };
export default defineConfig([ export default defineConfig([
safeAlign, { safeAlign, {
@ -36,9 +33,7 @@ export default defineConfig([
}, { }, {
files: ['{client,common,static}/**/*.js'], files: ['{client,common,static}/**/*.js'],
languageOptions: { languageOptions: {
globals: { globals: globals.browser,
...globals.browser,
},
}, },
}, },
...matchToFlat(match), ...matchToFlat(match),

View file

@ -42,5 +42,15 @@
<script> <script>
CloudCmd({{ config }}); CloudCmd({{ config }});
</script> </script>
<script data-name="aleman-importmap" type="importmap">
{
"imports": {
"putout": "https://esm.sh/@putout/bundle@4.7.6",
"@putout/processor-html": "https://esm.sh/@putout/processor-html",
"fullstore": "https://esm.sh/fullstore",
"jessy": "https://esm.sh/jessy"
}
}
</script>
</body> </body>
</html> </html>

View file

@ -5,7 +5,7 @@
"password": "2b64f2e3f9fee1942af9ff60d40aa5a719db33b8ba8dd4864bb4f11e25ca2bee00907de32a59429602336cac832c8f2eeff5177cc14c864dd116c8bf6ca5d9a9", "password": "2b64f2e3f9fee1942af9ff60d40aa5a719db33b8ba8dd4864bb4f11e25ca2bee00907de32a59429602336cac832c8f2eeff5177cc14c864dd116c8bf6ca5d9a9",
"algo": "sha512WithRSAEncryption", "algo": "sha512WithRSAEncryption",
"editor": "edward", "editor": "edward",
"menu": "supermenu", "menu": "aleman",
"packer": "tar", "packer": "tar",
"diff": true, "diff": true,
"zip": true, "zip": true,

View file

@ -1,6 +1,6 @@
{ {
"name": "cloudcmd", "name": "cloudcmd",
"version": "18.8.4", "version": "19.1.9",
"type": "commonjs", "type": "commonjs",
"author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)", "author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
"description": "File manager for the web with console and editor", "description": "File manager for the web with console and editor",
@ -89,7 +89,7 @@
"@cloudcmd/move-files": "^8.0.0", "@cloudcmd/move-files": "^8.0.0",
"@cloudcmd/read-files-sync": "^2.0.0", "@cloudcmd/read-files-sync": "^2.0.0",
"@putout/cli-validate-args": "^2.0.0", "@putout/cli-validate-args": "^2.0.0",
"aleman": "^1.4.9", "aleman": "^1.16.5",
"apart": "^2.0.0", "apart": "^2.0.0",
"chalk": "^5.3.0", "chalk": "^5.3.0",
"compression": "^1.7.4", "compression": "^1.7.4",
@ -105,10 +105,10 @@
"execon": "^1.2.0", "execon": "^1.2.0",
"express": "^5.1.0", "express": "^5.1.0",
"files-io": "^4.0.0", "files-io": "^4.0.0",
"find-up": "^7.0.0", "find-up": "^8.0.0",
"for-each-key": "^2.0.0", "for-each-key": "^2.0.0",
"format-io": "^2.0.0", "format-io": "^2.0.0",
"fullstore": "^3.0.0", "fullstore": "^4.0.0",
"http-auth": "^4.2.1", "http-auth": "^4.2.1",
"inly": "^5.0.0", "inly": "^5.0.0",
"jaguar": "^6.0.0", "jaguar": "^6.0.0",
@ -124,12 +124,12 @@
"object.omit": "^3.0.0", "object.omit": "^3.0.0",
"once": "^1.4.0", "once": "^1.4.0",
"onezip": "^6.0.1", "onezip": "^6.0.1",
"open": "^10.0.3", "open": "^11.0.0",
"package-json": "^10.0.0", "package-json": "^10.0.0",
"pipe-io": "^4.0.1", "pipe-io": "^4.0.1",
"ponse": "^7.0.0", "ponse": "^7.0.0",
"pullout": "^5.0.0", "pullout": "^5.0.0",
"putout": "^40.0.3", "putout": "^41.0.0",
"redzip": "^3.0.0", "redzip": "^3.0.0",
"rendy": "^4.1.3", "rendy": "^4.1.3",
"restafary": "^12.0.0", "restafary": "^12.0.0",
@ -140,8 +140,8 @@
"socket.io-client": "^4.0.1", "socket.io-client": "^4.0.1",
"squad": "^3.0.0", "squad": "^3.0.0",
"table": "^6.0.1", "table": "^6.0.1",
"try-catch": "^3.0.0", "try-catch": "^4.0.4",
"try-to-catch": "^3.0.0", "try-to-catch": "^4.0.0",
"tryrequire": "^3.0.0", "tryrequire": "^3.0.0",
"win32": "^7.0.0", "win32": "^7.0.0",
"wraptile": "^3.0.0", "wraptile": "^3.0.0",
@ -156,9 +156,9 @@
"@cloudcmd/create-element": "^2.0.0", "@cloudcmd/create-element": "^2.0.0",
"@cloudcmd/modal": "^3.0.0", "@cloudcmd/modal": "^3.0.0",
"@cloudcmd/olark": "^3.0.2", "@cloudcmd/olark": "^3.0.2",
"@cloudcmd/stub": "^4.0.1", "@cloudcmd/stub": "^5.0.0",
"@iocmd/wait": "^2.1.0", "@iocmd/wait": "^2.1.0",
"@putout/eslint-flat": "^3.0.1", "@putout/eslint-flat": "^4.0.0",
"@putout/plugin-cloudcmd": "^4.0.0", "@putout/plugin-cloudcmd": "^4.0.0",
"@types/node-fetch": "^2.6.11", "@types/node-fetch": "^2.6.11",
"auto-globals": "^4.0.0", "auto-globals": "^4.0.0",
@ -170,27 +170,29 @@
"codegen.macro": "^4.0.0", "codegen.macro": "^4.0.0",
"css-loader": "^7.1.2", "css-loader": "^7.1.2",
"css-modules-require-hook": "^4.2.3", "css-modules-require-hook": "^4.2.3",
"cssnano-preset-default": "^7.0.10",
"domtokenlist-shim": "^1.2.0", "domtokenlist-shim": "^1.2.0",
"emitify": "^4.0.1", "emitify": "^4.0.1",
"eslint": "^9.23.0", "eslint": "^9.23.0",
"eslint-plugin-n": "^17.0.0-4", "eslint-plugin-n": "^17.0.0-4",
"eslint-plugin-putout": "^28.0.0", "eslint-plugin-putout": "^30.0.0",
"globals": "^16.3.0", "globals": "^17.0.0",
"gritty": "^8.0.0", "gritty": "^9.0.0",
"gunzip-maybe": "^1.3.1", "gunzip-maybe": "^1.3.1",
"html-webpack-plugin": "^5.6.3", "html-webpack-plugin": "^5.6.3",
"inherits": "^2.0.3", "inherits": "^2.0.3",
"itype": "^3.0.1",
"just-capitalize": "^3.2.0", "just-capitalize": "^3.2.0",
"just-pascal-case": "^3.2.0", "just-pascal-case": "^3.2.0",
"limier": "^3.0.0", "limier": "^3.0.0",
"load.js": "^3.0.0", "load.js": "^3.0.0",
"madrun": "^11.0.0", "madrun": "^12.1.0",
"memfs": "^4.2.0", "memfs": "^4.2.0",
"mini-css-extract-plugin": "^2.9.2", "mini-css-extract-plugin": "^2.9.2",
"minor": "^1.2.2", "minor": "^1.2.2",
"mock-require": "^3.0.1", "mock-require": "^3.0.1",
"morgan": "^1.6.1", "morgan": "^1.6.1",
"multi-rename": "^2.0.0", "multi-rename": "^3.0.0",
"nodemon": "^3.0.1", "nodemon": "^3.0.1",
"optimize-css-assets-webpack-plugin": "^6.0.1", "optimize-css-assets-webpack-plugin": "^6.0.1",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
@ -199,7 +201,7 @@
"postcss": "^8.5.3", "postcss": "^8.5.3",
"process": "^0.11.10", "process": "^0.11.10",
"readjson": "^2.0.1", "readjson": "^2.0.1",
"redlint": "^4.1.1", "redlint": "^5.0.0",
"request": "^2.76.0", "request": "^2.76.0",
"rimraf": "^6.0.1", "rimraf": "^6.0.1",
"scroll-into-view-if-needed": "^3.0.4", "scroll-into-view-if-needed": "^3.0.4",
@ -207,7 +209,7 @@
"smalltalk": "^4.0.0", "smalltalk": "^4.0.0",
"style-loader": "^4.0.0", "style-loader": "^4.0.0",
"supermenu": "^4.0.1", "supermenu": "^4.0.1",
"supertape": "^11.0.4", "supertape": "^12.0.0",
"tar-stream": "^3.0.0", "tar-stream": "^3.0.0",
"unionfs": "^4.0.0", "unionfs": "^4.0.0",
"url-loader": "^4.0.0", "url-loader": "^4.0.0",
@ -216,8 +218,11 @@
"webpack-merge": "^6.0.1", "webpack-merge": "^6.0.1",
"webpackbar": "^7.0.0" "webpackbar": "^7.0.0"
}, },
"imports": {
"#dom/events": "./client/dom/events/index.mjs"
},
"engines": { "engines": {
"node": ">=20.19" "node": ">=22"
}, },
"license": "MIT", "license": "MIT",
"publishConfig": { "publishConfig": {

View file

@ -2,7 +2,7 @@ import path, {dirname, join} from 'node:path';
import {fileURLToPath} from 'node:url'; import {fileURLToPath} from 'node:url';
import process from 'node:process'; import process from 'node:process';
import fs from 'node:fs'; import fs from 'node:fs';
import fullstore from 'fullstore'; import {fullstore} from 'fullstore';
import currify from 'currify'; import currify from 'currify';
import apart from 'apart'; import apart from 'apart';
import ponse from 'ponse'; import ponse from 'ponse';
@ -14,12 +14,12 @@ import dword from 'dword';
import deepword from 'deepword'; import deepword from 'deepword';
import nomine from 'nomine'; import nomine from 'nomine';
import fileop from '@cloudcmd/fileop'; import fileop from '@cloudcmd/fileop';
import cloudfunc from '../common/cloudfunc.js'; import * as cloudfunc from '../common/cloudfunc.mjs';
import authentication from './auth.js'; import authentication from './auth.js';
import {createConfig, configPath} from './config.js'; import {createConfig, configPath} from './config.mjs';
import modulas from './modulas.js'; import modulas from './modulas.mjs';
import userMenu from './user-menu.mjs'; import userMenu from './user-menu.mjs';
import rest from './rest/index.js'; import rest from './rest/index.mjs';
import route from './route.mjs'; import route from './route.mjs';
import * as validate from './validate.mjs'; import * as validate from './validate.mjs';
import prefixer from './prefixer.js'; import prefixer from './prefixer.js';

View file

@ -2,11 +2,12 @@ import path, {dirname} from 'node:path';
import {fileURLToPath} from 'node:url'; import {fileURLToPath} from 'node:url';
import process from 'node:process'; import process from 'node:process';
import fs from 'node:fs'; import fs from 'node:fs';
import fullstore from 'fullstore'; import {fullstore} from 'fullstore';
import nanomemoizeDefault from 'nano-memoize'; import * as nanomemoizeDefault from 'nano-memoize';
import readFilesSync from '@cloudcmd/read-files-sync'; import readFilesSync from '@cloudcmd/read-files-sync';
const {nanomemoize} = nanomemoizeDefault; const nanomemoize = nanomemoizeDefault.default.nanomemoize || nanomemoizeDefault.default;
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename); const __dirname = dirname(__filename);
const isMap = (a) => /\.(map|js)$/.test(a); const isMap = (a) => /\.(map|js)$/.test(a);

View file

@ -1,7 +1,12 @@
'use strict'; import {fileURLToPath} from 'node:url';
import {dirname} from 'node:path';
import {createRequire} from 'node:module';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const require = createRequire(import.meta.url);
const DIR_SERVER = `${__dirname}/`; const DIR_SERVER = `${__dirname}/`;
const DIR_COMMON = '../common/';
const path = require('node:path'); const path = require('node:path');
const fs = require('node:fs'); const fs = require('node:fs');
@ -11,17 +16,17 @@ const {homedir} = require('node:os');
const currify = require('currify'); const currify = require('currify');
const wraptile = require('wraptile'); const wraptile = require('wraptile');
const tryToCatch = require('try-to-catch'); const {tryToCatch} = require('try-to-catch');
const pullout = require('pullout'); const pullout = require('pullout');
const ponse = require('ponse'); const ponse = require('ponse');
const jonny = require('jonny'); const jonny = require('jonny');
const jju = require('jju'); const jju = require('jju');
const writejson = require('writejson'); const writejson = require('writejson');
const tryCatch = require('try-catch'); const {tryCatch} = require('try-catch');
const criton = require('criton'); const criton = require('criton');
const exit = require(`${DIR_SERVER}exit`); const exit = require(`${DIR_SERVER}exit`);
const CloudFunc = require(`${DIR_COMMON}cloudfunc`); const CloudFunc = require('../common/cloudfunc.mjs');
const isUndefined = (a) => typeof a === 'undefined'; const isUndefined = (a) => typeof a === 'undefined';
const DIR = `${DIR_SERVER}../`; const DIR = `${DIR_SERVER}../`;
const HOME = homedir(); const HOME = homedir();
@ -36,7 +41,6 @@ const key = (a) => Object
.pop(); .pop();
const ConfigPath = path.join(DIR, 'json/config.json'); const ConfigPath = path.join(DIR, 'json/config.json');
const ConfigHome = path.join(HOME, '.cloudcmd.json');
const connection = currify(_connection); const connection = currify(_connection);
const connectionWrapped = wraptile(_connection); const connectionWrapped = wraptile(_connection);
@ -65,8 +69,7 @@ function read(filename) {
}; };
} }
module.exports.createConfig = createConfig; export const configPath = path.join(HOME, '.cloudcmd.json');
module.exports.configPath = ConfigHome;
const manageListen = currify((manage, socket, auth) => { const manageListen = currify((manage, socket, auth) => {
if (!manage('configDialog')) if (!manage('configDialog'))
@ -84,7 +87,7 @@ function initWrite(filename, configManager) {
return resolve; return resolve;
} }
function createConfig({configPath} = {}) { export function createConfig({configPath} = {}) {
const config = {}; const config = {};
const changeEmitter = new Emitter(); const changeEmitter = new Emitter();
@ -237,7 +240,8 @@ function traverse([manage, json]) {
} }
} }
module.exports._cryptoPass = cryptoPass; export const _cryptoPass = cryptoPass;
function cryptoPass(manage, json) { function cryptoPass(manage, json) {
const algo = manage('algo'); const algo = manage('algo');

View file

@ -1,7 +1,7 @@
import {createRequire} from 'node:module'; import {createRequire} from 'node:module';
import {test, stub} from 'supertape'; import {test, stub} from 'supertape';
import {createConfig, _cryptoPass} from './config.js'; import {createConfig, _cryptoPass} from './config.mjs';
import {apiURL} from '../common/cloudfunc.js'; import {apiURL} from '../common/cloudfunc.mjs';
import {connect} from '../test/before.mjs'; import {connect} from '../test/before.mjs';
const require = createRequire(import.meta.url); const require = createRequire(import.meta.url);

Some files were not shown because too many files have changed in this diff Show more