refactor: moving repository
* refactor * updates * add circle * update circle * fix circlepull/347/head
parent
d97f17facb
commit
613a3f2811
13
.babelrc
13
.babelrc
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"presets": [
|
||||
["env", { "modules": false }],
|
||||
"stage-2"
|
||||
],
|
||||
"plugins": ["transform-runtime"],
|
||||
"env": {
|
||||
"test": {
|
||||
"presets": ["env", "stage-2"],
|
||||
"plugins": [ "istanbul" ]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/golang:1.9
|
||||
|
||||
working_directory: /go/src/github.com/filebrowser/filebrowser
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
run: |
|
||||
go get github.com/alecthomas/gometalinter
|
||||
/go/bin/gometalinter --install
|
||||
|
||||
- run:
|
||||
name: Run linting
|
||||
command: gometalinter --disable-all -E gofmt -E misspell -E ineffassign -E goimports -E deadcode --exclude="rice-box.go" --tests ./...
|
|
@ -1,4 +1,2 @@
|
|||
assets/
|
||||
testdata/
|
||||
caddy/
|
||||
.github/
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# 4 space indentation
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
|
@ -1,2 +0,0 @@
|
|||
build/*.js
|
||||
config/*.js
|
27
.eslintrc.js
27
.eslintrc.js
|
@ -1,27 +0,0 @@
|
|||
// http://eslint.org/docs/user-guide/configuring
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: 'babel-eslint',
|
||||
parserOptions: {
|
||||
sourceType: 'module'
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
},
|
||||
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
|
||||
extends: 'standard',
|
||||
// required to lint *.vue files
|
||||
plugins: [
|
||||
'html'
|
||||
],
|
||||
// add your custom rules here
|
||||
'rules': {
|
||||
// allow paren-less arrow functions
|
||||
'arrow-parens': 0,
|
||||
// allow async-await
|
||||
'generator-star-spacing': 0,
|
||||
// allow debugger during development
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
build:
|
||||
main: cmd/filemanager/main.go
|
||||
binary: filemanager
|
||||
main: cmd/filebrowser/main.go
|
||||
binary: filebrowser
|
||||
goos:
|
||||
- darwin
|
||||
- linux
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* 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
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hacdias@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
|
@ -1,14 +0,0 @@
|
|||
# Contributing
|
||||
|
||||
If you want to contribute or want to build the code from source, you will need to have the most recent version of Go and, if you want to change the static assets (JS, CSS, ...), Node.js installed on your computer. To start developing, you just need to do the following:
|
||||
|
||||
1. `go get github.com/hacdias/filemanager/cmd/filemanager`
|
||||
2. `cd $GOPATH/src/github.com/hacdias/filemanager`
|
||||
3. `npm install`
|
||||
4. `npm run dev` - regenerates the static assets automatically
|
||||
5. `go install github.com/hacdias/filemanager/cmd/filemanager`
|
||||
6. Execute `$GOPATH/bin/filemanager`
|
||||
|
||||
The steps 3 and 4 are only required **if you want to develop the front-end**. Otherwise, you can ignore them. Before pulling, if you made any change on assets folder, you must run the `build.sh` script on the root of this repository.
|
||||
|
||||
If you are using this as a Caddy plugin, you should use its [official instructions for plugins](https://github.com/mholt/caddy/wiki/Extending-Caddy#2-plug-in-your-plugin) and import `github.com/hacdias/filemanager/caddy/filemanager`.
|
13
Dockerfile
13
Dockerfile
|
@ -1,17 +1,18 @@
|
|||
|
||||
FROM golang:alpine
|
||||
|
||||
COPY . /go/src/github.com/hacdias/filemanager
|
||||
COPY . /go/src/github.com/filebrowser/filebrowser
|
||||
|
||||
WORKDIR /go/src/github.com/hacdias/filemanager
|
||||
WORKDIR /go/src/github.com/filebrowser/filebrowser
|
||||
RUN apk add --no-cache git
|
||||
RUN go get ./...
|
||||
|
||||
WORKDIR /go/src/github.com/hacdias/filemanager/cmd/filemanager
|
||||
WORKDIR /go/src/github.com/filebrowser/filebrowser/cmd/filebrowser
|
||||
RUN CGO_ENABLED=0 go build -a
|
||||
RUN mv filemanager /go/bin/filemanager
|
||||
RUN mv filebrowser /go/bin/filebrowser
|
||||
|
||||
FROM scratch
|
||||
COPY --from=0 /go/bin/filemanager /filemanager
|
||||
COPY --from=0 /go/bin/filebrowser /filebrowser
|
||||
|
||||
VOLUME /tmp
|
||||
VOLUME /srv
|
||||
|
@ -19,5 +20,5 @@ EXPOSE 80
|
|||
|
||||
COPY Docker.json /config.json
|
||||
|
||||
ENTRYPOINT ["/filemanager"]
|
||||
ENTRYPOINT ["/filebrowser"]
|
||||
CMD ["--config", "/config.json"]
|
||||
|
|
18
README.md
18
README.md
|
@ -1,13 +1,13 @@
|
|||
![Preview](https://user-images.githubusercontent.com/5447088/28537288-39be4288-70a2-11e7-8ce9-0813d59f46b7.gif)
|
||||
|
||||
# filemanager
|
||||
# filebrowser
|
||||
|
||||
[![Build](https://img.shields.io/travis/hacdias/filemanager.svg?style=flat-square)](https://travis-ci.org/hacdias/filemanager)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/hacdias/filemanager?style=flat-square)](https://goreportcard.com/report/hacdias/filemanager)
|
||||
[![Documentation](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](http://godoc.org/github.com/hacdias/filemanager)
|
||||
[![Version](https://img.shields.io/github/release/hacdias/filemanager.svg?style=flat-square)](https://github.com/hacdias/filemanager/releases/latest)
|
||||
[![Build](https://img.shields.io/travis/filebrowser/filebrowser.svg?style=flat-square)](https://travis-ci.org/filebrowser/filebrowser)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/filebrowser/filebrowser?style=flat-square)](https://goreportcard.com/report/github.com/filebrowser/filebrowser)
|
||||
[![Documentation](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](http://godoc.org/github.com/filebrowser/filebrowser)
|
||||
[![Version](https://img.shields.io/github/release/filebrowser/filebrowser.svg?style=flat-square)](https://github.com/filebrowser/filebrowser/releases/latest)
|
||||
|
||||
filemanager provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app or as a middleware.
|
||||
filebrowser provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app or as a middleware.
|
||||
|
||||
# Table of contents
|
||||
|
||||
|
@ -20,7 +20,7 @@ filemanager provides a file managing interface within a specified directory and
|
|||
|
||||
# Getting started
|
||||
|
||||
You can find the Getting Started guide on the [documentation](https://henriquedias.com/filemanager/quick-start/).
|
||||
You can find the Getting Started guide on the [documentation](https://filebrowser.github.io/quick-start/).
|
||||
|
||||
# Features
|
||||
|
||||
|
@ -50,7 +50,7 @@ We support multiple users and each user can have its own scope and custom styles
|
|||
|
||||
## Search
|
||||
|
||||
FileManager allows you to search through your files and it has some options. By default, your search will be something like this:
|
||||
File Browser allows you to search through your files and it has some options. By default, your search will be something like this:
|
||||
|
||||
```
|
||||
this are keywords
|
||||
|
@ -72,4 +72,4 @@ this are keywords case:insensitive
|
|||
|
||||
# Contributing
|
||||
|
||||
The contributing guidelines can be found [here](https://github.com/hacdias/filemanager/blob/master/CONTRIBUTING.md).
|
||||
The contributing guidelines can be found [here](https://github.com/filebrowser/community).
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
require('./check-versions')()
|
||||
|
||||
process.env.NODE_ENV = 'production'
|
||||
|
||||
var ora = require('ora')
|
||||
var rm = require('rimraf')
|
||||
var path = require('path')
|
||||
var chalk = require('chalk')
|
||||
var webpack = require('webpack')
|
||||
var config = require('./config')
|
||||
var webpackConfig = require('./webpack.prod.conf')
|
||||
|
||||
var spinner = ora('building for production...')
|
||||
spinner.start()
|
||||
|
||||
rm(path.join(config.assetsRoot, config.assetsSubDirectory), err => {
|
||||
if (err) throw err
|
||||
webpack(webpackConfig, function (err, stats) {
|
||||
spinner.stop()
|
||||
if (err) throw err
|
||||
process.stdout.write(stats.toString({
|
||||
colors: true,
|
||||
modules: false,
|
||||
children: false,
|
||||
chunks: false,
|
||||
chunkModules: false
|
||||
}) + '\n\n')
|
||||
|
||||
console.log(chalk.cyan(' Build complete.\n'))
|
||||
})
|
||||
})
|
|
@ -1,48 +0,0 @@
|
|||
var chalk = require('chalk')
|
||||
var semver = require('semver')
|
||||
var packageConfig = require('../../package.json')
|
||||
var shell = require('shelljs')
|
||||
function exec (cmd) {
|
||||
return require('child_process').execSync(cmd).toString().trim()
|
||||
}
|
||||
|
||||
var versionRequirements = [
|
||||
{
|
||||
name: 'node',
|
||||
currentVersion: semver.clean(process.version),
|
||||
versionRequirement: packageConfig.engines.node
|
||||
}
|
||||
]
|
||||
|
||||
if (shell.which('npm')) {
|
||||
versionRequirements.push({
|
||||
name: 'npm',
|
||||
currentVersion: exec('npm --version'),
|
||||
versionRequirement: packageConfig.engines.npm
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = function () {
|
||||
var warnings = []
|
||||
for (var i = 0; i < versionRequirements.length; i++) {
|
||||
var mod = versionRequirements[i]
|
||||
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
|
||||
warnings.push(mod.name + ': ' +
|
||||
chalk.red(mod.currentVersion) + ' should be ' +
|
||||
chalk.green(mod.versionRequirement)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (warnings.length) {
|
||||
console.log('')
|
||||
console.log(chalk.yellow('To use this template, you must update following to modules:'))
|
||||
console.log()
|
||||
for (var i = 0; i < warnings.length; i++) {
|
||||
var warning = warnings[i]
|
||||
console.log(' ' + warning)
|
||||
}
|
||||
console.log()
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
// see http://vuejs-templates.github.io/webpack for documentation.
|
||||
var path = require('path')
|
||||
|
||||
module.exports = {
|
||||
index: path.resolve(__dirname, '../dist/index.html'),
|
||||
assetsRoot: path.resolve(__dirname, '../dist'),
|
||||
assetsSubDirectory: 'static',
|
||||
assetsPublicPath: '{{ .BaseURL }}/',
|
||||
build: {
|
||||
env: {
|
||||
NODE_ENV: '"production"'
|
||||
},
|
||||
productionSourceMap: true,
|
||||
// Run the build command with an extra argument to
|
||||
// View the bundle analyzer report after build finishes:
|
||||
// `npm run build --report`
|
||||
// Set to `true` or `false` to always turn it on or off
|
||||
bundleAnalyzerReport: process.env.npm_config_report
|
||||
},
|
||||
dev: {
|
||||
env: {
|
||||
NODE_ENV: '"development"'
|
||||
},
|
||||
produceSourceMap: true
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
require('./check-versions')()
|
||||
|
||||
process.env.NODE_ENV = 'development'
|
||||
|
||||
var rm = require('rimraf')
|
||||
var path = require('path')
|
||||
var chalk = require('chalk')
|
||||
var webpack = require('webpack')
|
||||
var config = require('./config')
|
||||
var webpackConfig = require('./webpack.dev.conf')
|
||||
var fs = require('fs')
|
||||
|
||||
if (fs.existsSync('./rice-box.go')) {
|
||||
fs.unlinkSync('./rice-box.go')
|
||||
}
|
||||
|
||||
if (fs.existsSync('./plugins/rice-box.go')) {
|
||||
fs.unlinkSync('./plugins/rice-box.go')
|
||||
}
|
||||
|
||||
rm(path.join(config.assetsRoot, config.assetsSubDirectory), err => {
|
||||
if (err) throw err
|
||||
webpack(webpackConfig, function (err, stats) {
|
||||
if (err) throw err
|
||||
process.stdout.write(stats.toString({
|
||||
colors: true,
|
||||
modules: false,
|
||||
children: false,
|
||||
chunks: false,
|
||||
chunkModules: false
|
||||
}) + '\n\n')
|
||||
|
||||
console.log(chalk.cyan(' Build complete.\n'))
|
||||
})
|
||||
})
|
|
@ -1,17 +0,0 @@
|
|||
// This service worker file is effectively a 'no-op' that will reset any
|
||||
// previous service worker registered for the same host:port combination.
|
||||
// In the production build, this file is replaced with an actual service worker
|
||||
// file that will precache your site's local assets.
|
||||
// See https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432
|
||||
|
||||
self.addEventListener('install', () => self.skipWaiting());
|
||||
|
||||
self.addEventListener('activate', () => {
|
||||
self.clients.matchAll({ type: 'window' }).then(windowClients => {
|
||||
for (let windowClient of windowClients) {
|
||||
// Force open pages to refresh, so that they have a chance to load the
|
||||
// fresh navigation response from the local dev server.
|
||||
windowClient.navigate(windowClient.url);
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,55 +0,0 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Check to make sure service workers are supported in the current browser,
|
||||
// and that the current page is accessed from a secure origin. Using a
|
||||
// service worker from an insecure origin will trigger JS console errors.
|
||||
const isLocalhost = Boolean(window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
if ('serviceWorker' in navigator &&
|
||||
(window.location.protocol === 'https:' || isLocalhost)) {
|
||||
navigator.serviceWorker.register('{{ .BaseURL }}/sw.js')
|
||||
.then(function(registration) {
|
||||
// updatefound is fired if service-worker.js changes.
|
||||
registration.onupdatefound = function() {
|
||||
// updatefound is also fired the very first time the SW is installed,
|
||||
// and there's no need to prompt for a reload at that point.
|
||||
// So check here to see if the page is already controlled,
|
||||
// i.e. whether there's an existing service worker.
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// The updatefound event implies that registration.installing is set
|
||||
const installingWorker = registration.installing;
|
||||
|
||||
installingWorker.onstatechange = function() {
|
||||
switch (installingWorker.state) {
|
||||
case 'installed':
|
||||
// At this point, the old content will have been purged and the
|
||||
// fresh content will have been added to the cache.
|
||||
// It's the perfect time to display a "New content is
|
||||
// available; please refresh." message in the page's interface.
|
||||
break;
|
||||
|
||||
case 'redundant':
|
||||
throw new Error('The installing ' +
|
||||
'service worker became redundant.');
|
||||
|
||||
default:
|
||||
// Ignore
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}).catch(function(e) {
|
||||
console.error('Error during service worker registration:', e);
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
|
@ -1,70 +0,0 @@
|
|||
var path = require('path')
|
||||
var config = require('./config')
|
||||
var ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
|
||||
exports.assetsPath = function (_path) {
|
||||
var assetsSubDirectory = config.assetsSubDirectory
|
||||
|
||||
return path.posix.join(assetsSubDirectory, _path)
|
||||
}
|
||||
|
||||
exports.cssLoaders = function (options) {
|
||||
options = options || {}
|
||||
|
||||
var cssLoader = {
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
minimize: process.env.NODE_ENV === 'production',
|
||||
sourceMap: options.sourceMap
|
||||
}
|
||||
}
|
||||
|
||||
// generate loader string to be used with extract text plugin
|
||||
function generateLoaders (loader, loaderOptions) {
|
||||
var loaders = [cssLoader]
|
||||
if (loader) {
|
||||
loaders.push({
|
||||
loader: loader + '-loader',
|
||||
options: Object.assign({}, loaderOptions, {
|
||||
sourceMap: options.sourceMap
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Extract CSS when that option is specified
|
||||
// (which is the case during production build)
|
||||
if (options.extract) {
|
||||
return ExtractTextPlugin.extract({
|
||||
use: loaders,
|
||||
fallback: 'vue-style-loader'
|
||||
})
|
||||
} else {
|
||||
return ['vue-style-loader'].concat(loaders)
|
||||
}
|
||||
}
|
||||
|
||||
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
|
||||
return {
|
||||
css: generateLoaders(),
|
||||
postcss: generateLoaders(),
|
||||
less: generateLoaders('less'),
|
||||
sass: generateLoaders('sass', { indentedSyntax: true }),
|
||||
scss: generateLoaders('sass'),
|
||||
stylus: generateLoaders('stylus'),
|
||||
styl: generateLoaders('stylus')
|
||||
}
|
||||
}
|
||||
|
||||
// Generate loaders for standalone style files (outside of .vue)
|
||||
exports.styleLoaders = function (options) {
|
||||
var output = []
|
||||
var loaders = exports.cssLoaders(options)
|
||||
for (var extension in loaders) {
|
||||
var loader = loaders[extension]
|
||||
output.push({
|
||||
test: new RegExp('\\.' + extension + '$'),
|
||||
use: loader
|
||||
})
|
||||
}
|
||||
return output
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
var utils = require('./utils')
|
||||
var config = require('./config')
|
||||
var isProduction = process.env.NODE_ENV === 'production'
|
||||
|
||||
module.exports = {
|
||||
loaders: utils.cssLoaders({
|
||||
sourceMap: isProduction
|
||||
? config.build.productionSourceMap
|
||||
: config.dev.produceSourceMap,
|
||||
extract: isProduction
|
||||
})
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
var path = require('path')
|
||||
var utils = require('./utils')
|
||||
var config = require('./config')
|
||||
var vueLoaderConfig = require('./vue-loader.conf')
|
||||
|
||||
function resolve (dir) {
|
||||
return path.join(__dirname, '..', dir)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
app: './assets/src/main.js'
|
||||
},
|
||||
output: {
|
||||
path: config.assetsRoot,
|
||||
filename: '[name].js',
|
||||
publicPath: config.assetsPublicPath
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.vue', '.json'],
|
||||
alias: {
|
||||
'vue$': 'vue/dist/vue.esm.js',
|
||||
'@': resolve('src')
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(yml|yaml)$/,
|
||||
loader: 'yml-loader'
|
||||
},
|
||||
{
|
||||
test: /\.(js|vue)$/,
|
||||
loader: 'eslint-loader',
|
||||
enforce: 'pre',
|
||||
include: [resolve('src'), resolve('test')],
|
||||
options: {
|
||||
formatter: require('eslint-friendly-formatter')
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
options: vueLoaderConfig
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
include: [resolve('src'), resolve('test')]
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: utils.assetsPath('img/[name].[hash:7].[ext]')
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
// limit: 10000,
|
||||
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
var fs = require('fs')
|
||||
var path = require('path')
|
||||
var utils = require('./utils')
|
||||
var webpack = require('webpack')
|
||||
var config = require('./config')
|
||||
var merge = require('webpack-merge')
|
||||
var baseWebpackConfig = require('./webpack.base.conf')
|
||||
var HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
var ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
|
||||
var CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
|
||||
module.exports = merge(baseWebpackConfig, {
|
||||
watch: true,
|
||||
module: {
|
||||
rules: utils.styleLoaders({
|
||||
sourceMap: config.dev.produceSourceMap,
|
||||
extract: true
|
||||
})
|
||||
},
|
||||
devtool: '#cheap-module-eval-source-map',
|
||||
output: {
|
||||
path: config.assetsRoot,
|
||||
filename: utils.assetsPath('js/[name].[chunkhash].js'),
|
||||
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
|
||||
},
|
||||
plugins: [
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
new FriendlyErrorsPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': config.dev.env
|
||||
}),
|
||||
// extract css into its own file
|
||||
new ExtractTextPlugin({
|
||||
filename: utils.assetsPath('css/[name].[contenthash].css')
|
||||
}),
|
||||
// generate dist index.html with correct asset hash for caching.
|
||||
// you can customize output by editing /index.html
|
||||
// see https://github.com/ampedandwired/html-webpack-plugin
|
||||
new HtmlWebpackPlugin({
|
||||
filename: config.index,
|
||||
template: 'assets/index.html',
|
||||
inject: true,
|
||||
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
|
||||
chunksSortMode: 'dependency',
|
||||
serviceWorkerLoader: `<script>${fs.readFileSync(path.join(__dirname,
|
||||
'./service-worker-dev.js'), 'utf-8')}</script>`
|
||||
}),
|
||||
// split vendor js into its own file
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'vendor',
|
||||
minChunks: function (module, count) {
|
||||
// any required modules inside node_modules are extracted to vendor
|
||||
return (
|
||||
module.resource &&
|
||||
/\.js$/.test(module.resource) &&
|
||||
module.resource.indexOf(
|
||||
path.join(__dirname, '../../node_modules')
|
||||
) === 0
|
||||
)
|
||||
}
|
||||
}),
|
||||
// extract webpack runtime and module manifest to its own file in order to
|
||||
// prevent vendor hash from being updated whenever app bundle is updated
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'manifest',
|
||||
chunks: ['vendor']
|
||||
}),
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: path.resolve(__dirname, '../static'),
|
||||
to: config.assetsSubDirectory,
|
||||
ignore: ['.*']
|
||||
},
|
||||
{
|
||||
from: path.resolve(__dirname, '../../node_modules/codemirror/mode/*/*'),
|
||||
to: path.join(config.assetsSubDirectory, 'js/codemirror/mode/[name]/[name].js')
|
||||
}
|
||||
])
|
||||
]
|
||||
})
|
|
@ -1,127 +0,0 @@
|
|||
var fs = require('fs')
|
||||
var path = require('path')
|
||||
var utils = require('./utils')
|
||||
var webpack = require('webpack')
|
||||
var config = require('./config')
|
||||
var merge = require('webpack-merge')
|
||||
var baseWebpackConfig = require('./webpack.base.conf')
|
||||
var CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
var HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
var ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
|
||||
var SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin')
|
||||
var UglifyJS = require('uglify-js')
|
||||
|
||||
var env = config.build.env
|
||||
|
||||
var webpackConfig = merge(baseWebpackConfig, {
|
||||
module: {
|
||||
rules: utils.styleLoaders({
|
||||
sourceMap: config.build.productionSourceMap,
|
||||
extract: true
|
||||
})
|
||||
},
|
||||
devtool: config.build.productionSourceMap ? '#source-map' : false,
|
||||
output: {
|
||||
path: config.assetsRoot,
|
||||
filename: utils.assetsPath('js/[name].[chunkhash].js'),
|
||||
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
|
||||
},
|
||||
plugins: [
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: path.resolve(__dirname, '../static'),
|
||||
to: config.assetsSubDirectory,
|
||||
ignore: ['.*']
|
||||
},
|
||||
{
|
||||
from: path.resolve(__dirname, '../../node_modules/codemirror/mode/*/*'),
|
||||
to: path.join(config.assetsSubDirectory, 'js/codemirror/mode/[name]/[name].js'),
|
||||
transform: function (source, path) {
|
||||
let result = UglifyJS.minify(source.toString('utf8'))
|
||||
if (result.error !== undefined) {
|
||||
return source
|
||||
}
|
||||
return result.code
|
||||
}
|
||||
}
|
||||
]),
|
||||
// http://vuejs.github.io/vue-loader/en/workflow/production.html
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': env
|
||||
}),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
compress: {
|
||||
warnings: false
|
||||
},
|
||||
sourceMap: true
|
||||
}),
|
||||
// extract css into its own file
|
||||
new ExtractTextPlugin({
|
||||
filename: utils.assetsPath('css/[name].[contenthash].css')
|
||||
}),
|
||||
// Compress extracted CSS. We are using this plugin so that possible
|
||||
// duplicated CSS from different components can be deduped.
|
||||
new OptimizeCSSPlugin({
|
||||
cssProcessorOptions: {
|
||||
safe: true
|
||||
}
|
||||
}),
|
||||
// generate dist index.html with correct asset hash for caching.
|
||||
// you can customize output by editing /index.html
|
||||
// see https://github.com/ampedandwired/html-webpack-plugin
|
||||
new HtmlWebpackPlugin({
|
||||
filename: config.index,
|
||||
template: 'assets/index.html',
|
||||
inject: true,
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeAttributeQuotes: true,
|
||||
minifyCSS: true
|
||||
// more options:
|
||||
// https://github.com/kangax/html-minifier#options-quick-reference
|
||||
},
|
||||
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
|
||||
chunksSortMode: 'dependency',
|
||||
serviceWorkerLoader: `<script>${fs.readFileSync(path.join(__dirname,
|
||||
'./service-worker-prod.js'), 'utf-8')}</script>`
|
||||
}),
|
||||
// split vendor js into its own file
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'vendor',
|
||||
minChunks: function (module, count) {
|
||||
// any required modules inside node_modules are extracted to vendor
|
||||
return (
|
||||
module.resource &&
|
||||
/\.js$/.test(module.resource) &&
|
||||
module.resource.indexOf(
|
||||
path.join(__dirname, '../../node_modules')
|
||||
) === 0
|
||||
)
|
||||
}
|
||||
}),
|
||||
// extract webpack runtime and module manifest to its own file in order to
|
||||
// prevent vendor hash from being updated whenever app bundle is updated
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'manifest',
|
||||
chunks: ['vendor']
|
||||
}),
|
||||
// service worker caching
|
||||
new SWPrecacheWebpackPlugin({
|
||||
cacheId: 'File Manager',
|
||||
filename: 'sw.js',
|
||||
replacePrefix: '{{ .BaseURL }}/',
|
||||
staticFileGlobs: ['dist/**/*.{js,html,css}'],
|
||||
minify: true,
|
||||
stripPrefix: 'dist/'
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
if (config.build.bundleAnalyzerReport) {
|
||||
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
|
||||
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
|
||||
}
|
||||
|
||||
module.exports = webpackConfig
|
|
@ -1,116 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
||||
<meta name="base" content="{{ .BaseURL }}">
|
||||
<meta name="staticgen" content="{{ .StaticGen }}">
|
||||
<meta name="noauth" content="{{ .NoAuth }}">
|
||||
<meta name="version" content="{{ .Version }}">
|
||||
<meta name="recaptcha" content="{{ .ReCaptchaKey }}">
|
||||
<title>File Manager</title>
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{{ .BaseURL }}/static/img/icons/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{{ .BaseURL }}/static/img/icons/favicon-16x16.png">
|
||||
<!--[if IE]><link rel="shortcut icon" href="{{ .BaseURL }}/static/img/icons/favicon.ico"><![endif]-->
|
||||
<!-- Add to home screen for Android and modern mobile browsers -->
|
||||
<link rel="manifest" href="{{ .BaseURL }}/static/manifest.json">
|
||||
<meta name="theme-color" content="#2979ff">
|
||||
|
||||
<!-- Add to home screen for Safari on iOS -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="assets">
|
||||
<link rel="apple-touch-icon" href="{{ .BaseURL }}/static/img/icons/apple-touch-icon-152x152.png">
|
||||
<!-- Add to home screen for Windows -->
|
||||
<meta name="msapplication-TileImage" content="{{ .BaseURL }}/static/img/icons/msapplication-icon-144x144.png">
|
||||
<meta name="msapplication-TileColor" content="#2979ff">
|
||||
|
||||
<script>CSS = "{{ .CSS }}"</script>
|
||||
|
||||
{{ if .ReCaptcha -}}
|
||||
<script src='https://www.google.com/recaptcha/api.js?render=explicit'></script>
|
||||
{{ end }}
|
||||
|
||||
<% for (var chunk of webpack.chunks) {
|
||||
for (var file of chunk.files) {
|
||||
if (file.match(/\.(js|css)$/)) { %>
|
||||
<link rel="preload" href="{{ .BaseURL }}/<%= file %>" as="<%= file.match(/\.css$/)?'style':'script' %>"><% }}} %>
|
||||
|
||||
<style>
|
||||
#loading {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
z-index: 9999;
|
||||
transition: .1s ease opacity;
|
||||
-webkit-transition: .1s ease opacity;
|
||||
}
|
||||
|
||||
#loading.done {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.spinner > div {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: #333;
|
||||
border-radius: 100%;
|
||||
display: inline-block;
|
||||
-webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
.spinner .bounce1 {
|
||||
-webkit-animation-delay: -0.32s;
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
.spinner .bounce2 {
|
||||
-webkit-animation-delay: -0.16s;
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes sk-bouncedelay {
|
||||
0%, 80%, 100% { -webkit-transform: scale(0) }
|
||||
40% { -webkit-transform: scale(1.0) }
|
||||
}
|
||||
|
||||
@keyframes sk-bouncedelay {
|
||||
0%, 80%, 100% {
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
} 40% {
|
||||
-webkit-transform: scale(1.0);
|
||||
transform: scale(1.0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
<div id="loading">
|
||||
<div class="spinner">
|
||||
<div class="bounce1"></div>
|
||||
<div class="bounce2"></div>
|
||||
<div class="bounce3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= htmlWebpackPlugin.options.serviceWorkerLoader %>
|
||||
</body>
|
||||
</html>
|
|
@ -1,78 +0,0 @@
|
|||
<template>
|
||||
<router-view :dependencies="loaded" @update:css="updateCSS" @clean:css="cleanCSS"></router-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
computed: mapState(['recaptcha']),
|
||||
data () {
|
||||
return {
|
||||
loaded: false
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.recaptcha.length === 0) {
|
||||
this.unload()
|
||||
return
|
||||
}
|
||||
|
||||
let check = () => {
|
||||
if (typeof window.grecaptcha === 'undefined') {
|
||||
setTimeout(check, 100)
|
||||
return
|
||||
}
|
||||
|
||||
this.unload()
|
||||
}
|
||||
|
||||
check()
|
||||
},
|
||||
methods: {
|
||||
unload () {
|
||||
this.loaded = true
|
||||
// Remove loading animation.
|
||||
let loading = document.getElementById('loading')
|
||||
loading.classList.add('done')
|
||||
|
||||
setTimeout(function () {
|
||||
loading.parentNode.removeChild(loading)
|
||||
}, 200)
|
||||
|
||||
this.updateCSS()
|
||||
},
|
||||
updateCSS (global = false) {
|
||||
let css = this.$store.state.css
|
||||
|
||||
if (typeof this.$store.state.user.css === 'string' && !global) {
|
||||
css += '\n' + this.$store.state.user.css
|
||||
}
|
||||
|
||||
this.removeCSS()
|
||||
|
||||
let style = document.createElement('style')
|
||||
style.title = 'custom-css'
|
||||
style.type = 'text/css'
|
||||
style.appendChild(document.createTextNode(css))
|
||||
document.head.appendChild(style)
|
||||
},
|
||||
removeCSS () {
|
||||
let style = document.querySelector('style[title="custom-css"]')
|
||||
if (style === undefined || style === null) {
|
||||
return
|
||||
}
|
||||
|
||||
style.parentElement.removeChild(style)
|
||||
},
|
||||
cleanCSS () {
|
||||
this.updateCSS(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import './css/styles.css';
|
||||
</style>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,5 +0,0 @@
|
|||
<svg id="content" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144 144">
|
||||
<circle cx="72" cy="72" r="72" fill="#2979ff"/>
|
||||
<circle cx="72" cy="72" r="48" fill="#40c4ff"/>
|
||||
<circle cx="72" cy="72" r="24" fill="#fff"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 239 B |
|
@ -1,220 +0,0 @@
|
|||
<template>
|
||||
<header>
|
||||
<div>
|
||||
<button @click="openSidebar" :aria-label="$t('buttons.toggleSidebar')" :title="$t('buttons.toggleSidebar')" class="action">
|
||||
<i class="material-icons">menu</i>
|
||||
</button>
|
||||
<img src="../assets/logo.svg" alt="File Manager">
|
||||
<search></search>
|
||||
</div>
|
||||
<div>
|
||||
<button @click="openSearch" :aria-label="$t('buttons.search')" :title="$t('buttons.search')" class="search-button action">
|
||||
<i class="material-icons">search</i>
|
||||
</button>
|
||||
|
||||
<button v-show="showSaveButton" :aria-label="$t('buttons.save')" :title="$t('buttons.save')" class="action" id="save-button">
|
||||
<i class="material-icons">save</i>
|
||||
</button>
|
||||
|
||||
<template v-if="staticGen.length > 0">
|
||||
<button v-show="showPublishButton" :aria-label="$t('buttons.publish')" :title="$t('buttons.publish')" class="action" id="publish-button">
|
||||
<i class="material-icons">send</i>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<button @click="openMore" id="more" :aria-label="$t('buttons.more')" :title="$t('buttons.more')" class="action">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
|
||||
<!-- Menu that shows on listing AND mobile when there are files selected -->
|
||||
<div id="file-selection" v-if="isMobile && req.kind === 'listing'">
|
||||
<span v-if="selectedCount > 0">{{ selectedCount }} selected</span>
|
||||
<share-button v-show="showRenameButton"></share-button>
|
||||
<rename-button v-show="showRenameButton"></rename-button>
|
||||
<copy-button v-show="showMoveButton"></copy-button>
|
||||
<move-button v-show="showMoveButton"></move-button>
|
||||
<delete-button v-show="showDeleteButton"></delete-button>
|
||||
</div>
|
||||
|
||||
<!-- This buttons are shown on a dropdown on mobile phones -->
|
||||
<div id="dropdown" :class="{ active: showMore }">
|
||||
<div v-if="!isListing || !isMobile">
|
||||
<share-button v-show="showRenameButton"></share-button>
|
||||
<rename-button v-show="showRenameButton"></rename-button>
|
||||
<copy-button v-show="showMoveButton"></copy-button>
|
||||
<move-button v-show="showMoveButton"></move-button>
|
||||
<delete-button v-show="showDeleteButton"></delete-button>
|
||||
</div>
|
||||
|
||||
<template v-if="staticGen.length > 0">
|
||||
<schedule-button v-show="showPublishButton"></schedule-button>
|
||||
</template>
|
||||
|
||||
<switch-button v-show="showSwitchButton"></switch-button>
|
||||
<download-button v-show="showCommonButton"></download-button>
|
||||
<upload-button v-show="showUpload"></upload-button>
|
||||
<info-button v-show="showCommonButton"></info-button>
|
||||
|
||||
<button v-show="showSelectButton" @click="openSelect" :aria-label="$t('buttons.selectMultiple')" :title="$t('buttons.selectMultiple')" class="action">
|
||||
<i class="material-icons">check_circle</i>
|
||||
<span>{{ $t('buttons.select') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div v-show="showOverlay" @click="resetPrompts" class="overlay"></div>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Search from './Search'
|
||||
import InfoButton from './buttons/Info'
|
||||
import DeleteButton from './buttons/Delete'
|
||||
import RenameButton from './buttons/Rename'
|
||||
import UploadButton from './buttons/Upload'
|
||||
import DownloadButton from './buttons/Download'
|
||||
import SwitchButton from './buttons/SwitchView'
|
||||
import MoveButton from './buttons/Move'
|
||||
import CopyButton from './buttons/Copy'
|
||||
import ScheduleButton from './buttons/Schedule'
|
||||
import ShareButton from './buttons/Share'
|
||||
import {mapGetters, mapState} from 'vuex'
|
||||
import * as api from '@/utils/api'
|
||||
import buttons from '@/utils/buttons'
|
||||
|
||||
export default {
|
||||
name: 'main',
|
||||
components: {
|
||||
Search,
|
||||
InfoButton,
|
||||
DeleteButton,
|
||||
ShareButton,
|
||||
RenameButton,
|
||||
DownloadButton,
|
||||
CopyButton,
|
||||
UploadButton,
|
||||
SwitchButton,
|
||||
MoveButton,
|
||||
ScheduleButton
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
width: window.innerWidth,
|
||||
pluginData: {
|
||||
api,
|
||||
buttons,
|
||||
'store': this.$store,
|
||||
'router': this.$router
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
window.addEventListener('resize', () => {
|
||||
this.width = window.innerWidth
|
||||
})
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'selectedCount'
|
||||
]),
|
||||
...mapState([
|
||||
'req',
|
||||
'user',
|
||||
'loading',
|
||||
'reload',
|
||||
'multiple',
|
||||
'staticGen'
|
||||
]),
|
||||
isMobile () {
|
||||
return this.width <= 736
|
||||
},
|
||||
isListing () {
|
||||
return this.req.kind === 'listing'
|
||||
},
|
||||
showSelectButton () {
|
||||
return this.req.kind === 'listing' && !this.loading && this.$route.name === 'Files'
|
||||
},
|
||||
showSaveButton () {
|
||||
return (this.req.kind === 'editor' && !this.loading)
|
||||
},
|
||||
showPublishButton () {
|
||||
return (this.req.kind === 'editor' && !this.loading && this.user.allowPublish)
|
||||
},
|
||||
showSwitchButton () {
|
||||
return this.req.kind === 'listing' && this.$route.name === 'Files' && !this.loading
|
||||
},
|
||||
showCommonButton () {
|
||||
return !(this.$route.name !== 'Files' || this.loading)
|
||||
},
|
||||
showUpload () {
|
||||
if (this.$route.name !== 'Files' || this.loading) return false
|
||||
|
||||
if (this.req.kind === 'editor') return false
|
||||
return this.user.allowNew
|
||||
},
|
||||
showDeleteButton () {
|
||||
if (this.$route.name !== 'Files' || this.loading) return false
|
||||
|
||||
if (this.req.kind === 'listing') {
|
||||
if (this.selectedCount === 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.user.allowEdit
|
||||
}
|
||||
|
||||
return this.user.allowEdit
|
||||
},
|
||||
showRenameButton () {
|
||||
if (this.$route.name !== 'Files' || this.loading) return false
|
||||
|
||||
if (this.req.kind === 'listing') {
|
||||
if (this.selectedCount === 1) {
|
||||
return this.user.allowEdit
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return this.user.allowEdit
|
||||
},
|
||||
showMoveButton () {
|
||||
if (this.$route.name !== 'Files' || this.loading) return false
|
||||
|
||||
if (this.req.kind !== 'listing') {
|
||||
return false
|
||||
}
|
||||
|
||||
if (this.selectedCount > 0) {
|
||||
return this.user.allowEdit
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
showMore () {
|
||||
if (this.$route.name !== 'Files' || this.loading) return false
|
||||
return (this.$store.state.show === 'more')
|
||||
},
|
||||
showOverlay () {
|
||||
return (this.$store.state.show === 'more')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openSidebar () {
|
||||
this.$store.commit('showHover', 'sidebar')
|
||||
},
|
||||
openMore () {
|
||||
this.$store.commit('showHover', 'more')
|
||||
},
|
||||
openSearch () {
|
||||
this.$store.commit('showHover', 'search')
|
||||
},
|
||||
openSelect () {
|
||||
this.$store.commit('multiple', true)
|
||||
this.resetPrompts()
|
||||
},
|
||||
resetPrompts () {
|
||||
this.$store.commit('closeHovers')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,23 +0,0 @@
|
|||
<template>
|
||||
<select v-on:change="change" :value="selected">
|
||||
<option value="en">{{ $t('languages.en') }}</option>
|
||||
<option value="fr">{{ $t('languages.fr') }}</option>
|
||||
<option value="pt">{{ $t('languages.pt') }}</option>
|
||||
<option value="ja">{{ $t('languages.ja') }}</option>
|
||||
<option value="zh-cn">{{ $t('languages.zhCN') }}</option>
|
||||
<option value="zh-tw">{{ $t('languages.zhTW') }}</option>
|
||||
<option value="es">{{ $t('languages.es') }}</option>
|
||||
</select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'languages',
|
||||
props: [ 'selected' ],
|
||||
methods: {
|
||||
change (event) {
|
||||
this.$emit('update:selected', event.target.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,265 +0,0 @@
|
|||
<template>
|
||||
<div id="search" @click="open" v-bind:class="{ active , ongoing }">
|
||||
<div id="input">
|
||||
<button v-if="active" class="action" @click="close" :aria-label="$t('buttons.close')" :title="$t('buttons.close')">
|
||||
<i class="material-icons">arrow_back</i>
|
||||
</button>
|
||||
<i v-else class="material-icons">search</i>
|
||||
<input type="text"
|
||||
@keyup="keyup"
|
||||
@keyup.enter="submit"
|
||||
ref="input"
|
||||
:autofocus="active"
|
||||
v-model.trim="value"
|
||||
:aria-label="$t('search.writeToSearch')"
|
||||
:placeholder="placeholder">
|
||||
</div>
|
||||
|
||||
<div id="result">
|
||||
<div>
|
||||
<template v-if="search.length === 0 && commands.length === 0">
|
||||
<p>{{ text }}</p>
|
||||
|
||||
<template v-if="value.length === 0">
|
||||
<div class="boxes">
|
||||
<h3>{{ $t('search.types') }}</h3>
|
||||
<div>
|
||||
<div tabindex="0"
|
||||
role="button"
|
||||
@click="init('type:image')"
|
||||
:aria-label="$t('search.images')">
|
||||
<i class="material-icons">insert_photo</i>
|
||||
<p>{{ $t('search.images') }}</p>
|
||||
</div>
|
||||
|
||||
<div tabindex="0"
|
||||
role="button"
|
||||
@click="init('type:audio')"
|
||||
:aria-label="$t('search.music')">
|
||||
<i class="material-icons">volume_up</i>
|
||||
<p>{{ $t('search.music') }}</p>
|
||||
</div>
|
||||
|
||||
<div tabindex="0"
|
||||
role="button"
|
||||
@click="init('type:video')"
|
||||
:aria-label="$t('search.video')">
|
||||
<i class="material-icons">movie</i>
|
||||
<p>{{ $t('search.video') }}</p>
|
||||
</div>
|
||||
|
||||
<div tabindex="0"
|
||||
role="button"
|
||||
@click="init('type:pdf')"
|
||||
:aria-label="$t('search.pdf')">
|
||||
<i class="material-icons">picture_as_pdf</i>
|
||||
<p>{{ $t('search.pdf') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
<ul v-else-if="search.length > 0">
|
||||
<li v-for="s in search">
|
||||
<router-link @click.native="close" :to="'./' + s.path">
|
||||
<i v-if="s.dir" class="material-icons">folder</i>
|
||||
<i v-else class="material-icons">insert_drive_file</i>
|
||||
<span>./{{ s.path }}</span>
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<pre v-else-if="commands.length > 0">
|
||||
<template v-for="c in commands">{{ c }}</template>
|
||||
</pre>
|
||||
</div>
|
||||
<p id="renew"><i class="material-icons spin">autorenew</i></p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import url from '@/utils/url'
|
||||
import * as api from '@/utils/api'
|
||||
|
||||
export default {
|
||||
name: 'search',
|
||||
data: function () {
|
||||
return {
|
||||
value: '',
|
||||
active: false,
|
||||
ongoing: false,
|
||||
scrollable: null,
|
||||
search: [],
|
||||
commands: [],
|
||||
reload: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show (val, old) {
|
||||
this.active = (val === 'search')
|
||||
|
||||
// If the hover was search and now it's something else
|
||||
// we should blur the input.
|
||||
if (old === 'search' && val !== 'search') {
|
||||
if (this.reload) {
|
||||
this.$store.commit('setReload', true)
|
||||
}
|
||||
|
||||
document.body.style.overflow = 'auto'
|
||||
this.reset()
|
||||
this.$refs.input.blur()
|
||||
}
|
||||
|
||||
// If we are starting to show the search box, we should
|
||||
// focus the input.
|
||||
if (val === 'search') {
|
||||
this.reload = false
|
||||
this.$refs.input.focus()
|
||||
document.body.style.overflow = 'hidden'
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['user', 'show']),
|
||||
// Placeholder value.
|
||||
placeholder: function () {
|
||||
if (this.user.allowCommands && this.user.commands.length > 0) {
|
||||
return this.$t('search.searchOrCommand')
|
||||
}
|
||||
|
||||
return this.$t('search.search')
|
||||
},
|
||||
// The text that is shown on the results' box while
|
||||
// there is no search result or command output to show.
|
||||
text: function () {
|
||||
if (this.ongoing) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (this.value.length === 0) {
|
||||
if (this.user.allowCommands && this.user.commands.length > 0) {
|
||||
return `${this.$t('search.searchOrSupportedCommand')} ${this.user.commands.join(', ')}.`
|
||||
}
|
||||
|
||||
this.$t('search.type')
|
||||
}
|
||||
|
||||
if (!this.supported() || !this.user.allowCommands) {
|
||||
return this.$t('search.pressToSearch')
|
||||
} else {
|
||||
return this.$t('search.pressToExecute')
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
// Gets the result div which will be scrollable.
|
||||
this.scrollable = document.querySelector('#search #result')
|
||||
|
||||
// Adds the keydown event on window for the ESC key, so
|
||||
// when it's pressed, it closes the search window.
|
||||
window.addEventListener('keydown', (event) => {
|
||||
if (event.keyCode === 27) {
|
||||
this.$store.commit('closeHovers')
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
// Sets the search to active.
|
||||
open (event) {
|
||||
this.$store.commit('showHover', 'search')
|
||||
},
|
||||
// Closes the search and prevents the event
|
||||
// of propagating so it doesn't trigger the
|
||||
// click event on #search.
|
||||
close (event) {
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
this.$store.commit('closeHovers')
|
||||
},
|
||||
// Checks if the current input is a supported command.
|
||||
supported () {
|
||||
let pieces = this.value.split(' ')
|
||||
|
||||
for (let i = 0; i < this.user.commands.length; i++) {
|
||||
if (pieces[0] === this.user.commands[i]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
// Initializes the search with a default value.
|
||||
init (string) {
|
||||
this.value = string + ' '
|
||||
this.$refs.input.focus()
|
||||
},
|
||||
// Resets the search box value.
|
||||
reset () {
|
||||
this.value = ''
|
||||
this.active = false
|
||||
this.ongoing = false
|
||||
this.search = []
|
||||
this.commands = []
|
||||
},
|
||||
// When the user presses a key, if it is ESC
|
||||
// then it will close the search box. Otherwise,
|
||||
// it will set the search box to active and clean
|
||||
// the search results, as well as commands'.
|
||||
keyup (event) {
|
||||
if (event.keyCode === 27) {
|
||||
this.close(event)
|
||||
return
|
||||
}
|
||||
|
||||
this.search.length = 0
|
||||
this.commands.length = 0
|
||||
},
|
||||
// Submits the input to the server and sets ongoing to true.
|
||||
submit (event) {
|
||||
this.ongoing = true
|
||||
|
||||
let path = this.$route.path
|
||||
if (this.$store.state.req.kind !== 'listing') {
|
||||
path = url.removeLastDir(path) + '/'
|
||||
}
|
||||
|
||||
// In case of being a command.
|
||||
if (this.supported() && this.user.allowCommands) {
|
||||
api.command(path, this.value,
|
||||
(event) => {
|
||||
this.commands.push(event.data)
|
||||
this.scrollable.scrollTop = this.scrollable.scrollHeight
|
||||
},
|
||||
(event) => {
|
||||
this.reload = true
|
||||
this.ongoing = false
|
||||
this.scrollable.scrollTop = this.scrollable.scrollHeight
|
||||
}
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// In case of being a search.
|
||||
api.search(path, this.value,
|
||||
(event) => {
|
||||
let response = JSON.parse(event.data)
|
||||
if (response.path[0] === '/') {
|
||||
response.path = response.path.substring(1)
|
||||
}
|
||||
|
||||
this.search.push(response)
|
||||
this.scrollable.scrollTop = this.scrollable.scrollHeight
|
||||
},
|
||||
(event) => {
|
||||
this.ongoing = false
|
||||
this.scrollable.scrollTop = this.scrollable.scrollHeight
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,90 +0,0 @@
|
|||
<template>
|
||||
<nav :class="{active}">
|
||||
<router-link class="action" to="/files/" :aria-label="$t('sidebar.myFiles')" :title="$t('sidebar.myFiles')">
|
||||
<i class="material-icons">folder</i>
|
||||
<span>{{ $t('sidebar.myFiles') }}</span>
|
||||
</router-link>
|
||||
|
||||
<div v-if="user.allowNew">
|
||||
<button @click="$store.commit('showHover', 'newDir')" class="action" :aria-label="$t('sidebar.newFolder')" :title="$t('sidebar.newFolder')">
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>{{ $t('sidebar.newFolder') }}</span>
|
||||
</button>
|
||||
|
||||
<button @click="$store.commit('showHover', 'newFile')" class="action" :aria-label="$t('sidebar.newFile')" :title="$t('sidebar.newFile')">
|
||||
<i class="material-icons">note_add</i>
|
||||
<span>{{ $t('sidebar.newFile') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="staticGen.length > 0">
|
||||
<router-link to="/files/settings"
|
||||
:aria-label="$t('sidebar.siteSettings')"
|
||||
:title="$t('sidebar.siteSettings')"
|
||||
class="action">
|
||||
<i class="material-icons">settings</i>
|
||||
<span>{{ $t('sidebar.siteSettings') }}</span>
|
||||
</router-link>
|
||||
|
||||
<template v-if="staticGen === 'hugo'">
|
||||
<button class="action"
|
||||
:aria-label="$t('sidebar.hugoNew')"
|
||||
:title="$t('sidebar.hugoNew')"
|
||||
v-if="user.allowNew"
|
||||
@click="$store.commit('showHover', 'new-archetype')">
|
||||
<i class="material-icons">merge_type</i>
|
||||
<span>{{ $t('sidebar.hugoNew') }}</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<button class="action"
|
||||
:aria-label="$t('sidebar.preview')"
|
||||
:title="$t('sidebar.preview')"
|
||||
@click="preview">
|
||||
<i class="material-icons">remove_red_eye</i>
|
||||
<span>{{ $t('sidebar.preview') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="!$store.state.noAuth">
|
||||
<router-link class="action" to="/settings" :aria-label="$t('sidebar.settings')" :title="$t('sidebar.settings')">
|
||||
<i class="material-icons">settings_applications</i>
|
||||
<span>{{ $t('sidebar.settings') }}</span>
|
||||
</router-link>
|
||||
|
||||
<button @click="logout" class="action" id="logout" :aria-label="$t('sidebar.logout')" :title="$t('sidebar.logout')">
|
||||
<i class="material-icons">exit_to_app</i>
|
||||
<span>{{ $t('sidebar.logout') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="credits">
|
||||
<span><a rel="noopener noreferrer" href="https://github.com/hacdias/filemanager">File Manager</a> v{{ version }}</span>
|
||||
<span><a @click="help">{{ $t('sidebar.help') }}</a></span>
|
||||
</p>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState} from 'vuex'
|
||||
import auth from '@/utils/auth'
|
||||
|
||||
export default {
|
||||
name: 'sidebar',
|
||||
computed: {
|
||||
...mapState(['user', 'staticGen', 'version']),
|
||||
active () {
|
||||
return this.$store.state.show === 'sidebar'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
help () {
|
||||
this.$store.commit('showHover', 'help')
|
||||
},
|
||||
preview () {
|
||||
window.open(this.$store.state.baseURL + '/preview/')
|
||||
},
|
||||
logout: auth.logout
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,17 +0,0 @@
|
|||
<template>
|
||||
<button @click="show" :aria-label="$t('buttons.copy')" :title="$t('buttons.copy')" class="action" id="copy-button">
|
||||
<i class="material-icons">content_copy</i>
|
||||
<span>{{ $t('buttons.copyFile') }}</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'copy-button',
|
||||
methods: {
|
||||
show: function (event) {
|
||||
this.$store.commit('showHover', 'copy')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,17 +0,0 @@
|
|||
<template>
|
||||
<button @click="show" :aria-label="$t('buttons.delete')" :title="$t('buttons.delete')" class="action" id="delete-button">
|
||||
<i class="material-icons">delete</i>
|
||||
<span>{{ $t('buttons.delete') }}</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'delete-button',
|
||||
methods: {
|
||||
show: function (event) {
|
||||
this.$store.commit('showHover', 'delete')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,39 +0,0 @@
|
|||
<template>
|
||||
<button @click="download" :aria-label="$t('buttons.download')" :title="$t('buttons.download')" id="download-button" class="action">
|
||||
<i class="material-icons">file_download</i>
|
||||
<span>{{ $t('buttons.download') }}</span>
|
||||
<span v-if="selectedCount > 0" class="counter">{{ selectedCount }}</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters, mapState} from 'vuex'
|
||||
import * as api from '@/utils/api'
|
||||
|
||||
export default {
|
||||
name: 'download-button',
|
||||
computed: {
|
||||
...mapState(['req', 'selected']),
|
||||
...mapGetters(['selectedCount'])
|
||||
},
|
||||
methods: {
|
||||
download: function (event) {
|
||||
// If we are not on a listing, download the current file.
|
||||
if (this.req.kind !== 'listing') {
|
||||
api.download(null, this.$route.path)
|
||||
return
|
||||
}
|
||||
|
||||
// If we are on a listing and there is one element selected,
|
||||
// download it.
|
||||
if (this.selectedCount === 1 && !this.req.items[this.selected[0]].isDir) {
|
||||
api.download(null, this.req.items[this.selected[0]].url)
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise show the prompt to choose the formt of the download.
|
||||
this.$store.commit('showHover', 'download')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,17 +0,0 @@
|
|||
<template>
|
||||
<button :title="$t('buttons.info')" :aria-label="$t('buttons.info')" class="action" @click="show">
|
||||
<i class="material-icons">info</i>
|
||||
<span>{{ $t('buttons.info') }}</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'info-button',
|
||||
methods: {
|
||||
show: function (event) {
|
||||
this.$store.commit('showHover', 'info')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,17 +0,0 @@
|
|||
<template>
|
||||
<button @click="show" :aria-label="$t('buttons.move')" :title="$t('buttons.move')" class="action" id="move-button">
|
||||
<i class="material-icons">forward</i>
|
||||
<span>{{ $t('buttons.moveFile') }}</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'move-button',
|
||||
methods: {
|
||||
show: function (event) {
|
||||
this.$store.commit('showHover', 'move')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,17 +0,0 @@
|
|||
<template>
|
||||
<button @click="show" :aria-label="$t('buttons.rename')" :title="$t('buttons.rename')" class="action" id="rename-button">
|
||||
<i class="material-icons">mode_edit</i>
|
||||
<span>{{ $t('buttons.rename') }}</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'rename-button',
|
||||
methods: {
|
||||
show: function (event) {
|
||||
this.$store.commit('showHover', 'rename')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,21 +0,0 @@
|
|||
<template>
|
||||
<button @click="show"
|
||||
:aria-label="$t('buttons.schedule')"
|
||||
:title="$t('buttons.schedule')"
|
||||
id="schedule-button"
|
||||
class="action">
|
||||
<i class="material-icons">alarm</i>
|
||||
<span>{{ $t('buttons.schedule') }}</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'schedule-button',
|
||||
methods: {
|
||||
show: function (event) {
|
||||
this.$store.commit('showHover', 'schedule')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,17 +0,0 @@
|
|||
<template>
|
||||
<button @click="show" :aria-label="$t('buttons.share')" :title="$t('buttons.share')" class="action">
|
||||
<i class="material-icons">share</i>
|
||||
<span>{{ $t('buttons.share') }}</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'share-button',
|
||||
methods: {
|
||||
show (event) {
|
||||
this.$store.commit('showHover', 'share')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,36 +0,0 @@
|
|||
<template>
|
||||
<button @click="change" :aria-label="$t('buttons.switchView')" :title="$t('buttons.switchView')" class="action" id="switch-view-button">
|
||||
<i class="material-icons">{{ icon }}</i>
|
||||
<span>{{ $t('buttons.switchView') }}</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations } from 'vuex'
|
||||
import { updateUser } from '@/utils/api'
|
||||
|
||||
export default {
|
||||
name: 'switch-button',
|
||||
computed: {
|
||||
...mapState(['user']),
|
||||
icon: function () {
|
||||
if (this.user.viewMode === 'mosaic') return 'view_list'
|
||||
return 'view_module'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(['updateUser']),
|
||||
change: function (event) {
|
||||
// If we are on mobile we should close the dropdown.
|
||||
this.$store.commit('closeHovers')
|
||||
|
||||
let user = {...this.user}
|
||||
user.viewMode = (this.icon === 'view_list') ? 'list' : 'mosaic'
|
||||
|
||||
updateUser(user, 'partial').then(() => {
|
||||
this.updateUser({ viewMode: user.viewMode })
|
||||
}).catch(this.$showError)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,17 +0,0 @@
|
|||
<template>
|
||||
<button @click="upload" :aria-label="$t('buttons.upload')" :title="$t('buttons.upload')" class="action" id="upload-button">
|
||||
<i class="material-icons">file_upload</i>
|
||||
<span>{{ $t('buttons.upload') }}</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'upload-button',
|
||||
methods: {
|
||||
upload: function (event) {
|
||||
document.getElementById('upload-input').click()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,143 +0,0 @@
|
|||
<template>
|
||||
<form id="editor" :class="req.language">
|
||||
<div v-if="hasMetadata" id="metadata">
|
||||
<h2>{{ $t('files.metadata') }}</h2>
|
||||
</div>
|
||||
|
||||
<h2 v-if="hasMetadata">{{ $t('files.body') }}</h2>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import CodeMirror from '@/utils/codemirror'
|
||||
import * as api from '@/utils/api'
|
||||
import buttons from '@/utils/buttons'
|
||||
|
||||
export default {
|
||||
name: 'editor',
|
||||
computed: {
|
||||
...mapState(['req', 'schedule']),
|
||||
hasMetadata: function () {
|
||||
return (this.req.metadata !== undefined && this.req.metadata !== null)
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
metadata: null,
|
||||
metalang: null,
|
||||
content: null
|
||||
}
|
||||
},
|
||||
created () {
|
||||
window.addEventListener('keydown', this.keyEvent)
|
||||
document.getElementById('save-button').addEventListener('click', this.save)
|
||||
|
||||
let publish = document.getElementById('publish-button')
|
||||
if (publish !== null) {
|
||||
publish.addEventListener('click', this.publish)
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
window.removeEventListener('keydown', this.keyEvent)
|
||||
document.getElementById('save-button').removeEventListener('click', this.save)
|
||||
|
||||
let publish = document.getElementById('publish-button')
|
||||
if (publish !== null) {
|
||||
publish.removeEventListener('click', this.publish)
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
if (this.req.content === undefined || this.req.content === null) {
|
||||
this.req.content = ''
|
||||
}
|
||||
|
||||
// Set up the main content editor.
|
||||
this.content = CodeMirror(document.getElementById('editor'), {
|
||||
value: this.req.content,
|
||||
lineNumbers: (this.req.language !== 'markdown'),
|
||||
viewportMargin: 500,
|
||||
autofocus: true,
|
||||
mode: this.req.language,
|
||||
theme: (this.req.language === 'markdown') ? 'markdown' : 'ttcn',
|
||||
lineWrapping: (this.req.language === 'markdown')
|
||||
})
|
||||
|
||||
CodeMirror.autoLoadMode(this.content, this.req.language)
|
||||
|
||||
// Prevent of going on if there is no metadata.
|
||||
if (!this.hasMetadata) {
|
||||
return
|
||||
}
|
||||
|
||||
this.parseMetadata()
|
||||
|
||||
// Set up metadata editor.
|
||||
this.metadata = CodeMirror(document.getElementById('metadata'), {
|
||||
value: this.req.metadata,
|
||||
viewportMargin: Infinity,
|
||||
lineWrapping: true,
|
||||
theme: 'markdown',
|
||||
mode: this.metalang
|
||||
})
|
||||
|
||||
CodeMirror.autoLoadMode(this.metadata, this.metalang)
|
||||
},
|
||||
methods: {
|
||||
// Saves the content when the user presses CTRL-S.
|
||||
keyEvent (event) {
|
||||
if (!event.ctrlKey && !event.metaKey) {
|
||||
return
|
||||
}
|
||||
|
||||
if (String.fromCharCode(event.which).toLowerCase() !== 's') {
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
this.save()
|
||||
},
|
||||
// Parses the metadata and gets the language in which
|
||||
// it is written.
|
||||
parseMetadata () {
|
||||
if (this.req.metadata.startsWith('{')) {
|
||||
this.metalang = 'json'
|
||||
}
|
||||
|
||||
if (this.req.metadata.startsWith('---')) {
|
||||
this.metalang = 'yaml'
|
||||
}
|
||||
|
||||
if (this.req.metadata.startsWith('+++')) {
|
||||
this.metalang = 'toml'
|
||||
}
|
||||
},
|
||||
// Publishes the file.
|
||||
publish (event) {
|
||||
this.save(event, true)
|
||||
},
|
||||
// Saves the file.
|
||||
save (event, regenerate = false) {
|
||||
let button = regenerate ? 'publish' : 'save'
|
||||
if (this.schedule !== '') button = 'schedule'
|
||||
let content = this.content.getValue()
|
||||
buttons.loading(button)
|
||||
|
||||
if (this.hasMetadata) {
|
||||
content = this.metadata.getValue() + '\n\n' + content
|
||||
}
|
||||
|
||||
api.put(this.$route.path, content, regenerate, this.schedule)
|
||||
.then(() => {
|
||||
buttons.success(button)
|
||||
this.$store.commit('setSchedule', '')
|
||||
})
|
||||
.catch(error => {
|
||||
buttons.done(button)
|
||||
this.$showError(error)
|
||||
this.$store.commit('setSchedule', '')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,381 +0,0 @@
|
|||
<template>
|
||||
<div v-if="(req.numDirs + req.numFiles) == 0">
|
||||
<h2 class="message">
|
||||
<i class="material-icons">sentiment_dissatisfied</i>
|
||||
<span>{{ $t('files.lonely') }}</span>
|
||||
</h2>
|
||||
<input style="display:none" type="file" id="upload-input" @change="uploadInput($event)" multiple>
|
||||
</div>
|
||||
<div v-else id="listing"
|
||||
:class="user.viewMode"
|
||||
@dragenter="dragEnter"
|
||||
@dragend="dragEnd">
|
||||
<div>
|
||||
<div class="item header">
|
||||
<div></div>
|
||||
<div>
|
||||
<p :class="{ active: nameSorted }" class="name"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="sort('name')"
|
||||
:title="$t('files.sortByName')"
|
||||
:aria-label="$t('files.sortByName')">
|
||||
<span>{{ $t('files.name') }}</span>
|
||||
<i class="material-icons">{{ nameIcon }}</i>
|
||||
</p>
|
||||
|
||||
<p :class="{ active: sizeSorted }" class="size"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="sort('size')"
|
||||
:title="$t('files.sortBySize')"
|
||||
:aria-label="$t('files.sortBySize')">
|
||||
<span>{{ $t('files.size') }}</span>
|
||||
<i class="material-icons">{{ sizeIcon }}</i>
|
||||
</p>
|
||||
<p :class="{ active: modifiedSorted }" class="modified"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="sort('modified')"
|
||||
:title="$t('files.sortByLastModified')"
|
||||
:aria-label="$t('files.sortByLastModified')">
|
||||
<span>{{ $t('files.lastModified') }}</span>
|
||||
<i class="material-icons">{{ modifiedIcon }}</i>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 v-if="req.numDirs > 0">{{ $t('files.folders') }}</h2>
|
||||
<div v-if="req.numDirs > 0">
|
||||
<item v-for="(item, index) in req.items"
|
||||
v-if="item.isDir"
|
||||
:key="base64(item.name)"
|
||||
v-bind:index="index"
|
||||
v-bind:name="item.name"
|
||||
v-bind:isDir="item.isDir"
|
||||
v-bind:url="item.url"
|
||||
v-bind:modified="item.modified"
|
||||
v-bind:type="item.type"
|
||||
v-bind:size="item.size">
|
||||
</item>
|
||||
</div>
|
||||
|
||||
<h2 v-if="req.numFiles > 0">{{ $t('files.files') }}</h2>
|
||||
<div v-if="req.numFiles > 0">
|
||||
<item v-for="(item, index) in req.items"
|
||||
v-if="!item.isDir"
|
||||
:key="base64(item.name)"
|
||||
v-bind:index="index"
|
||||
v-bind:name="item.name"
|
||||
v-bind:isDir="item.isDir"
|
||||
v-bind:url="item.url"
|
||||
v-bind:modified="item.modified"
|
||||
v-bind:type="item.type"
|
||||
v-bind:size="item.size">
|
||||
</item>
|
||||
</div>
|
||||
|
||||
<input style="display:none" type="file" id="upload-input" @change="uploadInput($event)" multiple>
|
||||
|
||||
<div v-show="$store.state.multiple" :class="{ active: $store.state.multiple }" id="multiple-selection">
|
||||
<p>{{ $t('files.multipleSelectionEnabled') }}</p>
|
||||
<div @click="$store.commit('multiple', false)" tabindex="0" role="button" :title="$t('files.clear')" :aria-label="$t('files.clear')" class="action">
|
||||
<i class="material-icons">clear</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState} from 'vuex'
|
||||
import Item from './ListingItem'
|
||||
import css from '@/utils/css'
|
||||
import * as api from '@/utils/api'
|
||||
import buttons from '@/utils/buttons'
|
||||
|
||||
export default {
|
||||
name: 'listing',
|
||||
components: { Item },
|
||||
computed: {
|
||||
...mapState(['req', 'selected', 'user']),
|
||||
nameSorted () {
|
||||
return (this.req.sort === 'name')
|
||||
},
|
||||
sizeSorted () {
|
||||
return (this.req.sort === 'size')
|
||||
},
|
||||
modifiedSorted () {
|
||||
return (this.req.sort === 'modified')
|
||||
},
|
||||
ascOrdered () {
|
||||
return (this.req.order === 'asc')
|
||||
},
|
||||
nameIcon () {
|
||||
if (this.nameSorted && !this.ascOrdered) {
|
||||
return 'arrow_upward'
|
||||
}
|
||||
|
||||
return 'arrow_downward'
|
||||
},
|
||||
sizeIcon () {
|
||||
if (this.sizeSorted && this.ascOrdered) {
|
||||
return 'arrow_downward'
|
||||
}
|
||||
|
||||
return 'arrow_upward'
|
||||
},
|
||||
modifiedIcon () {
|
||||
if (this.modifiedSorted && this.ascOrdered) {
|
||||
return 'arrow_downward'
|
||||
}
|
||||
|
||||
return 'arrow_upward'
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
// Check the columns size for the first time.
|
||||
this.resizeEvent()
|
||||
|
||||
// Add the needed event listeners to the window and document.
|
||||
window.addEventListener('keydown', this.keyEvent)
|
||||
window.addEventListener('resize', this.resizeEvent)
|
||||
document.addEventListener('dragover', this.preventDefault)
|
||||
document.addEventListener('drop', this.drop)
|
||||
},
|
||||
beforeDestroy () {
|
||||
// Remove event listeners before destroying this page.
|
||||
window.removeEventListener('keydown', this.keyEvent)
|
||||
window.removeEventListener('resize', this.resizeEvent)
|
||||
document.removeEventListener('dragover', this.preventDefault)
|
||||
document.removeEventListener('drop', this.drop)
|
||||
},
|
||||
methods: {
|
||||
base64: function (name) {
|
||||
return window.btoa(unescape(encodeURIComponent(name)))
|
||||
},
|
||||
keyEvent (event) {
|
||||
if (!event.ctrlKey && !event.metaKey) {
|
||||
return
|
||||
}
|
||||
|
||||
let key = String.fromCharCode(event.which).toLowerCase()
|
||||
|
||||
switch (key) {
|
||||
case 'f':
|
||||
event.preventDefault()
|
||||
this.$store.commit('showHover', 'search')
|
||||
break
|
||||
case 'c':
|
||||
case 'x':
|
||||
this.copyCut(event, key)
|
||||
break
|
||||
case 'v':
|
||||
this.paste(event)
|
||||
break
|
||||
}
|
||||
},
|
||||
preventDefault (event) {
|
||||
// Wrapper around prevent default.
|
||||
event.preventDefault()
|
||||
},
|
||||
copyCut (event, key) {
|
||||
event.preventDefault()
|
||||
let items = []
|
||||
|
||||
for (let i of this.selected) {
|
||||
items.push({
|
||||
from: this.req.items[i].url,
|
||||
name: encodeURIComponent(this.req.items[i].name)
|
||||
})
|
||||
}
|
||||
|
||||
this.$store.commit('updateClipboard', {
|
||||
key: key,
|
||||
items: items
|
||||
})
|
||||
},
|
||||
paste (event) {
|
||||
if (event.target.tagName.toLowerCase() === 'input') {
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
let items = []
|
||||
|
||||
for (let item of this.$store.state.clipboard.items) {
|
||||
items.push({
|
||||
from: item.from,
|
||||
to: this.$route.path + item.name
|
||||
})
|
||||
}
|
||||
|
||||
if (this.$store.state.clipboard.key === 'x') {
|
||||
api.move(items).then(() => {
|
||||
this.$store.commit('setReload', true)
|
||||
}).catch(this.$showError)
|
||||
return
|
||||
}
|
||||
|
||||
api.copy(items).then(() => {
|
||||
this.$store.commit('setReload', true)
|
||||
}).catch(this.$showError)
|
||||
},
|
||||
resizeEvent () {
|
||||
// Update the columns size based on the window width.
|
||||
let columns = Math.floor(document.querySelector('main').offsetWidth / 300)
|
||||
let items = css(['#listing.mosaic .item', '.mosaic#listing .item'])
|
||||
if (columns === 0) columns = 1
|
||||
items.style.width = `calc(${100 / columns}% - 1em)`
|
||||
},
|
||||
dragEnter (event) {
|
||||
// When the user starts dragging an item, put every
|
||||
// file on the listing with 50% opacity.
|
||||
let items = document.getElementsByClassName('item')
|
||||
|
||||
Array.from(items).forEach(file => {
|
||||
file.style.opacity = 0.5
|
||||
})
|
||||
},
|
||||
dragEnd (event) {
|
||||
this.resetOpacity()
|
||||
},
|
||||
drop: function (event) {
|
||||
event.preventDefault()
|
||||
this.resetOpacity()
|
||||
|
||||
let dt = event.dataTransfer
|
||||
let files = dt.files
|
||||
let el = event.target
|
||||
|
||||
if (files.length <= 0) return
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
if (el !== null && !el.classList.contains('item')) {
|
||||
el = el.parentElement
|
||||
}
|
||||
}
|
||||
|
||||
let base = ''
|
||||
if (el !== null && el.classList.contains('item') && el.dataset.dir === 'true') {
|
||||
base = el.querySelector('.name').innerHTML + '/'
|
||||
}
|
||||
|
||||
if (base !== '') {
|
||||
api.fetch(this.$route.path + base)
|
||||
.then(req => {
|
||||
this.checkConflict(files, req.items, base)
|
||||
})
|
||||
.catch(this.$showError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
this.checkConflict(files, this.req.items, base)
|
||||
},
|
||||
checkConflict (files, items, base) {
|
||||
if (typeof items === 'undefined' || items === null) {
|
||||
items = []
|
||||
}
|
||||
|
||||
let conflict = false
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
let res = items.findIndex(function hasConflict (element) {
|
||||
return (element.name === this)
|
||||
}, files[i].name)
|
||||
|
||||
if (res >= 0) {
|
||||
conflict = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!conflict) {
|
||||
this.handleFiles(files, base)
|
||||
return
|
||||
}
|
||||
|
||||
this.$store.commit('showHover', {
|
||||
prompt: 'replace',
|
||||
confirm: (event) => {
|
||||
event.preventDefault()
|
||||
this.$store.commit('closeHovers')
|
||||
this.handleFiles(files, base, true)
|
||||
}
|
||||
})
|
||||
},
|
||||
uploadInput (event) {
|
||||
this.checkConflict(event.currentTarget.files, this.req.items, '')
|
||||
},
|
||||
resetOpacity () {
|
||||
let items = document.getElementsByClassName('item')
|
||||
|
||||
Array.from(items).forEach(file => {
|
||||
file.style.opacity = 1
|
||||
})
|
||||
},
|
||||
handleFiles (files, base, overwrite = false) {
|
||||
buttons.loading('upload')
|
||||
let promises = []
|
||||
let progress = new Array(files.length).fill(0)
|
||||
|
||||
let onupload = (id) => (event) => {
|
||||
progress[id] = (event.loaded / event.total) * 100
|
||||
|
||||
let sum = 0
|
||||
for (let i = 0; i < progress.length; i++) {
|
||||
sum += progress[i]
|
||||
}
|
||||
|
||||
this.$store.commit('setProgress', Math.ceil(sum / progress.length))
|
||||
}
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
let file = files[i]
|
||||
promises.push(api.post(this.$route.path + base + file.name, file, overwrite, onupload(i)))
|
||||
}
|
||||
|
||||
let finish = () => {
|
||||
buttons.success('upload')
|
||||
this.$store.commit('setProgress', 0)
|
||||
}
|
||||
|
||||
Promise.all(promises)
|
||||
.then(() => {
|
||||
finish()
|
||||
this.$store.commit('setReload', true)
|
||||
})
|
||||
.catch(error => {
|
||||
finish()
|
||||
this.$showError(error)
|
||||
})
|
||||
|
||||
return false
|
||||
},
|
||||
sort (sort) {
|
||||
let order = 'desc'
|
||||
|
||||
if (sort === 'name') {
|
||||
if (this.nameIcon === 'arrow_upward') {
|
||||
order = 'asc'
|
||||
}
|
||||
} else if (sort === 'size') {
|
||||
if (this.sizeIcon === 'arrow_upward') {
|
||||
order = 'asc'
|
||||
}
|
||||
} else if (sort === 'modified') {
|
||||
if (this.modifiedIcon === 'arrow_upward') {
|
||||
order = 'asc'
|
||||
}
|
||||
}
|
||||
|
||||
let path = this.$store.state.baseURL
|
||||
if (path === '') path = '/'
|
||||
document.cookie = `sort=${sort}; max-age=31536000; path=${path}`
|
||||
document.cookie = `order=${order}; max-age=31536000; path=${path}`
|
||||
this.$store.commit('setReload', true)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,158 +0,0 @@
|
|||
<template>
|
||||
<div class="item"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
draggable="true"
|
||||
@dragstart="dragStart"
|
||||
@dragover="dragOver"
|
||||
@drop="drop"
|
||||
@click="click"
|
||||
@dblclick="open"
|
||||
@touchstart="touchstart"
|
||||
:data-dir="isDir"
|
||||
:aria-label="name"
|
||||
:aria-selected="isSelected">
|
||||
<div>
|
||||
<i class="material-icons">{{ icon }}</i>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="name">{{ name }}</p>
|
||||
|
||||
<p v-if="isDir" class="size" data-order="-1">—</p>
|
||||
<p v-else class="size" :data-order="humanSize()">{{ humanSize() }}</p>
|
||||
|
||||
<p class="modified">
|
||||
<time :datetime="modified">{{ humanTime() }}</time>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapMutations, mapGetters, mapState } from 'vuex'
|
||||
import filesize from 'filesize'
|
||||
import moment from 'moment'
|
||||
import * as api from '@/utils/api'
|
||||
|
||||
export default {
|
||||
name: 'item',
|
||||
data: function () {
|
||||
return {
|
||||
touches: 0
|
||||
}
|
||||
},
|
||||
props: ['name', 'isDir', 'url', 'type', 'size', 'modified', 'index'],
|
||||
computed: {
|
||||
...mapState(['selected', 'req']),
|
||||
...mapGetters(['selectedCount']),
|
||||
isSelected () {
|
||||
return (this.selected.indexOf(this.index) !== -1)
|
||||
},
|
||||
icon () {
|
||||
if (this.isDir) return 'folder'
|
||||
if (this.type === 'image') return 'insert_photo'
|
||||
if (this.type === 'audio') return 'volume_up'
|
||||
if (this.type === 'video') return 'movie'
|
||||
return 'insert_drive_file'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(['addSelected', 'removeSelected', 'resetSelected']),
|
||||
humanSize: function () {
|
||||
return filesize(this.size)
|
||||
},
|
||||
humanTime: function () {
|
||||
return moment(this.modified).fromNow()
|
||||
},
|
||||
dragStart: function (event) {
|
||||
if (this.selectedCount === 0) {
|
||||
this.addSelected(this.index)
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.isSelected) {
|
||||
this.resetSelected()
|
||||
this.addSelected(this.index)
|
||||
}
|
||||
},
|
||||
dragOver: function (event) {
|
||||
if (!this.isDir) return
|
||||
|
||||
event.preventDefault()
|
||||
let el = event.target
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
if (!el.classList.contains('item')) {
|
||||
el = el.parentElement
|
||||
}
|
||||
}
|
||||
|
||||
el.style.opacity = 1
|
||||
},
|
||||
drop: function (event) {
|
||||
if (!this.isDir) return
|
||||
event.preventDefault()
|
||||
|
||||
if (this.selectedCount === 0) return
|
||||
|
||||
let items = []
|
||||
|
||||
for (let i of this.selected) {
|
||||
items.push({
|
||||
from: this.req.items[i].url,
|
||||
to: this.url + encodeURIComponent(this.req.items[i].name)
|
||||
})
|
||||
}
|
||||
|
||||
api.move(items)
|
||||
.then(() => {
|
||||
this.$store.commit('setReload', true)
|
||||
})
|
||||
.catch(this.$showError)
|
||||
},
|
||||
click: function (event) {
|
||||
if (this.selectedCount !== 0) event.preventDefault()
|
||||
if (this.$store.state.selected.indexOf(this.index) !== -1) {
|
||||
this.removeSelected(this.index)
|
||||
return
|
||||
}
|
||||
|
||||
if (event.shiftKey && this.selected.length === 1) {
|
||||
let fi = 0
|
||||
let la = 0
|
||||
|
||||
if (this.index > this.selected[0]) {
|
||||
fi = this.selected[0] + 1
|
||||
la = this.index
|
||||
} else {
|
||||
fi = this.index
|
||||
la = this.selected[0] - 1
|
||||
}
|
||||
|
||||
for (; fi <= la; fi++) {
|
||||
this.addSelected(fi)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (!event.ctrlKey && !this.$store.state.multiple) this.resetSelected()
|
||||
this.addSelected(this.index)
|
||||
},
|
||||
touchstart (event) {
|
||||
setTimeout(() => {
|
||||
this.touches = 0
|
||||
}, 300)
|
||||
|
||||
this.touches++
|
||||
if (this.touches > 1) {
|
||||
this.open()
|
||||
}
|
||||
},
|
||||
open: function (event) {
|
||||
this.$router.push({path: this.url})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,139 +0,0 @@
|
|||
<template>
|
||||
<div id="previewer">
|
||||
<div class="bar">
|
||||
<button @click="back" class="action" :title="$t('files.closePreview')" :aria-label="$t('files.closePreview')" id="close">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
|
||||
<rename-button v-if="allowEdit()"></rename-button>
|
||||
<delete-button v-if="allowEdit()"></delete-button>
|
||||
<download-button></download-button>
|
||||
<info-button></info-button>
|
||||
</div>
|
||||
|
||||
<button class="action" @click="prev" v-show="hasPrevious" :aria-label="$t('buttons.previous')" :title="$t('buttons.previous')">
|
||||
<i class="material-icons">chevron_left</i>
|
||||
</button>
|
||||
<button class="action" @click="next" v-show="hasNext" :aria-label="$t('buttons.next')" :title="$t('buttons.next')">
|
||||
<i class="material-icons">chevron_right</i>
|
||||
</button>
|
||||
|
||||
<div class="preview">
|
||||
<img v-if="req.type == 'image'" :src="raw()">
|
||||
<audio v-else-if="req.type == 'audio'" :src="raw()" autoplay controls></audio>
|
||||
<video v-else-if="req.type == 'video'" :src="raw()" autoplay controls>
|
||||
Sorry, your browser doesn't support embedded videos,
|
||||
but don't worry, you can <a :href="download()">download it</a>
|
||||
and watch it with your favorite video player!
|
||||
</video>
|
||||
<object v-else-if="req.extension == '.pdf'" class="pdf" :data="raw()"></object>
|
||||
<a v-else-if="req.type == 'blob'" :href="download()">
|
||||
<h2 class="message">{{ $t('buttons.download') }} <i class="material-icons">file_download</i></h2>
|
||||
</a>
|
||||
<pre v-else >{{ req.content }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import url from '@/utils/url'
|
||||
import * as api from '@/utils/api'
|
||||
import InfoButton from '@/components/buttons/Info'
|
||||
import DeleteButton from '@/components/buttons/Delete'
|
||||
import RenameButton from '@/components/buttons/Rename'
|
||||
import DownloadButton from '@/components/buttons/Download'
|
||||
|
||||
export default {
|
||||
name: 'preview',
|
||||
components: {
|
||||
InfoButton,
|
||||
DeleteButton,
|
||||
RenameButton,
|
||||
DownloadButton
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
previousLink: '',
|
||||
nextLink: '',
|
||||
listing: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['req', 'oldReq']),
|
||||
hasPrevious () {
|
||||
return (this.previousLink !== '')
|
||||
},
|
||||
hasNext () {
|
||||
return (this.nextLink !== '')
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
window.addEventListener('keyup', this.key)
|
||||
api.fetch(url.removeLastDir(this.$route.path))
|
||||
.then(req => {
|
||||
this.listing = req
|
||||
this.updateLinks()
|
||||
})
|
||||
.catch(this.$showError)
|
||||
},
|
||||
beforeDestroy () {
|
||||
window.removeEventListener('keyup', this.key)
|
||||
},
|
||||
methods: {
|
||||
download () {
|
||||
let url = `${this.$store.state.baseURL}/api/download`
|
||||
url += this.req.url.slice(6)
|
||||
|
||||
return url
|
||||
},
|
||||
raw () {
|
||||
return `${this.download()}?&inline=true`
|
||||
},
|
||||
back (event) {
|
||||
let uri = url.removeLastDir(this.$route.path) + '/'
|
||||
this.$router.push({ path: uri })
|
||||
},
|
||||
prev () {
|
||||
this.$router.push({ path: this.previousLink })
|
||||
},
|
||||
next () {
|
||||
this.$router.push({ path: this.nextLink })
|
||||
},
|
||||
key (event) {
|
||||
event.preventDefault()
|
||||
|
||||
if (event.which === 13 || event.which === 39) { // right arrow
|
||||
if (this.hasNext) this.next()
|
||||
} else if (event.which === 37) { // left arrow
|
||||
if (this.hasPrevious) this.prev()
|
||||
}
|
||||
},
|
||||
updateLinks () {
|
||||
let pos = null
|
||||
|
||||
for (let i = 0; i < this.listing.items.length; i++) {
|
||||
if (this.listing.items[i].name === this.req.name) {
|
||||
pos = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (pos === null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (pos !== 0) {
|
||||
this.previousLink = this.listing.items[pos - 1].url
|
||||
}
|
||||
|
||||
if (pos !== this.listing.items.length - 1) {
|
||||
this.nextLink = this.listing.items[pos + 1].url
|
||||
}
|
||||
},
|
||||
allowEdit (event) {
|
||||
return this.$store.state.user.allowEdit
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,69 +0,0 @@
|
|||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.copy') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t('prompts.copyMessage') }}</p>
|
||||
<file-list @update:selected="val => dest = val"></file-list>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="cancel flat"
|
||||
@click="$store.commit('closeHovers')"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
||||
<button class="flat"
|
||||
@click="copy"
|
||||
:disabled="$route.path === dest"
|
||||
:aria-label="$t('buttons.copy')"
|
||||
:title="$t('buttons.copy')">{{ $t('buttons.copy') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import FileList from './FileList'
|
||||
import * as api from '@/utils/api'
|
||||
import buttons from '@/utils/buttons'
|
||||
|
||||
export default {
|
||||
name: 'copy',
|
||||
components: { FileList },
|
||||
data: function () {
|
||||
return {
|
||||
current: window.location.pathname,
|
||||
dest: null
|
||||
}
|
||||
},
|
||||
computed: mapState(['req', 'selected']),
|
||||
methods: {
|
||||
copy: function (event) {
|
||||
event.preventDefault()
|
||||
buttons.loading('copy')
|
||||
let items = []
|
||||
|
||||
// Create a new promise for each file.
|
||||
for (let item of this.selected) {
|
||||
items.push({
|
||||
from: this.req.items[item].url,
|
||||
to: this.dest + encodeURIComponent(this.req.items[item].name)
|
||||
})
|
||||
}
|
||||
|
||||
// Execute the promises.
|
||||
api.copy(items)
|
||||
.then(() => {
|
||||
buttons.success('copy')
|
||||
this.$router.push({ path: this.dest })
|
||||
})
|
||||
.catch(error => {
|
||||
buttons.done('copy')
|
||||
this.$showError(error)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,80 +0,0 @@
|
|||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-content">
|
||||
<p v-if="req.kind !== 'listing'">{{ $t('prompts.deleteMessageSingle') }}</p>
|
||||
<p v-else>{{ $t('prompts.deleteMessageMultiple', { count: selectedCount}) }}</p>
|
||||
</div>
|
||||
<div class="card-action">
|
||||
<button @click="$store.commit('closeHovers')"
|
||||
class="flat cancel"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
||||
<button @click="submit"
|
||||
class="flat"
|
||||
:aria-label="$t('buttons.delete')"
|
||||
:title="$t('buttons.delete')">{{ $t('buttons.delete') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters, mapMutations, mapState} from 'vuex'
|
||||
import { remove } from '@/utils/api'
|
||||
import url from '@/utils/url'
|
||||
import buttons from '@/utils/buttons'
|
||||
|
||||
export default {
|
||||
name: 'delete',
|
||||
computed: {
|
||||
...mapGetters(['selectedCount']),
|
||||
...mapState(['req', 'selected'])
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(['closeHovers']),
|
||||
submit: function (event) {
|
||||
this.closeHovers()
|
||||
buttons.loading('delete')
|
||||
|
||||
// If we are not on a listing, delete the current
|
||||
// opened file.
|
||||
if (this.req.kind !== 'listing') {
|
||||
remove(this.$route.path)
|
||||
.then(() => {
|
||||
buttons.success('delete')
|
||||
this.$router.push({ path: url.removeLastDir(this.$route.path) + '/' })
|
||||
})
|
||||
.catch(error => {
|
||||
buttons.done('delete')
|
||||
this.$showError(error)
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (this.selectedCount === 0) {
|
||||
// This shouldn't happen...
|
||||
return
|
||||
}
|
||||
|
||||
// Create the promises array and fill it with
|
||||
// the delete request for every selected file.
|
||||
let promises = []
|
||||
|
||||
for (let index of this.selected) {
|
||||
promises.push(remove(this.req.items[index].url))
|
||||
}
|
||||
|
||||
Promise.all(promises)
|
||||
.then(() => {
|
||||
buttons.success('delete')
|
||||
this.$store.commit('setReload', true)
|
||||
})
|
||||
.catch(error => {
|
||||
buttons.done('delete')
|
||||
this.$store.commit('setReload', true)
|
||||
this.$showError(error)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,47 +0,0 @@
|
|||
<template>
|
||||
<div class="card floating" id="download">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.download') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t('prompts.downloadMessage') }}</p>
|
||||
|
||||
<button class="block cancel" @click="download('zip')" autofocus>zip</button>
|
||||
<button class="block cancel" @click="download('tar')" autofocus>tar</button>
|
||||
<button class="block cancel" @click="download('targz')" autofocus>tar.gz</button>
|
||||
<button class="block cancel" @click="download('tarbz2')" autofocus>tar.bz2</button>
|
||||
<button class="block cancel" @click="download('tarxz')" autofocus>tar.xz</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters, mapState} from 'vuex'
|
||||
import * as api from '@/utils/api'
|
||||
|
||||
export default {
|
||||
name: 'download',
|
||||
computed: {
|
||||
...mapState(['selected', 'req']),
|
||||
...mapGetters(['selectedCount'])
|
||||
},
|
||||
methods: {
|
||||
download: function (format) {
|
||||
if (this.selectedCount === 0) {
|
||||
api.download(format, this.$route.path)
|
||||
} else {
|
||||
let files = []
|
||||
|
||||
for (let i of this.selected) {
|
||||
files.push(this.req.items[i].url)
|
||||
}
|
||||
|
||||
api.download(format, ...files)
|
||||
}
|
||||
|
||||
this.$store.commit('closeHovers')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,140 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<ul class="file-list">
|
||||
<li @click="select"
|
||||
@touchstart="touchstart"
|
||||
@dblclick="next"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
:aria-label="item.name"
|
||||
:aria-selected="selected == item.url"
|
||||
:key="item.name" v-for="item in items"
|
||||
:data-url="item.url">{{ item.name }}</li>
|
||||
</ul>
|
||||
|
||||
<p>{{ $t('prompts.currentlyNavigating') }} <code>{{ nav }}</code>.</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import url from '@/utils/url'
|
||||
import * as api from '@/utils/api'
|
||||
|
||||
export default {
|
||||
name: 'file-list',
|
||||
data: function () {
|
||||
return {
|
||||
items: [],
|
||||
touches: {
|
||||
id: '',
|
||||
count: 0
|
||||
},
|
||||
selected: null,
|
||||
current: window.location.pathname
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['req']),
|
||||
nav () {
|
||||
return decodeURIComponent(this.current)
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// If we're showing this on a listing,
|
||||
// we can use the current request object
|
||||
// to fill the move options.
|
||||
if (this.req.kind === 'listing') {
|
||||
this.fillOptions(this.req)
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, we must be on a preview or editor
|
||||
// so we fetch the data from the previous directory.
|
||||
api.fetch(url.removeLastDir(this.$route.path))
|
||||
.then(this.fillOptions)
|
||||
.catch(this.$showError)
|
||||
},
|
||||
methods: {
|
||||
fillOptions (req) {
|
||||
// Sets the current path and resets
|
||||
// the current items.
|
||||
this.current = req.url
|
||||
this.items = []
|
||||
|
||||
this.$emit('update:selected', this.current)
|
||||
|
||||
// If the path isn't the root path,
|
||||
// show a button to navigate to the previous
|
||||
// directory.
|
||||
if (req.url !== '/files/') {
|
||||
this.items.push({
|
||||
name: '..',
|
||||
url: url.removeLastDir(req.url) + '/'
|
||||
})
|
||||
}
|
||||
|
||||
// If this folder is empty, finish here.
|
||||
if (req.items === null) return
|
||||
|
||||
// Otherwise we add every directory to the
|
||||
// move options.
|
||||
for (let item of req.items) {
|
||||
if (!item.isDir) continue
|
||||
|
||||
this.items.push({
|
||||
name: item.name,
|
||||
url: item.url
|
||||
})
|
||||
}
|
||||
},
|
||||
next: function (event) {
|
||||
// Retrieves the URL of the directory the user
|
||||
// just clicked in and fill the options with its
|
||||
// content.
|
||||
let uri = event.currentTarget.dataset.url
|
||||
|
||||
api.fetch(uri)
|
||||
.then(this.fillOptions)
|
||||
.catch(this.$showError)
|
||||
},
|
||||
touchstart (event) {
|
||||
let url = event.currentTarget.dataset.url
|
||||
|
||||
// In 300 milliseconds, we shall reset the count.
|
||||
setTimeout(() => {
|
||||
this.touches.count = 0
|
||||
}, 300)
|
||||
|
||||
// If the element the user is touching
|
||||
// is different from the last one he touched,
|
||||
// reset the count.
|
||||
if (this.touches.id !== url) {
|
||||
this.touches.id = url
|
||||
this.touches.count = 1
|
||||
return
|
||||
}
|
||||
|
||||
this.touches.count++
|
||||
|
||||
// If there is more than one touch already,
|
||||
// open the next screen.
|
||||
if (this.touches.count > 1) {
|
||||
this.next(event)
|
||||
}
|
||||
},
|
||||
select: function (event) {
|
||||
// If the element is already selected, unselect it.
|
||||
if (this.selected === event.currentTarget.dataset.url) {
|
||||
this.selected = null
|
||||
this.$emit('update:selected', this.current)
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise select the element.
|
||||
this.selected = event.currentTarget.dataset.url
|
||||
this.$emit('update:selected', this.selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,34 +0,0 @@
|
|||
<template>
|
||||
<div class="card floating help">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('help.help') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<ul>
|
||||
<li><strong>F1</strong> - {{ $t('help.f1') }}</li>
|
||||
<li><strong>F2</strong> - {{ $t('help.f2') }}</li>
|
||||
<li><strong>DEL</strong> - {{ $t('help.del') }}</li>
|
||||
<li><strong>ESC</strong> - {{ $t('help.esc') }}</li>
|
||||
<li><strong>CTRL + S</strong> - {{ $t('help.ctrl.s') }}</li>
|
||||
<li><strong>CTRL + F</strong> - {{ $t('help.ctrl.f') }}</li>
|
||||
<li><strong>CTRL + Click</strong> - {{ $t('help.ctrl.click') }}</li>
|
||||
<li><strong>Click</strong> - {{ $t('help.click') }}</li>
|
||||
<li><strong>Double click</strong> - {{ $t('help.doubleClick') }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button type="submit"
|
||||
@click="$store.commit('closeHovers')"
|
||||
class="flat"
|
||||
:aria-label="$t('buttons.ok')"
|
||||
:title="$t('buttons.ok')">{{ $t('buttons.ok') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {name: 'help'}
|
||||
</script>
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.fileInfo') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p v-if="selected.length > 1">{{ $t('prompts.filesSelected', { count: selected.length }) }}</p>
|
||||
|
||||
<p v-if="selected.length < 2"><strong>{{ $t('prompts.displayName') }}</strong> {{ name() }}</p>
|
||||
<p><strong>{{ $t('prompts.size') }}:</strong> <span id="content_length"></span>{{ humanSize() }}</p>
|
||||
<p v-if="selected.length < 2"><strong>{{ $t('prompts.lastModified') }}:</strong> {{ humanTime() }}</p>
|
||||
|
||||
<template v-if="dir() && selected.length === 0">
|
||||
<p><strong>{{ $t('prompts.numberFiles') }}:</strong> {{ req.numFiles }}</p>
|
||||
<p><strong>{{ $t('prompts.numberDirs') }}:</strong> {{ req.numDirs }}</p>
|
||||
</template>
|
||||
|
||||
<template v-if="!dir()">
|
||||
<p><strong>MD5:</strong> <code><a @click="checksum($event, 'md5')">{{ $t('prompts.show') }}</a></code></p>
|
||||
<p><strong>SHA1:</strong> <code><a @click="checksum($event, 'sha1')">{{ $t('prompts.show') }}</a></code></p>
|
||||
<p><strong>SHA256:</strong> <code><a @click="checksum($event, 'sha256')">{{ $t('prompts.show') }}</a></code></p>
|
||||
<p><strong>SHA512:</strong> <code><a @click="checksum($event, 'sha512')">{{ $t('prompts.show') }}</a></code></p>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button type="submit"
|
||||
@click="$store.commit('closeHovers')"
|
||||
class="flat"
|
||||
:aria-label="$t('buttons.ok')"
|
||||
:title="$t('buttons.ok')">{{ $t('buttons.ok') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapState, mapGetters} from 'vuex'
|
||||
import filesize from 'filesize'
|
||||
import moment from 'moment'
|
||||
import * as api from '@/utils/api'
|
||||
|
||||
export default {
|
||||
name: 'info',
|
||||
computed: {
|
||||
...mapState(['req', 'selected']),
|
||||
...mapGetters(['selectedCount'])
|
||||
},
|
||||
methods: {
|
||||
humanSize: function () {
|
||||
// If there are no files selected or this is not a listing
|
||||
// show the human file size of the current request.
|
||||
if (this.selectedCount === 0 || this.req.kind !== 'listing') {
|
||||
return filesize(this.req.size)
|
||||
}
|
||||
|
||||
// Otherwise, sum the sizes of each selected file and returns
|
||||
// its human form.
|
||||
var sum = 0
|
||||
|
||||
for (let i = 0; i < this.selectedCount; i++) {
|
||||
sum += this.req.items[this.selected[i]].size
|
||||
}
|
||||
|
||||
return filesize(sum)
|
||||
},
|
||||
humanTime: function () {
|
||||
// If there are no selected files, return the current request
|
||||
// modified time.
|
||||
if (this.selectedCount === 0) {
|
||||
return moment(this.req.modified).fromNow()
|
||||
}
|
||||
|
||||
// Otherwise return the modified time of the first item
|
||||
// that is selected since this should not appear when
|
||||
// there is more than one file selected.
|
||||
return moment(this.req.items[this.selected[0]]).fromNow()
|
||||
},
|
||||
name: function () {
|
||||
// Return the name of the current opened file if there
|
||||
// are no selected files.
|
||||
if (this.selectedCount === 0) {
|
||||
return this.req.name
|
||||
}
|
||||
|
||||
// Otherwise, just return the name of the selected file.
|
||||
// This field won't show when there is more than one
|
||||
// file selected.
|
||||
return this.req.items[this.selected[0]].name
|
||||
},
|
||||
dir: function () {
|
||||
if (this.selectedCount > 1) {
|
||||
// Don't show when multiple selected.
|
||||
return true
|
||||
}
|
||||
|
||||
if (this.selectedCount === 0) {
|
||||
return this.req.isDir
|
||||
}
|
||||
|
||||
return this.req.items[this.selected[0]].isDir
|
||||
},
|
||||
checksum: function (event, hash) {
|
||||
// Gets the checksum of the current selected or
|
||||
// opened file. Doesn't work for directories.
|
||||
event.preventDefault()
|
||||
|
||||
let link
|
||||
|
||||
if (this.selectedCount) {
|
||||
link = this.req.items[this.selected[0]].url
|
||||
} else {
|
||||
link = this.$route.path
|
||||
}
|
||||
|
||||
api.checksum(link, hash)
|
||||
.then((hash) => { event.target.innerHTML = hash })
|
||||
.catch(this.$showError)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,70 +0,0 @@
|
|||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.move') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<file-list @update:selected="val => dest = val"></file-list>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="flat cancel"
|
||||
@click="$store.commit('closeHovers')"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
||||
<button class="flat"
|
||||
@click="move"
|
||||
:disabled="$route.path === dest"
|
||||
:aria-label="$t('buttons.move')"
|
||||
:title="$t('buttons.move')">{{ $t('buttons.move') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import FileList from './FileList'
|
||||
import * as api from '@/utils/api'
|
||||
import buttons from '@/utils/buttons'
|
||||
|
||||
export default {
|
||||
name: 'move',
|
||||
components: { FileList },
|
||||
data: function () {
|
||||
return {
|
||||
current: window.location.pathname,
|
||||
dest: null
|
||||
}
|
||||
},
|
||||
computed: mapState(['req', 'selected']),
|
||||
methods: {
|
||||
move: function (event) {
|
||||
event.preventDefault()
|
||||
buttons.loading('move')
|
||||
let items = []
|
||||
|
||||
// Create a new promise for each file.
|
||||
for (let item of this.selected) {
|
||||
items.push({
|
||||
from: this.req.items[item].url,
|
||||
to: this.dest + encodeURIComponent(this.req.items[item].name)
|
||||
})
|
||||
}
|
||||
|
||||
// Execute the promises.
|
||||
api.move(items)
|
||||
.then(() => {
|
||||
buttons.success('move')
|
||||
this.$router.push({ path: this.dest })
|
||||
})
|
||||
.catch(error => {
|
||||
buttons.done('move')
|
||||
this.$showError(error)
|
||||
})
|
||||
|
||||
event.preventDefault()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,76 +0,0 @@
|
|||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.newFile') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t('prompts.newArchetype') }}</p>
|
||||
<input autofocus type="text" @keyup.enter="submit" v-model.trim="name">
|
||||
<input type="text" @keyup.enter="submit" v-model.trim="archetype">
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="flat cancel"
|
||||
@click="$store.commit('closeHovers')"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
||||
<button class="flat"
|
||||
@click="submit"
|
||||
:aria-label="$t('buttons.create')"
|
||||
:title="$t('buttons.create')">{{ $t('buttons.create') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { removePrefix } from '@/utils/api'
|
||||
|
||||
export default {
|
||||
name: 'new-archetype',
|
||||
data: function () {
|
||||
return {
|
||||
name: '',
|
||||
archetype: 'default'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submit: function (event) {
|
||||
event.preventDefault()
|
||||
this.$store.commit('closeHovers')
|
||||
|
||||
this.new('/' + this.name, this.archetype)
|
||||
.then((url) => {
|
||||
this.$router.push({ path: url })
|
||||
})
|
||||
.catch(this.$showError)
|
||||
},
|
||||
new (url, type) {
|
||||
url = removePrefix(url)
|
||||
|
||||
if (!url.endsWith('.md') && !url.endsWith('.markdown')) {
|
||||
url += '.markdown'
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('POST', `${this.$store.state.baseURL}/api/resource${url}`, true)
|
||||
if (!this.$store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${this.$store.state.jwt}`)
|
||||
request.setRequestHeader('Archetype', encodeURIComponent(type))
|
||||
|
||||
request.onload = () => {
|
||||
if (request.status === 200) {
|
||||
resolve(request.getResponseHeader('Location'))
|
||||
} else {
|
||||
reject(request.responseText)
|
||||
}
|
||||
}
|
||||
|
||||
request.onerror = (error) => reject(error)
|
||||
request.send()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.newDir') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t('prompts.newDirMessage') }}</p>
|
||||
<input autofocus type="text" @keyup.enter="submit" v-model.trim="name">
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="cancel flat"
|
||||
@click="$store.commit('closeHovers')"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
||||
<button class="flat"
|
||||
:aria-label="$t('buttons.create')"
|
||||
:title="$t('buttons.create')"
|
||||
@click="submit">{{ $t('buttons.create') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import url from '@/utils/url'
|
||||
import * as api from '@/utils/api'
|
||||
|
||||
export default {
|
||||
name: 'new-dir',
|
||||
data: function () {
|
||||
return {
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submit: function (event) {
|
||||
event.preventDefault()
|
||||
if (this.new === '') return
|
||||
|
||||
// Build the path of the new directory.
|
||||
let uri = this.$route.path
|
||||
if (this.$store.state.req.kind !== 'listing') {
|
||||
uri = url.removeLastDir(uri) + '/'
|
||||
}
|
||||
|
||||
uri += this.name + '/'
|
||||
uri = uri.replace('//', '/')
|
||||
|
||||
api.post(uri)
|
||||
.then(() => { this.$router.push({ path: uri }) })
|
||||
.catch(this.$showError)
|
||||
|
||||
// Close the prompt
|
||||
this.$store.commit('closeHovers')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.newFile') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t('prompts.newFileMessage') }}</p>
|
||||
<input autofocus type="text" @keyup.enter="submit" v-model.trim="name">
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="cancel flat"
|
||||
@click="$store.commit('closeHovers')"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
||||
<button class="flat"
|
||||
@click="submit"
|
||||
:aria-label="$t('buttons.create')"
|
||||
:title="$t('buttons.create')">{{ $t('buttons.create') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import url from '@/utils/url'
|
||||
import * as api from '@/utils/api'
|
||||
|
||||
export default {
|
||||
name: 'new-file',
|
||||
data: function () {
|
||||
return {
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submit: function (event) {
|
||||
event.preventDefault()
|
||||
if (this.new === '') return
|
||||
|
||||
// Build the path of the new file.
|
||||
let uri = this.$route.path
|
||||
if (this.$store.state.req.kind !== 'listing') {
|
||||
uri = url.removeLastDir(uri) + '/'
|
||||
}
|
||||
|
||||
uri += this.name
|
||||
uri = uri.replace('//', '/')
|
||||
|
||||
// Create the new file.
|
||||
api.post(uri)
|
||||
.then(() => { this.$router.push({ path: uri }) })
|
||||
.catch(this.$showError)
|
||||
|
||||
// Close the prompt.
|
||||
this.$store.commit('closeHovers')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<help v-if="showHelp" ></help>
|
||||
<download v-else-if="showDownload"></download>
|
||||
<new-file v-else-if="showNewFile"></new-file>
|
||||
<new-dir v-else-if="showNewDir"></new-dir>
|
||||
<rename v-else-if="showRename"></rename>
|
||||
<delete v-else-if="showDelete"></delete>
|
||||
<info v-else-if="showInfo"></info>
|
||||
<move v-else-if="showMove"></move>
|
||||
<copy v-else-if="showCopy"></copy>
|
||||
<replace v-else-if="showReplace"></replace>
|
||||
<schedule v-else-if="show === 'schedule'"></schedule>
|
||||
<new-archetype v-else-if="show === 'new-archetype'"></new-archetype>
|
||||
<share v-else-if="show === 'share'"></share>
|
||||
<div v-show="showOverlay" @click="resetPrompts" class="overlay"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Help from './Help'
|
||||
import Info from './Info'
|
||||
import Delete from './Delete'
|
||||
import Rename from './Rename'
|
||||
import Download from './Download'
|
||||
import Move from './Move'
|
||||
import Copy from './Copy'
|
||||
import NewFile from './NewFile'
|
||||
import NewDir from './NewDir'
|
||||
import NewArchetype from './NewArchetype'
|
||||
import Replace from './Replace'
|
||||
import Schedule from './Schedule'
|
||||
import Share from './Share'
|
||||
import { mapState } from 'vuex'
|
||||
import buttons from '@/utils/buttons'
|
||||
import * as api from '@/utils/api'
|
||||
|
||||
export default {
|
||||
name: 'prompts',
|
||||
components: {
|
||||
Info,
|
||||
Delete,
|
||||
NewArchetype,
|
||||
Schedule,
|
||||
Rename,
|
||||
Download,
|
||||
Move,
|
||||
Copy,
|
||||
Share,
|
||||
NewFile,
|
||||
NewDir,
|
||||
Help,
|
||||
Replace
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
pluginData: {
|
||||
api,
|
||||
buttons,
|
||||
'store': this.$store,
|
||||
'router': this.$router
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['show', 'plugins']),
|
||||
showInfo: function () { return this.show === 'info' },
|
||||
showHelp: function () { return this.show === 'help' },
|
||||
showDelete: function () { return this.show === 'delete' },
|
||||
showRename: function () { return this.show === 'rename' },
|
||||
showMove: function () { return this.show === 'move' },
|
||||
showCopy: function () { return this.show === 'copy' },
|
||||
showNewFile: function () { return this.show === 'newFile' },
|
||||
showNewDir: function () { return this.show === 'newDir' },
|
||||
showDownload: function () { return this.show === 'download' },
|
||||
showReplace: function () { return this.show === 'replace' },
|
||||
showOverlay: function () {
|
||||
return (this.show !== null && this.show !== 'search' && this.show !== 'more')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetPrompts () {
|
||||
this.$store.commit('closeHovers')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,84 +0,0 @@
|
|||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.rename') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t('prompts.renameMessage') }} <code>{{ oldName() }}</code>:</p>
|
||||
<input autofocus type="text" @keyup.enter="submit" v-model.trim="name">
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="cancel flat"
|
||||
@click="$store.commit('closeHovers')"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
||||
<button @click="submit"
|
||||
class="flat"
|
||||
type="submit"
|
||||
:aria-label="$t('buttons.rename')"
|
||||
:title="$t('buttons.rename')">{{ $t('buttons.rename') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import url from '@/utils/url'
|
||||
import * as api from '@/utils/api'
|
||||
|
||||
export default {
|
||||
name: 'rename',
|
||||
data: function () {
|
||||
return {
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
computed: mapState(['req', 'selected', 'selectedCount']),
|
||||
methods: {
|
||||
cancel: function (event) {
|
||||
this.$store.commit('closeHovers')
|
||||
},
|
||||
oldName: function () {
|
||||
// Get the current name of the file we are editing.
|
||||
if (this.req.kind !== 'listing') {
|
||||
return this.req.name
|
||||
}
|
||||
|
||||
if (this.selectedCount === 0 || this.selectedCount > 1) {
|
||||
// This shouldn't happen.
|
||||
return
|
||||
}
|
||||
|
||||
return this.req.items[this.selected[0]].name
|
||||
},
|
||||
submit: function (event) {
|
||||
let oldLink = ''
|
||||
let newLink = ''
|
||||
|
||||
if (this.req.kind !== 'listing') {
|
||||
oldLink = this.req.url
|
||||
} else {
|
||||
oldLink = this.req.items[this.selected[0]].url
|
||||
}
|
||||
|
||||
this.name = encodeURIComponent(this.name)
|
||||
newLink = url.removeLastDir(oldLink) + '/' + this.name
|
||||
|
||||
api.move([{ from: oldLink, to: newLink }])
|
||||
.then(() => {
|
||||
if (this.req.kind !== 'listing') {
|
||||
this.$router.push({ path: newLink })
|
||||
return
|
||||
}
|
||||
this.$store.commit('setReload', true)
|
||||
}).catch(error => {
|
||||
this.$showError(error)
|
||||
})
|
||||
|
||||
this.$store.commit('closeHovers')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,31 +0,0 @@
|
|||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.replace') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t('prompts.replaceMessage') }}</p>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="flat cancel"
|
||||
@click="$store.commit('closeHovers')"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
||||
<button class="flat"
|
||||
@click="showConfirm"
|
||||
:aria-label="$t('buttons.replace')"
|
||||
:title="$t('buttons.replace')">{{ $t('buttons.replace') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'replace',
|
||||
computed: mapState(['showConfirm'])
|
||||
}
|
||||
</script>
|
|
@ -1,47 +0,0 @@
|
|||
<template>
|
||||
<div class="card floating">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('prompts.schedule') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ $t('prompts.scheduleMessage') }}</p>
|
||||
<input autofocus type="datetime-local" v-model="date">
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="cancel flat"
|
||||
@click="close"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
||||
<button class="falt"
|
||||
@click="submit"
|
||||
:aria-label="$t('buttons.schedule')"
|
||||
:title="$t('buttons.schedule')">{{ $t('buttons.schedule') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'schedule',
|
||||
data: function () {
|
||||
return {
|
||||
date: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$store.commit('closeHovers')
|
||||
},
|
||||
submit: function (event) {
|
||||
event.preventDefault()
|
||||
if (this.date === '') return
|
||||
this.close()
|
||||
this.$store.commit('setSchedule', this.date)
|
||||
document.getElementById('save-button').click()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
<template>
|
||||
<div class="card floating" id="share">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t('buttons.share') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<ul>
|
||||
<li v-if="!hasPermanent">
|
||||
<a @click="getPermalink" :aria-label="$t('buttons.permalink')">{{ $t('buttons.permalink') }}</a>
|
||||
</li>
|
||||
|
||||
<li v-for="link in links" :key="link.hash">
|
||||
<a :href="buildLink(link.hash)" target="_blank">
|
||||
<template v-if="link.expires">{{ humanTime(link.expireDate) }}</template>
|
||||
<template v-else>{{ $t('permanent') }}</template>
|
||||
</a>
|
||||
|
||||
<button class="action"
|
||||
@click="deleteLink($event, link)"
|
||||
:aria-label="$t('buttons.delete')"
|
||||
:title="$t('buttons.delete')"><i class="material-icons">delete</i></button>
|
||||
|
||||
<button class="action copy-clipboard"
|
||||
:data-clipboard-text="buildLink(link.hash)"
|
||||
:aria-label="$t('buttons.copyToClipboard')"
|
||||
:title="$t('buttons.copyToClipboard')"><i class="material-icons">content_paste</i></button>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<input autofocus
|
||||
type="number"
|
||||
max="2147483647"
|
||||
min="0"
|
||||
@keyup.enter="submit"
|
||||
v-model.trim="time">
|
||||
<select v-model="unit" :aria-label="$t('time.unit')">
|
||||
<option value="seconds">{{ $t('time.seconds') }}</option>
|
||||
<option value="minutes">{{ $t('time.minutes') }}</option>
|
||||
<option value="hours">{{ $t('time.hours') }}</option>
|
||||
<option value="days">{{ $t('time.days') }}</option>
|
||||
</select>
|
||||
<button class="action"
|
||||
@click="submit"
|
||||
:aria-label="$t('buttons.create')"
|
||||
:title="$t('buttons.create')"><i class="material-icons">add</i></button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button class="flat"
|
||||
@click="$store.commit('closeHovers')"
|
||||
:aria-label="$t('buttons.close')"
|
||||
:title="$t('buttons.close')">{{ $t('buttons.close') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import { getShare, deleteShare, share } from '@/utils/api'
|
||||
import moment from 'moment'
|
||||
import Clipboard from 'clipboard'
|
||||
|
||||
export default {
|
||||
name: 'share',
|
||||
data: function () {
|
||||
return {
|
||||
time: '',
|
||||
unit: 'hours',
|
||||
hasPermanent: false,
|
||||
links: [],
|
||||
clip: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState([ 'baseURL', 'req', 'selected', 'selectedCount' ]),
|
||||
url () {
|
||||
// Get the current name of the file we are editing.
|
||||
if (this.req.kind !== 'listing') {
|
||||
return this.$route.path
|
||||
}
|
||||
|
||||
if (this.selectedCount === 0 || this.selectedCount > 1) {
|
||||
// This shouldn't happen.
|
||||
return
|
||||
}
|
||||
|
||||
return this.req.items[this.selected[0]].url
|
||||
}
|
||||
},
|
||||
beforeMount () {
|
||||
getShare(this.url)
|
||||
.then(links => {
|
||||
this.links = links
|
||||
this.sort()
|
||||
|
||||
for (let link of this.links) {
|
||||
if (!link.expires) {
|
||||
this.hasPermanent = true
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if (error === 404) return
|
||||
this.$showError(error)
|
||||
})
|
||||
},
|
||||
mounted () {
|
||||
this.clip = new Clipboard('.copy-clipboard')
|
||||
this.clip.on('success', (e) => {
|
||||
this.$showSuccess(this.$t('success.linkCopied'))
|
||||
})
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.clip.destroy()
|
||||
},
|
||||
methods: {
|
||||
submit: function (event) {
|
||||
if (!this.time) return
|
||||
|
||||
share(this.url, this.time, this.unit)
|
||||
.then(result => { this.links.push(result); this.sort() })
|
||||
.catch(this.$showError)
|
||||
},
|
||||
getPermalink (event) {
|
||||
share(this.url)
|
||||
.then(result => {
|
||||
this.links.push(result)
|
||||
this.sort()
|
||||
this.hasPermanent = true
|
||||
})
|
||||
.catch(this.$showError)
|
||||
},
|
||||
deleteLink (event, link) {
|
||||
event.preventDefault()
|
||||
deleteShare(link.hash)
|
||||
.then(() => {
|
||||
if (!link.expires) this.hasPermanent = false
|
||||
this.links = this.links.filter(item => item.hash !== link.hash)
|
||||
})
|
||||
.catch(this.$showError)
|
||||
},
|
||||
humanTime (time) {
|
||||
return moment(time).fromNow()
|
||||
},
|
||||
buildLink (hash) {
|
||||
return `${window.location.origin}${this.baseURL}/share/${hash}`
|
||||
},
|
||||
sort () {
|
||||
this.links = this.links.sort((a, b) => {
|
||||
if (!a.expires) return -1
|
||||
if (!b.expires) return 1
|
||||
return new Date(a.expireDate) - new Date(b.expireDate)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,214 +0,0 @@
|
|||
body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
padding-top: 4em;
|
||||
background-color: #fafafa;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*,
|
||||
*:hover,
|
||||
*:active,
|
||||
*:focus {
|
||||
outline: 0
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
audio,
|
||||
video {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 1em;
|
||||
border: 1px solid #e6e6e6;
|
||||
border-radius: 0.5em;
|
||||
background-color: #f5f5f5;
|
||||
white-space: pre-wrap;
|
||||
white-space: -moz-pre-wrap;
|
||||
white-space: -pre-wrap;
|
||||
white-space: -o-pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
input[type="submit"],
|
||||
button {
|
||||
border: 0;
|
||||
padding: .5em 1em;
|
||||
margin-left: .5em;
|
||||
border-radius: .1em;
|
||||
cursor: pointer;
|
||||
background: #2196f3;
|
||||
color: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.05);
|
||||
transition: .1s ease all;
|
||||
}
|
||||
|
||||
input[type="submit"]:hover,
|
||||
button:hover {
|
||||
background-color: #1E88E5;
|
||||
}
|
||||
|
||||
input[type="submit"].block,
|
||||
button.block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 0 0 1em;
|
||||
}
|
||||
|
||||
button.delete {
|
||||
background: #F44336;
|
||||
}
|
||||
|
||||
button.delete:hover {
|
||||
background: #D32F2F;
|
||||
}
|
||||
|
||||
button.cancel {
|
||||
background-color: #ECEFF1;
|
||||
color: #37474F;
|
||||
}
|
||||
|
||||
button.cancel:hover {
|
||||
background-color: #e9eaeb;
|
||||
}
|
||||
|
||||
button.flat,
|
||||
input[type="submit"].flat {
|
||||
color: #1E88E5;
|
||||
background: transparent;
|
||||
box-shadow: 0 0 0;
|
||||
border: 0;
|
||||
margin-left: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
button.flat:hover,
|
||||
input[type="submit"].flat:hover {
|
||||
background: rgba(0,0,0,0.05)
|
||||
}
|
||||
|
||||
button.flat.delete {
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
button.flat.cancel {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
button.flat[disabled] {
|
||||
color: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.mobile-only {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 95%;
|
||||
max-width: 960px;
|
||||
margin: 1em auto 0;
|
||||
}
|
||||
|
||||
i.spin {
|
||||
animation: 1s spin linear infinite;
|
||||
}
|
||||
|
||||
#app {
|
||||
transition: .2s ease padding;
|
||||
}
|
||||
|
||||
#app.multiple {
|
||||
padding-bottom: 4em;
|
||||
}
|
||||
|
||||
nav {
|
||||
width: 16em;
|
||||
position: fixed;
|
||||
top: 4em;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
nav .action {
|
||||
width: 100%;
|
||||
display: block;
|
||||
border-radius: 0;
|
||||
font-size: 1.1em;
|
||||
padding: .5em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
nav>div {
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
nav .action>* {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
main {
|
||||
min-height: 1em;
|
||||
margin: 0 1em 1em auto;
|
||||
width: calc(100% - 19em);
|
||||
}
|
||||
|
||||
#breadcrumbs {
|
||||
height: 3em;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
#breadcrumbs span,
|
||||
#breadcrumbs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #6f6f6f;
|
||||
}
|
||||
|
||||
#breadcrumbs a {
|
||||
color: inherit;
|
||||
transition: .1s ease-in;
|
||||
border-radius: .125em;
|
||||
}
|
||||
|
||||
#breadcrumbs a:hover {
|
||||
background-color: rgba(0,0,0, 0.05);
|
||||
}
|
||||
|
||||
#breadcrumbs span a {
|
||||
padding: .2em;
|
||||
}
|
||||
|
||||
#progress {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
z-index: 9999999999;
|
||||
}
|
||||
|
||||
#progress div {
|
||||
height: 100%;
|
||||
background-color: #40c4ff;
|
||||
width: 0;
|
||||
transition: .2s ease width;
|
||||
}
|
|
@ -1,420 +0,0 @@
|
|||
.dashboard {
|
||||
max-width: 600px;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit
|
||||
}
|
||||
|
||||
select,
|
||||
textarea,
|
||||
input[type="text"],
|
||||
input[type="password"] {
|
||||
padding: 0.5em 0;
|
||||
line-height: 1;
|
||||
display: block;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
transition: .2s ease border;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
textarea {
|
||||
line-height: 1.15;
|
||||
padding: .5em;
|
||||
border: 1px solid #ddd;
|
||||
font-family: monospace;
|
||||
min-height: 10em;
|
||||
resize: none;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.dashboard #locale,
|
||||
.dashboard #username,
|
||||
.dashboard #password,
|
||||
.dashboard #scope {
|
||||
max-width: 18em;
|
||||
}
|
||||
|
||||
.dashboard #locale {
|
||||
margin-top: .5em;
|
||||
}
|
||||
|
||||
textarea:focus,
|
||||
textarea:hover,
|
||||
input[type="text"]:focus,
|
||||
input[type="password"]:focus,
|
||||
input[type="text"]:hover,
|
||||
input[type="password"]:hover {
|
||||
border-color: #2979ff;
|
||||
}
|
||||
|
||||
input.red {
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
input.green {
|
||||
border-color: green;
|
||||
}
|
||||
|
||||
.dashboard p label {
|
||||
margin-bottom: .2em;
|
||||
display: block;
|
||||
font-size: .8em;
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.57);
|
||||
}
|
||||
|
||||
li code,
|
||||
p code {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
padding: .1em;
|
||||
border-radius: .2em;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: .8em;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.dashboard #nav {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
color: rgb(84, 110, 122);
|
||||
font-weight: 500;
|
||||
margin: 0 0 1em;
|
||||
font-size: .8em;
|
||||
text-align: center;
|
||||
justify-content: space-between;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dashboard #nav li {
|
||||
width: 100%;
|
||||
padding: 0 0 1em;
|
||||
border-bottom: 2px solid rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.dashboard #nav li.active {
|
||||
border-color: #2196f3
|
||||
}
|
||||
|
||||
.dashboard #nav i {
|
||||
font-size: 1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table tr {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
table tr:last-child {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
table th {
|
||||
font-weight: 500;
|
||||
color: #757575;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table th,
|
||||
table td {
|
||||
padding: .5em 0;
|
||||
}
|
||||
|
||||
table td.small {
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
table tr>*:first-child {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
table tr>*:last-child {
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.card {
|
||||
position: relative;
|
||||
margin: .5rem 0 1rem 0;
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.card.floating {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 99999;
|
||||
max-width: 25em;
|
||||
width: 90%;
|
||||
max-height: 95%;
|
||||
z-index: 99999;
|
||||
animation: .1s show forwards;
|
||||
}
|
||||
|
||||
.card>*>*:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.card>*>*:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.card .card-title {
|
||||
padding: 1.5em 1em 1em;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.card .card-title>*:first-child {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.card>div {
|
||||
padding: 1em 1em;
|
||||
}
|
||||
|
||||
.card>div:first-child {
|
||||
padding-top: 1.5em;
|
||||
}
|
||||
|
||||
.card>div:last-child {
|
||||
padding-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.card .card-title * {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.card .card-action {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.card .card-content.full {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
color: rgba(0, 0, 0, 0.53);
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
margin: 2em 0 1em;
|
||||
}
|
||||
|
||||
.card-content table {
|
||||
margin: 0 -1em;
|
||||
width: calc(100% + 2em);
|
||||
}
|
||||
|
||||
.card code {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.card#download {
|
||||
max-width: 15em;
|
||||
}
|
||||
|
||||
.card#share ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.card#share ul li {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card#share ul li a {
|
||||
color: #2196F3;
|
||||
cursor: pointer;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.card#share ul li .action i {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.card#share ul li input,
|
||||
.card#share ul li select {
|
||||
padding: .2em;
|
||||
margin-right: .5em;
|
||||
border: 1px solid #dadada;
|
||||
}
|
||||
|
||||
.card#share .action.copy-clipboard::after {
|
||||
content: 'Copied!';
|
||||
position: absolute;
|
||||
left: -25%;
|
||||
width: 150%;
|
||||
font-size: .6em;
|
||||
text-align: center;
|
||||
background: #44a6f5;
|
||||
color: #fff;
|
||||
padding: .5em .2em;
|
||||
border-radius: .4em;
|
||||
top: -2em;
|
||||
transition: .1s ease opacity;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.card#share .action.copy-clipboard.active::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
animation: .1s show forwards;
|
||||
}
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
* PROMPT - MOVE *
|
||||
* * * * * * * * * * * * * * * */
|
||||
|
||||
.file-list {
|
||||
max-height: 50vh;
|
||||
overflow: auto;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.file-list li {
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
border-radius: .2em;
|
||||
padding: .3em;
|
||||
}
|
||||
|
||||
.file-list li[aria-selected=true] {
|
||||
background: #2196f3 !important;
|
||||
color: #fff !important;
|
||||
transition: .1s ease all;
|
||||
}
|
||||
|
||||
.file-list li:hover {
|
||||
background-color: #e9eaeb;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.file-list li:before {
|
||||
content: "folder";
|
||||
color: #6f6f6f;
|
||||
vertical-align: middle;
|
||||
line-height: 1.4;
|
||||
font-family: 'Material Icons';
|
||||
font-size: 1.75em;
|
||||
margin-right: .25em;
|
||||
}
|
||||
|
||||
.file-list li[aria-selected=true]:before {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.help {
|
||||
max-width: 24em;
|
||||
}
|
||||
|
||||
.help ul {
|
||||
padding: 0;
|
||||
margin: 1em 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
@keyframes show {
|
||||
0% {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
}
|
||||
1% {
|
||||
display: block;
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.collapsible {
|
||||
border-top: 1px solid rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.collapsible:last-of-type {
|
||||
border-bottom: 1px solid rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.collapsible > input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.collapsible > label {
|
||||
padding: 1em 0;
|
||||
cursor: pointer;
|
||||
border-right: 0;
|
||||
border-left: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.collapsible > label * {
|
||||
margin: 0;
|
||||
color: rgba(0,0,0,0.57);
|
||||
}
|
||||
|
||||
.collapsible > label i {
|
||||
transition: .2s ease transform;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.collapsible .collapse {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: .2s ease all;
|
||||
}
|
||||
|
||||
.collapsible > input:checked ~ .collapse {
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
max-height: 20em;
|
||||
}
|
||||
|
||||
.collapsible > input:checked ~ label i {
|
||||
transform: rotate(180deg)
|
||||
}
|
||||
|
||||
.card .collapsible {
|
||||
width: calc(100% + 2em);
|
||||
margin: 0 -1em;
|
||||
}
|
||||
|
||||
.card .collapsible > label {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.card .collapsible .collapse {
|
||||
padding: 0 1em;
|
||||
}
|
|
@ -1,184 +0,0 @@
|
|||
@import "~codemirror/lib/codemirror.css";
|
||||
@import "~codemirror/theme/ttcn.css";
|
||||
#editor {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#editor .CodeMirror {
|
||||
box-shadow: rgba(0, 0, 0, 0.06) 0px 1px 3px, rgba(0, 0, 0, 0.12) 0px 1px 2px;
|
||||
margin: 2em 0;
|
||||
border-radius: .5em;
|
||||
}
|
||||
|
||||
#editor h2 {
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.markdown .CodeMirror {
|
||||
padding: .75em;
|
||||
}
|
||||
|
||||
.cm-s-markdown .CodeMirror-gutter {
|
||||
border-right: 1px solid #eff3f5;
|
||||
padding-right: 5px;
|
||||
margin-right: 15px;
|
||||
min-width: 2.5em;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
.cm-s-markdown .CodeMirror-cursor {
|
||||
border-right: 2px solid #667880;
|
||||
}
|
||||
|
||||
.cm-s-markdown .CodeMirror-lines {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.cm-s-markdown {
|
||||
color: #3D494E;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-header {
|
||||
color: #3D494E;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-variable-2 {
|
||||
color: #3D494E;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-meta {
|
||||
color: #516066;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-hr {
|
||||
color: #516066;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-comment {
|
||||
color: #868f93;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-qualifier {
|
||||
color: #868f93;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-number {
|
||||
color: #197987;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-variable {
|
||||
color: #197987;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-builtin {
|
||||
color: #197987;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-link {
|
||||
color: #197987;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-tag {
|
||||
color: #197987;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-string {
|
||||
color: #48abb9;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-string-2 {
|
||||
color: #48abb9;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-quote {
|
||||
color: #48abb9;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-atom {
|
||||
color: #48abb9;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-property {
|
||||
color: #82a367;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-operator {
|
||||
color: #82a367;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-variable-3 {
|
||||
color: #82a367;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-attribute {
|
||||
color: #90bb74;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-def {
|
||||
color: #90bb74;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-keyword {
|
||||
color: #ec6c45;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-bracket {
|
||||
color: #ec6c45;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-error {
|
||||
color: #e45346;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.cm-s-markdown span.cm-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cm-s-markdown .cm-header-1 {
|
||||
font-size: 200%;
|
||||
line-height: 200%;
|
||||
}
|
||||
|
||||
.cm-s-markdown .cm-header-2 {
|
||||
font-size: 160%;
|
||||
line-height: 160%;
|
||||
}
|
||||
|
||||
.cm-s-markdown .cm-header-3 {
|
||||
font-size: 125%;
|
||||
line-height: 125%;
|
||||
}
|
||||
|
||||
.cm-s-markdown .cm-header-4 {
|
||||
font-size: 110%;
|
||||
line-height: 110%;
|
||||
}
|
||||
|
||||
.cm-s-markdown .cm-comment {
|
||||
background: rgba(0, 0, 0, .05);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.cm-s-markdown .cm-link {
|
||||
color: #7f8c8d;
|
||||
}
|
||||
|
||||
.cm-s-markdown .cm-url {
|
||||
color: #aab2b3;
|
||||
}
|
||||
|
||||
.cm-s-markdown .cm-strikethrough {
|
||||
text-decoration: line-through;
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-cyrillic-ext.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-cyrillic.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-greek-ext.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-greek.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-vietnamese.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-latin-ext.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-latin.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-cyrillic-ext.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-cyrillic.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-greek-ext.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-greek.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-vietnamese.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-latin-ext.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-latin.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Material Icons'), local('MaterialIcons-Regular'), url(../assets/fonts/material/icons.woff2) format('woff2');
|
||||
}
|
||||
|
||||
.prompt .file-list ul li:before,
|
||||
.material-icons {
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
direction: ltr;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-feature-settings: 'liga';
|
||||
}
|
|
@ -1,260 +0,0 @@
|
|||
header {
|
||||
z-index: 1000;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.075);
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
header .overlay {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
header a,
|
||||
header a:hover {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
header>div:first-child>.action,
|
||||
header img {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 2.5em;
|
||||
}
|
||||
|
||||
header>div:first-child>.action {
|
||||
display: none;
|
||||
}
|
||||
|
||||
header>div {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 0.5em 0.5em 0.5em 1em;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
header .action span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
header>div div {
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
header>div:last-child div {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
header>div:first-child {
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
header>div:last-child {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
header .search-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#more {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#search {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
max-width: 25em;
|
||||
}
|
||||
|
||||
#search.active {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#search #input {
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
padding: 0.75em;
|
||||
border-radius: 0.3em;
|
||||
transition: .1s ease all;
|
||||
align-items: center;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#search.active #input {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.075);
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
|
||||
background-color: #fff;
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
#search.active>div {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
#search.active i,
|
||||
#search.active input {
|
||||
color: #212121;
|
||||
}
|
||||
|
||||
#search #input>.action,
|
||||
#search #input>i {
|
||||
margin-right: 0.3em;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#search input {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
line-height: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#search #result {
|
||||
visibility: visible;
|
||||
max-height: none;
|
||||
background-color: #f8f8f8;
|
||||
text-align: left;
|
||||
padding: 0;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
height: 0;
|
||||
transition: .1s ease height, .1s ease padding;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#search #result>div>*:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#search.active #result {
|
||||
padding: .5em;
|
||||
height: calc(100% - 4em);
|
||||
}
|
||||
|
||||
#search ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#search li {
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
#search #result>div {
|
||||
max-width: 45em;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#search #result #renew {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
display: none;
|
||||
margin: 0;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
#search.ongoing #result #renew {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#search.active #result i {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
#search.active #result>p>i {
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
display: table;
|
||||
}
|
||||
|
||||
#search.active #result ul li a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: .3em 0;
|
||||
}
|
||||
|
||||
#search.active #result ul li a i {
|
||||
margin-right: .3em;
|
||||
}
|
||||
|
||||
#search::-webkit-input-placeholder {
|
||||
color: rgba(255, 255, 255, .5);
|
||||
}
|
||||
|
||||
#search:-moz-placeholder {
|
||||
opacity: 1;
|
||||
color: rgba(255, 255, 255, .5);
|
||||
}
|
||||
|
||||
#search::-moz-placeholder {
|
||||
opacity: 1;
|
||||
color: rgba(255, 255, 255, .5);
|
||||
}
|
||||
|
||||
#search:-ms-input-placeholder {
|
||||
color: rgba(255, 255, 255, .5);
|
||||
}
|
||||
|
||||
#search .boxes {
|
||||
border: 1px solid rgba(0, 0, 0, 0.075);
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
|
||||
background: #fff;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
#search .boxes h3 {
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
font-size: 1em;
|
||||
color: #212121;
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
#search .boxes>div {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
margin-right: -1em;
|
||||
margin-bottom: -1em;
|
||||
}
|
||||
|
||||
#search .boxes>div>div {
|
||||
background: #2196F3;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
width: 10em;
|
||||
padding: 1em;
|
||||
cursor: pointer;
|
||||
margin-bottom: 1em;
|
||||
margin-right: 1em;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#search .boxes p {
|
||||
margin: 1em 0 0;
|
||||
}
|
||||
|
||||
#search .boxes i {
|
||||
color: #fff !important;
|
||||
font-size: 3.5em;
|
||||
}
|
|
@ -1,237 +0,0 @@
|
|||
#listing h2 {
|
||||
margin: 0 0 0 0.5em;
|
||||
font-size: .9em;
|
||||
color: rgba(0, 0, 0, 0.38);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
#listing .item div:last-of-type * {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#listing>div {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#listing .item {
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
color: #6f6f6f;
|
||||
transition: .1s ease background, .1s ease opacity;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#listing .item div:last-of-type {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#listing .item p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#listing .item .size,
|
||||
#listing .item .modified {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
#listing .item .name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#listing .item i {
|
||||
font-size: 4em;
|
||||
margin-right: 0.1em;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.message {
|
||||
text-align: center;
|
||||
font-size: 2em;
|
||||
margin: 1em auto;
|
||||
display: block !important;
|
||||
width: 95%;
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.message i {
|
||||
font-size: 2.5em;
|
||||
margin-bottom: .2em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#listing.mosaic {
|
||||
padding-top: 1em;
|
||||
margin: 0 -0.5em;
|
||||
}
|
||||
|
||||
#listing.mosaic .item {
|
||||
width: calc(33% - 1em);
|
||||
margin: .5em;
|
||||
padding: 0.5em;
|
||||
border-radius: 0.2em;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .06), 0 1px 2px rgba(0, 0, 0, .12);
|
||||
}
|
||||
|
||||
#listing.mosaic .item:hover {
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24) !important;
|
||||
}
|
||||
|
||||
#listing.mosaic .header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#listing.mosaic .item div:first-of-type {
|
||||
width: 5em;
|
||||
}
|
||||
|
||||
#listing.mosaic .item div:last-of-type {
|
||||
width: calc(100% - 5vw);
|
||||
}
|
||||
|
||||
#listing.list {
|
||||
flex-direction: column;
|
||||
padding-top: 3.25em;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#listing.list .item {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
padding: 1em;
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
#listing.list h2 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#listing .item[aria-selected=true] {
|
||||
background: #2196f3 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
#listing.list .item div:first-of-type {
|
||||
width: 3em;
|
||||
}
|
||||
|
||||
#listing.list .item div:first-of-type i {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
#listing.list .item div:last-of-type {
|
||||
width: calc(100% - 3em);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#listing.list .item .name {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#listing.list .item .size {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
#listing .item.header {
|
||||
display: none !important;
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
#listing.list .header i {
|
||||
font-size: 1.5em;
|
||||
vertical-align: middle;
|
||||
margin-left: .2em;
|
||||
}
|
||||
|
||||
#listing.list .item.header {
|
||||
display: flex !important;
|
||||
background: #fafafa;
|
||||
position: fixed;
|
||||
width: calc(100% - 19em);
|
||||
top: 7em;
|
||||
right: 1em;
|
||||
z-index: 999;
|
||||
padding: .85em;
|
||||
border: 0;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
#listing.list .item.header>div:first-child {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
#listing.list .item.header .name {
|
||||
margin-right: 3em;
|
||||
}
|
||||
|
||||
#listing.list .header a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#listing.list .item.header>div:first-child {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
#listing.list .name {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#listing.list .item.header .name {
|
||||
margin-right: 3em;
|
||||
}
|
||||
|
||||
#listing.list .header span {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#listing.list .header i {
|
||||
opacity: 0;
|
||||
transition: .1s ease all;
|
||||
}
|
||||
|
||||
#listing.list .header p:hover i,
|
||||
#listing.list .header .active i {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#listing.list .item.header .active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#listing #multiple-selection {
|
||||
position: fixed;
|
||||
bottom: -4em;
|
||||
left: 0;
|
||||
z-index: 99999;
|
||||
width: 100%;
|
||||
background-color: #2196f3;
|
||||
height: 4em;
|
||||
display: flex !important;
|
||||
padding: 0.5em 0.5em 0.5em 1em;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
transition: .2s ease bottom;
|
||||
}
|
||||
|
||||
#listing #multiple-selection.active {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
#listing #multiple-selection p,
|
||||
#listing #multiple-selection i {
|
||||
color: #fff;
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
#login {
|
||||
background: #fff;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#login img {
|
||||
width: 4em;
|
||||
height: 4em;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#login h1 {
|
||||
text-align: center;
|
||||
font-size: 2.5em;
|
||||
margin: .4em 0 .67em;
|
||||
}
|
||||
|
||||
#login form {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
max-width: 16em;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
#login.recaptcha form {
|
||||
min-width: 304px;
|
||||
}
|
||||
|
||||
#login #recaptcha {
|
||||
margin: .5em 0 0;
|
||||
}
|
||||
|
||||
#login input {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
margin: .5em 0 0;
|
||||
}
|
||||
|
||||
#login .wrong {
|
||||
background: #F44336;
|
||||
color: #fff;
|
||||
padding: .5em;
|
||||
text-align: center;
|
||||
animation: .2s opac forwards;
|
||||
}
|
||||
|
||||
@keyframes opac {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
#login input[type="text"],
|
||||
#login input[type="password"] {
|
||||
padding: .5em 1em;
|
||||
border: 1px solid #e9e9e9;
|
||||
transition: .2s ease border;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#login input[type="text"]:focus,
|
||||
#login input[type="password"]:focus,
|
||||
#login input[type="text"]:hover,
|
||||
#login input[type="password"]:hover {
|
||||
border-color: #9f9f9f;
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
@media (max-width: 1024px) {
|
||||
nav {
|
||||
width: 10em
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
#listing.list .item.header,
|
||||
main {
|
||||
width: calc(100% - 13em)
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 736px) {
|
||||
#more {
|
||||
display: inherit
|
||||
}
|
||||
header .overlay {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
#dropdown {
|
||||
position: fixed;
|
||||
top: 1em;
|
||||
right: 1em;
|
||||
display: block;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
|
||||
transform: scale(0);
|
||||
transition: .1s ease-in-out transform;
|
||||
transform-origin: top right;
|
||||
z-index: 99999;
|
||||
}
|
||||
#dropdown > div {
|
||||
display: block;
|
||||
}
|
||||
#dropdown.active {
|
||||
transform: scale(1);
|
||||
}
|
||||
#dropdown .action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 0;
|
||||
width: 100%;
|
||||
}
|
||||
#dropdown .action span:not(.counter) {
|
||||
display: inline-block;
|
||||
padding: .4em;
|
||||
}
|
||||
#dropdown .counter {
|
||||
left: 2.25em;
|
||||
}
|
||||
#file-selection {
|
||||
position: fixed;
|
||||
bottom: 1em;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
box-shadow: rgba(0, 0, 0, 0.06) 0px 1px 3px, rgba(0, 0, 0, 0.12) 0px 1px 2px;
|
||||
width: 95%;
|
||||
max-width: 20em;
|
||||
}
|
||||
#file-selection .action {
|
||||
border-radius: 50%;
|
||||
width: auto;
|
||||
}
|
||||
#file-selection > span {
|
||||
display: inline-block;
|
||||
margin-left: 1em;
|
||||
color: #6f6f6f;
|
||||
margin-right: auto;
|
||||
}
|
||||
nav {
|
||||
top: 0;
|
||||
z-index: 99999;
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
width: 16em;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
|
||||
transition: .1s ease left;
|
||||
left: -17em;
|
||||
}
|
||||
nav.active {
|
||||
left: 0;
|
||||
}
|
||||
header .search-button,
|
||||
header>div:first-child>.action {
|
||||
display: inherit;
|
||||
}
|
||||
header img {
|
||||
display: none;
|
||||
}
|
||||
#listing {
|
||||
margin-bottom: 5em;
|
||||
}
|
||||
#listing.list .item.header,
|
||||
main {
|
||||
width: calc(100% - 2em);
|
||||
}
|
||||
main {
|
||||
margin: 0 1em;
|
||||
width: calc(100% - 2em);
|
||||
}
|
||||
#search {
|
||||
display: none;
|
||||
}
|
||||
#search.active {
|
||||
display: block;
|
||||
}
|
||||
}
|
|
@ -1,228 +0,0 @@
|
|||
@import "~normalize.css/normalize.css";
|
||||
@import "~noty/lib/noty.css";
|
||||
@import "./fonts.css";
|
||||
@import "./base.css";
|
||||
@import "./header.css";
|
||||
@import "./listing.css";
|
||||
@import "./editor.css";
|
||||
@import "./dashboard.css";
|
||||
@import "./login.css";
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
* ACTION *
|
||||
* * * * * * * * * * * * * * * */
|
||||
|
||||
.action {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
transition: 0.2s ease all;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
color: #546E7A;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
vertical-align: middle;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.action.disabled {
|
||||
opacity: 0.2;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.action i {
|
||||
padding: 0.4em;
|
||||
transition: .1s ease-in-out all;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.action:hover {
|
||||
background-color: rgba(0, 0, 0, .1);
|
||||
}
|
||||
|
||||
.action ul {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
color: #7d7d7d;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.action ul li {
|
||||
line-height: 1;
|
||||
padding: .7em;
|
||||
transition: .1s ease background-color;
|
||||
}
|
||||
|
||||
.action ul li:hover {
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
#click-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#click-overlay.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.action .counter {
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background: #2196f3;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
font-size: .75em;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
text-align: center;
|
||||
line-height: 1.25em;
|
||||
border: 2px solid white;
|
||||
}
|
||||
|
||||
|
||||
/* PREVIEWER */
|
||||
|
||||
#previewer {
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 9999;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#previewer .bar {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
display: flex;
|
||||
padding: 0.5em;
|
||||
height: 3.7em;
|
||||
}
|
||||
|
||||
#previewer .action:first-of-type {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#previewer .action i {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#previewer .action:hover {
|
||||
background-color: rgba(255, 255, 255, 0.3)
|
||||
}
|
||||
|
||||
#previewer .action span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#previewer .preview {
|
||||
margin: 2em auto 4em;
|
||||
max-width: 80%;
|
||||
text-align: center;
|
||||
height: calc(100vh - 9.7em);
|
||||
}
|
||||
|
||||
#previewer .preview pre {
|
||||
text-align: left;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#previewer .preview pre,
|
||||
#previewer .preview video,
|
||||
#previewer .preview img {
|
||||
max-height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#previewer .pdf {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#previewer h2.message {
|
||||
color: rgba(255, 255, 255, 0.5)
|
||||
}
|
||||
|
||||
#previewer>button {
|
||||
margin: 0;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
#previewer>button:first-of-type {
|
||||
left: 0.5em;
|
||||
}
|
||||
|
||||
#previewer>button:last-of-type {
|
||||
right: 0.5em;
|
||||
}
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
* PROMPT *
|
||||
* * * * * * * * * * * * * * * */
|
||||
|
||||
.noty_buttons {
|
||||
text-align: right;
|
||||
padding: 0 10px 10px !important;
|
||||
}
|
||||
|
||||
.noty_buttons button {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border: 1px solid rgba(0,0,0,0.1);
|
||||
box-shadow: 0 0 0 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
* FOOTER *
|
||||
* * * * * * * * * * * * * * * */
|
||||
|
||||
.credits {
|
||||
font-size: 0.6em;
|
||||
margin: 3em 2.5em;
|
||||
color: #a5a5a5;
|
||||
}
|
||||
|
||||
.credits span {
|
||||
display: block;
|
||||
margin: .3em 0;
|
||||
}
|
||||
|
||||
.credits a,
|
||||
.credits a:hover {
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
* ANIMATIONS *
|
||||
* * * * * * * * * * * * * * * */
|
||||
|
||||
@keyframes spin {
|
||||
100% {
|
||||
-webkit-transform: rotate(-360deg);
|
||||
transform: rotate(-360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@import './mobile.css';
|
|
@ -1,201 +0,0 @@
|
|||
permanent: Permanent
|
||||
buttons:
|
||||
cancel: Cancel
|
||||
close: Close
|
||||
copy: Copy
|
||||
copyFile: Copy file
|
||||
copyToClipboard: Copy to clipboard
|
||||
create: Create
|
||||
delete: Delete
|
||||
download: Download
|
||||
info: Info
|
||||
more: More
|
||||
move: Move
|
||||
moveFile: Move file
|
||||
new: New
|
||||
next: Next
|
||||
ok: OK
|
||||
replace: Replace
|
||||
previous: Previous
|
||||
rename: Rename
|
||||
reportIssue: Report Issue
|
||||
save: Save
|
||||
search: Search
|
||||
select: Select
|
||||
share: Share
|
||||
publish: Publish
|
||||
selectMultiple: Select multiple
|
||||
schedule: Schedule
|
||||
switchView: Switch view
|
||||
toggleSidebar: Toggle sidebar
|
||||
update: Update
|
||||
upload: Upload
|
||||
permalink: Get Permanent Link
|
||||
success:
|
||||
linkCopied: Link copied!
|
||||
errors:
|
||||
forbidden: You're not welcome here.
|
||||
internal: Something really went wrong.
|
||||
notFound: This location can't be reached.
|
||||
files:
|
||||
folders: Folders
|
||||
files: Files
|
||||
body: Body
|
||||
clear: Clear
|
||||
closePreview: Close preview
|
||||
home: Home
|
||||
lastModified: Last modified
|
||||
loading: Loading...
|
||||
lonely: It feels lonely here...
|
||||
metadata: Metadata
|
||||
multipleSelectionEnabled: Multiple selection enabled
|
||||
name: Name
|
||||
size: Size
|
||||
sortByName: Sort by name
|
||||
sortBySize: Sort by size
|
||||
sortByLastModified: Sort by last modified
|
||||
help:
|
||||
click: select file or directory
|
||||
ctrl:
|
||||
click: select multiple files or directories
|
||||
f: opens search
|
||||
s: save a file or download the directory where you are
|
||||
del: delete selected items
|
||||
doubleClick: open a file or directory
|
||||
esc: clear selection and/or close the prompt
|
||||
f1: this information
|
||||
f2: rename file
|
||||
help: Help
|
||||
login:
|
||||
password: Password
|
||||
submit: Login
|
||||
username: Username
|
||||
wrongCredentials: Wrong credentials
|
||||
prompts:
|
||||
copy: Copy
|
||||
copyMessage: 'Choose the place to copy your files:'
|
||||
currentlyNavigating: 'Currently navigating on:'
|
||||
deleteMessageMultiple: Are you sure you want to delete {count} file(s)?
|
||||
deleteMessageSingle: Are you sure you want to delete this file/folder?
|
||||
deleteTitle: Delete files
|
||||
displayName: 'Display Name:'
|
||||
download: Download files
|
||||
downloadMessage: Choose the format you want to download.
|
||||
error: Something went wrong
|
||||
fileInfo: File information
|
||||
filesSelected: "{count} files selected."
|
||||
lastModified: Last Modified
|
||||
move: Move
|
||||
moveMessage: 'Choose new house for your file(s)/folder(s):'
|
||||
newDir: New directory
|
||||
newDirMessage: Write the name of the new directory.
|
||||
newFile: New file
|
||||
newFileMessage: Write the name of the new file.
|
||||
numberDirs: Number of directories
|
||||
numberFiles: Number of files
|
||||
replace: Replace
|
||||
replaceMessage: >
|
||||
One of the files you're trying to upload is conflicting because of its name.
|
||||
Do you wish to replace the existing one?
|
||||
rename: Rename
|
||||
renameMessage: Insert a new name for
|
||||
show: Show
|
||||
size: Size
|
||||
schedule: Schedule
|
||||
scheduleMessage: Pick a date and time to schedule the publication of this post.
|
||||
newArchetype: Create a new post based on an archetype. Your file will be created on content folder.
|
||||
settings:
|
||||
admin: Admin
|
||||
administrator: Administrator
|
||||
allowCommands: Execute commands
|
||||
allowEdit: Edit, rename and delete files or directories
|
||||
allowNew: Create new files and directories
|
||||
allowPublish: Publish new posts and pages
|
||||
avoidChanges: "(leave blank to avoid changes)"
|
||||
changePassword: Change Password
|
||||
commands: Commands
|
||||
commandsHelp: >
|
||||
Here you can set commands that are executed in the named events. You
|
||||
write one command per line. If the event is related to files, such as before and
|
||||
after saving, the environment variable "FILE" will be available with the path
|
||||
of the file.
|
||||
commandsUpdated: Commands updated!
|
||||
customStylesheet: Custom Stylesheet
|
||||
examples: Examples
|
||||
globalSettings: Global Settings
|
||||
language: Language
|
||||
lockPassword: Prevent the user from changing the password
|
||||
newPassword: Your new password
|
||||
newPasswordConfirm: Confirm your new password
|
||||
newUser: New User
|
||||
password: Password
|
||||
passwordUpdated: Password updated!
|
||||
permissions: Permissions
|
||||
permissionsHelp: >
|
||||
You can set the user to be an administrator or choose the permissions
|
||||
individually. If you select "Administrator", all of the other options will be
|
||||
automatically checked. The management of users remains a privilege of an administrator.
|
||||
profileSettings: Profile Settings
|
||||
ruleExample1: >
|
||||
prevents the access to any dot file (such as .git, .gitignore) in
|
||||
every folder.
|
||||
ruleExample2: blocks the access to the file named Caddyfile on the root of the scope.
|
||||
rules: Rules
|
||||
rulesHelp1: >
|
||||
Here you can define a set of allow and disallow rules for this specific
|
||||
user. The blocked files won't show up in the listings and they wont be accessible
|
||||
to the user. We support regex and paths relative to the users scope.
|
||||
rulesHelp2: >
|
||||
Each rule goes in one different line and must start with the keyword
|
||||
{0} or {1}. Then you should write {2} if you are using a regular expression and
|
||||
then the expression or the path.
|
||||
scope: Scope
|
||||
settingsUpdated: Settings updated!
|
||||
user: User
|
||||
userCommands: Commands
|
||||
userCommandsHelp: >
|
||||
A space separated list with the available commands for this user.
|
||||
Example:
|
||||
userCreated: User created!
|
||||
userDeleted: User deleted!
|
||||
userManagement: User Management
|
||||
username: Username
|
||||
users: Users
|
||||
userUpdated: User updated!
|
||||
sidebar:
|
||||
help: Help
|
||||
logout: Logout
|
||||
myFiles: My files
|
||||
newFile: New file
|
||||
newFolder: New folder
|
||||
settings: Settings
|
||||
siteSettings: Site Settings
|
||||
hugoNew: Hugo New
|
||||
preview: Preview
|
||||
search:
|
||||
images: Images
|
||||
music: Music
|
||||
pdf: PDF
|
||||
pressToExecute: Press enter to execute.
|
||||
pressToSearch: Press enter to search.
|
||||
search: Search...
|
||||
searchOrCommand: Search or execute a command...
|
||||
searchOrSupportedCommand: 'Search or use one of your supported commands:'
|
||||
type: Type and press enter to search.
|
||||
types: Types
|
||||
video: Video
|
||||
writeToSearch: Write here to search
|
||||
languages:
|
||||
en: English
|
||||
fr: Français
|
||||
pt: Português
|
||||
ja: 日本語
|
||||
zhCN: 中文 (简体)
|
||||
zhTW: 中文 (繁體)
|
||||
es: Español
|
||||
time:
|
||||
unit: Time Unit
|
||||
seconds: Seconds
|
||||
minutes: Minutes
|
||||
hours: Hours
|
||||
days: Days
|
|
@ -1,202 +0,0 @@
|
|||
permanent: Permanente
|
||||
buttons:
|
||||
cancel: Cancelar
|
||||
close: Cerrar
|
||||
copy: Copiar
|
||||
copyFile: Copiar archivo
|
||||
copyToClipboard: Copiar al portapapeles
|
||||
create: Crear
|
||||
delete: Borrar
|
||||
download: Descargar
|
||||
info: Info
|
||||
more: Más
|
||||
move: Mover
|
||||
moveFile: Mover archivo
|
||||
new: Nuevo
|
||||
next: Siguiente
|
||||
ok: OK
|
||||
replace: Reemplazar
|
||||
previous: Anterior
|
||||
rename: Renombrar
|
||||
reportIssue: Reportar problema
|
||||
save: Guardar
|
||||
search: Buscar
|
||||
select: Seleccionar
|
||||
share: Compartir
|
||||
publish: Publicar
|
||||
selectMultiple: Selección múltiple
|
||||
schedule: Programar
|
||||
switchView: Cambiar vista
|
||||
toggleSidebar: Mostrar/Ocultar menú
|
||||
update: Actualizar
|
||||
upload: Subir
|
||||
permalink: Link permanente
|
||||
success:
|
||||
linkCopied: ¡Link copiado!
|
||||
errors:
|
||||
forbidden: No eres bienvenido aquí.
|
||||
internal: La verdad es que algo ha ido mal.
|
||||
notFound: No se puede acceder a este lugar.
|
||||
files:
|
||||
folders: Carpetas
|
||||
files: Archivos
|
||||
body: Cuerpo
|
||||
clear: Limpiar
|
||||
closePreview: Cerrar vista previa
|
||||
home: Inicio
|
||||
lastModified: Última modificación
|
||||
loading: Cargando...
|
||||
lonely: Uno se siente muy sólo aquí...
|
||||
metadata: Metadatos
|
||||
multipleSelectionEnabled: Selección múltiple activada
|
||||
name: Nombre
|
||||
size: Tamaño
|
||||
sortByName: Ordenar por nombre
|
||||
sortBySize: Ordenar por tamaño
|
||||
sortByLastModified: Ordenar por última modificación
|
||||
help:
|
||||
click: seleccionar archivo o carpeta
|
||||
ctrl:
|
||||
click: seleccionar múltiples archivos o carpetas
|
||||
f: abre la búsqueda
|
||||
s: guarda un archivo o lo descarga a la carpeta en la que estás
|
||||
del: elimina los items seleccionados
|
||||
doubleClick: abre un archivo o carpeta
|
||||
esc: limpia la selección y/o cierra la ventana
|
||||
f1: esta información
|
||||
f2: renombrar archivo
|
||||
help: Ayuda
|
||||
login:
|
||||
password: Contraseña
|
||||
submit: Iniciar sesión
|
||||
username: Usuario
|
||||
wrongCredentials: Usuario y/o contraseña incorrectos
|
||||
prompts:
|
||||
copy: Copiar
|
||||
copyMessage: 'Elige el lugar donde quieres copiar tus archivos:'
|
||||
currentlyNavigating: 'Actualmente estás en:'
|
||||
deleteMessageMultiple: ¿Estás seguro que quieres eliminar {count} archivo(s)?
|
||||
deleteMessageSingle: ¿Estás seguro que quieres eliminar este archivo/carpeta?
|
||||
deleteTitle: Borrar archivos
|
||||
displayName: 'Nombre:'
|
||||
download: Descargar archivos
|
||||
downloadMessage: Elige el formato de descarga.
|
||||
error: Algo ha fallado
|
||||
fileInfo: Información del archivo
|
||||
filesSelected: "{count} archivos seleccionados."
|
||||
lastModified: Última modificación
|
||||
move: Mover
|
||||
moveMessage: 'Elige una nueva casa para tus archivo(s)/carpeta(s):'
|
||||
newDir: Nueva carpeta
|
||||
newDirMessage: Escribe el nombre de la nueva carpeta.
|
||||
newFile: Nuevo archivo
|
||||
newFileMessage: Escribe el nombre del nuevo archivo.
|
||||
numberDirs: Número de carpetas
|
||||
numberFiles: Número de archivos
|
||||
replace: Reemplazar
|
||||
replaceMessage: >
|
||||
Uno de los archivos ue intentas subir está creando conflicto por su nombre.
|
||||
¿Quieres cambiar el nombre del ya existente?
|
||||
rename: Renombrar
|
||||
renameMessage: Escribe el nuevo nombre para
|
||||
show: Mostrar
|
||||
size: Tamaño
|
||||
schedule: Programar
|
||||
scheduleMessage: Elige una hora y fecha para programar la publicación de este post.
|
||||
newArchetype: Crea un nuevo post basado en un arquetipo. Tu archivo será creado en la carpeta de contenido.
|
||||
settings:
|
||||
admin: Admin
|
||||
administrator: Administrador
|
||||
allowCommands: Ejecutar comandos
|
||||
allowEdit: Editar, renombrar y borrar archivos o carpetas
|
||||
allowNew: Crear nuevos archivos y carpetas
|
||||
allowPublish: Publicar nuevos posts y páginas
|
||||
avoidChanges: "(dejar en blanco para evitar cambios)"
|
||||
changePassword: Cambiar contraseña
|
||||
commands: Comandos
|
||||
commandsHelp: >
|
||||
Aquí puedes crear comandos que serán ejecutados en los eventos. Debes
|
||||
escribir un comando por linea. Si el evento está relacionado con archivos, como
|
||||
por ejemplo, antes y después de guardar, la variable de entorno "FILE" estará
|
||||
disponible en la ruta del archivo.
|
||||
commandsUpdated: ¡Comandos actualizados!
|
||||
customStylesheet: Modificar hoja de estilos
|
||||
examples: Ejemplos
|
||||
globalSettings: Ajustes globales
|
||||
language: Idioma
|
||||
lockPassword: Evitar que el usuario cambie la contraseña
|
||||
newPassword: Tu nueva contraseña
|
||||
newPasswordConfirm: Confirma tu contraseña
|
||||
newUser: Nuevo usuario
|
||||
password: Contraseña
|
||||
passwordUpdated: ¡Contraseña actualizada!
|
||||
permissions: Permisos
|
||||
permissionsHelp: >
|
||||
Puedes nombrar al usuario como administrador o elegir los permisos
|
||||
individualmente. Si seleccionas "Administrador", todas las otras opciones
|
||||
serán activadas automáticamente. La administración de usuarios es un privilegio de administrador.
|
||||
profileSettings: Ajustes del perfil
|
||||
ruleExample1: >
|
||||
previene el acceso a una extensión de archivo (Como .git) en
|
||||
cada carpeta.
|
||||
ruleExample2: bloquea el acceso al archivo llamado Caddyfile en la carpeta raíz.
|
||||
rules: Reglas
|
||||
rulesHelp1: >
|
||||
Aquí puedes definir un conjunto de reglas de permisos para este usuario
|
||||
específico. Los archivos bloqueados no se mostrarán en las listas y no serán accesibles
|
||||
por el usuario. Puedes utilizar regex y rutas relativas a la raíz del usuario.
|
||||
rulesHelp2: >
|
||||
Cada regla va en una línea diferente, y debe comenzar con la palabra clave
|
||||
{0} or {1}. Entonces, debes escribir {2} si estás usando una expresión regular (REGEX) y
|
||||
luego la expresión o la ruta.
|
||||
scope: Raíz
|
||||
settingsUpdated: ¡Ajustes actualizados!
|
||||
user: Usuario
|
||||
userCommands: Comandos
|
||||
userCommandsHelp: >
|
||||
Una lista separada por espacios con los comandos permitidos para este usuario.
|
||||
Ejemplo:
|
||||
userCreated: ¡Usuario creado!
|
||||
userDeleted: ¡Usuario eliminado!
|
||||
userManagement: Administración de usuarios
|
||||
username: Usuario
|
||||
users: Usuarios
|
||||
userUpdated: ¡Usuario actualizado!
|
||||
sidebar:
|
||||
help: Ayuda
|
||||
logout: Cerrar sesión
|
||||
myFiles: Mis archivos
|
||||
newFile: Nuevo archivo
|
||||
newFolder: Nueva carpeta
|
||||
settings: Ajustes
|
||||
siteSettings: Ajustes del sitio
|
||||
hugoNew: Nuevo Hugo
|
||||
preview: Vista previa
|
||||
search:
|
||||
images: Images
|
||||
music: Música
|
||||
pdf: PDF
|
||||
pressToExecute: Presiona enter para ejecutar.
|
||||
pressToSearch: Presiona enter para buscar.
|
||||
search: Buscar...
|
||||
searchOrCommand: Buscar o ejecutar un comando...
|
||||
searchOrSupportedCommand: 'Buscar o ejecutar uno de los comandos soportados:'
|
||||
type: Escribe y presiona enter para buscar.
|
||||
types: Tipos
|
||||
video: Vídeo
|
||||
writeToSearch: Escribe aquí para buscar
|
||||
languages:
|
||||
en: English
|
||||
fr: Français
|
||||
pt: Português
|
||||
es: Español
|
||||
ja: 日本語
|
||||
zhCN: 中文 (简体)
|
||||
zhTW: 中文 (繁體)
|
||||
|
||||
time:
|
||||
unit: Unidad
|
||||
seconds: Segundos
|
||||
minutes: Minutos
|
||||
hours: Horas
|
||||
days: Días
|
|
@ -1,194 +0,0 @@
|
|||
permanent: Permanent
|
||||
buttons:
|
||||
cancel: Annuler
|
||||
close: Fermer
|
||||
copy: Copier
|
||||
copyFile: Copier le fichier
|
||||
copyToClipboard: Copier dans le presse-papier
|
||||
create: Créer
|
||||
delete: Supprimer
|
||||
download: Télécharger
|
||||
info: Info
|
||||
more: Plus
|
||||
move: Déplacer
|
||||
moveFile: Déplacer le fichier
|
||||
new: Nouveau
|
||||
next: Suivant
|
||||
ok: OK
|
||||
replace: Remplacer
|
||||
previous: Précédent
|
||||
rename: Renommer
|
||||
reportIssue: Rapport d'erreur
|
||||
save: Enregistrer
|
||||
search: Chercher
|
||||
select: Sélectionner
|
||||
share: Partager
|
||||
publish: Publier
|
||||
selectMultiple: Sélection multiple
|
||||
schedule: Fixer la date
|
||||
switchView: Changer le mode d'affichage
|
||||
toggleSidebar: Afficher/Masquer la barre latérale
|
||||
update: Mettre à jour
|
||||
upload: Importer
|
||||
permalink: Obtenir un lien permanent
|
||||
errors:
|
||||
forbidden: Vous n'êtes pas autorisé à être ici.
|
||||
internal: Aïe ! Quelque chose s'est mal passé.
|
||||
notFound: Impossible d'accéder à cet emplacement.
|
||||
files:
|
||||
folders: Dossiers
|
||||
files: Fichiers
|
||||
body: Corps
|
||||
clear: Fermer
|
||||
closePreview: Fermer la prévisualisation
|
||||
home: Accueil
|
||||
lastModified: Dernière modification
|
||||
loading: Chargement...
|
||||
lonely: Il semble qu'il n'y ai rien par ici...
|
||||
metadata: Metadonnées
|
||||
multipleSelectionEnabled: Sélection multiple activée
|
||||
name: Nom
|
||||
size: Taille
|
||||
sortByName: Trier par nom
|
||||
sortBySize: Trier par taille
|
||||
sortByLastModified: Trier par date de dernière modification
|
||||
help:
|
||||
click: Sélectionner un élément
|
||||
ctrl:
|
||||
click: Sélectionner plusieurs éléments
|
||||
f: Ouvrir l'invité de recherche
|
||||
s: Télécharger l'élément actuel
|
||||
del: Supprimer les éléments sélectionnés
|
||||
doubleClick: Ouvrir un élément
|
||||
esc: Désélectionner et/ou fermer la boîte de dialogue
|
||||
f1: Ouvrir l'aide
|
||||
f2: Renommer le fichier
|
||||
help: Aide
|
||||
login:
|
||||
password: Mot de passe
|
||||
submit: Se connecter
|
||||
username: Utilisateur
|
||||
wrongCredentials: Identifiants incorrects !
|
||||
prompts:
|
||||
copy: Copier
|
||||
copyMessage: 'Choisissez l''emplacement où copier la sélection :'
|
||||
currentlyNavigating: 'Dossier courant :'
|
||||
deleteMessageMultiple: Etes-vous sûr de vouloir supprimer ces {count} élément(s) ?
|
||||
deleteMessageSingle: Etes-vous sûr de vouloir supprimer cet élément ?
|
||||
deleteTitle: Supprimer
|
||||
displayName: 'Nom :'
|
||||
download: Télécharger
|
||||
downloadMessage: 'Choisissez le format de téléchargement :'
|
||||
error: Quelque chose s'est mal passé
|
||||
fileInfo: Informations
|
||||
filesSelected: "{count} éléments sélectionnés"
|
||||
lastModified: Dernière modification
|
||||
move: Déplacer
|
||||
moveMessage: 'Choisissez l''emplacement où déplacer la sélection :'
|
||||
newDir: Nouveau dossier
|
||||
newDirMessage: 'Nom du nouveau dossier :'
|
||||
newFile: Nouveau fichier
|
||||
newFileMessage: 'Nom du nouveau fichier :'
|
||||
numberDirs: Nombre de dossiers
|
||||
numberFiles: Nombre de fichiers
|
||||
replace: Remplacer
|
||||
replaceMessage: >
|
||||
Un des fichiers que vous êtes en train d'importer a le même nom qu'un autre déjà présent.
|
||||
Voulez-vous remplacer le fichier actuel par le nouveau ?
|
||||
rename: Renommer
|
||||
renameMessage: Nouveau nom pour
|
||||
show: Montrer
|
||||
size: Taille
|
||||
schedule: Fixer la date
|
||||
scheduleMessage: Choisissez une date pour planifier la publication de ce post
|
||||
newArchetype: Créer un nouveau post basé sur un archétype. Votre fichier sera créé dans le dossier de contenu.
|
||||
settings:
|
||||
admin: Admin
|
||||
administrator: Administrateur
|
||||
allowCommands: Exécuter des commandes
|
||||
allowEdit: Editer, renommer et supprimer des fichiers ou des dossiers
|
||||
allowNew: Créer de nouveaux fichiers et dossiers
|
||||
allowPublish: Publier de nouveaux posts et pages
|
||||
avoidChanges: "(Laisser vide pour conserver l'actuel)"
|
||||
changePassword: Modifier le mot de passe
|
||||
commands: Commandes
|
||||
commandsHelp: >
|
||||
Ici vous pouvez définir des commandes qui seront exécutées lors de l'évènement correspondant.
|
||||
Vous devez indiquer une commande par ligne. Si l'évènement est en rapport avec des fichiers,
|
||||
par exemple avant et après enregistrement, la variable d'environement "FILE" sera disponible
|
||||
et contiendra le chemin d'accès vers le fichier.
|
||||
commandsUpdated: Commandes mises à jour !
|
||||
customStylesheet: Feuille de style personnalisée
|
||||
examples: Exemples
|
||||
globalSettings: Paramètres généraux
|
||||
language: Langue
|
||||
newPassword: Votre nouveau mot de passe
|
||||
newPasswordConfirm: Confirmation du nouveau mot de passe
|
||||
newUser: Nouvel Utilisateur
|
||||
password: Mot de passe
|
||||
passwordUpdated: Mot de passe mis à jour !
|
||||
permissions: Permissions
|
||||
permissionsHelp: >
|
||||
Vous pouvez définir l'utilisateur comme étant un administrateur ou encore choisir les
|
||||
permissions individuellement. Si vous sélectionnez "Administrateur", toutes les autres
|
||||
options seront automatiquement activées. La gestion des utilisateurs est un privilège que
|
||||
seul l'administrateur possède.
|
||||
profileSettings: Paramètres du profil
|
||||
ruleExample1: Bloque l'accès à tous les fichiers commençant par un point (comme par exemple .git, .gitignore) dans tous les dossiers
|
||||
ruleExample2: Bloque l'accès au fichier nommé "Caddyfile" à la racine du dossier utilisateur
|
||||
rules: Règles
|
||||
rulesHelp1: >
|
||||
Vous pouvez définir ici un ensemble de règles pour cet utilisateur.
|
||||
Les fichiers bloqués ne seront pas affichés et ne seront pas accessibles par l'utilisateur.
|
||||
Les expressions régulières sont supportées et les chemins d'accès sont relatifs par rapport au dossier de l'utilisateur.
|
||||
rulesHelp2: >
|
||||
Chaque règle est définie sur une ligne différente et doit commencer par le mot clé {0} ou {1}.
|
||||
Vous devez ensuite ajouter {2} si vous utilisez une expression régulière puis l'expression en question ou bien seulement le chemin d'accès.
|
||||
scope: Portée du dossier utilisateur
|
||||
settingsUpdated: Les paramètres ont été mis à jour !
|
||||
user: Utilisateur
|
||||
userCommands: Commandes
|
||||
userCommandsHelp: 'Une liste séparée par des espaces des commandes permises pour l''utilisateur. Exemple :'
|
||||
userCreated: Utilisateur créé !
|
||||
userDeleted: Utilisateur supprimé !
|
||||
userManagement: Gestion des utilisateurs
|
||||
username: Nom d'utilisateur
|
||||
users: Utilisateurs
|
||||
userUpdated: Utilisateur mis à jour !
|
||||
sidebar:
|
||||
help: Aide
|
||||
logout: Se déconnecter
|
||||
myFiles: Mes fichiers
|
||||
newFile: Nouveau fichier
|
||||
newFolder: Nouveau dossier
|
||||
settings: Paramètres
|
||||
siteSettings: Paramètres du site
|
||||
hugoNew: Nouveau Hugo
|
||||
preview: Prévisualiser
|
||||
search:
|
||||
images: Images
|
||||
music: Musique
|
||||
pdf: PDF
|
||||
pressToExecute: Appuyez sur Entrée pour exécuter
|
||||
pressToSearch: Appuyez sur Entrée pour lancer la recherche
|
||||
search: Recherche en cours...
|
||||
searchOrCommand: Rechercher ou exécuter une commande...
|
||||
searchOrSupportedCommand: 'Lancez une recherche ou exécutez une commande parmis les suivantes :'
|
||||
type: Tapez votre recherche et appuyez sur Entrée
|
||||
types: Types
|
||||
video: Video
|
||||
writeToSearch: Ecrivez ici pour lancer une recherche
|
||||
languages:
|
||||
en: English
|
||||
fr: Français
|
||||
pt: Português
|
||||
ja: 日本語
|
||||
zhCN: 中文 (简体)
|
||||
zhTW: 中文 (繁體)
|
||||
es: Español
|
||||
time:
|
||||
unit: Unité de temps
|
||||
seconds: Secondes
|
||||
minutes: Minutes
|
||||
hours: Heures
|
||||
days: Jours
|
|
@ -1,61 +0,0 @@
|
|||
import Vue from 'vue'
|
||||
import VueI18n from 'vue-i18n'
|
||||
import en from './en.yaml'
|
||||
import fr from './fr.yaml'
|
||||
import pt from './pt.yaml'
|
||||
import ja from './ja.yaml'
|
||||
import zhCN from './zh-cn.yaml'
|
||||
import zhTW from './zh-tw.yaml'
|
||||
import es from './es.yaml'
|
||||
|
||||
Vue.use(VueI18n)
|
||||
|
||||
export function detectLocale () {
|
||||
let locale = (navigator.language || navigator.browserLangugae).toLowerCase()
|
||||
switch (true) {
|
||||
case /^en.*/i.test(locale):
|
||||
locale = 'en'
|
||||
break
|
||||
case /^fr.*/i.test(locale):
|
||||
locale = 'fr'
|
||||
break
|
||||
case /^pt.*/i.test(locale):
|
||||
locale = 'pt'
|
||||
break
|
||||
case /^ja.*/i.test(locale):
|
||||
locale = 'ja'
|
||||
break
|
||||
case /^zh-CN/i.test(locale):
|
||||
locale = 'zh-cn'
|
||||
break
|
||||
case /^zh-TW/i.test(locale):
|
||||
locale = 'zh-tw'
|
||||
break
|
||||
case /^zh.*/i.test(locale):
|
||||
locale = 'zh-cn'
|
||||
break
|
||||
case /^es.*/i.test(locale):
|
||||
locale = 'es'
|
||||
break
|
||||
default:
|
||||
locale = 'en'
|
||||
}
|
||||
|
||||
return locale
|
||||
}
|
||||
|
||||
const i18n = new VueI18n({
|
||||
locale: detectLocale(),
|
||||
fallbackLocale: 'en',
|
||||
messages: {
|
||||
'en': en,
|
||||
'fr': fr,
|
||||
'pt': pt,
|
||||
'ja': ja,
|
||||
'zh-cn': zhCN,
|
||||
'zh-tw': zhTW,
|
||||
'es': es
|
||||
}
|
||||
})
|
||||
|
||||
export default i18n
|
|
@ -1,201 +0,0 @@
|
|||
permanent: 永久
|
||||
buttons:
|
||||
cancel: キャンセル
|
||||
close: 閉じる
|
||||
copy: コピー
|
||||
copyFile: ファイルをコピー
|
||||
copyToClipboard: クリップボードにコピー
|
||||
create: 作成
|
||||
delete: 削除
|
||||
download: ダウンロード
|
||||
info: 情報
|
||||
more: More
|
||||
move: 移動
|
||||
moveFile: ファイルを移動
|
||||
new: 新規
|
||||
next: 次
|
||||
ok: OK
|
||||
replace: 置き換える
|
||||
previous: 前
|
||||
rename: 名前を変更
|
||||
reportIssue: 問題を報告
|
||||
save: 保存
|
||||
search: 検索
|
||||
select: 選択
|
||||
share: シェア
|
||||
publish: 発表
|
||||
selectMultiple: 複数選択
|
||||
schedule: スケジュール
|
||||
switchView: 表示を切り替わる
|
||||
toggleSidebar: サイドバーを表示する
|
||||
update: 更新
|
||||
upload: アップロード
|
||||
permalink: 固定リンク
|
||||
success:
|
||||
linkCopied: リンクがコピーされました!
|
||||
errors:
|
||||
forbidden: アクセスが拒否されました。
|
||||
internal: 内部エラーが発生しました。
|
||||
notFound: リソースが見つからなりませんでした。
|
||||
files:
|
||||
folders: フォルダ
|
||||
files: ファイル
|
||||
body: 本文
|
||||
clear: クリアー
|
||||
closePreview: プレビューを閉じる
|
||||
home: ホーム
|
||||
lastModified: 最終変更
|
||||
loading: ローディング...
|
||||
lonely: ここには何もない...
|
||||
metadata: メタデータ
|
||||
multipleSelectionEnabled: 複数選択有効
|
||||
name: 名前
|
||||
size: サイズ
|
||||
sortByName: 名前によるソート
|
||||
sortBySize: サイズによるソート
|
||||
sortByLastModified: 最終変更日付によるソート
|
||||
help:
|
||||
click: ファイルやディレクトリを選択
|
||||
ctrl:
|
||||
click: 複数のファイルやディレクトリを選択
|
||||
f: 検索を有効にする
|
||||
s: ファイルを保存またはカレントディレクトリをダウンロード
|
||||
del: 選択した項目を削除
|
||||
doubleClick: ファイルやディレクトリをオープン
|
||||
esc: 選択をクリアーまたはプロンプトを閉じる
|
||||
f1: このヘルプを表示
|
||||
f2: ファイルの名前を変更
|
||||
help: ヘルプ
|
||||
login:
|
||||
password: パスワード
|
||||
submit: ログイン
|
||||
username: ユーザ名
|
||||
wrongCredentials: ユーザ名またはパスワードが間違っています。
|
||||
prompts:
|
||||
copy: コピー
|
||||
copyMessage: コピーの目標ディレクトリを選択してください:
|
||||
currentlyNavigating: 現在閲覧しているディレクトリ:
|
||||
deleteMessageMultiple: '{count} つのファイルを本当に削除してよろしいですか。'
|
||||
deleteMessageSingle: このファイル/フォルダを本当に削除してよろしいですか。
|
||||
deleteTitle: ファイルを削除
|
||||
displayName: 名前:
|
||||
download: ファイルをダウンロード
|
||||
downloadMessage: 圧縮形式を選択してください。
|
||||
error: あるエラーが発生しました。
|
||||
fileInfo: ファイル情報
|
||||
filesSelected: '{count} つのファイルは選択されました。'
|
||||
lastModified: 最終変更
|
||||
move: 移動
|
||||
moveMessage: 移動の目標ディレクトリを選択してください:
|
||||
newDir: 新しいディレクトリを作成
|
||||
newDirMessage: 新しいディレクトリの名前を入力してください。
|
||||
newFile: 新しいファイルを作成
|
||||
newFileMessage: 新しいファイルの名前を入力してください。
|
||||
numberDirs: ディレクトリ個数
|
||||
numberFiles: ファイル個数
|
||||
replace: 置き換える
|
||||
replaceMessage: >
|
||||
アップロードするファイルの中でかち合う名前が一つあります。
|
||||
既存のファイルを置き換えりませんか。
|
||||
rename: 名前を変更
|
||||
renameMessage: 名前を変更しようファイルは:
|
||||
show: 表示
|
||||
size: サイズ
|
||||
schedule: スケジュール
|
||||
scheduleMessage: このポストの発表日付をスケジュールしてください。
|
||||
newArchetype: ある元型に基づいて新しいポストを作成します。ファイルは コンテンツフォルダに作成されます。
|
||||
settings:
|
||||
admin: 管理者
|
||||
administrator: 管理者
|
||||
allowCommands: コマンドの実行
|
||||
allowEdit: ファイルやディレクトリの編集、名前変更と削除
|
||||
allowNew: ファイルとディレクトリの作成
|
||||
allowPublish: ポストとぺーじの発表
|
||||
avoidChanges: "(変更を避けるために空白にしてください)"
|
||||
changePassword: パスワードを変更
|
||||
commands: コマンド
|
||||
commandsHelp: "\
|
||||
ここで、名前付きイベントに実行するコマンドを設定することができます。\
|
||||
一行にコマンド一つを入力してください。\
|
||||
イベントはファイルに関連する場合、例えばファイル保存の前にまたは後で、\
|
||||
環境変数 FILE はファイルのパスに割り当てられます。"
|
||||
commandsUpdated: コマンドは更新されました!
|
||||
customStylesheet: カスタムスタイルシ ート
|
||||
examples: 例
|
||||
globalSettings: グローバル設定
|
||||
language: 言語
|
||||
lockPassword: 新しいパスワードを変更に禁止
|
||||
newPassword: 新しいパスワード
|
||||
newPasswordConfirm: 新しいパスワードを確認します
|
||||
newUser: 新しいユーザー
|
||||
password: パスワード
|
||||
passwordUpdated: パスワードは更新されました!
|
||||
permissions: 権限
|
||||
permissionsHelp: "\
|
||||
あなたはユーザーを管理者に設定し、または権限を個々に設定しできます。\
|
||||
\"管理者\"を選択する場合、その他のすべての選択肢は自動的に設定されます。\
|
||||
ユーザーの管理は管理者の権限として保留されました。"
|
||||
profileSettings: プロファイル設定
|
||||
ruleExample1: "\
|
||||
各フォルダに名前はドットで始まるファイル(例えば、.git、.gitignore)\
|
||||
へのアクセスを制限します。"
|
||||
ruleExample2: 範囲のルートパスに名前は Caddyfile のファイルへのアクセスを制限します。
|
||||
rules: 規則
|
||||
rulesHelp1: "\
|
||||
ここに、あなたはこのユーザーの許可または拒否規則を設定できます。\
|
||||
ブロックされたファイルはリストに表示されません、それではアクセスも制限されます。\
|
||||
正規表現(regex)のサポートと範囲に相対のパスが提供されています。"
|
||||
rulesHelp2: "\
|
||||
一行に規則一つを入力してください、\
|
||||
その間に規則はキーワード {0} や {1} で始める必要があります。\
|
||||
そして正規表現を使う場合、{2} と入力し、表現やパスを入力してください。"
|
||||
scope: 範囲
|
||||
settingsUpdated: 設定は更新されました!
|
||||
user: ユーザー
|
||||
userCommands: ユーザーのコマンド
|
||||
userCommandsHelp: "\
|
||||
空白区切りの有効のコマンドのリストを指定してください。\
|
||||
例:"
|
||||
userCreated: ユーザーは作成されました!
|
||||
userDeleted: ユーザーは削除されました!
|
||||
userManagement: ユーザー管理
|
||||
username: ユーザー名
|
||||
users: ユーザー
|
||||
userUpdated: ユーザーは更新されました!
|
||||
sidebar:
|
||||
help: ヘルプ
|
||||
logout: ログアウト
|
||||
myFiles: 私のファイル
|
||||
newFile: 新しいファイルを作成
|
||||
newFolder: 新しいフォルダを作成
|
||||
settings: 設定
|
||||
siteSettings: サイト設定
|
||||
hugoNew: Hugo New
|
||||
preview: プレビュー
|
||||
search:
|
||||
images: 画像
|
||||
music: 音楽
|
||||
pdf: PDF
|
||||
pressToExecute: Enter を押して実行します。
|
||||
pressToSearch: Enter を押して検索します。
|
||||
search: 検索...
|
||||
searchOrCommand: コマンドを検索または実行します。
|
||||
searchOrSupportedCommand: サポートしているコマンドを検索または実行します:
|
||||
type: キーワードを入力し、Enter を押して検索します。
|
||||
types: 種類
|
||||
video: ビデオ
|
||||
writeToSearch: ここにキーワードを入力してください
|
||||
languages:
|
||||
en: English
|
||||
fr: Français
|
||||
pt: Português
|
||||
ja: 日本語
|
||||
zhCN: 中文 (简体)
|
||||
zhTW: 中文 (繁體)
|
||||
es: Español
|
||||
time:
|
||||
unit: 時間単位
|
||||
seconds: 秒
|
||||
minutes: 分
|
||||
hours: 時間
|
||||
days: 日
|
|
@ -1,204 +0,0 @@
|
|||
permanent: Permanente
|
||||
buttons:
|
||||
cancel: Cancelar
|
||||
close: Fechar
|
||||
copy: Copiar
|
||||
copyFile: Copiar ficheiro
|
||||
copyToClipboard: Copiar
|
||||
create: Criar
|
||||
delete: Eliminar
|
||||
download: Descarregar
|
||||
info: Info
|
||||
more: Mais
|
||||
move: Mover
|
||||
moveFile: Mover ficheiro
|
||||
new: Novo
|
||||
next: Próximo
|
||||
ok: OK
|
||||
previous: Anterior
|
||||
publish: Publicar
|
||||
rename: Renomear
|
||||
replace: Substituir
|
||||
reportIssue: Reportar Erro
|
||||
save: Guardar
|
||||
share: Partilhar
|
||||
schedule: Agendar
|
||||
search: Pesquisar
|
||||
select: Selecionar
|
||||
selectMultiple: Selecionar múltiplos
|
||||
switchView: Alterar modo de visão
|
||||
toggleSidebar: Alternar barra lateral
|
||||
update: Atualizar
|
||||
upload: Enviar
|
||||
permalink: Obter link permanente
|
||||
success:
|
||||
linkCopied: Link copiado!
|
||||
errors:
|
||||
forbidden: Tu não és bem-vindo aqui.
|
||||
internal: Algo correu bastante mal.
|
||||
notFound: Não conseguimos chegar a esta localização.
|
||||
files:
|
||||
body: Corpo
|
||||
clear: Limpar
|
||||
closePreview: Fechar pré-visualização
|
||||
files: Ficheiros
|
||||
folders: Pastas
|
||||
home: Início
|
||||
lastModified: Última modificação
|
||||
loading: A carregar...
|
||||
lonely: Sinto-me sozinho...
|
||||
metadata: Metadados
|
||||
multipleSelectionEnabled: Seleção múltipla ativada
|
||||
name: Nome
|
||||
size: Tamanho
|
||||
sortByLastModified: Ordenar pela última modificação
|
||||
sortByName: Ordenar pelo nome
|
||||
sortBySize: Ordenar pelo tamanho
|
||||
help:
|
||||
click: selecionar pasta ou ficheiro
|
||||
ctrl:
|
||||
click: selecionar várias pastas e ficheiros
|
||||
f: pesquisar
|
||||
s: guardar um ficheiro ou descarregar a pasta em que estás a navegar
|
||||
del: eliminar os ficheiros selecionados
|
||||
doubleClick: abrir pasta ou ficheiro
|
||||
esc: limpar seleção e/ou fechar menu
|
||||
f1: esta informação
|
||||
f2: renomear ficheiro
|
||||
help: Ajuda
|
||||
languages:
|
||||
en: English
|
||||
fr: Français
|
||||
pt: Português
|
||||
ja: 日本語
|
||||
zhCN: 中文 (简体)
|
||||
zhTW: 中文 (繁體)
|
||||
es: Español
|
||||
login:
|
||||
password: Palavra-passe
|
||||
submit: Login
|
||||
username: Nome de utilizador
|
||||
wrongCredentials: Dados errados
|
||||
prompts:
|
||||
copy: Copiar
|
||||
copyMessage: 'Escolhe um lugar para copiar os ficheiros:'
|
||||
currentlyNavigating: 'A navegar em:'
|
||||
deleteMessageMultiple: Deseja eliminar {count} ficheiro(s)?
|
||||
deleteMessageSingle: Deseja eliminar esta pasta/ficheiro?
|
||||
deleteTitle: Eliminar ficheiros
|
||||
displayName: 'Nome:'
|
||||
download: Descarregar ficheiros
|
||||
downloadMessage: Escolha o formato do ficheiro.
|
||||
error: Algo correu mal
|
||||
fileInfo: Informação do ficheiro
|
||||
filesSelected: "{count} ficheiros selecionados."
|
||||
lastModified: Última Modificação
|
||||
move: Mover
|
||||
moveMessage: 'Escolha uma nova casa para os seus ficheiros:'
|
||||
newArchetype: Criar um novo post baseado num "archetype". O seu ficheiro será criado
|
||||
na pasta "content".
|
||||
newDir: Nova pasta
|
||||
newDirMessage: Escreva o nome da nova pasta.
|
||||
newFile: Novo ficheiro
|
||||
newFileMessage: Escreva o nome do novo ficheiro.
|
||||
numberDirs: Número de pastas
|
||||
numberFiles: Número de ficheiros
|
||||
rename: Renomear
|
||||
renameMessage: Insira um novo nome para
|
||||
replace: Substituir
|
||||
replaceMessage: >
|
||||
Já existe um ficheiro com nome igual a um dos que está a tentar
|
||||
enviar. Deseja substituir?
|
||||
schedule: Agendar
|
||||
scheduleMessage: Escolha uma data para publicar este post.
|
||||
show: Mostrar
|
||||
size: Tamanho
|
||||
search:
|
||||
images: Imagens
|
||||
music: Música
|
||||
pdf: PDF
|
||||
pressToExecute: Prima enter para executar.
|
||||
pressToSearch: Prima enter para pesquisar.
|
||||
search: Pesquise...
|
||||
searchOrCommand: Pesquise ou execute um comando...
|
||||
searchOrSupportedCommand: 'Pesquise ou utilize um dos seus comandos:'
|
||||
type: Escreva e prima enter para pesquisar.
|
||||
types: Tipos
|
||||
video: Vídeos
|
||||
writeToSearch: Escreva aqui para pesquisar
|
||||
settings:
|
||||
admin: Admin
|
||||
administrator: Administrador
|
||||
allowCommands: Executar comandos
|
||||
allowEdit: Editar, renomear e eliminar ficheiros ou pastas
|
||||
allowNew: Criar novos ficheiros e pastas
|
||||
allowPublish: Publicar novas páginas e conteúdos
|
||||
avoidChanges: "(deixe em branco para manter)"
|
||||
changePassword: Alterar Password
|
||||
commands: Comandos
|
||||
commandsHelp: >
|
||||
Pode definir um conjunto de comandos a executar em determiandos eventos.
|
||||
Deve escrever um comando por linha. Se o evento estiver relacionado com ficheiros,
|
||||
como antes e depois de guardar, irá existir uma variável de ambiente denominada
|
||||
"FILE" com o caminho do ficheiro.
|
||||
commandsUpdated: Comandos atualizados!
|
||||
customStylesheet: Estilos Personalizados
|
||||
examples: Exemplos
|
||||
globalSettings: Configurações Globais
|
||||
language: Linguagem
|
||||
lockPassword: Não permitir que o utilizador altere a palavra-passe
|
||||
newPassword: Nova palavra-passe
|
||||
newPasswordConfirm: Confirme a nova palavra-passe
|
||||
newUser: Novo Utilizador
|
||||
password: Palavra-passe
|
||||
passwordUpdated: Palavra-passe atualizada!
|
||||
permissions: Permissões
|
||||
permissionsHelp: >
|
||||
Pode definir o utilizador como administrador ou escolher as permissões
|
||||
manualmente. Se selecionar a opção "Administrador", todas as outras opções serão
|
||||
automaticamente selecionadas. A gestão dos utilizadores é um privilégio restringido
|
||||
aos administradores.
|
||||
profileSettings: Configurações do Utilizador
|
||||
ruleExample1: >
|
||||
previne o acesso a qualquer "dotfile" (como .git, .gitignore) em
|
||||
qualquer pasta
|
||||
ruleExample2: bloqueia o acesso ao ficheiro chamado Caddyfile.
|
||||
rules: Regras
|
||||
rulesHelp1: >
|
||||
Aqui pode definir um conjunto de regras para permitir ou bloquear o
|
||||
acesso do utilizador a determinados ficheiros ou pastas. Os ficheiros bloqueados
|
||||
não irão aparecer durante a navegação. Suportamos expressões regulares e os caminhos
|
||||
dos ficheiros devem ser relativos à base do utilizador.
|
||||
rulesHelp2: >
|
||||
Cada regra deve ser colocada numa linha diferente e deve começar com
|
||||
as palavras {0} (permite) ou {1} (bloqueia). Deve escrever, logo de seguida, {2},
|
||||
caso queira utilizar uma expressão regular. Depois, escreva o caminho do ficheiro/pasta
|
||||
ou a expressão regular.
|
||||
scope: Base
|
||||
settingsUpdated: Configurações atualizadas!
|
||||
user: Utilizador
|
||||
userCommands: Comandos
|
||||
userCommandsHelp: 'Uma lista, separada com espaços, de comandos disponíveis para
|
||||
este utilizados. Exemplo:'
|
||||
userCreated: Utilizador criado!
|
||||
userDeleted: Utilizador eliminado!
|
||||
userManagement: Gestão de Utilizadores
|
||||
username: Nome de utilizador
|
||||
users: Utilizadores
|
||||
userUpdated: Utilizador atualizado!
|
||||
sidebar:
|
||||
help: Ajuda
|
||||
hugoNew: Hugo New
|
||||
logout: Sair
|
||||
myFiles: Ficheiros
|
||||
newFile: Novo ficheiro
|
||||
newFolder: Nova pasta
|
||||
preview: Pré-visualizar
|
||||
settings: Configurações
|
||||
siteSettings: Configurações do Site
|
||||
time:
|
||||
unit: Unidades de Tempo
|
||||
seconds: Segundos
|
||||
minutes: Minutos
|
||||
hours: Horas
|
||||
days: Dias
|
|
@ -1,199 +0,0 @@
|
|||
permanent: 永久
|
||||
buttons:
|
||||
cancel: 取消
|
||||
close: 关闭
|
||||
copy: 复制
|
||||
copyFile: 复制文件
|
||||
copyToClipboard: 复制到剪贴板
|
||||
create: 创建
|
||||
delete: 删除
|
||||
download: 下载
|
||||
info: 信息
|
||||
more: 更多
|
||||
move: 移动
|
||||
moveFile: 移动文件
|
||||
new: 新
|
||||
next: 下一个
|
||||
ok: 确定
|
||||
replace: 替换
|
||||
previous: 上一个
|
||||
rename: 重命名
|
||||
reportIssue: 报告问题
|
||||
save: 保存
|
||||
search: 搜索
|
||||
select: 选择
|
||||
share: 分享
|
||||
publish: 发布
|
||||
selectMultiple: 选择多个
|
||||
schedule: 计划
|
||||
switchView: 切换显示方式
|
||||
toggleSidebar: 切换侧边栏
|
||||
update: 更新
|
||||
upload: 上传
|
||||
permalink: 获取永久链接
|
||||
success:
|
||||
linkCopied: 链接已复制!
|
||||
errors:
|
||||
forbidden: 你被禁止访问。
|
||||
internal: 内部出现麻烦了。
|
||||
notFound: 找不到文件。
|
||||
files:
|
||||
folders: 文件夹
|
||||
files: 文件
|
||||
body: Body
|
||||
clear: 清空
|
||||
closePreview: 关闭预览
|
||||
home: 主页
|
||||
lastModified: 最后修改
|
||||
loading: 加载中...
|
||||
lonely: 这里没有任何文件...
|
||||
metadata: 元数据
|
||||
multipleSelectionEnabled: 多选模式已开启
|
||||
name: 名称
|
||||
size: 大小
|
||||
sortByName: 按名称排序
|
||||
sortBySize: 按大小排序
|
||||
sortByLastModified: 按最后修改时间排序
|
||||
help:
|
||||
click: 选择文件或目录
|
||||
ctrl:
|
||||
click: 选择多个文件或目录
|
||||
f: 打开搜索框
|
||||
s: 保存文件或下载当前文件夹
|
||||
del: 删除所选的文件/文件夹
|
||||
doubleClick: 打开文件/文件夹
|
||||
esc: 清除已选项或关闭提示信息
|
||||
f1: 显示该帮助信息
|
||||
f2: 重命名文件/文件夹
|
||||
help: 帮助
|
||||
login:
|
||||
password: 密码
|
||||
submit: 登录
|
||||
username: 用户名
|
||||
wrongCredentials: 用户名或密码错误
|
||||
prompts:
|
||||
copy: 复制
|
||||
copyMessage: 请选择欲复制至的目录:
|
||||
currentlyNavigating: 当前目录:
|
||||
deleteMessageMultiple: 你确定要删除这 {count} 个文件吗?
|
||||
deleteMessageSingle: 你确定要删除这个文件/文件夹吗?
|
||||
deleteTitle: 删除文件
|
||||
displayName: 名称:
|
||||
download: 下载文件
|
||||
downloadMessage: 请选择要下载的压缩格式。
|
||||
error: 出了一点问题...
|
||||
fileInfo: 文件信息
|
||||
filesSelected: 已选择 {count} 个文件。
|
||||
lastModified: 最后修改
|
||||
move: 移动
|
||||
moveMessage: 请选择欲移动至的目录:
|
||||
newDir: 新建目录
|
||||
newDirMessage: 请输入新目录的名称。
|
||||
newFile: 新建文件
|
||||
newFileMessage: 请输入新文件的名称。
|
||||
numberDirs: 目录数
|
||||
numberFiles: 文件数
|
||||
replace: 替换
|
||||
replaceMessage: "\
|
||||
您尝试上传的文件中有一个与现有文件的名称存在冲突。\
|
||||
是否替换现有的同名文件?"
|
||||
rename: 重命名
|
||||
renameMessage: 请输入新名称,旧名称为:
|
||||
show: 揭示
|
||||
size: 大小
|
||||
schedule: 计划
|
||||
scheduleMessage: 请选择发布这篇帖子的日期。
|
||||
newArchetype: 创建一个基于原型的新帖子。您的文件将会创建在内容文件夹中。
|
||||
settings:
|
||||
admin: 管理员
|
||||
administrator: 管理员
|
||||
allowCommands: 执行命令(Linux 代码)
|
||||
allowEdit: 编辑、重命名或删除文件/目录
|
||||
allowNew: 创建新文件和目录
|
||||
allowPublish: 发布新的帖子与页面
|
||||
avoidChanges: '(留空以避免更改)'
|
||||
changePassword: 更改密码
|
||||
commands: 命令(linux 代码)
|
||||
commandsHelp: "\
|
||||
在这里,您可以设置在指定事件下执行的命令,一行一条。\
|
||||
若事件与文件相关,如“在保存文件前”,\
|
||||
则文件的路径会被赋值给环境变量 \"FILE\"。"
|
||||
commandsUpdated: 命令已更新!
|
||||
customStylesheet: 自定义样式表
|
||||
examples: 例子
|
||||
globalSettings: 全局设置
|
||||
language: 语言
|
||||
lockPassword: 禁止用户修改密码
|
||||
newPassword: 您的新密码
|
||||
newPasswordConfirm: 重输一遍新密码
|
||||
newUser: 新建用户
|
||||
password: 密码
|
||||
passwordUpdated: 密码已更新!
|
||||
permissions: 权限
|
||||
permissionsHelp: "\
|
||||
您可以将该用户设置为管理员,也可以单独选择各项权限。\
|
||||
如果选择了“管理员”,则其他的选项会被自动勾上,\
|
||||
同时该用户可以管理其他用户。"
|
||||
profileSettings: 个人设置
|
||||
ruleExample1: "\
|
||||
阻止用户访问所有文件夹下任何以 . 开头的文件\
|
||||
(隐藏文件, 例如: .git, .gitignore)。"
|
||||
ruleExample2: 阻止用户访问其目录范围的根目录下名为 Caddyfile 的文件。
|
||||
rules: 规则
|
||||
rulesHelp1: "\
|
||||
您可以为该用户制定一组黑名单或白名单式的规则,\
|
||||
被屏蔽的文件将不会显示在列表中,用户也无权限访问,\
|
||||
支持相对于目录范围的路径。"
|
||||
rulesHelp2: "\
|
||||
每行一条规则,且必须以关键词 {0} 或 {1} 开头。\
|
||||
如要使用正则表达式,请在加上 {2} 之后再附上表达式或路径。"
|
||||
scope: 目录范围
|
||||
settingsUpdated: 设置已更新!
|
||||
user: 用户
|
||||
userCommands: 用户命令(Linux 代码)
|
||||
userCommandsHelp: "\
|
||||
指定该用户可以执行的命令(Linux 代码),用空格分隔。\
|
||||
例如:"
|
||||
userCreated: 用户已创建!
|
||||
userDeleted: 用户已删除!
|
||||
userManagement: 用户管理
|
||||
username: 用户名
|
||||
users: 用户
|
||||
userUpdated: 用户已更新!
|
||||
sidebar:
|
||||
help: 帮助
|
||||
logout: 登出
|
||||
myFiles: 我的文件
|
||||
newFile: 新建文件
|
||||
newFolder: 新建文件夹
|
||||
settings: 设置
|
||||
siteSettings: 网站设置
|
||||
hugoNew: Hugo New
|
||||
preview: 预览
|
||||
search:
|
||||
images: 图像
|
||||
music: 音乐
|
||||
pdf: PDF
|
||||
pressToExecute: 按回车键执行。
|
||||
pressToSearch: 按回车键搜索。
|
||||
search: 搜索...
|
||||
searchOrCommand: 搜索或者执行命令(Linux 代码)...
|
||||
searchOrSupportedCommand: 搜索或使用您可以使用的命令(一次只能执行一个命令):
|
||||
type: 键入并按回车键进行搜索。
|
||||
types: 类型
|
||||
video: 视频
|
||||
writeToSearch: 请输入要搜索的内容
|
||||
languages:
|
||||
en: English
|
||||
fr: Français
|
||||
pt: Português
|
||||
ja: 日本語
|
||||
zhCN: 中文 (简体)
|
||||
zhTW: 中文 (繁體)
|
||||
es: Español
|
||||
time:
|
||||
unit: 时间单位
|
||||
seconds: 秒
|
||||
minutes: 分钟
|
||||
hours: 小时
|
||||
days: 天
|
|
@ -1,199 +0,0 @@
|
|||
permanent: 永久
|
||||
buttons:
|
||||
cancel: 取消
|
||||
close: 關閉
|
||||
copy: 複製
|
||||
copyFile: 複製檔案
|
||||
copyToClipboard: 複製到剪貼簿
|
||||
create: 建立
|
||||
delete: 刪除
|
||||
download: 下載
|
||||
info: 資訊
|
||||
more: 更多
|
||||
move: 移動
|
||||
moveFile: 移動檔案
|
||||
new: 新
|
||||
next: 下一個
|
||||
ok: 確認
|
||||
replace: 更換
|
||||
previous: 上一個
|
||||
rename: 重新命名
|
||||
reportIssue: 報告問題
|
||||
save: 儲存
|
||||
search: 搜尋
|
||||
select: 選擇
|
||||
share: 分享
|
||||
publish: 發佈
|
||||
selectMultiple: 選擇多個
|
||||
schedule: 計畫
|
||||
switchView: 切換顯示方式
|
||||
toggleSidebar: 切換側邊欄
|
||||
update: 更新
|
||||
upload: 上傳
|
||||
permalink: 獲取永久連結
|
||||
success:
|
||||
linkCopied: 連結已複製!
|
||||
errors:
|
||||
forbidden: 你被禁止存取。
|
||||
internal: 內部出現麻煩了。
|
||||
notFound: 找不到檔案。
|
||||
files:
|
||||
folders: 資料夾
|
||||
files: 檔案
|
||||
body: Body
|
||||
clear: 清空
|
||||
closePreview: 關閉預覽
|
||||
home: 主頁
|
||||
lastModified: 最後修改
|
||||
loading: 讀取中...
|
||||
lonely: 這裡沒有任何檔案...
|
||||
metadata: 詮釋資料
|
||||
multipleSelectionEnabled: 多選模式已開啟
|
||||
name: 名稱
|
||||
size: 大小
|
||||
sortByName: 按名稱排序
|
||||
sortBySize: 按大小排序
|
||||
sortByLastModified: 按最後修改時間排序
|
||||
help:
|
||||
click: 選擇檔案或目錄
|
||||
ctrl:
|
||||
click: 選擇多個檔案或目錄
|
||||
f: 打開搜尋列
|
||||
s: 儲存檔案或下載目前資料夾
|
||||
del: 刪除所選的檔案/資料夾
|
||||
doubleClick: 打開檔案/資料夾
|
||||
esc: 清除已選項或關閉提示資訊
|
||||
f1: 顯示該幫助資訊
|
||||
f2: 重新命名檔案/資料夾
|
||||
help: 幫助
|
||||
login:
|
||||
password: 密碼
|
||||
submit: 登入
|
||||
username: 帳號
|
||||
wrongCredentials: 帳號或密碼錯誤
|
||||
prompts:
|
||||
copy: 複製
|
||||
copyMessage: 請選擇欲複製至的目錄:
|
||||
currentlyNavigating: 目前目錄:
|
||||
deleteMessageMultiple: 你確定要刪除這 {count} 個檔案嗎?
|
||||
deleteMessageSingle: 你確定要刪除這個檔案/資料夾嗎?
|
||||
deleteTitle: 刪除檔案
|
||||
displayName: 名稱:
|
||||
download: 下載檔案
|
||||
downloadMessage: 請選擇要下載的壓縮格式。
|
||||
error: 發出了一點錯誤...
|
||||
fileInfo: 檔案資訊
|
||||
filesSelected: 已選擇 {count} 個檔案。
|
||||
lastModified: 最後修改
|
||||
move: 移動
|
||||
moveMessage: 請選擇欲移動至的目錄:
|
||||
newDir: 建立目錄
|
||||
newDirMessage: 請輸入新目錄的名稱。
|
||||
newFile: 建立檔案
|
||||
newFileMessage: 請輸入新檔案的名稱。
|
||||
numberDirs: 目錄數
|
||||
numberFiles: 檔案數
|
||||
replace: 替換
|
||||
replaceMessage: "\
|
||||
您嘗試上傳的檔案中有一個與現有檔案的名稱存在衝突。\
|
||||
是否取代現有的同名檔案?"
|
||||
rename: 重新命名
|
||||
renameMessage: 請輸入新名稱,舊名稱為:
|
||||
show: 顯示
|
||||
size: 大小
|
||||
schedule: 計畫
|
||||
scheduleMessage: 請選擇發佈這篇貼文的日期。
|
||||
newArchetype: 建立一個基於原型的新貼文。您的檔案將會建立在內容資料夾中。
|
||||
settings:
|
||||
admin: 管理員
|
||||
administrator: 管理員
|
||||
allowCommands: 執行命令
|
||||
allowEdit: 編輯、重命名或刪除檔案/目錄
|
||||
allowNew: 創建新檔案和目錄
|
||||
allowPublish: 發佈新的貼文與頁面
|
||||
avoidChanges: '(留空以避免更改)'
|
||||
changePassword: 更改密碼
|
||||
commands: 命令
|
||||
commandsHelp: "\
|
||||
在這裡,您可以設定在指定事件下執行的命令,一行一條。\
|
||||
若事件與檔案相關,如“在保存檔案前”,\
|
||||
則檔案的路徑會被賦值給環境變數 \"FILE\"。"
|
||||
commandsUpdated: 命令已更新!
|
||||
customStylesheet: 自定義樣式表
|
||||
examples: 範例
|
||||
globalSettings: 全域設定
|
||||
language: 語言
|
||||
lockPassword: 禁止使用者修改密碼
|
||||
newPassword: 您的新密碼
|
||||
newPasswordConfirm: 重輸一遍新密碼
|
||||
newUser: 建立使用者
|
||||
password: 密碼
|
||||
passwordUpdated: 密碼已更新!
|
||||
permissions: 權限
|
||||
permissionsHelp: "\
|
||||
您可以將該使用者設置為管理員,也可以單獨選擇各項權限。\
|
||||
如果選擇了“管理員”,則其他的選項會被自動勾上,\
|
||||
同時該使用者可以管理其他使用者。"
|
||||
profileSettings: 個人設定
|
||||
ruleExample1: "\
|
||||
封鎖使用者存取所有資料夾下任何以 . 開頭的檔案\
|
||||
(隱藏文件, 例如: .git, .gitignore)。"
|
||||
ruleExample2: 封鎖使用者存取其目錄範圍的根目錄下名為 Caddyfile 的檔案。
|
||||
rules: 規則
|
||||
rulesHelp1: "\
|
||||
您可以為該使用者製定一組黑名單或白名單式的規則,\
|
||||
被屏蔽的檔案將不會顯示在清單中,使用者也無權限存取,\
|
||||
支持相對於目錄範圍的路徑。"
|
||||
rulesHelp2: "\
|
||||
每行一條規則,且必須以關鍵字 {0} 或 {1} 開頭。\
|
||||
如要使用規則運算式,請在加上 {2} 之後再附上運算式或路徑。"
|
||||
scope: 目錄範圍
|
||||
settingsUpdated: 設定已更新!
|
||||
user: 使用者
|
||||
userCommands: 使用者命令
|
||||
userCommandsHelp: "\
|
||||
指定該使用者可以執行的命令,用空格分隔。\
|
||||
例如:"
|
||||
userCreated: 使用者已建立!
|
||||
userDeleted: 使用者已刪除!
|
||||
userManagement: 使用者管理
|
||||
username: 使用者名稱
|
||||
users: 使用者
|
||||
userUpdated: 使用者已更新!
|
||||
sidebar:
|
||||
help: 幫助
|
||||
logout: 登出
|
||||
myFiles: 我的檔案
|
||||
newFile: 建立檔案
|
||||
newFolder: 建立資料夾
|
||||
settings: 設定
|
||||
siteSettings: 網站設定
|
||||
hugoNew: Hugo New
|
||||
preview: 預覽
|
||||
search:
|
||||
images: 影像
|
||||
music: 音樂
|
||||
pdf: PDF
|
||||
pressToExecute: 按確定鍵執行。
|
||||
pressToSearch: 按確定鍵搜尋。
|
||||
search: 搜尋...
|
||||
searchOrCommand: 搜尋或者執行命令...
|
||||
searchOrSupportedCommand: 搜尋或使用您可以使用的命令(一次只能執行一個命令):
|
||||
type: 輸入並按確定鍵進行搜尋。
|
||||
types: 類型
|
||||
video: 影片
|
||||
writeToSearch: 請輸入要搜尋的內容
|
||||
languages:
|
||||
en: English
|
||||
fr: Français
|
||||
pt: Português
|
||||
ja: 日本語
|
||||
zhCN: 中文 (简体)
|
||||
zhTW: 中文 (繁體)
|
||||
es: Español
|
||||
time:
|
||||
unit: 時間單位
|
||||
seconds: 秒
|
||||
minutes: 分鐘
|
||||
hours: 小時
|
||||
days: 天
|
|
@ -1,54 +0,0 @@
|
|||
import Vue from 'vue'
|
||||
import App from './App'
|
||||
import store from './store'
|
||||
import router from './router'
|
||||
import i18n from './i18n'
|
||||
import Noty from 'noty'
|
||||
|
||||
Vue.config.productionTip = true
|
||||
|
||||
const notyDefault = {
|
||||
type: 'info',
|
||||
layout: 'bottomRight',
|
||||
timeout: 1000,
|
||||
progressBar: true
|
||||
}
|
||||
|
||||
Vue.prototype.$noty = function (opts) {
|
||||
new Noty(Object.assign({}, notyDefault, opts)).show()
|
||||
}
|
||||
|
||||
Vue.prototype.$showSuccess = function (message) {
|
||||
new Noty(Object.assign({}, notyDefault, {
|
||||
text: message,
|
||||
type: 'success'
|
||||
})).show()
|
||||
}
|
||||
|
||||
Vue.prototype.$showError = function (error) {
|
||||
let n = new Noty(Object.assign({}, notyDefault, {
|
||||
text: error,
|
||||
type: 'error',
|
||||
timeout: null,
|
||||
buttons: [
|
||||
Noty.button(i18n.t('buttons.reportIssue'), '', function () {
|
||||
window.open('https://github.com/hacdias/filemanager/issues/new')
|
||||
}),
|
||||
Noty.button(i18n.t('buttons.close'), '', function () {
|
||||
n.close()
|
||||
})
|
||||
]
|
||||
}))
|
||||
|
||||
n.show()
|
||||
}
|
||||
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
el: '#app',
|
||||
store,
|
||||
router,
|
||||
i18n,
|
||||
template: '<App/>',
|
||||
components: { App }
|
||||
})
|
|
@ -1,161 +0,0 @@
|
|||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import Login from '@/views/Login'
|
||||
import Layout from '@/views/Layout'
|
||||
import Files from '@/views/Files'
|
||||
import Users from '@/views/settings/Users'
|
||||
import User from '@/views/settings/User'
|
||||
import Settings from '@/views/Settings'
|
||||
import GlobalSettings from '@/views/settings/Global'
|
||||
import ProfileSettings from '@/views/settings/Profile'
|
||||
import Error403 from '@/views/errors/403'
|
||||
import Error404 from '@/views/errors/404'
|
||||
import Error500 from '@/views/errors/500'
|
||||
import auth from '@/utils/auth'
|
||||
import store from '@/store'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
const router = new Router({
|
||||
base: document.querySelector('meta[name="base"]').getAttribute('content'),
|
||||
mode: 'history',
|
||||
routes: [
|
||||
{
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
component: Login,
|
||||
beforeEnter: function (to, from, next) {
|
||||
auth.loggedIn()
|
||||
.then(() => {
|
||||
next({ path: '/files' })
|
||||
})
|
||||
.catch(() => {
|
||||
document.title = 'Login'
|
||||
next()
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/*',
|
||||
component: Layout,
|
||||
meta: {
|
||||
requiresAuth: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/files/*',
|
||||
name: 'Files',
|
||||
component: Files
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
name: 'Settings',
|
||||
component: Settings,
|
||||
redirect: {
|
||||
path: '/settings/profile'
|
||||
},
|
||||
meta: {
|
||||
disableOnNoAuth: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/settings/profile',
|
||||
name: 'Profile Settings',
|
||||
component: ProfileSettings
|
||||
},
|
||||
{
|
||||
path: '/settings/global',
|
||||
name: 'Global Settings',
|
||||
component: GlobalSettings,
|
||||
meta: {
|
||||
requiresAdmin: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/settings/users',
|
||||
name: 'Users',
|
||||
component: Users,
|
||||
meta: {
|
||||
requiresAdmin: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/settings/users/*',
|
||||
name: 'User',
|
||||
component: User,
|
||||
meta: {
|
||||
requiresAdmin: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/403',
|
||||
name: 'Forbidden',
|
||||
component: Error403
|
||||
},
|
||||
{
|
||||
path: '/404',
|
||||
name: 'Not Found',
|
||||
component: Error404
|
||||
},
|
||||
{
|
||||
path: '/500',
|
||||
name: 'Internal Server Error',
|
||||
component: Error500
|
||||
},
|
||||
{
|
||||
path: '/files',
|
||||
redirect: {
|
||||
path: '/files/'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/*',
|
||||
redirect: {
|
||||
name: 'Files'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
document.title = to.name
|
||||
|
||||
if (to.matched.some(record => record.meta.requiresAuth)) {
|
||||
// this route requires auth, check if logged in
|
||||
// if not, redirect to login page.
|
||||
auth.loggedIn()
|
||||
.then(() => {
|
||||
if (to.matched.some(record => record.meta.requiresAdmin)) {
|
||||
if (!store.state.user.admin) {
|
||||
next({ path: '/403' })
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (to.matched.some(record => record.meta.disableOnNoAuth)) {
|
||||
if (store.state.noAuth) {
|
||||
next({ path: '/403' })
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
.catch(e => {
|
||||
next({
|
||||
path: '/login',
|
||||
query: { redirect: to.fullPath }
|
||||
})
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
export default router
|
|
@ -1,5 +0,0 @@
|
|||
const getters = {
|
||||
selectedCount: state => state.selected.length
|
||||
}
|
||||
|
||||
export default getters
|
|
@ -1,42 +0,0 @@
|
|||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import mutations from './mutations'
|
||||
import getters from './getters'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
const state = {
|
||||
user: {},
|
||||
req: {},
|
||||
clipboard: {
|
||||
key: '',
|
||||
items: []
|
||||
},
|
||||
css: (() => {
|
||||
let css = window.CSS
|
||||
window.CSS = null
|
||||
return css
|
||||
})(),
|
||||
recaptcha: document.querySelector('meta[name="recaptcha"]').getAttribute('content'),
|
||||
staticGen: document.querySelector('meta[name="staticgen"]').getAttribute('content'),
|
||||
baseURL: document.querySelector('meta[name="base"]').getAttribute('content'),
|
||||
noAuth: (document.querySelector('meta[name="noauth"]').getAttribute('content') === 'true'),
|
||||
version: document.querySelector('meta[name="version"]').getAttribute('content'),
|
||||
jwt: '',
|
||||
progress: 0,
|
||||
schedule: '',
|
||||
loading: false,
|
||||
reload: false,
|
||||
selected: [],
|
||||
multiple: false,
|
||||
show: null,
|
||||
showMessage: null,
|
||||
showConfirm: null
|
||||
}
|
||||
|
||||
export default new Vuex.Store({
|
||||
strict: process.env.NODE_ENV !== 'production',
|
||||
state,
|
||||
getters,
|
||||
mutations
|
||||
})
|
|
@ -1,81 +0,0 @@
|
|||
import * as i18n from '@/i18n'
|
||||
import moment from 'moment'
|
||||
|
||||
const mutations = {
|
||||
closeHovers: state => {
|
||||
state.show = null
|
||||
state.showMessage = null
|
||||
},
|
||||
showHover: (state, value) => {
|
||||
if (typeof value !== 'object') {
|
||||
state.show = value
|
||||
return
|
||||
}
|
||||
|
||||
state.show = value.prompt
|
||||
state.showMessage = value.message
|
||||
state.showConfirm = value.confirm
|
||||
},
|
||||
showError: (state, value) => {
|
||||
state.show = 'error'
|
||||
state.showMessage = value
|
||||
},
|
||||
showSuccess: (state, value) => {
|
||||
state.show = 'success'
|
||||
state.showMessage = value
|
||||
},
|
||||
setLoading: (state, value) => { state.loading = value },
|
||||
setReload: (state, value) => { state.reload = value },
|
||||
setUser: (state, value) => {
|
||||
let locale = value.locale
|
||||
|
||||
if (locale === '') {
|
||||
locale = i18n.detectLocale()
|
||||
}
|
||||
|
||||
moment.locale(locale)
|
||||
i18n.default.locale = locale
|
||||
state.user = value
|
||||
},
|
||||
setCSS: (state, value) => (state.css = value),
|
||||
setJWT: (state, value) => (state.jwt = value),
|
||||
multiple: (state, value) => (state.multiple = value),
|
||||
addSelected: (state, value) => (state.selected.push(value)),
|
||||
addPlugin: (state, value) => {
|
||||
state.plugins.push(value)
|
||||
},
|
||||
removeSelected: (state, value) => {
|
||||
let i = state.selected.indexOf(value)
|
||||
if (i === -1) return
|
||||
state.selected.splice(i, 1)
|
||||
},
|
||||
resetSelected: (state) => {
|
||||
state.selected = []
|
||||
},
|
||||
updateUser: (state, value) => {
|
||||
if (typeof value !== 'object') return
|
||||
|
||||
for (let field in value) {
|
||||
state.user[field] = value[field]
|
||||
}
|
||||
},
|
||||
updateRequest: (state, value) => {
|
||||
state.req = value
|
||||
},
|
||||
updateClipboard: (state, value) => {
|
||||
state.clipboard.key = value.key
|
||||
state.clipboard.items = value.items
|
||||
},
|
||||
resetClipboard: (state) => {
|
||||
state.clipboard.key = ''
|
||||
state.clipboard.items = []
|
||||
},
|
||||
setSchedule: (state, value) => {
|
||||
state.schedule = value
|
||||
},
|
||||
setProgress: (state, value) => {
|
||||
state.progress = value
|
||||
}
|
||||
}
|
||||
|
||||
export default mutations
|
|
@ -1,455 +0,0 @@
|
|||
import store from '@/store'
|
||||
|
||||
const ssl = (window.location.protocol === 'https:')
|
||||
|
||||
export function removePrefix (url) {
|
||||
if (url.startsWith('/files')) {
|
||||
url = url.slice(6)
|
||||
}
|
||||
|
||||
if (url === '') url = '/'
|
||||
if (url[0] !== '/') url = '/' + url
|
||||
return url
|
||||
}
|
||||
|
||||
export function fetch (url) {
|
||||
url = removePrefix(url)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('GET', `${store.state.baseURL}/api/resource${url}`, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
|
||||
request.onload = () => {
|
||||
switch (request.status) {
|
||||
case 200:
|
||||
resolve(JSON.parse(request.responseText))
|
||||
break
|
||||
default:
|
||||
reject(new Error(request.status))
|
||||
break
|
||||
}
|
||||
}
|
||||
request.onerror = (error) => reject(error)
|
||||
request.send()
|
||||
})
|
||||
}
|
||||
|
||||
export function remove (url) {
|
||||
url = removePrefix(url)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('DELETE', `${store.state.baseURL}/api/resource${url}`, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
|
||||
request.onload = () => {
|
||||
if (request.status === 200) {
|
||||
resolve(request.responseText)
|
||||
} else {
|
||||
reject(request.responseText)
|
||||
}
|
||||
}
|
||||
|
||||
request.onerror = (error) => reject(error)
|
||||
request.send()
|
||||
})
|
||||
}
|
||||
|
||||
export function post (url, content = '', overwrite = false, onupload) {
|
||||
url = removePrefix(url)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('POST', `${store.state.baseURL}/api/resource${url}`, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
|
||||
if (typeof onupload === 'function') {
|
||||
request.upload.onprogress = onupload
|
||||
}
|
||||
|
||||
if (overwrite) {
|
||||
request.setRequestHeader('Action', `override`)
|
||||
}
|
||||
|
||||
request.onload = () => {
|
||||
if (request.status === 200) {
|
||||
resolve(request.responseText)
|
||||
} else if (request.status === 409) {
|
||||
reject(request.status)
|
||||
} else {
|
||||
reject(request.responseText)
|
||||
}
|
||||
}
|
||||
|
||||
request.onerror = (error) => {
|
||||
reject(error)
|
||||
}
|
||||
request.send(content)
|
||||
})
|
||||
}
|
||||
|
||||
export function put (url, content = '', publish = false, date = '') {
|
||||
url = removePrefix(url)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('PUT', `${store.state.baseURL}/api/resource${url}`, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
request.setRequestHeader('Publish', publish)
|
||||
|
||||
if (date !== '') {
|
||||
request.setRequestHeader('Schedule', date)
|
||||
}
|
||||
|
||||
request.onload = () => {
|
||||
if (request.status === 200) {
|
||||
resolve(request.responseText)
|
||||
} else {
|
||||
reject(request.responseText)
|
||||
}
|
||||
}
|
||||
|
||||
request.onerror = (error) => reject(error)
|
||||
request.send(content)
|
||||
})
|
||||
}
|
||||
|
||||
function moveCopy (items, copy = false) {
|
||||
let promises = []
|
||||
|
||||
for (let item of items) {
|
||||
let from = removePrefix(item.from)
|
||||
let to = removePrefix(item.to)
|
||||
|
||||
promises.push(new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('PATCH', `${store.state.baseURL}/api/resource${from}`, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
request.setRequestHeader('Destination', to)
|
||||
|
||||
if (copy) {
|
||||
request.setRequestHeader('Action', 'copy')
|
||||
}
|
||||
|
||||
request.onload = () => {
|
||||
if (request.status === 200) {
|
||||
resolve(request.responseText)
|
||||
} else {
|
||||
reject(request.responseText)
|
||||
}
|
||||
}
|
||||
|
||||
request.onerror = (error) => reject(error)
|
||||
request.send()
|
||||
}))
|
||||
}
|
||||
|
||||
return Promise.all(promises)
|
||||
}
|
||||
|
||||
export function move (items) {
|
||||
return moveCopy(items)
|
||||
}
|
||||
|
||||
export function copy (items) {
|
||||
return moveCopy(items, true)
|
||||
}
|
||||
|
||||
export function checksum (url, algo) {
|
||||
url = removePrefix(url)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('GET', `${store.state.baseURL}/api/checksum${url}?algo=${algo}`, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
|
||||
request.onload = () => {
|
||||
if (request.status === 200) {
|
||||
resolve(request.responseText)
|
||||
} else {
|
||||
reject(request.responseText)
|
||||
}
|
||||
}
|
||||
request.onerror = (error) => reject(error)
|
||||
request.send()
|
||||
})
|
||||
}
|
||||
|
||||
export function command (url, command, onmessage, onclose) {
|
||||
let protocol = (ssl ? 'wss:' : 'ws:')
|
||||
url = removePrefix(url)
|
||||
url = `${protocol}//${window.location.host}${store.state.baseURL}/api/command${url}`
|
||||
|
||||
let conn = new window.WebSocket(url)
|
||||
conn.onopen = () => conn.send(command)
|
||||
conn.onmessage = onmessage
|
||||
conn.onclose = onclose
|
||||
}
|
||||
|
||||
export function search (url, search, onmessage, onclose) {
|
||||
let protocol = (ssl ? 'wss:' : 'ws:')
|
||||
url = removePrefix(url)
|
||||
url = `${protocol}//${window.location.host}${store.state.baseURL}/api/search${url}`
|
||||
|
||||
let conn = new window.WebSocket(url)
|
||||
conn.onopen = () => conn.send(search)
|
||||
conn.onmessage = onmessage
|
||||
conn.onclose = onclose
|
||||
}
|
||||
|
||||
export function download (format, ...files) {
|
||||
let url = `${store.state.baseURL}/api/download`
|
||||
|
||||
if (files.length === 1) {
|
||||
url += removePrefix(files[0]) + '?'
|
||||
} else {
|
||||
let arg = ''
|
||||
|
||||
for (let file of files) {
|
||||
arg += removePrefix(file) + ','
|
||||
}
|
||||
|
||||
arg = arg.substring(0, arg.length - 1)
|
||||
arg = encodeURIComponent(arg)
|
||||
url += `/?files=${arg}&`
|
||||
}
|
||||
|
||||
if (format !== null) {
|
||||
url += `&format=${format}`
|
||||
}
|
||||
|
||||
window.open(url)
|
||||
}
|
||||
|
||||
export function getSettings () {
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('GET', `${store.state.baseURL}/api/settings/`, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
|
||||
request.onload = () => {
|
||||
switch (request.status) {
|
||||
case 200:
|
||||
resolve(JSON.parse(request.responseText))
|
||||
break
|
||||
default:
|
||||
reject(request.responseText)
|
||||
break
|
||||
}
|
||||
}
|
||||
request.onerror = (error) => reject(error)
|
||||
request.send()
|
||||
})
|
||||
}
|
||||
|
||||
export function updateSettings (param, which) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let data = {
|
||||
what: 'settings',
|
||||
which: which,
|
||||
data: {}
|
||||
}
|
||||
|
||||
data.data[which] = param
|
||||
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('PUT', `${store.state.baseURL}/api/settings/`, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
|
||||
request.onload = () => {
|
||||
switch (request.status) {
|
||||
case 200:
|
||||
resolve()
|
||||
break
|
||||
default:
|
||||
reject(request.responseText)
|
||||
break
|
||||
}
|
||||
}
|
||||
request.onerror = (error) => { reject(error) }
|
||||
request.send(JSON.stringify(data))
|
||||
})
|
||||
}
|
||||
|
||||
// USERS
|
||||
|
||||
export function getUsers () {
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('GET', `${store.state.baseURL}/api/users/`, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
|
||||
request.onload = () => {
|
||||
switch (request.status) {
|
||||
case 200:
|
||||
resolve(JSON.parse(request.responseText))
|
||||
break
|
||||
default:
|
||||
reject(request.responseText)
|
||||
break
|
||||
}
|
||||
}
|
||||
request.onerror = (error) => reject(error)
|
||||
request.send()
|
||||
})
|
||||
}
|
||||
|
||||
export function getUser (id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('GET', `${store.state.baseURL}/api/users/${id}`, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
|
||||
request.onload = () => {
|
||||
switch (request.status) {
|
||||
case 200:
|
||||
resolve(JSON.parse(request.responseText))
|
||||
break
|
||||
default:
|
||||
reject(request.responseText)
|
||||
break
|
||||
}
|
||||
}
|
||||
request.onerror = (error) => reject(error)
|
||||
request.send()
|
||||
})
|
||||
}
|
||||
|
||||
export function newUser (user) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('POST', `${store.state.baseURL}/api/users/`, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
|
||||
request.onload = () => {
|
||||
switch (request.status) {
|
||||
case 201:
|
||||
resolve(request.getResponseHeader('Location'))
|
||||
break
|
||||
default:
|
||||
reject(request.responseText)
|
||||
break
|
||||
}
|
||||
}
|
||||
request.onerror = (error) => reject(error)
|
||||
request.send(JSON.stringify({
|
||||
what: 'user',
|
||||
which: 'new',
|
||||
data: user
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
export function updateUser (user, which) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('PUT', `${store.state.baseURL}/api/users/${user.ID}`, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
|
||||
request.onload = () => {
|
||||
switch (request.status) {
|
||||
case 200:
|
||||
resolve(request.getResponseHeader('Location'))
|
||||
break
|
||||
default:
|
||||
reject(request.responseText)
|
||||
break
|
||||
}
|
||||
}
|
||||
request.onerror = (error) => reject(error)
|
||||
request.send(JSON.stringify({
|
||||
what: 'user',
|
||||
which: (typeof which === 'string') ? which : 'all',
|
||||
data: user
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteUser (id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('DELETE', `${store.state.baseURL}/api/users/${id}`, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
|
||||
request.onload = () => {
|
||||
switch (request.status) {
|
||||
case 200:
|
||||
resolve()
|
||||
break
|
||||
default:
|
||||
reject(request.responseText)
|
||||
break
|
||||
}
|
||||
}
|
||||
request.onerror = (error) => reject(error)
|
||||
request.send()
|
||||
})
|
||||
}
|
||||
|
||||
// SHARE
|
||||
|
||||
export function getShare (url) {
|
||||
url = removePrefix(url)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('GET', `${store.state.baseURL}/api/share${url}`, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
|
||||
request.onload = () => {
|
||||
if (request.status === 200) {
|
||||
resolve(JSON.parse(request.responseText))
|
||||
} else {
|
||||
reject(request.status)
|
||||
}
|
||||
}
|
||||
|
||||
request.onerror = (error) => reject(error)
|
||||
request.send()
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteShare (hash) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('DELETE', `${store.state.baseURL}/api/share/${hash}`, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
|
||||
request.onload = () => {
|
||||
if (request.status === 200) {
|
||||
resolve()
|
||||
} else {
|
||||
reject(request.status)
|
||||
}
|
||||
}
|
||||
|
||||
request.onerror = (error) => reject(error)
|
||||
request.send()
|
||||
})
|
||||
}
|
||||
|
||||
export function share (url, expires = '', unit = 'hours') {
|
||||
url = removePrefix(url)
|
||||
url = `${store.state.baseURL}/api/share${url}`
|
||||
if (expires !== '') {
|
||||
url += `?expires=${expires}&unit=${unit}`
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('POST', url, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
|
||||
request.onload = () => {
|
||||
if (request.status === 200) {
|
||||
resolve(JSON.parse(request.responseText))
|
||||
} else {
|
||||
reject(request.responseStatus)
|
||||
}
|
||||
}
|
||||
|
||||
request.onerror = (error) => reject(error)
|
||||
request.send()
|
||||
})
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
import cookie from './cookie'
|
||||
import store from '@/store'
|
||||
import router from '@/router'
|
||||
import { Base64 } from 'js-base64'
|
||||
|
||||
function parseToken (token) {
|
||||
let path = store.state.baseURL
|
||||
if (path === '') path = '/'
|
||||
document.cookie = `auth=${token}; max-age=86400; path=${path}`
|
||||
let res = token.split('.')
|
||||
let user = JSON.parse(Base64.decode(res[1]))
|
||||
if (!user.commands) {
|
||||
user.commands = []
|
||||
}
|
||||
|
||||
store.commit('setJWT', token)
|
||||
store.commit('setUser', user)
|
||||
}
|
||||
|
||||
function loggedIn () {
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('GET', `${store.state.baseURL}/api/auth/renew`, true)
|
||||
if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${cookie('auth')}`)
|
||||
|
||||
request.onload = () => {
|
||||
if (request.status === 200) {
|
||||
parseToken(request.responseText)
|
||||
resolve()
|
||||
} else {
|
||||
reject(new Error(request.responseText))
|
||||
}
|
||||
}
|
||||
request.onerror = () => reject(new Error('Could not finish the request'))
|
||||
request.send()
|
||||
})
|
||||
}
|
||||
|
||||
function login (user, password, captcha) {
|
||||
let data = {username: user, password: password, recaptcha: captcha}
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('POST', `${store.state.baseURL}/api/auth/get`, true)
|
||||
|
||||
request.onload = () => {
|
||||
if (request.status === 200) {
|
||||
parseToken(request.responseText)
|
||||
resolve()
|
||||
} else {
|
||||
reject(request.responseText)
|
||||
}
|
||||
}
|
||||
request.onerror = () => reject(new Error('Could not finish the request'))
|
||||
request.send(JSON.stringify(data))
|
||||
})
|
||||
}
|
||||
|
||||
function logout () {
|
||||
let path = store.state.baseURL
|
||||
if (path === '') path = '/'
|
||||
document.cookie = `auth='nothing'; max-age=0; path=${path}`
|
||||
router.push({path: '/login'})
|
||||
}
|
||||
|
||||
export default {
|
||||
loggedIn: loggedIn,
|
||||
login: login,
|
||||
logout: logout
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
function loading (button) {
|
||||
let el = document.querySelector(`#${button}-button > i`)
|
||||
|
||||
if (el === undefined || el === null) {
|
||||
console.log('Error getting button ' + button)
|
||||
return
|
||||
}
|
||||
|
||||
el.dataset.icon = el.innerHTML
|
||||
el.style.opacity = 0
|
||||
|
||||
setTimeout(() => {
|
||||
el.classList.add('spin')
|
||||
el.innerHTML = 'autorenew'
|
||||
el.style.opacity = 1
|
||||
}, 100)
|
||||
}
|
||||
|
||||
function done (button) {
|
||||
let el = document.querySelector(`#${button}-button > i`)
|
||||
|
||||
if (el === undefined || el === null) {
|
||||
console.log('Error getting button ' + button)
|
||||
return
|
||||
}
|
||||
|
||||
el.style.opacity = 0
|
||||
|
||||
setTimeout(() => {
|
||||
el.classList.remove('spin')
|
||||
el.innerHTML = el.dataset.icon
|
||||
el.style.opacity = 1
|
||||
}, 100)
|
||||
}
|
||||
|
||||
function success (button) {
|
||||
let el = document.querySelector(`#${button}-button > i`)
|
||||
|
||||
if (el === undefined || el === null) {
|
||||
console.log('Error getting button ' + button)
|
||||
return
|
||||
}
|
||||
|
||||
el.style.opacity = 0
|
||||
|
||||
setTimeout(() => {
|
||||
el.classList.remove('spin')
|
||||
el.innerHTML = 'done'
|
||||
el.style.opacity = 1
|
||||
|
||||
setTimeout(() => {
|
||||
el.style.opacity = 0
|
||||
|
||||
setTimeout(() => {
|
||||
el.innerHTML = el.dataset.icon
|
||||
el.style.opacity = 1
|
||||
}, 100)
|
||||
}, 500)
|
||||
}, 100)
|
||||
}
|
||||
|
||||
export default {
|
||||
loading,
|
||||
done,
|
||||
success
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
// Most of the code from this file comes from:
|
||||
// https://github.com/codemirror/CodeMirror/blob/master/addon/mode/loadmode.js
|
||||
import * as CodeMirror from 'codemirror'
|
||||
import store from '@/store'
|
||||
|
||||
// Make CodeMirror available globally so the modes' can register themselves.
|
||||
window.CodeMirror = CodeMirror
|
||||
CodeMirror.modeURL = store.state.baseURL + '/static/js/codemirror/mode/%N/%N.js'
|
||||
|
||||
var loading = {}
|
||||
|
||||
function splitCallback (cont, n) {
|
||||
var countDown = n
|
||||
return function () {
|
||||
if (--countDown === 0) cont()
|
||||
}
|
||||
}
|
||||
|
||||
function ensureDeps (mode, cont) {
|
||||
var deps = CodeMirror.modes[mode].dependencies
|
||||
if (!deps) return cont()
|
||||
var missing = []
|
||||
for (var i = 0; i < deps.length; ++i) {
|
||||
if (!CodeMirror.modes.hasOwnProperty(deps[i])) missing.push(deps[i])
|
||||
}
|
||||
if (!missing.length) return cont()
|
||||
var split = splitCallback(cont, missing.length)
|
||||
for (i = 0; i < missing.length; ++i) CodeMirror.requireMode(missing[i], split)
|
||||
}
|
||||
|
||||
CodeMirror.requireMode = function (mode, cont) {
|
||||
if (typeof mode !== 'string') mode = mode.name
|
||||
if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont)
|
||||
if (loading.hasOwnProperty(mode)) return loading[mode].push(cont)
|
||||
|
||||
var file = CodeMirror.modeURL.replace(/%N/g, mode)
|
||||
|
||||
var script = document.createElement('script')
|
||||
script.src = file
|
||||
var others = document.getElementsByTagName('script')[0]
|
||||
var list = loading[mode] = [cont]
|
||||
|
||||
CodeMirror.on(script, 'load', function () {
|
||||
ensureDeps(mode, function () {
|
||||
for (var i = 0; i < list.length; ++i) list[i]()
|
||||
})
|
||||
})
|
||||
|
||||
others.parentNode.insertBefore(script, others)
|
||||
}
|
||||
|
||||
CodeMirror.autoLoadMode = function (instance, mode) {
|
||||
if (CodeMirror.modes.hasOwnProperty(mode)) return
|
||||
|
||||
CodeMirror.requireMode(mode, function () {
|
||||
instance.setOption('mode', mode)
|
||||
})
|
||||
}
|
||||
|
||||
export default CodeMirror
|
|
@ -1,4 +0,0 @@
|
|||
export default function (name) {
|
||||
let re = new RegExp('(?:(?:^|.*;\\s*)' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$')
|
||||
return document.cookie.replace(re, '$1')
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue