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