aboutsummaryrefslogtreecommitdiffhomepage
path: root/frontend/js/app.js
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/js/app.js')
-rw-r--r--frontend/js/app.js321
1 files changed, 140 insertions, 181 deletions
diff --git a/frontend/js/app.js b/frontend/js/app.js
index b8bd1d9..d659b18 100644
--- a/frontend/js/app.js
+++ b/frontend/js/app.js
@@ -6,6 +6,7 @@ function getProfile(accessToken, callback) {
6 6
7 superagent.get('/api/profile').query({ access_token: accessToken }).end(function (error, result) { 7 superagent.get('/api/profile').query({ access_token: accessToken }).end(function (error, result) {
8 app.busy = false; 8 app.busy = false;
9 app.ready = true;
9 10
10 if (error && !error.response) return callback(error); 11 if (error && !error.response) return callback(error);
11 if (result.statusCode !== 200) { 12 if (result.statusCode !== 200) {
@@ -21,36 +22,6 @@ function getProfile(accessToken, callback) {
21 }); 22 });
22} 23}
23 24
24function login(username, password) {
25 username = username || app.loginData.username;
26 password = password || app.loginData.password;
27
28 app.busy = true;
29
30 superagent.post('/api/login').send({ username: username, password: password }).end(function (error, result) {
31 app.busy = false;
32
33 if (error) return console.error(error);
34 if (result.statusCode === 401) return console.error('Invalid credentials');
35
36 getProfile(result.body.accessToken, function (error) {
37 if (error) return console.error(error);
38
39 loadDirectory(window.location.hash.slice(1));
40 });
41 });
42}
43
44function logout() {
45 superagent.post('/api/logout').query({ access_token: localStorage.accessToken }).end(function (error) {
46 if (error) console.error(error);
47
48 app.session.valid = false;
49
50 delete localStorage.accessToken;
51 });
52}
53
54function sanitize(filePath) { 25function sanitize(filePath) {
55 filePath = '/' + filePath; 26 filePath = '/' + filePath;
56 return filePath.replace(/\/+/g, '/'); 27 return filePath.replace(/\/+/g, '/');
@@ -122,19 +93,13 @@ function loadDirectory(filePath) {
122 93
123 // update in case this was triggered from code 94 // update in case this was triggered from code
124 window.location.hash = app.path; 95 window.location.hash = app.path;
125
126 Vue.nextTick(function () {
127 $(function () {
128 $('[data-toggle="tooltip"]').tooltip();
129 });
130 });
131 }); 96 });
132} 97}
133 98
134function open(entry) { 99function open(row, event, column) {
135 var path = sanitize(app.path + '/' + entry.filePath); 100 var path = sanitize(app.path + '/' + row.filePath);
136 101
137 if (entry.isDirectory) { 102 if (row.isDirectory) {
138 window.location.hash = path; 103 window.location.hash = path;
139 return; 104 return;
140 } 105 }
@@ -142,12 +107,6 @@ function open(entry) {
142 window.open(encode(path)); 107 window.open(encode(path));
143} 108}
144 109
145function download(entry) {
146 if (entry.isDirectory) return;
147
148 window.location.href = encode('/api/files/' + sanitize(app.path + '/' + entry.filePath)) + '?access_token=' + localStorage.accessToken;
149}
150
151function up() { 110function up() {
152 window.location.hash = sanitize(app.path.split('/').slice(0, -1).filter(function (p) { return !!p; }).join('/')); 111 window.location.hash = sanitize(app.path.split('/').slice(0, -1).filter(function (p) { return !!p; }).join('/'));
153} 112}
@@ -194,105 +153,6 @@ function uploadFiles(files) {
194 } 153 }
195} 154}
196 155
197function upload() {
198 $(app.$els.upload).on('change', function () {
199
200 // detach event handler
201 $(app.$els.upload).off('change');
202
203 uploadFiles(app.$els.upload.files || []);
204 });
205
206 // reset the form first to make the change handler retrigger even on the same file selected
207 $('#fileUploadForm')[0].reset();
208
209 app.$els.upload.click();
210}
211
212function delAsk(entry) {
213 $('#modalDelete').modal('show');
214 app.deleteData = entry;
215}
216
217function del(entry) {
218 app.busy = true;
219
220 var path = encode(sanitize(app.path + '/' + entry.filePath));
221
222 superagent.del('/api/files' + path).query({ access_token: localStorage.accessToken, recursive: true }).end(function (error, result) {
223 app.busy = false;
224
225 if (result && result.statusCode === 401) return logout();
226 if (result && result.statusCode !== 200) return console.error('Error deleting file: ', result.statusCode);
227 if (error) return console.error(error);
228
229 refresh();
230
231 $('#modalDelete').modal('hide');
232 });
233}
234
235function renameAsk(entry) {
236 app.renameData.entry = entry;
237 app.renameData.error = null;
238 app.renameData.newFilePath = entry.filePath;
239
240 $('#modalRename').modal('show');
241}
242
243function rename(data) {
244 app.busy = true;
245
246 var path = encode(sanitize(app.path + '/' + data.entry.filePath));
247 var newFilePath = sanitize(app.path + '/' + data.newFilePath);
248
249 superagent.put('/api/files' + path).query({ access_token: localStorage.accessToken }).send({ newFilePath: newFilePath }).end(function (error, result) {
250 app.busy = false;
251
252 if (result && result.statusCode === 401) return logout();
253 if (result && result.statusCode !== 200) return console.error('Error renaming file: ', result.statusCode);
254 if (error) return console.error(error);
255
256 refresh();
257
258 $('#modalRename').modal('hide');
259 });
260}
261
262function createDirectoryAsk() {
263 $('#modalcreateDirectory').modal('show');
264 app.createDirectoryData = '';
265 app.createDirectoryError = null;
266}
267
268function createDirectory(name) {
269 app.busy = true;
270 app.createDirectoryError = null;
271
272 var path = encode(sanitize(app.path + '/' + name));
273
274 superagent.post('/api/files' + path).query({ access_token: localStorage.accessToken, directory: true }).end(function (error, result) {
275 app.busy = false;
276
277 if (result && result.statusCode === 401) return logout();
278 if (result && result.statusCode === 403) {
279 app.createDirectoryError = 'Name not allowed';
280 return;
281 }
282 if (result && result.statusCode === 409) {
283 app.createDirectoryError = 'Directory already exists';
284 return;
285 }
286 if (result && result.statusCode !== 201) return console.error('Error creating directory: ', result.statusCode);
287 if (error) return console.error(error);
288
289 app.createDirectoryData = '';
290 refresh();
291
292 $('#modalcreateDirectory').modal('hide');
293 });
294}
295
296function dragOver(event) { 156function dragOver(event) {
297 event.preventDefault(); 157 event.preventDefault();
298} 158}
@@ -302,18 +162,10 @@ function drop(event) {
302 uploadFiles(event.dataTransfer.files || []); 162 uploadFiles(event.dataTransfer.files || []);
303} 163}
304 164
305Vue.filter('prettyDate', function (value) {
306 var d = new Date(value);
307 return d.toDateString();
308});
309
310Vue.filter('prettyFileSize', function (value) {
311 return filesize(value);
312});
313
314var app = new Vue({ 165var app = new Vue({
315 el: '#app', 166 el: '#app',
316 data: { 167 data: {
168 ready: false,
317 busy: true, 169 busy: true,
318 uploadStatus: { 170 uploadStatus: {
319 busy: false, 171 busy: false,
@@ -326,38 +178,152 @@ var app = new Vue({
326 session: { 178 session: {
327 valid: false 179 valid: false
328 }, 180 },
329 loginData: {}, 181 folderListingEnabled: false,
330 deleteData: {}, 182 loginData: {
331 renameData: { 183 username: '',
332 entry: {}, 184 password: ''
333 error: null,
334 newFilePath: ''
335 }, 185 },
336 createDirectoryData: '',
337 createDirectoryError: null,
338 entries: [] 186 entries: []
339 }, 187 },
340 methods: { 188 methods: {
341 login: login, 189 onLogin: function () {
342 logout: logout, 190 app.busy = true;
191
192 superagent.post('/api/login').send({ username: app.loginData.username, password: app.loginData.password }).end(function (error, result) {
193 app.busy = false;
194
195 if (error) return console.error(error);
196 if (result.statusCode === 401) return console.error('Invalid credentials');
197
198 getProfile(result.body.accessToken, function (error) {
199 if (error) return console.error(error);
200
201 loadDirectory(window.location.hash.slice(1));
202 });
203 });
204 },
205 onOptionsMenu: function (command) {
206 if (command === 'folderListing') {
207 console.log('Not implemented');
208 } else if (command === 'about') {
209 this.$msgbox({
210 title: 'About Surfer',
211 message: 'Surfer is a static file server written by <a href="https://cloudron.io" target="_blank">Cloudron</a>.<br/><br/>The source code is licensed under MIT and available <a href="https://git.cloudron.io/cloudron/surfer" target="_blank">here</a>.',
212 dangerouslyUseHTMLString: true,
213 confirmButtonText: 'OK',
214 showCancelButton: false,
215 type: 'info',
216 center: true
217 }).then(function () {}).catch(function () {});
218 } else if (command === 'logout') {
219 superagent.post('/api/logout').query({ access_token: localStorage.accessToken }).end(function (error) {
220 if (error) console.error(error);
221
222 app.session.valid = false;
223
224 delete localStorage.accessToken;
225 });
226 }
227 },
228 onDownload: function (entry) {
229 if (entry.isDirectory) return;
230 window.location.href = encode('/api/files/' + sanitize(app.path + '/' + entry.filePath)) + '?access_token=' + localStorage.accessToken;
231 },
232 onUpload: function () {
233 $(app.$refs.upload).on('change', function () {
234
235 // detach event handler
236 $(app.$refs.upload).off('change');
237
238 uploadFiles(app.$refs.upload.files || []);
239 });
240
241 // reset the form first to make the change handler retrigger even on the same file selected
242 app.$refs.upload.value = '';
243 app.$refs.upload.click();
244 },
245 onDelete: function (entry) {
246 var title = 'Really delete ' + (entry.isDirectory ? 'folder ' : '') + entry.filePath;
247 this.$confirm('', title, { confirmButtonText: 'Yes', cancelButtonText: 'No' }).then(function () {
248 var path = encode(sanitize(app.path + '/' + entry.filePath));
249
250 superagent.del('/api/files' + path).query({ access_token: localStorage.accessToken, recursive: true }).end(function (error, result) {
251 if (result && result.statusCode === 401) return logout();
252 if (result && result.statusCode !== 200) return console.error('Error deleting file: ', result.statusCode);
253 if (error) return console.error(error);
254
255 refresh();
256 });
257 }).catch(function () {
258 console.log('delete error:', arguments);
259 });
260 },
261 onRename: function (entry) {
262 var title = 'Rename ' + entry.filePath;
263 this.$prompt('', title, { confirmButtonText: 'Yes', cancelButtonText: 'No', inputPlaceholder: 'new filename', inputValue: entry.filePath }).then(function (data) {
264 var path = encode(sanitize(app.path + '/' + entry.filePath));
265 var newFilePath = sanitize(app.path + '/' + data.value);
266
267 superagent.put('/api/files' + path).query({ access_token: localStorage.accessToken }).send({ newFilePath: newFilePath }).end(function (error, result) {
268 if (result && result.statusCode === 401) return logout();
269 if (result && result.statusCode !== 200) return console.error('Error renaming file: ', result.statusCode);
270 if (error) return console.error(error);
271
272 refresh();
273 });
274 }).catch(function () {
275 console.log('rename error:', arguments);
276 });
277 },
278 onNewFolder: function () {
279 var title = 'Create New Folder';
280 this.$prompt('', title, { confirmButtonText: 'Yes', cancelButtonText: 'No', inputPlaceholder: 'new foldername' }).then(function (data) {
281 var path = encode(sanitize(app.path + '/' + data.value));
282
283 superagent.post('/api/files' + path).query({ access_token: localStorage.accessToken, directory: true }).end(function (error, result) {
284 if (result && result.statusCode === 401) return logout();
285 if (result && result.statusCode === 403) return console.error('Name not allowed');
286 if (result && result.statusCode === 409) return console.error('Directory already exists');
287 if (result && result.statusCode !== 201) return console.error('Error creating directory: ', result.statusCode);
288 if (error) return console.error(error);
289
290 refresh();
291 });
292 }).catch(function () {
293 console.log('create folder error:', arguments);
294 });
295 },
296 prettyDate: function (row, column, cellValue, index) {
297 var date = new Date(cellValue),
298 diff = (((new Date()).getTime() - date.getTime()) / 1000),
299 day_diff = Math.floor(diff / 86400);
300
301 if (isNaN(day_diff) || day_diff < 0)
302 return;
303
304 return day_diff === 0 && (
305 diff < 60 && 'just now' ||
306 diff < 120 && '1 minute ago' ||
307 diff < 3600 && Math.floor( diff / 60 ) + ' minutes ago' ||
308 diff < 7200 && '1 hour ago' ||
309 diff < 86400 && Math.floor( diff / 3600 ) + ' hours ago') ||
310 day_diff === 1 && 'Yesterday' ||
311 day_diff < 7 && day_diff + ' days ago' ||
312 day_diff < 31 && Math.ceil( day_diff / 7 ) + ' weeks ago' ||
313 day_diff < 365 && Math.round( day_diff / 30 ) + ' months ago' ||
314 Math.round( day_diff / 365 ) + ' years ago';
315 },
316 prettyFileSize: function (row, column, cellValue, index) {
317 return filesize(cellValue);
318 },
343 loadDirectory: loadDirectory, 319 loadDirectory: loadDirectory,
344 open: open,
345 download: download,
346 up: up, 320 up: up,
347 upload: upload, 321 open: open,
348 delAsk: delAsk,
349 del: del,
350 renameAsk: renameAsk,
351 rename: rename,
352 createDirectoryAsk: createDirectoryAsk,
353 createDirectory: createDirectory,
354 drop: drop, 322 drop: drop,
355 dragOver: dragOver 323 dragOver: dragOver
356 } 324 }
357}); 325});
358 326
359window.app = app;
360
361getProfile(localStorage.accessToken, function (error) { 327getProfile(localStorage.accessToken, function (error) {
362 if (error) return console.error(error); 328 if (error) return console.error(error);
363 329
@@ -368,11 +334,4 @@ $(window).on('hashchange', function () {
368 loadDirectory(window.location.hash.slice(1)); 334 loadDirectory(window.location.hash.slice(1));
369}); 335});
370 336
371// setup all the dialog focus handling
372['modalcreateDirectory'].forEach(function (id) {
373 $('#' + id).on('shown.bs.modal', function () {
374 $(this).find("[autofocus]:first").focus();
375 });
376});
377
378})(); 337})();