]> git.immae.eu Git - perso/Immae/Projets/Nodejs/Surfer.git/blob - frontend/js/app.js
b8bd1d985f2b6751be3de20989fe2f82f93dd030
[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 // simple extension detection, does not work with double extension like .tar.gz
89 function getExtension(entry) {
90 if (entry.isFile) return entry.filePath.slice(entry.filePath.lastIndexOf('.') + 1);
91 return '';
92 }
93
94 function refresh() {
95 loadDirectory(app.path);
96 }
97
98 function loadDirectory(filePath) {
99 app.busy = true;
100
101 filePath = filePath ? sanitize(filePath) : '/';
102
103 superagent.get('/api/files/' + encode(filePath)).query({ access_token: localStorage.accessToken }).end(function (error, result) {
104 app.busy = false;
105
106 if (result && result.statusCode === 401) return logout();
107 if (error) return console.error(error);
108
109 result.body.entries.sort(function (a, b) { return a.isDirectory && b.isFile ? -1 : 1; });
110 app.entries = result.body.entries.map(function (entry) {
111 entry.previewUrl = getPreviewUrl(entry, filePath);
112 entry.extension = getExtension(entry);
113 return entry;
114 });
115 app.path = filePath;
116 app.pathParts = decode(filePath).split('/').filter(function (e) { return !!e; }).map(function (e, i, a) {
117 return {
118 name: e,
119 link: '#' + sanitize('/' + a.slice(0, i).join('/') + '/' + e)
120 };
121 });
122
123 // update in case this was triggered from code
124 window.location.hash = app.path;
125
126 Vue.nextTick(function () {
127 $(function () {
128 $('[data-toggle="tooltip"]').tooltip();
129 });
130 });
131 });
132 }
133
134 function open(entry) {
135 var path = sanitize(app.path + '/' + entry.filePath);
136
137 if (entry.isDirectory) {
138 window.location.hash = path;
139 return;
140 }
141
142 window.open(encode(path));
143 }
144
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() {
152 window.location.hash = sanitize(app.path.split('/').slice(0, -1).filter(function (p) { return !!p; }).join('/'));
153 }
154
155 function uploadFiles(files) {
156 if (!files || !files.length) return;
157
158 app.uploadStatus = {
159 busy: true,
160 count: files.length,
161 done: 0,
162 percentDone: 0
163 };
164
165 function uploadFile(file) {
166 var path = encode(sanitize(app.path + '/' + file.name));
167
168 var formData = new FormData();
169 formData.append('file', file);
170
171 superagent.post('/api/files' + path).query({ access_token: localStorage.accessToken }).send(formData).end(function (error, result) {
172 if (result && result.statusCode === 401) return logout();
173 if (result && result.statusCode !== 201) console.error('Error uploading file: ', result.statusCode);
174 if (error) console.error(error);
175
176 app.uploadStatus.done += 1;
177 app.uploadStatus.percentDone = Math.round(app.uploadStatus.done / app.uploadStatus.count * 100);
178
179 if (app.uploadStatus.done >= app.uploadStatus.count) {
180 app.uploadStatus = {
181 busy: false,
182 count: 0,
183 done: 0,
184 percentDone: 100
185 };
186
187 refresh();
188 }
189 });
190 }
191
192 for(var i = 0; i < app.uploadStatus.count; ++i) {
193 uploadFile(files[i]);
194 }
195 }
196
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) {
297 event.preventDefault();
298 }
299
300 function drop(event) {
301 event.preventDefault();
302 uploadFiles(event.dataTransfer.files || []);
303 }
304
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({
315 el: '#app',
316 data: {
317 busy: true,
318 uploadStatus: {
319 busy: false,
320 count: 0,
321 done: 0,
322 percentDone: 50
323 },
324 path: '/',
325 pathParts: [],
326 session: {
327 valid: false
328 },
329 loginData: {},
330 deleteData: {},
331 renameData: {
332 entry: {},
333 error: null,
334 newFilePath: ''
335 },
336 createDirectoryData: '',
337 createDirectoryError: null,
338 entries: []
339 },
340 methods: {
341 login: login,
342 logout: logout,
343 loadDirectory: loadDirectory,
344 open: open,
345 download: download,
346 up: up,
347 upload: upload,
348 delAsk: delAsk,
349 del: del,
350 renameAsk: renameAsk,
351 rename: rename,
352 createDirectoryAsk: createDirectoryAsk,
353 createDirectory: createDirectory,
354 drop: drop,
355 dragOver: dragOver
356 }
357 });
358
359 window.app = app;
360
361 getProfile(localStorage.accessToken, function (error) {
362 if (error) return console.error(error);
363
364 loadDirectory(window.location.hash.slice(1));
365 });
366
367 $(window).on('hashchange', function () {
368 loadDirectory(window.location.hash.slice(1));
369 });
370
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 })();