]> git.immae.eu Git - perso/Immae/Projets/Nodejs/Surfer.git/blob - frontend/js/app.js
Add file download button
[perso/Immae/Projets/Nodejs/Surfer.git] / frontend / js / app.js
1 (function () {
2 'use strict';
3
4 function getProfile(accessToken, callback) {
5 callback = callback || function (error) { if (error) console.error(error); };
6
7 superagent.get('/api/profile').query({ access_token: accessToken }).end(function (error, result) {
8 app.busy = false;
9
10 if (error && !error.response) return callback(error);
11 if (result.statusCode !== 200) {
12 delete localStorage.accessToken;
13 return callback('Invalid access token');
14 }
15
16 localStorage.accessToken = accessToken;
17 app.session.username = result.body.username;
18 app.session.valid = true;
19
20 callback();
21 });
22 }
23
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) {
55 filePath = '/' + filePath;
56 return filePath.replace(/\/+/g, '/');
57 }
58
59 function encode(filePath) {
60 return filePath.split('/').map(encodeURIComponent).join('/');
61 }
62
63 function decode(filePath) {
64 return filePath.split('/').map(decodeURIComponent).join('/');
65 }
66
67 var mimeTypes = {
68 images: [ '.png', '.jpg', '.jpeg', '.tiff', '.gif' ],
69 text: [ '.txt', '.md' ],
70 pdf: [ '.pdf' ],
71 html: [ '.html', '.htm', '.php' ],
72 video: [ '.mp4', '.mpg', '.mpeg', '.ogg', '.mkv' ]
73 };
74
75 function getPreviewUrl(entry, basePath) {
76 var path = '/_admin/img/';
77
78 if (entry.isDirectory) return path + 'directory.png';
79 if (mimeTypes.images.some(function (e) { return entry.filePath.endsWith(e); })) return sanitize(basePath + '/' + entry.filePath);
80 if (mimeTypes.text.some(function (e) { return entry.filePath.endsWith(e); })) return path +'text.png';
81 if (mimeTypes.pdf.some(function (e) { return entry.filePath.endsWith(e); })) return path + 'pdf.png';
82 if (mimeTypes.html.some(function (e) { return entry.filePath.endsWith(e); })) return path + 'html.png';
83 if (mimeTypes.video.some(function (e) { return entry.filePath.endsWith(e); })) return path + 'video.png';
84
85 return path + 'unknown.png';
86 }
87
88 function refresh() {
89 loadDirectory(app.path);
90 }
91
92 function loadDirectory(filePath) {
93 app.busy = true;
94
95 filePath = filePath ? sanitize(filePath) : '/';
96
97 superagent.get('/api/files/' + encode(filePath)).query({ access_token: localStorage.accessToken }).end(function (error, result) {
98 app.busy = false;
99
100 if (result && result.statusCode === 401) return logout();
101 if (error) return console.error(error);
102
103 result.body.entries.sort(function (a, b) { return a.isDirectory && b.isFile ? -1 : 1; });
104 app.entries = result.body.entries.map(function (entry) {
105 entry.previewUrl = getPreviewUrl(entry, filePath);
106 return entry;
107 });
108 app.path = filePath;
109 app.pathParts = decode(filePath).split('/').filter(function (e) { return !!e; }).map(function (e, i, a) {
110 return {
111 name: e,
112 link: '#' + sanitize('/' + a.slice(0, i).join('/') + '/' + e)
113 };
114 });
115
116 // update in case this was triggered from code
117 window.location.hash = app.path;
118
119 Vue.nextTick(function () {
120 $(function () {
121 $('[data-toggle="tooltip"]').tooltip();
122 });
123 });
124 });
125 }
126
127 function open(entry) {
128 var path = sanitize(app.path + '/' + entry.filePath);
129
130 if (entry.isDirectory) {
131 window.location.hash = path;
132 return;
133 }
134
135 window.open(encode(path));
136 }
137
138 function download(entry) {
139 if (entry.isDirectory) return;
140
141 window.open(encode('/api/files/' + sanitize(app.path + '/' + entry.filePath)) + '?access_token=' + localStorage.accessToken);
142 }
143
144 function up() {
145 window.location.hash = sanitize(app.path.split('/').slice(0, -1).filter(function (p) { return !!p; }).join('/'));
146 }
147
148 function uploadFiles(files) {
149 if (!files || !files.length) return;
150
151 app.uploadStatus = {
152 busy: true,
153 count: files.length,
154 done: 0,
155 percentDone: 0
156 };
157
158 function uploadFile(file) {
159 var path = encode(sanitize(app.path + '/' + file.name));
160
161 var formData = new FormData();
162 formData.append('file', file);
163
164 superagent.post('/api/files' + path).query({ access_token: localStorage.accessToken }).send(formData).end(function (error, result) {
165 if (result && result.statusCode === 401) return logout();
166 if (result && result.statusCode !== 201) console.error('Error uploading file: ', result.statusCode);
167 if (error) console.error(error);
168
169 app.uploadStatus.done += 1;
170 app.uploadStatus.percentDone = Math.round(app.uploadStatus.done / app.uploadStatus.count * 100);
171
172 if (app.uploadStatus.done >= app.uploadStatus.count) {
173 app.uploadStatus = {
174 busy: false,
175 count: 0,
176 done: 0,
177 percentDone: 100
178 };
179
180 refresh();
181 }
182 });
183 }
184
185 for(var i = 0; i < app.uploadStatus.count; ++i) {
186 uploadFile(files[i]);
187 }
188 }
189
190 function upload() {
191 $(app.$els.upload).on('change', function () {
192
193 // detach event handler
194 $(app.$els.upload).off('change');
195
196 uploadFiles(app.$els.upload.files || []);
197 });
198
199 // reset the form first to make the change handler retrigger even on the same file selected
200 $('#fileUploadForm')[0].reset();
201
202 app.$els.upload.click();
203 }
204
205 function delAsk(entry) {
206 $('#modalDelete').modal('show');
207 app.deleteData = entry;
208 }
209
210 function del(entry) {
211 app.busy = true;
212
213 var path = encode(sanitize(app.path + '/' + entry.filePath));
214
215 superagent.del('/api/files' + path).query({ access_token: localStorage.accessToken, recursive: true }).end(function (error, result) {
216 app.busy = false;
217
218 if (result && result.statusCode === 401) return logout();
219 if (result && result.statusCode !== 200) return console.error('Error deleting file: ', result.statusCode);
220 if (error) return console.error(error);
221
222 refresh();
223
224 $('#modalDelete').modal('hide');
225 });
226 }
227
228 function renameAsk(entry) {
229 app.renameData.entry = entry;
230 app.renameData.error = null;
231 app.renameData.newFilePath = entry.filePath;
232
233 $('#modalRename').modal('show');
234 }
235
236 function rename(data) {
237 app.busy = true;
238
239 var path = encode(sanitize(app.path + '/' + data.entry.filePath));
240 var newFilePath = sanitize(app.path + '/' + data.newFilePath);
241
242 superagent.put('/api/files' + path).query({ access_token: localStorage.accessToken }).send({ newFilePath: newFilePath }).end(function (error, result) {
243 app.busy = false;
244
245 if (result && result.statusCode === 401) return logout();
246 if (result && result.statusCode !== 200) return console.error('Error renaming file: ', result.statusCode);
247 if (error) return console.error(error);
248
249 refresh();
250
251 $('#modalRename').modal('hide');
252 });
253 }
254
255 function createDirectoryAsk() {
256 $('#modalcreateDirectory').modal('show');
257 app.createDirectoryData = '';
258 app.createDirectoryError = null;
259 }
260
261 function createDirectory(name) {
262 app.busy = true;
263 app.createDirectoryError = null;
264
265 var path = encode(sanitize(app.path + '/' + name));
266
267 superagent.post('/api/files' + path).query({ access_token: localStorage.accessToken, directory: true }).end(function (error, result) {
268 app.busy = false;
269
270 if (result && result.statusCode === 401) return logout();
271 if (result && result.statusCode === 403) {
272 app.createDirectoryError = 'Name not allowed';
273 return;
274 }
275 if (result && result.statusCode === 409) {
276 app.createDirectoryError = 'Directory already exists';
277 return;
278 }
279 if (result && result.statusCode !== 201) return console.error('Error creating directory: ', result.statusCode);
280 if (error) return console.error(error);
281
282 app.createDirectoryData = '';
283 refresh();
284
285 $('#modalcreateDirectory').modal('hide');
286 });
287 }
288
289 function dragOver(event) {
290 event.preventDefault();
291 }
292
293 function drop(event) {
294 event.preventDefault();
295 uploadFiles(event.dataTransfer.files || []);
296 }
297
298 Vue.filter('prettyDate', function (value) {
299 var d = new Date(value);
300 return d.toDateString();
301 });
302
303 Vue.filter('prettyFileSize', function (value) {
304 return filesize(value);
305 });
306
307 var app = new Vue({
308 el: '#app',
309 data: {
310 busy: true,
311 uploadStatus: {
312 busy: false,
313 count: 0,
314 done: 0,
315 percentDone: 50
316 },
317 path: '/',
318 pathParts: [],
319 session: {
320 valid: false
321 },
322 loginData: {},
323 deleteData: {},
324 renameData: {
325 entry: {},
326 error: null,
327 newFilePath: ''
328 },
329 createDirectoryData: '',
330 createDirectoryError: null,
331 entries: []
332 },
333 methods: {
334 login: login,
335 logout: logout,
336 loadDirectory: loadDirectory,
337 open: open,
338 download: download,
339 up: up,
340 upload: upload,
341 delAsk: delAsk,
342 del: del,
343 renameAsk: renameAsk,
344 rename: rename,
345 createDirectoryAsk: createDirectoryAsk,
346 createDirectory: createDirectory,
347 drop: drop,
348 dragOver: dragOver
349 }
350 });
351
352 window.app = app;
353
354 getProfile(localStorage.accessToken, function (error) {
355 if (error) return console.error(error);
356
357 loadDirectory(window.location.hash.slice(1));
358 });
359
360 $(window).on('hashchange', function () {
361 loadDirectory(window.location.hash.slice(1));
362 });
363
364 // setup all the dialog focus handling
365 ['modalcreateDirectory'].forEach(function (id) {
366 $('#' + id).on('shown.bs.modal', function () {
367 $(this).find("[autofocus]:first").focus();
368 });
369 });
370
371 })();