mirror of
https://github.com/coderaiser/cloudcmd.git
synced 2026-07-24 16:42:01 +00:00
Compare commits
No commits in common. "master" and "v14.6.0" have entirely different histories.
281 changed files with 7545 additions and 13217 deletions
8
.babelrc
Normal file
8
.babelrc
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
"@babel/preset-env"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"module:babel-plugin-macros",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"presets": [
|
|
||||||
"@babel/preset-env"
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
"module:babel-plugin-macros",
|
|
||||||
"@babel/plugin-transform-optional-chaining"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
last 2 Chrome versions
|
last 2 Chrome versions
|
||||||
last 2 Safari versions
|
last 2 Safari versions
|
||||||
Firefox ESR
|
Firefox ESR
|
||||||
|
maintained node versions
|
||||||
not dead
|
not dead
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,25 @@
|
||||||
export default {
|
'use strict';
|
||||||
'F2 - Rename File': renameCurrent,
|
|
||||||
'L - Lint': run('npm run lint'),
|
module.exports = {
|
||||||
'F - Fix Lint': run('npm run fix:lint'),
|
'F2 - Rename file': async ({DOM}) => {
|
||||||
'T - Test': run('npm run test'),
|
await DOM.renameCurrent();
|
||||||
'C - Coverage': run('npm run coverage'),
|
},
|
||||||
'D - Build Dev': run('npm run build:client:dev'),
|
'D - Build Dev': async ({CloudCmd}) => {
|
||||||
'P - Build Prod': run('npm run build:client'),
|
await CloudCmd.TerminalRun.show({
|
||||||
|
command: 'npm run build:client:dev',
|
||||||
|
autoClose: false,
|
||||||
|
closeMessage: 'Press any button to close Terminal',
|
||||||
|
});
|
||||||
|
|
||||||
|
CloudCmd.refresh();
|
||||||
|
},
|
||||||
|
'P - Build Prod': async ({CloudCmd}) => {
|
||||||
|
await CloudCmd.TerminalRun.show({
|
||||||
|
command: 'npm run build:client',
|
||||||
|
autoClose: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
CloudCmd.refresh();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
async function renameCurrent(DOM) {
|
|
||||||
await DOM.renameCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
function run(command) {
|
|
||||||
return async ({CloudCmd, DOM}) => {
|
|
||||||
const {TerminalRun, config} = CloudCmd;
|
|
||||||
|
|
||||||
const {CurrentInfo} = DOM;
|
|
||||||
const {dirPath} = CurrentInfo;
|
|
||||||
|
|
||||||
const cwd = config('root') + dirPath;
|
|
||||||
|
|
||||||
return await TerminalRun.show({
|
|
||||||
cwd,
|
|
||||||
command,
|
|
||||||
closeMessage: 'Press any key to close Terminal',
|
|
||||||
autoClose: false,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -19,5 +19,6 @@ cssnano.config.js
|
||||||
bin/release.js
|
bin/release.js
|
||||||
|
|
||||||
client
|
client
|
||||||
|
legacy
|
||||||
server_
|
server_
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ root = true
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = true
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
|
|
|
||||||
34
.eslintrc.js
Normal file
34
.eslintrc.js
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
extends: [
|
||||||
|
'plugin:putout/recommended',
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
'putout',
|
||||||
|
'node',
|
||||||
|
],
|
||||||
|
overrides: [{
|
||||||
|
files: ['bin/release.js'],
|
||||||
|
rules: {
|
||||||
|
'no-console': 'off',
|
||||||
|
'node/shebang': 'off',
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'plugin:node/recommended',
|
||||||
|
],
|
||||||
|
}, {
|
||||||
|
files: ['bin/cloudcmd.js'],
|
||||||
|
rules: {
|
||||||
|
'no-console': 'off',
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'plugin:node/recommended',
|
||||||
|
],
|
||||||
|
}, {
|
||||||
|
files: ['{client,common}/**/*.js'],
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
};
|
||||||
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
|
|
@ -1,3 +0,0 @@
|
||||||
github: coderaiser
|
|
||||||
open_collective: cloudcmd
|
|
||||||
ko_fi: coderaiser
|
|
||||||
12
.github/ISSUE_TEMPLATE.md
vendored
Normal file
12
.github/ISSUE_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!--
|
||||||
|
Thank you for reporting an issue. Please fill in the template below. If unsure
|
||||||
|
about something, just do as best as you're able.
|
||||||
|
-->
|
||||||
|
|
||||||
|
* **Version** (`cloudcmd -v`):
|
||||||
|
* **Node Version** `node -v`:
|
||||||
|
* **OS** (`uname -a` on Linux):
|
||||||
|
* **Browser name/version**:
|
||||||
|
* **Used Command Line Parameters**:
|
||||||
|
* **Changed Config**:
|
||||||
|
|
||||||
45
.github/ISSUE_TEMPLATE/bug_report.md
vendored
45
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -1,45 +0,0 @@
|
||||||
---
|
|
||||||
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve
|
|
||||||
title: ''
|
|
||||||
labels: needs clarification
|
|
||||||
assignees: coderaiser
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. See error
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Desktop (please complete the following information):**
|
|
||||||
|
|
||||||
- **Version** (`cloudcmd -v`):
|
|
||||||
- **Node Version** `node -v`:
|
|
||||||
- **OS** (`uname -a` on Linux):
|
|
||||||
- **Browser name/version**:
|
|
||||||
- **Used Command Line Parameters**:
|
|
||||||
- **Changed Config**:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{}
|
|
||||||
```
|
|
||||||
- [ ] 🎁 **I'm ready to donate on https://opencollective.com/cloudcmd**
|
|
||||||
- [ ] 🎁 **I'm ready to donate on https://ko-fi.com/coderaiser**
|
|
||||||
- [ ] 💪 **I'm willing to work on this issue**
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
||||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -1,5 +0,0 @@
|
||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: Stack Overflow
|
|
||||||
url: https://stackoverflow.com/search?q=cloudcmd
|
|
||||||
about: Please ask and answer questions here.
|
|
||||||
21
.github/ISSUE_TEMPLATE/feature_request.md
vendored
21
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
|
@ -1,21 +0,0 @@
|
||||||
---
|
|
||||||
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
||||||
24
.github/ISSUE_TEMPLATE/issue_template.md
vendored
24
.github/ISSUE_TEMPLATE/issue_template.md
vendored
|
|
@ -1,24 +0,0 @@
|
||||||
***
|
|
||||||
|
|
||||||
name: Tracking issue
|
|
||||||
about: Create an issue with bug report or feature request.
|
|
||||||
title: ""
|
|
||||||
labels: needs triage
|
|
||||||
assignees: coderaiser
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
- **Version** (`cloudcmd -v`):
|
|
||||||
- **Node Version** `node -v`:
|
|
||||||
- **OS** (`uname -a` on Linux):
|
|
||||||
- **Browser name/version**:
|
|
||||||
- **Used Command Line Parameters**:
|
|
||||||
- **Changed Config**:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{}
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] 🎁 **I'm ready to donate on https://opencollective.com/cloudcmd**
|
|
||||||
- [ ] 🎁 **I'm ready to donate on https://ko-fi.com/coderaiser**
|
|
||||||
- [ ] 💪 **I'm willing to work on this issue**
|
|
||||||
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -4,5 +4,6 @@ about something, just do as best as you're able.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
- [ ] commit message named according to [Contributing Guide](https://github.com/coderaiser/cloudcmd/blob/master/CONTRIBUTING.md "Contributting Guide")
|
- [ ] commit message named according to [Contributing Guide](https://github.com/coderaiser/cloudcmd/blob/master/CONTRIBUTING.md "Contributting Guide")
|
||||||
- [ ] `npm run fix:lint` is OK
|
- [ ] `npm run codestyle` is OK
|
||||||
- [ ] `npm test` is OK
|
- [ ] `npm test` is OK
|
||||||
|
|
||||||
|
|
|
||||||
60
.github/workflows/docker-io.yml
vendored
60
.github/workflows/docker-io.yml
vendored
|
|
@ -1,60 +0,0 @@
|
||||||
name: Docker IO
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
on:
|
|
||||||
- push
|
|
||||||
jobs:
|
|
||||||
buildx:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
- uses: oven-sh/setup-bun@v2
|
|
||||||
with:
|
|
||||||
bun-version: latest
|
|
||||||
- name: Use Node.js 24.x
|
|
||||||
uses: actions/setup-node@v6
|
|
||||||
with:
|
|
||||||
node-version: 24.x
|
|
||||||
- name: Install Redrun
|
|
||||||
run: bun i redrun -g --no-save
|
|
||||||
- name: NPM Install
|
|
||||||
run: bun i --no-save
|
|
||||||
- name: Lint
|
|
||||||
run: redrun lint
|
|
||||||
- name: Build
|
|
||||||
id: build
|
|
||||||
run: |
|
|
||||||
redrun build
|
|
||||||
VERSION=$(grep '"version":' package.json -m1 | cut -d\" -f4)
|
|
||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v4
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v4
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v4
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_TOKEN }}
|
|
||||||
- name: Login to GitHub Container Registry
|
|
||||||
uses: docker/login-action@v4
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Build and push io-image
|
|
||||||
uses: docker/build-push-action@v7
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: docker/Dockerfile.io
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
coderaiser/cloudcmd:io
|
|
||||||
coderaiser/cloudcmd:${{ steps.build.outputs.version }}-io
|
|
||||||
ghcr.io/${{ github.repository }}-io
|
|
||||||
ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}-io
|
|
||||||
88
.github/workflows/docker.yml
vendored
88
.github/workflows/docker.yml
vendored
|
|
@ -1,88 +0,0 @@
|
||||||
name: Docker CI
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "*"
|
|
||||||
jobs:
|
|
||||||
buildx:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
- uses: oven-sh/setup-bun@v2
|
|
||||||
with:
|
|
||||||
bun-version: latest
|
|
||||||
- name: Use Node.js 22.x
|
|
||||||
uses: actions/setup-node@v6
|
|
||||||
with:
|
|
||||||
node-version: 22.x
|
|
||||||
- name: Install Redrun
|
|
||||||
run: bun i redrun -g --no-save
|
|
||||||
- name: NPM Install
|
|
||||||
run: bun i --no-save
|
|
||||||
- name: Lint
|
|
||||||
run: redrun lint
|
|
||||||
- name: Build
|
|
||||||
id: build
|
|
||||||
run: |
|
|
||||||
redrun build
|
|
||||||
VERSION=$(grep '"version":' package.json -m1 | cut -d\" -f4)
|
|
||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v4
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v4
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v4
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_TOKEN }}
|
|
||||||
- name: Login to GitHub Container Registry
|
|
||||||
uses: docker/login-action@v4
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Build and push base-image
|
|
||||||
uses: docker/build-push-action@v7
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: docker/Dockerfile
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
coderaiser/cloudcmd:latest
|
|
||||||
coderaiser/cloudcmd:${{ steps.build.outputs.version }}
|
|
||||||
ghcr.io/${{ github.repository }}:latest
|
|
||||||
ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}
|
|
||||||
- name: Build and push alpine-image
|
|
||||||
uses: docker/build-push-action@v7
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: docker/Dockerfile.alpine
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
coderaiser/cloudcmd:alpine
|
|
||||||
coderaiser/cloudcmd:latest-alpine
|
|
||||||
coderaiser/cloudcmd:${{ steps.build.outputs.version }}-alpine
|
|
||||||
ghcr.io/${{ github.repository }}:latest-alpine
|
|
||||||
ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}-alpine
|
|
||||||
- name: Build and push slim-image
|
|
||||||
uses: docker/build-push-action@v7
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: docker/Dockerfile.slim
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
coderaiser/cloudcmd:slim
|
|
||||||
coderaiser/cloudcmd:latest-slim
|
|
||||||
coderaiser/cloudcmd:${{ steps.build.outputs.version }}-slim
|
|
||||||
ghcr.io/${{ github.repository }}-slim
|
|
||||||
ghcr.io/${{ github.repository }}:${{ steps.build.outputs.version }}-slim
|
|
||||||
63
.github/workflows/nodejs.yml
vendored
63
.github/workflows/nodejs.yml
vendored
|
|
@ -1,53 +1,20 @@
|
||||||
name: Node CI
|
name: Node CI
|
||||||
on:
|
|
||||||
- push
|
on: [push]
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
|
||||||
NAME: cloudcmd
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
node-version:
|
|
||||||
- 22.x
|
|
||||||
- 24.x
|
|
||||||
- 26.x
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@master
|
||||||
- uses: oven-sh/setup-bun@v2
|
- name: Use Node.js 12.x
|
||||||
with:
|
uses: actions/setup-node@v1
|
||||||
bun-version: latest
|
with:
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
version: 12.x
|
||||||
uses: actions/setup-node@v6
|
- name: npm install, build, and test
|
||||||
with:
|
run: |
|
||||||
node-version: ${{ matrix.node-version }}
|
npm install
|
||||||
- name: Install Redrun
|
npm run build
|
||||||
run: bun i redrun madrun -g --no-save
|
npm test
|
||||||
- name: Install
|
|
||||||
run: bun i --no-save
|
|
||||||
- name: Init Madrun
|
|
||||||
run: madrun --init
|
|
||||||
- name: Lint
|
|
||||||
run: redrun fix:lint
|
|
||||||
- name: Typos
|
|
||||||
uses: coderaiser/typos.ai@v1.1.8
|
|
||||||
with:
|
|
||||||
key: ${{ secrets.TYPOS_AI_KEY }}
|
|
||||||
- name: Commit fixes
|
|
||||||
uses: EndBug/add-and-commit@v10
|
|
||||||
continue-on-error: true
|
|
||||||
with:
|
|
||||||
message: "chore: ${{ env.NAME }}: actions: lint ☘️"
|
|
||||||
- name: Build
|
|
||||||
run: redrun build
|
|
||||||
- name: Test
|
|
||||||
run: redrun test
|
|
||||||
- name: Coverage
|
|
||||||
run: redrun coverage coverage:report
|
|
||||||
- name: Coveralls
|
|
||||||
uses: coverallsapp/github-action@v2
|
|
||||||
continue-on-error: true
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
|
||||||
21
.gitignore
vendored
21
.gitignore
vendored
|
|
@ -1,17 +1,18 @@
|
||||||
*.swp
|
|
||||||
*.log
|
|
||||||
*.lock
|
|
||||||
|
|
||||||
.nyc_output
|
|
||||||
.DS_Store
|
|
||||||
.idea
|
|
||||||
|
|
||||||
package-lock.json
|
package-lock.json
|
||||||
npm-debug.log*
|
yarn.lock
|
||||||
|
yarn-error.log
|
||||||
node_modules
|
node_modules
|
||||||
|
npm-debug.log*
|
||||||
coverage
|
coverage
|
||||||
|
|
||||||
modules/execon
|
modules/execon
|
||||||
modules/emitify
|
modules/emitify
|
||||||
|
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
*.swp
|
||||||
|
.putoutcache
|
||||||
|
|
||||||
dist
|
dist
|
||||||
dist-dev
|
dist-dev
|
||||||
|
|
||||||
|
|
|
||||||
204
.madrun.js
204
.madrun.js
|
|
@ -1,62 +1,178 @@
|
||||||
import {run, cutEnv} from 'madrun';
|
'use strict';
|
||||||
import {defineEnv} from 'supertape/env';
|
|
||||||
|
|
||||||
const testEnv = defineEnv({
|
const {
|
||||||
timeout: 7000,
|
run,
|
||||||
css: true,
|
parallel,
|
||||||
});
|
predefined,
|
||||||
|
} = require('madrun');
|
||||||
|
|
||||||
const buildEnv = {
|
const {version} = require('./package');
|
||||||
NODE_ENV: 'production',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
const names = [
|
||||||
|
'bin/cloudcmd.js',
|
||||||
|
'client',
|
||||||
|
'common',
|
||||||
|
'server',
|
||||||
|
'test',
|
||||||
|
'bin/release.js',
|
||||||
|
'webpack.config.js',
|
||||||
|
'cssnano.config.js',
|
||||||
|
'.webpack',
|
||||||
|
'.eslintrc.js',
|
||||||
|
'.madrun.js',
|
||||||
|
'{client,server,common}/**/*.spec.js',
|
||||||
|
];
|
||||||
|
|
||||||
|
const {putout} = predefined;
|
||||||
|
|
||||||
|
const env = 'THREAD_IT_COUNT=0';
|
||||||
|
const dockerName = 'coderaiser/cloudcmd';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
'start': () => 'node bin/cloudcmd.js',
|
'start': () => 'node bin/cloudcmd.js',
|
||||||
'start:dev': async () => await run('start', null, {
|
'start:dev': () => `NODE_ENV=development ${run('start')}`,
|
||||||
NODE_ENV: 'development',
|
|
||||||
}),
|
|
||||||
'build:start': () => run(['build:client', 'start']),
|
'build:start': () => run(['build:client', 'start']),
|
||||||
'build:start:dev': () => run([
|
'build:start:dev': () => run(['build:client:dev', 'start:dev']),
|
||||||
'build:client:dev',
|
'lint:all': () => run(['lint', 'lint:css', 'spell']),
|
||||||
'start:dev',
|
'lint:base': () => putout({
|
||||||
]),
|
names,
|
||||||
'lint:all': () => run('lint:progress'),
|
}),
|
||||||
'lint': () => 'redlint fix; putout .',
|
'lint:css': () => 'stylelint css/*.css',
|
||||||
'lint:progress': () => run('lint', '-f progress'),
|
'spell': () => 'yaspeller .',
|
||||||
'watch:lint': () => 'nodemon -w client -w server -w test -w common -w .webpack -x "putout -s"',
|
'fix:lint': () => run(['lint', 'lint:css'], '--fix'),
|
||||||
'fresh:lint': () => run('lint', '--fresh'),
|
'lint': () => run('lint:progress', '--cache'),
|
||||||
'lint:fresh': () => run('lint', '--fresh'),
|
'lint:progress': () => run('lint:base', '-f progress'),
|
||||||
'fix:lint': async () => `putout --fix . && redlint fix`,
|
'lint:stream': () => run('lint:base', '-f stream'),
|
||||||
'lint:stream': () => run('lint', '-f stream'),
|
|
||||||
'test': () => [testEnv, `tape '{test}/**/*.js' '{bin,client,static,common,server}/**/*.spec.js' -f fail`],
|
'test:base': () => {
|
||||||
'test:e2e': () => `tape 'test-e2e/**/*.js'`,
|
const cmd = 'tape';
|
||||||
|
const names = `'test/**/*.js' '{client,static,common,server}/**/*.spec.js'`;
|
||||||
|
|
||||||
|
return `${cmd} ${names}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
'test': () => `${env} ${run('test:base')}`,
|
||||||
'test:client': () => `tape 'test/client/**/*.js'`,
|
'test:client': () => `tape 'test/client/**/*.js'`,
|
||||||
'test:server': () => `tape 'test/**/*.js' 'server/**/*.spec.js' 'common/**/*.spec.js'`,
|
'test:server': () => `tape 'test/**/*.js' 'server/**/*.spec.js' 'common/**/*.spec.js'`,
|
||||||
'wisdom': async () => await run(['lint:all', 'build', 'test'], null, {
|
'wisdom': () => run(['lint:all', 'build', 'test']),
|
||||||
CI: 1,
|
|
||||||
}),
|
|
||||||
'wisdom:type': () => 'bin/release.js',
|
'wisdom:type': () => 'bin/release.js',
|
||||||
'coverage': async () => [testEnv, `c8 ${await cutEnv('test')}`],
|
'docker:pull': () => 'docker pull node',
|
||||||
'coverage:report': () => 'c8 report --reporter=lcov',
|
'docker:pull:alpine': () => 'docker pull mhart/alpine-node',
|
||||||
'report': () => 'c8 report --reporter=lcov',
|
'docker:pull:arm32': () => 'docker pull arm32v7/node:slim',
|
||||||
'6to5': () => [buildEnv, 'rspack build --config rspack.config.js'],
|
'docker:pull:arm64': () => 'docker pull arm64v8/node:slim',
|
||||||
|
'docker:push': () => dockerPush('x64', version),
|
||||||
|
'docker:push:latest': () => dockerPush('x64'),
|
||||||
|
'docker:push:alpine': () => dockerPush('alpine', version),
|
||||||
|
'docker:push:alpine:latest': () => dockerPush('alpine'),
|
||||||
|
'docker:push:arm32': () => dockerPush('arm32', version),
|
||||||
|
'docker:push:arm32:latest': () => dockerPush('arm32'),
|
||||||
|
'docker:push:arm64': () => dockerPush('arm64', version),
|
||||||
|
'docker:push:arm64:latest': () => dockerPush('arm64'),
|
||||||
|
'docker:build': () => dockerBuild('docker/Dockerfile', 'x64', version),
|
||||||
|
'docker:build:alpine': () => dockerBuild('docker/Dockerfile.alpine', 'alpine', version),
|
||||||
|
'docker:build:arm32': () => dockerBuild('docker/arm/Dockerfile.arm32v7', 'arm32', version),
|
||||||
|
'docker:build:arm64': () => dockerBuild('docker/arm/Dockerfile.arm64v8', 'arm64', version),
|
||||||
|
'docker:manifest:create': () => {
|
||||||
|
const images = [
|
||||||
|
`${dockerName}:latest`,
|
||||||
|
`${dockerName}:latest-x64`,
|
||||||
|
// `${dockerName}:latest-arm32`,
|
||||||
|
// `${dockerName}:latest-arm64`,
|
||||||
|
].join(' ');
|
||||||
|
|
||||||
|
return `docker manifest create ${images}`;
|
||||||
|
},
|
||||||
|
'docker:manifest:push': () => `docker manifest push ${dockerName}:latest`,
|
||||||
|
'docker': () => run(['docker:x64', 'docker:alpine', 'docker:manifest:*']),
|
||||||
|
'docker-ci': () => run(['build', 'docker-login', 'docker']),
|
||||||
|
'docker-login': () => 'docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD',
|
||||||
|
|
||||||
|
'docker:x64': () => run([
|
||||||
|
'docker:pull',
|
||||||
|
'docker:build',
|
||||||
|
'docker:tag',
|
||||||
|
'docker:push',
|
||||||
|
'docker:push:latest',
|
||||||
|
]),
|
||||||
|
|
||||||
|
'docker:alpine': () => run([
|
||||||
|
'docker:pull:alpine',
|
||||||
|
'docker:build:alpine',
|
||||||
|
'docker:tag:alpine',
|
||||||
|
'docker:push:alpine',
|
||||||
|
'docker:push:alpine:latest',
|
||||||
|
]),
|
||||||
|
|
||||||
|
/*
|
||||||
|
'docker:arm32': () => run([
|
||||||
|
'docker:pull:arm32',
|
||||||
|
'docker:build:arm32',
|
||||||
|
'docker:tag:arm32',
|
||||||
|
'docker:push:arm32',
|
||||||
|
'docker:push:arm32:latest',
|
||||||
|
]),
|
||||||
|
|
||||||
|
'docker:arm64': () => run([
|
||||||
|
'docker:pull:arm64',
|
||||||
|
'docker:build:arm64',
|
||||||
|
'docker:tag:arm64',
|
||||||
|
'docker:push:arm64',
|
||||||
|
'docker:push:arm64:latest',
|
||||||
|
]),
|
||||||
|
*/
|
||||||
|
|
||||||
|
'docker:manifest': () => run([
|
||||||
|
'docker:manifest:create',
|
||||||
|
'docker:manifest:push',
|
||||||
|
]),
|
||||||
|
|
||||||
|
'docker:tag': () => dockerTag('x64', version),
|
||||||
|
'docker:tag:alpine': () => dockerTag('alpine', version),
|
||||||
|
'docker:tag:arm32': () => dockerTag('arm32', version),
|
||||||
|
'docker:tag:arm64': () => dockerTag('arm64', version),
|
||||||
|
'docker:rm:version': () => dockerRmi('x64', version),
|
||||||
|
'docker:rm:latest': () => dockerRmi('x64'),
|
||||||
|
'docker:rm:alpine': () => dockerRmi('alpine', version),
|
||||||
|
'docker:rm:latest-alpine': () => dockerRmi('alpine'),
|
||||||
|
'docker:rm:arm32': () => dockerRmi('arm32', version),
|
||||||
|
'docker:rm:latest-arm32': () => dockerRmi('arm32'),
|
||||||
|
'docker:rm:arm64': () => dockerRmi('arm64', version),
|
||||||
|
'docker:rm:latest-arm64': () => dockerRmi('arm64'),
|
||||||
|
'docker:rm-old': () => `${parallel('docker:rm:*')} || true`,
|
||||||
|
|
||||||
|
'coverage': () => `${env} nyc ${run('test:base')}`,
|
||||||
|
'report': () => 'nyc report --reporter=text-lcov | coveralls',
|
||||||
|
'6to5': () => 'webpack --progress',
|
||||||
'6to5:client': () => run('6to5', '--mode production'),
|
'6to5:client': () => run('6to5', '--mode production'),
|
||||||
'6to5:client:dev': async () => await run('6to5', '--mode development', {
|
'6to5:client:dev': () => `NODE_ENV=development ${run('6to5', '--mode development')}`,
|
||||||
NODE_ENV: 'development',
|
|
||||||
}),
|
|
||||||
'pre6to5:client': () => 'rimraf dist',
|
'pre6to5:client': () => 'rimraf dist',
|
||||||
'pre6to5:client:dev': () => 'rimraf dist-dev',
|
'pre6to5:client:dev': () => 'rimraf dist-dev',
|
||||||
'watch:client': () => run('6to5:client', '--watch'),
|
'watch:client': () => run('6to5:client', '--watch'),
|
||||||
'watch:client:dev': () => run('6to5:client:dev', '--watch'),
|
'watch:client:dev': () => run('6to5:client:dev', '--watch'),
|
||||||
'watch:server': () => 'nodemon bin/cloudcmd.js',
|
'watch:server': () => 'nodemon bin/cloudcmd.js',
|
||||||
'watch:test': async () => [testEnv, `nodemon -w client -w server -w test -w common -x ${await cutEnv('test')}`],
|
'watch:lint': () => `nodemon -w client -w server -w webpack.config.js -x ${run('lint')}`,
|
||||||
'watch:test:client': async () => `nodemon -w client -w test/client -x ${await run('test:client')}`,
|
'watch:test': () => `nodemon -w client -w server -w test -w common -x ${run('test')}`,
|
||||||
'watch:test:server': async () => `nodemon -w client -w test/client -x ${await run('test:server')}`,
|
'watch:test:client': () => `nodemon -w client -w test/client -x ${run('test:client')}`,
|
||||||
'watch:coverage': async () => [testEnv, `nodemon -w server -w test -w common -x ${await cutEnv('coverage')}`],
|
'watch:test:server': () => `nodemon -w client -w test/client -x ${run('test:server')}`,
|
||||||
'watch:fix:lint': async () => `nodemon -w client -w server -w test -w common -x '${await run('fix:lint')}'`,
|
'watch:coverage': () => `nodemon -w server -w test -w common -x ${run('coverage')}`,
|
||||||
'build': async () => run('6to5:*'),
|
'build': () => run('6to5:*'),
|
||||||
'build:dev': async () => run('build:client:dev'),
|
|
||||||
'build:client': () => run('6to5:client'),
|
'build:client': () => run('6to5:client'),
|
||||||
'build:client:dev': () => run('6to5:client:dev'),
|
'build:client:dev': () => run('6to5:client:dev'),
|
||||||
'heroku-postbuild': () => run('6to5:client'),
|
'heroku-postbuild': () => run('6to5:client'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function dockerPush(type, version = 'latest') {
|
||||||
|
return `docker push coderaiser/cloudcmd:${version}-${type}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dockerBuild(file, type, version) {
|
||||||
|
return `docker build -f ${file} -t coderaiser/cloudcmd:${version}-${type} .`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dockerTag(type, version) {
|
||||||
|
return `docker tag coderaiser/cloudcmd:${version}-${type} coderaiser/cloudcmd:latest-${type}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dockerRmi(type, version = 'latest') {
|
||||||
|
return `docker rmi -f coderaiser/cloudcmd:${version}-${type}`;
|
||||||
|
}
|
||||||
|
|
|
||||||
43
.npmignore
43
.npmignore
|
|
@ -1,29 +1,30 @@
|
||||||
*.spec.*
|
|
||||||
*.config.*
|
|
||||||
*.fixture.js*
|
|
||||||
*.ai
|
|
||||||
*.cdr
|
|
||||||
*.eps
|
|
||||||
*.log
|
|
||||||
*.lock
|
|
||||||
|
|
||||||
.*
|
.*
|
||||||
|
*.spec.js
|
||||||
|
*.fixture.js*
|
||||||
manifest.yml
|
manifest.yml
|
||||||
docker-compose.yml
|
|
||||||
now.json
|
|
||||||
app.json
|
|
||||||
bower.json
|
|
||||||
deno.json
|
|
||||||
bin/release.*
|
|
||||||
img/logo/cloudcmd-hq.png
|
|
||||||
webpack.config.js
|
|
||||||
|
|
||||||
docker
|
docker
|
||||||
test*
|
docker-compose.yml
|
||||||
|
test
|
||||||
fixture
|
fixture
|
||||||
fixture-*
|
|
||||||
coverage
|
coverage
|
||||||
css
|
css
|
||||||
html
|
html
|
||||||
|
yarn-error.log
|
||||||
|
yarn.lock
|
||||||
|
now.json
|
||||||
|
cssnano.config.js
|
||||||
|
|
||||||
|
app.json
|
||||||
|
bower.json
|
||||||
|
manifest.yml
|
||||||
|
|
||||||
|
bin/release.js
|
||||||
|
|
||||||
client
|
client
|
||||||
|
|
||||||
|
webpack.config.js
|
||||||
|
|
||||||
|
*.ai
|
||||||
|
*.cdr
|
||||||
|
*.eps
|
||||||
|
|
||||||
|
|
|
||||||
15
.nycrc.json
15
.nycrc.json
|
|
@ -1,15 +0,0 @@
|
||||||
{
|
|
||||||
"checkCoverage": false,
|
|
||||||
"all": false,
|
|
||||||
"exclude": [
|
|
||||||
"**/*.spec.js",
|
|
||||||
"**/*.*.js",
|
|
||||||
"**/*.config.*",
|
|
||||||
"**/fixture",
|
|
||||||
"**/test/**"
|
|
||||||
],
|
|
||||||
"branches": 100,
|
|
||||||
"lines": 100,
|
|
||||||
"functions": 100,
|
|
||||||
"statements": 100
|
|
||||||
}
|
|
||||||
63
.putout.json
63
.putout.json
|
|
@ -1,60 +1,27 @@
|
||||||
{
|
{
|
||||||
"plugins": ["cloudcmd"],
|
|
||||||
"ignore": [
|
|
||||||
"*.md",
|
|
||||||
"app.json",
|
|
||||||
"fontello.json",
|
|
||||||
"html",
|
|
||||||
"fixture*"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"package-json/add-type": "off"
|
|
||||||
},
|
|
||||||
"match": {
|
"match": {
|
||||||
".filesystem.json": {
|
|
||||||
"nodejs/rename-file-cjs-to-js": "off"
|
|
||||||
},
|
|
||||||
"base64": {
|
|
||||||
"types/convert-typeof-to-is-type": "off"
|
|
||||||
},
|
|
||||||
"*.md": {
|
|
||||||
"nodejs/convert-commonjs-to-esm": "on"
|
|
||||||
},
|
|
||||||
".webpack": {
|
|
||||||
"webpack": "on"
|
|
||||||
},
|
|
||||||
"server": {
|
"server": {
|
||||||
"nodejs/remove-process-exit": "on"
|
"remove-process-exit": true
|
||||||
},
|
},
|
||||||
"server/{server,exit}.js": {
|
"server/(server|exit).js": {
|
||||||
"nodejs/remove-process-exit": "off"
|
"remove-process-exit": false,
|
||||||
|
"remove-console": false
|
||||||
},
|
},
|
||||||
"server/{server,exit,terminal,distribute/log}.{js,mjs}": {
|
"server/(terminal|distribute/log).js": {
|
||||||
"remove-console": "off"
|
"remove-console": false
|
||||||
},
|
},
|
||||||
"client/{client,cloudcmd,load-module}.{js,mjs}": {
|
"client/(client|cloudcmd|load-module).js": {
|
||||||
"remove-console": "off"
|
"remove-console": false
|
||||||
},
|
},
|
||||||
"client": {
|
"client/modules/config/index.js": {
|
||||||
"nodejs": "off"
|
"apply-shorthand-properties": [{
|
||||||
},
|
"ignore": [
|
||||||
"client/sw": {
|
"ONE_MINUTE"
|
||||||
"remove-console": "off"
|
]
|
||||||
|
}]
|
||||||
},
|
},
|
||||||
"test/common/cloudfunc.js": {
|
"test/common/cloudfunc.js": {
|
||||||
"remove-console": "off"
|
"remove-console": false
|
||||||
},
|
|
||||||
"storage.js": {
|
|
||||||
"promises/remove-useless-async": "off"
|
|
||||||
},
|
|
||||||
"docker.yml": {
|
|
||||||
"github/set-node-versions": "off"
|
|
||||||
},
|
|
||||||
"vim.js": {
|
|
||||||
"merge-duplicate-functions": "off"
|
|
||||||
},
|
|
||||||
"common": {
|
|
||||||
"nodejs/declare": "off"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
import {env} from 'node:process';
|
|
||||||
import {rspack} from '@rspack/core';
|
|
||||||
|
|
||||||
const {CssExtractRspackPlugin} = rspack;
|
|
||||||
const isDev = env.NODE_ENV === 'development';
|
|
||||||
|
|
||||||
const plugins = [
|
|
||||||
new CssExtractRspackPlugin({
|
|
||||||
filename: '[name].css',
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
const rules = [{
|
|
||||||
test: /\.css$/i,
|
|
||||||
use: [CssExtractRspackPlugin.loader, {
|
|
||||||
loader: 'css-loader',
|
|
||||||
options: {
|
|
||||||
url: true,
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
}, {
|
|
||||||
test: /\.(png|gif|svg|woff|woff2|eot|ttf)$/,
|
|
||||||
type: 'asset/inline',
|
|
||||||
}];
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mode: isDev ? 'development' : 'production',
|
|
||||||
plugins,
|
|
||||||
module: {
|
|
||||||
rules,
|
|
||||||
},
|
|
||||||
optimization: {
|
|
||||||
minimize: !isDev,
|
|
||||||
minimizer: [
|
|
||||||
new rspack.LightningCssMinimizerRspackPlugin(),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
import {env} from 'node:process';
|
|
||||||
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
|
||||||
|
|
||||||
const isDev = env.NODE_ENV === 'development';
|
|
||||||
|
|
||||||
export const plugins = [
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
inject: false,
|
|
||||||
template: 'html/index.html',
|
|
||||||
minify: !isDev && getMinifyHtmlOptions(),
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
function getMinifyHtmlOptions() {
|
|
||||||
return {
|
|
||||||
removeComments: true,
|
|
||||||
removeCommentsFromCDATA: true,
|
|
||||||
removeCDATASectionsFromCDATA: true,
|
|
||||||
collapseWhitespace: true,
|
|
||||||
collapseBooleanAttributes: true,
|
|
||||||
removeAttributeQuotes: true,
|
|
||||||
removeRedundantAttributes: true,
|
|
||||||
useShortDoctype: true,
|
|
||||||
removeEmptyAttributes: true,
|
|
||||||
/* оставляем, поскольку у нас
|
|
||||||
* в элемент fm генерируеться
|
|
||||||
* таблица файлов
|
|
||||||
*/
|
|
||||||
removeEmptyElements: false,
|
|
||||||
removeOptionalTags: true,
|
|
||||||
removeScriptTypeAttributes: true,
|
|
||||||
removeStyleLinkTypeAttributes: true,
|
|
||||||
|
|
||||||
minifyJS: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
173
.rspack/js.js
173
.rspack/js.js
|
|
@ -1,173 +0,0 @@
|
||||||
import {resolve, sep} from 'node:path';
|
|
||||||
import {fileURLToPath} from 'node:url';
|
|
||||||
import {env} from 'node:process';
|
|
||||||
import {rspack} from '@rspack/core';
|
|
||||||
|
|
||||||
const resolveModule = (a) => fileURLToPath(import.meta.resolve(a));
|
|
||||||
|
|
||||||
const {
|
|
||||||
EnvironmentPlugin,
|
|
||||||
NormalModuleReplacementPlugin,
|
|
||||||
ProvidePlugin,
|
|
||||||
} = rspack;
|
|
||||||
|
|
||||||
const modules = './modules';
|
|
||||||
const dirModules = './client/modules';
|
|
||||||
const dirCss = './css';
|
|
||||||
const dirThemes = `${dirCss}/themes`;
|
|
||||||
const dirColumns = `${dirCss}/columns`;
|
|
||||||
const dir = './client';
|
|
||||||
const {NODE_ENV} = env;
|
|
||||||
const isDev = NODE_ENV === 'development';
|
|
||||||
|
|
||||||
const rootDir = new URL('..', import.meta.url).pathname;
|
|
||||||
const dist = resolve(rootDir, 'dist');
|
|
||||||
const distDev = resolve(rootDir, 'dist-dev');
|
|
||||||
const devtool = isDev ? 'eval' : 'source-map';
|
|
||||||
|
|
||||||
const noParse = (a) => a.endsWith('.spec.js');
|
|
||||||
|
|
||||||
// codegen.macro is a babel-macro (build-time codegen), not supported by
|
|
||||||
// Rspack's native SWC transform, so client/sw/sw.js (the only file that
|
|
||||||
// uses it) keeps going through babel-loader. Everything else uses
|
|
||||||
// Rspack's built-in SWC loader, which is the main source of the speedup.
|
|
||||||
const rules = [{
|
|
||||||
test: /sw\/sw\.js$/,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
loader: 'babel-loader',
|
|
||||||
}, {
|
|
||||||
test: /\.[mc]?js$/,
|
|
||||||
exclude: [/node_modules/, /sw\/sw\.js$/],
|
|
||||||
loader: 'builtin:swc-loader',
|
|
||||||
options: {
|
|
||||||
jsc: {
|
|
||||||
parser: {
|
|
||||||
syntax: 'ecmascript',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
targets: 'defaults',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}];
|
|
||||||
|
|
||||||
const plugins = [
|
|
||||||
new NormalModuleReplacementPlugin(/^node:/, (resource) => {
|
|
||||||
resource.request = resource.request.replace(/^node:/, '');
|
|
||||||
}),
|
|
||||||
new NormalModuleReplacementPlugin(/^putout$/, '@putout/bundle'),
|
|
||||||
new EnvironmentPlugin({
|
|
||||||
NODE_ENV,
|
|
||||||
}),
|
|
||||||
new ProvidePlugin({
|
|
||||||
process: 'process/browser',
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
const splitChunks = {
|
|
||||||
chunks: 'all',
|
|
||||||
cacheGroups: {
|
|
||||||
abcCommon: {
|
|
||||||
name: 'cloudcmd.common',
|
|
||||||
chunks: (chunk) => {
|
|
||||||
const lazyChunks = [
|
|
||||||
'sw',
|
|
||||||
'nojs',
|
|
||||||
'view',
|
|
||||||
'edit',
|
|
||||||
'terminal',
|
|
||||||
'config',
|
|
||||||
'user-menu',
|
|
||||||
'help',
|
|
||||||
'themes/dark',
|
|
||||||
'themes/light',
|
|
||||||
'columns/name-size',
|
|
||||||
'columns/name-size-date',
|
|
||||||
'columns/name-size-time',
|
|
||||||
'columns/name-size-date-time',
|
|
||||||
];
|
|
||||||
|
|
||||||
return !lazyChunks.includes(chunk.name);
|
|
||||||
},
|
|
||||||
minChunks: 1,
|
|
||||||
enforce: true,
|
|
||||||
priority: -1,
|
|
||||||
reuseExistingChunk: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
resolve: {
|
|
||||||
symlinks: false,
|
|
||||||
alias: {
|
|
||||||
'node:process': 'process',
|
|
||||||
'node:path': 'path',
|
|
||||||
},
|
|
||||||
fallback: {
|
|
||||||
path: resolveModule('path-browserify'),
|
|
||||||
process: resolveModule('process/browser'),
|
|
||||||
util: resolveModule('util'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
devtool,
|
|
||||||
optimization: {
|
|
||||||
splitChunks,
|
|
||||||
},
|
|
||||||
entry: {
|
|
||||||
'themes/dark': `${dirThemes}/dark.css`,
|
|
||||||
'themes/light': `${dirThemes}/light.css`,
|
|
||||||
'columns/name-size': `${dirColumns}/name-size.css`,
|
|
||||||
'columns/name-size-date': `${dirColumns}/name-size-date.css`,
|
|
||||||
'columns/name-size-date-time': `${dirColumns}/name-size-date-time.css`,
|
|
||||||
'nojs': `${dirCss}/nojs.css`,
|
|
||||||
'help': `${dirCss}/help.css`,
|
|
||||||
'view': `${dirCss}/view.css`,
|
|
||||||
'config': `${dirCss}/config.css`,
|
|
||||||
'terminal': `${dirCss}/terminal.css`,
|
|
||||||
'user-menu': `${dirCss}/user-menu.css`,
|
|
||||||
'sw': `${dir}/sw/sw.js`,
|
|
||||||
'cloudcmd': `${dir}/cloudcmd.js`,
|
|
||||||
[`${modules}/edit`]: `${dirModules}/edit.js`,
|
|
||||||
[`${modules}/edit-file`]: `${dirModules}/edit-file.js`,
|
|
||||||
[`${modules}/edit-file-vim`]: `${dirModules}/edit-file-vim.js`,
|
|
||||||
[`${modules}/edit-names`]: `${dirModules}/edit-names.js`,
|
|
||||||
[`${modules}/edit-names-vim`]: `${dirModules}/edit-names-vim.js`,
|
|
||||||
[`${modules}/menu`]: `${dirModules}/menu/index.js`,
|
|
||||||
[`${modules}/view`]: `${dirModules}/view/index.js`,
|
|
||||||
[`${modules}/help`]: `${dirModules}/help.js`,
|
|
||||||
[`${modules}/markdown`]: `${dirModules}/markdown.js`,
|
|
||||||
[`${modules}/config`]: `${dirModules}/config/index.js`,
|
|
||||||
[`${modules}/contact`]: `${dirModules}/contact.js`,
|
|
||||||
[`${modules}/upload`]: `${dirModules}/upload.js`,
|
|
||||||
[`${modules}/operation`]: `${dirModules}/operation/index.js`,
|
|
||||||
[`${modules}/konsole`]: `${dirModules}/konsole.js`,
|
|
||||||
[`${modules}/terminal`]: `${dirModules}/terminal.js`,
|
|
||||||
[`${modules}/terminal-run`]: `${dirModules}/terminal-run.js`,
|
|
||||||
[`${modules}/cloud`]: `${dirModules}/cloud.js`,
|
|
||||||
[`${modules}/user-menu`]: `${dirModules}/user-menu/index.js`,
|
|
||||||
[`${modules}/polyfill`]: `${dirModules}/polyfill.js`,
|
|
||||||
[`${modules}/command-line`]: `${dirModules}/command-line.js`,
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
filename: '[name].js',
|
|
||||||
path: isDev ? distDev : dist,
|
|
||||||
pathinfo: isDev,
|
|
||||||
devtoolModuleFilenameTemplate,
|
|
||||||
publicPath: '/dist/',
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules,
|
|
||||||
noParse,
|
|
||||||
},
|
|
||||||
plugins,
|
|
||||||
performance: {
|
|
||||||
maxEntrypointSize: 800_000,
|
|
||||||
maxAssetSize: 600_000,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function devtoolModuleFilenameTemplate(info) {
|
|
||||||
const resource = info.absoluteResourcePath.replace(rootDir + sep, '');
|
|
||||||
return `file://cloudcmd/${resource}`;
|
|
||||||
}
|
|
||||||
17
.stylelintrc.yml
Normal file
17
.stylelintrc.yml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
extends: stylelint-config-standard
|
||||||
|
rules:
|
||||||
|
indentation: 4
|
||||||
|
declaration-block-trailing-semicolon: always
|
||||||
|
declaration-colon-space-before: null
|
||||||
|
selector-list-comma-newline-after: null
|
||||||
|
comment-empty-line-before: null
|
||||||
|
number-leading-zero: null
|
||||||
|
number-no-trailing-zeros: null
|
||||||
|
string-quotes: single
|
||||||
|
function-url-quotes: never
|
||||||
|
no-eol-whitespace: null
|
||||||
|
font-family-name-quotes: always-unless-keyword
|
||||||
|
font-family-no-missing-generic-family-keyword: null
|
||||||
|
rule-empty-line-before: null
|
||||||
|
max-empty-lines: 2
|
||||||
|
|
||||||
52
.travis.yml
Normal file
52
.travis.yml
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- 14
|
||||||
|
- 12
|
||||||
|
- 10
|
||||||
|
|
||||||
|
cache:
|
||||||
|
npm: false
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- os: windows
|
||||||
|
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
- windows
|
||||||
|
|
||||||
|
# https://docs.travis-ci.com/user/customizing-the-build/#git-end-of-line-conversion-control
|
||||||
|
# need for windows
|
||||||
|
git:
|
||||||
|
autocrlf: input
|
||||||
|
|
||||||
|
|
||||||
|
script:
|
||||||
|
- npm run lint && npm run build && npm run coverage && npm run report
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email:
|
||||||
|
on_success: never
|
||||||
|
on_failure: change
|
||||||
|
|
||||||
|
sudo: required
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
|
||||||
|
before_deploy:
|
||||||
|
- echo '{"experimental":"enabled"}' | sudo tee /etc/docker/daemon.json
|
||||||
|
- mkdir -p $HOME/.docker
|
||||||
|
- echo '{"experimental":"enabled"}' | tee $HOME/.docker/config.json
|
||||||
|
- sudo service docker start
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
provider: script
|
||||||
|
script: npm run docker-ci
|
||||||
|
skip_cleanup: true
|
||||||
|
on:
|
||||||
|
node: 12
|
||||||
|
condition: $TRAVIS_OS_NAME = linux
|
||||||
|
tags: true
|
||||||
|
all_branches: false
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
[files]
|
|
||||||
extend-exclude = ["ChangeLog"]
|
|
||||||
81
.webpack/css.js
Normal file
81
.webpack/css.js
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const {
|
||||||
|
basename,
|
||||||
|
extname,
|
||||||
|
join,
|
||||||
|
} = require('path');
|
||||||
|
|
||||||
|
const {env} = process;
|
||||||
|
const isDev = env.NODE_ENV === 'development';
|
||||||
|
|
||||||
|
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
|
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||||
|
|
||||||
|
const extractCSS = (a) => new ExtractTextPlugin(`${a}.css`);
|
||||||
|
const extractMain = extractCSS('[name]');
|
||||||
|
|
||||||
|
const cssNames = [
|
||||||
|
'nojs',
|
||||||
|
'view',
|
||||||
|
'config',
|
||||||
|
'terminal',
|
||||||
|
'user-menu',
|
||||||
|
...getCSSList('columns'),
|
||||||
|
];
|
||||||
|
|
||||||
|
const cssPlugins = cssNames.map(extractCSS);
|
||||||
|
const clean = (a) => a.filter(Boolean);
|
||||||
|
|
||||||
|
const plugins = clean([
|
||||||
|
...cssPlugins,
|
||||||
|
extractMain,
|
||||||
|
!isDev && new OptimizeCssAssetsPlugin(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const rules = [{
|
||||||
|
test: /\.css$/,
|
||||||
|
exclude: /css\/(nojs|view|config|terminal|user-menu|columns.*)\.css/,
|
||||||
|
use: extractMain.extract([
|
||||||
|
'css-loader',
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
...cssPlugins.map(extract), {
|
||||||
|
test: /\.(png|gif|svg|woff|woff2|eot|ttf)$/,
|
||||||
|
use: {
|
||||||
|
loader: 'url-loader',
|
||||||
|
options: {
|
||||||
|
limit: 100000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins,
|
||||||
|
module: {
|
||||||
|
rules,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function getCSSList(dir) {
|
||||||
|
const base = (a) => basename(a, extname(a));
|
||||||
|
const addDir = (name) => `${dir}/${name}`;
|
||||||
|
const rootDir = join(__dirname, '..');
|
||||||
|
|
||||||
|
return fs.readdirSync(`${rootDir}/css/${dir}`)
|
||||||
|
.map(base)
|
||||||
|
.map(addDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
function extract(extractPlugin) {
|
||||||
|
const {filename} = extractPlugin;
|
||||||
|
|
||||||
|
return {
|
||||||
|
test: RegExp(`css/${filename}`),
|
||||||
|
use: extractPlugin.extract([
|
||||||
|
'css-loader',
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
43
.webpack/html.js
Normal file
43
.webpack/html.js
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const {env} = process;
|
||||||
|
const isDev = env.NODE_ENV === 'development';
|
||||||
|
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
|
||||||
|
const plugins = [
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
inject: false,
|
||||||
|
template: 'html/index.html',
|
||||||
|
minify: !isDev && getMinifyHtmlOptions(),
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins,
|
||||||
|
};
|
||||||
|
|
||||||
|
function getMinifyHtmlOptions() {
|
||||||
|
return {
|
||||||
|
removeComments: true,
|
||||||
|
removeCommentsFromCDATA: true,
|
||||||
|
removeCDATASectionsFromCDATA: true,
|
||||||
|
collapseWhitespace: true,
|
||||||
|
collapseBooleanAttributes: true,
|
||||||
|
removeAttributeQuotes: true,
|
||||||
|
removeRedundantAttributes: true,
|
||||||
|
useShortDoctype: true,
|
||||||
|
removeEmptyAttributes: true,
|
||||||
|
/* оставляем, поскольку у нас
|
||||||
|
* в элемент fm генерируеться
|
||||||
|
* таблица файлов
|
||||||
|
*/
|
||||||
|
removeEmptyElements: false,
|
||||||
|
removeOptionalTags: true,
|
||||||
|
removeScriptTypeAttributes: true,
|
||||||
|
removeStyleLinkTypeAttributes: true,
|
||||||
|
|
||||||
|
minifyJS: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
134
.webpack/js.js
Normal file
134
.webpack/js.js
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const {
|
||||||
|
resolve,
|
||||||
|
sep,
|
||||||
|
join,
|
||||||
|
} = require('path');
|
||||||
|
|
||||||
|
const {EnvironmentPlugin} = require('webpack');
|
||||||
|
|
||||||
|
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');
|
||||||
|
|
||||||
|
const dir = './client';
|
||||||
|
const dirModules = './client/modules';
|
||||||
|
const modules = './modules';
|
||||||
|
|
||||||
|
const {env} = process;
|
||||||
|
const {NODE_ENV} = env;
|
||||||
|
const isDev = NODE_ENV === 'development';
|
||||||
|
|
||||||
|
const rootDir = join(__dirname, '..');
|
||||||
|
const dist = resolve(rootDir, 'dist');
|
||||||
|
const distDev = resolve(rootDir, 'dist-dev');
|
||||||
|
const devtool = isDev ? 'eval' : 'source-map';
|
||||||
|
|
||||||
|
const notEmpty = (a) => a;
|
||||||
|
const clean = (array) => array.filter(notEmpty);
|
||||||
|
|
||||||
|
const noParse = (a) => /\.spec\.js$/.test(a);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
babelrc: false,
|
||||||
|
plugins: [
|
||||||
|
'module:babel-plugin-macros',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const rules = clean([
|
||||||
|
!isDev && {
|
||||||
|
test: /\.js$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
loader: 'babel-loader',
|
||||||
|
},
|
||||||
|
isDev && {
|
||||||
|
test: /\.js$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
loader: 'babel-loader',
|
||||||
|
options,
|
||||||
|
}]);
|
||||||
|
|
||||||
|
const plugins = [
|
||||||
|
new EnvironmentPlugin({
|
||||||
|
NODE_ENV,
|
||||||
|
}),
|
||||||
|
|
||||||
|
new ServiceWorkerWebpackPlugin({
|
||||||
|
entry: join(__dirname, '..', 'client', 'sw', 'sw.js'),
|
||||||
|
excludes: ['*'],
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const splitChunks = {
|
||||||
|
name: 'cloudcmd.common',
|
||||||
|
chunks: 'all',
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
resolve: {
|
||||||
|
symlinks: false,
|
||||||
|
},
|
||||||
|
devtool,
|
||||||
|
optimization: {
|
||||||
|
splitChunks,
|
||||||
|
},
|
||||||
|
entry: {
|
||||||
|
cloudcmd: `${dir}/cloudcmd.js`,
|
||||||
|
[modules + '/edit']: `${dirModules}/edit.js`,
|
||||||
|
[modules + '/edit-file']: `${dirModules}/edit-file.js`,
|
||||||
|
[modules + '/edit-file-vim']: `${dirModules}/edit-file-vim.js`,
|
||||||
|
[modules + '/edit-names']: `${dirModules}/edit-names.js`,
|
||||||
|
[modules + '/edit-names-vim']: `${dirModules}/edit-names-vim.js`,
|
||||||
|
[modules + '/menu']: `${dirModules}/menu.js`,
|
||||||
|
[modules + '/view']: `${dirModules}/view.js`,
|
||||||
|
[modules + '/help']: `${dirModules}/help.js`,
|
||||||
|
[modules + '/markdown']: `${dirModules}/markdown.js`,
|
||||||
|
[modules + '/config']: `${dirModules}/config/index.js`,
|
||||||
|
[modules + '/contact']: `${dirModules}/contact.js`,
|
||||||
|
[modules + '/upload']: `${dirModules}/upload.js`,
|
||||||
|
[modules + '/operation']: `${dirModules}/operation/index.js`,
|
||||||
|
[modules + '/konsole']: `${dirModules}/konsole.js`,
|
||||||
|
[modules + '/terminal']: `${dirModules}/terminal.js`,
|
||||||
|
[modules + '/terminal-run']: `${dirModules}/terminal-run.js`,
|
||||||
|
[modules + '/cloud']: `${dirModules}/cloud.js`,
|
||||||
|
[modules + '/user-menu']: `${dirModules}/user-menu/index.js`,
|
||||||
|
[modules + '/polyfill']: `${dirModules}/polyfill.js`,
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: '[name].js',
|
||||||
|
path: isDev ? distDev : dist,
|
||||||
|
pathinfo: isDev,
|
||||||
|
devtoolModuleFilenameTemplate,
|
||||||
|
publicPath: '/dist/',
|
||||||
|
},
|
||||||
|
externals: [
|
||||||
|
externals,
|
||||||
|
],
|
||||||
|
module: {
|
||||||
|
rules,
|
||||||
|
noParse,
|
||||||
|
},
|
||||||
|
plugins,
|
||||||
|
performance: {
|
||||||
|
maxEntrypointSize: 600000,
|
||||||
|
maxAssetSize: 600000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function externals(context, request, fn) {
|
||||||
|
if (!isDev)
|
||||||
|
return fn();
|
||||||
|
|
||||||
|
const list = [];
|
||||||
|
|
||||||
|
if (list.includes(request))
|
||||||
|
return fn(null, request);
|
||||||
|
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
function devtoolModuleFilenameTemplate(info) {
|
||||||
|
const resource = info.absoluteResourcePath.replace(rootDir + sep, '');
|
||||||
|
return `file://cloudcmd/${resource}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -22,14 +22,12 @@
|
||||||
"Iptables",
|
"Iptables",
|
||||||
"JitSu",
|
"JitSu",
|
||||||
"Node",
|
"Node",
|
||||||
"IO",
|
|
||||||
"Olena",
|
"Olena",
|
||||||
"TarZak",
|
"TarZak",
|
||||||
"Termux",
|
"Termux",
|
||||||
"Zalitok",
|
"Zalitok",
|
||||||
"WebSocket",
|
"WebSocket",
|
||||||
"auth",
|
"auth",
|
||||||
"autostart",
|
|
||||||
"binded",
|
"binded",
|
||||||
"cd",
|
"cd",
|
||||||
"cloudcmd",
|
"cloudcmd",
|
||||||
|
|
@ -46,7 +44,6 @@
|
||||||
"gz",
|
"gz",
|
||||||
"io",
|
"io",
|
||||||
"js",
|
"js",
|
||||||
"linux",
|
|
||||||
"maintainers",
|
"maintainers",
|
||||||
"markdown",
|
"markdown",
|
||||||
"microservice",
|
"microservice",
|
||||||
|
|
@ -56,8 +53,6 @@
|
||||||
"nginx",
|
"nginx",
|
||||||
"npm",
|
"npm",
|
||||||
"or io",
|
"or io",
|
||||||
"patreon",
|
|
||||||
"rc",
|
|
||||||
"refactor",
|
"refactor",
|
||||||
"sexualized",
|
"sexualized",
|
||||||
"sslPort",
|
"sslPort",
|
||||||
|
|
@ -66,7 +61,6 @@
|
||||||
"v0",
|
"v0",
|
||||||
"v1",
|
"v1",
|
||||||
"v2",
|
"v2",
|
||||||
"yml",
|
"yml"
|
||||||
"systemd"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment include:
|
Examples of behavior that contributes to creating a positive environment include:
|
||||||
|
|
||||||
- Using welcoming and inclusive language
|
* Using welcoming and inclusive language
|
||||||
- Being respectful of differing viewpoints and experiences
|
* Being respectful of differing viewpoints and experiences
|
||||||
- Gracefully accepting constructive criticism
|
* Gracefully accepting constructive criticism
|
||||||
- Focusing on what is best for the community
|
* Focusing on what is best for the community
|
||||||
- Showing empathy towards other community members
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
- Public or private harassment
|
* Public or private harassment
|
||||||
- Publishing others' private information, such as a physical or electronic address, without explicit permission
|
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||||
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||||
|
|
||||||
## Our Responsibilities
|
## Our Responsibilities
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,27 @@
|
||||||
## Commit
|
Commit
|
||||||
|
---------------
|
||||||
Format of the commit message: **type(scope) subject**
|
Format of the commit message: **type(scope) subject**
|
||||||
|
|
||||||
**Type**:
|
**Type**:
|
||||||
|
- feature(scope) subject
|
||||||
- feature: scope: subject
|
- fix(scope) subject
|
||||||
- fix: scope: subject
|
- docs(scope) subject
|
||||||
- docs: scope: subject
|
- refactor(scope) subject
|
||||||
- refactor: scope: subject
|
- test(scope) subject
|
||||||
- test: scope: subject
|
- chore(scope) subject
|
||||||
- chore: scope: subject
|
|
||||||
|
|
||||||
**Scope**:
|
**Scope**:
|
||||||
Scope could be anything specifying place of the commit change.
|
Scope could be anything specifying place of the commit change.
|
||||||
For example util, console, view, edit, style etc...
|
For example util, console, view, edit, style etc...
|
||||||
|
|
||||||
**Subject text**:
|
**Subject text**:
|
||||||
|
|
||||||
- use imperative, present tense: “change” not “changed” nor “changes”
|
- use imperative, present tense: “change” not “changed” nor “changes”
|
||||||
- don't capitalize first letter
|
- don't capitalize first letter
|
||||||
- no dot (.) at the end
|
- no dot (.) at the end
|
||||||
**Message body**:
|
**Message body**:
|
||||||
- just as in <subject> use imperative, present tense: “change” not “changed” nor “changes”
|
- just as in <subject> use imperative, present tense: “change” not “changed” nor “changes”
|
||||||
- includes motivation for the change and contrasts with previous behavior
|
- includes motivation for the change and contrasts with previous behavior
|
||||||
|
|
||||||
**Examples**:
|
**Examples**:
|
||||||
|
- [fix(style) .name{width}: 37% -> 35%](https://github.com/coderaiser/cloudcmd/commit/94b0642e3990c17b3a0ee3efeb75f343e1e7c050)
|
||||||
- [fix: style: .name{width}: 37% -> 35%](https://github.com/coderaiser/cloudcmd/commit/94b0642e3990c17b3a0ee3efeb75f343e1e7c050)
|
- [fix(console) dispatch: focus -> mouseup](https://github.com/coderaiser/cloudcmd/commit/f41ec5058d1411e86a881f8e8077e0572e0409ec)
|
||||||
- [fix: console: dispatch: focus -> mouseup](https://github.com/coderaiser/cloudcmd/commit/f41ec5058d1411e86a881f8e8077e0572e0409ec)
|
|
||||||
|
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
||||||
(The MIT License)
|
(The MIT License)
|
||||||
|
|
||||||
Copyright (c) 2012-2025 Coderaiser <mnemonic.enemy@gmail.com>
|
Copyright (c) 2012-2019 Coderaiser <mnemonic.enemy@gmail.com>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
|
|
|
||||||
87
README.md
87
README.md
|
|
@ -1,22 +1,25 @@
|
||||||
# Cloud Commander v19.19.1 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL] [![Deploy on InstaPods][DeployInstaPodsIMG]][DeployInstaPodsURL]
|
# Cloud Commander v14.6.0 [![Build Status][BuildStatusIMGURL]][BuildStatusURL] [![Codacy][CodacyIMG]][CodacyURL] [![Gitter][GitterIMGURL]][GitterURL] [](https://app.fossa.io/projects/git%2Bgithub.com%2Fcoderaiser%2Fcloudcmd?ref=badge_shield)
|
||||||
|
|
||||||
### [Main][MainURL] [Blog][BlogURL] [Support][SupportURL] [Demo][DemoURL]
|
### [Main][MainURL] [Blog][BlogURL] Live([Heroku][HerokuURL])
|
||||||
|
|
||||||
[MainURL]: https://cloudcmd.io "Main"
|
[NPM_INFO_IMG]: https://nodei.co/npm/cloudcmd.png
|
||||||
[BlogURL]: https://blog.cloudcmd.io "Blog"
|
[MainURL]: http://cloudcmd.io "Main"
|
||||||
[SupportURL]: https://patreon.com/coderaiser "Patreon"
|
[BlogURL]: http://blog.cloudcmd.io "Blog"
|
||||||
[DemoURL]: https://cloudcmd-zdp6.onrender.com
|
[HerokuURL]: https://cloudcmd.herokuapp.com/ "Heroku"
|
||||||
[NPM_INFO_IMG]: https://nodei.co/npm/cloudcmd.png
|
[BuildStatusURL]: https://travis-ci.org/coderaiser/cloudcmd "Build Status"
|
||||||
[BuildStatusURL]: https://github.com/coderaiser/cloudcmd/actions/workflows/nodejs.yml "Build Status"
|
[BuildStatusIMGURL]: https://img.shields.io/travis/coderaiser/cloudcmd.svg?style=flat-squere&longCache=true
|
||||||
[BuildStatusIMGURL]: https://github.com/coderaiser/cloudcmd/actions/workflows/nodejs.yml/badge.svg
|
|
||||||
[CodacyURL]: https://www.codacy.com/app/coderaiser/cloudcmd
|
[BuildAppveyorURL]: https://ci.appveyor.com/project/coderaiser/cloudcmd
|
||||||
[CodacyIMG]: https://api.codacy.com/project/badge/Grade/ddda78be780549ce8754f8d47a8c0e36
|
[BuildAppveyorIMGURL]: https://ci.appveyor.com/api/projects/status/tse6sc8dxrqxqehi?svg=true
|
||||||
[GitterURL]: https://gitter.im/cloudcmd/hello
|
|
||||||
[GitterIMGURL]: https://img.shields.io/gitter/room/coderaiser/cloudcmd.js.svg
|
[CodacyURL]: https://www.codacy.com/app/coderaiser/cloudcmd
|
||||||
[DeployURL]: https://heroku.com/deploy?template=https://github.com/coderaiser/cloudcmd "Deploy"
|
[CodacyIMG]: https://api.codacy.com/project/badge/Grade/ddda78be780549ce8754f8d47a8c0e36
|
||||||
[DeployIMG]: https://www.herokucdn.com/deploy/button.png
|
|
||||||
[DeployInstaPodsIMG]: https://img.shields.io/badge/deploy%20on-InstaPods-blue
|
[GitterURL]: https://gitter.im/cloudcmd/hello
|
||||||
[DeployInstaPodsURL]: https://app.instapods.com/dashboard/pods/create?app=cloudcmd&ref=cloudcmd
|
[GitterIMGURL]: https://img.shields.io/gitter/room/coderaiser/cloudcmd.js.svg
|
||||||
|
|
||||||
|
[DeployURL]: https://heroku.com/deploy?template=https://github.com/coderaiser/cloudcmd "Deploy"
|
||||||
|
[DeployIMG]: https://www.herokucdn.com/deploy/button.png
|
||||||
|
|
||||||
**Cloud Commander** a file manager for the web with console and editor.
|
**Cloud Commander** a file manager for the web with console and editor.
|
||||||
|
|
||||||
|
|
@ -27,7 +30,6 @@
|
||||||
```
|
```
|
||||||
npm i cloudcmd -g
|
npm i cloudcmd -g
|
||||||
```
|
```
|
||||||
|
|
||||||
## Start
|
## Start
|
||||||
|
|
||||||
For starting just type in console:
|
For starting just type in console:
|
||||||
|
|
@ -45,8 +47,8 @@ Open url `http://localhost:8000` in browser.
|
||||||
You will see something similar to this.
|
You will see something similar to this.
|
||||||

|

|
||||||
|
|
||||||
## Deploy
|
|
||||||
|
|
||||||
|
## Deploy
|
||||||
`Cloud Commander` could be easily deployed to [Heroku][DeployURL].
|
`Cloud Commander` could be easily deployed to [Heroku][DeployURL].
|
||||||
|
|
||||||
[![Deploy][DeployIMG]][DeployURL]
|
[![Deploy][DeployIMG]][DeployURL]
|
||||||
|
|
@ -70,19 +72,17 @@ npm i cloudcmd express socket.io -S
|
||||||
And create `index.js`:
|
And create `index.js`:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import http from 'node:http';
|
const http = require('http');
|
||||||
import {cloudcmd} from 'cloudcmd';
|
const cloudcmd = require('cloudcmd');
|
||||||
import {Server} from 'socket.io';
|
const io = require('socket.io');
|
||||||
import express from 'express';
|
const app = require('express')();
|
||||||
|
|
||||||
const app = express();
|
|
||||||
|
|
||||||
const port = 1337;
|
const port = 1337;
|
||||||
const prefix = '/';
|
const prefix = '/';
|
||||||
|
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
const socket = new Server(server, {
|
const socket = io.listen(server, {
|
||||||
path: `${prefix}socket.io`,
|
path: `${prefix}/socket.io`
|
||||||
});
|
});
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
|
|
@ -93,8 +93,8 @@ const filePicker = {
|
||||||
data: {
|
data: {
|
||||||
FilePicker: {
|
FilePicker: {
|
||||||
key: 'key',
|
key: 'key',
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// override option from json/modules.json
|
// override option from json/modules.json
|
||||||
|
|
@ -109,11 +109,11 @@ const {
|
||||||
|
|
||||||
const configManager = createConfigManager({
|
const configManager = createConfigManager({
|
||||||
configPath,
|
configPath,
|
||||||
});
|
}),
|
||||||
|
|
||||||
app.use(prefix, cloudcmd({
|
app.use(prefix, cloudcmd({
|
||||||
socket, // used by Config, Edit (optional) and Console (required)
|
socket, // used by Config, Edit (optional) and Console (required)
|
||||||
config, // config data (optional)
|
config, // config data (optional)
|
||||||
modules, // optional
|
modules, // optional
|
||||||
configManager, // optional
|
configManager, // optional
|
||||||
}));
|
}));
|
||||||
|
|
@ -121,21 +121,12 @@ app.use(prefix, cloudcmd({
|
||||||
server.listen(port);
|
server.listen(port);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Docker
|
Docker
|
||||||
|
---------------
|
||||||
The docker images are provided for multiple architectures and types. The following list shows all existing images:
|
|
||||||
|
|
||||||
| Architecture | Type |
|
|
||||||
|----------------|--------------|
|
|
||||||
| amd64 | linux |
|
|
||||||
| arm64 (arm/v8) | linux |
|
|
||||||
| amd64 | linux-alpine |
|
|
||||||
| arm64 (arm/v8) | linux-alpine |
|
|
||||||
|
|
||||||
`Cloud Commander` could be used as a [docker container](https://hub.docker.com/r/coderaiser/cloudcmd/ "Docker container") this way:
|
`Cloud Commander` could be used as a [docker container](https://hub.docker.com/r/coderaiser/cloudcmd/ "Docker container") this way:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker run -it --rm -v ~:/root -v /:/mnt/fs -w=/root -p 8000:8000 coderaiser/cloudcmd
|
docker run -t --rm -v ~:/root -v /:/mnt/fs -w=/root -p 8000:8000 coderaiser/cloudcmd
|
||||||
```
|
```
|
||||||
|
|
||||||
Config would be read from home directory, hosts root file system would be mount to `/mnt/fs`,
|
Config would be read from home directory, hosts root file system would be mount to `/mnt/fs`,
|
||||||
|
|
@ -161,15 +152,11 @@ When you create this file run:
|
||||||
docker-compose up
|
docker-compose up
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
Get involved
|
||||||
|
---------------
|
||||||
More documentation you can find on https://cloudcmd.io/.
|
|
||||||
|
|
||||||
## Get involved
|
|
||||||
|
|
||||||
There is a lot ways to be involved in `Cloud Commander` development:
|
There is a lot ways to be involved in `Cloud Commander` development:
|
||||||
|
|
||||||
- support project on patreon: https://patreon.com/coderaiser;
|
|
||||||
- if you find a bug or got idea to share [create an issue](https://github.com/coderaiser/cloudcmd/issues/new "Create issue");
|
- if you find a bug or got idea to share [create an issue](https://github.com/coderaiser/cloudcmd/issues/new "Create issue");
|
||||||
- if you fixed a bug, typo or implemented new feature [create pull request](https://github.com/coderaiser/cloudcmd/compare "Create pull request");
|
- if you fixed a bug, typo or implemented new feature [create pull request](https://github.com/coderaiser/cloudcmd/compare "Create pull request");
|
||||||
- if you know languages you can help with [site translations](https://github.com/coderaiser/cloudcmd/wiki "Cloud Commander community wiki");
|
- if you know languages you can help with [site translations](https://github.com/coderaiser/cloudcmd/wiki "Cloud Commander community wiki");
|
||||||
|
|
|
||||||
6
app.json
6
app.json
|
|
@ -11,7 +11,6 @@
|
||||||
"folder",
|
"folder",
|
||||||
"orthodox"
|
"orthodox"
|
||||||
],
|
],
|
||||||
"stack": "heroku-18",
|
|
||||||
"env": {
|
"env": {
|
||||||
"NPM_CONFIG_PRODUCTION": {
|
"NPM_CONFIG_PRODUCTION": {
|
||||||
"description": "Keep false to install devDependencies and build frontend",
|
"description": "Keep false to install devDependencies and build frontend",
|
||||||
|
|
@ -63,6 +62,11 @@
|
||||||
"value": "",
|
"value": "",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
|
"CLOUDCMD_CONFIG_DIALOG": {
|
||||||
|
"description": "show config dialog",
|
||||||
|
"value": "false",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
"CLOUDCMD_ONE_FILE_PANEL": {
|
"CLOUDCMD_ONE_FILE_PANEL": {
|
||||||
"description": "show one file panel",
|
"description": "show one file panel",
|
||||||
"value": "false",
|
"value": "false",
|
||||||
|
|
|
||||||
280
bin/cloudcmd.js
280
bin/cloudcmd.js
|
|
@ -1,48 +1,37 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import process from 'node:process';
|
'use strict';
|
||||||
import {promisify} from 'node:util';
|
|
||||||
import {tryToCatch} from 'try-to-catch';
|
|
||||||
import parse from 'yargs-parser';
|
|
||||||
import exit from '../server/exit.js';
|
|
||||||
import {createConfig, configPath} from '../server/config.js';
|
|
||||||
import * as env from '../server/env.js';
|
|
||||||
import prefixer from '../server/prefixer.js';
|
|
||||||
import * as validate from '../server/validate.js';
|
|
||||||
import Info from '../package.json' with {
|
|
||||||
type: 'json',
|
|
||||||
};
|
|
||||||
|
|
||||||
process.on('unhandledRejection', exit);
|
const Info = require('../package');
|
||||||
|
const DIR_SERVER = '../server/';
|
||||||
|
|
||||||
const isUndefined = (a) => typeof a === 'undefined';
|
const {promisify} = require('util');
|
||||||
|
const tryToCatch = require('try-to-catch');
|
||||||
|
|
||||||
const choose = (a, b) => {
|
const exit = require(DIR_SERVER + 'exit');
|
||||||
if (isUndefined(a))
|
const {
|
||||||
return b;
|
createConfig,
|
||||||
|
configPath,
|
||||||
return a;
|
} = require(DIR_SERVER + 'config');
|
||||||
};
|
|
||||||
|
|
||||||
const config = createConfig({
|
const config = createConfig({
|
||||||
configPath,
|
configPath,
|
||||||
});
|
});
|
||||||
|
|
||||||
const maybeRoot = (a) => {
|
const env = require(DIR_SERVER + 'env');
|
||||||
if (a === '.')
|
const prefixer = require(DIR_SERVER + '/prefixer');
|
||||||
return process.cwd();
|
|
||||||
|
const choose = (a, b) => {
|
||||||
|
if (a === undefined)
|
||||||
|
return b;
|
||||||
|
|
||||||
return a;
|
return a;
|
||||||
};
|
};
|
||||||
|
|
||||||
const yargsOptions = {
|
process.on('unhandledRejection', exit);
|
||||||
configuration: {
|
|
||||||
'strip-aliased': true,
|
const {argv} = process;
|
||||||
'strip-dashed': true,
|
const args = require('minimist')(argv.slice(2), {
|
||||||
},
|
|
||||||
coerce: {
|
|
||||||
root: maybeRoot,
|
|
||||||
},
|
|
||||||
string: [
|
string: [
|
||||||
'name',
|
'name',
|
||||||
'port',
|
'port',
|
||||||
|
|
@ -57,8 +46,6 @@ const yargsOptions = {
|
||||||
'terminal-path',
|
'terminal-path',
|
||||||
'terminal-command',
|
'terminal-command',
|
||||||
'columns',
|
'columns',
|
||||||
'menu',
|
|
||||||
'theme',
|
|
||||||
'import-url',
|
'import-url',
|
||||||
'import-token',
|
'import-token',
|
||||||
'export-token',
|
'export-token',
|
||||||
|
|
@ -73,7 +60,6 @@ const yargsOptions = {
|
||||||
'open',
|
'open',
|
||||||
'config-dialog',
|
'config-dialog',
|
||||||
'config-auth',
|
'config-auth',
|
||||||
'config-port',
|
|
||||||
'console',
|
'console',
|
||||||
'sync-console-path',
|
'sync-console-path',
|
||||||
'contact',
|
'contact',
|
||||||
|
|
@ -83,7 +69,6 @@ const yargsOptions = {
|
||||||
'confirm-copy',
|
'confirm-copy',
|
||||||
'confirm-move',
|
'confirm-move',
|
||||||
'show-config',
|
'show-config',
|
||||||
'show-dot-files',
|
|
||||||
'show-file-name',
|
'show-file-name',
|
||||||
'vim',
|
'vim',
|
||||||
'keys-panel',
|
'keys-panel',
|
||||||
|
|
@ -92,70 +77,65 @@ const yargsOptions = {
|
||||||
'import',
|
'import',
|
||||||
'import-listen',
|
'import-listen',
|
||||||
'log',
|
'log',
|
||||||
'zip',
|
|
||||||
'dropbox',
|
'dropbox',
|
||||||
],
|
],
|
||||||
default: {
|
default: {
|
||||||
'server': true,
|
'server' : true,
|
||||||
'name': choose(env.parse('name'), config('name')),
|
'name' : choose(env('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.parse('editor') || config('editor'),
|
'editor' : env('editor') || config('editor'),
|
||||||
'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('cloudcmd_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') || '',
|
'vim' : choose(env.bool('vim'), config('vim')),
|
||||||
'theme': env.parse('theme') || config('theme') || '',
|
'log' : config('log'),
|
||||||
'vim': choose(env.bool('vim'), config('vim')),
|
|
||||||
'log': choose(env.bool('log'), config('log')),
|
|
||||||
|
|
||||||
'import-url': env.parse('import_url') || config('importUrl'),
|
'import-url': env('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')),
|
||||||
|
|
||||||
'prefix-socket': config('prefixSocket'),
|
'prefix-socket': config('prefixSocket'),
|
||||||
'show-dot-files': choose(env.bool('show_dot_files'), config('showDotFiles')),
|
|
||||||
'show-file-name': choose(env.bool('show_file_name'), config('showFileName')),
|
'show-file-name': choose(env.bool('show_file_name'), config('showFileName')),
|
||||||
'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')),
|
||||||
'config-port': choose(env.bool('config_port'), config('configPort')),
|
'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.parse('import_token') || config('importToken'),
|
'import-token': env('import_token') || config('importToken'),
|
||||||
'export-token': env.parse('export_token') || config('exportToken'),
|
'export-token': env('export_token') || config('exportToken'),
|
||||||
|
|
||||||
'dropbox': config('dropbox'),
|
'dropbox': config('dropbox'),
|
||||||
'dropbox-token': config('dropboxToken') || '',
|
'dropbox-token': config('dropboxToken'),
|
||||||
},
|
},
|
||||||
alias: {
|
alias: {
|
||||||
version: 'v',
|
v: 'version',
|
||||||
help: 'h',
|
h: 'help',
|
||||||
password: 'p',
|
p: 'password',
|
||||||
online: 'o',
|
o: 'online',
|
||||||
username: 'u',
|
u: 'username',
|
||||||
save: 's',
|
s: 'save',
|
||||||
auth: 'a',
|
a: 'auth',
|
||||||
config: 'c',
|
c: 'config',
|
||||||
},
|
},
|
||||||
};
|
unknown: (cmd) => {
|
||||||
|
exit('\'%s\' is not a cloudcmd option. See \'cloudcmd --help\'.', cmd);
|
||||||
const {argv} = process;
|
},
|
||||||
const args = parse(argv.slice(2), yargsOptions);
|
});
|
||||||
|
|
||||||
if (args.version)
|
if (args.version)
|
||||||
version();
|
version();
|
||||||
|
|
@ -165,21 +145,8 @@ else
|
||||||
main();
|
main();
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const {validateArgs} = await import('@putout/cli-validate-args');
|
|
||||||
|
|
||||||
const error = await validateArgs(args, [
|
|
||||||
...yargsOptions.boolean,
|
|
||||||
...yargsOptions.string,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return exit(error);
|
|
||||||
|
|
||||||
if (args.repl)
|
if (args.repl)
|
||||||
await repl();
|
repl();
|
||||||
|
|
||||||
validate.columns(args.columns);
|
|
||||||
validate.theme(args.theme);
|
|
||||||
|
|
||||||
port(args.port);
|
port(args.port);
|
||||||
|
|
||||||
|
|
@ -189,41 +156,37 @@ async function main() {
|
||||||
config('open', args.open);
|
config('open', args.open);
|
||||||
config('username', args.username);
|
config('username', args.username);
|
||||||
config('console', args.console);
|
config('console', args.console);
|
||||||
config('syncConsolePath', args.syncConsolePath);
|
config('syncConsolePath', args['sync-console-path']);
|
||||||
config('showDotFiles', args.showDotFiles);
|
config('showFileName', args['show-file-name']);
|
||||||
config('showFileName', args.showFileName);
|
|
||||||
config('contact', args.contact);
|
config('contact', args.contact);
|
||||||
config('terminal', args.terminal);
|
config('terminal', args.terminal);
|
||||||
config('terminalPath', args.terminalPath);
|
config('terminalPath', args['terminal-path']);
|
||||||
config('terminalCommand', args.terminalCommand);
|
config('terminalCommand', args['terminal-command']);
|
||||||
config('terminalAutoRestart', args.terminalAutoRestart);
|
config('terminalAutoRestart', args['terminal-auto-restart']);
|
||||||
config('editor', args.editor);
|
config('editor', args.editor);
|
||||||
config('menu', args.menu);
|
|
||||||
config('prefix', prefixer(args.prefix));
|
config('prefix', prefixer(args.prefix));
|
||||||
config('prefixSocket', prefixer(args.prefixSocket));
|
config('prefixSocket', prefixer(args['prefix-socket']));
|
||||||
config('root', args.root || '/');
|
config('root', args.root || '/');
|
||||||
config('vim', args.vim);
|
config('vim', args.vim);
|
||||||
config('theme', args.theme);
|
|
||||||
config('columns', args.columns);
|
config('columns', args.columns);
|
||||||
config('log', args.log);
|
config('log', args.log);
|
||||||
config('confirmCopy', args.confirmCopy);
|
config('confirmCopy', args['confirm-copy']);
|
||||||
config('confirmMove', args.confirmMove);
|
config('confirmMove', args['confirm-move']);
|
||||||
config('oneFilePanel', args.oneFilePanel);
|
config('oneFilePanel', args['one-file-panel']);
|
||||||
config('configDialog', args.configDialog);
|
config('configDialog', args['config-dialog']);
|
||||||
config('configAuth', args.configAuth);
|
config('configAuth', args['config-auth']);
|
||||||
config('configPort', args.configPort);
|
config('keysPanel', args['keys-panel']);
|
||||||
config('keysPanel', args.keysPanel);
|
|
||||||
config('export', args.export);
|
config('export', args.export);
|
||||||
config('exportToken', args.exportToken);
|
config('exportToken', args['export-token']);
|
||||||
config('import', args.import);
|
config('import', args.import);
|
||||||
config('importToken', args.importToken);
|
config('importToken', args['import-token']);
|
||||||
config('importListen', args.importListen);
|
config('importListen', args['import-listen']);
|
||||||
config('importUrl', args.importUrl);
|
config('importUrl', args['import-url']);
|
||||||
|
|
||||||
config('dropbox', args.dropbox);
|
config('dropbox', args['dropbox']);
|
||||||
config('dropboxToken', args.dropboxToken);
|
config('dropboxToken', args['dropbox-token'] || '');
|
||||||
|
|
||||||
await readConfig(args.config);
|
readConfig(args.config);
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
root: config('root'),
|
root: config('root'),
|
||||||
|
|
@ -232,44 +195,42 @@ async function main() {
|
||||||
prefix: config('prefix'),
|
prefix: config('prefix'),
|
||||||
prefixSocket: config('prefixSocket'),
|
prefixSocket: config('prefixSocket'),
|
||||||
columns: config('columns'),
|
columns: config('columns'),
|
||||||
theme: config('theme'),
|
|
||||||
menu: config('menu'),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const password = env.parse('password') || args.password;
|
const password = env('password') || args.password;
|
||||||
|
|
||||||
if (password)
|
if (password)
|
||||||
config('password', await getPassword(password));
|
config('password', getPassword(password));
|
||||||
|
|
||||||
validateRoot(options.root, config);
|
validateRoot(options.root, config);
|
||||||
|
|
||||||
if (args.showConfig)
|
if (args['show-config'])
|
||||||
await showConfig();
|
showConfig();
|
||||||
|
|
||||||
const {distributeImport} = await import('../server/distribute/import.js');
|
const distribute = require('../server/distribute');
|
||||||
const importConfig = promisify(distributeImport);
|
const importConfig = promisify(distribute.import);
|
||||||
|
|
||||||
await start(options, config);
|
await importConfig(config);
|
||||||
|
|
||||||
if (args.save)
|
if (args.save)
|
||||||
config.write();
|
config.write();
|
||||||
|
|
||||||
|
start(options, config);
|
||||||
await tryToCatch(checkUpdate);
|
await tryToCatch(checkUpdate);
|
||||||
await importConfig(config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateRoot(root, config) {
|
function validateRoot(root, config) {
|
||||||
|
const validate = require(DIR_SERVER + 'validate');
|
||||||
validate.root(root, config);
|
validate.root(root, config);
|
||||||
|
|
||||||
if (root === '/')
|
if (root === '/')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (config('log'))
|
console.log(`root: ${root}`);
|
||||||
console.log(`root: ${root}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPassword(password) {
|
function getPassword(password) {
|
||||||
const {default: criton} = await import('criton');
|
const criton = require('criton');
|
||||||
return criton(password, config('algo'));
|
return criton(password, config('algo'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -277,11 +238,13 @@ function version() {
|
||||||
console.log('v' + Info.version);
|
console.log('v' + Info.version);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function start(options, config) {
|
function start(options, config) {
|
||||||
|
const SERVER = DIR_SERVER + 'server';
|
||||||
|
|
||||||
if (!args.server)
|
if (!args.server)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const {default: server} = await import('../server/server.js');
|
const server = require(SERVER);
|
||||||
server(options, config);
|
server(options, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -294,38 +257,38 @@ function port(arg) {
|
||||||
exit('cloudcmd --port: should be a number');
|
exit('cloudcmd --port: should be a number');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function showConfig() {
|
function showConfig() {
|
||||||
const {showConfig} = await import('../server/show-config.js');
|
const show = require('../server/show-config');
|
||||||
const data = showConfig(config('*'));
|
const data = show(config('*'));
|
||||||
|
|
||||||
console.log(data);
|
console.log(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function readConfig(name) {
|
function readConfig(name) {
|
||||||
if (!name)
|
if (!name)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const {default: forEachKey} = await import('for-each-key');
|
const fs = require('fs');
|
||||||
|
const tryCatch = require('try-catch');
|
||||||
|
const jju = require('jju');
|
||||||
|
const forEachKey = require('for-each-key');
|
||||||
|
|
||||||
const data = await import(name, {
|
const readjsonSync = (name) => jju.parse(fs.readFileSync(name, 'utf8'), {
|
||||||
with: {
|
mode: 'json',
|
||||||
type: 'json',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [error, data] = tryCatch(readjsonSync, name);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return exit(error.message);
|
||||||
|
|
||||||
forEachKey(config, data);
|
forEachKey(config, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function help() {
|
function help() {
|
||||||
const {default: bin} = await import('../json/help.json', {
|
const bin = require('../json/help');
|
||||||
with: {
|
const forEachKey = require('for-each-key');
|
||||||
type: 'json',
|
const currify = require('currify');
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const {default: forEachKey} = await import('for-each-key');
|
|
||||||
const {default: currify} = await import('currify');
|
|
||||||
|
|
||||||
const usage = 'Usage: cloudcmd [options]';
|
const usage = 'Usage: cloudcmd [options]';
|
||||||
const url = Info.homepage;
|
const url = Info.homepage;
|
||||||
const log = currify((a, b, c) => console.log(a, b, c));
|
const log = currify((a, b, c) => console.log(a, b, c));
|
||||||
|
|
@ -336,27 +299,28 @@ async function help() {
|
||||||
console.log('\nGeneral help using Cloud Commander: <%s>', url);
|
console.log('\nGeneral help using Cloud Commander: <%s>', url);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function repl() {
|
function repl() {
|
||||||
console.log('REPL mode enabled (telnet localhost 1337)');
|
console.log('REPL mode enabled (telnet localhost 1337)');
|
||||||
await import('../server/repl.js');
|
require(DIR_SERVER + 'repl');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkUpdate() {
|
async function checkUpdate() {
|
||||||
const {default: load} = await import('package-json');
|
const load = require('package-json');
|
||||||
const {version} = await load(Info.name, 'latest');
|
|
||||||
|
|
||||||
await showUpdateInfo(version);
|
const {version} = await load(Info.name, 'latest');
|
||||||
|
showUpdateInfo(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function showUpdateInfo(version) {
|
function showUpdateInfo(version) {
|
||||||
if (version === Info.version)
|
if (version === Info.version)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const {default: chalk} = await import('chalk');
|
const chalk = require('chalk');
|
||||||
|
|
||||||
const latestVersion = chalk.green.bold(`v${version}`);
|
const latestVersion = chalk.green.bold('v' + version);
|
||||||
const latest = `update available: ${latestVersion}`;
|
const latest = `update available: ${latestVersion}`;
|
||||||
const current = chalk.dim(`(current: v${Info.version})`);
|
const current = chalk.dim(`(current: v${Info.version})`);
|
||||||
|
|
||||||
console.log('%s %s', latest, current);
|
console.log('%s %s', latest, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
import {spawnSync} from 'node:child_process';
|
|
||||||
import {test} from 'supertape';
|
|
||||||
import info from '../package.json' with {
|
|
||||||
type: 'json',
|
|
||||||
};
|
|
||||||
|
|
||||||
const cliPath = new URL('cloudcmd.js', import.meta.url).pathname;
|
|
||||||
|
|
||||||
test('cloudcmd: bin: cli: -h', (t) => {
|
|
||||||
const {stdout} = spawnSync(cliPath, ['-h'], {
|
|
||||||
encoding: 'utf8',
|
|
||||||
});
|
|
||||||
|
|
||||||
t.match(stdout, `Options`);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: bin: cli: -v', (t) => {
|
|
||||||
const {version} = info;
|
|
||||||
const {stdout} = spawnSync(cliPath, ['-v'], {
|
|
||||||
encoding: 'utf8',
|
|
||||||
});
|
|
||||||
|
|
||||||
t.match(stdout, `v${version}`);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
@ -1,72 +1,67 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import {promisify} from 'node:util';
|
'use strict';
|
||||||
import process from 'node:process';
|
|
||||||
import {tryToCatch} from 'try-to-catch';
|
|
||||||
import minor from 'minor';
|
|
||||||
import _place from 'place';
|
|
||||||
import {rendy} from 'rendy';
|
|
||||||
import shortdate from 'shortdate';
|
|
||||||
import Info from '../package.json' with {
|
|
||||||
type: 'json',
|
|
||||||
};
|
|
||||||
|
|
||||||
const place = promisify(_place);
|
const DIR = '../';
|
||||||
|
const Info = require(DIR + 'package');
|
||||||
|
|
||||||
await main();
|
const minor = require('minor');
|
||||||
|
const place = require('place');
|
||||||
|
const rendy = require('rendy');
|
||||||
|
const shortdate = require('shortdate');
|
||||||
|
|
||||||
async function main() {
|
const ERROR = Error('ERROR: version is missing. release --patch|--minor|--major');
|
||||||
const history = '## Version history\n\n';
|
|
||||||
|
main((error) => {
|
||||||
|
if (error)
|
||||||
|
console.error(error.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
function main(callback) {
|
||||||
|
const history = 'Version history\n---------------\n';
|
||||||
const link = '//github.com/coderaiser/cloudcmd/releases/tag/';
|
const link = '//github.com/coderaiser/cloudcmd/releases/tag/';
|
||||||
|
const template = '- *{{ date }}*, ' +
|
||||||
const template = '- ' +
|
'**[v{{ version }}]' +
|
||||||
'*{{ date }}*, ' +
|
'(' + link + 'v{{ version }})**\n';
|
||||||
'**[v{{ version }}]' +
|
|
||||||
'(' +
|
|
||||||
link +
|
|
||||||
'v{{ version }})**\n';
|
|
||||||
|
|
||||||
const {version} = Info;
|
const {version} = Info;
|
||||||
|
|
||||||
const [error, versionNew] = await tryToCatch(cl);
|
cl((error, versionNew) => {
|
||||||
|
if (error)
|
||||||
if (error)
|
return callback(error);
|
||||||
return console.error(error);
|
|
||||||
|
replaceVersion('README.md', version, versionNew, callback);
|
||||||
await replaceVersion('README.md', version, versionNew);
|
replaceVersion('HELP.md', version, versionNew, () => {
|
||||||
await replaceVersion('HELP.md', version, versionNew);
|
const historyNew = history + rendy(template, {
|
||||||
|
date : shortdate(),
|
||||||
const historyNew = history +
|
version : versionNew,
|
||||||
rendy(template, {
|
});
|
||||||
date: shortdate(),
|
|
||||||
version: versionNew,
|
replaceVersion('HELP.md', history, historyNew, callback);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
await replaceVersion('HELP.md', history, historyNew);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function replaceVersion(name, version, versionNew) {
|
function replaceVersion(name, version, versionNew, callback) {
|
||||||
const [error] = await tryToCatch(place, name, version, versionNew);
|
place(name, version, versionNew, (error) => {
|
||||||
|
if (error)
|
||||||
if (error)
|
return callback(error);
|
||||||
return console.error(error);
|
|
||||||
|
callback(null, 'done: ' + name);
|
||||||
console.log(`done: ${name}`);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function cl() {
|
function cl(callback) {
|
||||||
const {argv} = process;
|
const {argv} = process;
|
||||||
const length = argv.length - 1;
|
const length = argv.length - 1;
|
||||||
const last = process.argv[length];
|
const last = process.argv[length];
|
||||||
const regExp = /^--(major|minor|patch)$/;
|
const regExp = /^--(major|minor|patch)$/;
|
||||||
const [, match] = last.match(regExp) || [];
|
const [, match] = last.match(regExp) || [];
|
||||||
|
|
||||||
console.log(last);
|
|
||||||
|
|
||||||
if (!regExp.test(last))
|
if (!regExp.test(last))
|
||||||
throw Error('ERROR: version is missing. release --patch|--minor|--major');
|
return callback(ERROR);
|
||||||
|
|
||||||
return getVersionNew(last, match);
|
callback(null, getVersionNew(last, match));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVersionNew(last, match) {
|
function getVersionNew(last, match) {
|
||||||
|
|
@ -75,3 +70,4 @@ function getVersionNew(last, match) {
|
||||||
|
|
||||||
return last.substr(3);
|
return last.substr(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
195
client/client.js
195
client/client.js
|
|
@ -1,59 +1,74 @@
|
||||||
import process from 'node:process';
|
'use strict';
|
||||||
import Emitify from 'emitify';
|
|
||||||
import inherits from 'inherits';
|
/* global DOM */
|
||||||
import {rendy} from 'rendy';
|
|
||||||
import load from 'load.js';
|
const Emitify = require('emitify');
|
||||||
import {tryToCatch} from 'try-to-catch';
|
const inherits = require('inherits');
|
||||||
import {addSlashToEnd} from 'format-io';
|
const rendy = require('rendy');
|
||||||
import pascalCase from 'just-pascal-case';
|
const load = require('load.js');
|
||||||
import currify from 'currify';
|
const tryToCatch = require('try-to-catch');
|
||||||
import {
|
|
||||||
apiURL,
|
const pascalCase = require('just-pascal-case');
|
||||||
formatMsg,
|
const isDev = process.env.NODE_ENV === 'development';
|
||||||
buildFromJSON,
|
|
||||||
} from '#common/cloudfunc';
|
const Images = require('./dom/images');
|
||||||
import * as Images from '#dom/images';
|
const {unregisterSW} = require('./sw/register');
|
||||||
import {unregisterSW} from './sw/register.js';
|
const getJsonFromFileTable = require('./get-json-from-file-table');
|
||||||
import {getJsonFromFileTable} from './get-json-from-file-table.js';
|
|
||||||
import {Key} from './key/index.js';
|
const currify = require('currify');
|
||||||
import {loadModule} from './load-module.js';
|
|
||||||
|
|
||||||
const noJS = (a) => a.replace(/.js$/, '');
|
const noJS = (a) => a.replace(/.js$/, '');
|
||||||
|
|
||||||
const isDev = process.env.NODE_ENV === 'development';
|
const {
|
||||||
|
apiURL,
|
||||||
|
formatMsg,
|
||||||
|
buildFromJSON,
|
||||||
|
} = require('../common/cloudfunc');
|
||||||
|
|
||||||
|
const loadModule = require('./load-module');
|
||||||
|
|
||||||
inherits(CloudCmdProto, Emitify);
|
inherits(CloudCmdProto, Emitify);
|
||||||
|
|
||||||
export const createCloudCmd = ({DOM, Listeners}) => {
|
module.exports = new CloudCmdProto(DOM);
|
||||||
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, Listeners}) {
|
function CloudCmdProto(DOM) {
|
||||||
|
let Listeners;
|
||||||
|
|
||||||
|
const log = (...a) => {
|
||||||
|
if (!isDev )
|
||||||
|
return;
|
||||||
|
|
||||||
|
console.log(...a);
|
||||||
|
};
|
||||||
|
|
||||||
Emitify.call(this);
|
Emitify.call(this);
|
||||||
|
|
||||||
const CloudCmd = this;
|
const CloudCmd = this;
|
||||||
const Info = DOM.CurrentInfo;
|
const Info = DOM.CurrentInfo;
|
||||||
|
|
||||||
const {Storage, Files} = DOM;
|
const {
|
||||||
|
Storage,
|
||||||
|
Files,
|
||||||
|
} = DOM;
|
||||||
|
|
||||||
this.log = () => {
|
this.log = log;
|
||||||
if (!isDev)
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
this.prefix = '';
|
this.prefix = '';
|
||||||
this.prefixSocket = '';
|
this.prefixSocket = '';
|
||||||
this.prefixURL = '';
|
this.prefixURL = '';
|
||||||
|
this.DIRCLIENT = '/dist/';
|
||||||
|
this.DIRCLIENT_MODULES = this.DIRCLIENT + 'modules/';
|
||||||
|
|
||||||
this.MIN_ONE_PANEL_WIDTH = DOM.getCSSVar('min-one-panel-width');
|
this.MIN_ONE_PANEL_WIDTH = 1155;
|
||||||
this.HOST = location.origin || location.protocol + '//' + location.host;
|
this.HOST = location.origin || location.protocol + '//' + location.host;
|
||||||
|
|
||||||
|
const TITLE = 'Cloud Commander';
|
||||||
|
this.TITLE = TITLE;
|
||||||
|
|
||||||
this.sort = {
|
this.sort = {
|
||||||
left: 'name',
|
left: 'name',
|
||||||
right: 'name',
|
right: 'name',
|
||||||
|
|
@ -64,16 +79,28 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
right: 'asc',
|
right: 'asc',
|
||||||
};
|
};
|
||||||
|
|
||||||
this.changeDir = async (path, overrides = {}) => {
|
/**
|
||||||
|
* Функция привязываеться ко всем ссылкам и
|
||||||
|
* загружает содержимое каталогов
|
||||||
|
*
|
||||||
|
* @param params - {
|
||||||
|
* paramLink - ссылка
|
||||||
|
* needRefresh - необходимость обязательной загрузки данных с сервера
|
||||||
|
* panel
|
||||||
|
* }
|
||||||
|
* @param callback
|
||||||
|
*/
|
||||||
|
this.loadDir = async (params) => {
|
||||||
|
const p = params;
|
||||||
|
const refresh = p.isRefresh;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isRefresh,
|
|
||||||
panel,
|
panel,
|
||||||
history = true,
|
history,
|
||||||
noCurrent,
|
noCurrent,
|
||||||
currentName,
|
currentName,
|
||||||
} = overrides;
|
} = p;
|
||||||
|
|
||||||
const refresh = isRefresh;
|
|
||||||
let panelChanged;
|
let panelChanged;
|
||||||
|
|
||||||
if (!noCurrent && panel && panel !== Info.panel) {
|
if (!noCurrent && panel && panel !== Info.panel) {
|
||||||
|
|
@ -89,12 +116,11 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
Images.show.load(imgPosition, panel);
|
Images.show.load(imgPosition, panel);
|
||||||
|
|
||||||
/* загружаем содержимое каталога */
|
/* загружаем содержимое каталога */
|
||||||
await ajaxLoad(addSlashToEnd(path), {
|
await ajaxLoad(p.path, {
|
||||||
refresh,
|
refresh,
|
||||||
history,
|
history,
|
||||||
noCurrent,
|
noCurrent,
|
||||||
currentName,
|
currentName,
|
||||||
showDotFiles: CloudCmd.config('showDotFiles'),
|
|
||||||
}, panel);
|
}, panel);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -107,8 +133,6 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
CloudCmd.prefix = prefix;
|
CloudCmd.prefix = prefix;
|
||||||
CloudCmd.prefixURL = `${prefix}${apiURL}`;
|
CloudCmd.prefixURL = `${prefix}${apiURL}`;
|
||||||
CloudCmd.prefixSocket = config.prefixSocket;
|
CloudCmd.prefixSocket = config.prefixSocket;
|
||||||
CloudCmd.DIR_DIST = `${prefix}/dist`;
|
|
||||||
CloudCmd.DIR_MODULES = `${this.DIR_DIST}/modules`;
|
|
||||||
|
|
||||||
CloudCmd.config = (key) => config[key];
|
CloudCmd.config = (key) => config[key];
|
||||||
CloudCmd.config.if = currify((key, fn, a) => config[key] && fn(a));
|
CloudCmd.config.if = currify((key, fn, a) => config[key] && fn(a));
|
||||||
|
|
@ -117,6 +141,7 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
* should be called from config.js only
|
* should be called from config.js only
|
||||||
* after successful update on server
|
* after successful update on server
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (key === 'password')
|
if (key === 'password')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -127,14 +152,22 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
CloudCmd.MIN_ONE_PANEL_WIDTH = Infinity;
|
CloudCmd.MIN_ONE_PANEL_WIDTH = Infinity;
|
||||||
|
|
||||||
if (!document.body.scrollIntoViewIfNeeded)
|
if (!document.body.scrollIntoViewIfNeeded)
|
||||||
await load.js(`${CloudCmd.DIR_MODULES}/polyfill.js`);
|
await load.js(prefix + CloudCmd.DIRCLIENT_MODULES + 'polyfill.js');
|
||||||
|
|
||||||
await initModules();
|
await initModules();
|
||||||
await baseInit();
|
await baseInit();
|
||||||
|
await loadStyle();
|
||||||
|
|
||||||
CloudCmd.route(location.hash);
|
CloudCmd.route(location.hash);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function loadStyle() {
|
||||||
|
const {prefix} = CloudCmd;
|
||||||
|
const name = prefix + '/dist/cloudcmd.common.css';
|
||||||
|
|
||||||
|
await load.css(name);
|
||||||
|
}
|
||||||
|
|
||||||
this.route = (path) => {
|
this.route = (path) => {
|
||||||
const query = path.split('/');
|
const query = path.split('/');
|
||||||
|
|
||||||
|
|
@ -144,13 +177,12 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
const [kebabModule] = query;
|
const [kebabModule] = query;
|
||||||
const module = noJS(pascalCase(kebabModule.slice(1)));
|
const module = noJS(pascalCase(kebabModule.slice(1)));
|
||||||
|
|
||||||
const [, file] = query;
|
const file = query[1];
|
||||||
const current = DOM.getCurrentByName(file);
|
const current = DOM.getCurrentByName(file);
|
||||||
|
|
||||||
if (file && !current) {
|
if (file && !current) {
|
||||||
const msg = formatMsg('set current file', file, 'error');
|
const msg = formatMsg('set current file', file, 'error');
|
||||||
CloudCmd.log(msg);
|
CloudCmd.log(msg);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,7 +204,7 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const initModules = async () => {
|
const initModules = async () => {
|
||||||
CloudCmd.Key = Key;
|
CloudCmd.Key = new CloudCmd.Key();
|
||||||
CloudCmd.Key.bind();
|
CloudCmd.Key.bind();
|
||||||
|
|
||||||
const [, modules] = await tryToCatch(Files.get, 'modules');
|
const [, modules] = await tryToCatch(Files.get, 'modules');
|
||||||
|
|
@ -222,7 +254,7 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
});
|
});
|
||||||
|
|
||||||
const dirPath = DOM.getCurrentDirPath();
|
const dirPath = DOM.getCurrentDirPath();
|
||||||
|
Listeners = CloudCmd.Listeners;
|
||||||
Listeners.init();
|
Listeners.init();
|
||||||
|
|
||||||
const panels = getPanels();
|
const panels = getPanels();
|
||||||
|
|
@ -236,7 +268,7 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
const data = await Storage.get(dirPath);
|
const data = await Storage.get(dirPath);
|
||||||
|
|
||||||
if (!data)
|
if (!data)
|
||||||
await Storage.setJson(dirPath, getJsonFromFileTable());
|
await Storage.set(dirPath, getJsonFromFileTable());
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPanels() {
|
function getPanels() {
|
||||||
|
|
@ -259,15 +291,19 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.refresh = async (options = {}) => {
|
this.refresh = async (options = {}) => {
|
||||||
const {panel = Info.panel, currentName} = options;
|
const {
|
||||||
|
panel = Info.panel,
|
||||||
|
currentName,
|
||||||
|
} = options;
|
||||||
|
|
||||||
const path = DOM.getCurrentDirPath(panel);
|
const path = DOM.getCurrentDirPath(panel);
|
||||||
|
|
||||||
const isRefresh = true;
|
const isRefresh = true;
|
||||||
const history = false;
|
const history = false;
|
||||||
const noCurrent = options?.noCurrent;
|
const noCurrent = options ? options.noCurrent : false;
|
||||||
|
|
||||||
await CloudCmd.changeDir(path, {
|
await CloudCmd.loadDir({
|
||||||
|
path,
|
||||||
isRefresh,
|
isRefresh,
|
||||||
history,
|
history,
|
||||||
panel,
|
panel,
|
||||||
|
|
@ -283,18 +319,22 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
* @param options
|
* @param options
|
||||||
* { refresh, history } - необходимость обновить данные о каталоге
|
* { refresh, history } - необходимость обновить данные о каталоге
|
||||||
* @param panel
|
* @param panel
|
||||||
|
* @param callback
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
async function ajaxLoad(path, options = {}, panel) {
|
async function ajaxLoad(path, options = {}, panel) {
|
||||||
const {RESTful} = DOM;
|
const {RESTful} = DOM;
|
||||||
|
|
||||||
CloudCmd.log(`reading dir: "${path}";`);
|
CloudCmd.log('reading dir: "' + path + '";');
|
||||||
|
|
||||||
const dirStorage = CloudCmd.config('dirStorage');
|
const dirStorage = CloudCmd.config('dirStorage');
|
||||||
const json = dirStorage && await Storage.getJson(path);
|
const json = dirStorage && await Storage.get(path);
|
||||||
|
|
||||||
const name = options.currentName || Info.name;
|
const name = options.currentName || Info.name;
|
||||||
const {noCurrent, refresh} = options;
|
const {
|
||||||
|
noCurrent,
|
||||||
|
refresh,
|
||||||
|
} = options;
|
||||||
|
|
||||||
if (!refresh && json)
|
if (!refresh && json)
|
||||||
return await createFileTable(json, panel, options);
|
return await createFileTable(json, panel, options);
|
||||||
|
|
@ -311,8 +351,7 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
const [, newObj] = await RESTful.read(path + query, 'json');
|
const [, newObj] = await RESTful.read(path + query, 'json');
|
||||||
|
|
||||||
if (!newObj)
|
if (!newObj)
|
||||||
// that's OK, error handled by RESTful
|
return; // that's OK, error handled by RESTful
|
||||||
return;
|
|
||||||
|
|
||||||
options.sort = sort;
|
options.sort = sort;
|
||||||
options.order = order;
|
options.order = order;
|
||||||
|
|
@ -325,35 +364,28 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
if (!CloudCmd.config('dirStorage'))
|
if (!CloudCmd.config('dirStorage'))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Storage.setJson(path, newObj);
|
Storage.set(path, newObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Функция строит файловую таблицу
|
* Функция строит файловую таблицу
|
||||||
* @param data - данные о файлах
|
* @param json - данные о файлах
|
||||||
* @param panelParam
|
* @param panelParam
|
||||||
* @param options - history, noCurrent, showDotFiles
|
* @param history
|
||||||
|
* @param callback
|
||||||
*/
|
*/
|
||||||
async function createFileTable(data, panelParam, options) {
|
async function createFileTable(data, panelParam, options) {
|
||||||
const {
|
const {
|
||||||
history,
|
history,
|
||||||
noCurrent,
|
noCurrent,
|
||||||
showDotFiles,
|
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
const names = [
|
const names = ['file', 'path', 'link', 'pathLink'];
|
||||||
'file',
|
|
||||||
'path',
|
|
||||||
'link',
|
|
||||||
'pathLink',
|
|
||||||
];
|
|
||||||
|
|
||||||
const [error, [
|
const [
|
||||||
file,
|
error,
|
||||||
path,
|
[file, path, link, pathLink],
|
||||||
link,
|
] = await tryToCatch(Files.get, names);
|
||||||
pathLink,
|
|
||||||
]] = await tryToCatch(Files.get, names);
|
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
return DOM.Dialog.alert(error.responseText);
|
return DOM.Dialog.alert(error.responseText);
|
||||||
|
|
@ -361,7 +393,10 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
const panel = panelParam || DOM.getPanel();
|
const panel = panelParam || DOM.getPanel();
|
||||||
const {prefix} = CloudCmd;
|
const {prefix} = CloudCmd;
|
||||||
|
|
||||||
const {dir, name} = Info;
|
const {
|
||||||
|
dir,
|
||||||
|
name,
|
||||||
|
} = Info;
|
||||||
|
|
||||||
const {childNodes} = panel;
|
const {childNodes} = panel;
|
||||||
let i = childNodes.length;
|
let i = childNodes.length;
|
||||||
|
|
@ -370,13 +405,12 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
panel.removeChild(panel.lastChild);
|
panel.removeChild(panel.lastChild);
|
||||||
|
|
||||||
panel.innerHTML = buildFromJSON({
|
panel.innerHTML = buildFromJSON({
|
||||||
sort: options.sort,
|
sort : options.sort,
|
||||||
order: options.order,
|
order : options.order,
|
||||||
data,
|
data,
|
||||||
id: panel.id,
|
id : panel.id,
|
||||||
prefix,
|
prefix,
|
||||||
showDotFiles,
|
template : {
|
||||||
template: {
|
|
||||||
file,
|
file,
|
||||||
path,
|
path,
|
||||||
pathLink,
|
pathLink,
|
||||||
|
|
@ -416,7 +450,9 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
|
|
||||||
const path = parentDirPath;
|
const path = parentDirPath;
|
||||||
|
|
||||||
await CloudCmd.changeDir(path);
|
await CloudCmd.loadDir({
|
||||||
|
path,
|
||||||
|
});
|
||||||
|
|
||||||
const current = DOM.getCurrentByName(dir);
|
const current = DOM.getCurrentByName(dir);
|
||||||
const [first] = DOM.getFiles(panel);
|
const [first] = DOM.getFiles(panel);
|
||||||
|
|
@ -426,3 +462,4 @@ function CloudCmdProto({DOM, Listeners}) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,42 @@
|
||||||
import '../css/main.css';
|
'use strict';
|
||||||
import process from 'node:process';
|
|
||||||
import wraptile from 'wraptile';
|
require('../css/main.css');
|
||||||
import load from 'load.js';
|
require('../css/nojs.css');
|
||||||
import * as Util from '#common/util';
|
require('../css/columns/name-size-date.css');
|
||||||
import * as CloudFunc from '#common/cloudfunc';
|
require('../css/columns/name-size.css');
|
||||||
import DOM from '#dom';
|
|
||||||
import {registerSW, listenSW} from './sw/register.js';
|
const wraptile = require('wraptile');
|
||||||
import {initSortPanel, sortPanel} from './sort.js';
|
const load = require('load.js');
|
||||||
import {createCloudCmd} from './client.js';
|
|
||||||
import * as Listeners from './listeners/index.js';
|
|
||||||
|
|
||||||
const isDev = process.env.NODE_ENV === 'development';
|
const isDev = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
export default init;
|
const {
|
||||||
|
registerSW,
|
||||||
|
listenSW,
|
||||||
|
} = require('./sw/register');
|
||||||
|
|
||||||
globalThis.CloudCmd = init;
|
// prevent additional loading of emitify
|
||||||
|
window.Emitify = require('emitify');
|
||||||
|
|
||||||
async function init(config) {
|
module.exports = window.CloudCmd = (config) => {
|
||||||
globalThis.CloudCmd = createCloudCmd({
|
window.Util = require('../common/util');
|
||||||
DOM,
|
window.CloudFunc = require('../common/cloudfunc');
|
||||||
Listeners,
|
|
||||||
});
|
|
||||||
globalThis.DOM = DOM;
|
|
||||||
globalThis.Util = Util;
|
|
||||||
globalThis.CloudFunc = CloudFunc;
|
|
||||||
|
|
||||||
await register(config);
|
const DOM = require('./dom');
|
||||||
|
|
||||||
|
window.DOM = DOM;
|
||||||
|
window.CloudCmd = require('./client');
|
||||||
|
|
||||||
|
register(config);
|
||||||
|
|
||||||
|
require('./listeners');
|
||||||
|
require('./key');
|
||||||
|
require('./sort');
|
||||||
|
|
||||||
initSortPanel();
|
|
||||||
globalThis.CloudCmd.sortPanel = sortPanel;
|
|
||||||
const prefix = getPrefix(config.prefix);
|
const prefix = getPrefix(config.prefix);
|
||||||
|
|
||||||
globalThis.CloudCmd.init(prefix, config);
|
window.CloudCmd.init(prefix, config);
|
||||||
|
};
|
||||||
if (globalThis.CloudCmd.config('menu') === 'aleman')
|
|
||||||
setTimeout(() => {
|
|
||||||
import('https://esm.sh/@putout/processor-html');
|
|
||||||
import('https://esm.sh/@putout/bundle@5.5');
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPrefix(prefix) {
|
function getPrefix(prefix) {
|
||||||
if (!prefix)
|
if (!prefix)
|
||||||
|
|
@ -54,7 +52,7 @@ const onUpdateFound = wraptile(async (config) => {
|
||||||
if (isDev)
|
if (isDev)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const {DOM} = globalThis;
|
const {DOM} = window;
|
||||||
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`);
|
||||||
|
|
@ -63,7 +61,7 @@ const onUpdateFound = wraptile(async (config) => {
|
||||||
console.log('cloudcmd: sw: updated');
|
console.log('cloudcmd: sw: updated');
|
||||||
|
|
||||||
DOM.Events.removeAll();
|
DOM.Events.removeAll();
|
||||||
globalThis.CloudCmd(config);
|
window.CloudCmd(config);
|
||||||
});
|
});
|
||||||
|
|
||||||
async function register(config) {
|
async function register(config) {
|
||||||
|
|
@ -72,3 +70,4 @@ async function register(config) {
|
||||||
|
|
||||||
listenSW(sw, 'updatefound', onUpdateFound(config));
|
listenSW(sw, 'updatefound', onUpdateFound(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,124 +1,137 @@
|
||||||
/* global CloudCmd*/
|
'use strict';
|
||||||
import * as Storage from '#dom/storage';
|
|
||||||
import tryToPromiseAll from '../../common/try-to-promise-all.js';
|
|
||||||
|
|
||||||
const CLASS = 'cut-file';
|
/* global CloudCmd */
|
||||||
const COPY = 'copy';
|
|
||||||
const CUT = 'cut';
|
|
||||||
|
|
||||||
function showMessage(msg) {
|
const tryToPromiseAll = require('../../common/try-to-promise-all');
|
||||||
globalThis.DOM.Dialog.alert(msg);
|
const Storage = require('./storage');
|
||||||
}
|
const DOM = require('./');
|
||||||
|
|
||||||
function getNames() {
|
module.exports = new BufferProto();
|
||||||
const {DOM} = globalThis;
|
|
||||||
const files = DOM.getActiveFiles();
|
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),
|
||||||
|
};
|
||||||
|
|
||||||
return DOM.getFilenames(files);
|
function showMessage(msg) {
|
||||||
}
|
DOM.Dialog.alert(msg);
|
||||||
|
|
||||||
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) {
|
function getNames() {
|
||||||
element.classList.remove(CLASS);
|
const files = DOM.getActiveFiles();
|
||||||
|
const names = DOM.getFilenames(files);
|
||||||
|
|
||||||
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.get(COPY),
|
||||||
|
Storage.get(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.set(COPY, {
|
||||||
|
from,
|
||||||
|
names,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cut() {
|
||||||
|
const names = getNames();
|
||||||
|
const from = Info.dirPath;
|
||||||
|
|
||||||
|
await clear();
|
||||||
|
|
||||||
|
if (!names.length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
addCutClass();
|
||||||
|
|
||||||
|
await Storage.set(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;
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
|
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
/* global DOM */
|
|
||||||
const SELECTED_FILE = 'selected-file';
|
|
||||||
const Cmd = {
|
|
||||||
getSelectedFiles,
|
|
||||||
isSelected,
|
|
||||||
unselectFile,
|
|
||||||
selectFile,
|
|
||||||
selectAllFiles,
|
|
||||||
toggleSelectedFile,
|
|
||||||
toggleAllSelectedFiles,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* selected file check
|
|
||||||
*
|
|
||||||
* @param currentFile
|
|
||||||
*/
|
|
||||||
export function isSelected(currentFile) {
|
|
||||||
if (!currentFile)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return DOM.isContainClass(currentFile, SELECTED_FILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* select current file
|
|
||||||
* @param currentFile
|
|
||||||
*/
|
|
||||||
export function selectFile(currentFile) {
|
|
||||||
const current = currentFile || DOM.getCurrentFile();
|
|
||||||
|
|
||||||
current.classList.add(SELECTED_FILE);
|
|
||||||
|
|
||||||
return Cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unselectFile(currentFile) {
|
|
||||||
const current = currentFile || DOM.getCurrentFile();
|
|
||||||
|
|
||||||
current.classList.remove(SELECTED_FILE);
|
|
||||||
|
|
||||||
return Cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function toggleSelectedFile(currentFile) {
|
|
||||||
const current = currentFile || DOM.getCurrentFile();
|
|
||||||
const name = DOM.getCurrentName(current);
|
|
||||||
|
|
||||||
if (name === '..')
|
|
||||||
return Cmd;
|
|
||||||
|
|
||||||
current.classList.toggle(SELECTED_FILE);
|
|
||||||
|
|
||||||
return Cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function toggleAllSelectedFiles() {
|
|
||||||
DOM
|
|
||||||
.getAllFiles()
|
|
||||||
.map(DOM.toggleSelectedFile);
|
|
||||||
|
|
||||||
return Cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function selectAllFiles() {
|
|
||||||
DOM
|
|
||||||
.getAllFiles()
|
|
||||||
.map(DOM.selectFile);
|
|
||||||
|
|
||||||
return Cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* unified way to get selected files
|
|
||||||
*
|
|
||||||
* @currentFile
|
|
||||||
*/
|
|
||||||
export function getSelectedFiles() {
|
|
||||||
const panel = DOM.getPanel();
|
|
||||||
const selected = DOM.getByClassAll(SELECTED_FILE, panel);
|
|
||||||
|
|
||||||
return Array.from(selected);
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +1,28 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
/* global DOM */
|
/* global DOM */
|
||||||
/* global CloudCmd */
|
/* global CloudCmd */
|
||||||
import createElement from '@cloudcmd/create-element';
|
|
||||||
import {getTitle, FS} from '#common/cloudfunc';
|
const btoa = require('../../common/btoa');
|
||||||
import {encode, decode} from '#common/entity';
|
const createElement = require('@cloudcmd/create-element');
|
||||||
|
|
||||||
|
const {
|
||||||
|
encode,
|
||||||
|
decode,
|
||||||
|
} = require('../../common/entity');
|
||||||
|
|
||||||
|
const {
|
||||||
|
getTitle,
|
||||||
|
FS,
|
||||||
|
} = require('../../common/cloudfunc');
|
||||||
|
|
||||||
let Title;
|
let Title;
|
||||||
|
|
||||||
const CURRENT_FILE = 'current-file';
|
const CURRENT_FILE = 'current-file';
|
||||||
const encodeNBSP = (a) => a?.replace('\xa0', ' ');
|
const NBSP_REG = RegExp(String.fromCharCode(160), 'g');
|
||||||
const decodeNBSP = (a) => a?.replace(' ', '\xa0');
|
const SPACE = ' ';
|
||||||
|
|
||||||
export const _CURRENT_FILE = CURRENT_FILE;
|
module.exports._CURRENT_FILE = CURRENT_FILE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set name from current (or param) file
|
* set name from current (or param) file
|
||||||
|
|
@ -18,7 +30,7 @@ export const _CURRENT_FILE = CURRENT_FILE;
|
||||||
* @param name
|
* @param name
|
||||||
* @param current
|
* @param current
|
||||||
*/
|
*/
|
||||||
export const setCurrentName = (name, current) => {
|
module.exports.setCurrentName = (name, current) => {
|
||||||
const Info = DOM.CurrentInfo;
|
const Info = DOM.CurrentInfo;
|
||||||
const {link} = Info;
|
const {link} = Info;
|
||||||
const {prefix} = CloudCmd;
|
const {prefix} = CloudCmd;
|
||||||
|
|
@ -29,7 +41,7 @@ export const setCurrentName = (name, current) => {
|
||||||
link.href = dir + encoded;
|
link.href = dir + encoded;
|
||||||
link.innerHTML = encoded;
|
link.innerHTML = encoded;
|
||||||
|
|
||||||
current.setAttribute('data-name', createNameAttribute(name));
|
current.setAttribute('data-name', 'js-file-' + btoa(encodeURI(name)));
|
||||||
CloudCmd.emit('current-file', current);
|
CloudCmd.emit('current-file', current);
|
||||||
|
|
||||||
return link;
|
return link;
|
||||||
|
|
@ -40,48 +52,28 @@ export const setCurrentName = (name, current) => {
|
||||||
*
|
*
|
||||||
* @param currentFile
|
* @param currentFile
|
||||||
*/
|
*/
|
||||||
export const getCurrentName = (currentFile) => {
|
module.exports.getCurrentName = (currentFile) => {
|
||||||
const current = currentFile || DOM.getCurrentFile();
|
const current = currentFile || DOM.getCurrentFile();
|
||||||
|
|
||||||
if (!current)
|
if (!current)
|
||||||
return '';
|
return '';
|
||||||
|
|
||||||
return parseNameAttribute(current.getAttribute('data-name'));
|
const link = DOM.getCurrentLink(current);
|
||||||
|
|
||||||
|
if (!link)
|
||||||
|
return '';
|
||||||
|
|
||||||
|
return decode(link.title)
|
||||||
|
.replace(NBSP_REG, SPACE);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a `data-name` attribute for the given filename
|
* get current direcotory path
|
||||||
* @param name The string name to encode
|
|
||||||
*/
|
*/
|
||||||
const createNameAttribute = (name) => {
|
module.exports.getCurrentDirPath = (panel = DOM.getPanel()) => {
|
||||||
const encoded = btoa(encodeURI(name));
|
|
||||||
return `js-file-${encoded}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a `data-name` attribute string back into the original filename
|
|
||||||
* @param attribute The string we wish to decode
|
|
||||||
*/
|
|
||||||
const parseNameAttribute = (attribute) => {
|
|
||||||
attribute = attribute.replace('js-file-', '');
|
|
||||||
return decodeNBSP(decodeURI(atob(attribute)));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const _parseNameAttribute = parseNameAttribute;
|
|
||||||
|
|
||||||
const parseHrefAttribute = (prefix, attribute) => {
|
|
||||||
attribute = attribute.replace(RegExp('^' + prefix + FS), '');
|
|
||||||
return decode(decodeNBSP(attribute));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const _parseHrefAttribute = parseHrefAttribute;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get current directory path
|
|
||||||
*/
|
|
||||||
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
|
||||||
|
.replace(NBSP_REG, SPACE);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -89,31 +81,36 @@ export const getCurrentDirPath = (panel = DOM.getPanel()) => {
|
||||||
*
|
*
|
||||||
* @param currentFile - current file by default
|
* @param currentFile - current file by default
|
||||||
*/
|
*/
|
||||||
export const getCurrentPath = (currentFile) => {
|
module.exports.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;
|
||||||
|
|
||||||
return parseHrefAttribute(prefix, element.getAttribute('href'));
|
const path = element
|
||||||
|
.getAttribute('href')
|
||||||
|
.replace(RegExp('^' + prefix + FS), '')
|
||||||
|
.replace(NBSP_REG, SPACE);
|
||||||
|
|
||||||
|
return decode(path);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get current directory name
|
* get current direcotory name
|
||||||
*/
|
*/
|
||||||
export const getCurrentDirName = () => {
|
module.exports.getCurrentDirName = () => {
|
||||||
const href = DOM
|
const href = DOM.getCurrentDirPath()
|
||||||
.getCurrentDirPath()
|
|
||||||
.replace(/\/$/, '');
|
.replace(/\/$/, '');
|
||||||
|
|
||||||
const substr = href.substr(href, href.lastIndexOf('/'));
|
const substr = href.substr(href, href.lastIndexOf('/'));
|
||||||
|
const ret = href.replace(substr + '/', '') || '/';
|
||||||
|
|
||||||
return href.replace(`${substr}/`, '') || '/';
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get current directory path
|
* get current direcotory path
|
||||||
*/
|
*/
|
||||||
export const getParentDirPath = (panel) => {
|
module.exports.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);
|
||||||
|
|
@ -125,9 +122,9 @@ export const getParentDirPath = (panel) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get not current directory path
|
* get not current direcotory path
|
||||||
*/
|
*/
|
||||||
export const getNotCurrentDirPath = () => {
|
module.exports.getNotCurrentDirPath = () => {
|
||||||
const panel = DOM.getPanel({
|
const panel = DOM.getPanel({
|
||||||
active: false,
|
active: false,
|
||||||
});
|
});
|
||||||
|
|
@ -140,20 +137,20 @@ export const getNotCurrentDirPath = () => {
|
||||||
*
|
*
|
||||||
* @currentFile
|
* @currentFile
|
||||||
*/
|
*/
|
||||||
export const getCurrentFile = () => {
|
module.exports.getCurrentFile = () => {
|
||||||
return DOM.getByClass(CURRENT_FILE);
|
return DOM.getByClass(CURRENT_FILE);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get current file by name
|
* get current file by name
|
||||||
*/
|
*/
|
||||||
export const getCurrentByName = (name, panel = DOM.CurrentInfo.panel) => {
|
module.exports.getCurrentByName = (name, panel = DOM.CurrentInfo.panel) => {
|
||||||
const dataName = 'js-file-' + btoa(encodeURI(encodeNBSP(name)));
|
const dataName = 'js-file-' + btoa(encodeURI(name));
|
||||||
return DOM.getByDataName(dataName, panel);
|
return DOM.getByDataName(dataName, panel);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* private function that unset currentFile
|
* private function thet unset currentfile
|
||||||
*
|
*
|
||||||
* @currentFile
|
* @currentFile
|
||||||
*/
|
*/
|
||||||
|
|
@ -169,7 +166,7 @@ function unsetCurrentFile(currentFile) {
|
||||||
/**
|
/**
|
||||||
* unified way to set current file
|
* unified way to set current file
|
||||||
*/
|
*/
|
||||||
export const setCurrentFile = (currentFile, options) => {
|
module.exports.setCurrentFile = (currentFile, options) => {
|
||||||
const o = options;
|
const o = options;
|
||||||
const currentFileWas = DOM.getCurrentFile();
|
const currentFileWas = DOM.getCurrentFile();
|
||||||
|
|
||||||
|
|
@ -198,7 +195,7 @@ export const setCurrentFile = (currentFile, options) => {
|
||||||
* but it should be false
|
* but it should be false
|
||||||
* to prevent default behavior
|
* to prevent default behavior
|
||||||
*/
|
*/
|
||||||
if (!o || o.history) {
|
if (!o || o.history !== false) {
|
||||||
const historyPath = path === '/' ? path : FS + path;
|
const historyPath = path === '/' ? path : FS + path;
|
||||||
DOM.setHistory(historyPath, null, historyPath);
|
DOM.setHistory(historyPath, null, historyPath);
|
||||||
}
|
}
|
||||||
|
|
@ -206,7 +203,6 @@ export const setCurrentFile = (currentFile, options) => {
|
||||||
|
|
||||||
/* scrolling to current file */
|
/* scrolling to current file */
|
||||||
const CENTER = true;
|
const CENTER = true;
|
||||||
|
|
||||||
DOM.scrollIntoViewIfNeeded(currentFile, CENTER);
|
DOM.scrollIntoViewIfNeeded(currentFile, CENTER);
|
||||||
|
|
||||||
CloudCmd.emit('current-file', currentFile);
|
CloudCmd.emit('current-file', currentFile);
|
||||||
|
|
@ -216,7 +212,7 @@ export const setCurrentFile = (currentFile, options) => {
|
||||||
return DOM;
|
return DOM;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setCurrentByName = (name) => {
|
this.setCurrentByName = (name) => {
|
||||||
const current = DOM.getCurrentByName(name);
|
const current = DOM.getCurrentByName(name);
|
||||||
return DOM.setCurrentFile(current);
|
return DOM.setCurrentFile(current);
|
||||||
};
|
};
|
||||||
|
|
@ -227,7 +223,7 @@ export const setCurrentByName = (name) => {
|
||||||
* @param layer - element
|
* @param layer - element
|
||||||
* @param - position {x, y}
|
* @param - position {x, y}
|
||||||
*/
|
*/
|
||||||
export const getCurrentByPosition = ({x, y}) => {
|
module.exports.getCurrentByPosition = ({x, y}) => {
|
||||||
const element = document.elementFromPoint(x, y);
|
const element = document.elementFromPoint(x, y);
|
||||||
|
|
||||||
const getEl = (el) => {
|
const getEl = (el) => {
|
||||||
|
|
@ -259,7 +255,7 @@ export const getCurrentByPosition = ({x, y}) => {
|
||||||
*
|
*
|
||||||
* @param currentFile
|
* @param currentFile
|
||||||
*/
|
*/
|
||||||
export const isCurrentFile = (currentFile) => {
|
module.exports.isCurrentFile = (currentFile) => {
|
||||||
if (!currentFile)
|
if (!currentFile)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -271,7 +267,8 @@ export const isCurrentFile = (currentFile) => {
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name
|
||||||
*/
|
*/
|
||||||
export const setTitle = (name) => {
|
|
||||||
|
module.exports.setTitle = (name) => {
|
||||||
if (!Title)
|
if (!Title)
|
||||||
Title = DOM.getByTag('title')[0] || createElement('title', {
|
Title = DOM.getByTag('title')[0] || createElement('title', {
|
||||||
innerHTML: name,
|
innerHTML: name,
|
||||||
|
|
@ -288,24 +285,20 @@ export const setTitle = (name) => {
|
||||||
*
|
*
|
||||||
* @param currentFile
|
* @param currentFile
|
||||||
*/
|
*/
|
||||||
export const isCurrentIsDir = (currentFile) => {
|
module.exports.isCurrentIsDir = (currentFile) => {
|
||||||
const current = currentFile || DOM.getCurrentFile();
|
const current = currentFile || DOM.getCurrentFile();
|
||||||
const path = DOM.getCurrentPath(current);
|
|
||||||
const fileType = DOM.getCurrentType(current);
|
const fileType = DOM.getCurrentType(current);
|
||||||
|
|
||||||
const isZip = path.endsWith('.zip');
|
return /^directory(-link)?/.test(fileType);
|
||||||
const isDir = /^directory(-link)?/.test(fileType);
|
|
||||||
|
|
||||||
return isDir || isZip;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getCurrentType = (currentFile) => {
|
module.exports.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
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.pop();
|
.pop();
|
||||||
|
|
||||||
return type;
|
return type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,72 +1,69 @@
|
||||||
import {test, stub} from 'supertape';
|
'use strict';
|
||||||
import {create} from 'auto-globals';
|
|
||||||
import wraptile from 'wraptile';
|
|
||||||
import * as currentFile from './current-file.js';
|
|
||||||
|
|
||||||
|
const test = require('supertape');
|
||||||
|
const {create} = require('auto-globals');
|
||||||
|
const stub = require('@cloudcmd/stub');
|
||||||
const id = (a) => a;
|
const id = (a) => a;
|
||||||
|
const wraptile = require('wraptile');
|
||||||
const returns = wraptile(id);
|
const returns = wraptile(id);
|
||||||
|
|
||||||
|
const currentFile = require('./current-file');
|
||||||
const {_CURRENT_FILE} = currentFile;
|
const {_CURRENT_FILE} = currentFile;
|
||||||
|
|
||||||
test('current-file: setCurrentName: setAttribute', (t) => {
|
test('current-file: setCurrentName: setAttribute', (t) => {
|
||||||
const {DOM, CloudCmd} = globalThis;
|
const {
|
||||||
|
DOM,
|
||||||
|
CloudCmd,
|
||||||
|
} = global;
|
||||||
|
|
||||||
globalThis.DOM = getDOM();
|
global.DOM = getDOM();
|
||||||
globalThis.CloudCmd = getCloudCmd();
|
global.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const current = create();
|
const current = create();
|
||||||
const {setAttribute} = current;
|
const {setAttribute} = current;
|
||||||
|
|
||||||
currentFile.setCurrentName('hello', current);
|
currentFile.setCurrentName('hello', current);
|
||||||
|
|
||||||
globalThis.DOM = DOM;
|
t.ok(setAttribute.calledWith('data-name', 'js-file-aGVsbG8='), 'should call setAttribute');
|
||||||
globalThis.CloudCmd = CloudCmd;
|
|
||||||
|
global.DOM = DOM;
|
||||||
|
global.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.calledWith(setAttribute, [
|
|
||||||
'data-name',
|
|
||||||
'js-file-aGVsbG8=',
|
|
||||||
], 'should call setAttribute');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: setCurrentName: setAttribute: cyrillic', (t) => {
|
test('current-file: setCurrentName: setAttribute: cyrillic', (t) => {
|
||||||
const {DOM, CloudCmd} = globalThis;
|
const {
|
||||||
|
DOM,
|
||||||
|
CloudCmd,
|
||||||
|
} = global;
|
||||||
|
|
||||||
globalThis.DOM = getDOM();
|
global.DOM = getDOM();
|
||||||
globalThis.CloudCmd = getCloudCmd();
|
global.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const current = create();
|
const current = create();
|
||||||
const {setAttribute} = current;
|
const {setAttribute} = current;
|
||||||
|
|
||||||
currentFile.setCurrentName('ай', current);
|
currentFile.setCurrentName('ай', current);
|
||||||
|
|
||||||
globalThis.DOM = DOM;
|
t.ok(setAttribute.calledWith('data-name', 'js-file-JUQwJUIwJUQwJUI5'), 'should call setAttribute');
|
||||||
globalThis.CloudCmd = CloudCmd;
|
|
||||||
|
|
||||||
t.calledWith(setAttribute, [
|
global.DOM = DOM;
|
||||||
'data-name',
|
global.CloudCmd = CloudCmd;
|
||||||
'js-file-JUQwJUIwJUQwJUI5',
|
|
||||||
], 'should call setAttribute');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('current-file: getCurrentName', (t) => {
|
|
||||||
const current = create();
|
|
||||||
current.getAttribute.returns('js-file-Ymlu');
|
|
||||||
|
|
||||||
const result = currentFile.getCurrentName(current);
|
|
||||||
|
|
||||||
t.equal(result, 'bin');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: emit', (t) => {
|
test('current-file: emit', (t) => {
|
||||||
const {DOM, CloudCmd} = globalThis;
|
const {
|
||||||
|
DOM,
|
||||||
|
CloudCmd,
|
||||||
|
} = global;
|
||||||
|
|
||||||
const emit = stub();
|
const emit = stub();
|
||||||
|
|
||||||
globalThis.DOM = getDOM();
|
global.DOM = getDOM();
|
||||||
globalThis.CloudCmd = getCloudCmd({
|
global.CloudCmd = getCloudCmd({
|
||||||
emit,
|
emit,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -74,41 +71,47 @@ test('current-file: emit', (t) => {
|
||||||
|
|
||||||
currentFile.setCurrentName('hello', current);
|
currentFile.setCurrentName('hello', current);
|
||||||
|
|
||||||
globalThis.DOM = DOM;
|
t.ok(emit.calledWith('current-file', current), 'should call emit');
|
||||||
globalThis.CloudCmd = CloudCmd;
|
|
||||||
|
global.DOM = DOM;
|
||||||
|
global.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.calledWith(emit, ['current-file', current], 'should call emit');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: setCurrentName: return', (t) => {
|
test('current-file: setCurrentName: return', (t) => {
|
||||||
const {DOM, CloudCmd} = globalThis;
|
const {
|
||||||
|
DOM,
|
||||||
|
CloudCmd,
|
||||||
|
} = global;
|
||||||
|
|
||||||
const link = {};
|
const link = {};
|
||||||
|
|
||||||
globalThis.DOM = getDOM({
|
global.DOM = getDOM({
|
||||||
link,
|
link,
|
||||||
});
|
});
|
||||||
|
|
||||||
globalThis.CloudCmd = getCloudCmd();
|
global.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const current = create();
|
const current = create();
|
||||||
|
|
||||||
const result = currentFile.setCurrentName('hello', current);
|
const result = currentFile.setCurrentName('hello', current);
|
||||||
|
|
||||||
globalThis.DOM = DOM;
|
|
||||||
globalThis.CloudCmd = CloudCmd;
|
|
||||||
|
|
||||||
t.equal(result, link, 'should return link');
|
t.equal(result, link, 'should return link');
|
||||||
|
|
||||||
|
global.DOM = DOM;
|
||||||
|
global.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: getParentDirPath: result', (t) => {
|
test('current-file: getParentDirPath: result', (t) => {
|
||||||
const {DOM} = globalThis;
|
const {DOM} = global;
|
||||||
|
|
||||||
const getCurrentDirPath = returns('/D/Films/+++favorite films/');
|
const getCurrentDirPath = returns('/D/Films/+++favorite films/');
|
||||||
const getCurrentDirName = returns('+++favorite films');
|
const getCurrentDirName = returns('+++favorite films');
|
||||||
|
|
||||||
globalThis.DOM = getDOM({
|
global.DOM = getDOM({
|
||||||
getCurrentDirPath,
|
getCurrentDirPath,
|
||||||
getCurrentDirName,
|
getCurrentDirName,
|
||||||
});
|
});
|
||||||
|
|
@ -116,55 +119,65 @@ test('current-file: getParentDirPath: result', (t) => {
|
||||||
const result = currentFile.getParentDirPath();
|
const result = currentFile.getParentDirPath();
|
||||||
const expected = '/D/Films/';
|
const expected = '/D/Films/';
|
||||||
|
|
||||||
globalThis.DOM = DOM;
|
global.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} = globalThis;
|
const {
|
||||||
|
DOM,
|
||||||
|
CloudCmd,
|
||||||
|
} = global;
|
||||||
|
|
||||||
globalThis.DOM = getDOM();
|
global.DOM = getDOM();
|
||||||
globalThis.CloudCmd = getCloudCmd();
|
global.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const result = currentFile.isCurrentFile();
|
const result = currentFile.isCurrentFile();
|
||||||
|
const expect = false;
|
||||||
|
|
||||||
globalThis.DOM = DOM;
|
global.DOM = DOM;
|
||||||
globalThis.CloudCmd = CloudCmd;
|
global.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.notOk(result);
|
t.equal(result, expect, 'should equal');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: isCurrentFile', (t) => {
|
test('current-file: isCurrentFile', (t) => {
|
||||||
const {DOM, CloudCmd} = globalThis;
|
const {
|
||||||
|
DOM,
|
||||||
|
CloudCmd,
|
||||||
|
} = global;
|
||||||
|
|
||||||
const isContainClass = stub();
|
const isContainClass = stub();
|
||||||
|
|
||||||
globalThis.DOM = getDOM({
|
global.DOM = getDOM({
|
||||||
isContainClass,
|
isContainClass,
|
||||||
});
|
});
|
||||||
|
|
||||||
globalThis.CloudCmd = getCloudCmd();
|
global.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const current = {};
|
const current = {};
|
||||||
currentFile.isCurrentFile(current);
|
currentFile.isCurrentFile(current);
|
||||||
|
|
||||||
globalThis.DOM = DOM;
|
global.DOM = DOM;
|
||||||
globalThis.CloudCmd = CloudCmd;
|
global.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.calledWith(isContainClass, [current, _CURRENT_FILE], 'should call isContainClass');
|
t.ok(isContainClass.calledWith(current, _CURRENT_FILE), 'should call isContainClass');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: getCurrentType', (t) => {
|
test('current-file: getCurrentType', (t) => {
|
||||||
const {DOM, CloudCmd} = globalThis;
|
const {
|
||||||
|
DOM,
|
||||||
|
CloudCmd,
|
||||||
|
} = global;
|
||||||
|
|
||||||
globalThis.DOM = getDOM();
|
global.DOM = getDOM();
|
||||||
globalThis.CloudCmd = getCloudCmd();
|
global.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const {getByDataName} = globalThis.DOM;
|
const {getByDataName} = global.DOM;
|
||||||
|
|
||||||
getByDataName.returns({
|
getByDataName.returns({
|
||||||
className: 'mini-icon directory',
|
className: 'mini-icon directory',
|
||||||
|
|
@ -174,129 +187,122 @@ test('current-file: getCurrentType', (t) => {
|
||||||
|
|
||||||
currentFile.getCurrentType(current);
|
currentFile.getCurrentType(current);
|
||||||
|
|
||||||
globalThis.DOM = DOM;
|
global.DOM = DOM;
|
||||||
globalThis.CloudCmd = CloudCmd;
|
global.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.calledWith(getByDataName, ['js-type', current]);
|
t.ok(getByDataName.calledWith('js-type', current));
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: isCurrentIsDir: getCurrentType', (t) => {
|
test('current-file: isCurrentIsDir: getCurrentType', (t) => {
|
||||||
const {DOM, CloudCmd} = globalThis;
|
const {
|
||||||
|
DOM,
|
||||||
|
CloudCmd,
|
||||||
|
} = global;
|
||||||
|
|
||||||
globalThis.DOM = getDOM();
|
global.DOM = getDOM();
|
||||||
globalThis.CloudCmd = getCloudCmd();
|
global.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const {getCurrentType} = globalThis.DOM;
|
const {getCurrentType} = global.DOM;
|
||||||
|
|
||||||
const current = create();
|
const current = create();
|
||||||
|
|
||||||
currentFile.isCurrentIsDir(current);
|
currentFile.isCurrentIsDir(current);
|
||||||
|
|
||||||
globalThis.DOM = DOM;
|
global.DOM = DOM;
|
||||||
globalThis.CloudCmd = CloudCmd;
|
global.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.calledWith(getCurrentType, [current]);
|
t.ok(getCurrentType.calledWith(current));
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('current-file: isCurrentIsDir: directory', (t) => {
|
test('current-file: isCurrentIsDir: directory', (t) => {
|
||||||
const {DOM, CloudCmd} = globalThis;
|
const {
|
||||||
|
DOM,
|
||||||
|
CloudCmd,
|
||||||
|
} = global;
|
||||||
|
|
||||||
globalThis.DOM = getDOM({
|
global.DOM = getDOM({
|
||||||
getCurrentType: stub().returns('directory'),
|
getCurrentType: stub().returns('directory'),
|
||||||
});
|
});
|
||||||
|
|
||||||
globalThis.CloudCmd = getCloudCmd();
|
global.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const current = create();
|
const current = create();
|
||||||
|
|
||||||
const result = currentFile.isCurrentIsDir(current);
|
const result = currentFile.isCurrentIsDir(current);
|
||||||
|
|
||||||
globalThis.DOM = DOM;
|
global.DOM = DOM;
|
||||||
globalThis.CloudCmd = CloudCmd;
|
global.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} = globalThis;
|
const {
|
||||||
|
DOM,
|
||||||
|
CloudCmd,
|
||||||
|
} = global;
|
||||||
|
|
||||||
globalThis.DOM = getDOM({
|
global.DOM = getDOM({
|
||||||
getCurrentType: stub().returns('directory-link'),
|
getCurrentType: stub().returns('directory-link'),
|
||||||
});
|
});
|
||||||
|
|
||||||
globalThis.CloudCmd = getCloudCmd();
|
global.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const current = create();
|
const current = create();
|
||||||
|
|
||||||
const result = currentFile.isCurrentIsDir(current);
|
const result = currentFile.isCurrentIsDir(current);
|
||||||
|
|
||||||
globalThis.DOM = DOM;
|
global.DOM = DOM;
|
||||||
globalThis.CloudCmd = CloudCmd;
|
global.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} = globalThis;
|
const {
|
||||||
|
DOM,
|
||||||
|
CloudCmd,
|
||||||
|
} = global;
|
||||||
|
|
||||||
globalThis.DOM = getDOM({
|
global.DOM = getDOM({
|
||||||
getCurrentType: stub().returns('file'),
|
getCurrentType: stub().returns('file'),
|
||||||
});
|
});
|
||||||
|
|
||||||
globalThis.CloudCmd = getCloudCmd();
|
global.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
const current = create();
|
const current = create();
|
||||||
|
|
||||||
const result = currentFile.isCurrentIsDir(current);
|
const result = currentFile.isCurrentIsDir(current);
|
||||||
|
|
||||||
globalThis.DOM = DOM;
|
global.DOM = DOM;
|
||||||
globalThis.CloudCmd = CloudCmd;
|
global.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.notOk(result);
|
t.notOk(result);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
const getCloudCmd = ({emit} = {}) => ({
|
function getCloudCmd({emit} = {}) {
|
||||||
prefix: '',
|
return {
|
||||||
emit: emit || stub(),
|
prefix: '',
|
||||||
});
|
emit: emit || stub(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
test('current-file: parseNameAttribute', (t) => {
|
function getDOM({
|
||||||
const result = currentFile._parseNameAttribute('js-file-aGVsbG8mbmJzcDt3b3JsZA==');
|
link = {},
|
||||||
const expected = 'hello\xa0world';
|
getCurrentDirPath = stub(),
|
||||||
|
getCurrentDirName = stub(),
|
||||||
t.equal(result, expected);
|
getByDataName = stub(),
|
||||||
t.end();
|
isContainClass = stub(),
|
||||||
});
|
getCurrentType = stub(),
|
||||||
|
} = {}) {
|
||||||
test('current-file: parseHrefAttribute', (t) => {
|
|
||||||
const prefix = '/api/v1';
|
|
||||||
const result = currentFile._parseHrefAttribute(prefix, '/api/v1/fs/hello world');
|
|
||||||
const expected = '/hello\xa0world';
|
|
||||||
|
|
||||||
t.equal(result, expected);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
function getDOM(overrides = {}) {
|
|
||||||
const {
|
|
||||||
link = {},
|
|
||||||
getCurrentDirPath = stub(),
|
|
||||||
getCurrentDirName = stub(),
|
|
||||||
getByDataName = stub(),
|
|
||||||
isContainClass = stub(),
|
|
||||||
getCurrentType = stub(),
|
|
||||||
getCurrentPath = stub().returns(''),
|
|
||||||
} = overrides;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getCurrentDirPath,
|
getCurrentDirPath,
|
||||||
getCurrentDirName,
|
getCurrentDirName,
|
||||||
getCurrentPath,
|
|
||||||
getByDataName,
|
getByDataName,
|
||||||
isContainClass,
|
isContainClass,
|
||||||
getCurrentType,
|
getCurrentType,
|
||||||
|
|
@ -306,3 +312,4 @@ function getDOM(overrides = {}) {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,27 @@
|
||||||
import {tryToCatch} from 'try-to-catch';
|
'use strict';
|
||||||
import * as smalltalk from 'smalltalk';
|
|
||||||
|
const tryToCatch = require('try-to-catch');
|
||||||
|
|
||||||
|
const {
|
||||||
|
alert,
|
||||||
|
prompt,
|
||||||
|
confirm,
|
||||||
|
progress,
|
||||||
|
} = require('smalltalk');
|
||||||
|
|
||||||
const title = 'Cloud Commander';
|
const title = 'Cloud Commander';
|
||||||
|
|
||||||
export const alert = (...a) => smalltalk.alert(title, ...a, {
|
module.exports.alert = (...a) => alert(title, ...a, {
|
||||||
cancel: false,
|
cancel: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const prompt = (...a) => tryToCatch(smalltalk.prompt, title, ...a);
|
module.exports.prompt = (...a) => tryToCatch(prompt, title, ...a);
|
||||||
export const confirm = (...a) => tryToCatch(smalltalk.confirm, title, ...a);
|
module.exports.confirm = (...a) => tryToCatch(confirm, title, ...a);
|
||||||
export const progress = (...a) => smalltalk.progress(title, ...a);
|
module.exports.progress = (...a) => progress(title, ...a);
|
||||||
|
|
||||||
alert.noFiles = () => {
|
module.exports.alert.noFiles = () => {
|
||||||
return smalltalk.alert(title, 'No files selected!', {
|
return alert(title, 'No files selected!', {
|
||||||
cancel: false,
|
cancel: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,27 @@
|
||||||
/* global DOM, CloudCmd */
|
'use strict';
|
||||||
import philip from 'philip';
|
|
||||||
import * as Dialog from '#dom/dialog';
|
|
||||||
import {FS} from '#common/cloudfunc';
|
|
||||||
import * as Images from '#dom/images';
|
|
||||||
|
|
||||||
export const uploadDirectory = (items) => {
|
/* global CloudCmd */
|
||||||
|
|
||||||
|
const philip = require('philip');
|
||||||
|
|
||||||
|
const Images = require('./images');
|
||||||
|
const {FS} = require('../../common/cloudfunc');
|
||||||
|
const DOM = require('.');
|
||||||
|
const Dialog = require('./dialog');
|
||||||
|
|
||||||
|
const {getCurrentDirPath: getPathWhenRootEmpty} = DOM;
|
||||||
|
|
||||||
|
module.exports = (items) => {
|
||||||
if (items.length)
|
if (items.length)
|
||||||
Images.show('top');
|
Images.show('top');
|
||||||
|
|
||||||
const entries = Array
|
const entries = Array.from(items).map((item) => {
|
||||||
.from(items)
|
return item.webkitGetAsEntry();
|
||||||
.map((item) => item.webkitGetAsEntry());
|
});
|
||||||
|
|
||||||
const dirPath = DOM.getCurrentDirPath();
|
const dirPath = getPathWhenRootEmpty();
|
||||||
const path = dirPath.replace(/\/$/, '');
|
const path = dirPath
|
||||||
|
.replace(/\/$/, '');
|
||||||
|
|
||||||
const progress = Dialog.progress('Uploading...');
|
const progress = Dialog.progress('Uploading...');
|
||||||
|
|
||||||
|
|
@ -57,8 +65,15 @@ export const uploadDirectory = (items) => {
|
||||||
uploader.on('end', CloudCmd.refresh);
|
uploader.on('end', CloudCmd.refresh);
|
||||||
};
|
};
|
||||||
|
|
||||||
const percent = (i, n, per = 100) => Math.round(i * per / n);
|
function percent(i, n, per = 100) {
|
||||||
|
return Math.round(i * per / n);
|
||||||
|
}
|
||||||
|
|
||||||
const uploadFile = (url, data) => DOM.load.put(url, data);
|
function uploadFile(url, data) {
|
||||||
|
return DOM.load.put(url, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadDir(url) {
|
||||||
|
return DOM.load.put(url + '?dir');
|
||||||
|
}
|
||||||
|
|
||||||
const uploadDir = (url) => DOM.load.put(`${url}?dir`);
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,8 @@
|
||||||
import currify from 'currify';
|
'use strict';
|
||||||
|
|
||||||
const DOM = {
|
const currify = require('currify');
|
||||||
show,
|
|
||||||
hide,
|
const DOM = module.exports;
|
||||||
getByClass,
|
|
||||||
getByClassAll,
|
|
||||||
getByDataName,
|
|
||||||
getById,
|
|
||||||
getByTag,
|
|
||||||
isContainClass,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check class of element
|
* check class of element
|
||||||
|
|
@ -17,7 +10,7 @@ const DOM = {
|
||||||
* @param element
|
* @param element
|
||||||
* @param className
|
* @param className
|
||||||
*/
|
*/
|
||||||
export function isContainClass(element, className) {
|
const isContainClass = (element, className) => {
|
||||||
if (!element)
|
if (!element)
|
||||||
throw Error('element could not be empty!');
|
throw Error('element could not be empty!');
|
||||||
|
|
||||||
|
|
@ -25,68 +18,67 @@ export function isContainClass(element, className) {
|
||||||
throw Error('className could not be empty!');
|
throw Error('className could not be empty!');
|
||||||
|
|
||||||
if (Array.isArray(className))
|
if (Array.isArray(className))
|
||||||
return className.some(currify(
|
return className.some(currify(isContainClass, element));
|
||||||
isContainClass,
|
|
||||||
element,
|
|
||||||
));
|
|
||||||
|
|
||||||
const {classList} = element;
|
const {classList} = element;
|
||||||
|
|
||||||
return classList.contains(className);
|
return classList.contains(className);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
module.exports.isContainClass = isContainClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function search element by tag
|
* Function search element by tag
|
||||||
* @param tag - className
|
* @param tag - className
|
||||||
* @param element - element
|
* @param element - element
|
||||||
*/
|
*/
|
||||||
export function getByTag(tag, element = document) {
|
module.exports.getByTag = (tag, element = document) => {
|
||||||
return element.getElementsByTagName(tag);
|
return element.getElementsByTagName(tag);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function search element by id
|
* Function search element by id
|
||||||
* @param id
|
* @param Id - id
|
||||||
* @param element
|
|
||||||
*/
|
*/
|
||||||
export function getById(id, element = document) {
|
module.exports.getById = (id, element = document) => {
|
||||||
return element.querySelector(`#${id}`);
|
return element.querySelector('#' + id);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function search first element by class name
|
* Function search first element by class name
|
||||||
* @param className - className
|
* @param className - className
|
||||||
* @param element - element
|
* @param element - element
|
||||||
*/
|
*/
|
||||||
export function getByClass(className, element = document) {
|
module.exports.getByClass = (className, element = document) => {
|
||||||
return DOM.getByClassAll(className, element)[0];
|
return DOM.getByClassAll(className, element)[0];
|
||||||
}
|
};
|
||||||
|
|
||||||
export function getByDataName(attribute, element = document) {
|
module.exports.getByDataName = (attribute, element = document) => {
|
||||||
const selector = '[' + 'data-name="' + attribute + '"]';
|
const selector = '[' + 'data-name="' + attribute + '"]';
|
||||||
return element.querySelector(selector);
|
return element.querySelector(selector);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function search element by class name
|
* Function search element by class name
|
||||||
* @param className
|
* @param pClass - className
|
||||||
* @param element
|
* @param element - element
|
||||||
*/
|
*/
|
||||||
export function getByClassAll(className, element) {
|
module.exports.getByClassAll = (className, element) => {
|
||||||
return (element || document).getElementsByClassName(className);
|
return (element || document).getElementsByClassName(className);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add class=hidden to element
|
* add class=hidden to element
|
||||||
*
|
*
|
||||||
* @param element
|
* @param element
|
||||||
*/
|
*/
|
||||||
export function hide(element) {
|
module.exports.hide = (element) => {
|
||||||
element.classList.add('hidden');
|
element.classList.add('hidden');
|
||||||
return DOM;
|
return DOM;
|
||||||
}
|
};
|
||||||
|
|
||||||
export function show(element) {
|
module.exports.show = (element) => {
|
||||||
element.classList.remove('hidden');
|
element.classList.remove('hidden');
|
||||||
return DOM;
|
return DOM;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,19 @@
|
||||||
import {test, stub} from 'supertape';
|
'use strict';
|
||||||
import {create} from 'auto-globals';
|
|
||||||
import {tryCatch} from 'try-catch';
|
const test = require('supertape');
|
||||||
import {
|
const {create} = require('auto-globals');
|
||||||
isContainClass,
|
const tryCatch = require('try-catch');
|
||||||
getByTag,
|
|
||||||
getById,
|
const {isContainClass} = require('./dom-tree');
|
||||||
getByClass,
|
|
||||||
getByDataName,
|
|
||||||
getByClassAll,
|
|
||||||
hide,
|
|
||||||
show,
|
|
||||||
} from './dom-tree.js';
|
|
||||||
|
|
||||||
test('dom: isContainClass: no element', (t) => {
|
test('dom: isContainClass: no element', (t) => {
|
||||||
const [e] = tryCatch(isContainClass);
|
const [e] = tryCatch(isContainClass);
|
||||||
|
|
||||||
t.equal(e.message, 'element could not be empty!', 'should throw when no element');
|
t.equal(e.message, 'element could not be empty!', 'should throw when no element');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('dom: isContainClass: no className', (t) => {
|
test('dom: isContainClass: no className', (t) => {
|
||||||
const [e] = tryCatch(isContainClass, {});
|
const [e] = tryCatch(isContainClass, {});
|
||||||
|
|
||||||
t.equal(e.message, 'className could not be empty!', 'should throw when no element');
|
t.equal(e.message, 'className could not be empty!', 'should throw when no element');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
@ -33,7 +25,7 @@ test('dom: isContainClass: contains', (t) => {
|
||||||
const className = 'hello';
|
const className = 'hello';
|
||||||
isContainClass(el, className);
|
isContainClass(el, className);
|
||||||
|
|
||||||
t.calledWith(contains, [className], 'should call contains');
|
t.ok(contains.calledWith(className), 'should call contains');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -42,111 +34,13 @@ test('dom: isContainClass: contains: array', (t) => {
|
||||||
const {contains} = el.classList;
|
const {contains} = el.classList;
|
||||||
|
|
||||||
const className = 'hello';
|
const className = 'hello';
|
||||||
isContainClass(el, ['world', className, 'hello']);
|
isContainClass(el, [
|
||||||
|
'world',
|
||||||
|
className,
|
||||||
|
'hello',
|
||||||
|
]);
|
||||||
|
|
||||||
t.calledWith(contains, [className], 'should call contains');
|
t.ok(contains.calledWith(className), 'should call contains');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('dom: getByTag', (t) => {
|
|
||||||
const getElementsByTagName = stub();
|
|
||||||
const element = {
|
|
||||||
getElementsByTagName,
|
|
||||||
};
|
|
||||||
|
|
||||||
getByTag('div', element);
|
|
||||||
|
|
||||||
t.calledWith(getElementsByTagName, ['div'], 'should call getElementsByTagName');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('dom: getById', (t) => {
|
|
||||||
const querySelector = stub();
|
|
||||||
const element = {
|
|
||||||
querySelector,
|
|
||||||
};
|
|
||||||
|
|
||||||
getById('my-id', element);
|
|
||||||
|
|
||||||
t.calledWith(querySelector, ['#my-id'], 'should call querySelector with id selector');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('dom: getByClassAll', (t) => {
|
|
||||||
const getElementsByClassName = stub();
|
|
||||||
const element = {
|
|
||||||
getElementsByClassName,
|
|
||||||
};
|
|
||||||
|
|
||||||
getByClassAll('my-class', element);
|
|
||||||
|
|
||||||
t.calledWith(getElementsByClassName, ['my-class'], 'should call getElementsByClassName');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('dom: getByClass: calls getByClassAll', (t) => {
|
|
||||||
const element = {
|
|
||||||
getElementsByClassName: stub().returns(['first']),
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = getByClass('my-class', element);
|
|
||||||
|
|
||||||
t.equal(result, 'first', 'should return first element from class list');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('dom: getByDataName', (t) => {
|
|
||||||
const querySelector = stub();
|
|
||||||
const element = {
|
|
||||||
querySelector,
|
|
||||||
};
|
|
||||||
|
|
||||||
getByDataName('hello', element);
|
|
||||||
|
|
||||||
t.calledWith(querySelector, ['[data-name="hello"]'], 'should call querySelector with data-name selector');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('dom: hide', (t) => {
|
|
||||||
const add = stub();
|
|
||||||
const element = {
|
|
||||||
classList: {
|
|
||||||
add,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
hide(element);
|
|
||||||
|
|
||||||
t.calledWith(add, ['hidden'], 'should add hidden class');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('dom: show', (t) => {
|
|
||||||
const remove = stub();
|
|
||||||
const element = {
|
|
||||||
classList: {
|
|
||||||
remove,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
show(element);
|
|
||||||
|
|
||||||
t.calledWith(remove, ['hidden'], 'should remove hidden class');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('dom: getByClassAll: without element uses document', (t) => {
|
|
||||||
const getElementsByClassName = stub();
|
|
||||||
const prevDocument = globalThis.document;
|
|
||||||
|
|
||||||
globalThis.document = {
|
|
||||||
getElementsByClassName,
|
|
||||||
};
|
|
||||||
|
|
||||||
getByClassAll('my-class');
|
|
||||||
|
|
||||||
globalThis.document = prevDocument;
|
|
||||||
|
|
||||||
t.calledWith(getElementsByClassName, ['my-class'], 'should fallback to document when no element');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
let list = [];
|
let list = [];
|
||||||
|
|
||||||
export const add = (el, name, fn) => {
|
module.exports.add = (el, name, fn) => {
|
||||||
list.push([
|
list.push([
|
||||||
el,
|
el,
|
||||||
name,
|
name,
|
||||||
|
|
@ -8,8 +10,11 @@ export const add = (el, name, fn) => {
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clear = () => {
|
module.exports.clear = () => {
|
||||||
list = [];
|
list = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const get = () => list;
|
module.exports.get = () => {
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import {test} from 'supertape';
|
'use strict';
|
||||||
import * as eventStore from './event-store.js';
|
|
||||||
|
const test = require('supertape');
|
||||||
|
const eventStore = require('./event-store');
|
||||||
|
|
||||||
test('event-store: get', (t) => {
|
test('event-store: get', (t) => {
|
||||||
const el = {};
|
const el = {};
|
||||||
|
|
@ -8,16 +10,11 @@ test('event-store: get', (t) => {
|
||||||
|
|
||||||
eventStore.add(el, name, fn);
|
eventStore.add(el, name, fn);
|
||||||
const result = eventStore.get();
|
const result = eventStore.get();
|
||||||
|
|
||||||
const expected = [
|
const expected = [
|
||||||
[
|
[el, name, fn],
|
||||||
el,
|
|
||||||
name,
|
|
||||||
fn,
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
t.deepEqual(result, expected);
|
t.deepEqual(expected, result, 'should equal');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -32,6 +29,6 @@ test('event-store: clear', (t) => {
|
||||||
const result = eventStore.get();
|
const result = eventStore.get();
|
||||||
const expected = [];
|
const expected = [];
|
||||||
|
|
||||||
t.deepEqual(result, expected);
|
t.deepEqual(expected, result, 'should equal');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,203 +1,250 @@
|
||||||
import itype from 'itype';
|
'use strict';
|
||||||
import * as EventStore from './event-store.js';
|
|
||||||
|
|
||||||
/**
|
const itype = require('itype');
|
||||||
* safe add event listener
|
const EventStore = require('./event-store');
|
||||||
*
|
|
||||||
* @param type
|
module.exports = new EventsProto();
|
||||||
* @param element - document by default
|
|
||||||
* @param listener
|
function EventsProto() {
|
||||||
*/
|
const Events = this;
|
||||||
export const add = (type, element, listener) => {
|
|
||||||
checkType(type);
|
|
||||||
|
|
||||||
parseArgs(type, element, listener, (element, args) => {
|
const getEventOptions = (eventName) => {
|
||||||
const [name, fn, options] = args;
|
if (eventName !== 'touchstart')
|
||||||
|
return false;
|
||||||
|
|
||||||
element.addEventListener(name, fn, options);
|
return {
|
||||||
EventStore.add(element, name, fn);
|
passive: true,
|
||||||
});
|
};
|
||||||
|
|
||||||
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) {
|
function parseArgs(eventName, element, listener, callback) {
|
||||||
listener = element;
|
let isFunc;
|
||||||
element = null;
|
const args = [
|
||||||
|
eventName,
|
||||||
|
element,
|
||||||
|
listener,
|
||||||
|
callback,
|
||||||
|
];
|
||||||
|
|
||||||
|
const EVENT_NAME = 1;
|
||||||
|
const ELEMENT = 0;
|
||||||
|
const type = itype(eventName);
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
default:
|
||||||
|
if (!/element$/.test(type))
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
add(type, element, once);
|
/**
|
||||||
|
* safe add event listener
|
||||||
return Events;
|
*
|
||||||
};
|
* @param type
|
||||||
|
* @param element {document by default}
|
||||||
/**
|
* @param listener
|
||||||
* safe remove event listener
|
*/
|
||||||
*
|
this.add = (type, element, listener) => {
|
||||||
* @param type
|
checkType(type);
|
||||||
* @param listener
|
|
||||||
* @param element - document by default
|
parseArgs(type, element, listener, (element, args) => {
|
||||||
*/
|
const [name, fn, options] = args;
|
||||||
export const remove = (type, element, listener) => {
|
|
||||||
checkType(type);
|
element.addEventListener(name, fn, options);
|
||||||
|
EventStore.add(element, name, fn);
|
||||||
parseArgs(type, element, listener, (element, args) => {
|
});
|
||||||
element.removeEventListener(...args);
|
|
||||||
});
|
return Events;
|
||||||
|
|
||||||
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;
|
* safe add event listener
|
||||||
const type = itype(eventName);
|
*
|
||||||
|
* @param type
|
||||||
switch(type) {
|
* @param listener
|
||||||
default:
|
* @param element {document by default}
|
||||||
if (!type.endsWith('element'))
|
*/
|
||||||
throw Error(`unknown eventName: ${type}`);
|
this.addOnce = (type, element, listener) => {
|
||||||
|
const once = (event) => {
|
||||||
|
Events.remove(type, element, once);
|
||||||
|
listener(event);
|
||||||
|
};
|
||||||
|
|
||||||
parseArgs(args[EVENT_NAME], args[ELEMENT], listener, callback);
|
if (!listener) {
|
||||||
break;
|
|
||||||
|
|
||||||
case 'string':
|
|
||||||
isFunc = itype.function(element);
|
|
||||||
|
|
||||||
if (isFunc) {
|
|
||||||
listener = element;
|
listener = element;
|
||||||
element = null;
|
element = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!element)
|
this.add(type, element, once);
|
||||||
element = window;
|
|
||||||
|
|
||||||
callback(element, [
|
return Events;
|
||||||
eventName,
|
};
|
||||||
listener,
|
|
||||||
getEventOptions(eventName),
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'array':
|
/**
|
||||||
|
* safe remove event listener
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* @param listener
|
||||||
|
* @param element {document by default}
|
||||||
|
*/
|
||||||
|
this.remove = (type, element, listener) => {
|
||||||
|
checkType(type);
|
||||||
|
|
||||||
for (const name of eventName) {
|
parseArgs(type, element, listener, (element, args) => {
|
||||||
parseArgs(name, element, listener, callback);
|
element.removeEventListener(...args);
|
||||||
}
|
});
|
||||||
|
|
||||||
break;
|
return Events;
|
||||||
|
};
|
||||||
|
|
||||||
case 'object':
|
/**
|
||||||
|
* remove all added event listeners
|
||||||
|
*
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
this.removeAll = () => {
|
||||||
|
const events = EventStore.get();
|
||||||
|
|
||||||
for (const name of Object.keys(eventName)) {
|
for (const [el, name, fn] of events)
|
||||||
const eventListener = eventName[name];
|
el.removeEventListener(name, fn);
|
||||||
|
|
||||||
parseArgs(name, element, eventListener, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
EventStore.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe add event keydown listener
|
||||||
|
*
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
this.addKey = function(...argsArr) {
|
||||||
|
const name = 'keydown';
|
||||||
|
const args = [name, ...argsArr];
|
||||||
|
|
||||||
|
return Events.add(...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe remove event click listener
|
||||||
|
*
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
this.rmKey = function(...argsArr) {
|
||||||
|
const name = 'keydown';
|
||||||
|
const args = [name, ...argsArr];
|
||||||
|
|
||||||
|
return Events.remove(...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe add event click listener
|
||||||
|
*
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
this.addClick = function(...argsArr) {
|
||||||
|
const name = 'click';
|
||||||
|
const args = [name, ...argsArr];
|
||||||
|
|
||||||
|
return Events.add(...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe remove event click listener
|
||||||
|
*
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
this.rmClick = function(...argsArr) {
|
||||||
|
const name = 'click';
|
||||||
|
const args = [name, ...argsArr];
|
||||||
|
|
||||||
|
return Events.remove(...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.addContextMenu = function(...argsArr) {
|
||||||
|
const name = 'contextmenu';
|
||||||
|
const args = [name, ...argsArr];
|
||||||
|
|
||||||
|
return Events.add(...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe add event click listener
|
||||||
|
*
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
this.addError = function(...argsArr) {
|
||||||
|
const name = 'error';
|
||||||
|
const args = [name, ...argsArr];
|
||||||
|
|
||||||
|
return Events.add(...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe add load click listener
|
||||||
|
*
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
this.addLoad = function(...argsArr) {
|
||||||
|
const name = 'load';
|
||||||
|
const args = [name, ...argsArr];
|
||||||
|
|
||||||
|
return Events.add(...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
function checkType(type) {
|
||||||
|
if (!type)
|
||||||
|
throw Error('type could not be empty!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Events = {
|
|
||||||
add,
|
|
||||||
addClick,
|
|
||||||
addContextMenu,
|
|
||||||
addKey,
|
|
||||||
addLoad,
|
|
||||||
addOnce,
|
|
||||||
remove,
|
|
||||||
removeAll,
|
|
||||||
rmClick,
|
|
||||||
rmKey,
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,37 @@
|
||||||
/* global CloudCmd */
|
/* global CloudCmd */
|
||||||
import itype from 'itype';
|
|
||||||
import {promisify} from 'es6-promisify';
|
'use strict';
|
||||||
import * as load from '#dom/load';
|
|
||||||
import * as RESTful from '#dom/rest';
|
const itype = require('itype');
|
||||||
|
const currify = require('currify');
|
||||||
|
const {promisify} = require('es6-promisify');
|
||||||
|
|
||||||
|
const load = require('./load');
|
||||||
|
const RESTful = require('./rest');
|
||||||
|
|
||||||
const Promises = {};
|
const Promises = {};
|
||||||
const FILES_JSON = 'config|modules';
|
const FILES_JSON = 'config|modules';
|
||||||
const FILES_HTML = 'file|path|link|pathLink|media';
|
const FILES_HTML = 'file|path|link|pathLink|media';
|
||||||
const FILES_HTML_ROOT = 'view/media-tmpl|config-tmpl|upload';
|
const FILES_HTML_ROOT = 'view/media-tmpl|config-tmpl|upload';
|
||||||
const DIR_HTML = '/tmpl/';
|
const DIR_HTML = '/tmpl/';
|
||||||
const DIR_HTML_FS = `${DIR_HTML}fs/`;
|
const DIR_HTML_FS = DIR_HTML + 'fs/';
|
||||||
const DIR_JSON = '/json/';
|
const DIR_JSON = '/json/';
|
||||||
const timeout = getTimeoutOnce(2000);
|
const timeout = getTimeoutOnce(2000);
|
||||||
|
|
||||||
export const get = getFile;
|
const get = currify(getFile);
|
||||||
|
const unaryMap = (array, fn) => array.map((a) => fn(a));
|
||||||
|
|
||||||
function getFile(name) {
|
module.exports.get = get;
|
||||||
|
|
||||||
|
async function getFile(name) {
|
||||||
const type = itype(name);
|
const type = itype(name);
|
||||||
check(name);
|
check(name);
|
||||||
|
|
||||||
if (type === 'string')
|
if (type === 'string')
|
||||||
return getModule(name);
|
return await getModule(name);
|
||||||
|
|
||||||
if (type === 'array')
|
if (type === 'array')
|
||||||
return Promise.all(name.map(getFile));
|
return Promise.all(unaryMap(name, get));
|
||||||
}
|
}
|
||||||
|
|
||||||
function check(name) {
|
function check(name) {
|
||||||
|
|
@ -31,9 +39,9 @@ function check(name) {
|
||||||
throw Error('name could not be empty!');
|
throw Error('name could not be empty!');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getModule(name) {
|
async function getModule(name) {
|
||||||
const regExpHTML = RegExp(FILES_HTML + '|' + FILES_HTML_ROOT);
|
const regExpHTML = new RegExp(FILES_HTML + '|' + FILES_HTML_ROOT);
|
||||||
const regExpJSON = RegExp(FILES_JSON);
|
const regExpJSON = new RegExp(FILES_JSON);
|
||||||
|
|
||||||
const isHTML = regExpHTML.test(name);
|
const isHTML = regExpHTML.test(name);
|
||||||
const isJSON = regExpJSON.test(name);
|
const isJSON = regExpJSON.test(name);
|
||||||
|
|
@ -45,13 +53,12 @@ function getModule(name) {
|
||||||
return getConfig();
|
return getConfig();
|
||||||
|
|
||||||
const path = getPath(name, isHTML, isJSON);
|
const path = getPath(name, isHTML, isJSON);
|
||||||
|
|
||||||
return getSystemFile(path);
|
return getSystemFile(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPath(name, isHTML, isJSON) {
|
function getPath(name, isHTML, isJSON) {
|
||||||
let path;
|
let path;
|
||||||
const regExp = RegExp(FILES_HTML_ROOT);
|
const regExp = new RegExp(FILES_HTML_ROOT);
|
||||||
const isRoot = regExp.test(name);
|
const isRoot = regExp.test(name);
|
||||||
|
|
||||||
if (isHTML) {
|
if (isHTML) {
|
||||||
|
|
@ -69,8 +76,8 @@ function getPath(name, isHTML, isJSON) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function showError(name) {
|
function showError(name) {
|
||||||
const str = `Wrong file name: ${name}`;
|
const str = 'Wrong file name: ' + name;
|
||||||
const error = Error(str);
|
const error = new Error(str);
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
@ -134,3 +141,4 @@ function getTimeoutOnce(time) {
|
||||||
}, time);
|
}, time);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,20 @@
|
||||||
/* 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';
|
||||||
const ERROR = 'error';
|
const ERROR = 'error';
|
||||||
|
|
||||||
const getLoadingType = () => isSVG() ? '-svg' : '-gif';
|
function getLoadingType() {
|
||||||
|
return isSVG() ? '-svg' : '-gif';
|
||||||
|
}
|
||||||
|
|
||||||
export const get = getElement;
|
module.exports.get = getElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check SVG SMIL animation support
|
* check SVG SMIL animation support
|
||||||
|
|
@ -36,7 +43,7 @@ function getElement() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Функция создаёт картинку загрузки */
|
/* Функция создаёт картинку загрузки */
|
||||||
export const loading = () => {
|
module.exports.loading = () => {
|
||||||
const element = getElement();
|
const element = getElement();
|
||||||
const {classList} = element;
|
const {classList} = element;
|
||||||
const loadingImage = LOADING + getLoadingType();
|
const loadingImage = LOADING + getLoadingType();
|
||||||
|
|
@ -48,7 +55,7 @@ export const loading = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Функция создаёт картинку ошибки загрузки */
|
/* Функция создаёт картинку ошибки загрузки */
|
||||||
export const error = () => {
|
module.exports.error = () => {
|
||||||
const element = getElement();
|
const element = getElement();
|
||||||
const {classList} = element;
|
const {classList} = element;
|
||||||
const loadingImage = LOADING + getLoadingType();
|
const loadingImage = LOADING + getLoadingType();
|
||||||
|
|
@ -59,21 +66,15 @@ export const error = () => {
|
||||||
return element;
|
return element;
|
||||||
};
|
};
|
||||||
|
|
||||||
show.load = show;
|
module.exports.show = show;
|
||||||
show.error = (text) => {
|
module.exports.show.load = show;
|
||||||
const image = Images.error();
|
module.exports.show.error = error;
|
||||||
|
|
||||||
DOM.show(image);
|
|
||||||
image.title = text;
|
|
||||||
|
|
||||||
return image;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function shows loading spinner
|
* Function shows loading spinner
|
||||||
* position = {top: true};
|
* position = {top: true};
|
||||||
*/
|
*/
|
||||||
export function show(position, panel) {
|
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);
|
||||||
|
|
@ -99,25 +100,34 @@ export 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
|
||||||
*/
|
*/
|
||||||
export function hide() {
|
module.exports.hide = () => {
|
||||||
const element = Images.get();
|
const element = Images.get();
|
||||||
|
|
||||||
DOM.hide(element);
|
DOM.hide(element);
|
||||||
|
|
||||||
return Images;
|
return Images;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const setProgress = (value, title) => {
|
module.exports.setProgress = (value, title) => {
|
||||||
const DATA = 'data-progress';
|
const DATA = 'data-progress';
|
||||||
const element = Images.get();
|
const element = Images.get();
|
||||||
|
|
||||||
if (!element)
|
if (!element)
|
||||||
return Images;
|
return Images;
|
||||||
|
|
||||||
element.setAttribute(DATA, `${value}%`);
|
element.setAttribute(DATA, value + '%');
|
||||||
|
|
||||||
if (title)
|
if (title)
|
||||||
element.title = title;
|
element.title = title;
|
||||||
|
|
@ -125,7 +135,7 @@ export const setProgress = (value, title) => {
|
||||||
return Images;
|
return Images;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clearProgress = () => {
|
module.exports.clearProgress = () => {
|
||||||
const DATA = 'data-progress';
|
const DATA = 'data-progress';
|
||||||
const element = Images.get();
|
const element = Images.get();
|
||||||
|
|
||||||
|
|
@ -138,12 +148,3 @@ export const clearProgress = () => {
|
||||||
return Images;
|
return Images;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Images = {
|
|
||||||
clearProgress,
|
|
||||||
setProgress,
|
|
||||||
show,
|
|
||||||
hide,
|
|
||||||
get,
|
|
||||||
error,
|
|
||||||
loading,
|
|
||||||
};
|
|
||||||
|
|
|
||||||
1619
client/dom/index.js
1619
client/dom/index.js
File diff suppressed because it is too large
Load diff
|
|
@ -1,52 +0,0 @@
|
||||||
import {test, stub} from 'supertape';
|
|
||||||
import {getCSSVar, goToDirectory} from '#dom';
|
|
||||||
|
|
||||||
globalThis.CloudCmd = {};
|
|
||||||
|
|
||||||
test('cloudcmd: client: dom: goToDirectory', async (t) => {
|
|
||||||
const path = '';
|
|
||||||
const changeDir = stub();
|
|
||||||
const prompt = stub().returns([null, path]);
|
|
||||||
|
|
||||||
await goToDirectory({
|
|
||||||
prompt,
|
|
||||||
changeDir,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWith(changeDir, [path]);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: dom: getCSSVar', (t) => {
|
|
||||||
const body = {};
|
|
||||||
const getPropertyValue = stub().returns(0);
|
|
||||||
|
|
||||||
globalThis.getComputedStyle = stub().returns({
|
|
||||||
getPropertyValue,
|
|
||||||
});
|
|
||||||
const result = getCSSVar('hello', {
|
|
||||||
body,
|
|
||||||
});
|
|
||||||
|
|
||||||
delete globalThis.getComputedStyle;
|
|
||||||
|
|
||||||
t.notOk(result);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: dom: getCSSVar: 1', (t) => {
|
|
||||||
const body = {};
|
|
||||||
const getPropertyValue = stub().returns(1);
|
|
||||||
|
|
||||||
globalThis.getComputedStyle = stub().returns({
|
|
||||||
getPropertyValue,
|
|
||||||
});
|
|
||||||
const result = getCSSVar('hello', {
|
|
||||||
body,
|
|
||||||
});
|
|
||||||
|
|
||||||
delete globalThis.getComputedStyle;
|
|
||||||
|
|
||||||
t.ok(result);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
216
client/dom/io.js
Normal file
216
client/dom/io.js
Normal file
|
|
@ -0,0 +1,216 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/* global CloudCmd*/
|
||||||
|
|
||||||
|
const itype = require('itype');
|
||||||
|
const {promisify} = require('es6-promisify');
|
||||||
|
|
||||||
|
const {FS} = require('../../common/cloudfunc');
|
||||||
|
|
||||||
|
const Images = require('./images');
|
||||||
|
const load = require('./load');
|
||||||
|
|
||||||
|
const imgPosition = {
|
||||||
|
top: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports._replaceHash = replaceHash;
|
||||||
|
function replaceHash(url) {
|
||||||
|
/*
|
||||||
|
* if we send ajax request -
|
||||||
|
* no need in hash so we escape #
|
||||||
|
*/
|
||||||
|
return url.replace(/#/g, '%23');
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.delete = promisify((url, data, callback) => {
|
||||||
|
const isFunc = itype.function(data);
|
||||||
|
|
||||||
|
if (!callback && isFunc) {
|
||||||
|
callback = data;
|
||||||
|
data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendRequest({
|
||||||
|
method : 'DELETE',
|
||||||
|
url : FS + url,
|
||||||
|
data,
|
||||||
|
callback,
|
||||||
|
imgPosition : {
|
||||||
|
top: !!data,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.patch = promisify((url, data, callback) => {
|
||||||
|
const isFunc = itype.function(data);
|
||||||
|
|
||||||
|
if (!callback && isFunc) {
|
||||||
|
callback = data;
|
||||||
|
data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendRequest({
|
||||||
|
method: 'PATCH',
|
||||||
|
url: FS + url,
|
||||||
|
data,
|
||||||
|
callback,
|
||||||
|
imgPosition,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.write = promisify((url, data, callback) => {
|
||||||
|
const isFunc = itype.function(data);
|
||||||
|
|
||||||
|
if (!callback && isFunc) {
|
||||||
|
callback = data;
|
||||||
|
data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendRequest({
|
||||||
|
method: 'PUT',
|
||||||
|
url: FS + url,
|
||||||
|
data,
|
||||||
|
callback,
|
||||||
|
imgPosition,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.read = promisify((url, dataType, callback) => {
|
||||||
|
const notLog = !url.includes('?');
|
||||||
|
const isFunc = itype.function(dataType);
|
||||||
|
|
||||||
|
if (!callback && isFunc) {
|
||||||
|
callback = dataType;
|
||||||
|
dataType = 'text';
|
||||||
|
}
|
||||||
|
|
||||||
|
sendRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: FS + url,
|
||||||
|
callback,
|
||||||
|
notLog,
|
||||||
|
dataType,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.cp = promisify((data, callback) => {
|
||||||
|
sendRequest({
|
||||||
|
method: 'PUT',
|
||||||
|
url: '/cp',
|
||||||
|
data,
|
||||||
|
callback,
|
||||||
|
imgPosition,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.pack = promisify((data, callback) => {
|
||||||
|
sendRequest({
|
||||||
|
method: 'PUT',
|
||||||
|
url: '/pack',
|
||||||
|
data,
|
||||||
|
callback,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.extract = promisify((data, callback) => {
|
||||||
|
sendRequest({
|
||||||
|
method : 'PUT',
|
||||||
|
url : '/extract',
|
||||||
|
data,
|
||||||
|
callback,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.mv = promisify((data, callback) => {
|
||||||
|
sendRequest({
|
||||||
|
method : 'PUT',
|
||||||
|
url : '/mv',
|
||||||
|
data,
|
||||||
|
callback,
|
||||||
|
imgPosition,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.Config = {
|
||||||
|
read: promisify((callback) => {
|
||||||
|
sendRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/config',
|
||||||
|
callback,
|
||||||
|
imgPosition,
|
||||||
|
notLog: true,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
write: promisify((data, callback) => {
|
||||||
|
sendRequest({
|
||||||
|
method: 'PATCH',
|
||||||
|
url: '/config',
|
||||||
|
data,
|
||||||
|
callback,
|
||||||
|
imgPosition,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.Markdown = {
|
||||||
|
read: promisify((url, callback) => {
|
||||||
|
sendRequest({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/markdown' + url,
|
||||||
|
callback,
|
||||||
|
imgPosition,
|
||||||
|
notLog: true,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
render: promisify((data, callback) => {
|
||||||
|
sendRequest({
|
||||||
|
method: 'PUT',
|
||||||
|
url: '/markdown',
|
||||||
|
data,
|
||||||
|
callback,
|
||||||
|
imgPosition,
|
||||||
|
notLog: true,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
function sendRequest(params) {
|
||||||
|
const p = params;
|
||||||
|
const {prefixURL} = CloudCmd;
|
||||||
|
|
||||||
|
p.url = prefixURL + p.url;
|
||||||
|
p.url = encodeURI(p.url);
|
||||||
|
|
||||||
|
p.url = replaceHash(p.url);
|
||||||
|
|
||||||
|
load.ajax({
|
||||||
|
method : p.method,
|
||||||
|
url : p.url,
|
||||||
|
data : p.data,
|
||||||
|
dataType : p.dataType,
|
||||||
|
error : (jqXHR) => {
|
||||||
|
const response = jqXHR.responseText;
|
||||||
|
|
||||||
|
const {
|
||||||
|
statusText,
|
||||||
|
status,
|
||||||
|
} = jqXHR;
|
||||||
|
|
||||||
|
const text = status === 404 ? response : statusText;
|
||||||
|
|
||||||
|
p.callback(Error(text));
|
||||||
|
},
|
||||||
|
success: (data) => {
|
||||||
|
Images.hide();
|
||||||
|
|
||||||
|
if (!p.notLog)
|
||||||
|
CloudCmd.log(data);
|
||||||
|
|
||||||
|
p.callback(null, data);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
import {test} from 'supertape';
|
'use strict';
|
||||||
import {_replaceHash} from './send-request.js';
|
|
||||||
|
const test = require('supertape');
|
||||||
|
const io = require('./io');
|
||||||
|
|
||||||
test('cloudcmd: client: io: replaceHash', (t) => {
|
test('cloudcmd: client: io: replaceHash', (t) => {
|
||||||
|
const {_replaceHash} = io;
|
||||||
const url = '/hello/####world';
|
const url = '/hello/####world';
|
||||||
const result = _replaceHash(url);
|
const result = _replaceHash(url);
|
||||||
const expected = '/hello/%23%23%23%23world';
|
const expected = '/hello/%23%23%23%23world';
|
||||||
|
|
||||||
t.equal(result, expected);
|
t.equal(result, expected, 'should equal');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1,166 +0,0 @@
|
||||||
import {FS} from '#common/cloudfunc';
|
|
||||||
import {sendRequest as _sendRequest} from './send-request.js';
|
|
||||||
|
|
||||||
const {assign} = Object;
|
|
||||||
|
|
||||||
const imgPosition = {
|
|
||||||
top: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const remove = async (url, data, overrides = {}) => {
|
|
||||||
const {
|
|
||||||
sendRequest = _sendRequest,
|
|
||||||
} = overrides;
|
|
||||||
|
|
||||||
const request = {
|
|
||||||
method: 'DELETE',
|
|
||||||
url: FS + url,
|
|
||||||
imgPosition: {
|
|
||||||
top: Boolean(data),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (data)
|
|
||||||
assign(request, {
|
|
||||||
data,
|
|
||||||
url: `${request.url}?files`,
|
|
||||||
});
|
|
||||||
|
|
||||||
return await sendRequest(request);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const patch = async (url, data) => {
|
|
||||||
return await _sendRequest({
|
|
||||||
method: 'PATCH',
|
|
||||||
url: FS + url,
|
|
||||||
data,
|
|
||||||
imgPosition,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const write = async (url, data) => {
|
|
||||||
return await _sendRequest({
|
|
||||||
method: 'PUT',
|
|
||||||
url: FS + url,
|
|
||||||
data,
|
|
||||||
imgPosition,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createDirectory = async (url, overrides = {}) => {
|
|
||||||
const {
|
|
||||||
sendRequest = _sendRequest,
|
|
||||||
} = overrides;
|
|
||||||
|
|
||||||
return await sendRequest({
|
|
||||||
method: 'PUT',
|
|
||||||
url: `${FS}${url}?dir`,
|
|
||||||
imgPosition,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const read = async (url, dataType = 'text') => {
|
|
||||||
const notLog = !url.includes('?');
|
|
||||||
|
|
||||||
return await _sendRequest({
|
|
||||||
method: 'GET',
|
|
||||||
url: FS + url,
|
|
||||||
notLog,
|
|
||||||
dataType,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const copy = async (from, to, names) => {
|
|
||||||
return await _sendRequest({
|
|
||||||
method: 'PUT',
|
|
||||||
url: '/copy',
|
|
||||||
data: {
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
names,
|
|
||||||
},
|
|
||||||
imgPosition,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const pack = async (data) => {
|
|
||||||
return await _sendRequest({
|
|
||||||
method: 'PUT',
|
|
||||||
url: '/pack',
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const extract = async (data) => {
|
|
||||||
return await _sendRequest({
|
|
||||||
method: 'PUT',
|
|
||||||
url: '/extract',
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const move = async (from, to, names) => {
|
|
||||||
return await _sendRequest({
|
|
||||||
method: 'PUT',
|
|
||||||
url: '/move',
|
|
||||||
data: {
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
names,
|
|
||||||
},
|
|
||||||
imgPosition,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const rename = async (from, to) => {
|
|
||||||
return await _sendRequest({
|
|
||||||
method: 'PUT',
|
|
||||||
url: '/rename',
|
|
||||||
data: {
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
},
|
|
||||||
imgPosition,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Config = {
|
|
||||||
read: async () => {
|
|
||||||
return await _sendRequest({
|
|
||||||
method: 'GET',
|
|
||||||
url: '/config',
|
|
||||||
imgPosition,
|
|
||||||
notLog: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
write: async (data) => {
|
|
||||||
return await _sendRequest({
|
|
||||||
method: 'PATCH',
|
|
||||||
url: '/config',
|
|
||||||
data,
|
|
||||||
imgPosition,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Markdown = {
|
|
||||||
read: async (url) => {
|
|
||||||
return await _sendRequest({
|
|
||||||
method: 'GET',
|
|
||||||
url: `/markdown${url}`,
|
|
||||||
imgPosition,
|
|
||||||
notLog: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
render: async (data) => {
|
|
||||||
return await _sendRequest({
|
|
||||||
method: 'PUT',
|
|
||||||
url: '/markdown',
|
|
||||||
data,
|
|
||||||
imgPosition,
|
|
||||||
notLog: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
import {test, stub} from 'supertape';
|
|
||||||
import * as io from './index.js';
|
|
||||||
|
|
||||||
test('client: dom: io', (t) => {
|
|
||||||
const sendRequest = stub();
|
|
||||||
|
|
||||||
io.createDirectory('/hello', {
|
|
||||||
sendRequest,
|
|
||||||
});
|
|
||||||
|
|
||||||
const expected = {
|
|
||||||
imgPosition: {
|
|
||||||
top: true,
|
|
||||||
},
|
|
||||||
method: 'PUT',
|
|
||||||
url: '/fs/hello?dir',
|
|
||||||
};
|
|
||||||
|
|
||||||
t.calledWith(sendRequest, [expected]);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('client: dom: io: remove: no files', async (t) => {
|
|
||||||
const sendRequest = stub();
|
|
||||||
|
|
||||||
await io.remove('/hello', null, {
|
|
||||||
sendRequest,
|
|
||||||
});
|
|
||||||
|
|
||||||
const expected = {
|
|
||||||
imgPosition: {
|
|
||||||
top: false,
|
|
||||||
},
|
|
||||||
method: 'DELETE',
|
|
||||||
url: '/fs/hello',
|
|
||||||
};
|
|
||||||
|
|
||||||
t.calledWith(sendRequest, [expected]);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('client: dom: io: remove: files', async (t) => {
|
|
||||||
const sendRequest = stub();
|
|
||||||
const files = ['world'];
|
|
||||||
|
|
||||||
await io.remove('/hello', files, {
|
|
||||||
sendRequest,
|
|
||||||
});
|
|
||||||
|
|
||||||
const expected = {
|
|
||||||
imgPosition: {
|
|
||||||
top: true,
|
|
||||||
},
|
|
||||||
data: ['world'],
|
|
||||||
method: 'DELETE',
|
|
||||||
url: '/fs/hello?files',
|
|
||||||
};
|
|
||||||
|
|
||||||
t.calledWith(sendRequest, [expected]);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
/* global CloudCmd */
|
|
||||||
import {promisify} from 'es6-promisify';
|
|
||||||
import * as Images from '#dom/images';
|
|
||||||
import * as load from '#dom/load';
|
|
||||||
|
|
||||||
export const sendRequest = promisify((params, callback) => {
|
|
||||||
const p = params;
|
|
||||||
const {prefixURL} = CloudCmd;
|
|
||||||
|
|
||||||
p.url = prefixURL + p.url;
|
|
||||||
p.url = encodeURI(p.url);
|
|
||||||
|
|
||||||
p.url = replaceHash(p.url);
|
|
||||||
|
|
||||||
load.ajax({
|
|
||||||
method: p.method,
|
|
||||||
url: p.url,
|
|
||||||
data: p.data,
|
|
||||||
dataType: p.dataType,
|
|
||||||
error: (jqXHR) => {
|
|
||||||
const response = jqXHR.responseText;
|
|
||||||
|
|
||||||
const {statusText, status} = jqXHR;
|
|
||||||
|
|
||||||
const text = status === 404 ? response : statusText;
|
|
||||||
|
|
||||||
callback(Error(text));
|
|
||||||
},
|
|
||||||
success: (data) => {
|
|
||||||
Images.hide();
|
|
||||||
|
|
||||||
if (!p.notLog)
|
|
||||||
CloudCmd.log(data);
|
|
||||||
|
|
||||||
callback(null, data);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
export const _replaceHash = replaceHash;
|
|
||||||
|
|
||||||
function replaceHash(url) {
|
|
||||||
/*
|
|
||||||
* if we send ajax request -
|
|
||||||
* no need in hash so we escape #
|
|
||||||
*/
|
|
||||||
return url.replace(/#/g, '%23');
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +1,60 @@
|
||||||
/* global CloudCmd */
|
'use strict';
|
||||||
import {callbackify} from 'node:util';
|
|
||||||
import {rendy} from 'rendy';
|
|
||||||
import itype from 'itype';
|
|
||||||
import * as load from 'load.js';
|
|
||||||
import {tryToCatch} from 'try-to-catch';
|
|
||||||
import {findObjByNameInArr} from '#common/util';
|
|
||||||
import * as Files from '#dom/files';
|
|
||||||
|
|
||||||
export const loadRemote = callbackify(async (name, options) => {
|
/* global CloudCmd */
|
||||||
|
|
||||||
|
const rendy = require('rendy');
|
||||||
|
const itype = require('itype');
|
||||||
|
const load = require('load.js');
|
||||||
|
const tryToCatch = require('try-to-catch');
|
||||||
|
|
||||||
|
const {findObjByNameInArr} = require('../../common/util');
|
||||||
|
|
||||||
|
const Files = require('./files');
|
||||||
|
|
||||||
|
module.exports = (name, options, callback = options) => {
|
||||||
const {prefix, config} = CloudCmd;
|
const {prefix, config} = CloudCmd;
|
||||||
const o = options;
|
const o = options;
|
||||||
|
|
||||||
if (o.name && window[o.name])
|
if (o.name && window[o.name])
|
||||||
return;
|
return callback();
|
||||||
|
|
||||||
const modules = await Files.get('modules');
|
Files.get('modules').then(async (modules) => {
|
||||||
|
const online = config('online') && navigator.onLine;
|
||||||
const online = config('online') && navigator.onLine;
|
const module = findObjByNameInArr(modules.remote, name);
|
||||||
const module = findObjByNameInArr(modules.remote, name);
|
|
||||||
|
|
||||||
const isArray = itype.array(module.local);
|
|
||||||
const {version} = module;
|
|
||||||
|
|
||||||
let remoteTmpls;
|
|
||||||
let local;
|
|
||||||
|
|
||||||
if (isArray) {
|
|
||||||
remoteTmpls = module.remote;
|
|
||||||
({local} = module);
|
|
||||||
} else {
|
|
||||||
remoteTmpls = [module.remote];
|
|
||||||
local = [module.local];
|
|
||||||
}
|
|
||||||
|
|
||||||
const localURL = [];
|
|
||||||
|
|
||||||
for (const url of local) {
|
|
||||||
localURL.push(prefix + url);
|
|
||||||
}
|
|
||||||
|
|
||||||
const remoteURL = [];
|
|
||||||
|
|
||||||
for (const tmpl of remoteTmpls) {
|
|
||||||
remoteURL.push(rendy(tmpl, {
|
|
||||||
version,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (online) {
|
|
||||||
const [e] = await tryToCatch(load.parallel, remoteURL);
|
|
||||||
|
|
||||||
if (!e)
|
const isArray = itype.array(module.local);
|
||||||
return;
|
const {version} = module;
|
||||||
}
|
|
||||||
|
let remoteTmpls;
|
||||||
await load.parallel(localURL);
|
let local;
|
||||||
});
|
|
||||||
|
if (isArray) {
|
||||||
|
remoteTmpls = module.remote;
|
||||||
|
local = module.local;
|
||||||
|
} else {
|
||||||
|
remoteTmpls = [module.remote];
|
||||||
|
local = [module.local];
|
||||||
|
}
|
||||||
|
|
||||||
|
const localURL = local.map((url) => {
|
||||||
|
return prefix + url;
|
||||||
|
});
|
||||||
|
|
||||||
|
const remoteURL = remoteTmpls.map((tmpl) => {
|
||||||
|
return rendy(tmpl, {
|
||||||
|
version,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (online) {
|
||||||
|
const [e] = await tryToCatch(load.parallel, remoteURL);
|
||||||
|
|
||||||
|
if (!e)
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
const [e] = await tryToCatch(load.parallel, localURL);
|
||||||
|
callback(e);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
import itype from 'itype';
|
'use strict';
|
||||||
import jonny from 'jonny';
|
|
||||||
import Emitify from 'emitify';
|
const itype = require('itype');
|
||||||
import exec from 'execon';
|
const jonny = require('jonny');
|
||||||
import * as Images from '#dom/images';
|
const Emitify = require('emitify');
|
||||||
|
const exec = require('execon');
|
||||||
|
const Images = require('./images');
|
||||||
|
|
||||||
|
module.exports.getIdBySrc = getIdBySrc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function gets id by src
|
* Function gets id by src
|
||||||
|
|
@ -10,18 +14,17 @@ import * as Images from '#dom/images';
|
||||||
*
|
*
|
||||||
* Example: http://domain.com/1.js -> 1_js
|
* Example: http://domain.com/1.js -> 1_js
|
||||||
*/
|
*/
|
||||||
export function getIdBySrc(src) {
|
function getIdBySrc(src) {
|
||||||
const isStr = itype.string(src);
|
const isStr = itype.string(src);
|
||||||
|
|
||||||
if (!isStr)
|
if (!isStr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (src.includes(':'))
|
if (~src.indexOf(':'))
|
||||||
src += '-join';
|
src += '-join';
|
||||||
|
|
||||||
const num = src.lastIndexOf('/') + 1;
|
const num = src.lastIndexOf('/') + 1;
|
||||||
const sub = src.substr(src, num);
|
const sub = src.substr(src, num);
|
||||||
|
|
||||||
const id = src
|
const id = src
|
||||||
.replace(sub, '')
|
.replace(sub, '')
|
||||||
.replace(/\./g, '-');
|
.replace(/\./g, '-');
|
||||||
|
|
@ -34,15 +37,15 @@ export function getIdBySrc(src) {
|
||||||
*
|
*
|
||||||
* @param params
|
* @param params
|
||||||
*/
|
*/
|
||||||
export const ajax = (params) => {
|
module.exports.ajax = (params) => {
|
||||||
const p = params;
|
const p = params;
|
||||||
const isObject = itype.object(p.data);
|
const isObject = itype.object(p.data);
|
||||||
const isArray = itype.array(p.data);
|
const isArray = itype.array(p.data);
|
||||||
const isArrayBuf = itype(p.data) === 'arraybuffer';
|
const isArrayBuf = itype(p.data) === 'arraybuffer';
|
||||||
const type = p.type || p.method || 'GET';
|
const type = p.type || p.method || 'GET';
|
||||||
|
const {
|
||||||
const {headers = {}} = p;
|
headers = {},
|
||||||
|
} = p;
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
xhr.open(type, p.url, true);
|
xhr.open(type, p.url, true);
|
||||||
|
|
@ -60,7 +63,7 @@ export const ajax = (params) => {
|
||||||
if (!isArrayBuf && isObject || isArray)
|
if (!isArrayBuf && isObject || isArray)
|
||||||
data = jonny.stringify(p.data);
|
data = jonny.stringify(p.data);
|
||||||
else
|
else
|
||||||
({data} = p);
|
data = p.data;
|
||||||
|
|
||||||
xhr.onreadystatechange = (event) => {
|
xhr.onreadystatechange = (event) => {
|
||||||
const xhr = event.target;
|
const xhr = event.target;
|
||||||
|
|
@ -78,7 +81,7 @@ export const ajax = (params) => {
|
||||||
return exec(p.error, xhr);
|
return exec(p.error, xhr);
|
||||||
|
|
||||||
const notText = p.dataType !== 'text';
|
const notText = p.dataType !== 'text';
|
||||||
const isContain = type.includes(TYPE_JSON);
|
const isContain = ~type.indexOf(TYPE_JSON);
|
||||||
|
|
||||||
let data = xhr.response;
|
let data = xhr.response;
|
||||||
|
|
||||||
|
|
@ -91,11 +94,12 @@ export const ajax = (params) => {
|
||||||
xhr.send(data);
|
xhr.send(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const put = (url, body) => {
|
module.exports.put = (url, body) => {
|
||||||
const emitter = Emitify();
|
const emitter = Emitify();
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
url = encodeURI(url).replace(/#/g, '#');
|
url = encodeURI(url)
|
||||||
|
.replace('#', '%23');
|
||||||
|
|
||||||
xhr.open('put', url, true);
|
xhr.open('put', url, true);
|
||||||
|
|
||||||
|
|
@ -116,10 +120,8 @@ export const put = (url, body) => {
|
||||||
if (!over)
|
if (!over)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (xhr.status === OK) {
|
if (xhr.status === OK)
|
||||||
emitter.emit('progress', 100);
|
|
||||||
return emitter.emit('end');
|
return emitter.emit('end');
|
||||||
}
|
|
||||||
|
|
||||||
const error = Error(xhr.responseText);
|
const error = Error(xhr.responseText);
|
||||||
emitter.emit('error', error);
|
emitter.emit('error', error);
|
||||||
|
|
@ -129,3 +131,4 @@ export const put = (url, body) => {
|
||||||
|
|
||||||
return emitter;
|
return emitter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,23 @@
|
||||||
/* global CloudCmd */
|
'use strict';
|
||||||
import capitalize from 'just-capitalize';
|
|
||||||
import * as _Dialog from '#dom/dialog';
|
|
||||||
import * as Storage from '#dom/storage';
|
|
||||||
import * as RESTful from '#dom/rest';
|
|
||||||
import * as _currentFile from '../current-file.js';
|
|
||||||
|
|
||||||
export default async (current, overrides = {}) => {
|
/* global CloudCmd */
|
||||||
const {
|
|
||||||
refresh = CloudCmd.refresh,
|
const capitalize = require('just-capitalize');
|
||||||
Dialog = _Dialog,
|
|
||||||
currentFile = _currentFile,
|
const Dialog = require('../dialog');
|
||||||
} = overrides;
|
const Storage = require('../storage');
|
||||||
|
const RESTful = require('../rest');
|
||||||
const {
|
const {
|
||||||
isCurrentFile,
|
isCurrentFile,
|
||||||
getCurrentName,
|
getCurrentName,
|
||||||
getCurrentFile,
|
getCurrentFile,
|
||||||
getCurrentByName,
|
getCurrentByName,
|
||||||
getCurrentType,
|
getCurrentType,
|
||||||
getCurrentDirPath,
|
getCurrentDirPath,
|
||||||
setCurrentName,
|
setCurrentName,
|
||||||
} = currentFile;
|
} = require('../current-file');
|
||||||
|
|
||||||
|
module.exports = async (current) => {
|
||||||
if (!isCurrentFile(current))
|
if (!isCurrentFile(current))
|
||||||
current = getCurrentFile();
|
current = getCurrentFile();
|
||||||
|
|
||||||
|
|
@ -50,11 +46,12 @@ export default async (current, overrides = {}) => {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const dirPath = getCurrentDirPath();
|
const dirPath = getCurrentDirPath();
|
||||||
|
const files = {
|
||||||
|
from : dirPath + from,
|
||||||
|
to : dirPath + to,
|
||||||
|
};
|
||||||
|
|
||||||
const fromFull = `${dirPath}${from}`;
|
const [e] = await RESTful.mv(files);
|
||||||
const toFull = `${dirPath}${to}`;
|
|
||||||
|
|
||||||
const [e] = await RESTful.rename(fromFull, toFull);
|
|
||||||
|
|
||||||
if (e)
|
if (e)
|
||||||
return;
|
return;
|
||||||
|
|
@ -62,5 +59,5 @@ export default async (current, overrides = {}) => {
|
||||||
setCurrentName(to, current);
|
setCurrentName(to, current);
|
||||||
|
|
||||||
Storage.remove(dirPath);
|
Storage.remove(dirPath);
|
||||||
refresh();
|
CloudCmd.refresh();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,37 @@
|
||||||
import {test, stub} from 'supertape';
|
'use strict';
|
||||||
import renameCurrent from './rename-current.js';
|
|
||||||
|
const test = require('supertape');
|
||||||
|
const stub = require('@cloudcmd/stub');
|
||||||
|
const mockRequire = require('mock-require');
|
||||||
|
|
||||||
|
const {reRequire} = 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();
|
||||||
|
|
||||||
const currentFile = stubCurrentFile({
|
mockRequire('../dialog', stubDialog());
|
||||||
|
mockRequire('../current-file', stubCurrentFile({
|
||||||
isCurrentFile,
|
isCurrentFile,
|
||||||
});
|
}));
|
||||||
|
|
||||||
await renameCurrent(current, {
|
const renameCurrent = reRequire('./rename-current');
|
||||||
Dialog: stubDialog(),
|
await renameCurrent(current);
|
||||||
currentFile,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWith(isCurrentFile, [current], 'should call isCurrentFile');
|
t.ok(isCurrentFile.calledWith(current), 'should call isCurrentFile');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
const CloudCmdStub = {
|
||||||
|
refresh: stub(),
|
||||||
|
};
|
||||||
|
|
||||||
|
global.CloudCmd = CloudCmdStub;
|
||||||
|
|
||||||
const prompt = stub().returns([null, name]);
|
const prompt = stub().returns([null, name]);
|
||||||
const confirm = stub().returns([true]);
|
const confirm = stub().returns([true]);
|
||||||
|
|
@ -28,24 +39,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');
|
||||||
|
|
||||||
const Dialog = stubDialog({
|
mockRequire('../dialog', stubDialog({
|
||||||
confirm,
|
confirm,
|
||||||
prompt,
|
prompt,
|
||||||
});
|
}));
|
||||||
|
|
||||||
const currentFile = stubCurrentFile({
|
mockRequire('../current-file', stubCurrentFile({
|
||||||
getCurrentByName,
|
getCurrentByName,
|
||||||
getCurrentType,
|
getCurrentType,
|
||||||
});
|
}));
|
||||||
|
|
||||||
await renameCurrent(null, {
|
const renameCurrent = reRequire('./rename-current');
|
||||||
Dialog,
|
await renameCurrent();
|
||||||
currentFile,
|
|
||||||
});
|
|
||||||
|
|
||||||
const expected = 'Directory "hello" already exists. Proceed?';
|
const expected = 'Directory "hello" already exists. Proceed?';
|
||||||
|
global.CloudCmd = CloudCmd;
|
||||||
|
|
||||||
t.calledWith(confirm, [expected], 'should call confirm');
|
t.ok(confirm.calledWith(expected), 'should call confirm');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -84,3 +94,4 @@ const stubCurrentFile = (fns = {}) => {
|
||||||
setCurrentName,
|
setCurrentName,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
import {tryToCatch} from 'try-to-catch';
|
'use strict';
|
||||||
import * as Dialog from '#dom/dialog';
|
|
||||||
import * as Images from '#dom/images';
|
const tryToCatch = require('try-to-catch');
|
||||||
import {encode} from '#common/entity';
|
|
||||||
import * as IO from './io/index.js';
|
const {encode} = require('../../common/entity');
|
||||||
|
|
||||||
|
const Images = require('./images');
|
||||||
|
const IO = require('./io');
|
||||||
|
const Dialog = require('./dialog');
|
||||||
|
|
||||||
const handleError = (promise) => async (...args) => {
|
const handleError = (promise) => async (...args) => {
|
||||||
const [e, data] = await tryToCatch(promise, ...args);
|
const [e, data] = await tryToCatch(promise, ...args);
|
||||||
|
|
@ -18,23 +22,22 @@ const handleError = (promise) => async (...args) => {
|
||||||
return [e, data];
|
return [e, data];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const remove = handleError(IO.remove);
|
module.exports.delete = handleError(IO.delete);
|
||||||
export const patch = handleError(IO.patch);
|
module.exports.patch = handleError(IO.patch);
|
||||||
export const write = handleError(IO.write);
|
module.exports.write = handleError(IO.write);
|
||||||
export const createDirectory = handleError(IO.createDirectory);
|
module.exports.read = handleError(IO.read);
|
||||||
export const read = handleError(IO.read);
|
module.exports.cp = handleError(IO.cp);
|
||||||
export const copy = handleError(IO.copy);
|
module.exports.pack = handleError(IO.pack);
|
||||||
export const pack = handleError(IO.pack);
|
module.exports.extract = handleError(IO.extract);
|
||||||
export const extract = handleError(IO.extract);
|
module.exports.mv = handleError(IO.mv);
|
||||||
export const move = handleError(IO.move);
|
|
||||||
export const rename = handleError(IO.rename);
|
|
||||||
|
|
||||||
export const Config = {
|
module.exports.Config = {
|
||||||
read: handleError(IO.Config.read),
|
read: handleError(IO.Config.read),
|
||||||
write: handleError(IO.Config.write),
|
write: handleError(IO.Config.write),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Markdown = {
|
module.exports.Markdown = {
|
||||||
read: handleError(IO.Markdown.read),
|
read: handleError(IO.Markdown.read),
|
||||||
render: handleError(IO.Markdown.render),
|
render: handleError(IO.Markdown.render),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,22 @@
|
||||||
import {alert, prompt} from '#dom/dialog';
|
'use strict';
|
||||||
import {getRegExp} from '#common/util';
|
|
||||||
import {getCurrentName} from './current-file.js';
|
|
||||||
import {
|
|
||||||
isSelected,
|
|
||||||
toggleSelectedFile,
|
|
||||||
} from './cmd.js';
|
|
||||||
|
|
||||||
let SelectType = '*.*';
|
let SelectType = '*.*';
|
||||||
|
|
||||||
export const selectByPattern = async (msg, files) => {
|
const {getRegExp} = require('../../common/util');
|
||||||
|
const {
|
||||||
|
alert,
|
||||||
|
prompt,
|
||||||
|
} = require('./dialog');
|
||||||
|
|
||||||
|
const DOM = require('.');
|
||||||
|
|
||||||
|
module.exports = async (msg, files) => {
|
||||||
if (!files)
|
if (!files)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const allMsg = `Specify file type for ${msg} selection`;
|
const allMsg = `Specify file type for ${msg} selection`;
|
||||||
|
|
||||||
|
/* eslint require-atomic-updates: 0 */
|
||||||
const [cancel, type] = await prompt(allMsg, SelectType);
|
const [cancel, type] = await prompt(allMsg, SelectType);
|
||||||
|
|
||||||
if (cancel)
|
if (cancel)
|
||||||
|
|
@ -24,23 +28,24 @@ export const selectByPattern = async (msg, files) => {
|
||||||
let matches = 0;
|
let matches = 0;
|
||||||
|
|
||||||
for (const current of files) {
|
for (const current of files) {
|
||||||
const name = getCurrentName(current);
|
const name = DOM.getCurrentName(current);
|
||||||
|
|
||||||
if (name === '..' || !regExp.test(name))
|
if (name === '..' || !regExp.test(name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
++matches;
|
++matches;
|
||||||
|
|
||||||
let selected = isSelected(current);
|
let isSelected = DOM.isSelected(current);
|
||||||
const shouldSel = msg === 'expand';
|
const shouldSel = msg === 'expand';
|
||||||
|
|
||||||
if (shouldSel)
|
if (shouldSel)
|
||||||
selected = !selected;
|
isSelected = !isSelected;
|
||||||
|
|
||||||
if (selected)
|
if (isSelected)
|
||||||
toggleSelectedFile(current);
|
DOM.toggleSelectedFile(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!matches)
|
if (!matches)
|
||||||
alert('No matches found!');
|
alert('No matches found!');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,38 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const tryCatch = require('try-catch');
|
||||||
|
|
||||||
const {parse, stringify} = JSON;
|
const {parse, stringify} = JSON;
|
||||||
|
const isObj = (a) => typeof a === 'object';
|
||||||
|
|
||||||
export const set = (name, data) => {
|
module.exports.set = (name, data) => {
|
||||||
localStorage.setItem(name, data);
|
const primitive = !isObj(data) ? data : stringify(data);
|
||||||
|
|
||||||
|
localStorage.setItem(name, primitive);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setJson = (name, data) => {
|
module.exports.get = async (name) => {
|
||||||
localStorage.setItem(name, stringify(data));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const get = (name) => {
|
|
||||||
return localStorage.getItem(name);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getJson = (name) => {
|
|
||||||
const data = localStorage.getItem(name);
|
const data = localStorage.getItem(name);
|
||||||
return parse(data);
|
const [, result = data] = tryCatch(parse, data);
|
||||||
|
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clear = () => {
|
module.exports.clear = () => {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const remove = (item) => {
|
module.exports.remove = (item) => {
|
||||||
localStorage.removeItem(item);
|
localStorage.removeItem(item);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.removeMatch = (string) => {
|
||||||
|
const reg = RegExp('^' + string + '.*$');
|
||||||
|
const test = (a) => reg.test(a);
|
||||||
|
const remove = (a) => localStorage.removeItem(a);
|
||||||
|
|
||||||
|
Object.keys(localStorage)
|
||||||
|
.filter(test)
|
||||||
|
.forEach(remove);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
import {test, stub} from 'supertape';
|
|
||||||
import * as storage from '#dom/storage';
|
|
||||||
|
|
||||||
const {stringify} = JSON;
|
|
||||||
|
|
||||||
test('cloudcmd: client: storage: set', async (t) => {
|
|
||||||
const {localStorage} = globalThis;
|
|
||||||
const setItem = stub();
|
|
||||||
|
|
||||||
globalThis.localStorage = {
|
|
||||||
setItem,
|
|
||||||
};
|
|
||||||
|
|
||||||
await storage.set('hello', 'world');
|
|
||||||
globalThis.localStorage = localStorage;
|
|
||||||
|
|
||||||
t.calledWith(setItem, ['hello', 'world'], 'should call setItem');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: storage: get', async (t) => {
|
|
||||||
const {localStorage} = globalThis;
|
|
||||||
const getItem = stub().returns('world');
|
|
||||||
|
|
||||||
globalThis.localStorage = {
|
|
||||||
getItem,
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await storage.get('hello');
|
|
||||||
|
|
||||||
globalThis.localStorage = localStorage;
|
|
||||||
|
|
||||||
t.equal(result, 'world');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: storage: getJson', async (t) => {
|
|
||||||
const {localStorage} = globalThis;
|
|
||||||
const expected = {
|
|
||||||
hello: 'world',
|
|
||||||
};
|
|
||||||
|
|
||||||
const getItem = stub().returns(stringify(expected));
|
|
||||||
|
|
||||||
globalThis.localStorage = {
|
|
||||||
getItem,
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await storage.getJson('hello');
|
|
||||||
|
|
||||||
globalThis.localStorage = localStorage;
|
|
||||||
|
|
||||||
t.deepEqual(result, expected);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: storage: setJson', async (t) => {
|
|
||||||
const {localStorage} = globalThis;
|
|
||||||
const data = {
|
|
||||||
hello: 'world',
|
|
||||||
};
|
|
||||||
|
|
||||||
const expected = stringify(data);
|
|
||||||
const setItem = stub();
|
|
||||||
|
|
||||||
globalThis.localStorage = {
|
|
||||||
setItem,
|
|
||||||
};
|
|
||||||
|
|
||||||
await storage.setJson('hello', data);
|
|
||||||
globalThis.localStorage = localStorage;
|
|
||||||
|
|
||||||
t.calledWith(setItem, ['hello', expected]);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: storage: remove', async (t) => {
|
|
||||||
const {localStorage} = globalThis;
|
|
||||||
const removeItem = stub();
|
|
||||||
|
|
||||||
globalThis.localStorage = {
|
|
||||||
removeItem,
|
|
||||||
};
|
|
||||||
|
|
||||||
await storage.remove('hello');
|
|
||||||
globalThis.localStorage = localStorage;
|
|
||||||
|
|
||||||
t.calledWith(removeItem, ['hello'], 'should call removeItem');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: storage: clear', async (t) => {
|
|
||||||
const {localStorage} = globalThis;
|
|
||||||
const clear = stub();
|
|
||||||
|
|
||||||
globalThis.localStorage = {
|
|
||||||
clear,
|
|
||||||
};
|
|
||||||
|
|
||||||
await storage.clear();
|
|
||||||
globalThis.localStorage = localStorage;
|
|
||||||
|
|
||||||
t.calledWithNoArgs(clear, 'should call clear');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
@ -1,19 +1,25 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
/* global CloudCmd */
|
/* global CloudCmd */
|
||||||
import {eachSeries} from 'execon';
|
|
||||||
import wraptile from 'wraptile';
|
|
||||||
import * as load from '#dom/load';
|
|
||||||
import {alert} from '#dom/dialog';
|
|
||||||
import {FS} from '#common/cloudfunc';
|
|
||||||
import * as Images from '#dom/images';
|
|
||||||
import {getCurrentDirPath} from './current-file.js';
|
|
||||||
|
|
||||||
const loadFile = wraptile(_loadFile);
|
const {eachSeries} = require('execon');
|
||||||
|
const wraptile = require('wraptile');
|
||||||
|
|
||||||
|
const load = require('./load');
|
||||||
|
const Images = require('./images');
|
||||||
|
const {alert} = require('./dialog');
|
||||||
|
|
||||||
|
const {FS} = require('../../common/cloudfunc');
|
||||||
|
|
||||||
const onEnd = wraptile(_onEnd);
|
const onEnd = wraptile(_onEnd);
|
||||||
|
const loadFile = wraptile(_loadFile);
|
||||||
|
|
||||||
export const uploadFiles = (dir, files) => {
|
const {getCurrentDirPath: getPathWhenRootEmpty} = require('.');
|
||||||
|
|
||||||
|
module.exports = (dir, files) => {
|
||||||
if (!files) {
|
if (!files) {
|
||||||
files = dir;
|
files = dir;
|
||||||
dir = getCurrentDirPath();
|
dir = getPathWhenRootEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
const n = files.length;
|
const n = files.length;
|
||||||
|
|
@ -49,8 +55,7 @@ function _loadFile(dir, n, file, callback) {
|
||||||
|
|
||||||
++i;
|
++i;
|
||||||
|
|
||||||
load
|
load.put(api + path, file)
|
||||||
.put(api + path, file)
|
|
||||||
.on('error', showError)
|
.on('error', showError)
|
||||||
.on('end', callback)
|
.on('end', callback)
|
||||||
.on('progress', (count) => {
|
.on('progress', (count) => {
|
||||||
|
|
@ -65,3 +70,4 @@ function _loadFile(dir, n, file, callback) {
|
||||||
function showError({message}) {
|
function showError({message}) {
|
||||||
alert(message);
|
alert(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,40 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
/* global DOM */
|
/* global DOM */
|
||||||
|
|
||||||
|
const Info = DOM.CurrentInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Функция генерирует JSON из html-таблицы файлов и
|
* Функция генерирует JSON из html-таблицы файлов и
|
||||||
* используеться при первом заходе в корень
|
* используеться при первом заходе в корень
|
||||||
*/
|
*/
|
||||||
export const getJsonFromFileTable = () => {
|
module.exports = () => {
|
||||||
const Info = DOM.CurrentInfo;
|
|
||||||
const path = DOM.getCurrentDirPath();
|
const path = DOM.getCurrentDirPath();
|
||||||
const infoFiles = Info.files || [];
|
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
|
const files = infoFiles
|
||||||
.filter(notParent)
|
.filter(notParent)
|
||||||
.map(parse);
|
.map(parse);
|
||||||
|
|
@ -20,25 +47,3 @@ export const getJsonFromFileTable = () => {
|
||||||
return fileTable;
|
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,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
export const createBinder = () => {
|
|
||||||
let binded = false;
|
|
||||||
|
|
||||||
return {
|
|
||||||
isBind() {
|
|
||||||
return binded;
|
|
||||||
},
|
|
||||||
setBind() {
|
|
||||||
binded = true;
|
|
||||||
},
|
|
||||||
unsetBind() {
|
|
||||||
binded = false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
import {test} from 'supertape';
|
|
||||||
import {createBinder} from './binder.js';
|
|
||||||
|
|
||||||
test('client: key: binder: isBind: default', (t) => {
|
|
||||||
const binder = createBinder();
|
|
||||||
|
|
||||||
t.notOk(binder.isBind(), 'should not be bind by default');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('client: key: binder: setBind', (t) => {
|
|
||||||
const binder = createBinder();
|
|
||||||
|
|
||||||
binder.setBind();
|
|
||||||
|
|
||||||
t.ok(binder.isBind(), 'should be bind');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('client: key: binder: unsetBind', (t) => {
|
|
||||||
const binder = createBinder();
|
|
||||||
|
|
||||||
binder.setBind();
|
|
||||||
binder.unsetBind();
|
|
||||||
|
|
||||||
t.notOk(binder.isBind(), 'should not be bind');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,56 +0,0 @@
|
||||||
import autoGlobals from 'auto-globals';
|
|
||||||
import supertape from 'supertape';
|
|
||||||
import {ESC} from './key.js';
|
|
||||||
import {Key, _listener} from './index.js';
|
|
||||||
import {getDOM, getCloudCmd} from './vim/globals.fixture.js';
|
|
||||||
|
|
||||||
const test = autoGlobals(supertape);
|
|
||||||
const {stub} = supertape;
|
|
||||||
|
|
||||||
globalThis.DOM = getDOM();
|
|
||||||
globalThis.CloudCmd = getCloudCmd();
|
|
||||||
|
|
||||||
test('cloudcmd: client: key: enable vim', async (t) => {
|
|
||||||
const vim = stub();
|
|
||||||
const config = stub().returns(true);
|
|
||||||
const _config = stub();
|
|
||||||
|
|
||||||
const event = {
|
|
||||||
keyCode: ESC,
|
|
||||||
key: 'Escape',
|
|
||||||
altKey: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
Key.setBind();
|
|
||||||
|
|
||||||
await _listener(event, {
|
|
||||||
vim,
|
|
||||||
config,
|
|
||||||
_config,
|
|
||||||
switchKey: stub(),
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWith(vim, ['Escape', event]);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: key: disable vim', async (t) => {
|
|
||||||
const _config = stub();
|
|
||||||
const config = stub();
|
|
||||||
|
|
||||||
const event = {
|
|
||||||
keyCode: ESC,
|
|
||||||
key: 'Escape',
|
|
||||||
altKey: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
Key.setBind();
|
|
||||||
await _listener(event, {
|
|
||||||
config,
|
|
||||||
_config,
|
|
||||||
switchKey: stub(),
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWith(_config, ['vim', true]);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
@ -1,58 +1,81 @@
|
||||||
export const BACKSPACE = 8;
|
'use strict';
|
||||||
export const TAB = 9;
|
|
||||||
export const ENTER = 13;
|
module.exports = {
|
||||||
export const CAPSLOCK = 20;
|
BACKSPACE : 8,
|
||||||
export const ESC = 27;
|
TAB : 9,
|
||||||
export const SPACE = 32;
|
ENTER : 13,
|
||||||
export const PAGE_UP = 33;
|
ESC : 27,
|
||||||
export const PAGE_DOWN = 34;
|
|
||||||
export const END = 35;
|
SPACE : 32,
|
||||||
export const HOME = 36;
|
PAGE_UP : 33,
|
||||||
export const LEFT = 37;
|
PAGE_DOWN : 34,
|
||||||
export const UP = 38;
|
END : 35,
|
||||||
export const RIGHT = 39;
|
HOME : 36,
|
||||||
export const DOWN = 40;
|
|
||||||
export const INSERT = 45;
|
LEFT : 37,
|
||||||
export const DELETE = 46;
|
UP : 38,
|
||||||
export const ZERO = 48;
|
RIGHT : 39,
|
||||||
export const SEMICOLON = 52;
|
DOWN : 40,
|
||||||
export const A = 65;
|
|
||||||
export const C = 67;
|
INSERT : 45,
|
||||||
export const D = 68;
|
DELETE : 46,
|
||||||
export const G = 71;
|
|
||||||
export const J = 74;
|
ZERO : 48,
|
||||||
export const K = 75;
|
|
||||||
export const L = 76;
|
SEMICOLON : 52,
|
||||||
export const M = 77;
|
|
||||||
export const O = 79;
|
COLON : 54,
|
||||||
export const P = 80;
|
|
||||||
export const Q = 81;
|
A : 65,
|
||||||
export const R = 82;
|
|
||||||
export const S = 83;
|
C : 67,
|
||||||
export const T = 84;
|
D : 68,
|
||||||
export const U = 85;
|
|
||||||
export const V = 86;
|
G : 71,
|
||||||
export const X = 88;
|
|
||||||
export const Z = 90;
|
J : 74,
|
||||||
export const INSERT_MAC = 96;
|
K : 75,
|
||||||
export const ASTERISK = 106;
|
|
||||||
export const PLUS = 107;
|
M : 77,
|
||||||
export const MINUS = 109;
|
|
||||||
export const F1 = 112;
|
O : 79,
|
||||||
export const F2 = 113;
|
P : 80,
|
||||||
export const F3 = 114;
|
Q : 81,
|
||||||
export const F4 = 115;
|
R : 82,
|
||||||
export const F5 = 116;
|
S : 83,
|
||||||
export const F6 = 117;
|
T : 84,
|
||||||
export const F7 = 118;
|
U : 85,
|
||||||
export const F8 = 119;
|
|
||||||
export const F9 = 120;
|
V : 86,
|
||||||
export const F10 = 121;
|
|
||||||
export const COLON = 186;
|
X : 88,
|
||||||
export const EQUAL = 187;
|
|
||||||
export const HYPHEN = 189;
|
Z : 90,
|
||||||
export const DOT = 190;
|
|
||||||
export const SLASH = 191;
|
INSERT_MAC : 96,
|
||||||
export const TRA = 192;
|
|
||||||
export const BACKSLASH = 220;
|
ASTERISK : 106,
|
||||||
export const BRACKET_CLOSE = 221;
|
PLUS : 107,
|
||||||
|
MINUS : 109,
|
||||||
|
|
||||||
|
F1 : 112,
|
||||||
|
F2 : 113,
|
||||||
|
F3 : 114,
|
||||||
|
F4 : 115,
|
||||||
|
F5 : 116,
|
||||||
|
F6 : 117,
|
||||||
|
F7 : 118,
|
||||||
|
F8 : 119,
|
||||||
|
F9 : 120,
|
||||||
|
F10 : 121,
|
||||||
|
|
||||||
|
EQUAL : 187,
|
||||||
|
HYPHEN : 189,
|
||||||
|
DOT : 190,
|
||||||
|
SLASH : 191,
|
||||||
|
TRA : 192, /* Typewritten Reverse Apostrophe (`) */
|
||||||
|
BACKSLASH : 220,
|
||||||
|
|
||||||
|
BRACKET_CLOSE: 221,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,18 @@
|
||||||
/* global DOM */
|
/* global DOM */
|
||||||
import {escapeRegExp} from '#common/util';
|
|
||||||
|
|
||||||
export default function setCurrentByChar(char, charStore) {
|
'use strict';
|
||||||
const Info = DOM.CurrentInfo;
|
|
||||||
|
const Info = DOM.CurrentInfo;
|
||||||
|
const {escapeRegExp} = require('../../common/util');
|
||||||
|
|
||||||
|
module.exports = function setCurrentByChar(char, charStore) {
|
||||||
let firstByName;
|
let firstByName;
|
||||||
let skipCount = 0;
|
let skipCount = 0;
|
||||||
let set = false;
|
let setted = false;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
const escapeChar = escapeRegExp(char);
|
const escapeChar = escapeRegExp(char);
|
||||||
const regExp = new RegExp(`^${escapeChar}.*$`, 'i');
|
const regExp = new RegExp('^' + escapeChar + '.*$', 'i');
|
||||||
const {files} = Info;
|
const {files} = Info;
|
||||||
const chars = charStore();
|
const chars = charStore();
|
||||||
const n = chars.length;
|
const n = chars.length;
|
||||||
|
|
@ -28,14 +31,12 @@ export default function setCurrentByChar(char, charStore) {
|
||||||
const isTest = (a) => regExp.test(a);
|
const isTest = (a) => regExp.test(a);
|
||||||
const isRoot = (a) => a === '..';
|
const isRoot = (a) => a === '..';
|
||||||
const not = (f) => (a) => !f(a);
|
const not = (f) => (a) => !f(a);
|
||||||
|
|
||||||
const setCurrent = (name) => {
|
const setCurrent = (name) => {
|
||||||
const byName = DOM.getCurrentByName(name);
|
const byName = DOM.getCurrentByName(name);
|
||||||
|
|
||||||
if (!skipCount) {
|
if (!skipCount) {
|
||||||
set = true;
|
setted = true;
|
||||||
DOM.setCurrentFile(byName);
|
DOM.setCurrentFile(byName);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,8 +51,9 @@ export default function setCurrentByChar(char, charStore) {
|
||||||
.filter(not(isRoot))
|
.filter(not(isRoot))
|
||||||
.some(setCurrent);
|
.some(setCurrent);
|
||||||
|
|
||||||
if (!set) {
|
if (!setted) {
|
||||||
DOM.setCurrentFile(firstByName);
|
DOM.setCurrentFile(firstByName);
|
||||||
charStore([char]);
|
charStore([char]);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
import {fullstore} from 'fullstore';
|
'use strict';
|
||||||
import limier from 'limier';
|
|
||||||
|
const fullstore = require('fullstore');
|
||||||
|
const limier = require('limier');
|
||||||
|
|
||||||
const searchStore = fullstore([]);
|
const searchStore = fullstore([]);
|
||||||
const searchIndex = fullstore(0);
|
const searchIndex = fullstore(0);
|
||||||
|
|
||||||
export const find = (value, names) => {
|
module.exports.find = (value, names) => {
|
||||||
const result = limier(value, names);
|
const result = limier(value, names);
|
||||||
|
|
||||||
searchStore(result);
|
searchStore(result);
|
||||||
|
|
@ -13,7 +15,7 @@ export const find = (value, names) => {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const findNext = () => {
|
module.exports.findNext = () => {
|
||||||
const names = searchStore();
|
const names = searchStore();
|
||||||
const index = next(searchIndex(), names.length);
|
const index = next(searchIndex(), names.length);
|
||||||
|
|
||||||
|
|
@ -21,7 +23,7 @@ export const findNext = () => {
|
||||||
return names[searchIndex()];
|
return names[searchIndex()];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const findPrevious = () => {
|
module.exports.findPrevious = () => {
|
||||||
const names = searchStore();
|
const names = searchStore();
|
||||||
const index = previous(searchIndex(), names.length);
|
const index = previous(searchIndex(), names.length);
|
||||||
|
|
||||||
|
|
@ -29,8 +31,8 @@ export const findPrevious = () => {
|
||||||
return names[index];
|
return names[index];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const _next = next;
|
module.exports._next = next;
|
||||||
export const _previous = previous;
|
module.exports._previous = previous;
|
||||||
|
|
||||||
function next(index, length) {
|
function next(index, length) {
|
||||||
if (index === length - 1)
|
if (index === length - 1)
|
||||||
|
|
@ -45,3 +47,4 @@ function previous(index, length) {
|
||||||
|
|
||||||
return --index;
|
return --index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
import test from 'supertape';
|
'use strict';
|
||||||
import {getDOM} from './globals.fixture.js';
|
|
||||||
import {
|
const test = require('supertape');
|
||||||
|
const dir = './';
|
||||||
|
|
||||||
|
const {getDOM} = require('./globals.fixture');
|
||||||
|
|
||||||
|
global.DOM = getDOM();
|
||||||
|
|
||||||
|
const {
|
||||||
_next,
|
_next,
|
||||||
_previous,
|
_previous,
|
||||||
find,
|
} = require(dir + 'find');
|
||||||
findNext,
|
|
||||||
findPrevious,
|
|
||||||
} from './find.js';
|
|
||||||
|
|
||||||
globalThis.DOM = getDOM();
|
|
||||||
|
|
||||||
test('cloudcmd: client: vim: _next', (t) => {
|
test('cloudcmd: client: vim: _next', (t) => {
|
||||||
const result = _next(1, 2);
|
const result = _next(1, 2);
|
||||||
|
|
@ -17,13 +19,6 @@ test('cloudcmd: client: vim: _next', (t) => {
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: vim: _next: increment', (t) => {
|
|
||||||
const result = _next(0, 2);
|
|
||||||
|
|
||||||
t.equal(result, 1, 'should return 1');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: vim: _previous', (t) => {
|
test('cloudcmd: client: vim: _previous', (t) => {
|
||||||
const result = _previous(0, 2);
|
const result = _previous(0, 2);
|
||||||
|
|
||||||
|
|
@ -31,25 +26,3 @@ test('cloudcmd: client: vim: _previous', (t) => {
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: vim: _previous: decrement', (t) => {
|
|
||||||
const result = _previous(1, 2);
|
|
||||||
|
|
||||||
t.equal(result, 0, 'should return 0');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: vim: findNext: after find', (t) => {
|
|
||||||
find('a', ['alpha', 'beta', 'apple']);
|
|
||||||
const result = findNext();
|
|
||||||
|
|
||||||
t.equal(result, 'beta', 'should return next found name');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: vim: findPrevious: after find', (t) => {
|
|
||||||
find('a', ['alpha', 'beta', 'apple']);
|
|
||||||
const result = findPrevious();
|
|
||||||
|
|
||||||
t.equal(result, 'apple', 'should return previous found name');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
const noop = () => {};
|
'use strict';
|
||||||
|
|
||||||
export const getDOM = () => {
|
module.exports.getDOM = () => {
|
||||||
const prompt = Promise.resolve.bind(Promise);
|
const prompt = Promise.resolve.bind(Promise);
|
||||||
const CurrentInfo = {
|
const CurrentInfo = {
|
||||||
element: {},
|
element: {},
|
||||||
files: [],
|
files: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const noop = () => {};
|
||||||
const Buffer = {
|
const Buffer = {
|
||||||
copy: noop,
|
copy: noop,
|
||||||
paste: noop,
|
paste: noop,
|
||||||
|
|
@ -27,20 +28,16 @@ export const getDOM = () => {
|
||||||
getCurrentName: noop,
|
getCurrentName: noop,
|
||||||
setCurrentByName: noop,
|
setCurrentByName: noop,
|
||||||
toggleSelectedFile: noop,
|
toggleSelectedFile: noop,
|
||||||
prompNewDirectory: noop,
|
|
||||||
promptNewFile: noop,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getCloudCmd = () => {
|
module.exports.getCloudCmd = () => {
|
||||||
const show = () => {};
|
const show = () => {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Operation: {
|
Operation: {
|
||||||
show,
|
show,
|
||||||
},
|
},
|
||||||
|
|
||||||
config: noop,
|
|
||||||
_config: noop,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,167 +1,102 @@
|
||||||
import vim from './vim.js';
|
'use strict';
|
||||||
import * as finder from './find.js';
|
|
||||||
import {
|
|
||||||
setCurrent,
|
|
||||||
selectFileNotParent,
|
|
||||||
} from './set-current.js';
|
|
||||||
|
|
||||||
export default (key, event, overrides = {}) => {
|
/* global CloudCmd */
|
||||||
const defaults = {
|
/* global DOM */
|
||||||
...globalThis.DOM,
|
|
||||||
...globalThis.CloudCmd,
|
const vim = require('./vim');
|
||||||
};
|
const finder = require('./find');
|
||||||
|
|
||||||
const deps = {
|
const Info = DOM.CurrentInfo;
|
||||||
...defaults,
|
const {Dialog} = DOM;
|
||||||
...overrides,
|
|
||||||
};
|
module.exports = async (key, event) => {
|
||||||
|
const operations = getOperations(event);
|
||||||
const operations = getOperations(event, deps);
|
await vim(key, operations);
|
||||||
|
|
||||||
vim(key, operations, deps);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOperations = (event, deps) => {
|
const getOperations = (event) => {
|
||||||
const {
|
|
||||||
Info = globalThis.DOM.CurrentInfo,
|
|
||||||
CloudCmd = globalThis.CloudCmd,
|
|
||||||
Operation,
|
|
||||||
unselectFiles,
|
|
||||||
setCurrentFile,
|
|
||||||
setCurrentByName,
|
|
||||||
getCurrentName,
|
|
||||||
prompt = globalThis.DOM.Dialog.prompt,
|
|
||||||
preventDefault = event?.preventDefault?.bind(event),
|
|
||||||
stopImmediatePropagation = event?.preventDefault?.bind(event),
|
|
||||||
promptNewFile = globalThis.DOM.promptNewFile,
|
|
||||||
toggleSelectedFile,
|
|
||||||
Buffer = {},
|
|
||||||
createFindNext = _createFindNext,
|
|
||||||
createFindPrevious = _createFindPrevious,
|
|
||||||
createMakeFile = _createMakeFile,
|
|
||||||
renameCurrent,
|
|
||||||
} = deps;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
makeFile: createMakeFile({
|
escape: DOM.unselectFiles,
|
||||||
promptNewFile,
|
|
||||||
preventDefault,
|
|
||||||
stopImmediatePropagation,
|
|
||||||
}),
|
|
||||||
findNext: createFindNext({
|
|
||||||
setCurrentByName,
|
|
||||||
}),
|
|
||||||
findPrevious: createFindPrevious({
|
|
||||||
setCurrentByName,
|
|
||||||
}),
|
|
||||||
escape: unselectFiles,
|
|
||||||
rename: () => {
|
|
||||||
event.preventDefault();
|
|
||||||
renameCurrent();
|
|
||||||
},
|
|
||||||
remove: () => {
|
remove: () => {
|
||||||
Operation.show('delete');
|
CloudCmd.Operation.show('delete');
|
||||||
},
|
},
|
||||||
operationCopy: () => {
|
|
||||||
event.preventDefault();
|
|
||||||
Operation.show('copy');
|
|
||||||
},
|
|
||||||
operationMove: () => {
|
|
||||||
event.preventDefault();
|
|
||||||
Operation.show('move');
|
|
||||||
},
|
|
||||||
|
|
||||||
makeDirectory: () => {
|
|
||||||
event.stopImmediatePropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
globalThis.DOM.promptNewDir();
|
|
||||||
},
|
|
||||||
|
|
||||||
terminal: () => {
|
|
||||||
CloudCmd.Terminal.show();
|
|
||||||
},
|
|
||||||
|
|
||||||
edit: () => {
|
|
||||||
CloudCmd.EditFileVim.show();
|
|
||||||
},
|
|
||||||
|
|
||||||
copy: () => {
|
copy: () => {
|
||||||
Buffer.copy();
|
DOM.Buffer.copy();
|
||||||
unselectFiles();
|
DOM.unselectFiles();
|
||||||
},
|
},
|
||||||
|
|
||||||
select: () => {
|
select: () => {
|
||||||
const current = Info.element;
|
const current = Info.element;
|
||||||
toggleSelectedFile(current);
|
DOM.toggleSelectedFile(current);
|
||||||
},
|
},
|
||||||
|
paste: DOM.Buffer.paste,
|
||||||
paste: Buffer.paste,
|
|
||||||
|
|
||||||
moveNext: ({count, isVisual, isDelete}) => {
|
moveNext: ({count, isVisual, isDelete}) => {
|
||||||
setCurrent('next', {
|
setCurrent('next', {
|
||||||
count,
|
count,
|
||||||
isVisual,
|
isVisual,
|
||||||
isDelete,
|
isDelete,
|
||||||
}, {
|
|
||||||
Info,
|
|
||||||
setCurrentFile,
|
|
||||||
unselectFiles,
|
|
||||||
Operation,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
movePrevious: ({count, isVisual, isDelete}) => {
|
movePrevious: ({count, isVisual, isDelete}) => {
|
||||||
setCurrent('previous', {
|
setCurrent('previous', {
|
||||||
count,
|
count,
|
||||||
isVisual,
|
isVisual,
|
||||||
isDelete,
|
isDelete,
|
||||||
}, {
|
|
||||||
Info,
|
|
||||||
setCurrentFile,
|
|
||||||
unselectFiles,
|
|
||||||
Operation,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
find: async () => {
|
find: async () => {
|
||||||
preventDefault();
|
event.preventDefault();
|
||||||
const [, value] = await prompt('Find', '');
|
const [, value] = await Dialog.prompt('Find', '');
|
||||||
|
|
||||||
if (!value)
|
if (!value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const names = Info.files.map(getCurrentName);
|
const names = Info.files.map(DOM.getCurrentName);
|
||||||
const [result] = finder.find(value, names);
|
const [result] = finder.find(value, names);
|
||||||
|
|
||||||
setCurrentByName(result);
|
DOM.setCurrentByName(result);
|
||||||
|
},
|
||||||
|
findNext: () => {
|
||||||
|
const name = finder.findNext();
|
||||||
|
DOM.setCurrentByName(name);
|
||||||
|
},
|
||||||
|
findPrevious: () => {
|
||||||
|
const name = finder.findPrevious();
|
||||||
|
DOM.setCurrentByName(name);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const selectFile = selectFileNotParent;
|
module.exports.selectFile = selectFile;
|
||||||
|
|
||||||
const _createFindPrevious = (overrides = {}) => () => {
|
function selectFile(current) {
|
||||||
const {setCurrentByName} = overrides;
|
const name = DOM.getCurrentName(current);
|
||||||
const name = finder.findPrevious();
|
|
||||||
|
|
||||||
setCurrentByName(name);
|
if (name === '..')
|
||||||
};
|
return;
|
||||||
|
|
||||||
|
DOM.selectFile(current);
|
||||||
|
}
|
||||||
|
|
||||||
const _createFindNext = (overrides = {}) => () => {
|
function setCurrent(sibling, {count, isVisual, isDelete}) {
|
||||||
const {setCurrentByName} = overrides;
|
let current = Info.element;
|
||||||
const name = finder.findNext();
|
const select = isVisual ? selectFile : DOM.unselectFile;
|
||||||
|
|
||||||
setCurrentByName(name);
|
select(current);
|
||||||
};
|
|
||||||
|
const position = `${sibling}Sibling`;
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const next = current[position];
|
||||||
|
|
||||||
|
if (!next)
|
||||||
|
break;
|
||||||
|
|
||||||
|
current = next;
|
||||||
|
select(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
DOM.setCurrentFile(current);
|
||||||
|
|
||||||
|
if (isDelete)
|
||||||
|
CloudCmd.Operation.show('delete');
|
||||||
|
}
|
||||||
|
|
||||||
const _createMakeFile = (overrides = {}) => () => {
|
|
||||||
const {
|
|
||||||
promptNewFile,
|
|
||||||
stopImmediatePropagation,
|
|
||||||
preventDefault,
|
|
||||||
} = overrides;
|
|
||||||
|
|
||||||
stopImmediatePropagation();
|
|
||||||
preventDefault();
|
|
||||||
promptNewFile();
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,46 @@
|
||||||
import {test, stub} from 'supertape';
|
'use strict';
|
||||||
import {getDOM, getCloudCmd} from './globals.fixture.js';
|
|
||||||
import vim, {selectFile as vimSelectFile} from './index.js';
|
|
||||||
import * as finder from './find.js';
|
|
||||||
|
|
||||||
globalThis.DOM = getDOM();
|
const {join} = require('path');
|
||||||
globalThis.CloudCmd = getCloudCmd();
|
|
||||||
|
|
||||||
const {assign} = Object;
|
const test = require('supertape');
|
||||||
const {DOM} = globalThis;
|
const stub = require('@cloudcmd/stub');
|
||||||
|
const mockRequire = require('mock-require');
|
||||||
|
const {reRequire, stopAll} = mockRequire;
|
||||||
|
|
||||||
|
const dir = '../';
|
||||||
|
|
||||||
|
const pathVim = join(dir, 'vim');
|
||||||
|
const pathFind = join(dir, 'vim', 'find');
|
||||||
|
|
||||||
|
const {
|
||||||
|
getDOM,
|
||||||
|
getCloudCmd,
|
||||||
|
} = require('./globals.fixture');
|
||||||
|
|
||||||
|
global.DOM = getDOM();
|
||||||
|
global.CloudCmd = getCloudCmd();
|
||||||
|
|
||||||
|
const {DOM} = global;
|
||||||
const {Buffer} = DOM;
|
const {Buffer} = DOM;
|
||||||
|
|
||||||
|
const vim = require(pathVim);
|
||||||
|
|
||||||
test('cloudcmd: client: key: set next file: no', (t) => {
|
test('cloudcmd: client: key: set next file: no', (t) => {
|
||||||
const element = {};
|
const element = {
|
||||||
const setCurrentFile = stub();
|
|
||||||
const unselectFiles = stub();
|
|
||||||
|
|
||||||
const Info = {
|
|
||||||
element,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
vim('j', {}, {
|
const setCurrentFile = stub();
|
||||||
Info,
|
|
||||||
setCurrentFile,
|
|
||||||
unselectFiles,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWith(setCurrentFile, [element], 'should set next file');
|
global.DOM.CurrentInfo.element = element;
|
||||||
|
global.DOM.setCurrentFile = setCurrentFile;
|
||||||
|
|
||||||
|
vim('j', {});
|
||||||
|
|
||||||
|
t.ok(setCurrentFile.calledWith(element), 'should set next file');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: set next file current: j', async (t) => {
|
test('cloudcmd: client: key: set next file current', (t) => {
|
||||||
const nextSibling = 'hello';
|
const nextSibling = 'hello';
|
||||||
const element = {
|
const element = {
|
||||||
nextSibling,
|
nextSibling,
|
||||||
|
|
@ -37,21 +48,17 @@ test('cloudcmd: client: key: set next file current: j', async (t) => {
|
||||||
|
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
const Info = {
|
global.DOM.CurrentInfo.element = element;
|
||||||
element,
|
global.DOM.setCurrentFile = setCurrentFile;
|
||||||
};
|
|
||||||
|
|
||||||
await vim('j', {}, {
|
vim('j', {});
|
||||||
Info,
|
|
||||||
setCurrentFile,
|
t.ok(setCurrentFile.calledWith(nextSibling), 'should set next file');
|
||||||
unselectFiles: stub(),
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWith(setCurrentFile, [nextSibling], 'should set next file');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: set next file current: mjj', (t) => {
|
test('cloudcmd: client: key: set next file current', (t) => {
|
||||||
const nextSibling = 'hello';
|
const nextSibling = 'hello';
|
||||||
const element = {
|
const element = {
|
||||||
nextSibling,
|
nextSibling,
|
||||||
|
|
@ -59,21 +66,15 @@ test('cloudcmd: client: key: set next file current: mjj', (t) => {
|
||||||
|
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
const Info = {
|
global.DOM.CurrentInfo.element = element;
|
||||||
element,
|
global.DOM.setCurrentFile = setCurrentFile;
|
||||||
};
|
|
||||||
|
|
||||||
const deps = {
|
vim('m', {});
|
||||||
Info,
|
vim('j', {});
|
||||||
setCurrentFile,
|
vim('j', {});
|
||||||
unselectFiles: stub(),
|
|
||||||
};
|
|
||||||
|
|
||||||
vim('m', {}, deps);
|
t.ok(setCurrentFile.calledWith(nextSibling), 'should set next file');
|
||||||
vim('j', {}, deps);
|
|
||||||
vim('j', {}, deps);
|
|
||||||
|
|
||||||
t.calledWith(setCurrentFile, [nextSibling], 'should set next file');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -85,44 +86,38 @@ test('cloudcmd: client: key: set next file current: g', (t) => {
|
||||||
|
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
const Info = {
|
global.DOM.CurrentInfo.element = element;
|
||||||
element,
|
global.DOM.setCurrentFile = setCurrentFile;
|
||||||
};
|
|
||||||
|
|
||||||
const deps = {
|
vim('g', {});
|
||||||
Info,
|
vim('j', {});
|
||||||
setCurrentFile,
|
|
||||||
unselectFiles: stub(),
|
|
||||||
};
|
|
||||||
|
|
||||||
vim('g', {}, deps);
|
t.ok(setCurrentFile.calledWith(nextSibling), 'should ignore g');
|
||||||
vim('j', {}, deps);
|
|
||||||
|
|
||||||
t.calledWith(setCurrentFile, [nextSibling], 'should ignore g');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: set +2 file current', (t) => {
|
test('cloudcmd: client: key: set +2 file current', (t) => {
|
||||||
const last = {};
|
const last = {};
|
||||||
|
const nextSibling = {
|
||||||
|
nextSibling: last,
|
||||||
|
};
|
||||||
|
const element = {
|
||||||
|
nextSibling,
|
||||||
|
};
|
||||||
|
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
const element = {};
|
|
||||||
|
|
||||||
const Info = {
|
global.DOM.CurrentInfo.element = element;
|
||||||
element,
|
global.DOM.setCurrentFile = setCurrentFile;
|
||||||
};
|
|
||||||
|
|
||||||
const deps = {
|
|
||||||
setCurrentFile,
|
|
||||||
Info,
|
|
||||||
unselectFiles: stub(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const event = {};
|
const event = {};
|
||||||
|
|
||||||
vim('2', event, deps);
|
vim('2', event);
|
||||||
vim('j', event, deps);
|
vim('j', event);
|
||||||
|
|
||||||
|
t.ok(setCurrentFile.calledWith(last), 'should set next file');
|
||||||
|
|
||||||
t.calledWith(setCurrentFile, [last], 'should set next file');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -131,39 +126,25 @@ test('cloudcmd: client: key: select +2 files from current before delete', (t) =>
|
||||||
const nextSibling = {
|
const nextSibling = {
|
||||||
nextSibling: last,
|
nextSibling: last,
|
||||||
};
|
};
|
||||||
|
|
||||||
const element = {
|
const element = {
|
||||||
nextSibling,
|
nextSibling,
|
||||||
};
|
};
|
||||||
|
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
const Info = {
|
global.DOM.CurrentInfo.element = element;
|
||||||
element,
|
global.DOM.setCurrentFile = setCurrentFile;
|
||||||
};
|
global.DOM.selectFile = stub();
|
||||||
|
global.DOM.getCurrentName = () => false;
|
||||||
const Operation = {
|
global.CloudCmd.Operation.show = stub();
|
||||||
show: stub(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectFile = stub();
|
|
||||||
const getCurrentName = stub().returns('x');
|
|
||||||
|
|
||||||
const event = {};
|
const event = {};
|
||||||
|
|
||||||
const deps = {
|
vim('d', event);
|
||||||
Info,
|
vim('2', event);
|
||||||
setCurrentFile,
|
vim('j', event);
|
||||||
selectFile,
|
|
||||||
getCurrentName,
|
|
||||||
Operation,
|
|
||||||
};
|
|
||||||
|
|
||||||
vim('d', event, deps);
|
t.ok(setCurrentFile.calledWith(last), 'should set next file');
|
||||||
vim('2', event, deps);
|
|
||||||
vim('j', event, deps);
|
|
||||||
|
|
||||||
t.calledWith(setCurrentFile, [last], 'should set next file');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -172,7 +153,6 @@ test('cloudcmd: client: key: delete +2 files from current', (t) => {
|
||||||
const nextSibling = {
|
const nextSibling = {
|
||||||
nextSibling: last,
|
nextSibling: last,
|
||||||
};
|
};
|
||||||
|
|
||||||
const element = {
|
const element = {
|
||||||
nextSibling,
|
nextSibling,
|
||||||
};
|
};
|
||||||
|
|
@ -180,26 +160,20 @@ test('cloudcmd: client: key: delete +2 files from current', (t) => {
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
const show = stub();
|
const show = stub();
|
||||||
|
|
||||||
const deps = {
|
global.DOM.CurrentInfo.element = element;
|
||||||
Info: {
|
global.DOM.setCurrentFile = setCurrentFile;
|
||||||
element,
|
global.DOM.selectFile = stub();
|
||||||
},
|
global.DOM.getCurrentName = () => false;
|
||||||
Operation: {
|
global.CloudCmd.Operation.show = show;
|
||||||
show,
|
|
||||||
},
|
|
||||||
setCurrentFile,
|
|
||||||
selectFile: stub(),
|
|
||||||
getCurrentName: stub().returns('x'),
|
|
||||||
unselectFiles: stub(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const event = {};
|
const event = {};
|
||||||
|
|
||||||
vim('d', event, deps);
|
vim('d', event);
|
||||||
vim('2', event, deps);
|
vim('2', event);
|
||||||
vim('j', event, deps);
|
vim('j', event);
|
||||||
|
|
||||||
|
t.ok(show.calledWith('delete'), 'should call delete');
|
||||||
|
|
||||||
t.calledWith(show, ['delete'], 'should call delete');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -210,84 +184,49 @@ test('cloudcmd: client: key: set previous file current', (t) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
const unselectFiles = stub();
|
|
||||||
|
|
||||||
const Info = {
|
global.DOM.CurrentInfo.element = element;
|
||||||
element,
|
global.DOM.setCurrentFile = setCurrentFile;
|
||||||
};
|
|
||||||
|
|
||||||
const deps = {
|
vim('k', {});
|
||||||
Info,
|
|
||||||
setCurrentFile,
|
|
||||||
unselectFiles,
|
|
||||||
};
|
|
||||||
|
|
||||||
vim('k', {}, deps);
|
t.ok(setCurrentFile.calledWith(previousSibling), 'should set previous file');
|
||||||
|
|
||||||
t.calledWith(setCurrentFile, [previousSibling], 'should set previous file');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: copy: no', (t) => {
|
test('cloudcmd: client: key: copy: no', (t) => {
|
||||||
const copy = stub();
|
const copy = stub();
|
||||||
|
|
||||||
vim('y', {}, {
|
Buffer.copy = copy;
|
||||||
unselectFiles: stub(),
|
|
||||||
Buffer: {
|
vim('y', {});
|
||||||
copy,
|
|
||||||
},
|
t.notOk(copy.called, 'should not copy files');
|
||||||
});
|
|
||||||
|
|
||||||
t.notCalled(copy, 'should not copy files');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: copy', (t) => {
|
test('cloudcmd: client: key: copy', (t) => {
|
||||||
const copy = stub();
|
const copy = stub();
|
||||||
const Info = {
|
|
||||||
element: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleSelectedFile = stub();
|
Buffer.copy = copy;
|
||||||
const unselectFiles = stub();
|
|
||||||
|
|
||||||
const deps = {
|
vim('v', {});
|
||||||
Info,
|
vim('y', {});
|
||||||
unselectFiles,
|
|
||||||
toggleSelectedFile,
|
|
||||||
Buffer: {
|
|
||||||
copy,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
vim('v', {}, deps);
|
t.ok(copy.calledWith(), 'should copy files');
|
||||||
vim('y', {}, deps);
|
|
||||||
|
|
||||||
t.calledWithNoArgs(copy, 'should copy files');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: copy: unselectFiles', (t) => {
|
test('cloudcmd: client: key: copy: unselectFiles', (t) => {
|
||||||
const unselectFiles = stub();
|
const unselectFiles = stub();
|
||||||
const Info = {
|
|
||||||
element: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleSelectedFile = stub();
|
DOM.unselectFiles = unselectFiles;
|
||||||
|
|
||||||
const deps = {
|
vim('v', {});
|
||||||
Info,
|
vim('y', {});
|
||||||
unselectFiles,
|
|
||||||
toggleSelectedFile,
|
|
||||||
Buffer: {
|
|
||||||
copy: stub(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
vim('v', {}, deps);
|
t.ok(unselectFiles.calledWith(), 'should unselect files');
|
||||||
vim('y', {}, deps);
|
|
||||||
|
|
||||||
t.calledWithNoArgs(unselectFiles, 'should unselect files');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -296,91 +235,63 @@ test('cloudcmd: client: key: paste', (t) => {
|
||||||
|
|
||||||
Buffer.paste = paste;
|
Buffer.paste = paste;
|
||||||
|
|
||||||
vim('p', {}, {
|
vim('p', {});
|
||||||
Buffer,
|
|
||||||
});
|
t.ok(paste.calledWith(), 'should paste files');
|
||||||
|
|
||||||
t.calledWithNoArgs(paste, 'should paste files');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: selectFile: ..', (t) => {
|
test('cloudcmd: client: key: selectFile: ..', (t) => {
|
||||||
const getCurrentName = stub().returns('..');
|
|
||||||
const selectFile = stub();
|
const selectFile = stub();
|
||||||
|
const getCurrentName = stub();
|
||||||
|
|
||||||
|
DOM.selectFile = selectFile;
|
||||||
|
DOM.getCurrentName = () => '..';
|
||||||
|
|
||||||
const current = {};
|
const current = {};
|
||||||
|
vim.selectFile(current);
|
||||||
|
|
||||||
vimSelectFile(current, {
|
t.notOk(getCurrentName.called, 'should not call selectFile');
|
||||||
selectFile,
|
|
||||||
getCurrentName,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.notCalled(selectFile, 'should not call selectFile');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: selectFile', (t) => {
|
test('cloudcmd: client: key: selectFile', (t) => {
|
||||||
const selectFile = stub();
|
const selectFile = stub();
|
||||||
const getCurrentName = stub().returns('x');
|
|
||||||
|
DOM.selectFile = selectFile;
|
||||||
|
DOM.getCurrentName = (a) => a.name;
|
||||||
|
|
||||||
const current = {};
|
const current = {};
|
||||||
|
|
||||||
vimSelectFile(current, {
|
vim.selectFile(current);
|
||||||
selectFile,
|
|
||||||
getCurrentName,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWith(selectFile, [current], 'should call selectFile');
|
t.ok(selectFile.calledWith(current), 'should call selectFile');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: set last file current: shift + g', async (t) => {
|
test('cloudcmd: client: key: set last file current', (t) => {
|
||||||
const last = 'last';
|
const last = 'last';
|
||||||
const nextSibling = {
|
const nextSibling = {
|
||||||
nextSibling: last,
|
nextSibling: last,
|
||||||
};
|
};
|
||||||
|
|
||||||
const element = {
|
const element = {
|
||||||
nextSibling,
|
nextSibling,
|
||||||
};
|
};
|
||||||
|
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
await vim('G', {}, {
|
global.DOM.CurrentInfo.element = element;
|
||||||
Info: {
|
global.DOM.setCurrentFile = setCurrentFile;
|
||||||
element,
|
|
||||||
},
|
vim('G', {});
|
||||||
setCurrentFile,
|
|
||||||
unselectFiles: stub(),
|
t.ok(setCurrentFile.calledWith(last), 'should set last file');
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWith(setCurrentFile, [last], 'should set last file');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: set last file current: $', (t) => {
|
test('cloudcmd: client: key: set first file current', (t) => {
|
||||||
const last = 'last';
|
|
||||||
const nextSibling = {
|
|
||||||
nextSibling: last,
|
|
||||||
};
|
|
||||||
|
|
||||||
const element = {
|
|
||||||
nextSibling,
|
|
||||||
};
|
|
||||||
|
|
||||||
const setCurrentFile = stub();
|
|
||||||
|
|
||||||
vim('$', {}, {
|
|
||||||
Info: {
|
|
||||||
element,
|
|
||||||
},
|
|
||||||
setCurrentFile,
|
|
||||||
unselectFiles: stub(),
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWith(setCurrentFile, [last], 'should set last file');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: key: set first file current: gg', (t) => {
|
|
||||||
const first = 'first';
|
const first = 'first';
|
||||||
const previousSibling = {
|
const previousSibling = {
|
||||||
previousSibling: first,
|
previousSibling: first,
|
||||||
|
|
@ -390,121 +301,66 @@ test('cloudcmd: client: key: set first file current: gg', (t) => {
|
||||||
previousSibling,
|
previousSibling,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Operation = {
|
|
||||||
show: stub(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const unselectFiles = stub();
|
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
const deps = {
|
global.DOM.CurrentInfo.element = element;
|
||||||
Operation,
|
global.DOM.setCurrentFile = setCurrentFile;
|
||||||
unselectFiles,
|
|
||||||
setCurrentFile,
|
|
||||||
Info: {
|
|
||||||
element,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
vim('g', {}, deps);
|
vim('g', {});
|
||||||
vim('g', {}, deps);
|
vim('g', {});
|
||||||
|
|
||||||
t.calledWith(setCurrentFile, [first], 'should set first file');
|
t.ok(setCurrentFile.calledWith(first), 'should set first file');
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: key: set first file current: ^', async (t) => {
|
|
||||||
const first = 'first';
|
|
||||||
const previousSibling = {
|
|
||||||
previousSibling: first,
|
|
||||||
};
|
|
||||||
|
|
||||||
const element = {
|
|
||||||
previousSibling,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Operation = {
|
|
||||||
show: stub(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const unselectFiles = stub();
|
|
||||||
const setCurrentFile = stub();
|
|
||||||
|
|
||||||
const deps = {
|
|
||||||
setCurrentFile,
|
|
||||||
Info: {
|
|
||||||
element,
|
|
||||||
},
|
|
||||||
unselectFiles,
|
|
||||||
Operation,
|
|
||||||
};
|
|
||||||
|
|
||||||
await vim('^', {}, deps);
|
|
||||||
|
|
||||||
t.calledWith(setCurrentFile, [first], 'should set first file');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: visual', (t) => {
|
test('cloudcmd: client: key: visual', (t) => {
|
||||||
const element = {};
|
const element = {
|
||||||
const toggleSelectedFile = stub();
|
|
||||||
|
|
||||||
const Info = {
|
|
||||||
element,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
vim('v', {}, {
|
const toggleSelectedFile = stub();
|
||||||
Info,
|
|
||||||
toggleSelectedFile,
|
global.DOM.CurrentInfo.element = element;
|
||||||
});
|
global.DOM.toggleSelectedFile = toggleSelectedFile;
|
||||||
|
|
||||||
|
vim('v', {});
|
||||||
|
|
||||||
|
t.ok(toggleSelectedFile.calledWith(element), 'should toggle selection');
|
||||||
|
|
||||||
t.calledWith(toggleSelectedFile, [element], 'should toggle selection');
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: ESC', (t) => {
|
test('cloudcmd: client: key: ESC', (t) => {
|
||||||
const element = {};
|
const element = {
|
||||||
const unselectFiles = stub();
|
|
||||||
|
|
||||||
const Info = {
|
|
||||||
element,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
vim('Escape', null, {
|
const unselectFiles = stub();
|
||||||
Info,
|
|
||||||
unselectFiles,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWithNoArgs(unselectFiles, 'should toggle selection');
|
global.DOM.CurrentInfo.element = element;
|
||||||
|
global.DOM.unselectFiles = unselectFiles ;
|
||||||
|
|
||||||
|
vim('Escape');
|
||||||
|
|
||||||
|
t.ok(unselectFiles.calledWith(), 'should toggle selection');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: Enter', async (t) => {
|
test('cloudcmd: client: key: Enter', (t) => {
|
||||||
const nextSibling = 'hello';
|
const nextSibling = 'hello';
|
||||||
const element = {
|
const element = {
|
||||||
nextSibling,
|
nextSibling,
|
||||||
};
|
};
|
||||||
|
|
||||||
const unselectFiles = stub();
|
|
||||||
const setCurrentFile = stub();
|
const setCurrentFile = stub();
|
||||||
|
|
||||||
const Info = {
|
DOM.CurrentInfo.element = element;
|
||||||
element,
|
DOM.setCurrentFile = setCurrentFile;
|
||||||
};
|
|
||||||
|
|
||||||
await vim('Enter', null, {
|
vim('Enter');
|
||||||
Info,
|
|
||||||
setCurrentFile,
|
|
||||||
unselectFiles,
|
|
||||||
});
|
|
||||||
|
|
||||||
await vim('j', null, {
|
vim('j');
|
||||||
Info,
|
|
||||||
setCurrentFile,
|
|
||||||
unselectFiles,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWith(setCurrentFile, [nextSibling], 'should set next file');
|
t.ok(setCurrentFile.calledWith(nextSibling), 'should set next file');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -512,245 +368,50 @@ test('cloudcmd: client: key: /', (t) => {
|
||||||
const preventDefault = stub();
|
const preventDefault = stub();
|
||||||
const element = {};
|
const element = {};
|
||||||
|
|
||||||
const Info = {
|
DOM.CurrentInfo.element = element;
|
||||||
element,
|
DOM.getCurrentName = () => '';
|
||||||
files: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCurrentName = stub().returns('');
|
vim('/', {
|
||||||
|
|
||||||
const event = {
|
|
||||||
preventDefault,
|
preventDefault,
|
||||||
};
|
|
||||||
|
|
||||||
const prompt = stub().returns([]);
|
|
||||||
|
|
||||||
vim('/', event, {
|
|
||||||
getCurrentName,
|
|
||||||
Info,
|
|
||||||
prompt,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
t.calledWithNoArgs(preventDefault);
|
t.ok(preventDefault.calledWith(), 'should call preventDefault');
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: find', (t) => {
|
|
||||||
assign(DOM.Dialog, {
|
|
||||||
prompt: stub().returns([]),
|
|
||||||
});
|
|
||||||
|
|
||||||
const setCurrentByName = stub();
|
|
||||||
|
|
||||||
assign(DOM, {
|
|
||||||
setCurrentByName,
|
|
||||||
});
|
|
||||||
|
|
||||||
const event = {
|
|
||||||
preventDefault: stub(),
|
|
||||||
};
|
|
||||||
|
|
||||||
vim('/', event);
|
|
||||||
|
|
||||||
t.notCalled(setCurrentByName);
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: n', (t) => {
|
test('cloudcmd: client: key: n', (t) => {
|
||||||
const findNext = stub();
|
const findNext = stub();
|
||||||
const createFindNext = stub().returns(findNext);
|
|
||||||
|
|
||||||
const event = {};
|
mockRequire(pathFind, {
|
||||||
|
findNext,
|
||||||
vim('n', event, {
|
|
||||||
createFindNext,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
t.calledWithNoArgs(findNext, 'should call findNext');
|
const vim = reRequire(pathVim);
|
||||||
|
const event = {};
|
||||||
|
|
||||||
|
vim('n', event);
|
||||||
|
|
||||||
|
stopAll(pathFind);
|
||||||
|
|
||||||
|
t.ok(findNext.calledWith(), 'should call findNext');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: N', (t) => {
|
test('cloudcmd: client: key: N', (t) => {
|
||||||
const findPrevious = stub();
|
const findPrevious = stub();
|
||||||
const createFindPrevious = stub().returns(findPrevious);
|
|
||||||
|
mockRequire(pathFind, {
|
||||||
|
findPrevious,
|
||||||
|
});
|
||||||
|
|
||||||
|
const vim = reRequire(dir + 'vim');
|
||||||
const event = {};
|
const event = {};
|
||||||
|
|
||||||
vim('N', event, {
|
vim('N', event);
|
||||||
createFindPrevious,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWithNoArgs(findPrevious);
|
stopAll(pathFind);
|
||||||
|
|
||||||
|
t.ok(findPrevious.calledWith(), 'should call findPrevious');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cloudcmd: client: key: make directory', async (t) => {
|
|
||||||
const {DOM} = globalThis;
|
|
||||||
|
|
||||||
assign(DOM, {
|
|
||||||
promptNewDir: stub(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const event = {
|
|
||||||
stopImmediatePropagation: stub(),
|
|
||||||
preventDefault: stub(),
|
|
||||||
};
|
|
||||||
|
|
||||||
await vim('m', event);
|
|
||||||
await vim('d', event);
|
|
||||||
|
|
||||||
t.calledWithNoArgs(DOM.promptNewDir);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: key: make file', (t) => {
|
|
||||||
const promptNewFile = stub();
|
|
||||||
|
|
||||||
const event = {
|
|
||||||
stopImmediatePropagation: stub(),
|
|
||||||
preventDefault: stub(),
|
|
||||||
};
|
|
||||||
|
|
||||||
vim('m', event);
|
|
||||||
vim('f', event, {
|
|
||||||
promptNewFile,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWithNoArgs(promptNewFile);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: vim: terminal', (t) => {
|
|
||||||
const CloudCmd = {
|
|
||||||
Terminal: {
|
|
||||||
show: stub(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const event = {};
|
|
||||||
|
|
||||||
vim('t', event, {
|
|
||||||
CloudCmd,
|
|
||||||
});
|
|
||||||
vim('t', event, {
|
|
||||||
CloudCmd,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWithNoArgs(CloudCmd.Terminal.show);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: vim: edit', async (t) => {
|
|
||||||
globalThis.DOM = getDOM();
|
|
||||||
globalThis.CloudCmd = getCloudCmd();
|
|
||||||
|
|
||||||
const {CloudCmd} = globalThis;
|
|
||||||
|
|
||||||
assign(CloudCmd, {
|
|
||||||
EditFileVim: {
|
|
||||||
show: stub(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const event = {};
|
|
||||||
|
|
||||||
await vim('e', event);
|
|
||||||
|
|
||||||
t.calledWithNoArgs(CloudCmd.EditFileVim.show);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: vim: rename', async (t) => {
|
|
||||||
const DOM = getDOM();
|
|
||||||
const renameCurrent = stub();
|
|
||||||
|
|
||||||
assign(DOM, {
|
|
||||||
renameCurrent,
|
|
||||||
});
|
|
||||||
|
|
||||||
const event = {
|
|
||||||
preventDefault: stub(),
|
|
||||||
};
|
|
||||||
|
|
||||||
await vim('rr', event, DOM);
|
|
||||||
|
|
||||||
t.calledWithNoArgs(renameCurrent);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: key: cc: operationCopy', (t) => {
|
|
||||||
const show = stub();
|
|
||||||
const preventDefault = stub();
|
|
||||||
|
|
||||||
const Operation = {
|
|
||||||
show,
|
|
||||||
};
|
|
||||||
|
|
||||||
const event = {
|
|
||||||
preventDefault,
|
|
||||||
};
|
|
||||||
|
|
||||||
vim('c', event, {
|
|
||||||
Operation,
|
|
||||||
});
|
|
||||||
|
|
||||||
vim('c', event, {
|
|
||||||
Operation,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWith(show, ['copy'], 'should show copy operation');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: key: mm: operationMove', (t) => {
|
|
||||||
const show = stub();
|
|
||||||
const preventDefault = stub();
|
|
||||||
|
|
||||||
const Operation = {
|
|
||||||
show,
|
|
||||||
};
|
|
||||||
|
|
||||||
const event = {
|
|
||||||
preventDefault,
|
|
||||||
};
|
|
||||||
|
|
||||||
vim('m', event, {
|
|
||||||
Operation,
|
|
||||||
});
|
|
||||||
|
|
||||||
vim('m', event, {
|
|
||||||
Operation,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWith(show, ['move'], 'should show move operation');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: key: n: findNext: real', (t) => {
|
|
||||||
const setCurrentByName = stub();
|
|
||||||
|
|
||||||
finder.find('a', ['alpha', 'beta', 'apple']);
|
|
||||||
|
|
||||||
const event = {};
|
|
||||||
|
|
||||||
vim('n', event, {
|
|
||||||
setCurrentByName,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWith(setCurrentByName, ['beta'], 'should set current by next found name');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: key: N: findPrevious: real', (t) => {
|
|
||||||
const setCurrentByName = stub();
|
|
||||||
|
|
||||||
finder.find('a', ['alpha', 'beta', 'apple']);
|
|
||||||
|
|
||||||
const event = {};
|
|
||||||
|
|
||||||
vim('N', event, {
|
|
||||||
setCurrentByName,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWith(setCurrentByName, ['apple'], 'should set current by previous found name');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
/* global DOM */
|
|
||||||
export function selectFileNotParent(current, {getCurrentName, selectFile} = DOM) {
|
|
||||||
const name = getCurrentName(current);
|
|
||||||
|
|
||||||
if (name === '..')
|
|
||||||
return;
|
|
||||||
|
|
||||||
selectFile(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const setCurrent = (sibling, {count, isVisual, isDelete}, {Info, setCurrentFile, unselectFiles, Operation}) => {
|
|
||||||
let current = Info.element;
|
|
||||||
const select = isVisual ? selectFileNotParent : unselectFiles;
|
|
||||||
|
|
||||||
select(current);
|
|
||||||
|
|
||||||
const position = `${sibling}Sibling`;
|
|
||||||
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
const next = current[position];
|
|
||||||
|
|
||||||
if (!next)
|
|
||||||
break;
|
|
||||||
|
|
||||||
current = next;
|
|
||||||
select(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
setCurrentFile(current);
|
|
||||||
|
|
||||||
if (isDelete)
|
|
||||||
Operation.show('delete');
|
|
||||||
};
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import {fullstore} from 'fullstore';
|
'use strict';
|
||||||
|
|
||||||
|
const fullstore = require('fullstore');
|
||||||
const store = fullstore('');
|
const store = fullstore('');
|
||||||
const visual = fullstore(false);
|
const visual = fullstore(false);
|
||||||
|
|
||||||
|
|
@ -20,11 +21,10 @@ const rmFirst = (a) => {
|
||||||
|
|
||||||
const noop = () => {};
|
const noop = () => {};
|
||||||
|
|
||||||
export default (key, operations = {}) => {
|
module.exports = (key, operations) => {
|
||||||
const prevStore = store();
|
const prevStore = store();
|
||||||
const isVisual = visual();
|
const isVisual = visual();
|
||||||
const value = store(prevStore.concat(key));
|
const value = store(prevStore.concat(key));
|
||||||
|
|
||||||
const {
|
const {
|
||||||
escape = noop,
|
escape = noop,
|
||||||
moveNext = noop,
|
moveNext = noop,
|
||||||
|
|
@ -36,13 +36,6 @@ export default (key, operations = {}) => {
|
||||||
find = noop,
|
find = noop,
|
||||||
findNext = noop,
|
findNext = noop,
|
||||||
findPrevious = noop,
|
findPrevious = noop,
|
||||||
makeFile = noop,
|
|
||||||
makeDirectory = noop,
|
|
||||||
terminal = noop,
|
|
||||||
edit = noop,
|
|
||||||
operationCopy = noop,
|
|
||||||
operationMove = noop,
|
|
||||||
rename = noop,
|
|
||||||
} = operations;
|
} = operations;
|
||||||
|
|
||||||
if (key === 'Enter')
|
if (key === 'Enter')
|
||||||
|
|
@ -51,11 +44,10 @@ export default (key, operations = {}) => {
|
||||||
if (key === 'Escape') {
|
if (key === 'Escape') {
|
||||||
visual(false);
|
visual(false);
|
||||||
escape();
|
escape();
|
||||||
|
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === 'j' || key === 'w') {
|
if (key === 'j') {
|
||||||
const {
|
const {
|
||||||
count,
|
count,
|
||||||
isDelete,
|
isDelete,
|
||||||
|
|
@ -71,7 +63,7 @@ export default (key, operations = {}) => {
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === 'k' || key === 'b') {
|
if (key === 'k') {
|
||||||
const {
|
const {
|
||||||
count,
|
count,
|
||||||
isDelete,
|
isDelete,
|
||||||
|
|
@ -87,8 +79,11 @@ export default (key, operations = {}) => {
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value === 'gg' || key === '^') {
|
if (/^gg$/.test(value)) {
|
||||||
const {isDelete, isVisual} = handleDelete(prevStore);
|
const {
|
||||||
|
isDelete,
|
||||||
|
isVisual,
|
||||||
|
} = handleDelete(prevStore);
|
||||||
|
|
||||||
movePrevious({
|
movePrevious({
|
||||||
count: Infinity,
|
count: Infinity,
|
||||||
|
|
@ -99,49 +94,13 @@ export default (key, operations = {}) => {
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value === 'md') {
|
|
||||||
makeDirectory();
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value === 'tt') {
|
|
||||||
terminal();
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value === 'e') {
|
|
||||||
edit();
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value === 'cc') {
|
|
||||||
operationCopy();
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value === 'mm') {
|
|
||||||
operationMove();
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value === 'mf') {
|
|
||||||
makeFile();
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value === 'rr') {
|
|
||||||
rename();
|
|
||||||
return end();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'd' && (visual() || prevStore === 'd')) {
|
if (key === 'd' && (visual() || prevStore === 'd')) {
|
||||||
stopVisual();
|
stopVisual();
|
||||||
remove();
|
remove();
|
||||||
|
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === 'G' || key === '$') {
|
if (key === 'G') {
|
||||||
moveNext({
|
moveNext({
|
||||||
count: Infinity,
|
count: Infinity,
|
||||||
isVisual,
|
isVisual,
|
||||||
|
|
@ -156,7 +115,6 @@ export default (key, operations = {}) => {
|
||||||
|
|
||||||
stopVisual();
|
stopVisual();
|
||||||
copy();
|
copy();
|
||||||
|
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,7 +126,6 @@ export default (key, operations = {}) => {
|
||||||
if (/^v$/i.test(key)) {
|
if (/^v$/i.test(key)) {
|
||||||
visual(!visual());
|
visual(!visual());
|
||||||
select();
|
select();
|
||||||
|
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,9 +143,6 @@ export default (key, operations = {}) => {
|
||||||
findPrevious();
|
findPrevious();
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === ' ')
|
|
||||||
return end();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleDelete(prevStore) {
|
function handleDelete(prevStore) {
|
||||||
|
|
@ -218,3 +172,4 @@ function getNumber(value) {
|
||||||
|
|
||||||
return parseInt(value);
|
return parseInt(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import {test, stub} from 'supertape';
|
'use strict';
|
||||||
import vim from './vim.js';
|
|
||||||
|
const test = require('supertape');
|
||||||
|
const vim = require('./vim');
|
||||||
|
|
||||||
test('vim: no operations', (t) => {
|
test('vim: no operations', (t) => {
|
||||||
const result = vim('hello', {});
|
const result = vim('hello', {});
|
||||||
|
|
@ -8,93 +10,3 @@ test('vim: no operations', (t) => {
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('vim: space', (t) => {
|
|
||||||
const moveNext = stub();
|
|
||||||
|
|
||||||
vim(' ');
|
|
||||||
vim('j', {
|
|
||||||
moveNext,
|
|
||||||
});
|
|
||||||
|
|
||||||
const args = [{
|
|
||||||
count: 1,
|
|
||||||
isDelete: false,
|
|
||||||
isVisual: false,
|
|
||||||
}];
|
|
||||||
|
|
||||||
t.calledWith(moveNext, args);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('vim: ^', (t) => {
|
|
||||||
const movePrevious = stub();
|
|
||||||
|
|
||||||
vim('^', {
|
|
||||||
movePrevious,
|
|
||||||
});
|
|
||||||
|
|
||||||
const expected = {
|
|
||||||
count: Infinity,
|
|
||||||
isVisual: false,
|
|
||||||
isDelete: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
t.calledWith(movePrevious, [expected], 'should call movePrevious');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('vim: cc', (t) => {
|
|
||||||
const operationCopy = stub();
|
|
||||||
|
|
||||||
vim('cc', {
|
|
||||||
operationCopy,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWithNoArgs(operationCopy);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('vim: mm', (t) => {
|
|
||||||
const operationMove = stub();
|
|
||||||
|
|
||||||
vim('mm', {
|
|
||||||
operationMove,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.calledWithNoArgs(operationMove);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('vim: w', (t) => {
|
|
||||||
const moveNext = stub();
|
|
||||||
|
|
||||||
vim('w', {
|
|
||||||
moveNext,
|
|
||||||
});
|
|
||||||
|
|
||||||
const expected = {
|
|
||||||
count: 1,
|
|
||||||
isVisual: false,
|
|
||||||
isDelete: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
t.calledWith(moveNext, [expected], 'should call moveNext');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('vim: b', (t) => {
|
|
||||||
const movePrevious = stub();
|
|
||||||
|
|
||||||
vim('b', {
|
|
||||||
movePrevious,
|
|
||||||
});
|
|
||||||
|
|
||||||
const expected = {
|
|
||||||
count: 1,
|
|
||||||
isVisual: false,
|
|
||||||
isDelete: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
t.calledWith(movePrevious, [expected], 'should call movePrevious');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import currify from 'currify';
|
'use strict';
|
||||||
|
|
||||||
export const getIndex = currify((array, item) => {
|
module.exports = (array, item) => {
|
||||||
const index = array.indexOf(item);
|
const index = array.indexOf(item);
|
||||||
|
|
||||||
if (!~index)
|
if (!~index)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
});
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
export const getRange = (indexFrom, indexTo, files) => {
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = (indexFrom, indexTo, files) => {
|
||||||
if (indexFrom < indexTo)
|
if (indexFrom < indexTo)
|
||||||
return files.slice(indexFrom, indexTo + 1);
|
return files.slice(indexFrom, indexTo + 1);
|
||||||
|
|
||||||
|
|
@ -7,3 +9,4 @@ export const getRange = (indexFrom, indexTo, files) => {
|
||||||
|
|
||||||
return [files[indexFrom]];
|
return [files[indexFrom]];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
import test from 'supertape';
|
|
||||||
import {getRange} from './get-range.js';
|
|
||||||
|
|
||||||
test('cloudcmd: client: listeners: getRange: direct', (t) => {
|
|
||||||
const expected = [
|
|
||||||
'hello',
|
|
||||||
'world',
|
|
||||||
];
|
|
||||||
|
|
||||||
const files = [
|
|
||||||
...expected,
|
|
||||||
'how',
|
|
||||||
'come',
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = getRange(0, 1, files);
|
|
||||||
|
|
||||||
t.deepEqual(result, expected, 'should return range');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: listeners: getRange: reverse', (t) => {
|
|
||||||
const expected = [
|
|
||||||
'hello',
|
|
||||||
'world',
|
|
||||||
];
|
|
||||||
|
|
||||||
const files = [
|
|
||||||
...expected,
|
|
||||||
'how',
|
|
||||||
'come',
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = getRange(1, 0, files);
|
|
||||||
|
|
||||||
t.deepEqual(result, expected, 'should return range');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: listeners: getRange: one', (t) => {
|
|
||||||
const expected = ['hello'];
|
|
||||||
const files = [
|
|
||||||
...expected,
|
|
||||||
'how',
|
|
||||||
'come',
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = getRange(0, 0, files);
|
|
||||||
|
|
||||||
t.deepEqual(result, expected, 'should return range');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
@ -1,30 +1,36 @@
|
||||||
/* global DOM, CloudCmd */
|
/* global DOM, CloudCmd */
|
||||||
import exec from 'execon';
|
|
||||||
import itype from 'itype';
|
'use strict';
|
||||||
import currify from 'currify';
|
|
||||||
import {tryToCatch} from 'try-to-catch';
|
const exec = require('execon');
|
||||||
import clipboard from '@cloudcmd/clipboard';
|
const itype = require('itype');
|
||||||
import * as Events from '#dom/events';
|
const currify = require('currify');
|
||||||
import {uploadFiles} from '#dom/upload-files';
|
const tryToCatch = require('try-to-catch');
|
||||||
import {FS} from '#common/cloudfunc';
|
const clipboard = require('@cloudcmd/clipboard');
|
||||||
import {getRange} from './get-range.js';
|
|
||||||
import {getIndex} from './get-index.js';
|
const getRange = require('./get-range');
|
||||||
|
const getIndex = currify(require('./get-index'));
|
||||||
|
const uploadFiles = require('../dom/upload-files');
|
||||||
|
|
||||||
|
const {FS} = require('../../common/cloudfunc');
|
||||||
|
|
||||||
const NBSP_REG = RegExp(String.fromCharCode(160), 'g');
|
const NBSP_REG = RegExp(String.fromCharCode(160), 'g');
|
||||||
const SPACE = ' ';
|
const SPACE = ' ';
|
||||||
|
|
||||||
export async function init() {
|
module.exports.init = () => {
|
||||||
contextMenu();
|
contextMenu();
|
||||||
dragndrop();
|
dragndrop();
|
||||||
unload();
|
unload();
|
||||||
pop();
|
pop();
|
||||||
resize();
|
resize();
|
||||||
|
config();
|
||||||
header();
|
header();
|
||||||
await config();
|
};
|
||||||
}
|
|
||||||
|
CloudCmd.Listeners = module.exports;
|
||||||
|
|
||||||
const unselect = (event) => {
|
const unselect = (event) => {
|
||||||
const isMac = /Mac/.test(globalThis.navigator.platform);
|
const isMac = /Mac/.test(window.navigator.platform);
|
||||||
const {
|
const {
|
||||||
shiftKey,
|
shiftKey,
|
||||||
metaKey,
|
metaKey,
|
||||||
|
|
@ -42,9 +48,14 @@ 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,
|
||||||
|
unselect,
|
||||||
|
]),
|
||||||
dragstart: exec.with(execIfNotUL, onDragStart),
|
dragstart: exec.with(execIfNotUL, onDragStart),
|
||||||
dblclick: exec.with(execIfNotUL, onDblClick),
|
dblclick: exec.with(execIfNotUL, onDblClick),
|
||||||
touchstart: exec.with(execIfNotUL, onTouch),
|
touchstart: exec.with(execIfNotUL, onTouch),
|
||||||
|
|
@ -55,7 +66,6 @@ let EXT;
|
||||||
function header() {
|
function header() {
|
||||||
const fm = DOM.getFM();
|
const fm = DOM.getFM();
|
||||||
const isDataset = (el) => el.dataset;
|
const isDataset = (el) => el.dataset;
|
||||||
|
|
||||||
const isPanel = (el) => {
|
const isPanel = (el) => {
|
||||||
return /^js-(left|right)$/.test(el.dataset.name);
|
return /^js-(left|right)$/.test(el.dataset.name);
|
||||||
};
|
};
|
||||||
|
|
@ -67,7 +77,8 @@ function header() {
|
||||||
if (parent.dataset.name !== 'js-fm-header')
|
if (parent.dataset.name !== 'js-fm-header')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const name = (el.dataset.name || '').replace('js-', '');
|
const name = (el.dataset.name || '')
|
||||||
|
.replace('js-', '');
|
||||||
|
|
||||||
if (!/^(name|size|date)$/.test(name))
|
if (!/^(name|size|date)$/.test(name))
|
||||||
return;
|
return;
|
||||||
|
|
@ -90,58 +101,58 @@ function getPath(el, path = []) {
|
||||||
|
|
||||||
async function config() {
|
async function config() {
|
||||||
const [, config] = await tryToCatch(DOM.Files.get, 'config');
|
const [, config] = await tryToCatch(DOM.Files.get, 'config');
|
||||||
const type = config?.packer;
|
const type = config && config.packer;
|
||||||
|
|
||||||
EXT = DOM.getPackerExt(type);
|
EXT = DOM.getPackerExt(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initKeysPanel = () => {
|
module.exports.initKeysPanel = () => {
|
||||||
const keysElement = DOM.getById('js-keyspanel');
|
const keysElement = DOM.getById('js-keyspanel');
|
||||||
|
|
||||||
if (!keysElement)
|
if (!keysElement)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Events.addClick(keysElement, (event) => {
|
Events.addClick(keysElement, ({target}) => {
|
||||||
const {target} = event;
|
|
||||||
const {id} = target;
|
const {id} = target;
|
||||||
|
|
||||||
const operation = (name) => {
|
const operation = (name) => {
|
||||||
const {Operation} = CloudCmd;
|
const {Operation} = CloudCmd;
|
||||||
|
const fn = Operation.show.bind(null, name);
|
||||||
|
|
||||||
return Operation.show.bind(null, name);
|
return fn;
|
||||||
};
|
};
|
||||||
|
|
||||||
const clickFuncs = {
|
const clickFuncs = {
|
||||||
'f1': CloudCmd.Help.show,
|
'f1' : CloudCmd.Help.show,
|
||||||
'f2': CloudCmd.UserMenu.show,
|
'f2' : initF2,
|
||||||
'f3': CloudCmd.View.show,
|
'f3' : CloudCmd.View.show,
|
||||||
'f4': CloudCmd.EditFile.show,
|
'f4' : CloudCmd.EditFile.show,
|
||||||
'f5': operation('copy'),
|
'f5' : operation('copy'),
|
||||||
'f6': operation('move'),
|
'f6' : operation('move'),
|
||||||
'f7': DOM.promptNewDir,
|
'f7' : DOM.promptNewDir,
|
||||||
'f8': operation('delete'),
|
'f8' : operation('delete'),
|
||||||
'f9': () => {
|
'f9' : CloudCmd.Menu.show,
|
||||||
event.stopPropagation();
|
'f10' : CloudCmd.Config.show,
|
||||||
CloudCmd.Menu.show();
|
'~' : CloudCmd.Konsole.show,
|
||||||
},
|
'shift~' : CloudCmd.Terminal.show,
|
||||||
'f10': CloudCmd.Config.show,
|
'contact' : CloudCmd.Contact.show,
|
||||||
'~': CloudCmd.Konsole.show,
|
|
||||||
'shift~': CloudCmd.Terminal.show,
|
|
||||||
'contact': CloudCmd.Contact.show,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
exec(clickFuncs[id]);
|
exec(clickFuncs[id]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function initF2() {
|
||||||
|
CloudCmd.UserMenu.show();
|
||||||
|
}
|
||||||
|
|
||||||
const getPanel = (side) => {
|
const getPanel = (side) => {
|
||||||
if (!itype.string(side))
|
if (!itype.string(side))
|
||||||
return side;
|
return side;
|
||||||
|
|
||||||
return DOM.getByDataName(`js-${side}`);
|
return DOM.getByDataName('js-' + side);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setOnPanel = (side) => {
|
module.exports.setOnPanel = (side) => {
|
||||||
const panel = getPanel(side);
|
const panel = getPanel(side);
|
||||||
|
|
||||||
const filesElement = DOM.getByDataName('js-files', panel);
|
const filesElement = DOM.getByDataName('js-files', panel);
|
||||||
|
|
@ -157,7 +168,6 @@ 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)
|
||||||
|
|
@ -176,13 +186,13 @@ function decodePath(path) {
|
||||||
|
|
||||||
return decodeURI(path)
|
return decodeURI(path)
|
||||||
.replace(url, '')
|
.replace(url, '')
|
||||||
.replace(prefixReg, '') // browser doesn't replace % -> %25% do it for him
|
.replace(prefixReg, '')
|
||||||
|
// browser doesn't replace % -> %25% do it for him
|
||||||
.replace('%%', '%25%')
|
.replace('%%', '%25%')
|
||||||
.replace(NBSP_REG, SPACE) || '/';
|
.replace(NBSP_REG, SPACE) || '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
@ -204,26 +214,19 @@ async function onPathElementClick(panel, event) {
|
||||||
const {href} = element;
|
const {href} = element;
|
||||||
const path = decodePath(href);
|
const path = decodePath(href);
|
||||||
|
|
||||||
await CloudCmd.changeDir(path, {
|
await CloudCmd.loadDir({
|
||||||
|
path,
|
||||||
isRefresh: false,
|
isRefresh: false,
|
||||||
panel: noCurrent ? panel : Info.panel,
|
panel: noCurrent ? panel : Info.panel,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyPath(el) {
|
function copyPath(el) {
|
||||||
clipboard
|
clipboard.writeText(el.parentElement.title)
|
||||||
.writeText(el.parentElement.title)
|
|
||||||
.then(CloudCmd.log)
|
.then(CloudCmd.log)
|
||||||
.catch(CloudCmd.log);
|
.catch(CloudCmd.log);
|
||||||
}
|
}
|
||||||
|
|
||||||
function execIfNotMobile(callback, event) {
|
|
||||||
const isMobile = DOM.getCSSVar('is-mobile');
|
|
||||||
|
|
||||||
if (!isMobile)
|
|
||||||
callback(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function execIfNotUL(callback, event) {
|
function execIfNotUL(callback, event) {
|
||||||
const {target} = event;
|
const {target} = event;
|
||||||
const {tagName} = target;
|
const {tagName} = target;
|
||||||
|
|
@ -238,14 +241,14 @@ function onClick(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleSelect(key, files) {
|
function toggleSelect(key, files) {
|
||||||
const isMac = /Mac/.test(globalThis.navigator.platform);
|
const isMac = /Mac/.test(window.navigator.platform);
|
||||||
|
|
||||||
if (!key)
|
if (!key)
|
||||||
throw Error('key should not be undefined!');
|
throw Error('key should not be undefined!');
|
||||||
|
|
||||||
const [file] = files;
|
const [file] = files;
|
||||||
|
|
||||||
if (isMac && key.meta)
|
if (isMac && key.meta || key.ctrl)
|
||||||
return DOM.toggleSelectedFile(file);
|
return DOM.toggleSelectedFile(file);
|
||||||
|
|
||||||
if (key.shift)
|
if (key.shift)
|
||||||
|
|
@ -253,7 +256,6 @@ 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);
|
||||||
|
|
@ -263,16 +265,21 @@ function changePanel(element) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onDblClick(event) {
|
async function onDblClick(event) {
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const current = getLIElement(event.target);
|
const current = getLIElement(event.target);
|
||||||
const isDir = DOM.isCurrentIsDir(current);
|
const isDir = DOM.isCurrentIsDir(current);
|
||||||
const path = DOM.getCurrentPath(current);
|
const path = DOM.getCurrentPath(current);
|
||||||
|
|
||||||
if (!isDir)
|
if (isDir) {
|
||||||
return CloudCmd.View.show();
|
await CloudCmd.loadDir({
|
||||||
|
path: path === '/' ? '/' : path + '/',
|
||||||
await CloudCmd.changeDir(path);
|
});
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
} else {
|
||||||
|
CloudCmd.View.show();
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onTouch(event) {
|
async function onTouch(event) {
|
||||||
|
|
@ -287,7 +294,9 @@ async function onTouch(event) {
|
||||||
if (!isCurrent)
|
if (!isCurrent)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await CloudCmd.changeDir(DOM.getCurrentPath(current));
|
await CloudCmd.loadDir({
|
||||||
|
path: DOM.getCurrentPath(current),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -295,7 +304,6 @@ 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;
|
||||||
|
|
@ -312,8 +320,8 @@ function onDragStart(event) {
|
||||||
|
|
||||||
event.dataTransfer.setData(
|
event.dataTransfer.setData(
|
||||||
'DownloadURL',
|
'DownloadURL',
|
||||||
'application/octet-stream' + ':' + name +
|
'application/octet-stream' + ':' +
|
||||||
':' +
|
name + ':' +
|
||||||
link,
|
link,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -336,7 +344,6 @@ 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 = {
|
||||||
|
|
@ -400,7 +407,10 @@ function dragndrop() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDrop = (event) => {
|
const onDrop = (event) => {
|
||||||
const {files, items} = event.dataTransfer;
|
const {
|
||||||
|
files,
|
||||||
|
items,
|
||||||
|
} = event.dataTransfer;
|
||||||
|
|
||||||
const {length: filesCount} = files;
|
const {length: filesCount} = files;
|
||||||
|
|
||||||
|
|
@ -410,10 +420,7 @@ function dragndrop() {
|
||||||
return uploadFiles(files);
|
return uploadFiles(files);
|
||||||
|
|
||||||
const isFile = (item) => item.kind === 'file';
|
const isFile = (item) => item.kind === 'file';
|
||||||
|
const dirFiles = Array.from(items).filter(isFile);
|
||||||
const dirFiles = Array
|
|
||||||
.from(items)
|
|
||||||
.filter(isFile);
|
|
||||||
|
|
||||||
if (dirFiles.length)
|
if (dirFiles.length)
|
||||||
return DOM.uploadDirectory(dirFiles);
|
return DOM.uploadDirectory(dirFiles);
|
||||||
|
|
@ -425,7 +432,7 @@ function dragndrop() {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In macOS Chrome dropEffect = 'none'
|
* In Mac OS Chrome dropEffect = 'none'
|
||||||
* so drop do not firing up when try
|
* so drop do not firing up when try
|
||||||
* to upload file from download bar
|
* to upload file from download bar
|
||||||
*/
|
*/
|
||||||
|
|
@ -450,9 +457,9 @@ function dragndrop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function unload() {
|
function unload() {
|
||||||
Events.add(['unload', 'beforeunload'], (event) => {
|
DOM.Events.add(['unload', 'beforeunload'], (event) => {
|
||||||
const {Key} = CloudCmd;
|
const {Key} = CloudCmd;
|
||||||
const isBind = Key?.isBind();
|
const isBind = Key && Key.isBind();
|
||||||
|
|
||||||
if (isBind)
|
if (isBind)
|
||||||
return;
|
return;
|
||||||
|
|
@ -470,8 +477,8 @@ function pop() {
|
||||||
return CloudCmd.route(location.hash);
|
return CloudCmd.route(location.hash);
|
||||||
|
|
||||||
const history = false;
|
const history = false;
|
||||||
|
await CloudCmd.loadDir({
|
||||||
await CloudCmd.changeDir(path, {
|
path,
|
||||||
history,
|
history,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -479,8 +486,7 @@ function pop() {
|
||||||
|
|
||||||
function resize() {
|
function resize() {
|
||||||
Events.add('resize', () => {
|
Events.add('resize', () => {
|
||||||
const Info = DOM.CurrentInfo;
|
const is = window.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH;
|
||||||
const is = globalThis.innerWidth < CloudCmd.MIN_ONE_PANEL_WIDTH;
|
|
||||||
|
|
||||||
if (!is)
|
if (!is)
|
||||||
return;
|
return;
|
||||||
|
|
@ -500,3 +506,4 @@ function resize() {
|
||||||
DOM.changePanel();
|
DOM.changePanel();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
/* global CloudCmd */
|
'use strict';
|
||||||
import exec from 'execon';
|
|
||||||
import {tryToCatch} from 'try-to-catch';
|
|
||||||
import {js as loadJS} from 'load.js';
|
|
||||||
import pascalCase from 'just-pascal-case';
|
|
||||||
|
|
||||||
|
/* global CloudCmd */
|
||||||
|
|
||||||
|
const exec = require('execon');
|
||||||
|
const tryToCatch = require('try-to-catch');
|
||||||
|
const loadJS = require('load.js').js;
|
||||||
|
|
||||||
|
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}
|
||||||
*/
|
*/
|
||||||
export const loadModule = (params) => {
|
module.exports = function loadModule(params) {
|
||||||
if (!params)
|
if (!params)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -22,24 +25,24 @@ export const loadModule = (params) => {
|
||||||
if (CloudCmd[name])
|
if (CloudCmd[name])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CloudCmd[name] = async () => {
|
CloudCmd[name] = () => {
|
||||||
exec(doBefore);
|
exec(doBefore);
|
||||||
|
const {prefix} = CloudCmd;
|
||||||
|
const pathFull = prefix + CloudCmd.DIRCLIENT_MODULES + path + '.js';
|
||||||
|
|
||||||
const {DIR_MODULES} = CloudCmd;
|
return loadJS(pathFull).then(async () => {
|
||||||
const pathFull = `${DIR_MODULES}/${path}.js`;
|
const newModule = async (f) => f && f();
|
||||||
|
const module = CloudCmd[name];
|
||||||
await loadJS(pathFull);
|
|
||||||
const newModule = async (f) => f && f();
|
Object.assign(newModule, module);
|
||||||
const module = CloudCmd[name];
|
|
||||||
|
CloudCmd[name] = newModule;
|
||||||
Object.assign(newModule, module);
|
|
||||||
|
CloudCmd.log('init', name);
|
||||||
CloudCmd[name] = newModule;
|
await module.init();
|
||||||
CloudCmd.log('init', name);
|
|
||||||
|
return newModule;
|
||||||
await module.init();
|
});
|
||||||
|
|
||||||
return newModule;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CloudCmd[name].show = async (...args) => {
|
CloudCmd[name].show = async (...args) => {
|
||||||
|
|
@ -49,8 +52,9 @@ export const loadModule = (params) => {
|
||||||
const [e, a] = await tryToCatch(m);
|
const [e, a] = await tryToCatch(m);
|
||||||
|
|
||||||
if (e)
|
if (e)
|
||||||
return;
|
return console.error(e);
|
||||||
|
|
||||||
return await a.show(...args);
|
await a.show(...args);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,31 @@
|
||||||
/* global CloudCmd, filepicker */
|
/* global CloudCmd, filepicker */
|
||||||
import exec from 'execon';
|
|
||||||
import currify from 'currify';
|
'use strict';
|
||||||
import load from 'load.js';
|
|
||||||
import {ajax} from '#dom/load';
|
const exec = require('execon');
|
||||||
import * as Files from '#dom/files';
|
const currify = require('currify');
|
||||||
import * as Images from '#dom/images';
|
const load = require('load.js');
|
||||||
|
|
||||||
const {log} = CloudCmd;
|
const {log} = CloudCmd;
|
||||||
|
|
||||||
|
const {ajax} = require('../dom/load');
|
||||||
|
const Files = require('../dom/files');
|
||||||
|
const Images = require('../dom/images');
|
||||||
|
|
||||||
const upload = currify(_upload);
|
const upload = currify(_upload);
|
||||||
|
|
||||||
const Name = 'Cloud';
|
const Name = 'Cloud';
|
||||||
|
CloudCmd[Name] = module.exports;
|
||||||
|
|
||||||
CloudCmd[Name] = {
|
module.exports.init = async () => {
|
||||||
init,
|
|
||||||
uploadFile,
|
|
||||||
saveFile,
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function init() {
|
|
||||||
const [modules] = await loadFiles();
|
const [modules] = await loadFiles();
|
||||||
const {key} = modules.data.FilePicker;
|
const {key} = modules.data.FilePicker;
|
||||||
|
|
||||||
filepicker.setKey(key);
|
filepicker.setKey(key);
|
||||||
Images.hide();
|
Images.hide();
|
||||||
}
|
};
|
||||||
|
|
||||||
export function uploadFile(filename, data) {
|
module.exports.uploadFile = (filename, data) => {
|
||||||
const mimetype = '';
|
const mimetype = '';
|
||||||
|
|
||||||
filepicker.store(data, {
|
filepicker.store(data, {
|
||||||
|
|
@ -35,14 +34,17 @@ export function uploadFile(filename, data) {
|
||||||
}, (fpFile) => {
|
}, (fpFile) => {
|
||||||
filepicker.exportFile(fpFile, log, log);
|
filepicker.exportFile(fpFile, log, log);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export function saveFile(callback) {
|
module.exports.saveFile = (callback) => {
|
||||||
filepicker.pick(upload(callback));
|
filepicker.pick(upload(callback));
|
||||||
}
|
};
|
||||||
|
|
||||||
function _upload(callback, file) {
|
function _upload(callback, file) {
|
||||||
const {url, filename} = file;
|
const {
|
||||||
|
url,
|
||||||
|
filename,
|
||||||
|
} = file;
|
||||||
|
|
||||||
const responseType = 'arraybuffer';
|
const responseType = 'arraybuffer';
|
||||||
const success = exec.with(callback, filename);
|
const success = exec.with(callback, filename);
|
||||||
|
|
@ -54,7 +56,7 @@ function _upload(callback, file) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadFiles() {
|
async function loadFiles() {
|
||||||
const js = '//api.filepicker.io/v2/filepicker.js';
|
const js = '//api.filepicker.io/v2/filepicker.js';
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
|
|
@ -62,3 +64,4 @@ function loadFiles() {
|
||||||
load.js(js),
|
load.js(js),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
/* global CloudCmd */
|
|
||||||
import * as Dialog from '#dom/dialog';
|
|
||||||
|
|
||||||
export function init() {}
|
|
||||||
CloudCmd.CommandLine = {
|
|
||||||
init,
|
|
||||||
show,
|
|
||||||
hide,
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function show() {
|
|
||||||
const [, cmd] = await Dialog.prompt('Command Line', '');
|
|
||||||
const TERMINAL = '^(t|terminal)';
|
|
||||||
|
|
||||||
if (RegExp(`${TERMINAL}$`).test(cmd))
|
|
||||||
return await CloudCmd.Terminal.show();
|
|
||||||
|
|
||||||
if (RegExp(TERMINAL).test(cmd)) {
|
|
||||||
const command = cmd.replace(RegExp(`${TERMINAL} `), '');
|
|
||||||
const exitCode = await CloudCmd.TerminalRun.show({
|
|
||||||
command: `bash -c '${command}'`,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (exitCode === -1)
|
|
||||||
await Dialog.alert(`☝️ Looks like Terminal is disabled, start Cloud Coammnder with '--terminal' flag.`);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hide() {}
|
|
||||||
|
|
@ -1,23 +1,28 @@
|
||||||
import '../../../css/config.css';
|
'use strict';
|
||||||
import {rendy} from 'rendy';
|
|
||||||
import currify from 'currify';
|
|
||||||
import wraptile from 'wraptile';
|
|
||||||
import squad from 'squad';
|
|
||||||
import {promisify} from 'es6-promisify';
|
|
||||||
import {tryToCatch} from 'try-to-catch';
|
|
||||||
import load from 'load.js';
|
|
||||||
import createElement from '@cloudcmd/create-element';
|
|
||||||
import * as Events from '#dom/events';
|
|
||||||
import * as Files from '#dom/files';
|
|
||||||
import {getTitle} from '#common/cloudfunc';
|
|
||||||
import * as Images from '#dom/images';
|
|
||||||
import * as input from './input.js';
|
|
||||||
|
|
||||||
const {CloudCmd, DOM} = globalThis;
|
/* global CloudCmd, DOM, io */
|
||||||
|
|
||||||
|
require('../../../css/config.css');
|
||||||
|
|
||||||
|
const rendy = require('rendy');
|
||||||
|
const currify = require('currify');
|
||||||
|
const wraptile = require('wraptile');
|
||||||
|
const squad = require('squad');
|
||||||
|
const {promisify} = require('es6-promisify');
|
||||||
|
const tryToCatch = require('try-to-catch');
|
||||||
|
const load = require('load.js');
|
||||||
|
const createElement = require('@cloudcmd/create-element');
|
||||||
|
|
||||||
|
const input = require('./input');
|
||||||
|
const Images = require('../../dom/images');
|
||||||
|
const Events = require('../../dom/events');
|
||||||
|
const Files = require('../../dom/files');
|
||||||
|
|
||||||
|
const {getTitle} = require('../../../common/cloudfunc');
|
||||||
const {Dialog, setTitle} = DOM;
|
const {Dialog, setTitle} = DOM;
|
||||||
|
|
||||||
const Name = 'Config';
|
const Name = 'Config';
|
||||||
|
CloudCmd[Name] = module.exports;
|
||||||
|
|
||||||
const loadSocket = promisify(DOM.loadSocket);
|
const loadSocket = promisify(DOM.loadSocket);
|
||||||
|
|
||||||
|
|
@ -41,25 +46,28 @@ let Template;
|
||||||
|
|
||||||
const loadCSS = load.css;
|
const loadCSS = load.css;
|
||||||
|
|
||||||
export async function init() {
|
module.exports.init = async () => {
|
||||||
if (!CloudCmd.config('configDialog'))
|
if (!CloudCmd.config('configDialog'))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
showLoad();
|
showLoad();
|
||||||
|
|
||||||
const {DIR_DIST} = CloudCmd;
|
const {prefix} = CloudCmd;
|
||||||
|
|
||||||
[Template] = await Promise.all([
|
[Template] = await Promise.all([
|
||||||
Files.get('config-tmpl'),
|
Files.get('config-tmpl'),
|
||||||
loadSocket(),
|
loadSocket(),
|
||||||
loadCSS(`${DIR_DIST}/config.css`),
|
loadCSS(prefix + '/dist/config.css'),
|
||||||
CloudCmd.View(),
|
CloudCmd.View(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
initSocket();
|
initSocket();
|
||||||
}
|
};
|
||||||
|
|
||||||
const {config, Key} = CloudCmd;
|
const {
|
||||||
|
config,
|
||||||
|
Key,
|
||||||
|
} = CloudCmd;
|
||||||
|
|
||||||
let Element;
|
let Element;
|
||||||
|
|
||||||
|
|
@ -69,20 +77,24 @@ function getHost() {
|
||||||
origin,
|
origin,
|
||||||
protocol,
|
protocol,
|
||||||
} = location;
|
} = location;
|
||||||
|
const href = origin || `${protocol}//${host}`;
|
||||||
|
|
||||||
return origin || `${protocol}//${host}`;
|
return href;
|
||||||
}
|
}
|
||||||
|
|
||||||
function initSocket() {
|
function initSocket() {
|
||||||
const href = getHost();
|
const href = getHost();
|
||||||
const {prefixSocket, prefix} = CloudCmd;
|
const {
|
||||||
|
prefixSocket,
|
||||||
|
prefix,
|
||||||
|
} = CloudCmd;
|
||||||
|
|
||||||
const ONE_MINUTE = 60 * 1000;
|
const ONE_MINUTE = 60 * 1000;
|
||||||
|
|
||||||
const socket = globalThis.io.connect(href + prefixSocket + '/config', {
|
const socket = io.connect(href + prefixSocket + '/config', {
|
||||||
reconnectionAttempts: Infinity,
|
reconnectionAttempts: Infinity,
|
||||||
reconnectionDelay: ONE_MINUTE,
|
reconnectionDelay: ONE_MINUTE,
|
||||||
path: `${prefix}/socket.io`,
|
path: prefix + '/socket.io',
|
||||||
});
|
});
|
||||||
|
|
||||||
const save = (data) => {
|
const save = (data) => {
|
||||||
|
|
@ -113,7 +125,9 @@ function authCheck(socket) {
|
||||||
|
|
||||||
Config.save = saveHttp;
|
Config.save = saveHttp;
|
||||||
|
|
||||||
export async function show() {
|
module.exports.show = show;
|
||||||
|
|
||||||
|
async function show() {
|
||||||
if (!CloudCmd.config('configDialog'))
|
if (!CloudCmd.config('configDialog'))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -128,27 +142,21 @@ async function fillTemplate() {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
editor,
|
editor,
|
||||||
menu,
|
|
||||||
packer,
|
packer,
|
||||||
columns,
|
columns,
|
||||||
theme,
|
|
||||||
configAuth,
|
configAuth,
|
||||||
configPort,
|
|
||||||
...obj
|
...obj
|
||||||
} = input.convert(config);
|
} = input.convert(config);
|
||||||
|
|
||||||
obj[`${menu}-selected`] = 'selected';
|
obj[editor + '-selected'] = 'selected';
|
||||||
obj[`${editor}-selected`] = 'selected';
|
obj[packer + '-selected'] = 'selected';
|
||||||
obj[`${packer}-selected`] = 'selected';
|
obj[columns + '-selected'] = 'selected';
|
||||||
obj[`${columns}-selected`] = 'selected';
|
|
||||||
obj[`${theme}-selected`] = 'selected';
|
|
||||||
obj.configAuth = configAuth ? '' : 'hidden';
|
obj.configAuth = configAuth ? '' : 'hidden';
|
||||||
obj.configPort = configPort ? '' : 'hidden';
|
|
||||||
|
|
||||||
const innerHTML = rendy(Template, obj);
|
const innerHTML = rendy(Template, obj);
|
||||||
|
|
||||||
Element = createElement('form', {
|
Element = createElement('form', {
|
||||||
className: 'config',
|
className : 'config',
|
||||||
innerHTML,
|
innerHTML,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -165,20 +173,20 @@ async function fillTemplate() {
|
||||||
const getTarget = ({target}) => target;
|
const getTarget = ({target}) => target;
|
||||||
const handleChange = squad(onChange, getTarget);
|
const handleChange = squad(onChange, getTarget);
|
||||||
|
|
||||||
Array
|
Array.from(inputs)
|
||||||
.from(inputs)
|
|
||||||
.map(addKey(onKey))
|
.map(addKey(onKey))
|
||||||
.map(addChange(handleChange));
|
.map(addChange(handleChange));
|
||||||
|
|
||||||
const autoSize = true;
|
const autoSize = true;
|
||||||
|
|
||||||
CloudCmd.View.show(Element, {
|
CloudCmd.View.show(Element, {
|
||||||
autoSize,
|
autoSize,
|
||||||
afterShow,
|
afterShow,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hide() {
|
module.exports.hide = hide;
|
||||||
|
|
||||||
|
function hide() {
|
||||||
CloudCmd.View.hide();
|
CloudCmd.View.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -220,7 +228,7 @@ function onAuthChange(checked) {
|
||||||
const elUsername = input.getElementByName('username', Element);
|
const elUsername = input.getElementByName('username', Element);
|
||||||
const elPassword = input.getElementByName('password', Element);
|
const elPassword = input.getElementByName('password', Element);
|
||||||
|
|
||||||
elUsername.disabled = !checked;
|
elUsername.disabled =
|
||||||
elPassword.disabled = !checked;
|
elPassword.disabled = !checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -230,18 +238,13 @@ function onNameChange(name) {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onKey({keyCode, target}) {
|
function onKey({keyCode, target}) {
|
||||||
switch(keyCode) {
|
switch(keyCode) {
|
||||||
case Key.ESC:
|
case Key.ESC:
|
||||||
return hide();
|
return hide();
|
||||||
|
|
||||||
case Key.ENTER:
|
case Key.ENTER:
|
||||||
return await onChange(target);
|
return onChange(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CloudCmd[Name] = {
|
|
||||||
init,
|
|
||||||
show,
|
|
||||||
hide,
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,23 @@
|
||||||
import {encode} from '#common/entity';
|
'use strict';
|
||||||
|
|
||||||
const isBool = (a) => typeof a === 'boolean';
|
const currify = require('currify');
|
||||||
const isString = (a) => typeof a === 'string';
|
|
||||||
|
|
||||||
const {keys} = Object;
|
const isType = currify((type, object, name) => {
|
||||||
|
return typeof object[name] === type;
|
||||||
|
});
|
||||||
|
|
||||||
export function getElementByName(selector, element) {
|
const isBool = isType('boolean');
|
||||||
|
|
||||||
|
module.exports.getElementByName = getElementByName;
|
||||||
|
|
||||||
|
function getElementByName(selector, element) {
|
||||||
const str = `[data-name="js-${selector}"]`;
|
const str = `[data-name="js-${selector}"]`;
|
||||||
|
|
||||||
return element.querySelector(str);
|
return element
|
||||||
|
.querySelector(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getName = (element) => {
|
module.exports.getName = (element) => {
|
||||||
const name = element
|
const name = element
|
||||||
.getAttribute('data-name')
|
.getAttribute('data-name')
|
||||||
.replace(/^js-/, '');
|
.replace(/^js-/, '');
|
||||||
|
|
@ -19,21 +25,17 @@ export const getName = (element) => {
|
||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const convert = (config) => {
|
module.exports.convert = (config) => {
|
||||||
const result = config;
|
const result = {
|
||||||
|
...config,
|
||||||
|
};
|
||||||
|
const array = Object.keys(config);
|
||||||
|
|
||||||
for (const name of keys(config)) {
|
const filtered = array.filter(isBool(config));
|
||||||
|
|
||||||
|
for (const name of filtered) {
|
||||||
const item = config[name];
|
const item = config[name];
|
||||||
|
result[name] = setState(item);
|
||||||
if (isBool(item)) {
|
|
||||||
result[name] = setState(item);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isString(item)) {
|
|
||||||
result[name] = encode(item);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -46,7 +48,7 @@ function setState(state) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getValue = (name, element) => {
|
module.exports.getValue = (name, element) => {
|
||||||
const el = getElementByName(name, element);
|
const el = getElementByName(name, element);
|
||||||
const {type} = el;
|
const {type} = el;
|
||||||
|
|
||||||
|
|
@ -62,7 +64,7 @@ export const getValue = (name, element) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setValue = (name, value, element) => {
|
module.exports.setValue = (name, value, element) => {
|
||||||
const el = getElementByName(name, element);
|
const el = getElementByName(name, element);
|
||||||
const {type} = el;
|
const {type} = el;
|
||||||
|
|
||||||
|
|
@ -76,3 +78,4 @@ export const setValue = (name, value, element) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,140 +0,0 @@
|
||||||
import {test, stub} from 'supertape';
|
|
||||||
import {
|
|
||||||
convert,
|
|
||||||
getName,
|
|
||||||
getValue,
|
|
||||||
setValue,
|
|
||||||
} from './input.js';
|
|
||||||
|
|
||||||
test('cloudcmd: client: config: input: convert', (t) => {
|
|
||||||
const result = convert({
|
|
||||||
name: 'hello <world>',
|
|
||||||
});
|
|
||||||
|
|
||||||
const expected = {
|
|
||||||
name: 'hello <world>',
|
|
||||||
};
|
|
||||||
|
|
||||||
t.deepEqual(result, expected);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: config: input: convert: bool', (t) => {
|
|
||||||
const result = convert({
|
|
||||||
auth: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const expected = {
|
|
||||||
auth: ' checked',
|
|
||||||
};
|
|
||||||
|
|
||||||
t.deepEqual(result, expected);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: config: input: convert: bool false', (t) => {
|
|
||||||
const result = convert({
|
|
||||||
auth: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const expected = {
|
|
||||||
auth: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
t.deepEqual(result, expected);
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: config: input: getName', (t) => {
|
|
||||||
const getAttribute = stub().returns('js-hello');
|
|
||||||
const element = {
|
|
||||||
getAttribute,
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = getName(element);
|
|
||||||
|
|
||||||
t.equal(result, 'hello', 'should strip js- prefix');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: config: input: getValue: checkbox', (t) => {
|
|
||||||
const querySelector = stub().returns({
|
|
||||||
type: 'checkbox',
|
|
||||||
checked: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const element = {
|
|
||||||
querySelector,
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = getValue('auth', element);
|
|
||||||
|
|
||||||
t.ok(result, 'should return checked value');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: config: input: getValue: number', (t) => {
|
|
||||||
const querySelector = stub().returns({
|
|
||||||
type: 'number',
|
|
||||||
value: '42',
|
|
||||||
});
|
|
||||||
|
|
||||||
const element = {
|
|
||||||
querySelector,
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = getValue('port', element);
|
|
||||||
|
|
||||||
t.equal(result, 42, 'should return number');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: config: input: getValue: default', (t) => {
|
|
||||||
const querySelector = stub().returns({
|
|
||||||
type: 'text',
|
|
||||||
value: 'hello',
|
|
||||||
});
|
|
||||||
|
|
||||||
const element = {
|
|
||||||
querySelector,
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = getValue('name', element);
|
|
||||||
|
|
||||||
t.equal(result, 'hello', 'should return value as is');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: config: input: setValue: checkbox', (t) => {
|
|
||||||
const el = {
|
|
||||||
type: 'checkbox',
|
|
||||||
checked: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const querySelector = stub().returns(el);
|
|
||||||
const element = {
|
|
||||||
querySelector,
|
|
||||||
};
|
|
||||||
|
|
||||||
setValue('auth', true, element);
|
|
||||||
|
|
||||||
t.ok(el.checked, 'should set checked');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('cloudcmd: client: config: input: setValue: default', (t) => {
|
|
||||||
const el = {
|
|
||||||
type: 'text',
|
|
||||||
value: 'old',
|
|
||||||
};
|
|
||||||
|
|
||||||
const querySelector = stub().returns(el);
|
|
||||||
const element = {
|
|
||||||
querySelector,
|
|
||||||
};
|
|
||||||
|
|
||||||
setValue('name', 'new', element);
|
|
||||||
|
|
||||||
t.equal(el.value, 'new', 'should set value');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
@ -1,34 +1,36 @@
|
||||||
/* global CloudCmd */
|
/* global CloudCmd */
|
||||||
/* global DOM */
|
/* global DOM */
|
||||||
import olark from '@cloudcmd/olark';
|
|
||||||
import * as Images from '#dom/images';
|
|
||||||
|
|
||||||
CloudCmd.Contact = {
|
'use strict';
|
||||||
init,
|
|
||||||
show,
|
CloudCmd.Contact = exports;
|
||||||
hide,
|
|
||||||
};
|
const olark = require('@cloudcmd/olark');
|
||||||
|
const Images = require('../dom/images');
|
||||||
|
|
||||||
const {Events} = DOM;
|
const {Events} = DOM;
|
||||||
const {Key} = CloudCmd;
|
const {Key} = CloudCmd;
|
||||||
|
|
||||||
export function init() {
|
module.exports.show = show;
|
||||||
|
module.exports.hide = hide;
|
||||||
|
|
||||||
|
module.exports.init = () => {
|
||||||
Events.addKey(onKey);
|
Events.addKey(onKey);
|
||||||
|
|
||||||
olark.identify('6216-545-10-4223');
|
olark.identify('6216-545-10-4223');
|
||||||
olark('api.box.onExpand', show);
|
olark('api.box.onExpand', show);
|
||||||
olark('api.box.onShow', show);
|
olark('api.box.onShow', show);
|
||||||
olark('api.box.onShrink', hide);
|
olark('api.box.onShrink', hide);
|
||||||
}
|
};
|
||||||
|
|
||||||
export function show() {
|
function show() {
|
||||||
Key.unsetBind();
|
Key.unsetBind();
|
||||||
Images.hide();
|
Images.hide();
|
||||||
|
|
||||||
olark('api.box.expand');
|
olark('api.box.expand');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hide() {
|
function hide() {
|
||||||
Key.setBind();
|
Key.setBind();
|
||||||
olark('api.box.hide');
|
olark('api.box.hide');
|
||||||
}
|
}
|
||||||
|
|
@ -37,3 +39,4 @@ function onKey({keyCode}) {
|
||||||
if (keyCode === Key.ESC)
|
if (keyCode === Key.ESC)
|
||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
import * as Events from '#dom/events';
|
'use strict';
|
||||||
|
|
||||||
const {CloudCmd} = globalThis;
|
/* global CloudCmd */
|
||||||
|
|
||||||
CloudCmd.EditFileVim = {
|
CloudCmd.EditFileVim = exports;
|
||||||
init,
|
|
||||||
show,
|
const Events = require('../dom/events');
|
||||||
hide,
|
|
||||||
};
|
|
||||||
|
|
||||||
const {Key} = CloudCmd;
|
const {Key} = CloudCmd;
|
||||||
|
|
||||||
|
|
@ -18,29 +16,34 @@ const ConfigView = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function init() {
|
module.exports.init = async () => {
|
||||||
await CloudCmd.EditFile();
|
await CloudCmd.EditFile();
|
||||||
}
|
};
|
||||||
|
|
||||||
export async function show() {
|
module.exports.show = () => {
|
||||||
Events.addKey(listener);
|
Events.addKey(listener);
|
||||||
|
|
||||||
const editFile = await CloudCmd.EditFile.show(ConfigView);
|
CloudCmd.EditFile
|
||||||
|
.show(ConfigView)
|
||||||
editFile
|
|
||||||
.getEditor()
|
.getEditor()
|
||||||
.setKeyMap('vim');
|
.setKeyMap('vim');
|
||||||
}
|
};
|
||||||
|
|
||||||
export function hide() {
|
module.exports.hide = hide;
|
||||||
|
|
||||||
|
function hide() {
|
||||||
CloudCmd.Edit.hide();
|
CloudCmd.Edit.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
function listener(event) {
|
function listener(event) {
|
||||||
const {keyCode, shiftKey} = event;
|
const {
|
||||||
|
keyCode,
|
||||||
|
shiftKey,
|
||||||
|
} = event;
|
||||||
|
|
||||||
if (shiftKey && keyCode === Key.ESC) {
|
if (shiftKey && keyCode === Key.ESC) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue