X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=frontend%2Fjs%2Fapp.js;h=7a80ba72d1849666da61d5038be3fc0fe947f565;hb=4dce7a3d2e99f489245bc8b4c6611061b78aa8cf;hp=6303a892984868c4d496b92efe04c556033e0028;hpb=f43b3c162f5a0857d9fd56f4d8005763fcdca4cc;p=perso%2FImmae%2FProjets%2FNodejs%2FSurfer.git diff --git a/frontend/js/app.js b/frontend/js/app.js index 6303a89..7a80ba7 100644 --- a/frontend/js/app.js +++ b/frontend/js/app.js @@ -1,6 +1,11 @@ (function () { 'use strict'; +/* global superagent */ +/* global Vue */ +/* global $ */ +/* global filesize */ + // poor man's async function asyncForEach(items, handler, callback) { var cur = 0; @@ -19,10 +24,7 @@ function asyncForEach(items, handler, callback) { } function getProfile(accessToken, callback) { - callback = callback || function (error) { if (error) console.error(error); }; - superagent.get('/api/profile').query({ access_token: accessToken }).end(function (error, result) { - app.busy = false; app.ready = true; if (error && !error.response) return callback(error); @@ -63,7 +65,7 @@ var mimeTypes = { text: [ '.txt', '.md' ], pdf: [ '.pdf' ], html: [ '.html', '.htm', '.php' ], - video: [ '.mp4', '.mpg', '.mpeg', '.ogg', '.mkv' ] + video: [ '.mp4', '.mpg', '.mpeg', '.ogg', '.mkv', '.avi', '.mov' ] }; function getPreviewUrl(entry, basePath) { @@ -89,6 +91,16 @@ function refresh() { loadDirectory(app.path); } +function logout() { + superagent.post('/api/logout').query({ access_token: localStorage.accessToken }).end(function (error) { + if (error) console.error(error); + + app.session.valid = false; + + delete localStorage.accessToken; + }); +} + function loadDirectory(filePath) { app.busy = true; @@ -135,23 +147,36 @@ function uploadFiles(files) { app.uploadStatus.busy = true; app.uploadStatus.count = files.length; + app.uploadStatus.size = 0; app.uploadStatus.done = 0; app.uploadStatus.percentDone = 0; + for (var i = 0; i < files.length; ++i) { + app.uploadStatus.size += files[i].size; + } + asyncForEach(files, function (file, callback) { - var path = encode(sanitize(app.path + '/' + file.name)); + var path = encode(sanitize(app.path + '/' + (file.webkitRelativePath || file.name))); var formData = new FormData(); formData.append('file', file); - superagent.post('/api/files' + path).query({ access_token: localStorage.accessToken }).send(formData).end(function (error, result) { + var finishedUploadSize = app.uploadStatus.done; + + superagent.post('/api/files' + path) + .query({ access_token: localStorage.accessToken }) + .send(formData) + .on('progress', function (event) { + // only handle upload events + if (!(event.target instanceof XMLHttpRequestUpload)) return; + + app.uploadStatus.done = finishedUploadSize + event.loaded; + app.uploadStatus.percentDone = Math.round(app.uploadStatus.done / app.uploadStatus.size * 100); + }).end(function (error, result) { if (result && result.statusCode === 401) return logout(); if (result && result.statusCode !== 201) return callback('Error uploading file: ', result.statusCode); if (error) return callback(error); - app.uploadStatus.done += 1; - app.uploadStatus.percentDone = Math.round(app.uploadStatus.done / app.uploadStatus.count * 100); - callback(); }); }, function (error) { @@ -159,6 +184,7 @@ function uploadFiles(files) { app.uploadStatus.busy = false; app.uploadStatus.count = 0; + app.uploadStatus.size = 0; app.uploadStatus.done = 0; app.uploadStatus.percentDone = 100; @@ -167,24 +193,72 @@ function uploadFiles(files) { } function dragOver(event) { + event.stopPropagation(); event.preventDefault(); + event.dataTransfer.dropEffect = 'copy'; } function drop(event) { + event.stopPropagation(); event.preventDefault(); - uploadFiles(event.dataTransfer.files || []); + + if (!event.dataTransfer.items[0]) return; + + // figure if a folder was dropped on a modern browser, in this case the first would have to be a directory + var folderItem; + try { + folderItem = event.dataTransfer.items[0].webkitGetAsEntry(); + if (folderItem.isFile) return uploadFiles(event.dataTransfer.files); + } catch (e) { + return uploadFiles(event.dataTransfer.files); + } + + // if we got here we have a folder drop and a modern browser + // now traverse the folder tree and create a file list + app.uploadStatus.busy = true; + app.uploadStatus.uploadListCount = 0; + + var fileList = []; + function traverseFileTree(item, path, callback) { + if (item.isFile) { + // Get file + item.file(function (file) { + fileList.push(file); + ++app.uploadStatus.uploadListCount; + callback(); + }); + } else if (item.isDirectory) { + // Get folder contents + var dirReader = item.createReader(); + dirReader.readEntries(function (entries) { + asyncForEach(entries, function (entry, callback) { + traverseFileTree(entry, path + item.name + '/', callback); + }, callback); + }); + } + } + + traverseFileTree(folderItem, '', function (error) { + app.uploadStatus.busy = false; + app.uploadStatus.uploadListCount = 0; + + if (error) return console.error(error); + + uploadFiles(fileList); + }); } var app = new Vue({ el: '#app', data: { ready: false, - busy: true, + busy: false, uploadStatus: { busy: false, count: 0, done: 0, - percentDone: 50 + percentDone: 50, + uploadListCount: 0 }, path: '/', pathParts: [], @@ -194,24 +268,27 @@ var app = new Vue({ folderListingEnabled: false, loginData: { username: '', - password: '' + password: '', + busy: false }, entries: [] }, methods: { onLogin: function () { - app.busy = true; + var that = this; + + that.loginData.busy = true; - superagent.post('/api/login').send({ username: app.loginData.username, password: app.loginData.password }).end(function (error, result) { - app.busy = false; + superagent.post('/api/login').send({ username: that.loginData.username, password: that.loginData.password }).end(function (error, result) { + that.loginData.busy = false; - if (error) return console.error(error); - if (result.statusCode === 401) return console.error('Invalid credentials'); + if (error && !result) return that.$message.error(error.message); + if (result.statusCode === 401) return that.$message.error('Wrong username or password'); getProfile(result.body.accessToken, function (error) { if (error) return console.error(error); - loadDirectory(window.location.hash.slice(1)); + loadDirectory(decode(window.location.hash.slice(1))); }); }); }, @@ -231,82 +308,89 @@ var app = new Vue({ center: true }).then(function () {}).catch(function () {}); } else if (command === 'logout') { - superagent.post('/api/logout').query({ access_token: localStorage.accessToken }).end(function (error) { - if (error) console.error(error); - - app.session.valid = false; - - delete localStorage.accessToken; - }); + logout(); } }, onDownload: function (entry) { if (entry.isDirectory) return; - window.location.href = encode('/api/files/' + sanitize(app.path + '/' + entry.filePath)) + '?access_token=' + localStorage.accessToken; + window.location.href = encode('/api/files/' + sanitize(this.path + '/' + entry.filePath)) + '?access_token=' + localStorage.accessToken; }, onUpload: function () { - $(app.$refs.upload).on('change', function () { + var that = this; + $(this.$refs.upload).on('change', function () { // detach event handler - $(app.$refs.upload).off('change'); + $(that.$refs.upload).off('change'); + uploadFiles(that.$refs.upload.files || []); + }); + + // reset the form first to make the change handler retrigger even on the same file selected + this.$refs.upload.value = ''; + this.$refs.upload.click(); + }, + onUploadFolder: function () { + var that = this; - uploadFiles(app.$refs.upload.files || []); + $(this.$refs.uploadFolder).on('change', function () { + // detach event handler + $(that.$refs.uploadFolder).off('change'); + uploadFiles(that.$refs.uploadFolder.files || []); }); // reset the form first to make the change handler retrigger even on the same file selected - app.$refs.upload.value = ''; - app.$refs.upload.click(); + this.$refs.uploadFolder.value = ''; + this.$refs.uploadFolder.click(); }, onDelete: function (entry) { + var that = this; + var title = 'Really delete ' + (entry.isDirectory ? 'folder ' : '') + entry.filePath; this.$confirm('', title, { confirmButtonText: 'Yes', cancelButtonText: 'No' }).then(function () { - var path = encode(sanitize(app.path + '/' + entry.filePath)); + var path = encode(sanitize(that.path + '/' + entry.filePath)); superagent.del('/api/files' + path).query({ access_token: localStorage.accessToken, recursive: true }).end(function (error, result) { if (result && result.statusCode === 401) return logout(); - if (result && result.statusCode !== 200) return console.error('Error deleting file: ', result.statusCode); - if (error) return console.error(error); + if (result && result.statusCode !== 200) return that.$message.error('Error deleting file: ' + result.statusCode); + if (error) return that.$message.error(error.message); refresh(); }); - }).catch(function () { - console.log('delete error:', arguments); - }); + }).catch(function () {}); }, onRename: function (entry) { + var that = this; + var title = 'Rename ' + entry.filePath; this.$prompt('', title, { confirmButtonText: 'Yes', cancelButtonText: 'No', inputPlaceholder: 'new filename', inputValue: entry.filePath }).then(function (data) { - var path = encode(sanitize(app.path + '/' + entry.filePath)); - var newFilePath = sanitize(app.path + '/' + data.value); + var path = encode(sanitize(that.path + '/' + entry.filePath)); + var newFilePath = sanitize(that.path + '/' + data.value); superagent.put('/api/files' + path).query({ access_token: localStorage.accessToken }).send({ newFilePath: newFilePath }).end(function (error, result) { if (result && result.statusCode === 401) return logout(); - if (result && result.statusCode !== 200) return console.error('Error renaming file: ', result.statusCode); - if (error) return console.error(error); + if (result && result.statusCode !== 200) return that.$message.error('Error renaming file: ' + result.statusCode); + if (error) return that.$message.error(error.message); refresh(); }); - }).catch(function () { - console.log('rename error:', arguments); - }); + }).catch(function () {}); }, onNewFolder: function () { + var that = this; + var title = 'Create New Folder'; this.$prompt('', title, { confirmButtonText: 'Yes', cancelButtonText: 'No', inputPlaceholder: 'new foldername' }).then(function (data) { - var path = encode(sanitize(app.path + '/' + data.value)); + var path = encode(sanitize(that.path + '/' + data.value)); superagent.post('/api/files' + path).query({ access_token: localStorage.accessToken, directory: true }).end(function (error, result) { if (result && result.statusCode === 401) return logout(); - if (result && result.statusCode === 403) return console.error('Name not allowed'); - if (result && result.statusCode === 409) return console.error('Directory already exists'); - if (result && result.statusCode !== 201) return console.error('Error creating directory: ', result.statusCode); - if (error) return console.error(error); + if (result && result.statusCode === 403) return that.$message.error('Folder name not allowed'); + if (result && result.statusCode === 409) return that.$message.error('Folder already exists'); + if (result && result.statusCode !== 201) return that.$message.error('Error creating folder: ' + result.statusCode); + if (error) return that.$message.error(error.message); refresh(); }); - }).catch(function () { - console.log('create folder error:', arguments); - }); + }).catch(function () {}); }, prettyDate: function (row, column, cellValue, index) { var date = new Date(cellValue), @@ -333,7 +417,7 @@ var app = new Vue({ }, loadDirectory: loadDirectory, onUp: function () { - window.location.hash = sanitize(app.path.split('/').slice(0, -1).filter(function (p) { return !!p; }).join('/')); + window.location.hash = sanitize(this.path.split('/').slice(0, -1).filter(function (p) { return !!p; }).join('/')); }, open: open, drop: drop, @@ -344,11 +428,11 @@ var app = new Vue({ getProfile(localStorage.accessToken, function (error) { if (error) return console.error(error); - loadDirectory(window.location.hash.slice(1)); + loadDirectory(decode(window.location.hash.slice(1))); }); $(window).on('hashchange', function () { - loadDirectory(window.location.hash.slice(1)); + loadDirectory(decode(window.location.hash.slice(1))); }); })();