mirror of
https://github.com/Luzifer/share.git
synced 2024-12-20 10:31:16 +00:00
Re-vamp frontend files
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
89944d242f
commit
81060aba97
12 changed files with 9512 additions and 824 deletions
8980
bindata.go
8980
bindata.go
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
@ -6,77 +6,17 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css"
|
||||
integrity="sha256-LA89z+k9fjgMKQ/kq4OO2Mrf8VltYml/VES+Rg0fh20=" crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/4.0.0/darkly/bootstrap.min.css"
|
||||
integrity="sha256-bhD4/DdCApNAYlOpvKssUa2hD0Du8xrgOvtY4w25shM=" crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/combine/npm/bootstrap@4.3.1/dist/css/bootstrap.min.css,npm/bootstrap-vue@2.0.0-rc.19/dist/bootstrap-vue.min.css,npm/bootswatch@4.3.1/dist/darkly/bootstrap.min.css,npm/highlight.js@9.15.6/styles/androidstudio.min.css">
|
||||
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.10/css/all.css"
|
||||
integrity="sha384-+d0P83n9kaQMCwj8F4RJB66tzIwOKmrdb46+porD/OvrJ+37WqIM7UoBtwHO6Nlg" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/androidstudio.min.css"
|
||||
integrity="sha256-rEVnyeMfkKzmBwocdeeiqGOu1M/ttUHB7K0meKIwqvw=" crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css"
|
||||
integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
|
||||
|
||||
<title>Share</title>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<a class="navbar-brand" href="#"><i class="fas fa-share-alt-square"></i>
|
||||
<span class="apptitle">Share</span></a>
|
||||
</nav>
|
||||
|
||||
<div class="container show-loading">
|
||||
<h1>
|
||||
<i class="fas fa-spinner fa-pulse" aria-hidden="true"></i>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="container show-generic">
|
||||
<h1>
|
||||
<i class="fas fa-cloud-download-alt"></i>
|
||||
</h1>
|
||||
<h2><a href="#" class="filelink-href filename">untitled</a></h2>
|
||||
</div>
|
||||
|
||||
<div class="container show-image text-center">
|
||||
<a href="#" class="filelink-href">
|
||||
<img src="" class="img-fluid filelink-src">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="container show-video">
|
||||
<div class="embed-responsive embed-responsive-16by9">
|
||||
<video controls>
|
||||
</video>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container show-audio text-center">
|
||||
<audio controls></audio>
|
||||
</div>
|
||||
|
||||
<div class="container show-text">
|
||||
<pre><code></code></pre>
|
||||
</div>
|
||||
|
||||
<div class="container show-error">
|
||||
<h1>
|
||||
<i class="fas fa-exclamation-circle"></i>
|
||||
</h1>
|
||||
<h2 class="error"></h2>
|
||||
</div>
|
||||
|
||||
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
|
||||
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.1/popper.min.js"
|
||||
integrity="sha256-ST2MecrXrJaAsqmfpk9XRQITlDoyMmUtgKBEndDisSc=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/js/bootstrap.min.js"
|
||||
integrity="sha256-5+02zu5UULQkO7w1GIr6vftCgMfFdZcAHeDtFnKZsBs=" crossorigin="anonymous"></script>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"
|
||||
integrity="sha256-/BfiIkHlHoVihZdc6TFuj7MmJ0TWcWsMXkeDFwhi0zw=" crossorigin="anonymous"></script>
|
||||
<div id="app"></div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/combine/npm/vue@2.6.10,npm/bootstrap-vue@2.0.0-rc.19/dist/bootstrap-vue.min.js,npm/axios@0.18.0/dist/axios.min.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
89
src/.eslintrc.js
Normal file
89
src/.eslintrc.js
Normal file
|
@ -0,0 +1,89 @@
|
|||
// https://eslint.org/docs/user-guide/configuring
|
||||
|
||||
module.exports = {
|
||||
'root': true,
|
||||
'parserOptions': {
|
||||
parser: 'babel-eslint',
|
||||
sourceType: 'module',
|
||||
},
|
||||
'env': {
|
||||
node: true,
|
||||
},
|
||||
'extends': [
|
||||
/*
|
||||
* https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
|
||||
* consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
|
||||
*/
|
||||
'plugin:vue/essential',
|
||||
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
|
||||
'eslint:recommended',
|
||||
],
|
||||
// required to lint *.vue files
|
||||
'plugins': ['vue'],
|
||||
'globals': {
|
||||
locale: true,
|
||||
process: true,
|
||||
version: true,
|
||||
},
|
||||
// add your custom rules here
|
||||
'rules': {
|
||||
'array-bracket-newline': ['error', { multiline: true }],
|
||||
'array-bracket-spacing': ['error'],
|
||||
'arrow-body-style': ['error', 'as-needed'],
|
||||
'arrow-parens': ['error', 'as-needed'],
|
||||
'arrow-spacing': ['error', { before: true, after: true }],
|
||||
'block-spacing': ['error'],
|
||||
'brace-style': ['error', '1tbs'],
|
||||
'comma-dangle': ['error', 'always-multiline'], // Apply Contentflow rules
|
||||
'comma-spacing': ['error'],
|
||||
'comma-style': ['error', 'last'],
|
||||
'curly': ['error'],
|
||||
'dot-location': ['error', 'property'],
|
||||
'dot-notation': ['error'],
|
||||
'eol-last': ['error', 'always'],
|
||||
'eqeqeq': ['error', 'always', { 'null': 'ignore' }],
|
||||
'func-call-spacing': ['error', 'never'],
|
||||
'function-paren-newline': ['error', 'multiline'],
|
||||
'generator-star-spacing': ['off'], // allow async-await
|
||||
'implicit-arrow-linebreak': ['error'],
|
||||
'indent': ['error', 2],
|
||||
'key-spacing': ['error', { beforeColon: false, afterColon: true, mode: 'strict' }],
|
||||
'keyword-spacing': ['error'],
|
||||
'linebreak-style': ['error', 'unix'],
|
||||
'lines-between-class-members': ['error'],
|
||||
'multiline-comment-style': ['warn'],
|
||||
'newline-per-chained-call': ['error'],
|
||||
'no-console': ['off'],
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', // allow debugger during development
|
||||
'no-else-return': ['error'],
|
||||
'no-extra-parens': ['error'],
|
||||
'no-implicit-coercion': ['error'],
|
||||
'no-lonely-if': ['error'],
|
||||
'no-multiple-empty-lines': ['warn', { max: 2, maxEOF: 0, maxBOF: 0 }],
|
||||
'no-multi-spaces': ['error'],
|
||||
'no-trailing-spaces': ['error'],
|
||||
'no-unneeded-ternary': ['error'],
|
||||
'no-useless-return': ['error'],
|
||||
'no-whitespace-before-property': ['error'],
|
||||
'object-curly-newline': ['error', { consistent: true }],
|
||||
'object-curly-spacing': ['error', 'always'],
|
||||
'object-shorthand': ['error'],
|
||||
'padded-blocks': ['error', 'never'],
|
||||
'prefer-arrow-callback': ['error'],
|
||||
'prefer-const': ['error'],
|
||||
'prefer-object-spread': ['error'],
|
||||
'prefer-template': ['error'],
|
||||
'quote-props': ['error', 'consistent-as-needed', { keywords: true }],
|
||||
'quotes': ['error', 'single', { allowTemplateLiterals: true }],
|
||||
'semi': ['error', 'never'],
|
||||
'space-before-blocks': ['error', 'always'],
|
||||
'spaced-comment': ['warn', 'always'],
|
||||
'space-infix-ops': ['error'],
|
||||
'space-in-parens': ['error', 'never'],
|
||||
'space-unary-ops': ['error', { words: true, nonwords: false }],
|
||||
'switch-colon-spacing': ['error'],
|
||||
'unicode-bom': ['error', 'never'],
|
||||
'wrap-iife': ['error'],
|
||||
'yoda': ['error'],
|
||||
},
|
||||
}
|
120
src/app.js
120
src/app.js
|
@ -1,120 +0,0 @@
|
|||
import './style.scss'
|
||||
|
||||
let fileURL = null;
|
||||
const MSG_NOT_FOUND = 'File not found';
|
||||
const MSG_NOT_PERMITTED = 'Not allowed to access file';
|
||||
const MSG_GENERIC_ERR = 'Something went wrong';
|
||||
|
||||
/* global $:false */
|
||||
|
||||
class Share {
|
||||
init() {
|
||||
$(window).bind('hashchange', (e) => {
|
||||
this.hashLoad();
|
||||
});
|
||||
|
||||
let appTitle = `Share @ ${window.location.host}`;
|
||||
$('title,.apptitle').text(appTitle);
|
||||
|
||||
this.hashLoad();
|
||||
}
|
||||
|
||||
embedFileInfo(file = '') {
|
||||
if (file === '') {
|
||||
this.handleErrorMessage(MSG_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
fileURL = file;
|
||||
$.ajax(file, {
|
||||
method: 'HEAD',
|
||||
success: (data, status, xhr) => {
|
||||
this.handleEmbed(data, status, xhr);
|
||||
},
|
||||
error: (xhr, status) => {
|
||||
this.handleError(xhr, status);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
handleEmbed(data, status, xhr) {
|
||||
let type = xhr.getResponseHeader('Content-Type');
|
||||
|
||||
$('.container').hide();
|
||||
$('.filename').text(fileURL.substring(fileURL.lastIndexOf('/') + 1));
|
||||
$('.filelink-href').attr('href', fileURL);
|
||||
|
||||
if (type.match(/^image\//)) {
|
||||
$('.filelink-src').attr('src', fileURL);
|
||||
$('.show-image').show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (type.match(/^video\//)) {
|
||||
let src = $('<source>');
|
||||
src.attr('src', fileURL);
|
||||
src.appendTo($('video'));
|
||||
$('.show-video').show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (type.match(/^audio\/(aac|mp3|mpeg)$/)) {
|
||||
let src = $('<source>');
|
||||
src.attr('src', fileURL);
|
||||
src.appendTo($('audio'));
|
||||
$('.show-audio').show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (type.match(/^(text\/|application\/javascript)/)) {
|
||||
$.ajax(fileURL, {
|
||||
dataType: 'text',
|
||||
method: 'GET',
|
||||
success: (data) => {
|
||||
$('code').text(data);
|
||||
$('.show-text').show();
|
||||
hljs.initHighlighting();
|
||||
},
|
||||
error: (xhr, status) => {
|
||||
this.handleError(xhr, status);
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
$('.show-generic').show();
|
||||
}
|
||||
|
||||
handleError(xhr, status) {
|
||||
let message = '';
|
||||
switch (xhr.status) {
|
||||
case 404:
|
||||
message = MSG_NOT_FOUND;
|
||||
break;
|
||||
case 403:
|
||||
message = MSG_NOT_PERMITTED;
|
||||
break;
|
||||
default:
|
||||
message = MSG_GENERIC_ERR;
|
||||
break;
|
||||
}
|
||||
|
||||
this.handleErrorMessage(message);
|
||||
}
|
||||
|
||||
handleErrorMessage(message) {
|
||||
$('.error').text(message);
|
||||
$('.container').hide();
|
||||
$('.show-error').show();
|
||||
}
|
||||
|
||||
hashLoad() {
|
||||
let file = window.location.hash.substring(1);
|
||||
this.embedFileInfo(file);
|
||||
}
|
||||
}
|
||||
|
||||
$(function() {
|
||||
let share = new Share();
|
||||
share.init();
|
||||
});
|
174
src/app.vue
Normal file
174
src/app.vue
Normal file
|
@ -0,0 +1,174 @@
|
|||
<template>
|
||||
<div>
|
||||
|
||||
<b-navbar variant="primary" type="dark">
|
||||
<b-navbar-brand href="#"><i class="fas fa-share-alt-square"></i> Share</b-navbar-brand>
|
||||
</b-navbar>
|
||||
|
||||
<b-container class="mt-4">
|
||||
<b-row>
|
||||
<b-col>
|
||||
|
||||
<b-card v-if="loading">
|
||||
<b-card-text class="text-center">
|
||||
<h2><i class="fas fa-spinner fa-pulse"></i></h2>
|
||||
{{ strings.loading }}
|
||||
</b-card-text>
|
||||
</b-card>
|
||||
|
||||
<template v-else>
|
||||
|
||||
<b-card v-if="error" bg-variant="danger" text-variant="white">
|
||||
<b-card-text class="text-center">
|
||||
<h2><i class="fas fa-exclamation-circle"></i></h2>
|
||||
{{ error }}
|
||||
</b-card-text>
|
||||
</b-card>
|
||||
|
||||
<b-card v-else-if="fileType.startsWith('image/')">
|
||||
<b-card-text class="text-center">
|
||||
<a :href="path">
|
||||
<b-img :src="path" fluid></b-img>
|
||||
</a>
|
||||
</b-card-text>
|
||||
</b-card>
|
||||
|
||||
<b-card v-else-if="fileType.startsWith('video/')">
|
||||
<b-embed type="video" :src="path" allowfullscreen controls></b-embed>
|
||||
</b-card>
|
||||
|
||||
<b-card v-else-if="fileType.startsWith('audio/')">
|
||||
<b-card-text class="text-center">
|
||||
<audio :src="path" controls></audio>
|
||||
</b-card-text>
|
||||
</b-card>
|
||||
|
||||
<b-card v-else-if="fileType.startsWith('text/')">
|
||||
<pre><code>{{ text }}</code></pre>
|
||||
</b-card>
|
||||
|
||||
<b-card v-else>
|
||||
<b-card-text class="text-center">
|
||||
<h2><i class="fas fa-cloud-download-alt"></i></h2>
|
||||
<b-button :href="path" variant="success">{{ fileName }}</b-button>
|
||||
</b-card-text>
|
||||
</b-card>
|
||||
|
||||
</template>
|
||||
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import hljs from 'highlight.js'
|
||||
import rewrites from './mime-rewrite.js'
|
||||
import strings from './strings.js'
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
|
||||
computed: {
|
||||
strings() {
|
||||
return strings
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
error: null,
|
||||
fileType: null,
|
||||
fileName: '',
|
||||
loading: true,
|
||||
path: '',
|
||||
text: '',
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
hashChange() {
|
||||
const hash = window.location.hash
|
||||
|
||||
if (hash.length > 0) {
|
||||
this.path = hash.substring(1)
|
||||
} else {
|
||||
this.error = strings.file_not_found
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
window.onhashchange = this.hashChange
|
||||
this.hashChange()
|
||||
},
|
||||
|
||||
watch: {
|
||||
fileType(v) {
|
||||
// Rewrite known file types not matching the expectations above
|
||||
if (rewrites[v]) {
|
||||
this.fileType = rewrites[v]
|
||||
return
|
||||
}
|
||||
|
||||
// Load text files directly and highlight them
|
||||
if (v.startsWith('text/')) {
|
||||
this.loading = true
|
||||
axios.get(this.path)
|
||||
.then(resp => {
|
||||
this.text = resp.data
|
||||
|
||||
if (this.text.length < 200*1024 && v !== 'text/plain') {
|
||||
// Only highlight up to 200k and not on text/plain
|
||||
window.setTimeout(() => hljs.initHighlighting(), 100)
|
||||
}
|
||||
this.loading = false
|
||||
})
|
||||
.catch(err => console.log(err))
|
||||
}
|
||||
},
|
||||
|
||||
path() {
|
||||
if (this.path.indexOf('://') >= 0) {
|
||||
// Strictly disallow loading files having any protocol in them
|
||||
this.error = strings.not_permitted
|
||||
this.loading = false
|
||||
return
|
||||
}
|
||||
|
||||
axios.head(this.path)
|
||||
.then(resp => {
|
||||
let contentType = 'application/octet-stream'
|
||||
if (resp && resp.headers && resp.headers['content-type']) {
|
||||
contentType = resp.headers['content-type']
|
||||
}
|
||||
|
||||
this.loading = false
|
||||
this.fileType = contentType
|
||||
})
|
||||
.catch(err => {
|
||||
switch (err.response.status) {
|
||||
case 403:
|
||||
this.error = strings.not_permitted
|
||||
break
|
||||
case 404:
|
||||
this.error = strings.file_not_found
|
||||
break
|
||||
default:
|
||||
this.error = `Something went wrong (Status ${err.response.status})`
|
||||
}
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
audio {
|
||||
width: 80%;
|
||||
}
|
||||
</style>
|
7
src/main.js
Normal file
7
src/main.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import app from './app.vue'
|
||||
|
||||
new Vue({
|
||||
components: { app },
|
||||
el: '#app',
|
||||
render: c => c('app'),
|
||||
})
|
3
src/mime-rewrite.js
Normal file
3
src/mime-rewrite.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default {
|
||||
'application/javascript': 'text/javascript',
|
||||
}
|
854
src/package-lock.json
generated
854
src/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -6,9 +6,11 @@
|
|||
"babel-loader": "^7.1.5",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"css-loader": "^1.0.0",
|
||||
"node-sass": "^4.9.2",
|
||||
"node-sass": "^4.12.0",
|
||||
"sass-loader": "^7.0.3",
|
||||
"style-loader": "^0.21.0",
|
||||
"vue-loader": "^15.7.0",
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"webpack": "^4.16.2",
|
||||
"webpack-cli": "^3.1.0",
|
||||
"webpack-dev-middleware": "^1.4.0",
|
||||
|
@ -21,5 +23,7 @@
|
|||
"build": "webpack -p"
|
||||
},
|
||||
"version": "0.0.1",
|
||||
"dependencies": {}
|
||||
"dependencies": {
|
||||
"highlight.js": "^9.15.6"
|
||||
}
|
||||
}
|
||||
|
|
5
src/strings.js
Normal file
5
src/strings.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
export default {
|
||||
file_not_found: 'The requested file has not been found.',
|
||||
loading: 'Loading file details...',
|
||||
not_permitted: 'Access to this file was denied.',
|
||||
}
|
|
@ -1,12 +1,14 @@
|
|||
const path = require('path')
|
||||
const webpack = require('webpack');
|
||||
|
||||
const VueLoaderPlugin = require('vue-loader/lib/plugin')
|
||||
|
||||
function resolve(dir) {
|
||||
return path.join(__dirname, dir)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
entry: './app.js',
|
||||
entry: './main.js',
|
||||
output: {
|
||||
filename: 'app.js',
|
||||
path: path.resolve(__dirname, '..', 'frontend')
|
||||
|
@ -17,13 +19,16 @@ module.exports = {
|
|||
'process.env': {
|
||||
'NODE_ENV': JSON.stringify('production')
|
||||
}
|
||||
})
|
||||
}),
|
||||
new VueLoaderPlugin(),
|
||||
],
|
||||
optimization: {
|
||||
minimize: true
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
rules: [
|
||||
|
||||
{
|
||||
test: /\.(s?)css$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
|
@ -31,6 +36,7 @@ module.exports = {
|
|||
'sass-loader',
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
|
@ -38,15 +44,17 @@ module.exports = {
|
|||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: [
|
||||
['env', {
|
||||
"targets": {
|
||||
"browsers": [">0.25%", "not ie 11", "not op_mini all"]
|
||||
}
|
||||
}]
|
||||
['env', { "targets": { "browsers": [">0.25%", "not ie 11", "not op_mini all"] } }]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
},
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue