diff options
Diffstat (limited to 'frontend/js/app.js')
-rw-r--r-- | frontend/js/app.js | 321 |
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 | ||
24 | function 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 | |||
44 | function 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 | |||
54 | function sanitize(filePath) { | 25 | function 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 | ||
134 | function open(entry) { | 99 | function 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 | ||
145 | function 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 | |||
151 | function up() { | 110 | function 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 | ||
197 | function 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 | |||
212 | function delAsk(entry) { | ||
213 | $('#modalDelete').modal('show'); | ||
214 | app.deleteData = entry; | ||
215 | } | ||
216 | |||
217 | function 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 | |||
235 | function 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 | |||
243 | function 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 | |||
262 | function createDirectoryAsk() { | ||
263 | $('#modalcreateDirectory').modal('show'); | ||
264 | app.createDirectoryData = ''; | ||
265 | app.createDirectoryError = null; | ||
266 | } | ||
267 | |||
268 | function 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 | |||
296 | function dragOver(event) { | 156 | function 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 | ||
305 | Vue.filter('prettyDate', function (value) { | ||
306 | var d = new Date(value); | ||
307 | return d.toDateString(); | ||
308 | }); | ||
309 | |||
310 | Vue.filter('prettyFileSize', function (value) { | ||
311 | return filesize(value); | ||
312 | }); | ||
313 | |||
314 | var app = new Vue({ | 165 | var 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 | ||
359 | window.app = app; | ||
360 | |||
361 | getProfile(localStorage.accessToken, function (error) { | 327 | getProfile(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 | })(); |