lfss 0.7.14__py3-none-any.whl → 0.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Readme.md +2 -2
- docs/Permission.md +4 -2
- frontend/api.js +214 -8
- frontend/index.html +40 -28
- frontend/login.css +21 -0
- frontend/login.js +83 -0
- frontend/scripts.js +85 -111
- frontend/state.js +72 -0
- frontend/styles.css +26 -8
- frontend/thumb.css +6 -0
- frontend/thumb.js +39 -24
- lfss/{client → api}/__init__.py +52 -35
- lfss/{client/api.py → api/connector.py} +89 -8
- lfss/cli/cli.py +1 -1
- lfss/cli/user.py +1 -1
- lfss/src/connection_pool.py +3 -2
- lfss/src/database.py +158 -72
- lfss/src/datatype.py +8 -3
- lfss/src/error.py +3 -1
- lfss/src/server.py +67 -9
- lfss/src/stat.py +1 -1
- lfss/src/thumb.py +17 -12
- lfss/src/utils.py +47 -13
- {lfss-0.7.14.dist-info → lfss-0.8.0.dist-info}/METADATA +4 -3
- lfss-0.8.0.dist-info/RECORD +43 -0
- lfss-0.7.14.dist-info/RECORD +0 -40
- {lfss-0.7.14.dist-info → lfss-0.8.0.dist-info}/WHEEL +0 -0
- {lfss-0.7.14.dist-info → lfss-0.8.0.dist-info}/entry_points.txt +0 -0
frontend/scripts.js
CHANGED
@@ -1,18 +1,18 @@
|
|
1
|
-
import
|
2
|
-
import { permMap } from './api.js';
|
1
|
+
import { permMap, listPath } from './api.js';
|
3
2
|
import { showFloatingWindowLineInput, showPopup } from './popup.js';
|
4
3
|
import { formatSize, decodePathURI, ensurePathURI, getRandomString, cvtGMT2Local, debounce, encodePathURI, asHtmlText } from './utils.js';
|
5
4
|
import { showInfoPanel, showDirInfoPanel } from './info.js';
|
6
5
|
import { makeThumbHtml } from './thumb.js';
|
6
|
+
import { store } from './state.js';
|
7
|
+
import { maybeShowLoginPanel } from './login.js';
|
7
8
|
|
8
|
-
|
9
|
+
/** @type {import('./api.js').UserRecord}*/
|
9
10
|
let userRecord = null;
|
11
|
+
|
10
12
|
const ensureSlashEnd = (path) => {
|
11
13
|
return path.endsWith('/') ? path : path + '/';
|
12
14
|
}
|
13
15
|
|
14
|
-
const endpointInput = document.querySelector('input#endpoint');
|
15
|
-
const tokenInput = document.querySelector('input#token');
|
16
16
|
const pathInput = document.querySelector('input#path');
|
17
17
|
const pathBackButton = document.querySelector('span#back-btn');
|
18
18
|
const pathHintDiv = document.querySelector('#position-hint');
|
@@ -23,53 +23,49 @@ const uploadFileSelector = document.querySelector('#file-selector');
|
|
23
23
|
const uploadFileNameInput = document.querySelector('#file-name');
|
24
24
|
const uploadButton = document.querySelector('#upload-btn');
|
25
25
|
const randomizeFnameButton = document.querySelector('#randomize-fname-btn');
|
26
|
+
const pageLimitSelect = document.querySelector('#page-limit-sel');
|
27
|
+
const pageNumInput = document.querySelector('#page-num-input');
|
28
|
+
const pageCountLabel = document.querySelector('#page-count-lbl');
|
26
29
|
const sortBySelect = document.querySelector('#sort-by-sel');
|
27
30
|
const sortOrderSelect = document.querySelector('#sort-order-sel');
|
28
31
|
|
29
|
-
conn
|
30
|
-
conn.config.token = tokenInput.value;
|
32
|
+
const conn = store.conn;
|
31
33
|
|
32
34
|
{
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
conn.config.endpoint = endpoint;
|
37
|
-
}
|
38
|
-
const token = window.localStorage.getItem('token');
|
39
|
-
if (token){
|
40
|
-
tokenInput.value = token;
|
41
|
-
conn.config.token = token;
|
42
|
-
}
|
43
|
-
const path = window.localStorage.getItem('path');
|
44
|
-
if (path){
|
45
|
-
pathInput.value = path;
|
46
|
-
}
|
35
|
+
// initialization
|
36
|
+
store.init();
|
37
|
+
pathInput.value = store.dirpath;
|
47
38
|
uploadFilePrefixLabel.textContent = pathInput.value;
|
48
|
-
|
49
|
-
|
50
|
-
|
39
|
+
sortBySelect.value = store.orderby;
|
40
|
+
sortOrderSelect.value = store.sortorder;
|
41
|
+
pageLimitSelect.value = store.pagelim;
|
42
|
+
pageNumInput.value = store.pagenum;
|
43
|
+
|
44
|
+
maybeShowLoginPanel(store,).then(
|
45
|
+
(user) => {
|
46
|
+
console.log("User record", user);
|
47
|
+
userRecord = user;
|
48
|
+
maybeRefreshFileList();
|
49
|
+
}
|
50
|
+
)
|
51
51
|
}
|
52
52
|
|
53
|
+
pathHintDiv.addEventListener('click', () => {
|
54
|
+
maybeShowLoginPanel(store, true).then(
|
55
|
+
(user) => {
|
56
|
+
console.log("User record", user);
|
57
|
+
userRecord = user;
|
58
|
+
maybeRefreshFileList();
|
59
|
+
}
|
60
|
+
);
|
61
|
+
});
|
62
|
+
|
53
63
|
function onPathChange(){
|
54
64
|
uploadFilePrefixLabel.textContent = pathInput.value;
|
55
|
-
|
65
|
+
store.dirpath = pathInput.value;
|
56
66
|
maybeRefreshFileList();
|
57
67
|
}
|
58
68
|
|
59
|
-
endpointInput.addEventListener('blur', () => {
|
60
|
-
conn.config.endpoint = endpointInput.value;
|
61
|
-
window.localStorage.setItem('endpoint', endpointInput.value);
|
62
|
-
maybeRefreshUserRecord().then(
|
63
|
-
() => maybeRefreshFileList()
|
64
|
-
);
|
65
|
-
});
|
66
|
-
tokenInput.addEventListener('blur', () => {
|
67
|
-
conn.config.token = tokenInput.value;
|
68
|
-
window.localStorage.setItem('token', tokenInput.value);
|
69
|
-
maybeRefreshUserRecord().then(
|
70
|
-
() => maybeRefreshFileList()
|
71
|
-
);
|
72
|
-
});
|
73
69
|
pathInput.addEventListener('input', () => {
|
74
70
|
onPathChange();
|
75
71
|
});
|
@@ -94,7 +90,7 @@ function onFileNameInpuChange(){
|
|
94
90
|
uploadFileNameInput.classList.remove('duplicate');
|
95
91
|
}
|
96
92
|
else {
|
97
|
-
const p = ensurePathURI(
|
93
|
+
const p = ensurePathURI(store.dirpath + fileName);
|
98
94
|
conn.getMetadata(p).then(
|
99
95
|
(data) => {
|
100
96
|
console.log("Got file meta", data);
|
@@ -126,7 +122,7 @@ uploadFileSelector.addEventListener('change', () => {
|
|
126
122
|
});
|
127
123
|
uploadButton.addEventListener('click', () => {
|
128
124
|
const file = uploadFileSelector.files[0];
|
129
|
-
let path =
|
125
|
+
let path = store.dirpath;
|
130
126
|
let fileName = uploadFileNameInput.value;
|
131
127
|
if (fileName.length === 0){
|
132
128
|
throw new Error('File name cannot be empty');
|
@@ -172,7 +168,7 @@ uploadFileNameInput.addEventListener('input', debounce(onFileNameInpuChange, 500
|
|
172
168
|
uploadFileNameInput.focus();
|
173
169
|
}
|
174
170
|
else if (files.length > 1){
|
175
|
-
let dstPath =
|
171
|
+
let dstPath = store.dirpath + uploadFileNameInput.value;
|
176
172
|
if (!dstPath.endsWith('/')){ dstPath += '/'; }
|
177
173
|
if (!confirm(`
|
178
174
|
You are trying to upload multiple files at once.
|
@@ -216,62 +212,69 @@ Are you sure you want to proceed?
|
|
216
212
|
|
217
213
|
function maybeRefreshFileList(){
|
218
214
|
if (
|
219
|
-
|
215
|
+
store.dirpath && store.dirpath.length > 0 && store.dirpath.endsWith('/')
|
220
216
|
){
|
221
217
|
refreshFileList();
|
222
218
|
}
|
223
219
|
}
|
224
220
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
}
|
234
|
-
/** @param {import('./api.js').FileRecord} files */
|
235
|
-
function sortFileList(files){
|
236
|
-
function timestr2num(timestr){
|
237
|
-
return new Date(timestr).getTime();
|
238
|
-
}
|
239
|
-
if (sortBy === 'name'){
|
240
|
-
files.sort((a, b) => { return a.url.localeCompare(b.url); });
|
241
|
-
}
|
242
|
-
if (sortBy === 'size'){
|
243
|
-
files.sort((a, b) => { return a.file_size - b.file_size; });
|
244
|
-
}
|
245
|
-
if (sortBy === 'access'){
|
246
|
-
files.sort((a, b) => { return timestr2num(a.access_time) - timestr2num(b.access_time); });
|
221
|
+
sortBySelect.addEventListener('change', (elem) => {store.orderby = elem.target.value; refreshFileList();});
|
222
|
+
sortOrderSelect.addEventListener('change', (elem) => {store.sortorder = elem.target.value; refreshFileList();});
|
223
|
+
pageLimitSelect.addEventListener('change', (elem) => {store.pagelim = elem.target.value; refreshFileList();});
|
224
|
+
pageNumInput.addEventListener('change', (elem) => {store.pagenum = elem.target.value; refreshFileList();});
|
225
|
+
|
226
|
+
window.addEventListener('keydown', (e) => {
|
227
|
+
if (document.activeElement !== document.body){
|
228
|
+
return;
|
247
229
|
}
|
248
|
-
if (
|
249
|
-
|
230
|
+
if (e.key === 'ArrowLeft'){
|
231
|
+
const num = Math.max(store.pagenum - 1, 1);
|
232
|
+
pageNumInput.value = num;
|
233
|
+
store.pagenum = num;
|
234
|
+
refreshFileList();
|
250
235
|
}
|
251
|
-
if (
|
252
|
-
|
236
|
+
else if (e.key === 'ArrowRight'){
|
237
|
+
const num = Math.min(Math.max(store.pagenum + 1, 1), parseInt(pageCountLabel.textContent));
|
238
|
+
pageNumInput.value = num;
|
239
|
+
store.pagenum = num;
|
240
|
+
refreshFileList();
|
253
241
|
}
|
254
|
-
|
255
|
-
}
|
256
|
-
sortBySelect.addEventListener('change', (elem) => {sortBy = elem.target.value; refreshFileList();});
|
257
|
-
sortOrderSelect.addEventListener('change', (elem) => {sortOrder = elem.target.value; refreshFileList();});
|
242
|
+
})
|
258
243
|
|
259
|
-
function refreshFileList(){
|
260
|
-
|
261
|
-
|
244
|
+
async function refreshFileList(){
|
245
|
+
|
246
|
+
listPath(conn, store.dirpath, {
|
247
|
+
offset: (store.pagenum - 1) * store.pagelim,
|
248
|
+
limit: store.pagelim,
|
249
|
+
orderBy: store.orderby,
|
250
|
+
orderDesc: store.sortorder === 'desc'
|
251
|
+
})
|
252
|
+
.then(async (res) => {
|
262
253
|
pathHintDiv.classList.remove('disconnected');
|
263
254
|
pathHintDiv.classList.add('connected');
|
264
|
-
pathHintLabel.textContent =
|
255
|
+
pathHintLabel.textContent = `[${userRecord.username}] ${store.endpoint}/${store.dirpath.startsWith('/') ? store.dirpath.slice(1) : store.dirpath}`;
|
265
256
|
tbody.innerHTML = '';
|
257
|
+
console.log("Got data", res);
|
258
|
+
|
259
|
+
const [data, count] = res;
|
266
260
|
|
267
|
-
|
261
|
+
{
|
262
|
+
const total = count.dirs + count.files;
|
263
|
+
const pageCount = Math.max(Math.ceil(total / store.pagelim), 1);
|
264
|
+
pageCountLabel.textContent = pageCount;
|
265
|
+
if (store.pagenum > pageCount){
|
266
|
+
store.pagenum = pageCount;
|
267
|
+
pageNumInput.value = pageCount;
|
268
268
|
|
269
|
+
await refreshFileList();
|
270
|
+
return;
|
271
|
+
}
|
272
|
+
}
|
273
|
+
|
274
|
+
// maybe undefined
|
269
275
|
if (!data.dirs){ data.dirs = []; }
|
270
276
|
if (!data.files){ data.files = []; }
|
271
277
|
|
272
|
-
sortDirList(data.dirs);
|
273
|
-
sortFileList(data.files);
|
274
|
-
|
275
278
|
data.dirs.forEach(dir => {
|
276
279
|
const tr = document.createElement('tr');
|
277
280
|
const sizeTd = document.createElement('td');
|
@@ -460,12 +463,6 @@ function refreshFileList(){
|
|
460
463
|
});
|
461
464
|
actContainer.appendChild(infoButton);
|
462
465
|
|
463
|
-
const viewButton = document.createElement('a');
|
464
|
-
viewButton.textContent = 'View';
|
465
|
-
viewButton.href = conn.config.endpoint + '/' + file.url + '?token=' + conn.config.token;
|
466
|
-
viewButton.target = '_blank';
|
467
|
-
actContainer.appendChild(viewButton);
|
468
|
-
|
469
466
|
const moveButton = document.createElement('a');
|
470
467
|
moveButton.textContent = 'Move';
|
471
468
|
moveButton.style.cursor = 'pointer';
|
@@ -520,7 +517,7 @@ function refreshFileList(){
|
|
520
517
|
(err) => {
|
521
518
|
pathHintDiv.classList.remove('connected');
|
522
519
|
pathHintDiv.classList.add('disconnected');
|
523
|
-
pathHintLabel.textContent =
|
520
|
+
pathHintLabel.textContent = store.dirpath;
|
524
521
|
tbody.innerHTML = '';
|
525
522
|
console.log("Error");
|
526
523
|
console.error(err);
|
@@ -528,27 +525,4 @@ function refreshFileList(){
|
|
528
525
|
);
|
529
526
|
}
|
530
527
|
|
531
|
-
|
532
|
-
async function maybeRefreshUserRecord(){
|
533
|
-
if (endpointInput.value && tokenInput.value){
|
534
|
-
await refreshUserRecord();
|
535
|
-
}
|
536
|
-
}
|
537
|
-
|
538
|
-
async function refreshUserRecord(){
|
539
|
-
try{
|
540
|
-
userRecord = await conn.whoami();
|
541
|
-
console.log("User record: ", userRecord);
|
542
|
-
}
|
543
|
-
catch (err){
|
544
|
-
userRecord = null;
|
545
|
-
console.error("Failed to get user record");
|
546
|
-
return false;
|
547
|
-
}
|
548
|
-
|
549
|
-
// UI updates.
|
550
|
-
|
551
|
-
return true;
|
552
|
-
}
|
553
|
-
|
554
528
|
console.log("Hello World");
|
frontend/state.js
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
|
2
|
+
import Connector from './api.js';
|
3
|
+
|
4
|
+
function loadPersistedState(key, defaultValue) {
|
5
|
+
const persistedValue = window.localStorage.getItem(key);
|
6
|
+
return persistedValue ? persistedValue : defaultValue;
|
7
|
+
}
|
8
|
+
|
9
|
+
function setPersistedState(key, value) {
|
10
|
+
window.localStorage.setItem(key, value);
|
11
|
+
}
|
12
|
+
|
13
|
+
export const store = {
|
14
|
+
conn: new Connector(),
|
15
|
+
|
16
|
+
init() {
|
17
|
+
this.conn.config.token = this.token;
|
18
|
+
this.conn.config.endpoint = this.endpoint;
|
19
|
+
},
|
20
|
+
|
21
|
+
get token() {
|
22
|
+
return loadPersistedState('token', '');
|
23
|
+
},
|
24
|
+
set token(t) {
|
25
|
+
setPersistedState('token', t);
|
26
|
+
this.conn.config.token = t;
|
27
|
+
},
|
28
|
+
|
29
|
+
get endpoint() {
|
30
|
+
return loadPersistedState('endpoint', 'http://localhost:8000');
|
31
|
+
},
|
32
|
+
set endpoint(url) {
|
33
|
+
setPersistedState('endpoint', url);
|
34
|
+
this.conn.config.endpoint = url;
|
35
|
+
},
|
36
|
+
|
37
|
+
get dirpath() {
|
38
|
+
return loadPersistedState('dirpath', '/');
|
39
|
+
},
|
40
|
+
set dirpath(pth) {
|
41
|
+
setPersistedState('dirpath', pth);
|
42
|
+
},
|
43
|
+
|
44
|
+
get orderby () {
|
45
|
+
return loadPersistedState('orderby', 'none');
|
46
|
+
},
|
47
|
+
set orderby (sb) {
|
48
|
+
setPersistedState('orderby', sb);
|
49
|
+
},
|
50
|
+
|
51
|
+
get sortorder () {
|
52
|
+
return loadPersistedState('sortorder', 'asc');
|
53
|
+
},
|
54
|
+
set sortorder (so) {
|
55
|
+
setPersistedState('sortorder', so);
|
56
|
+
},
|
57
|
+
|
58
|
+
get pagenum () {
|
59
|
+
return parseInt(loadPersistedState('pagenum', '1'));
|
60
|
+
},
|
61
|
+
set pagenum (pn) {
|
62
|
+
setPersistedState('pagenum', pn.toString());
|
63
|
+
},
|
64
|
+
|
65
|
+
get pagelim () {
|
66
|
+
return parseInt(loadPersistedState('pagelim', '100'));
|
67
|
+
},
|
68
|
+
set pagelim (ps) {
|
69
|
+
setPersistedState('pagelim', ps.toString());
|
70
|
+
},
|
71
|
+
|
72
|
+
};
|
frontend/styles.css
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
@import "./popup.css";
|
2
2
|
@import "./info.css";
|
3
3
|
@import "./thumb.css";
|
4
|
+
@import "./login.css";
|
5
|
+
|
6
|
+
html {
|
7
|
+
overflow: -moz-scrollbars-vertical;
|
8
|
+
overflow-y: scroll;
|
9
|
+
}
|
4
10
|
|
5
11
|
body{
|
6
12
|
font-family: Arial, sans-serif;
|
@@ -44,7 +50,9 @@ div.container.header, div.container.footer{
|
|
44
50
|
|
45
51
|
div.container.header{
|
46
52
|
top: 0;
|
47
|
-
height:
|
53
|
+
height: 6.5rem;
|
54
|
+
justify-content: flex-end;
|
55
|
+
gap: 0.2rem;
|
48
56
|
}
|
49
57
|
div.container.footer{
|
50
58
|
bottom: 0;
|
@@ -54,7 +62,7 @@ div.container.footer{
|
|
54
62
|
div.container.content{
|
55
63
|
width: 100%;
|
56
64
|
padding-inline: 0.5rem;
|
57
|
-
margin-top: calc(
|
65
|
+
margin-top: calc(6.5rem + 1rem);
|
58
66
|
margin-bottom: calc(4rem + 0.5rem);
|
59
67
|
}
|
60
68
|
|
@@ -122,13 +130,22 @@ input#path{
|
|
122
130
|
}
|
123
131
|
|
124
132
|
div#top-bar{
|
133
|
+
color: grey;
|
134
|
+
display: flex;
|
135
|
+
flex-direction: row;
|
136
|
+
width: calc(100% - 2rem);
|
137
|
+
}
|
138
|
+
div#settings-bar{
|
139
|
+
width: calc(100% - 2rem);
|
125
140
|
display: flex;
|
126
141
|
flex-direction: row;
|
127
142
|
justify-content: space-between;
|
128
143
|
align-items: center;
|
129
144
|
gap: 1rem;
|
145
|
+
padding-bottom: 0.2rem;
|
130
146
|
}
|
131
147
|
div#position-hint{
|
148
|
+
cursor: pointer;
|
132
149
|
color: rgb(138, 138, 138);
|
133
150
|
border-radius: 0.5rem;
|
134
151
|
display: flex;
|
@@ -137,8 +154,15 @@ div#position-hint{
|
|
137
154
|
gap: 0.25rem;
|
138
155
|
height: 1rem;
|
139
156
|
margin-bottom: 0.25rem;
|
157
|
+
font-size: x-small;
|
140
158
|
|
141
159
|
padding: 0.25rem;
|
160
|
+
transition: all 0.2s;
|
161
|
+
}
|
162
|
+
div#position-hint:hover{
|
163
|
+
background-color: rgb(240, 244, 246);
|
164
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
165
|
+
color:rgb(91, 107, 116)
|
142
166
|
}
|
143
167
|
div#position-hint span{
|
144
168
|
width: 0.75rem;
|
@@ -152,12 +176,6 @@ div#position-hint.connected span{
|
|
152
176
|
background-color: rgb(0, 166, 0);
|
153
177
|
}
|
154
178
|
|
155
|
-
div#settings {
|
156
|
-
display: flex;
|
157
|
-
flex-direction: column;
|
158
|
-
gap: 10px;
|
159
|
-
}
|
160
|
-
|
161
179
|
label#bucket-label{
|
162
180
|
color: #195f8b;
|
163
181
|
}
|
frontend/thumb.css
CHANGED
frontend/thumb.js
CHANGED
@@ -24,25 +24,26 @@ function getIconSVGFromMimeType(mimeType){
|
|
24
24
|
if (mimeType.startsWith('image/')){
|
25
25
|
return ICON_IMAGE;
|
26
26
|
}
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
return ICON_FILE;
|
27
|
+
|
28
|
+
if (['application/pdf', 'application/x-pdf'].includes(mimeType)){
|
29
|
+
return ICON_PDF;
|
30
|
+
}
|
31
|
+
if (['application/x-msdownload', 'application/x-msdos-program', 'application/x-msi', 'application/x-ms-wim', 'application/octet-stream', 'application/x-apple-diskimage'].includes(mimeType)){
|
32
|
+
return ICON_EXE;
|
33
|
+
}
|
34
|
+
if (['application/zip', 'application/x-zip-compressed', 'application/x-7z-compressed', 'application/x-rar-compressed', 'application/x-tar', 'application/x-gzip'].includes(mimeType)){
|
35
|
+
return ICON_ZIP;
|
36
|
+
}
|
37
|
+
if ([
|
38
|
+
"text/html", "application/xhtml+xml", "application/xml", "text/css", "application/javascript", "text/javascript", "application/json", "text/x-python", "text/x-java-source",
|
39
|
+
"application/x-httpd-php", "text/x-ruby", "text/x-perl", "application/x-sh", "application/sql", "text/x-c", "text/x-c++", "text/x-csharp", "text/x-go", "text/x-haskell",
|
40
|
+
"text/x-lua", "text/x-markdown", "application/wasm", "application/x-tcl", "text/x-yaml", "application/x-latex", "application/x-tex", "text/x-scss", "application/x-lisp",
|
41
|
+
"application/x-rust", "application/x-ruby", "text/x-asm"
|
42
|
+
].includes(mimeType)){
|
43
|
+
return ICON_CODE;
|
45
44
|
}
|
45
|
+
|
46
|
+
return ICON_FILE;
|
46
47
|
}
|
47
48
|
|
48
49
|
function getSafeIconUrl(icon_str){
|
@@ -66,10 +67,24 @@ export function makeThumbHtml(c, r){
|
|
66
67
|
const mtype = r.mime_type? r.mime_type : 'directory';
|
67
68
|
const thumb_id = `thumb-${thumb_counter++}`;
|
68
69
|
const url = mtype == 'directory'? ensureSlashEnd(r.url) : r.url;
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
70
|
+
|
71
|
+
if (mtype.startsWith('image/')){
|
72
|
+
return `
|
73
|
+
<div class="thumb" id="${thumb_id}"> \
|
74
|
+
<img src="${c.config.endpoint}/${url}?token=${token}&thumb=true" alt="${r.url}" class="thumb" \
|
75
|
+
onerror="this.src='${getSafeIconUrl(getIconSVGFromMimeType(mtype))}';this.classList.add('thumb-svg');" \
|
76
|
+
onclick="window.open('${c.config.endpoint}/${url}?token=${token}', '_blank');" \
|
77
|
+
/> \
|
78
|
+
</div>
|
79
|
+
`;
|
80
|
+
}
|
81
|
+
else{
|
82
|
+
return `
|
83
|
+
<div class="thumb" id="${thumb_id}"> \
|
84
|
+
<img src="${getSafeIconUrl(getIconSVGFromMimeType(mtype))}" alt="${r.url}" class="thumb thumb-svg" \
|
85
|
+
onclick="window.open('${c.config.endpoint}/${url}?token=${token}', '_blank');" \
|
86
|
+
/ > \
|
87
|
+
</div>
|
88
|
+
`;
|
89
|
+
}
|
75
90
|
}
|