From e1927d5bee11b561a293a9488bd27d90c2c043e6 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Mon, 12 Aug 2013 12:33:22 +0200 Subject: fix whitespace, check selected files before starting upload --- apps/files/js/file-upload.js | 710 +++++++++++++++++++++++++------------------ 1 file changed, 406 insertions(+), 304 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 942a07dfccc..71034a0b3f4 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -1,343 +1,445 @@ -$(document).ready(function() { - - file_upload_param = { - dropZone: $('#content'), // restrict dropZone to content div - //singleFileUploads is on by default, so the data.files array will always have length 1 - add: function(e, data) { - - if(data.files[0].type === '' && data.files[0].size == 4096) - { - data.textStatus = 'dirorzero'; - data.errorThrown = t('files','Unable to upload your file as it is a directory or has 0 bytes'); - var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - fu._trigger('fail', e, data); - return true; //don't upload this file but go on with next in queue - } - - var totalSize=0; - $.each(data.originalFiles, function(i,file){ - totalSize+=file.size; - }); - - if(totalSize>$('#max_upload').val()){ - data.textStatus = 'notenoughspace'; - data.errorThrown = t('files','Not enough space available'); - var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - fu._trigger('fail', e, data); - return false; //don't upload anything - } - - // start the actual file upload - var jqXHR = data.submit(); +OC.upload = { + _isProcessing:false, + isProcessing:function(){ + return this._isProcessing; + }, + _uploadQueue:[], + addUpload:function(data){ + this._uploadQueue.push(data); - // remember jqXHR to show warning to user when he navigates away but an upload is still in progress - if (typeof data.context !== 'undefined' && data.context.data('type') === 'dir') { - var dirName = data.context.data('file'); - if(typeof uploadingFiles[dirName] === 'undefined') { - uploadingFiles[dirName] = {}; + if ( ! OC.upload.isProcessing() ) { + OC.upload.startUpload(); } - uploadingFiles[dirName][data.files[0].name] = jqXHR; - } else { - uploadingFiles[data.files[0].name] = jqXHR; - } - - //show cancel button - if($('html.lte9').length === 0 && data.dataType !== 'iframe') { - $('#uploadprogresswrapper input.stop').show(); - } }, - /** - * called after the first add, does NOT have the data param - * @param e - */ - start: function(e) { - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return; - } - $('#uploadprogressbar').progressbar({value:0}); - $('#uploadprogressbar').fadeIn(); + startUpload:function(){ + if (this._uploadQueue.length > 0) { + this._isProcessing = true; + this.nextUpload(); + return true; + } else { + return false; + } }, - fail: function(e, data) { - if (typeof data.textStatus !== 'undefined' && data.textStatus !== 'success' ) { - if (data.textStatus === 'abort') { - $('#notification').text(t('files', 'Upload cancelled.')); + nextUpload:function(){ + if (this._uploadQueue.length > 0) { + var data = this._uploadQueue.pop(); + var jqXHR = data.submit(); + + // remember jqXHR to show warning to user when he navigates away but an upload is still in progress + if (typeof data.context !== 'undefined' && data.context.data('type') === 'dir') { + var dirName = data.context.data('file'); + if(typeof uploadingFiles[dirName] === 'undefined') { + uploadingFiles[dirName] = {}; + } + uploadingFiles[dirName][data.files[0].name] = jqXHR; + } else { + uploadingFiles[data.files[0].name] = jqXHR; + } } else { - // HTTP connection problem - $('#notification').text(data.errorThrown); + //queue is empty, we are done + this._isProcessing = false; } - $('#notification').fadeIn(); - //hide notification after 5 sec - setTimeout(function() { - $('#notification').fadeOut(); - }, 5000); - } - delete uploadingFiles[data.files[0].name]; }, - progress: function(e, data) { - // TODO: show nice progress bar in file row + onCancel:function(data){ + //TODO cancel all uploads + Files.cancelUploads(); + this._uploadQueue = []; + this._isProcessing = false; + }, + onSkip:function(data){ + this.nextUpload(); }, - progressall: function(e, data) { - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return; - } - var progress = (data.loaded/data.total)*100; - $('#uploadprogressbar').progressbar('value',progress); + onReplace:function(data){ + //TODO overwrite file + data.data.append('replace', true); + data.submit(); }, - /** - * called for every successful upload - * @param e - * @param data - */ - done:function(e, data) { - // handle different responses (json or body from iframe for ie) - var response; - if (typeof data.result === 'string') { - response = data.result; - } else { - //fetch response from iframe - response = data.result[0].body.innerText; - } - var result=$.parseJSON(response); + onRename:function(data, newName){ + //TODO rename file in filelist, stop spinner + data.data.append('new_name', newName); + data.submit(); + } +}; - if(typeof result[0] !== 'undefined' && result[0].status === 'success') { - var file = result[0]; - } else { - data.textStatus = 'servererror'; - data.errorThrown = t('files', result.data.message); - var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - fu._trigger('fail', e, data); - } +$(document).ready(function() { - var filename = result[0].originalname; + var file_upload_param = { + dropZone: $('#content'), // restrict dropZone to content div + //singleFileUploads is on by default, so the data.files array will always have length 1 + add: function(e, data) { + var that = $(this); - // delete jqXHR reference - if (typeof data.context !== 'undefined' && data.context.data('type') === 'dir') { - var dirName = data.context.data('file'); - delete uploadingFiles[dirName][filename]; - if ($.assocArraySize(uploadingFiles[dirName]) == 0) { - delete uploadingFiles[dirName]; - } - } else { - delete uploadingFiles[filename]; - } + if (typeof data.originalFiles.checked === 'undefined') { + + var totalSize = 0; + $.each(data.originalFiles, function(i, file) { + totalSize += file.size; - }, - /** - * called after last upload - * @param e - * @param data - */ - stop: function(e, data) { - if(data.dataType !== 'iframe') { - $('#uploadprogresswrapper input.stop').hide(); - } + if (file.type === '' && file.size === 4096) { + data.textStatus = 'dirorzero'; + data.errorThrown = t('files', 'Unable to upload {filename} as it is a directory or has 0 bytes', + {filename: file.name} + ); + return false; + } + }); - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return; - } + if (totalSize > $('#max_upload').val()) { + data.textStatus = 'notenoughspace'; + data.errorThrown = t('files', 'Not enough space available'); + } - $('#uploadprogressbar').progressbar('value',100); - $('#uploadprogressbar').fadeOut(); - } - } - var file_upload_handler = function() { - $('#file_upload_start').fileupload(file_upload_param); - }; + if (data.errorThrown) { + //don't upload anything + var fu = that.data('blueimp-fileupload') || that.data('fileupload'); + fu._trigger('fail', e, data); + return false; + } + + data.originalFiles.checked = true; // this will skip the checks on subsequent adds + } + + //TODO check filename already exists + /* + if ($('tr[data-file="'+data.files[0].name+'"][data-id]').length > 0) { + data.textStatus = 'alreadyexists'; + data.errorThrown = t('files', '{filename} already exists', + {filename: data.files[0].name} + ); + //TODO show "file already exists" dialog + var fu = that.data('blueimp-fileupload') || that.data('fileupload'); + fu._trigger('fail', e, data); + return false; + } + */ + //add files to queue + OC.upload.addUpload(data); + + //TODO refactor away: + //show cancel button + if($('html.lte9').length === 0 && data.dataType !== 'iframe') { + $('#uploadprogresswrapper input.stop').show(); + } + return true; + }, + /** + * called after the first add, does NOT have the data param + * @param e + */ + start: function(e) { + //IE < 10 does not fire the necessary events for the progress bar. + if($('html.lte9').length > 0) { + return true; + } + $('#uploadprogressbar').progressbar({value:0}); + $('#uploadprogressbar').fadeIn(); + }, + fail: function(e, data) { + if (typeof data.textStatus !== 'undefined' && data.textStatus !== 'success' ) { + if (data.textStatus === 'abort') { + $('#notification').text(t('files', 'Upload cancelled.')); + } else { + // HTTP connection problem + $('#notification').text(data.errorThrown); + } + $('#notification').fadeIn(); + //hide notification after 5 sec + setTimeout(function() { + $('#notification').fadeOut(); + }, 5000); + } + delete uploadingFiles[data.files[0].name]; + }, + progress: function(e, data) { + // TODO: show nice progress bar in file row + }, + progressall: function(e, data) { + //IE < 10 does not fire the necessary events for the progress bar. + if($('html.lte9').length > 0) { + return; + } + var progress = (data.loaded/data.total)*100; + $('#uploadprogressbar').progressbar('value', progress); + }, + /** + * called for every successful upload + * @param e + * @param data + */ + done:function(e, data) { + // handle different responses (json or body from iframe for ie) + var response; + if (typeof data.result === 'string') { + response = data.result; + } else { + //fetch response from iframe + response = data.result[0].body.innerText; + } + var result=$.parseJSON(response); + if(typeof result[0] !== 'undefined' && result[0].status === 'success') { + OC.upload.nextUpload(); + } else { + if (result[0].status === 'existserror') { + //TODO open dialog and retry with other name? + // get jqXHR reference + if (typeof data.context !== 'undefined' && data.context.data('type') === 'dir') { + var dirName = data.context.data('file'); + var jqXHR = uploadingFiles[dirName][filename]; + } else { + var jqXHR = uploadingFiles[filename]; + } + //filenames can only be changed on the server side + //TODO show "file already exists" dialog + //options: abort | skip | replace / rename + //TODO reset all-files flag? when done with selection? + var original = result[0]; + var replacement = data.files[0]; + OC.dialogs.fileexists(data, original, replacement, OC.upload); + } else { + data.textStatus = 'servererror'; + data.errorThrown = t('files', result.data.message); + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + fu._trigger('fail', e, data); + } + } - if ( document.getElementById('data-upload-form') ) { - $(file_upload_handler); - } - $.assocArraySize = function(obj) { - // http://stackoverflow.com/a/6700/11236 - var size = 0, key; - for (key in obj) { - if (obj.hasOwnProperty(key)) size++; - } - return size; - }; + var filename = result[0].originalname; - // warn user not to leave the page while upload is in progress - $(window).bind('beforeunload', function(e) { - if ($.assocArraySize(uploadingFiles) > 0) - return t('files','File upload is in progress. Leaving the page now will cancel the upload.'); - }); + // delete jqXHR reference + if (typeof data.context !== 'undefined' && data.context.data('type') === 'dir') { + var dirName = data.context.data('file'); + delete uploadingFiles[dirName][filename]; + if ($.assocArraySize(uploadingFiles[dirName]) === 0) { + delete uploadingFiles[dirName]; + } + } else { + delete uploadingFiles[filename]; + } + + }, + /** + * called after last upload + * @param e + * @param data + */ + stop: function(e, data) { + if(data.dataType !== 'iframe') { + $('#uploadprogresswrapper input.stop').hide(); + } - //add multiply file upload attribute to all browsers except konqueror (which crashes when it's used) - if(navigator.userAgent.search(/konqueror/i)==-1){ - $('#file_upload_start').attr('multiple','multiple') - } + //IE < 10 does not fire the necessary events for the progress bar. + if($('html.lte9').length > 0) { + return; + } - //if the breadcrumb is to long, start by replacing foldernames with '...' except for the current folder - var crumb=$('div.crumb').first(); - while($('div.controls').height()>40 && crumb.next('div.crumb').length>0){ - crumb.children('a').text('...'); - crumb=crumb.next('div.crumb'); - } - //if that isn't enough, start removing items from the breacrumb except for the current folder and it's parent - var crumb=$('div.crumb').first(); - var next=crumb.next('div.crumb'); - while($('div.controls').height()>40 && next.next('div.crumb').length>0){ - crumb.remove(); - crumb=next; - next=crumb.next('div.crumb'); - } - //still not enough, start shorting down the current folder name - var crumb=$('div.crumb>a').last(); - while($('div.controls').height()>40 && crumb.text().length>6){ - var text=crumb.text() - text=text.substr(0,text.length-6)+'...'; - crumb.text(text); - } + $('#uploadprogressbar').progressbar('value', 100); + $('#uploadprogressbar').fadeOut(); + } + }; + + var file_upload_handler = function() { + $('#file_upload_start').fileupload(file_upload_param); + }; - $(document).click(function(){ - $('#new>ul').hide(); - $('#new').removeClass('active'); - $('#new li').each(function(i,element){ - if($(element).children('p').length==0){ - $(element).children('form').remove(); - $(element).append('

'+$(element).data('text')+'

'); - } + if ( document.getElementById('data-upload-form') ) { + $(file_upload_handler); + } + $.assocArraySize = function(obj) { + // http://stackoverflow.com/a/6700/11236 + var size = 0, key; + for (key in obj) { + if (obj.hasOwnProperty(key)) { + size++; + } + } + return size; + }; + + // warn user not to leave the page while upload is in progress + $(window).bind('beforeunload', function(e) { + if ($.assocArraySize(uploadingFiles) > 0) { + return t('files', 'File upload is in progress. Leaving the page now will cancel the upload.'); + } }); - }); - $('#new li').click(function(){ - if($(this).children('p').length==0){ - return; + + //add multiply file upload attribute to all browsers except konqueror (which crashes when it's used) + if(navigator.userAgent.search(/konqueror/i) === -1) { + $('#file_upload_start').attr('multiple', 'multiple'); + } + + //if the breadcrumb is to long, start by replacing foldernames with '...' except for the current folder + var crumb = $('div.crumb').first(); + while($('div.controls').height() > 40 && crumb.next('div.crumb').length > 0) { + crumb.children('a').text('...'); + crumb = crumb.next('div.crumb'); + } + //if that isn't enough, start removing items from the breacrumb except for the current folder and it's parent + var crumb = $('div.crumb').first(); + var next = crumb.next('div.crumb'); + while($('div.controls').height() > 40 && next.next('div.crumb').length > 0) { + crumb.remove(); + crumb = next; + next = crumb.next('div.crumb'); } + //still not enough, start shorting down the current folder name + var crumb = $('div.crumb>a').last(); + while($('div.controls').height() > 40 && crumb.text().length > 6) { + var text = crumb.text(); + text = text.substr(0, text.length-6)+'...'; + crumb.text(text); + } + + $(document).click(function() { + $('#new>ul').hide(); + $('#new').removeClass('active'); + $('#new li').each(function(i, element) { + if($(element).children('p').length === 0) { + $(element).children('form').remove(); + $(element).append('

' + $(element).data('text') + '

'); + } + }); + }); + $('#new li').click(function() { + if($(this).children('p').length === 0) { + return; + } - $('#new li').each(function(i,element){ - if($(element).children('p').length==0){ - $(element).children('form').remove(); - $(element).append('

'+$(element).data('text')+'

'); - } + $('#new li').each(function(i, element) { + if($(element).children('p').length === 0) { + $(element).children('form').remove(); + $(element).append('

' + $(element).data('text') + '

'); + } }); - var type=$(this).data('type'); - var text=$(this).children('p').text(); - $(this).data('text',text); + var type = $(this).data('type'); + var text = $(this).children('p').text(); + $(this).data('text', text); $(this).children('p').remove(); - var form=$('
'); - var input=$(''); + var form = $('
'); + var input = $(''); form.append(input); $(this).append(form); input.focus(); - form.submit(function(event){ - event.stopPropagation(); - event.preventDefault(); - var newname=input.val(); - if(type == 'web' && newname.length == 0) { - OC.Notification.show(t('files', 'URL cannot be empty.')); - return false; - } else if (type != 'web' && !Files.isFileNameValid(newname)) { - return false; - } else if( type == 'folder' && $('#dir').val() == '/' && newname == 'Shared') { - OC.Notification.show(t('files','Invalid folder name. Usage of \'Shared\' is reserved by ownCloud')); - return false; - } - if (FileList.lastAction) { - FileList.lastAction(); - } - var name = getUniqueName(newname); - if (newname != name) { - FileList.checkName(name, newname, true); - var hidden = true; - } else { - var hidden = false; - } - switch(type){ - case 'file': - $.post( - OC.filePath('files','ajax','newfile.php'), - {dir:$('#dir').val(),filename:name}, - function(result){ - if (result.status == 'success') { - var date=new Date(); - FileList.addFile(name,0,date,false,hidden); - var tr=$('tr').filterAttr('data-file',name); - tr.attr('data-mime',result.data.mime); - tr.attr('data-id', result.data.id); - getMimeIcon(result.data.mime,function(path){ - tr.find('td.filename').attr('style','background-image:url('+path+')'); - }); - } else { - OC.dialogs.alert(result.data.message, t('core', 'Error')); - } - } - ); - break; - case 'folder': - $.post( - OC.filePath('files','ajax','newfolder.php'), - {dir:$('#dir').val(),foldername:name}, - function(result){ - if (result.status == 'success') { - var date=new Date(); - FileList.addDir(name,0,date,hidden); - var tr=$('tr').filterAttr('data-file',name); - tr.attr('data-id', result.data.id); - } else { - OC.dialogs.alert(result.data.message, t('core', 'Error')); - } - } - ); - break; - case 'web': - if(name.substr(0,8)!='https://' && name.substr(0,7)!='http://'){ - name='http://'+name; - } - var localName=name; - if(localName.substr(localName.length-1,1)=='/'){//strip / - localName=localName.substr(0,localName.length-1) + form.submit(function(event) { + event.stopPropagation(); + event.preventDefault(); + var newname=input.val(); + if(type === 'web' && newname.length === 0) { + OC.Notification.show(t('files', 'URL cannot be empty.')); + return false; + } else if (type !== 'web' && !Files.isFileNameValid(newname)) { + return false; + } else if( type === 'folder' && $('#dir').val() === '/' && newname === 'Shared') { + OC.Notification.show(t('files', 'Invalid folder name. Usage of \'Shared\' is reserved by ownCloud')); + return false; } - if(localName.indexOf('/')){//use last part of url - localName=localName.split('/').pop(); - } else { //or the domain - localName=(localName.match(/:\/\/(.[^\/]+)/)[1]).replace('www.',''); + if (FileList.lastAction) { + FileList.lastAction(); } - localName = getUniqueName(localName); - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { + var name = getUniqueName(newname); + if (newname !== name) { + FileList.checkName(name, newname, true); + var hidden = true; } else { - $('#uploadprogressbar').progressbar({value:0}); - $('#uploadprogressbar').fadeIn(); + var hidden = false; } + switch(type) { + case 'file': + $.post( + OC.filePath('files', 'ajax', 'newfile.php'), + {dir:$('#dir').val(), filename:name}, + function(result) { + if (result.status === 'success') { + var date = new Date(); + FileList.addFile(name, 0, date, false, hidden); + var tr = $('tr').filterAttr('data-file', name); + tr.attr('data-mime', result.data.mime); + tr.attr('data-id', result.data.id); + getMimeIcon(result.data.mime, function(path) { + tr.find('td.filename').attr('style', 'background-image:url('+path+')'); + }); + } else { + OC.dialogs.alert(result.data.message, t('core', 'Error')); + } + } + ); + break; + case 'folder': + $.post( + OC.filePath('files', 'ajax', 'newfolder.php'), + {dir:$('#dir').val(), foldername:name}, + function(result) { + if (result.status === 'success') { + var date = new Date(); + FileList.addDir(name, 0, date, hidden); + var tr = $('tr').filterAttr('data-file', name); + tr.attr('data-id', result.data.id); + } else { + OC.dialogs.alert(result.data.message, t('core', 'Error')); + } + } + ); + break; + case 'web': + if (name.substr(0, 8) !== 'https://' && name.substr(0, 7) !== 'http://') { + name = 'http://' + name; + } + var localName = name; + if(localName.substr(localName.length-1, 1) === '/') { //strip / + localName = localName.substr(0, localName.length-1); + } + if (localName.indexOf('/')) { //use last part of url + localName = localName.split('/').pop(); + } else { //or the domain + localName = (localName.match(/:\/\/(.[^\/]+)/)[1]).replace('www.', ''); + } + localName = getUniqueName(localName); + //IE < 10 does not fire the necessary events for the progress bar. + if ($('html.lte9').length > 0) { + } else { + $('#uploadprogressbar').progressbar({value:0}); + $('#uploadprogressbar').fadeIn(); + } - var eventSource=new OC.EventSource(OC.filePath('files','ajax','newfile.php'),{dir:$('#dir').val(),source:name,filename:localName}); - eventSource.listen('progress',function(progress){ - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - } else { - $('#uploadprogressbar').progressbar('value',progress); - } - }); - eventSource.listen('success',function(data){ - var mime=data.mime; - var size=data.size; - var id=data.id; - $('#uploadprogressbar').fadeOut(); - var date=new Date(); - FileList.addFile(localName,size,date,false,hidden); - var tr=$('tr').filterAttr('data-file',localName); - tr.data('mime',mime).data('id',id); - tr.attr('data-id', id); - getMimeIcon(mime,function(path){ - tr.find('td.filename').attr('style','background-image:url('+path+')'); - }); - }); - eventSource.listen('error',function(error){ - $('#uploadprogressbar').fadeOut(); - alert(error); + var eventSource = new OC.EventSource( + OC.filePath('files', 'ajax', 'newfile.php'), + {dir:$('#dir').val(), source:name, filename:localName} + ); + eventSource.listen('progress', function(progress) { + //IE < 10 does not fire the necessary events for the progress bar. + if($('html.lte9').length > 0) { + } else { + $('#uploadprogressbar').progressbar('value', progress); + } + }); + eventSource.listen('success', function(data) { + var mime = data.mime; + var size = data.size; + var id = data.id; + $('#uploadprogressbar').fadeOut(); + var date = new Date(); + FileList.addFile(localName, size, date, false, hidden); + var tr = $('tr').filterAttr('data-file', localName); + tr.data('mime', mime).data('id', id); + tr.attr('data-id', id); + getMimeIcon(mime, function(path) { + tr.find('td.filename').attr('style', 'background-image:url(' + path + ')'); + }); + }); + eventSource.listen('error', function(error) { + $('#uploadprogressbar').fadeOut(); + alert(error); + }); + break; + } + var li = form.parent(); + form.remove(); + li.append('

' + li.data('text') + '

'); + $('#new>a').click(); }); - break; - } - var li=form.parent(); - form.remove(); - li.append('

'+li.data('text')+'

'); - $('#new>a').click(); + }); - }); + }); -- cgit v1.2.3 From 9da49264ea6edbad13455a3e66d7f369f2e8448f Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Wed, 14 Aug 2013 17:49:45 +0200 Subject: change filelist ui updates --- apps/files/js/file-upload.js | 27 +++++++++++ apps/files/js/filelist.js | 106 ++++++++++++++++++++++--------------------- core/js/jquery.ocdialog.js | 9 ++-- core/js/oc-dialogs.js | 9 +--- 4 files changed, 88 insertions(+), 63 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 71034a0b3f4..bd9757b5ffc 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -1,3 +1,30 @@ +/** + * + * when versioning app is active -> always overwrite + * + * fileupload scenario: empty folder & d&d 20 files + * queue the 20 files + * check list of files for duplicates -> empty + * start uploading the queue (show progress dialog?) + * - no duplicates -> all good, add files to list + * - server reports duplicate -> show skip, replace or rename dialog (for individual files) + * + * fileupload scenario: files uploaded & d&d 20 files again + * queue the 20 files + * check list of files for duplicates -> find n duplicates -> + * show skip, replace or rename dialog as general option + * - show list of differences with preview (win 8) + * remember action for each file + * start uploading the queue (show progress dialog?) + * - no duplicates -> all good, add files to list + * - server reports duplicate -> use remembered action + * + * dialoge: + * -> skip, replace, choose (or abort) () + * -> choose left or right (with skip) (when only one file in list also show rename option and remember for all option) + */ + + OC.upload = { _isProcessing:false, isProcessing:function(){ diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index bcc77e68ce6..f4863837ce7 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -23,7 +23,7 @@ var FileList={ "href": linktarget }); //split extension from filename for non dirs - if (type != 'dir' && name.indexOf('.')!=-1) { + if (type !== 'dir' && name.indexOf('.')!==-1) { basename=name.substr(0,name.lastIndexOf('.')); extension=name.substr(name.lastIndexOf('.')); } else { @@ -36,7 +36,7 @@ var FileList={ name_span.append($('').addClass('extension').text(extension)); } //dirs can show the number of uploaded files - if (type == 'dir') { + if (type === 'dir') { link_elem.append($('').attr({ 'class': 'uploadtext', 'currentUploads': 0 @@ -46,7 +46,7 @@ var FileList={ tr.append(td); //size column - if(size!=t('files', 'Pending')){ + if(size!==t('files', 'Pending')){ simpleSize = humanFileSize(size); }else{ simpleSize=t('files', 'Pending'); @@ -135,7 +135,7 @@ var FileList={ }, refresh:function(data) { var result = jQuery.parseJSON(data.responseText); - if(typeof(result.data.breadcrumb) != 'undefined'){ + if(typeof(result.data.breadcrumb) !== 'undefined'){ updateBreadcrumb(result.data.breadcrumb); } FileList.update(result.data.files); @@ -144,7 +144,7 @@ var FileList={ remove:function(name){ $('tr').filterAttr('data-file',name).find('td.filename').draggable('destroy'); $('tr').filterAttr('data-file',name).remove(); - if($('tr[data-file]').length==0){ + if($('tr[data-file]').length===0){ $('#emptyfolder').show(); } }, @@ -163,14 +163,14 @@ var FileList={ } } if(fileElements.length){ - if(pos==-1){ + if(pos===-1){ $(fileElements[0]).before(element); }else{ $(fileElements[pos]).after(element); } - }else if(type=='dir' && $('tr[data-file]').length>0){ + }else if(type==='dir' && $('tr[data-file]').length>0){ $('tr[data-file]').first().before(element); - } else if(type=='file' && $('tr[data-file]').length>0) { + } else if(type==='file' && $('tr[data-file]').length>0) { $('tr[data-file]').last().before(element); }else{ $('#fileList').append(element); @@ -182,7 +182,7 @@ var FileList={ tr.data('loading',false); mime=tr.data('mime'); tr.attr('data-mime',mime); - if (id != null) { + if (id) { tr.attr('data-id', id); } getMimeIcon(mime,function(path){ @@ -217,7 +217,7 @@ var FileList={ var newname=input.val(); if (!Files.isFileNameValid(newname)) { return false; - } else if (newname != name) { + } else if (newname !== name) { if (FileList.checkName(name, newname, false)) { newname = name; } else { @@ -265,14 +265,14 @@ var FileList={ tr.attr('data-file', newname); var path = td.children('a.name').attr('href'); td.children('a.name').attr('href', path.replace(encodeURIComponent(name), encodeURIComponent(newname))); - if (newname.indexOf('.') > 0 && tr.data('type') != 'dir') { + if (newname.indexOf('.') > 0 && tr.data('type') !== 'dir') { var basename=newname.substr(0,newname.lastIndexOf('.')); } else { var basename=newname; } td.find('a.name span.nametext').text(basename); - if (newname.indexOf('.') > 0 && tr.data('type') != 'dir') { - if (td.find('a.name span.extension').length == 0 ) { + if (newname.indexOf('.') > 0 && tr.data('type') !== 'dir') { + if (td.find('a.name span.extension').length === 0 ) { td.find('a.name span.nametext').append(''); } td.find('a.name span.extension').text(newname.substr(newname.lastIndexOf('.'))); @@ -282,7 +282,7 @@ var FileList={ return false; }); input.keyup(function(event){ - if (event.keyCode == 27) { + if (event.keyCode === 27) { tr.data('renaming',false); form.remove(); td.children('a.name').show(); @@ -347,13 +347,13 @@ var FileList={ FileList.finishReplace(); }; if (!isNewFile) { - OC.Notification.showHtml(t('files', 'replaced {new_name} with {old_name}', {new_name: newName}, {old_name: oldName})+''+t('files', 'undo')+''); + OC.Notification.showHtml(t('files', 'replaced {new_name} with {old_name}', {new_name: newName}, {old_name: oldName})+''+t('files', 'undo')+''); } }, finishReplace:function() { if (!FileList.replaceCanceled && FileList.replaceOldName && FileList.replaceNewName) { $.ajax({url: OC.filePath('files', 'ajax', 'rename.php'), async: false, data: { dir: $('#dir').val(), newname: FileList.replaceNewName, file: FileList.replaceOldName }, success: function(result) { - if (result && result.status == 'success') { + if (result && result.status === 'success') { $('tr').filterAttr('data-replace', 'true').removeAttr('data-replace'); } else { OC.dialogs.alert(result.data.message, 'Error moving file'); @@ -384,7 +384,7 @@ var FileList={ $.post(OC.filePath('files', 'ajax', 'delete.php'), {dir:$('#dir').val(),files:fileNames}, function(result){ - if (result.status == 'success') { + if (result.status === 'success') { $.each(files,function(index,file){ var files = $('tr').filterAttr('data-file',file); files.remove(); @@ -412,6 +412,7 @@ $(document).ready(function(){ if ($('#fileList').length > 0) { var dropTarget = $(e.originalEvent.target).closest('tr'); if(dropTarget && dropTarget.data('type') === 'dir') { // drag&drop upload to folder + data.context = dropTarget; var dirName = dropTarget.data('file'); // update folder in form data.formData = function(form) { @@ -426,7 +427,7 @@ $(document).ready(function(){ formArray[2]['value'] += '/'+dirName; } return formArray; - } + }; } } }); @@ -434,11 +435,11 @@ $(document).ready(function(){ // only add to fileList if it exists if ($('#fileList').length > 0) { - if(FileList.deleteFiles && FileList.deleteFiles.indexOf(data.files[0].name)!=-1){//finish delete if we are uploading a deleted file + if(FileList.deleteFiles && FileList.deleteFiles.indexOf(data.files[0].name)!==-1){//finish delete if we are uploading a deleted file FileList.finishDelete(null, true); //delete file before continuing } - // add ui visualization to existing folder or as new stand-alone file? + // add ui visualization to existing folder var dropTarget = $(e.originalEvent.target).closest('tr'); if(dropTarget && dropTarget.data('type') === 'dir') { // add to existing folder @@ -460,21 +461,6 @@ $(document).ready(function(){ } else { uploadtext.text(currentUploads + ' ' + t('files', 'files uploading')); } - } else { - // add as stand-alone row to filelist - var uniqueName = getUniqueName(data.files[0].name); - var size=t('files','Pending'); - if(data.files[0].size>=0){ - size=data.files[0].size; - } - var date=new Date(); - var param = {}; - if ($('#publicUploadRequestToken').length) { - param.download_url = document.location.href + '&download&path=/' + $('#dir').val() + '/' + uniqueName; - } - // create new file context - data.context = FileList.addFile(uniqueName,size,date,true,false,param); - } } }); @@ -493,18 +479,8 @@ $(document).ready(function(){ if(typeof result[0] !== 'undefined' && result[0].status === 'success') { var file = result[0]; - if (data.context.data('type') === 'file') { - // update file data - data.context.attr('data-mime',file.mime).attr('data-id',file.id); - var size = data.context.data('size'); - if(size!=file.size){ - data.context.attr('data-size', file.size); - data.context.find('td.filesize').text(humanFileSize(file.size)); - } - if (FileList.loadingDone) { - FileList.loadingDone(file.name, file.id); - } - } else { + if (data.context && data.context.data('type') === 'dir') { + // update upload counter ui var uploadtext = data.context.find('.uploadtext'); var currentUploads = parseInt(uploadtext.attr('currentUploads')); @@ -525,6 +501,32 @@ $(document).ready(function(){ data.context.attr('data-size', size); data.context.find('td.filesize').text(humanFileSize(size)); + } else { + + // add as stand-alone row to filelist + var uniqueName = getUniqueName(data.files[0].name); + var size=t('files','Pending'); + if (data.files[0].size>=0){ + size=data.files[0].size; + } + var date=new Date(); + var param = {}; + if ($('#publicUploadRequestToken').length) { + param.download_url = document.location.href + '&download&path=/' + $('#dir').val() + '/' + uniqueName; + } + + //should the file exist in the list remove it + FileList.remove(file.name); + + // create new file context + data.context = FileList.addFile(file.name, file.size, date, false, false, param); + + // update file data + data.context.attr('data-mime',file.mime).attr('data-id',file.id); + + getMimeIcon(file.mime, function(path){ + data.context.find('td.filename').attr('style','background-image:url('+path+')'); + }); } } } @@ -574,16 +576,16 @@ $(document).ready(function(){ FileList.replaceIsNewFile = null; } FileList.lastAction = null; - OC.Notification.hide(); + OC.Notification.hide(); }); $('#notification:first-child').on('click', '.replace', function() { - OC.Notification.hide(function() { - FileList.replace($('#notification > span').attr('data-oldName'), $('#notification > span').attr('data-newName'), $('#notification > span').attr('data-isNewFile')); - }); + OC.Notification.hide(function() { + FileList.replace($('#notification > span').attr('data-oldName'), $('#notification > span').attr('data-newName'), $('#notification > span').attr('data-isNewFile')); + }); }); $('#notification:first-child').on('click', '.suggest', function() { $('tr').filterAttr('data-file', $('#notification > span').attr('data-oldName')).show(); - OC.Notification.hide(); + OC.Notification.hide(); }); $('#notification:first-child').on('click', '.cancel', function() { if ($('#notification > span').attr('data-isNewFile')) { diff --git a/core/js/jquery.ocdialog.js b/core/js/jquery.ocdialog.js index 52ff5633f9a..ce991052271 100644 --- a/core/js/jquery.ocdialog.js +++ b/core/js/jquery.ocdialog.js @@ -40,6 +40,9 @@ } // Escape if(event.keyCode === 27 && self.options.closeOnEscape) { + if (self.closeCB) { + self.closeCB(); + } self.close(); return false; } @@ -190,7 +193,7 @@ } }, widget: function() { - return this.$dialog + return this.$dialog; }, close: function() { this._destroyOverlay(); @@ -203,10 +206,10 @@ }, destroy: function() { if(this.$title) { - this.$title.remove() + this.$title.remove(); } if(this.$buttonrow) { - this.$buttonrow.remove() + this.$buttonrow.remove(); } if(this.originalTitle) { diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index cf77f5018a6..88a3f6628cb 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -297,14 +297,7 @@ var OCdialogs = { closeOnEscape: true, modal: true, buttons: buttonlist, - close: function(event, ui) { - try { - $(this).ocdialog('destroy').remove(); - } catch(e) { - alert (e); - } - self.$ = null; - } + closeButton: null }); OCdialogs.dialogs_counter++; -- cgit v1.2.3 From 2fd5178a0049abc474da551dbdb2ac71fc49dec1 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Thu, 15 Aug 2013 12:50:26 +0200 Subject: adjust New file dialog for new styles --- apps/files/css/files.css | 7 +++++-- apps/files/js/file-upload.js | 2 +- core/img/filetypes/web.png | Bin 2284 -> 2254 bytes core/img/filetypes/web.svg | 24 +++++++++++------------- 4 files changed, 17 insertions(+), 16 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 4f2f10da4dd..20eb5fd083f 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -22,7 +22,10 @@ #new>ul>li { height:36px; margin:.3em; padding-left:3em; padding-bottom:0.1em; background-repeat:no-repeat; cursor:pointer; } #new>ul>li>p { cursor:pointer; padding-top: 7px; padding-bottom: 7px;} -#new>ul>li>form>input { padding:0.3em; margin:-0.3em; } +#new>ul>li>form>input { + padding: 5px; + margin: 2px 0; +} #trash { margin: 0 1em; z-index:1010; float: right; } @@ -189,7 +192,7 @@ table td.filename form { font-size:.85em; margin-left:3em; margin-right:3em; } height: 50px; position: absolute; width: 50px; - z-index: 100; + z-index: 5; } #fileList tr td.filename>input[type="checkbox"] + label { left: 0; diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 942a07dfccc..02e940aa3c4 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -221,7 +221,7 @@ $(document).ready(function() { $(this).data('text',text); $(this).children('p').remove(); var form=$('
'); - var input=$(''); + var input=$(''); form.append(input); $(this).append(form); input.focus(); diff --git a/core/img/filetypes/web.png b/core/img/filetypes/web.png index 0868ca52747..c3802312645 100644 Binary files a/core/img/filetypes/web.png and b/core/img/filetypes/web.png differ diff --git a/core/img/filetypes/web.svg b/core/img/filetypes/web.svg index 6ea49d59fb4..67775a2233b 100644 --- a/core/img/filetypes/web.svg +++ b/core/img/filetypes/web.svg @@ -14,34 +14,32 @@ - + - + - + - + - - - - - - - - - + + + + + + + -- cgit v1.2.3 From f94e6036980644bdd6312e75a8973f2633cf5ff2 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Fri, 16 Aug 2013 11:40:55 +0200 Subject: progress fixes --- apps/files/ajax/upload.php | 9 +- apps/files/css/files.css | 23 +++- apps/files/js/file-upload.js | 263 ++++++++++++++++++++++++++------------- apps/files/js/fileactions.js | 2 +- apps/files/js/filelist.js | 265 ++++++++++++++++++++++++---------------- apps/files/js/files.js | 29 +---- apps/files_sharing/js/public.js | 7 +- core/js/oc-dialogs.js | 215 ++++++++++++++++++-------------- 8 files changed, 503 insertions(+), 310 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index 8d183bd1f9c..619b5f6a04b 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -99,8 +99,8 @@ if (strpos($dir, '..') === false) { $fileCount = count($files['name']); for ($i = 0; $i < $fileCount; $i++) { // $path needs to be normalized - this failed within drag'n'drop upload to a sub-folder - if (isset($_POST['new_name'])) { - $newName = $_POST['new_name']; + if (isset($_POST['newname'])) { + $newName = $_POST['newname']; } else { $newName = $files['name'][$i]; } @@ -109,11 +109,12 @@ if (strpos($dir, '..') === false) { } else { $replace = false; } - $target = \OC\Files\Filesystem::normalizePath(stripslashes($dir).$newName); + $target = \OC\Files\Filesystem::normalizePath(stripslashes($dir).'/'.$newName); if ( ! $replace && \OC\Files\Filesystem::file_exists($target)) { $meta = \OC\Files\Filesystem::getFileInfo($target); $result[] = array('status' => 'existserror', - 'mime' => $meta['mimetype'], + 'type' => $meta['mimetype'], + 'mtime' => $meta['mtime'], 'size' => $meta['size'], 'id' => $meta['fileid'], 'name' => basename($target), diff --git a/apps/files/css/files.css b/apps/files/css/files.css index acee8471aff..0ff25a24d76 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -190,6 +190,9 @@ table.dragshadow td.size { margin-left: -200px; } +.oc-dialog .fileexists table { + width: 100%; +} .oc-dialog .fileexists .original .icon { width: 64px; height: 64px; @@ -201,6 +204,7 @@ table.dragshadow td.size { .oc-dialog .fileexists .replacement { margin-top: 20px; + margin-bottom: 20px; } .oc-dialog .fileexists .replacement .icon { @@ -213,10 +217,23 @@ table.dragshadow td.size { clear: both; } -.oc-dialog .fileexists label[for="new-name"] { - margin-top: 20px; - display: block; +.oc-dialog .fileexists .toggle { + background-image: url('%webroot%/core/img/actions/triangle-e.png'); + width: 16px; + height: 16px; +} +.oc-dialog .fileexists #allfileslabel { + float:right; } +.oc-dialog .fileexists #allfiles { + vertical-align: bottom; + position: relative; + top: -3px; +} +.oc-dialog .fileexists #allfiles + span{ + vertical-align: bottom; +} + .oc-dialog .fileexists h3 { font-weight: bold; } diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index bd9757b5ffc..f8899cb07e6 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -1,4 +1,30 @@ /** + * 1. tracking which file to upload next -> upload queue with data elements added whenever add is called + * 2. tracking progress for each folder individually -> track progress in a progress[dirname] object + * - every new selection increases the total size and number of files for a directory + * - add increases, successful done decreases, skip decreases, cancel decreases + * 3. track selections -> the general skip / overwrite decision is selection based and can change + * - server might send already exists error -> show dialog & remember decision for selection again + * - server sends error, how do we find collection? + * 4. track jqXHR object to prevent browser from navigationg away -> track in a uploads[dirname][filename] object [x] + * + * selections can progress in parrallel but each selection progresses sequentially + * + * -> store everything in context? + * context.folder + * context.element? + * context.progressui? + * context.jqXHR + * context.selection + * context.selection.onExistsAction? + * + * context available in what events? + * build in drop() add dir + * latest in add() add file? add selection! + * progress? -> update progress? + * onsubmit -> context.jqXHR? + * fail() -> + * done() * * when versioning app is active -> always overwrite * @@ -22,24 +48,74 @@ * dialoge: * -> skip, replace, choose (or abort) () * -> choose left or right (with skip) (when only one file in list also show rename option and remember for all option) + * + * progress always based on filesize + * number of files as text, bytes as bar + * */ -OC.upload = { + +//TODO clean uploads when all progress has completed +OC.Upload = { + /** + * map to lookup the selections for a given directory. + * @type Array + */ + _selections: {}, + /* + * queue which progress tracker to use for the next upload + * @type Array + */ + _queue: [], + queueUpload:function(data) { + // add to queue + this._queue.push(data); //remember what to upload next + if ( ! this.isProcessing() ) { + this.startUpload(); + } + }, + getSelection:function(originalFiles) { + if (!originalFiles.selectionKey) { + originalFiles.selectionKey = 'selection-' + $.assocArraySize(this._selections); + this._selections[originalFiles.selectionKey] = { + selectionKey:originalFiles.selectionKey, + files:{}, + totalBytes:0, + loadedBytes:0, + currentFile:0, + uploads:{}, + checked:false + }; + } + return this._selections[originalFiles.selectionKey]; + }, + cancelUpload:function(dir, filename) { + var deleted = false; + jQuery.each(this._selections, function(i, selection) { + if (selection.dir === dir && selection.uploads[filename]) { + delete selection.uploads[filename]; + deleted = true; + return false; // end searching through selections + } + }); + return deleted; + }, + cancelUploads:function() { + jQuery.each(this._selections,function(i,selection){ + jQuery.each(selection.uploads, function (j, jqXHR) { + delete jqXHR; + }); + }); + this._queue = []; + this._isProcessing = false; + }, _isProcessing:false, isProcessing:function(){ return this._isProcessing; }, - _uploadQueue:[], - addUpload:function(data){ - this._uploadQueue.push(data); - - if ( ! OC.upload.isProcessing() ) { - OC.upload.startUpload(); - } - }, startUpload:function(){ - if (this._uploadQueue.length > 0) { + if (this._queue.length > 0) { this._isProcessing = true; this.nextUpload(); return true; @@ -48,32 +124,50 @@ OC.upload = { } }, nextUpload:function(){ - if (this._uploadQueue.length > 0) { - var data = this._uploadQueue.pop(); - var jqXHR = data.submit(); - - // remember jqXHR to show warning to user when he navigates away but an upload is still in progress - if (typeof data.context !== 'undefined' && data.context.data('type') === 'dir') { - var dirName = data.context.data('file'); - if(typeof uploadingFiles[dirName] === 'undefined') { - uploadingFiles[dirName] = {}; - } - uploadingFiles[dirName][data.files[0].name] = jqXHR; - } else { - uploadingFiles[data.files[0].name] = jqXHR; - } + if (this._queue.length > 0) { + var data = this._queue.pop(); + var selection = this.getSelection(data.originalFiles); + selection.uploads[data.files[0]] = data.submit(); + } else { //queue is empty, we are done this._isProcessing = false; + //TODO free data } + }, + progressBytes: function() { + var total = 0; + var loaded = 0; + jQuery.each(this._selections, function (i, selection) { + total += selection.totalBytes; + loaded += selection.loadedBytes; + }); + return (loaded/total)*100; + }, + loadedBytes: function() { + var loaded = 0; + jQuery.each(this._selections, function (i, selection) { + loaded += selection.loadedBytes; + }); + return loaded; + }, + totalBytes: function() { + var total = 0; + jQuery.each(this._selections, function (i, selection) { + total += selection.totalBytes; + }); + return total; + }, + handleExists:function(data) { + }, onCancel:function(data){ //TODO cancel all uploads - Files.cancelUploads(); - this._uploadQueue = []; - this._isProcessing = false; + OC.Upload.cancelUploads(); }, onSkip:function(data){ + var selection = this.getSelection(data.originalFiles); + selection.loadedBytes += data.loaded; this.nextUpload(); }, onReplace:function(data){ @@ -83,8 +177,14 @@ OC.upload = { }, onRename:function(data, newName){ //TODO rename file in filelist, stop spinner - data.data.append('new_name', newName); + data.data.append('newname', newName); data.submit(); + }, + setAction:function(data, action) { + + }, + setDefaultAction:function(action) { + } }; @@ -92,15 +192,23 @@ $(document).ready(function() { var file_upload_param = { dropZone: $('#content'), // restrict dropZone to content div + //singleFileUploads is on by default, so the data.files array will always have length 1 add: function(e, data) { var that = $(this); - - if (typeof data.originalFiles.checked === 'undefined') { + + // lookup selection for dir + var selection = OC.Upload.getSelection(data.originalFiles); + + if (!selection.dir) { + selection.dir = $('#dir').val(); + } + + if ( ! selection.checked ) { - var totalSize = 0; + selection.totalBytes = 0; $.each(data.originalFiles, function(i, file) { - totalSize += file.size; + selection.totalBytes += file.size; if (file.type === '' && file.size === 4096) { data.textStatus = 'dirorzero'; @@ -111,11 +219,10 @@ $(document).ready(function() { } }); - if (totalSize > $('#max_upload').val()) { + if (selection.totalBytes > $('#max_upload').val()) { data.textStatus = 'notenoughspace'; data.errorThrown = t('files', 'Not enough space available'); } - if (data.errorThrown) { //don't upload anything var fu = that.data('blueimp-fileupload') || that.data('fileupload'); @@ -123,9 +230,22 @@ $(document).ready(function() { return false; } - data.originalFiles.checked = true; // this will skip the checks on subsequent adds + //TODO refactor away: + //show cancel button + if($('html.lte9').length === 0 && data.dataType !== 'iframe') { + $('#uploadprogresswrapper input.stop').show(); + } } + //all subsequent add calls for this selection can be ignored + //allow navigating to the selection from a context + //context.selection = data.originalFiles.selection; + + //allow navigating to contexts / files of a selection + selection.files[data.files[0].name] = data; + + OC.Upload.queueUpload(data); + //TODO check filename already exists /* if ($('tr[data-file="'+data.files[0].name+'"][data-id]').length > 0) { @@ -140,14 +260,6 @@ $(document).ready(function() { } */ - //add files to queue - OC.upload.addUpload(data); - - //TODO refactor away: - //show cancel button - if($('html.lte9').length === 0 && data.dataType !== 'iframe') { - $('#uploadprogresswrapper input.stop').show(); - } return true; }, /** @@ -176,7 +288,8 @@ $(document).ready(function() { $('#notification').fadeOut(); }, 5000); } - delete uploadingFiles[data.files[0].name]; + var selection = OC.Upload.getSelection(data.originalFiles); + delete selection.uploads[data.files[0]]; }, progress: function(e, data) { // TODO: show nice progress bar in file row @@ -186,7 +299,8 @@ $(document).ready(function() { if($('html.lte9').length > 0) { return; } - var progress = (data.loaded/data.total)*100; + //var progress = (data.loaded/data.total)*100; + var progress = OC.Upload.progressBytes(); $('#uploadprogressbar').progressbar('value', progress); }, /** @@ -204,27 +318,22 @@ $(document).ready(function() { response = data.result[0].body.innerText; } var result=$.parseJSON(response); + var selection = OC.Upload.getSelection(data.originalFiles); - if(typeof result[0] !== 'undefined' && result[0].status === 'success') { - OC.upload.nextUpload(); + if(typeof result[0] !== 'undefined' + && result[0].status === 'success' + ) { + selection.loadedBytes+=data.loaded; + OC.Upload.nextUpload(); } else { if (result[0].status === 'existserror') { - //TODO open dialog and retry with other name? - // get jqXHR reference - if (typeof data.context !== 'undefined' && data.context.data('type') === 'dir') { - var dirName = data.context.data('file'); - var jqXHR = uploadingFiles[dirName][filename]; - } else { - var jqXHR = uploadingFiles[filename]; - } - //filenames can only be changed on the server side - //TODO show "file already exists" dialog - //options: abort | skip | replace / rename - //TODO reset all-files flag? when done with selection? + //show "file already exists" dialog var original = result[0]; var replacement = data.files[0]; - OC.dialogs.fileexists(data, original, replacement, OC.upload); + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu); } else { + delete selection.uploads[data.files[0]]; data.textStatus = 'servererror'; data.errorThrown = t('files', result.data.message); var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); @@ -232,19 +341,6 @@ $(document).ready(function() { } } - var filename = result[0].originalname; - - // delete jqXHR reference - if (typeof data.context !== 'undefined' && data.context.data('type') === 'dir') { - var dirName = data.context.data('file'); - delete uploadingFiles[dirName][filename]; - if ($.assocArraySize(uploadingFiles[dirName]) === 0) { - delete uploadingFiles[dirName]; - } - } else { - delete uploadingFiles[filename]; - } - }, /** * called after last upload @@ -252,17 +348,20 @@ $(document).ready(function() { * @param data */ stop: function(e, data) { - if(data.dataType !== 'iframe') { - $('#uploadprogresswrapper input.stop').hide(); - } + if(OC.Upload.progressBytes()>=100) { - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return; - } + if(data.dataType !== 'iframe') { + $('#uploadprogresswrapper input.stop').hide(); + } - $('#uploadprogressbar').progressbar('value', 100); - $('#uploadprogressbar').fadeOut(); + //IE < 10 does not fire the necessary events for the progress bar. + if($('html.lte9').length > 0) { + return; + } + + $('#uploadprogressbar').progressbar('value', 100); + $('#uploadprogressbar').fadeOut(); + } } }; diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index aa66a57a7b5..277abcfdb15 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -174,7 +174,7 @@ $(document).ready(function () { FileActions.register('all', 'Delete', OC.PERMISSION_DELETE, function () { return OC.imagePath('core', 'actions/delete'); }, function (filename) { - if (Files.cancelUpload(filename)) { + if (OC.Upload.cancelUpload($('#dir').val(), filename)) { if (filename.substr) { filename = [filename]; } diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index f4863837ce7..335f81e04b9 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -407,151 +407,212 @@ $(document).ready(function(){ // handle upload events var file_upload_start = $('#file_upload_start'); + file_upload_start.on('fileuploaddrop', function(e, data) { - // only handle drop to dir if fileList exists - if ($('#fileList').length > 0) { - var dropTarget = $(e.originalEvent.target).closest('tr'); - if(dropTarget && dropTarget.data('type') === 'dir') { // drag&drop upload to folder - data.context = dropTarget; - var dirName = dropTarget.data('file'); - // update folder in form - data.formData = function(form) { - var formArray = form.serializeArray(); - // array index 0 contains the max files size - // array index 1 contains the request token - // array index 2 contains the directory - var parentDir = formArray[2]['value']; - if (parentDir === '/') { - formArray[2]['value'] += dirName; - } else { - formArray[2]['value'] += '/'+dirName; - } - return formArray; - }; + console.log('fileuploaddrop ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + + var dropTarget = $(e.originalEvent.target).closest('tr'); + if(dropTarget && dropTarget.data('type') === 'dir') { // drag&drop upload to folder + + // lookup selection for dir + var selection = OC.Upload.getSelection(data.files); + + // remember drop target + selection.dropTarget = dropTarget; + + selection.dir = dropTarget.data('file'); + if (selection.dir !== '/') { + if ($('#dir').val() === '/') { + selection.dir = '/' + selection.dir; + } else { + selection.dir = $('#dir').val() + '/' + selection.dir; + } } - } + + // update folder in form + data.formData = function(form) { + var formArray = form.serializeArray(); + // array index 0 contains the max files size + // array index 1 contains the request token + // array index 2 contains the directory + var parentDir = formArray[2]['value']; + if (parentDir === '/') { + formArray[2]['value'] += selection.dir; + } else { + formArray[2]['value'] += '/' + selection.dir; + } + + return formArray; + }; + } + }); file_upload_start.on('fileuploadadd', function(e, data) { - // only add to fileList if it exists - if ($('#fileList').length > 0) { + console.log('fileuploadadd ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + + // lookup selection for dir + var selection = OC.Upload.getSelection(data.originalFiles); + + if(FileList.deleteFiles && FileList.deleteFiles.indexOf(data.files[0].name)!==-1){//finish delete if we are uploading a deleted file + FileList.finishDelete(null, true); //delete file before continuing + } + + // add ui visualization to existing folder + if(selection.dropTarget && selection.dropTarget.data('type') === 'dir') { + // add to existing folder + var dirName = selection.dropTarget.data('file'); + + // set dir context + data.context = $('tr').filterAttr('data-type', 'dir').filterAttr('data-file', dirName); - if(FileList.deleteFiles && FileList.deleteFiles.indexOf(data.files[0].name)!==-1){//finish delete if we are uploading a deleted file - FileList.finishDelete(null, true); //delete file before continuing + // update upload counter ui + var uploadtext = data.context.find('.uploadtext'); + var currentUploads = parseInt(uploadtext.attr('currentUploads')); + currentUploads += 1; + uploadtext.attr('currentUploads', currentUploads); + + if(currentUploads === 1) { + var img = OC.imagePath('core', 'loading.gif'); + data.context.find('td.filename').attr('style','background-image:url('+img+')'); + uploadtext.text(t('files', '1 file uploading')); + uploadtext.show(); + } else { + uploadtext.text(currentUploads + ' ' + t('files', 'files uploading')); } + } + + }); + file_upload_start.on('fileuploaddone', function(e, data) { + console.log('fileuploaddone ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + + var response; + if (typeof data.result === 'string') { + response = data.result; + } else { + // fetch response from iframe + response = data.result[0].body.innerText; + } + var result=$.parseJSON(response); - // add ui visualization to existing folder - var dropTarget = $(e.originalEvent.target).closest('tr'); - if(dropTarget && dropTarget.data('type') === 'dir') { - // add to existing folder - var dirName = dropTarget.data('file'); + if(typeof result[0] !== 'undefined' && result[0].status === 'success') { + var file = result[0]; - // set dir context - data.context = $('tr').filterAttr('data-type', 'dir').filterAttr('data-file', dirName); + if (data.context && data.context.data('type') === 'dir') { // update upload counter ui var uploadtext = data.context.find('.uploadtext'); var currentUploads = parseInt(uploadtext.attr('currentUploads')); - currentUploads += 1; + currentUploads -= 1; uploadtext.attr('currentUploads', currentUploads); - if(currentUploads === 1) { - var img = OC.imagePath('core', 'loading.gif'); + if(currentUploads === 0) { + var img = OC.imagePath('core', 'filetypes/folder.png'); data.context.find('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.text(t('files', '1 file uploading')); - uploadtext.show(); + uploadtext.text(''); + uploadtext.hide(); } else { uploadtext.text(currentUploads + ' ' + t('files', 'files uploading')); } - } - } - }); - file_upload_start.on('fileuploaddone', function(e, data) { - // only update the fileList if it exists - if ($('#fileList').length > 0) { - var response; - if (typeof data.result === 'string') { - response = data.result; + + // update folder size + var size = parseInt(data.context.data('size')); + size += parseInt(file.size); + data.context.attr('data-size', size); + data.context.find('td.filesize').text(humanFileSize(size)); + } else { - // fetch response from iframe - response = data.result[0].body.innerText; - } - var result=$.parseJSON(response); - - if(typeof result[0] !== 'undefined' && result[0].status === 'success') { - var file = result[0]; - - if (data.context && data.context.data('type') === 'dir') { - - // update upload counter ui - var uploadtext = data.context.find('.uploadtext'); - var currentUploads = parseInt(uploadtext.attr('currentUploads')); - currentUploads -= 1; - uploadtext.attr('currentUploads', currentUploads); - if(currentUploads === 0) { - var img = OC.imagePath('core', 'filetypes/folder.png'); - data.context.find('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.text(''); - uploadtext.hide(); - } else { - uploadtext.text(currentUploads + ' ' + t('files', 'files uploading')); - } - // update folder size - var size = parseInt(data.context.data('size')); - size += parseInt(file.size) ; - data.context.attr('data-size', size); - data.context.find('td.filesize').text(humanFileSize(size)); + // add as stand-alone row to filelist + var size=t('files','Pending'); + if (data.files[0].size>=0){ + size=data.files[0].size; + } + var date=new Date(); + var param = {}; + if ($('#publicUploadRequestToken').length) { + param.download_url = document.location.href + '&download&path=/' + $('#dir').val() + '/' + file.name; + } + //should the file exist in the list remove it + FileList.remove(file.name); + + // create new file context + data.context = FileList.addFile(file.name, file.size, date, false, false, param); - } else { - - // add as stand-alone row to filelist - var uniqueName = getUniqueName(data.files[0].name); - var size=t('files','Pending'); - if (data.files[0].size>=0){ - size=data.files[0].size; - } - var date=new Date(); - var param = {}; - if ($('#publicUploadRequestToken').length) { - param.download_url = document.location.href + '&download&path=/' + $('#dir').val() + '/' + uniqueName; - } - - //should the file exist in the list remove it - FileList.remove(file.name); + // update file data + data.context.attr('data-mime',file.mime).attr('data-id',file.id); - // create new file context - data.context = FileList.addFile(file.name, file.size, date, false, false, param); - - // update file data - data.context.attr('data-mime',file.mime).attr('data-id',file.id); - - getMimeIcon(file.mime, function(path){ - data.context.find('td.filename').attr('style','background-image:url('+path+')'); - }); - } + getMimeIcon(file.mime, function(path){ + data.context.find('td.filename').attr('style','background-image:url('+path+')'); + }); } } }); + + file_upload_start.on('fileuploadalways', function(e, data) { + console.log('fileuploadalways ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + }); + file_upload_start.on('fileuploadsend', function(e, data) { + console.log('fileuploadsend ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + + // TODOD add vis + //data.context.element = + }); + file_upload_start.on('fileuploadprogress', function(e, data) { + console.log('fileuploadprogress ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + }); + file_upload_start.on('fileuploadprogressall', function(e, data) { + console.log('fileuploadprogressall ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + }); + file_upload_start.on('fileuploadstop', function(e, data) { + console.log('fileuploadstop ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + }); + file_upload_start.on('fileuploadfail', function(e, data) { + console.log('fileuploadfail ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + }); + /* file_upload_start.on('fileuploadfail', function(e, data) { - // only update the fileList if it exists + console.log('fileuploadfail'+((data.files&&data.files.length>0)?' '+data.files[0].name:'')); + + // if we are uploading to a subdirectory + if (data.context && data.context.data('type') === 'dir') { + + // update upload counter ui + var uploadtext = data.context.find('.uploadtext'); + var currentUploads = parseInt(uploadtext.attr('currentUploads')); + currentUploads -= 1; + uploadtext.attr('currentUploads', currentUploads); + if(currentUploads === 0) { + var img = OC.imagePath('core', 'filetypes/folder.png'); + data.context.find('td.filename').attr('style','background-image:url('+img+')'); + uploadtext.text(''); + uploadtext.hide(); + } else { + uploadtext.text(currentUploads + ' ' + t('files', 'files uploading')); + } + + } + // cleanup files, error notification has been shown by fileupload code var tr = data.context; if (typeof tr === 'undefined') { tr = $('tr').filterAttr('data-file', data.files[0].name); } if (tr.attr('data-type') === 'dir') { + //cleanup uploading to a dir var uploadtext = tr.find('.uploadtext'); var img = OC.imagePath('core', 'filetypes/folder.png'); tr.find('td.filename').attr('style','background-image:url('+img+')'); uploadtext.text(''); uploadtext.hide(); //TODO really hide already + } else { + //TODO add row when sending file //remove file tr.fadeOut(); tr.remove(); } }); - +*/ $('#notification').hide(); $('#notification').on('click', '.undo', function(){ if (FileList.deleteFiles) { diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 3fad3fae7d3..a907aeab1fc 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -1,31 +1,5 @@ var uploadingFiles = {}; Files={ - cancelUpload:function(filename) { - if(uploadingFiles[filename]) { - uploadingFiles[filename].abort(); - delete uploadingFiles[filename]; - return true; - } - return false; - }, - cancelUploads:function() { - $.each(uploadingFiles,function(index,file) { - if(typeof file['abort'] === 'function') { - file.abort(); - var filename = $('tr').filterAttr('data-file',index); - filename.hide(); - filename.find('input[type="checkbox"]').removeAttr('checked'); - filename.removeClass('selected'); - } else { - $.each(file,function(i,f) { - f.abort(); - delete file[i]; - }); - } - delete uploadingFiles[index]; - }); - procesSelection(); - }, updateMaxUploadFilesize:function(response) { if(response == undefined) { return; @@ -117,7 +91,8 @@ $(document).ready(function() { // Trigger cancelling of file upload $('#uploadprogresswrapper .stop').on('click', function() { - Files.cancelUploads(); + OC.Upload.cancelUploads(); + procesSelection(); }); // Show trash bin diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index 294223aa094..a20b4ae636f 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -62,7 +62,10 @@ $(document).ready(function() { // Add Uploadprogress Wrapper to controls bar $('#controls').append($('#additional_controls div#uploadprogresswrapper')); - // Cancel upload trigger - $('#cancel_upload_button').click(Files.cancelUploads); + // Cancel upload trigger + $('#cancel_upload_button').click(function() { + OC.Upload.cancelUploads(); + procesSelection(); + }); }); diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index 88a3f6628cb..ea03ef21455 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -207,105 +207,142 @@ var OCdialogs = { * @param {object} controller a controller with onCancel, onSkip, onReplace and onRename methods */ fileexists:function(data, original, replacement, controller) { - if (typeof controller !== 'object') { - controller = {}; - } - var self = this; - $.when(this._getFileExistsTemplate()).then(function($tmpl) { - var dialog_name = 'oc-dialog-fileexists-' + OCdialogs.dialogs_counter + '-content'; - var dialog_id = '#' + dialog_name; - var title = t('files','Replace »{filename}«?',{filename: original.name}); - var $dlg = $tmpl.octemplate({ - dialog_name: dialog_name, - title: title, - type: 'fileexists', - - why: t('files','Another file with the same name already exists in "{dir}".',{dir:'somedir'}), - what: t('files','Replacing it will overwrite it\'s contents.'), - original_heading: t('files','Original file'), - original_size: t('files','Size: {size}',{size: original.size}), - original_mtime: t('files','Last changed: {mtime}',{mtime: original.mtime}), + var selection = controller.getSelection(data.originalFiles); + if (selection.defaultAction) { + controller[selection.defaultAction](data); + } else { + $.when(this._getFileExistsTemplate()).then(function($tmpl) { + var dialog_name = 'oc-dialog-fileexists-' + OCdialogs.dialogs_counter + '-content'; + var dialog_id = '#' + dialog_name; + var title = t('files','Replace »{filename}«?',{filename: original.name}); + var original_size= t('files','Size: {size}',{size: original.size}); + var original_mtime = t('files','Last changed: {mtime}',{mtime: original.mtime}); + var replacement_size= t('files','Size: {size}',{size: replacement.size}); + var replacement_mtime = t('files','Last changed: {mtime}',{mtime: replacement.mtime}); + var $dlg = $tmpl.octemplate({ + dialog_name: dialog_name, + title: title, + type: 'fileexists', - replacement_heading: t('files','Replace with'), - replacement_size: t('files','Size: {size}',{size: replacement.size}), - replacement_mtime: t('files','Last changed: {mtime}',{mtime: replacement.mtime}), + why: t('files','Another file with the same name already exists in "{dir}".',{dir:'somedir'}), + what: t('files','Replacing it will overwrite it\'s contents.'), + original_heading: t('files','Original file'), + original_size: original_size, + original_mtime: original_mtime, - new_name_label: t('files','Choose a new name for the target.'), - all_files_label: t('files','Use this action for all files.') - }); - $('body').append($dlg); - - $(dialog_id + ' .original .icon').css('background-image','url('+OC.imagePath('core', 'filetypes/file.png')+')'); - $(dialog_id + ' .replacement .icon').css('background-image','url('+OC.imagePath('core', 'filetypes/file.png')+')'); - $(dialog_id + ' #new-name').val(original.name); - - - $(dialog_id + ' #new-name').on('keyup', function(e){ - if ($(dialog_id + ' #new-name').val() === original.name) { - - $(dialog_id + ' + div .rename').removeClass('primary').hide(); - $(dialog_id + ' + div .replace').addClass('primary').show(); - } else { - $(dialog_id + ' + div .rename').addClass('primary').show(); - $(dialog_id + ' + div .replace').removeClass('primary').hide(); - } - }); + replacement_heading: t('files','Replace with'), + replacement_size: replacement_size, + replacement_mtime: replacement_mtime, - buttonlist = [{ - text: t('core', 'Cancel'), - classes: 'cancel', - click: function(){ - if ( typeof controller.onCancel !== 'undefined') { - controller.onCancel(data); - } - $(dialog_id).ocdialog('close'); + new_name_label: t('files','Choose a new name for the target.'), + all_files_label: t('files','Use this action for all files.') + }); + $('body').append($dlg); + + getMimeIcon(original.type,function(path){ + $(dialog_id + ' .original .icon').css('background-image','url('+path+')'); + }); + getMimeIcon(replacement.type,function(path){ + $(dialog_id + ' .replacement .icon').css('background-image','url('+path+')'); + }); + $(dialog_id + ' #newname').val(original.name); + + + $(dialog_id + ' #newname').on('keyup', function(e){ + if ($(dialog_id + ' #newname').val() === original.name) { + $(dialog_id + ' + div .rename').removeClass('primary').hide(); + $(dialog_id + ' + div .replace').addClass('primary').show(); + } else { + $(dialog_id + ' + div .rename').addClass('primary').show(); + $(dialog_id + ' + div .replace').removeClass('primary').hide(); } - }, - { - text: t('core', 'Skip'), - classes: 'skip', - click: function(){ - if ( typeof controller.onSkip !== 'undefined') { - controller.onSkip(data); + }); + + buttonlist = [{ + text: t('core', 'Cancel'), + classes: 'cancel', + click: function(){ + if ( typeof controller.onCancel !== 'undefined') { + controller.onCancel(data); + } + $(dialog_id).ocdialog('close'); } - $(dialog_id).ocdialog('close'); - } - }, - { - text: t('core', 'Replace'), - classes: 'replace', - click: function(){ - if ( typeof controller.onReplace !== 'undefined') { - controller.onReplace(data); + }, + { + text: t('core', 'Skip'), + classes: 'skip', + click: function(){ + if ( typeof controller.onSkip !== 'undefined') { + if($(dialog_id + ' #allfiles').prop('checked')){ + selection.defaultAction = 'onSkip'; + /*selection.defaultAction = function(){ + controller.onSkip(data); + };*/ + } + controller.onSkip(data); + } + // trigger fileupload done with status skip + //data.result[0].status = 'skip'; + //fileupload._trigger('done', data.e, data); + $(dialog_id).ocdialog('close'); } - $(dialog_id).ocdialog('close'); }, - defaultButton: true - }, - { - text: t('core', 'Rename'), - classes: 'rename', - click: function(){ - if ( typeof controller.onRename !== 'undefined') { - controller.onRename(data, $(dialog_id + ' #new-name').val()); + { + text: t('core', 'Replace'), + classes: 'replace', + click: function(){ + if ( typeof controller.onReplace !== 'undefined') { + if($(dialog_id + ' #allfiles').prop('checked')){ + selection.defaultAction = 'onReplace'; + /*selection.defaultAction = function(){ + controller.onReplace(data); + };*/ + } + controller.onReplace(data); + } + $(dialog_id).ocdialog('close'); + }, + defaultButton: true + }, + { + text: t('core', 'Rename'), + classes: 'rename', + click: function(){ + if ( typeof controller.onRename !== 'undefined') { + //TODO use autorename when repeat is checked + controller.onRename(data, $(dialog_id + ' #newname').val()); + } + $(dialog_id).ocdialog('close'); } - $(dialog_id).ocdialog('close'); - } - }]; + }]; - $(dialog_id).ocdialog({ - closeOnEscape: true, - modal: true, - buttons: buttonlist, - closeButton: null + $(dialog_id).ocdialog({ + width: 500, + closeOnEscape: true, + modal: true, + buttons: buttonlist, + closeButton: null + }); + OCdialogs.dialogs_counter++; + + $(dialog_id + ' + div .rename').hide(); + $(dialog_id + ' #newname').hide(); + + $(dialog_id + ' #newnamecb').on('change', function(){ + if ($(dialog_id + ' #newnamecb').prop('checked')) { + $(dialog_id + ' #newname').fadeIn(); + } else { + $(dialog_id + ' #newname').fadeOut(); + $(dialog_id + ' #newname').val(original.name); + } + }); + + + }) + .fail(function() { + alert(t('core', 'Error loading file exists template')); }); - OCdialogs.dialogs_counter++; - - $(dialog_id + ' + div .rename').hide(); - }) - .fail(function() { - alert(t('core', 'Error loading file exists template')); - }); + } }, _getFilePickerTemplate: function() { var defer = $.Deferred(); -- cgit v1.2.3 From bf04daff82758fe9913c706eef07aed30c9b35ea Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Wed, 21 Aug 2013 14:58:28 +0200 Subject: architecture too complex --- 3rdparty | 2 +- apps/files/js/file-upload.js | 109 +++- apps/files/js/filelist.js | 51 +- apps/files/js/files.js | 7 - apps/files/js/jquery.fileupload.js | 1023 ++++++++++++++++++++++-------- apps/files/js/jquery.iframe-transport.js | 70 +- apps/files/templates/part.list.php | 11 +- 7 files changed, 934 insertions(+), 339 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/3rdparty b/3rdparty index 2f3ae9f56a9..75a05d76ab8 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 2f3ae9f56a9838b45254393e13c14f8a8c380d6b +Subproject commit 75a05d76ab86ba7454b4312fd0ff2ca5bd5828cf diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index f8899cb07e6..c620942170c 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -1,4 +1,7 @@ /** + * + * use put t ocacnel upload before it starts? use chunked uploads? + * * 1. tracking which file to upload next -> upload queue with data elements added whenever add is called * 2. tracking progress for each folder individually -> track progress in a progress[dirname] object * - every new selection increases the total size and number of files for a directory @@ -63,6 +66,7 @@ OC.Upload = { * @type Array */ _selections: {}, + _selectionCount: 0, /* * queue which progress tracker to use for the next upload * @type Array @@ -77,7 +81,7 @@ OC.Upload = { }, getSelection:function(originalFiles) { if (!originalFiles.selectionKey) { - originalFiles.selectionKey = 'selection-' + $.assocArraySize(this._selections); + originalFiles.selectionKey = 'selection-' + this._selectionCount++; this._selections[originalFiles.selectionKey] = { selectionKey:originalFiles.selectionKey, files:{}, @@ -90,22 +94,41 @@ OC.Upload = { } return this._selections[originalFiles.selectionKey]; }, + deleteSelection:function(selectionKey) { + if (this._selections[selectionKey]) { + jQuery.each(this._selections[selectionKey].uploads, function(i, upload) { + upload.abort(); + }); + delete this._selections[selectionKey]; + } else { + console.log('OC.Upload: selection ' + selectionKey + ' does not exist'); + } + }, + deleteSelectionUpload:function(selection, filename) { + if(selection.uploads[filename]) { + selection.uploads[filename].abort(); + return true; + } else { + console.log('OC.Upload: selection ' + selection.selectionKey + ' does not contain upload for ' + filename); + } + return false; + }, cancelUpload:function(dir, filename) { + var self = this; var deleted = false; jQuery.each(this._selections, function(i, selection) { if (selection.dir === dir && selection.uploads[filename]) { - delete selection.uploads[filename]; - deleted = true; + deleted = self.deleteSelectionUpload(selection, filename); return false; // end searching through selections } }); return deleted; }, cancelUploads:function() { - jQuery.each(this._selections,function(i,selection){ - jQuery.each(selection.uploads, function (j, jqXHR) { - delete jqXHR; - }); + console.log('canceling uploads'); + var self = this; + jQuery.each(this._selections,function(i, selection){ + self.deleteSelection(selection.selectionKey); }); this._queue = []; this._isProcessing = false; @@ -132,7 +155,7 @@ OC.Upload = { } else { //queue is empty, we are done this._isProcessing = false; - //TODO free data + OC.Upload.cancelUploads(); } }, progressBytes: function() { @@ -157,13 +180,13 @@ OC.Upload = { total += selection.totalBytes; }); return total; - }, - handleExists:function(data) { - }, onCancel:function(data){ - //TODO cancel all uploads - OC.Upload.cancelUploads(); + //TODO cancel all uploads of this selection + + var selection = this.getSelection(data.originalFiles); + OC.Upload.deleteSelection(selection.selectionKey); + //FIXME hide progressbar }, onSkip:function(data){ var selection = this.getSelection(data.originalFiles); @@ -171,20 +194,19 @@ OC.Upload = { this.nextUpload(); }, onReplace:function(data){ - //TODO overwrite file data.data.append('replace', true); data.submit(); }, onRename:function(data, newName){ - //TODO rename file in filelist, stop spinner data.data.append('newname', newName); data.submit(); }, - setAction:function(data, action) { - - }, - setDefaultAction:function(action) { - + logStatus:function(caption, e, data) { + console.log(caption+' ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + if (data) { + console.log(data); + } + console.log(e); } }; @@ -195,6 +217,7 @@ $(document).ready(function() { //singleFileUploads is on by default, so the data.files array will always have length 1 add: function(e, data) { + OC.Upload.logStatus('add', e, data); var that = $(this); // lookup selection for dir @@ -267,14 +290,17 @@ $(document).ready(function() { * @param e */ start: function(e) { + OC.Upload.logStatus('start', e, null); //IE < 10 does not fire the necessary events for the progress bar. if($('html.lte9').length > 0) { return true; } + $('#uploadprogresswrapper input.stop').show(); $('#uploadprogressbar').progressbar({value:0}); $('#uploadprogressbar').fadeIn(); }, fail: function(e, data) { + OC.Upload.logStatus('fail', e, data); if (typeof data.textStatus !== 'undefined' && data.textStatus !== 'success' ) { if (data.textStatus === 'abort') { $('#notification').text(t('files', 'Upload cancelled.')); @@ -289,12 +315,26 @@ $(document).ready(function() { }, 5000); } var selection = OC.Upload.getSelection(data.originalFiles); - delete selection.uploads[data.files[0]]; + OC.Upload.deleteSelectionUpload(selection, data.files[0].name); + + //if user pressed cancel hide upload progress bar and cancel button + if (data.errorThrown === 'abort') { + $('#uploadprogresswrapper input.stop').fadeOut(); + $('#uploadprogressbar').fadeOut(); + } }, progress: function(e, data) { + OC.Upload.logStatus('progress', e, data); // TODO: show nice progress bar in file row }, + /** + * + * @param {type} e + * @param {type} data (only has loaded, total and lengthComputable) + * @returns {unresolved} + */ progressall: function(e, data) { + OC.Upload.logStatus('progressall', e, data); //IE < 10 does not fire the necessary events for the progress bar. if($('html.lte9').length > 0) { return; @@ -309,6 +349,7 @@ $(document).ready(function() { * @param data */ done:function(e, data) { + OC.Upload.logStatus('done', e, data); // handle different responses (json or body from iframe for ie) var response; if (typeof data.result === 'string') { @@ -323,7 +364,9 @@ $(document).ready(function() { if(typeof result[0] !== 'undefined' && result[0].status === 'success' ) { - selection.loadedBytes+=data.loaded; + if (selection) { + selection.loadedBytes+=data.loaded; + } OC.Upload.nextUpload(); } else { if (result[0].status === 'existserror') { @@ -333,13 +376,19 @@ $(document).ready(function() { var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu); } else { - delete selection.uploads[data.files[0]]; + OC.Upload.deleteSelectionUpload(selection, data.files[0].name); data.textStatus = 'servererror'; data.errorThrown = t('files', result.data.message); var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); fu._trigger('fail', e, data); } } + + //if user pressed cancel hide upload chrome + if (! OC.Upload.isProcessing()) { + $('#uploadprogresswrapper input.stop').fadeOut(); + $('#uploadprogressbar').fadeOut(); + } }, /** @@ -348,7 +397,10 @@ $(document).ready(function() { * @param data */ stop: function(e, data) { - if(OC.Upload.progressBytes()>=100) { + OC.Upload.logStatus('stop', e, data); + if(OC.Upload.progressBytes()>=100) { //only hide controls when all selections have ended uploading + + OC.Upload.cancelUploads(); //cleanup if(data.dataType !== 'iframe') { $('#uploadprogresswrapper input.stop').hide(); @@ -362,6 +414,11 @@ $(document).ready(function() { $('#uploadprogressbar').progressbar('value', 100); $('#uploadprogressbar').fadeOut(); } + //if user pressed cancel hide upload chrome + if (! OC.Upload.isProcessing()) { + $('#uploadprogresswrapper input.stop').fadeOut(); + $('#uploadprogressbar').fadeOut(); + } } }; @@ -384,8 +441,8 @@ $(document).ready(function() { }; // warn user not to leave the page while upload is in progress - $(window).bind('beforeunload', function(e) { - if ($.assocArraySize(uploadingFiles) > 0) { + $(window).on('beforeunload', function(e) { + if (OC.Upload.isProcessing()) { return t('files', 'File upload is in progress. Leaving the page now will cancel the upload.'); } }); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 335f81e04b9..eb57672e464 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -409,7 +409,7 @@ $(document).ready(function(){ var file_upload_start = $('#file_upload_start'); file_upload_start.on('fileuploaddrop', function(e, data) { - console.log('fileuploaddrop ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploaddrop', e, data); var dropTarget = $(e.originalEvent.target).closest('tr'); if(dropTarget && dropTarget.data('type') === 'dir') { // drag&drop upload to folder @@ -448,7 +448,7 @@ $(document).ready(function(){ }); file_upload_start.on('fileuploadadd', function(e, data) { - console.log('fileuploadadd ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadadd', e, data); // lookup selection for dir var selection = OC.Upload.getSelection(data.originalFiles); @@ -482,8 +482,11 @@ $(document).ready(function(){ } }); + file_upload_start.on('fileuploadstart', function(e, data) { + OC.Upload.logStatus('fileuploadstart', e, data); + }); file_upload_start.on('fileuploaddone', function(e, data) { - console.log('fileuploaddone ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploaddone', e, data); var response; if (typeof data.result === 'string') { @@ -545,28 +548,58 @@ $(document).ready(function(){ }); } } + + //if user pressed cancel hide upload chrome + if (! OC.Upload.isProcessing()) { + //cleanup uploading to a dir + var uploadtext = $('tr .uploadtext'); + var img = OC.imagePath('core', 'filetypes/folder.png'); + uploadtext.parents('td.filename').attr('style','background-image:url('+img+')'); + uploadtext.fadeOut(); + uploadtext.attr('currentUploads', 0); + } }); file_upload_start.on('fileuploadalways', function(e, data) { - console.log('fileuploadalways ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadalways', e, data); }); file_upload_start.on('fileuploadsend', function(e, data) { - console.log('fileuploadsend ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadsend', e, data); // TODOD add vis //data.context.element = }); file_upload_start.on('fileuploadprogress', function(e, data) { - console.log('fileuploadprogress ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadprogress', e, data); }); file_upload_start.on('fileuploadprogressall', function(e, data) { - console.log('fileuploadprogressall ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadprogressall', e, data); }); file_upload_start.on('fileuploadstop', function(e, data) { - console.log('fileuploadstop ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadstop', e, data); + + //if user pressed cancel hide upload chrome + if (! OC.Upload.isProcessing()) { + //cleanup uploading to a dir + var uploadtext = $('tr .uploadtext'); + var img = OC.imagePath('core', 'filetypes/folder.png'); + uploadtext.parents('td.filename').attr('style','background-image:url('+img+')'); + uploadtext.fadeOut(); + uploadtext.attr('currentUploads', 0); + } }); file_upload_start.on('fileuploadfail', function(e, data) { - console.log('fileuploadfail ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadfail', e, data); + + //if user pressed cancel hide upload chrome + if (data.errorThrown === 'abort') { + //cleanup uploading to a dir + var uploadtext = $('tr .uploadtext'); + var img = OC.imagePath('core', 'filetypes/folder.png'); + uploadtext.parents('td.filename').attr('style','background-image:url('+img+')'); + uploadtext.fadeOut(); + uploadtext.attr('currentUploads', 0); + } }); /* file_upload_start.on('fileuploadfail', function(e, data) { diff --git a/apps/files/js/files.js b/apps/files/js/files.js index a907aeab1fc..53405c7fe7e 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -1,4 +1,3 @@ -var uploadingFiles = {}; Files={ updateMaxUploadFilesize:function(response) { if(response == undefined) { @@ -235,12 +234,6 @@ $(document).ready(function() { return size; }; - // warn user not to leave the page while upload is in progress - $(window).bind('beforeunload', function(e) { - if ($.assocArraySize(uploadingFiles) > 0) - return t('files','File upload is in progress. Leaving the page now will cancel the upload.'); - }); - //add multiply file upload attribute to all browsers except konqueror (which crashes when it's used) if(navigator.userAgent.search(/konqueror/i)==-1){ $('#file_upload_start').attr('multiple','multiple') diff --git a/apps/files/js/jquery.fileupload.js b/apps/files/js/jquery.fileupload.js index a89e9dc2c44..f9f6cc3a382 100644 --- a/apps/files/js/jquery.fileupload.js +++ b/apps/files/js/jquery.fileupload.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Plugin 5.9 + * jQuery File Upload Plugin 5.32.2 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -10,7 +10,7 @@ */ /*jslint nomen: true, unparam: true, regexp: true */ -/*global define, window, document, Blob, FormData, location */ +/*global define, window, document, location, File, Blob, FormData */ (function (factory) { 'use strict'; @@ -27,12 +27,28 @@ }(function ($) { 'use strict'; + // Detect file input support, based on + // http://viljamis.com/blog/2012/file-upload-support-on-mobile/ + $.support.fileInput = !(new RegExp( + // Handle devices which give false positives for the feature detection: + '(Android (1\\.[0156]|2\\.[01]))' + + '|(Windows Phone (OS 7|8\\.0))|(XBLWP)|(ZuneWP)|(WPDesktop)' + + '|(w(eb)?OSBrowser)|(webOS)' + + '|(Kindle/(1\\.0|2\\.[05]|3\\.0))' + ).test(window.navigator.userAgent) || + // Feature detection for all other devices: + $('').prop('disabled')); + // The FileReader API is not actually used, but works as feature detection, // as e.g. Safari supports XHR file uploads via the FormData API, // but not non-multipart XHR file uploads: $.support.xhrFileUpload = !!(window.XMLHttpRequestUpload && window.FileReader); $.support.xhrFormDataFileUpload = !!window.FormData; + // Detect support for Blob slicing (required for chunked uploads): + $.support.blobSlice = window.Blob && (Blob.prototype.slice || + Blob.prototype.webkitSlice || Blob.prototype.mozSlice); + // The fileupload widget listens for change events on file input fields defined // via fileInput setting and paste or drop events of the given dropZone. // In addition to the default jQuery Widget methods, the fileupload widget @@ -44,17 +60,16 @@ $.widget('blueimp.fileupload', { options: { - // The namespace used for event handler binding on the dropZone and - // fileInput collections. - // If not set, the name of the widget ("fileupload") is used. - namespace: undefined, - // The drop target collection, by the default the complete document. - // Set to null or an empty collection to disable drag & drop support: + // The drop target element(s), by the default the complete document. + // Set to null to disable drag & drop support: dropZone: $(document), - // The file input field collection, that is listened for change events. + // The paste target element(s), by the default the complete document. + // Set to null to disable paste support: + pasteZone: $(document), + // The file input field(s), that are listened to for change events. // If undefined, it is set to the file input fields inside // of the widget element on plugin initialization. - // Set to null or an empty collection to disable the change listener. + // Set to null to disable the change listener. fileInput: undefined, // By default, the file input field is replaced with a clone after // each input field change event. This is required for iframe transport @@ -63,7 +78,8 @@ replaceFileInput: true, // The parameter name for the file form data (the request argument name). // If undefined or empty, the name property of the file input field is - // used, or "files[]" if the file input name property is also empty: + // used, or "files[]" if the file input name property is also empty, + // can be a string or an array of strings: paramName: undefined, // By default, each file of a selection is uploaded using an individual // request for XHR type uploads. Set to false to upload file @@ -108,6 +124,29 @@ // global progress calculation. Set the following option to false to // prevent recalculating the global progress data: recalculateProgress: true, + // Interval in milliseconds to calculate and trigger progress events: + progressInterval: 100, + // Interval in milliseconds to calculate progress bitrate: + bitrateInterval: 500, + // By default, uploads are started automatically when adding files: + autoUpload: true, + + // Error and info messages: + messages: { + uploadedBytes: 'Uploaded bytes exceed file size' + }, + + // Translation function, gets the message key to be translated + // and an object with context specific data as arguments: + i18n: function (message, context) { + message = this.messages[message] || message.toString(); + if (context) { + $.each(context, function (key, value) { + message = message.replace('{' + key + '}', value); + }); + } + return message; + }, // Additional form data to be sent along with the file uploads can be set // using this option, which accepts an array of objects with name and @@ -121,48 +160,81 @@ // The add callback is invoked as soon as files are added to the fileupload // widget (via file input selection, drag & drop, paste or add API call). // If the singleFileUploads option is enabled, this callback will be - // called once for each file in the selection for XHR file uplaods, else + // called once for each file in the selection for XHR file uploads, else // once for each file selection. + // // The upload starts when the submit method is invoked on the data parameter. // The data object contains a files property holding the added files - // and allows to override plugin options as well as define ajax settings. + // and allows you to override plugin options as well as define ajax settings. + // // Listeners for this callback can also be bound the following way: // .bind('fileuploadadd', func); + // // data.submit() returns a Promise object and allows to attach additional // handlers using jQuery's Deferred callbacks: // data.submit().done(func).fail(func).always(func); add: function (e, data) { - data.submit(); + if (data.autoUpload || (data.autoUpload !== false && + $(this).fileupload('option', 'autoUpload'))) { + data.process().done(function () { + data.submit(); + }); + } }, // Other callbacks: + // Callback for the submit event of each file upload: // submit: function (e, data) {}, // .bind('fileuploadsubmit', func); + // Callback for the start of each file upload request: // send: function (e, data) {}, // .bind('fileuploadsend', func); + // Callback for successful uploads: // done: function (e, data) {}, // .bind('fileuploaddone', func); + // Callback for failed (abort or error) uploads: // fail: function (e, data) {}, // .bind('fileuploadfail', func); + // Callback for completed (success, abort or error) requests: // always: function (e, data) {}, // .bind('fileuploadalways', func); + // Callback for upload progress events: // progress: function (e, data) {}, // .bind('fileuploadprogress', func); + // Callback for global upload progress events: // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func); + // Callback for uploads start, equivalent to the global ajaxStart event: // start: function (e) {}, // .bind('fileuploadstart', func); + // Callback for uploads stop, equivalent to the global ajaxStop event: // stop: function (e) {}, // .bind('fileuploadstop', func); - // Callback for change events of the fileInput collection: + + // Callback for change events of the fileInput(s): // change: function (e, data) {}, // .bind('fileuploadchange', func); - // Callback for paste events to the dropZone collection: + + // Callback for paste events to the pasteZone(s): // paste: function (e, data) {}, // .bind('fileuploadpaste', func); - // Callback for drop events of the dropZone collection: + + // Callback for drop events of the dropZone(s): // drop: function (e, data) {}, // .bind('fileuploaddrop', func); - // Callback for dragover events of the dropZone collection: + + // Callback for dragover events of the dropZone(s): // dragover: function (e) {}, // .bind('fileuploaddragover', func); + // Callback for the start of each chunk upload request: + // chunksend: function (e, data) {}, // .bind('fileuploadchunksend', func); + + // Callback for successful chunk uploads: + // chunkdone: function (e, data) {}, // .bind('fileuploadchunkdone', func); + + // Callback for failed (abort or error) chunk uploads: + // chunkfail: function (e, data) {}, // .bind('fileuploadchunkfail', func); + + // Callback for completed (success, abort or error) chunk upload requests: + // chunkalways: function (e, data) {}, // .bind('fileuploadchunkalways', func); + // The plugin options are used as settings object for the ajax calls. // The following are jQuery ajax settings required for the file uploads: processData: false, @@ -170,15 +242,36 @@ cache: false }, - // A list of options that require a refresh after assigning a new value: - _refreshOptionsList: [ - 'namespace', - 'dropZone', + // A list of options that require reinitializing event listeners and/or + // special initialization code: + _specialOptions: [ 'fileInput', + 'dropZone', + 'pasteZone', 'multipart', 'forceIframeTransport' ], + _blobSlice: $.support.blobSlice && function () { + var slice = this.slice || this.webkitSlice || this.mozSlice; + return slice.apply(this, arguments); + }, + + _BitrateTimer: function () { + this.timestamp = ((Date.now) ? Date.now() : (new Date()).getTime()); + this.loaded = 0; + this.bitrate = 0; + this.getBitrate = function (now, loaded, interval) { + var timeDiff = now - this.timestamp; + if (!this.bitrate || !interval || timeDiff > interval) { + this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8; + this.loaded = loaded; + this.timestamp = now; + } + return this.bitrate; + }; + }, + _isXHRUpload: function (options) { return !options.forceIframeTransport && ((!options.multipart && $.support.xhrFileUpload) || @@ -189,9 +282,11 @@ var formData; if (typeof options.formData === 'function') { return options.formData(options.form); - } else if ($.isArray(options.formData)) { + } + if ($.isArray(options.formData)) { return options.formData; - } else if (options.formData) { + } + if ($.type(options.formData) === 'object') { formData = []; $.each(options.formData, function (name, value) { formData.push({name: name, value: value}); @@ -209,28 +304,66 @@ return total; }, + _initProgressObject: function (obj) { + var progress = { + loaded: 0, + total: 0, + bitrate: 0 + }; + if (obj._progress) { + $.extend(obj._progress, progress); + } else { + obj._progress = progress; + } + }, + + _initResponseObject: function (obj) { + var prop; + if (obj._response) { + for (prop in obj._response) { + if (obj._response.hasOwnProperty(prop)) { + delete obj._response[prop]; + } + } + } else { + obj._response = {}; + } + }, + _onProgress: function (e, data) { if (e.lengthComputable) { - var total = data.total || this._getTotal(data.files), - loaded = parseInt( - e.loaded / e.total * (data.chunkSize || total), - 10 - ) + (data.uploadedBytes || 0); - this._loaded += loaded - (data.loaded || data.uploadedBytes || 0); - data.lengthComputable = true; - data.loaded = loaded; - data.total = total; + var now = ((Date.now) ? Date.now() : (new Date()).getTime()), + loaded; + if (data._time && data.progressInterval && + (now - data._time < data.progressInterval) && + e.loaded !== e.total) { + return; + } + data._time = now; + loaded = Math.floor( + e.loaded / e.total * (data.chunkSize || data._progress.total) + ) + (data.uploadedBytes || 0); + // Add the difference from the previously loaded state + // to the global loaded counter: + this._progress.loaded += (loaded - data._progress.loaded); + this._progress.bitrate = this._bitrateTimer.getBitrate( + now, + this._progress.loaded, + data.bitrateInterval + ); + data._progress.loaded = data.loaded = loaded; + data._progress.bitrate = data.bitrate = data._bitrateTimer.getBitrate( + now, + loaded, + data.bitrateInterval + ); // Trigger a custom progress event with a total data property set // to the file size(s) of the current upload and a loaded data // property calculated accordingly: this._trigger('progress', e, data); // Trigger a global progress event for all current file uploads, // including ajax calls queued for sequential file uploads: - this._trigger('progressall', e, { - lengthComputable: true, - loaded: this._loaded, - total: this._total - }); + this._trigger('progressall', e, this._progress); } }, @@ -254,34 +387,30 @@ } }, + _isInstanceOf: function (type, obj) { + // Cross-frame instanceof check + return Object.prototype.toString.call(obj) === '[object ' + type + ']'; + }, + _initXHRData: function (options) { - var formData, + var that = this, + formData, file = options.files[0], // Ignore non-multipart setting if not supported: - multipart = options.multipart || !$.support.xhrFileUpload; - if (!multipart || options.blob) { - // For non-multipart uploads and chunked uploads, - // file meta data is not part of the request body, - // so we transmit this data as part of the HTTP headers. - // For cross domain requests, these headers must be allowed - // via Access-Control-Allow-Headers or removed using - // the beforeSend callback: - options.headers = $.extend(options.headers, { - 'X-File-Name': file.name, - 'X-File-Type': file.type, - 'X-File-Size': file.size - }); - if (!options.blob) { - // Non-chunked non-multipart upload: - options.contentType = file.type; - options.data = file; - } else if (!multipart) { - // Chunked non-multipart upload: - options.contentType = 'application/octet-stream'; - options.data = options.blob; - } + multipart = options.multipart || !$.support.xhrFileUpload, + paramName = options.paramName[0]; + options.headers = options.headers || {}; + if (options.contentRange) { + options.headers['Content-Range'] = options.contentRange; + } + if (!multipart || options.blob || !this._isInstanceOf('File', file)) { + options.headers['Content-Disposition'] = 'attachment; filename="' + + encodeURI(file.name) + '"'; } - if (multipart && $.support.xhrFormDataFileUpload) { + if (!multipart) { + options.contentType = file.type; + options.data = options.blob || file; + } else if ($.support.xhrFormDataFileUpload) { if (options.postMessage) { // window.postMessage does not allow sending FormData // objects, so we just add the File/Blob objects to @@ -290,19 +419,19 @@ formData = this._getFormData(options); if (options.blob) { formData.push({ - name: options.paramName, + name: paramName, value: options.blob }); } else { $.each(options.files, function (index, file) { formData.push({ - name: options.paramName, + name: options.paramName[index] || paramName, value: file }); }); } } else { - if (options.formData instanceof FormData) { + if (that._isInstanceOf('FormData', options.formData)) { formData = options.formData; } else { formData = new FormData(); @@ -311,14 +440,18 @@ }); } if (options.blob) { - formData.append(options.paramName, options.blob, file.name); + formData.append(paramName, options.blob, file.name); } else { $.each(options.files, function (index, file) { - // File objects are also Blob instances. // This check allows the tests to run with // dummy objects: - if (file instanceof Blob) { - formData.append(options.paramName, file, file.name); + if (that._isInstanceOf('File', file) || + that._isInstanceOf('Blob', file)) { + formData.append( + options.paramName[index] || paramName, + file, + file.name + ); } }); } @@ -330,13 +463,13 @@ }, _initIframeSettings: function (options) { + var targetHost = $('').prop('href', options.url).prop('host'); // Setting the dataType to iframe enables the iframe transport: options.dataType = 'iframe ' + (options.dataType || ''); // The iframe transport accepts a serialized array as form data: options.formData = this._getFormData(options); // Add redirect url to form data on cross-domain uploads: - if (options.redirect && $('').prop('href', options.url) - .prop('host') !== location.host) { + if (options.redirect && targetHost && targetHost !== location.host) { options.formData.push({ name: options.redirectParamName || 'redirect', value: options.redirect @@ -358,29 +491,58 @@ options.dataType = 'postmessage ' + (options.dataType || ''); } } else { - this._initIframeSettings(options, 'iframe'); + this._initIframeSettings(options); } }, + _getParamName: function (options) { + var fileInput = $(options.fileInput), + paramName = options.paramName; + if (!paramName) { + paramName = []; + fileInput.each(function () { + var input = $(this), + name = input.prop('name') || 'files[]', + i = (input.prop('files') || [1]).length; + while (i) { + paramName.push(name); + i -= 1; + } + }); + if (!paramName.length) { + paramName = [fileInput.prop('name') || 'files[]']; + } + } else if (!$.isArray(paramName)) { + paramName = [paramName]; + } + return paramName; + }, + _initFormSettings: function (options) { // Retrieve missing options from the input field and the // associated form, if available: if (!options.form || !options.form.length) { options.form = $(options.fileInput.prop('form')); + // If the given file input doesn't have an associated form, + // use the default widget file input's form: + if (!options.form.length) { + options.form = $(this.options.fileInput.prop('form')); + } } - if (!options.paramName) { - options.paramName = options.fileInput.prop('name') || - 'files[]'; - } + options.paramName = this._getParamName(options); if (!options.url) { options.url = options.form.prop('action') || location.href; } // The HTTP request method must be "POST" or "PUT": options.type = (options.type || options.form.prop('method') || '') .toUpperCase(); - if (options.type !== 'POST' && options.type !== 'PUT') { + if (options.type !== 'POST' && options.type !== 'PUT' && + options.type !== 'PATCH') { options.type = 'POST'; } + if (!options.formAcceptCharset) { + options.formAcceptCharset = options.form.attr('accept-charset'); + } }, _getAJAXSettings: function (data) { @@ -390,6 +552,21 @@ return options; }, + // jQuery 1.6 doesn't provide .state(), + // while jQuery 1.8+ removed .isRejected() and .isResolved(): + _getDeferredState: function (deferred) { + if (deferred.state) { + return deferred.state(); + } + if (deferred.isResolved()) { + return 'resolved'; + } + if (deferred.isRejected()) { + return 'rejected'; + } + return 'pending'; + }, + // Maps jqXHR callbacks to the equivalent // methods of the given Promise object: _enhancePromise: function (promise) { @@ -414,24 +591,77 @@ return this._enhancePromise(promise); }, + // Adds convenience methods to the data callback argument: + _addConvenienceMethods: function (e, data) { + var that = this, + getPromise = function (data) { + return $.Deferred().resolveWith(that, [data]).promise(); + }; + data.process = function (resolveFunc, rejectFunc) { + if (resolveFunc || rejectFunc) { + data._processQueue = this._processQueue = + (this._processQueue || getPromise(this)) + .pipe(resolveFunc, rejectFunc); + } + return this._processQueue || getPromise(this); + }; + data.submit = function () { + if (this.state() !== 'pending') { + data.jqXHR = this.jqXHR = + (that._trigger('submit', e, this) !== false) && + that._onSend(e, this); + } + return this.jqXHR || that._getXHRPromise(); + }; + data.abort = function () { + if (this.jqXHR) { + return this.jqXHR.abort(); + } + return that._getXHRPromise(); + }; + data.state = function () { + if (this.jqXHR) { + return that._getDeferredState(this.jqXHR); + } + if (this._processQueue) { + return that._getDeferredState(this._processQueue); + } + }; + data.progress = function () { + return this._progress; + }; + data.response = function () { + return this._response; + }; + }, + + // Parses the Range header from the server response + // and returns the uploaded bytes: + _getUploadedBytes: function (jqXHR) { + var range = jqXHR.getResponseHeader('Range'), + parts = range && range.split('-'), + upperBytesPos = parts && parts.length > 1 && + parseInt(parts[1], 10); + return upperBytesPos && upperBytesPos + 1; + }, + // Uploads a file in multiple, sequential requests // by splitting the file up in multiple blob chunks. // If the second parameter is true, only tests if the file // should be uploaded in chunks, but does not invoke any // upload requests: _chunkedUpload: function (options, testOnly) { + options.uploadedBytes = options.uploadedBytes || 0; var that = this, file = options.files[0], fs = file.size, - ub = options.uploadedBytes = options.uploadedBytes || 0, + ub = options.uploadedBytes, mcs = options.maxChunkSize || fs, - // Use the Blob methods with the slice implementation - // according to the W3C Blob API specification: - slice = file.webkitSlice || file.mozSlice || file.slice, - upload, - n, + slice = this._blobSlice, + dfd = $.Deferred(), + promise = dfd.promise(), jqXHR, - pipe; + upload; if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) || options.data) { return false; @@ -440,62 +670,84 @@ return true; } if (ub >= fs) { - file.error = 'uploadedBytes'; + file.error = options.i18n('uploadedBytes'); return this._getXHRPromise( false, options.context, [null, 'error', file.error] ); } - // n is the number of blobs to upload, - // calculated via filesize, uploaded bytes and max chunk size: - n = Math.ceil((fs - ub) / mcs); - // The chunk upload method accepting the chunk number as parameter: - upload = function (i) { - if (!i) { - return that._getXHRPromise(true, options.context); - } - // Upload the blobs in sequential order: - return upload(i -= 1).pipe(function () { - // Clone the options object for each chunk upload: - var o = $.extend({}, options); - o.blob = slice.call( - file, - ub + i * mcs, - ub + (i + 1) * mcs - ); - // Store the current chunk size, as the blob itself - // will be dereferenced after data processing: - o.chunkSize = o.blob.size; - // Process the upload data (the blob and potential form data): - that._initXHRData(o); - // Add progress listeners for this chunk upload: - that._initProgressListener(o); - jqXHR = ($.ajax(o) || that._getXHRPromise(false, o.context)) - .done(function () { - // Create a progress event if upload is done and - // no progress event has been invoked for this chunk: - if (!o.loaded) { - that._onProgress($.Event('progress', { - lengthComputable: true, - loaded: o.chunkSize, - total: o.chunkSize - }), o); - } - options.uploadedBytes = o.uploadedBytes += - o.chunkSize; - }); - return jqXHR; - }); + // The chunk upload method: + upload = function () { + // Clone the options object for each chunk upload: + var o = $.extend({}, options), + currentLoaded = o._progress.loaded; + o.blob = slice.call( + file, + ub, + ub + mcs, + file.type + ); + // Store the current chunk size, as the blob itself + // will be dereferenced after data processing: + o.chunkSize = o.blob.size; + // Expose the chunk bytes position range: + o.contentRange = 'bytes ' + ub + '-' + + (ub + o.chunkSize - 1) + '/' + fs; + // Process the upload data (the blob and potential form data): + that._initXHRData(o); + // Add progress listeners for this chunk upload: + that._initProgressListener(o); + jqXHR = ((that._trigger('chunksend', null, o) !== false && $.ajax(o)) || + that._getXHRPromise(false, o.context)) + .done(function (result, textStatus, jqXHR) { + ub = that._getUploadedBytes(jqXHR) || + (ub + o.chunkSize); + // Create a progress event if no final progress event + // with loaded equaling total has been triggered + // for this chunk: + if (currentLoaded + o.chunkSize - o._progress.loaded) { + that._onProgress($.Event('progress', { + lengthComputable: true, + loaded: ub - o.uploadedBytes, + total: ub - o.uploadedBytes + }), o); + } + options.uploadedBytes = o.uploadedBytes = ub; + o.result = result; + o.textStatus = textStatus; + o.jqXHR = jqXHR; + that._trigger('chunkdone', null, o); + that._trigger('chunkalways', null, o); + if (ub < fs) { + // File upload not yet complete, + // continue with the next chunk: + upload(); + } else { + dfd.resolveWith( + o.context, + [result, textStatus, jqXHR] + ); + } + }) + .fail(function (jqXHR, textStatus, errorThrown) { + o.jqXHR = jqXHR; + o.textStatus = textStatus; + o.errorThrown = errorThrown; + that._trigger('chunkfail', null, o); + that._trigger('chunkalways', null, o); + dfd.rejectWith( + o.context, + [jqXHR, textStatus, errorThrown] + ); + }); }; - // Return the piped Promise object, enhanced with an abort method, - // which is delegated to the jqXHR object of the current upload, - // and jqXHR callbacks mapped to the equivalent Promise methods: - pipe = upload(n); - pipe.abort = function () { + this._enhancePromise(promise); + promise.abort = function () { return jqXHR.abort(); }; - return this._enhancePromise(pipe); + upload(); + return promise; }, _beforeSend: function (e, data) { @@ -504,99 +756,113 @@ // and no other uploads are currently running, // equivalent to the global ajaxStart event: this._trigger('start'); + // Set timer for global bitrate progress calculation: + this._bitrateTimer = new this._BitrateTimer(); + // Reset the global progress values: + this._progress.loaded = this._progress.total = 0; + this._progress.bitrate = 0; } + // Make sure the container objects for the .response() and + // .progress() methods on the data object are available + // and reset to their initial state: + this._initResponseObject(data); + this._initProgressObject(data); + data._progress.loaded = data.loaded = data.uploadedBytes || 0; + data._progress.total = data.total = this._getTotal(data.files) || 1; + data._progress.bitrate = data.bitrate = 0; this._active += 1; // Initialize the global progress values: - this._loaded += data.uploadedBytes || 0; - this._total += this._getTotal(data.files); + this._progress.loaded += data.loaded; + this._progress.total += data.total; }, _onDone: function (result, textStatus, jqXHR, options) { - if (!this._isXHRUpload(options)) { - // Create a progress event for each iframe load: + var total = options._progress.total, + response = options._response; + if (options._progress.loaded < total) { + // Create a progress event if no final progress event + // with loaded equaling total has been triggered: this._onProgress($.Event('progress', { lengthComputable: true, - loaded: 1, - total: 1 + loaded: total, + total: total }), options); } - options.result = result; - options.textStatus = textStatus; - options.jqXHR = jqXHR; + response.result = options.result = result; + response.textStatus = options.textStatus = textStatus; + response.jqXHR = options.jqXHR = jqXHR; this._trigger('done', null, options); }, _onFail: function (jqXHR, textStatus, errorThrown, options) { - options.jqXHR = jqXHR; - options.textStatus = textStatus; - options.errorThrown = errorThrown; - this._trigger('fail', null, options); + var response = options._response; if (options.recalculateProgress) { // Remove the failed (error or abort) file upload from // the global progress calculation: - this._loaded -= options.loaded || options.uploadedBytes || 0; - this._total -= options.total || this._getTotal(options.files); + this._progress.loaded -= options._progress.loaded; + this._progress.total -= options._progress.total; } + response.jqXHR = options.jqXHR = jqXHR; + response.textStatus = options.textStatus = textStatus; + response.errorThrown = options.errorThrown = errorThrown; + this._trigger('fail', null, options); }, _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) { - this._active -= 1; - options.textStatus = textStatus; - if (jqXHRorError && jqXHRorError.always) { - options.jqXHR = jqXHRorError; - options.result = jqXHRorResult; - } else { - options.jqXHR = jqXHRorResult; - options.errorThrown = jqXHRorError; - } + // jqXHRorResult, textStatus and jqXHRorError are added to the + // options object via done and fail callbacks this._trigger('always', null, options); - if (this._active === 0) { - // The stop callback is triggered when all uploads have - // been completed, equivalent to the global ajaxStop event: - this._trigger('stop'); - // Reset the global progress values: - this._loaded = this._total = 0; - } }, _onSend: function (e, data) { + if (!data.submit) { + this._addConvenienceMethods(e, data); + } var that = this, jqXHR, + aborted, slot, pipe, options = that._getAJAXSettings(data), - send = function (resolve, args) { + send = function () { that._sending += 1; + // Set timer for bitrate progress calculation: + options._bitrateTimer = new that._BitrateTimer(); jqXHR = jqXHR || ( - (resolve !== false && - that._trigger('send', e, options) !== false && - (that._chunkedUpload(options) || $.ajax(options))) || - that._getXHRPromise(false, options.context, args) + ((aborted || that._trigger('send', e, options) === false) && + that._getXHRPromise(false, options.context, aborted)) || + that._chunkedUpload(options) || $.ajax(options) ).done(function (result, textStatus, jqXHR) { that._onDone(result, textStatus, jqXHR, options); }).fail(function (jqXHR, textStatus, errorThrown) { that._onFail(jqXHR, textStatus, errorThrown, options); }).always(function (jqXHRorResult, textStatus, jqXHRorError) { - that._sending -= 1; that._onAlways( jqXHRorResult, textStatus, jqXHRorError, options ); + that._sending -= 1; + that._active -= 1; if (options.limitConcurrentUploads && options.limitConcurrentUploads > that._sending) { // Start the next queued upload, // that has not been aborted: var nextSlot = that._slots.shift(); while (nextSlot) { - if (!nextSlot.isRejected()) { + if (that._getDeferredState(nextSlot) === 'pending') { nextSlot.resolve(); break; } nextSlot = that._slots.shift(); } } + if (that._active === 0) { + // The stop callback is triggered when all uploads have + // been completed, equivalent to the global ajaxStop event: + that._trigger('stop'); + } }); return jqXHR; }; @@ -609,18 +875,19 @@ this._slots.push(slot); pipe = slot.pipe(send); } else { - pipe = (this._sequence = this._sequence.pipe(send, send)); + this._sequence = this._sequence.pipe(send, send); + pipe = this._sequence; } // Return the piped Promise object, enhanced with an abort method, // which is delegated to the jqXHR object of the current upload, // and jqXHR callbacks mapped to the equivalent Promise methods: pipe.abort = function () { - var args = [undefined, 'abort', 'abort']; + aborted = [undefined, 'abort', 'abort']; if (!jqXHR) { if (slot) { - slot.rejectWith(args); + slot.rejectWith(options.context, aborted); } - return send(false, args); + return send(); } return jqXHR.abort(); }; @@ -634,40 +901,43 @@ result = true, options = $.extend({}, this.options, data), limit = options.limitMultiFileUploads, + paramName = this._getParamName(options), + paramNameSet, + paramNameSlice, fileSet, i; if (!(options.singleFileUploads || limit) || !this._isXHRUpload(options)) { fileSet = [data.files]; + paramNameSet = [paramName]; } else if (!options.singleFileUploads && limit) { fileSet = []; + paramNameSet = []; for (i = 0; i < data.files.length; i += limit) { fileSet.push(data.files.slice(i, i + limit)); + paramNameSlice = paramName.slice(i, i + limit); + if (!paramNameSlice.length) { + paramNameSlice = paramName; + } + paramNameSet.push(paramNameSlice); } + } else { + paramNameSet = paramName; } data.originalFiles = data.files; $.each(fileSet || data.files, function (index, element) { - var files = fileSet ? element : [element], - newData = $.extend({}, data, {files: files}); - newData.submit = function () { - newData.jqXHR = this.jqXHR = - (that._trigger('submit', e, this) !== false) && - that._onSend(e, this); - return this.jqXHR; - }; - return (result = that._trigger('add', e, newData)); + var newData = $.extend({}, data); + newData.files = fileSet ? element : [element]; + newData.paramName = paramNameSet[index]; + that._initResponseObject(newData); + that._initProgressObject(newData); + that._addConvenienceMethods(e, newData); + result = that._trigger('add', e, newData); + return result; }); return result; }, - // File Normalization for Gecko 1.9.1 (Firefox 3.5) support: - _normalizeFile: function (index, file) { - if (file.name === undefined && file.size === undefined) { - file.name = file.fileName; - file.size = file.fileSize; - } - }, - _replaceFileInput: function (input) { var inputClone = input.clone(true); $('
').append(inputClone)[0].reset(); @@ -677,7 +947,7 @@ // Avoid memory leaks with the detached file input: $.cleanData(input.unbind('remove')); // Replace the original file input element in the fileInput - // collection with the clone, which has been copied including + // elements set with the clone, which has been copied including // event handlers: this.options.fileInput = this.options.fileInput.map(function (i, el) { if (el === input[0]) { @@ -692,102 +962,229 @@ } }, - _onChange: function (e) { - var that = e.data.fileupload, - data = { - files: $.each($.makeArray(e.target.files), that._normalizeFile), - fileInput: $(e.target), - form: $(e.target.form) - }; - if (!data.files.length) { + _handleFileTreeEntry: function (entry, path) { + var that = this, + dfd = $.Deferred(), + errorHandler = function (e) { + if (e && !e.entry) { + e.entry = entry; + } + // Since $.when returns immediately if one + // Deferred is rejected, we use resolve instead. + // This allows valid files and invalid items + // to be returned together in one set: + dfd.resolve([e]); + }, + dirReader; + path = path || ''; + if (entry.isFile) { + if (entry._file) { + // Workaround for Chrome bug #149735 + entry._file.relativePath = path; + dfd.resolve(entry._file); + } else { + entry.file(function (file) { + file.relativePath = path; + dfd.resolve(file); + }, errorHandler); + } + } else if (entry.isDirectory) { + dirReader = entry.createReader(); + dirReader.readEntries(function (entries) { + that._handleFileTreeEntries( + entries, + path + entry.name + '/' + ).done(function (files) { + dfd.resolve(files); + }).fail(errorHandler); + }, errorHandler); + } else { + // Return an empy list for file system items + // other than files or directories: + dfd.resolve([]); + } + return dfd.promise(); + }, + + _handleFileTreeEntries: function (entries, path) { + var that = this; + return $.when.apply( + $, + $.map(entries, function (entry) { + return that._handleFileTreeEntry(entry, path); + }) + ).pipe(function () { + return Array.prototype.concat.apply( + [], + arguments + ); + }); + }, + + _getDroppedFiles: function (dataTransfer) { + dataTransfer = dataTransfer || {}; + var items = dataTransfer.items; + if (items && items.length && (items[0].webkitGetAsEntry || + items[0].getAsEntry)) { + return this._handleFileTreeEntries( + $.map(items, function (item) { + var entry; + if (item.webkitGetAsEntry) { + entry = item.webkitGetAsEntry(); + if (entry) { + // Workaround for Chrome bug #149735: + entry._file = item.getAsFile(); + } + return entry; + } + return item.getAsEntry(); + }) + ); + } + return $.Deferred().resolve( + $.makeArray(dataTransfer.files) + ).promise(); + }, + + _getSingleFileInputFiles: function (fileInput) { + fileInput = $(fileInput); + var entries = fileInput.prop('webkitEntries') || + fileInput.prop('entries'), + files, + value; + if (entries && entries.length) { + return this._handleFileTreeEntries(entries); + } + files = $.makeArray(fileInput.prop('files')); + if (!files.length) { + value = fileInput.prop('value'); + if (!value) { + return $.Deferred().resolve([]).promise(); + } // If the files property is not available, the browser does not // support the File API and we add a pseudo File object with // the input value as name with path information removed: - data.files = [{name: e.target.value.replace(/^.*\\/, '')}]; - } - if (that.options.replaceFileInput) { - that._replaceFileInput(data.fileInput); + files = [{name: value.replace(/^.*\\/, '')}]; + } else if (files[0].name === undefined && files[0].fileName) { + // File normalization for Safari 4 and Firefox 3: + $.each(files, function (index, file) { + file.name = file.fileName; + file.size = file.fileSize; + }); } - if (that._trigger('change', e, data) === false || - that._onAdd(e, data) === false) { - return false; + return $.Deferred().resolve(files).promise(); + }, + + _getFileInputFiles: function (fileInput) { + if (!(fileInput instanceof $) || fileInput.length === 1) { + return this._getSingleFileInputFiles(fileInput); } + return $.when.apply( + $, + $.map(fileInput, this._getSingleFileInputFiles) + ).pipe(function () { + return Array.prototype.concat.apply( + [], + arguments + ); + }); + }, + + _onChange: function (e) { + var that = this, + data = { + fileInput: $(e.target), + form: $(e.target.form) + }; + this._getFileInputFiles(data.fileInput).always(function (files) { + data.files = files; + if (that.options.replaceFileInput) { + that._replaceFileInput(data.fileInput); + } + if (that._trigger('change', e, data) !== false) { + that._onAdd(e, data); + } + }); }, _onPaste: function (e) { - var that = e.data.fileupload, - cbd = e.originalEvent.clipboardData, - items = (cbd && cbd.items) || [], + var items = e.originalEvent && e.originalEvent.clipboardData && + e.originalEvent.clipboardData.items, data = {files: []}; - $.each(items, function (index, item) { - var file = item.getAsFile && item.getAsFile(); - if (file) { - data.files.push(file); + if (items && items.length) { + $.each(items, function (index, item) { + var file = item.getAsFile && item.getAsFile(); + if (file) { + data.files.push(file); + } + }); + if (this._trigger('paste', e, data) === false || + this._onAdd(e, data) === false) { + return false; } - }); - if (that._trigger('paste', e, data) === false || - that._onAdd(e, data) === false) { - return false; } }, _onDrop: function (e) { - var that = e.data.fileupload, - dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer, - data = { - files: $.each( - $.makeArray(dataTransfer && dataTransfer.files), - that._normalizeFile - ) - }; - if (that._trigger('drop', e, data) === false || - that._onAdd(e, data) === false) { - return false; + e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer; + var that = this, + dataTransfer = e.dataTransfer, + data = {}; + if (dataTransfer && dataTransfer.files && dataTransfer.files.length) { + e.preventDefault(); + this._getDroppedFiles(dataTransfer).always(function (files) { + data.files = files; + if (that._trigger('drop', e, data) !== false) { + that._onAdd(e, data); + } + }); } - e.preventDefault(); }, _onDragOver: function (e) { - var that = e.data.fileupload, - dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer; - if (that._trigger('dragover', e) === false) { - return false; - } + e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer; + var dataTransfer = e.dataTransfer; if (dataTransfer) { - dataTransfer.dropEffect = dataTransfer.effectAllowed = 'copy'; + if (this._trigger('dragover', e) === false) { + return false; + } + if ($.inArray('Files', dataTransfer.types) !== -1) { + dataTransfer.dropEffect = 'copy'; + e.preventDefault(); + } } - e.preventDefault(); }, _initEventHandlers: function () { - var ns = this.options.namespace; if (this._isXHRUpload(this.options)) { - this.options.dropZone - .bind('dragover.' + ns, {fileupload: this}, this._onDragOver) - .bind('drop.' + ns, {fileupload: this}, this._onDrop) - .bind('paste.' + ns, {fileupload: this}, this._onPaste); + this._on(this.options.dropZone, { + dragover: this._onDragOver, + drop: this._onDrop + }); + this._on(this.options.pasteZone, { + paste: this._onPaste + }); + } + if ($.support.fileInput) { + this._on(this.options.fileInput, { + change: this._onChange + }); } - this.options.fileInput - .bind('change.' + ns, {fileupload: this}, this._onChange); }, _destroyEventHandlers: function () { - var ns = this.options.namespace; - this.options.dropZone - .unbind('dragover.' + ns, this._onDragOver) - .unbind('drop.' + ns, this._onDrop) - .unbind('paste.' + ns, this._onPaste); - this.options.fileInput - .unbind('change.' + ns, this._onChange); + this._off(this.options.dropZone, 'dragover drop'); + this._off(this.options.pasteZone, 'paste'); + this._off(this.options.fileInput, 'change'); }, _setOption: function (key, value) { - var refresh = $.inArray(key, this._refreshOptionsList) !== -1; - if (refresh) { + var reinit = $.inArray(key, this._specialOptions) !== -1; + if (reinit) { this._destroyEventHandlers(); } - $.Widget.prototype._setOption.call(this, key, value); - if (refresh) { + this._super(key, value); + if (reinit) { this._initSpecialOptions(); this._initEventHandlers(); } @@ -796,42 +1193,68 @@ _initSpecialOptions: function () { var options = this.options; if (options.fileInput === undefined) { - options.fileInput = this.element.is('input:file') ? - this.element : this.element.find('input:file'); + options.fileInput = this.element.is('input[type="file"]') ? + this.element : this.element.find('input[type="file"]'); } else if (!(options.fileInput instanceof $)) { options.fileInput = $(options.fileInput); } if (!(options.dropZone instanceof $)) { options.dropZone = $(options.dropZone); } + if (!(options.pasteZone instanceof $)) { + options.pasteZone = $(options.pasteZone); + } + }, + + _getRegExp: function (str) { + var parts = str.split('/'), + modifiers = parts.pop(); + parts.shift(); + return new RegExp(parts.join('/'), modifiers); + }, + + _isRegExpOption: function (key, value) { + return key !== 'url' && $.type(value) === 'string' && + /^\/.*\/[igm]{0,3}$/.test(value); + }, + + _initDataAttributes: function () { + var that = this, + options = this.options; + // Initialize options set via HTML5 data-attributes: + $.each( + $(this.element[0].cloneNode(false)).data(), + function (key, value) { + if (that._isRegExpOption(key, value)) { + value = that._getRegExp(value); + } + options[key] = value; + } + ); }, _create: function () { - var options = this.options, - dataOpts = $.extend({}, this.element.data()); - dataOpts[this.widgetName] = undefined; - $.extend(options, dataOpts); - options.namespace = options.namespace || this.widgetName; + this._initDataAttributes(); this._initSpecialOptions(); this._slots = []; this._sequence = this._getXHRPromise(true); - this._sending = this._active = this._loaded = this._total = 0; + this._sending = this._active = 0; + this._initProgressObject(this); this._initEventHandlers(); }, - destroy: function () { - this._destroyEventHandlers(); - $.Widget.prototype.destroy.call(this); - }, - - enable: function () { - $.Widget.prototype.enable.call(this); - this._initEventHandlers(); + // This method is exposed to the widget API and allows to query + // the number of active uploads: + active: function () { + return this._active; }, - disable: function () { - this._destroyEventHandlers(); - $.Widget.prototype.disable.call(this); + // This method is exposed to the widget API and allows to query + // the widget upload progress. + // It returns an object with loaded, total and bitrate properties + // for the running uploads: + progress: function () { + return this._progress; }, // This method is exposed to the widget API and allows adding files @@ -839,21 +1262,65 @@ // must have a files property and can contain additional options: // .fileupload('add', {files: filesList}); add: function (data) { + var that = this; if (!data || this.options.disabled) { return; } - data.files = $.each($.makeArray(data.files), this._normalizeFile); - this._onAdd(null, data); + if (data.fileInput && !data.files) { + this._getFileInputFiles(data.fileInput).always(function (files) { + data.files = files; + that._onAdd(null, data); + }); + } else { + data.files = $.makeArray(data.files); + this._onAdd(null, data); + } }, // This method is exposed to the widget API and allows sending files // using the fileupload API. The data parameter accepts an object which - // must have a files property and can contain additional options: + // must have a files or fileInput property and can contain additional options: // .fileupload('send', {files: filesList}); // The method returns a Promise object for the file upload call. send: function (data) { if (data && !this.options.disabled) { - data.files = $.each($.makeArray(data.files), this._normalizeFile); + if (data.fileInput && !data.files) { + var that = this, + dfd = $.Deferred(), + promise = dfd.promise(), + jqXHR, + aborted; + promise.abort = function () { + aborted = true; + if (jqXHR) { + return jqXHR.abort(); + } + dfd.reject(null, 'abort', 'abort'); + return promise; + }; + this._getFileInputFiles(data.fileInput).always( + function (files) { + if (aborted) { + return; + } + if (!files.length) { + dfd.reject(); + return; + } + data.files = files; + jqXHR = that._onSend(null, data).then( + function (result, textStatus, jqXHR) { + dfd.resolve(result, textStatus, jqXHR); + }, + function (jqXHR, textStatus, errorThrown) { + dfd.reject(jqXHR, textStatus, errorThrown); + } + ); + } + ); + return this._enhancePromise(promise); + } + data.files = $.makeArray(data.files); if (data.files.length) { return this._onSend(null, data); } @@ -863,4 +1330,4 @@ }); -})); +})); \ No newline at end of file diff --git a/apps/files/js/jquery.iframe-transport.js b/apps/files/js/jquery.iframe-transport.js index d85c0c11297..5c9df77976b 100644 --- a/apps/files/js/jquery.iframe-transport.js +++ b/apps/files/js/jquery.iframe-transport.js @@ -1,5 +1,5 @@ /* - * jQuery Iframe Transport Plugin 1.3 + * jQuery Iframe Transport Plugin 1.7 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2011, Sebastian Tschan @@ -30,27 +30,45 @@ // The iframe transport accepts three additional options: // options.fileInput: a jQuery collection of file input fields // options.paramName: the parameter name for the file form data, - // overrides the name property of the file input field(s) + // overrides the name property of the file input field(s), + // can be a string or an array of strings. // options.formData: an array of objects with name and value properties, // equivalent to the return data of .serializeArray(), e.g.: // [{name: 'a', value: 1}, {name: 'b', value: 2}] $.ajaxTransport('iframe', function (options) { - if (options.async && (options.type === 'POST' || options.type === 'GET')) { + if (options.async) { var form, - iframe; + iframe, + addParamChar; return { send: function (_, completeCallback) { form = $('
'); + form.attr('accept-charset', options.formAcceptCharset); + addParamChar = /\?/.test(options.url) ? '&' : '?'; + // XDomainRequest only supports GET and POST: + if (options.type === 'DELETE') { + options.url = options.url + addParamChar + '_method=DELETE'; + options.type = 'POST'; + } else if (options.type === 'PUT') { + options.url = options.url + addParamChar + '_method=PUT'; + options.type = 'POST'; + } else if (options.type === 'PATCH') { + options.url = options.url + addParamChar + '_method=PATCH'; + options.type = 'POST'; + } // javascript:false as initial iframe src // prevents warning popups on HTTPS in IE6. // IE versions below IE8 cannot set the name property of // elements that have already been added to the DOM, // so we set the name along with the iframe HTML markup: + counter += 1; iframe = $( '' + counter + '">' ).bind('load', function () { - var fileInputClones; + var fileInputClones, + paramNames = $.isArray(options.paramName) ? + options.paramName : [options.paramName]; iframe .unbind('load') .bind('load', function () { @@ -79,7 +97,12 @@ // (happens on form submits to iframe targets): $('') .appendTo(form); - form.remove(); + window.setTimeout(function () { + // Removing the form in a setTimeout call + // allows Chrome's developer tools to display + // the response result + form.remove(); + }, 0); }); form .prop('target', iframe.prop('name')) @@ -101,8 +124,11 @@ return fileInputClones[index]; }); if (options.paramName) { - options.fileInput.each(function () { - $(this).prop('name', options.paramName); + options.fileInput.each(function (index) { + $(this).prop( + 'name', + paramNames[index] || options.paramName + ); }); } // Appending the file input fields to the hidden form @@ -144,22 +170,36 @@ }); // The iframe transport returns the iframe content document as response. - // The following adds converters from iframe to text, json, html, and script: + // The following adds converters from iframe to text, json, html, xml + // and script. + // Please note that the Content-Type for JSON responses has to be text/plain + // or text/html, if the browser doesn't include application/json in the + // Accept header, else IE will show a download dialog. + // The Content-Type for XML responses on the other hand has to be always + // application/xml or text/xml, so IE properly parses the XML response. + // See also + // https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation $.ajaxSetup({ converters: { 'iframe text': function (iframe) { - return $(iframe[0].body).text(); + return iframe && $(iframe[0].body).text(); }, 'iframe json': function (iframe) { - return $.parseJSON($(iframe[0].body).text()); + return iframe && $.parseJSON($(iframe[0].body).text()); }, 'iframe html': function (iframe) { - return $(iframe[0].body).html(); + return iframe && $(iframe[0].body).html(); + }, + 'iframe xml': function (iframe) { + var xmlDoc = iframe && iframe[0]; + return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc : + $.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) || + $(xmlDoc.body).html()); }, 'iframe script': function (iframe) { - return $.globalEval($(iframe[0].body).text()); + return iframe && $.globalEval($(iframe[0].body).text()); } } }); -})); +})); \ No newline at end of file diff --git a/apps/files/templates/part.list.php b/apps/files/templates/part.list.php index 0c7d6936697..39f5ac471e5 100644 --- a/apps/files/templates/part.list.php +++ b/apps/files/templates/part.list.php @@ -1,7 +1,8 @@ +$totalsize = 0; +$pc = 0; ?> $relative_date_color = round((time()-$file['mtime'])/60/60/24*14); if($relative_date_color>160) $relative_date_color = 160; $name = \OCP\Util::encodePath($file['name']); - $directory = \OCP\Util::encodePath($file['directory']); ?> + $directory = \OCP\Util::encodePath($file['directory']); + ?> + + + )"> -- cgit v1.2.3 From 7c9d9992432839f2265b8f6b0f43ed15bfca9ff1 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Thu, 22 Aug 2013 14:29:00 +0200 Subject: reduced complexity, added listing conflicts to dialog --- apps/files/ajax/upload.php | 1 + apps/files/css/files.css | 53 +++++--- apps/files/js/file-upload.js | 226 +++++++++++++++++++++++--------- core/js/oc-dialogs.js | 305 +++++++++++++++++++++++++++---------------- 4 files changed, 390 insertions(+), 195 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index 619b5f6a04b..218482cb41d 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -131,6 +131,7 @@ if (strpos($dir, '..') === false) { $result[] = array('status' => 'success', 'mime' => $meta['mimetype'], + 'mtime' => $meta['mtime'], 'size' => $meta['size'], 'id' => $meta['fileid'], 'name' => basename($target), diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 0ff25a24d76..dcd6aeadf82 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -193,28 +193,54 @@ table.dragshadow td.size { .oc-dialog .fileexists table { width: 100%; } -.oc-dialog .fileexists .original .icon { +.oc-dialog .fileexists th { + padding-left: 0; + padding-right: 0; +} +.oc-dialog .fileexists th input[type='checkbox'] { + margin-right: 3px; +} +.oc-dialog .fileexists th:first-child { + width: 235px; +} +.oc-dialog .fileexists th label { + font-weight: normal; + color:black; +} +.oc-dialog .fileexists th .count { + margin-left: 3px; +} +.oc-dialog .fileexists .conflict { + width: 100%; + height: 85px; +} +.oc-dialog .fileexists .conflict .filename { + color:#777; + word-break: break-all; +} +.oc-dialog .fileexists .icon { width: 64px; height: 64px; - margin: 5px 5px 5px 0px; + margin: 0px 5px 5px 5px; background-repeat: no-repeat; background-size: 64px 64px; float: left; } .oc-dialog .fileexists .replacement { - margin-top: 20px; - margin-bottom: 20px; + float: left; + width: 235px; } - -.oc-dialog .fileexists .replacement .icon { - width: 64px; - height: 64px; - margin: 5px 5px 5px 0px; - background-repeat: no-repeat; - background-size: 64px 64px; +.oc-dialog .fileexists .original { + float: left; + width: 235px; +} +.oc-dialog .fileexists .conflict-wrapper { + overflow-y:scroll; + max-height: 225px; +} +.oc-dialog .fileexists .conflict-wrapper input[type='checkbox'] { float: left; - clear: both; } .oc-dialog .fileexists .toggle { @@ -234,9 +260,6 @@ table.dragshadow td.size { vertical-align: bottom; } -.oc-dialog .fileexists h3 { - font-weight: bold; -} .oc-dialog .oc-dialog-buttonrow { diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index c620942170c..ec8c97ff457 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -1,4 +1,43 @@ /** + * + * and yet another idea how to handle file uploads: + * let the jquery fileupload thing handle as much as possible + * + * use singlefileupload + * on first add of every selection + * - check all files of originalFiles array with files in dir + * - on conflict show dialog + * - skip all -> remember as default action + * - replace all -> remember as default action + * - choose -> show choose dialog + * - mark files to keep + * - when only existing -> remember as single skip action + * - when only new -> remember as single replace action + * - when both -> remember as single autorename action + * - start uploading selection + * + * on send + * - if single action or default action + * - when skip -> abort upload + * ..- when replace -> add replace=true parameter + * ..- when rename -> add newName=filename parameter + * ..- when autorename -> add autorename=true parameter + * + * on fail + * - if server sent existserror + * - show dialog + * - on skip single -> abort single upload + * - on skip always -> remember as default action + * - on replace single -> replace single upload + * - on replace always -> remember as default action + * - on rename single -> rename single upload, propose autorename - when changed disable remember always checkbox + * - on rename always -> remember autorename as default action + * - resubmit data + * + * on uplad done + * - if last upload -> unset default action + * + * ------------------------------------------------------------- * * use put t ocacnel upload before it starts? use chunked uploads? * @@ -202,11 +241,11 @@ OC.Upload = { data.submit(); }, logStatus:function(caption, e, data) { - console.log(caption+' ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); - if (data) { - console.log(data); - } - console.log(e); + console.log(caption); + console.log(data); + }, + checkExistingFiles: function (selection, callbacks){ + callbacks.onNoConflicts(selection); } }; @@ -214,44 +253,110 @@ $(document).ready(function() { var file_upload_param = { dropZone: $('#content'), // restrict dropZone to content div + autoUpload: false, + sequentialUploads: true, //singleFileUploads is on by default, so the data.files array will always have length 1 + /** + * on first add of every selection + * - check all files of originalFiles array with files in dir + * - on conflict show dialog + * - skip all -> remember as single skip action for all conflicting files + * - replace all -> remember as single replace action for all conflicting files + * - choose -> show choose dialog + * - mark files to keep + * - when only existing -> remember as single skip action + * - when only new -> remember as single replace action + * - when both -> remember as single autorename action + * - start uploading selection + * @param {type} e + * @param {type} data + * @returns {Boolean} + */ add: function(e, data) { OC.Upload.logStatus('add', e, data); var that = $(this); - // lookup selection for dir - var selection = OC.Upload.getSelection(data.originalFiles); + // we need to collect all data upload objects before starting the upload so we can check their existence + // and set individual conflict actions. unfortunately there is only one variable that we can use to identify + // the selection a data upload is part of, so we have to collect them in data.originalFiles + // turning singleFileUploads off is not an option because we want to gracefully handle server errors like + // already exists - if (!selection.dir) { - selection.dir = $('#dir').val(); + // create a container where we can store the data objects + if ( ! data.originalFiles.selection ) { + // initialize selection and remember number of files to upload + data.originalFiles.selection = { + uploads: [], + filesToUpload: data.originalFiles.length, + totalBytes: 0 + }; } + var selection = data.originalFiles.selection; - if ( ! selection.checked ) { + // add uploads + if ( selection.uploads.length < selection.filesToUpload ){ + // remember upload + selection.uploads.push(data); + } + + //examine file + var file = data.files[0]; + + if (file.type === '' && file.size === 4096) { + data.textStatus = 'dirorzero'; + data.errorThrown = t('files', 'Unable to upload {filename} as it is a directory or has 0 bytes', + {filename: file.name} + ); + } + + // add size + selection.totalBytes += file.size; + + //check max upload size + if (selection.totalBytes > $('#max_upload').val()) { + data.textStatus = 'notenoughspace'; + data.errorThrown = t('files', 'Not enough space available'); + } + + // end upload for whole selection on error + if (data.errorThrown) { + // trigger fileupload fail + var fu = that.data('blueimp-fileupload') || that.data('fileupload'); + fu._trigger('fail', e, data); + return false; //don't upload anything + } + + // check existing files whan all is collected + if ( selection.uploads.length >= selection.filesToUpload ) { - selection.totalBytes = 0; - $.each(data.originalFiles, function(i, file) { - selection.totalBytes += file.size; + //remove our selection hack: + delete data.originalFiles.selection; - if (file.type === '' && file.size === 4096) { - data.textStatus = 'dirorzero'; - data.errorThrown = t('files', 'Unable to upload {filename} as it is a directory or has 0 bytes', - {filename: file.name} - ); - return false; + var callbacks = { + + onNoConflicts: function (selection) { + $.each(selection.uploads, function(i, upload) { + upload.submit(); + }); + }, + onSkipConflicts: function (selection) { + //TODO mark conflicting files as toskip + }, + onReplaceConflicts: function (selection) { + //TODO mark conflicting files as toreplace + }, + onChooseConflicts: function (selection) { + //TODO mark conflicting files as chosen + }, + onCancel: function (selection) { + $.each(selection.uploads, function(i, upload) { + upload.abort(); + }); } - }); + }; - if (selection.totalBytes > $('#max_upload').val()) { - data.textStatus = 'notenoughspace'; - data.errorThrown = t('files', 'Not enough space available'); - } - if (data.errorThrown) { - //don't upload anything - var fu = that.data('blueimp-fileupload') || that.data('fileupload'); - fu._trigger('fail', e, data); - return false; - } + OC.Upload.checkExistingFiles(selection, callbacks); //TODO refactor away: //show cancel button @@ -259,15 +364,8 @@ $(document).ready(function() { $('#uploadprogresswrapper input.stop').show(); } } + - //all subsequent add calls for this selection can be ignored - //allow navigating to the selection from a context - //context.selection = data.originalFiles.selection; - - //allow navigating to contexts / files of a selection - selection.files[data.files[0].name] = data; - - OC.Upload.queueUpload(data); //TODO check filename already exists /* @@ -283,7 +381,7 @@ $(document).ready(function() { } */ - return true; + return true; // continue adding files }, /** * called after the first add, does NOT have the data param @@ -314,8 +412,8 @@ $(document).ready(function() { $('#notification').fadeOut(); }, 5000); } - var selection = OC.Upload.getSelection(data.originalFiles); - OC.Upload.deleteSelectionUpload(selection, data.files[0].name); + //var selection = OC.Upload.getSelection(data.originalFiles); + //OC.Upload.deleteSelectionUpload(selection, data.files[0].name); //if user pressed cancel hide upload progress bar and cancel button if (data.errorThrown === 'abort') { @@ -339,8 +437,8 @@ $(document).ready(function() { if($('html.lte9').length > 0) { return; } - //var progress = (data.loaded/data.total)*100; - var progress = OC.Upload.progressBytes(); + var progress = (data.loaded/data.total)*100; + //var progress = OC.Upload.progressBytes(); $('#uploadprogressbar').progressbar('value', progress); }, /** @@ -359,15 +457,15 @@ $(document).ready(function() { response = data.result[0].body.innerText; } var result=$.parseJSON(response); - var selection = OC.Upload.getSelection(data.originalFiles); + //var selection = OC.Upload.getSelection(data.originalFiles); if(typeof result[0] !== 'undefined' && result[0].status === 'success' ) { - if (selection) { - selection.loadedBytes+=data.loaded; - } - OC.Upload.nextUpload(); + //if (selection) { + // selection.loadedBytes+=data.loaded; + //} + //OC.Upload.nextUpload(); } else { if (result[0].status === 'existserror') { //show "file already exists" dialog @@ -385,10 +483,10 @@ $(document).ready(function() { } //if user pressed cancel hide upload chrome - if (! OC.Upload.isProcessing()) { - $('#uploadprogresswrapper input.stop').fadeOut(); - $('#uploadprogressbar').fadeOut(); - } + //if (! OC.Upload.isProcessing()) { + // $('#uploadprogresswrapper input.stop').fadeOut(); + // $('#uploadprogressbar').fadeOut(); + //} }, /** @@ -398,27 +496,27 @@ $(document).ready(function() { */ stop: function(e, data) { OC.Upload.logStatus('stop', e, data); - if(OC.Upload.progressBytes()>=100) { //only hide controls when all selections have ended uploading + //if(OC.Upload.progressBytes()>=100) { //only hide controls when all selections have ended uploading - OC.Upload.cancelUploads(); //cleanup + //OC.Upload.cancelUploads(); //cleanup - if(data.dataType !== 'iframe') { - $('#uploadprogresswrapper input.stop').hide(); - } + // if(data.dataType !== 'iframe') { + // $('#uploadprogresswrapper input.stop').hide(); + // } //IE < 10 does not fire the necessary events for the progress bar. if($('html.lte9').length > 0) { return; } - $('#uploadprogressbar').progressbar('value', 100); - $('#uploadprogressbar').fadeOut(); - } + // $('#uploadprogressbar').progressbar('value', 100); + // $('#uploadprogressbar').fadeOut(); + //} //if user pressed cancel hide upload chrome - if (! OC.Upload.isProcessing()) { - $('#uploadprogresswrapper input.stop').fadeOut(); - $('#uploadprogressbar').fadeOut(); - } + //if (! OC.Upload.isProcessing()) { + // $('#uploadprogresswrapper input.stop').fadeOut(); + // $('#uploadprogressbar').fadeOut(); + //} } }; diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index ea03ef21455..a101cce9d1f 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -200,148 +200,221 @@ var OCdialogs = { alert(t('core', 'Error loading message template')); }); }, + _fileexistsshown: false, /** * Displays file exists dialog - * @param {object} original a file with name, size and mtime - * @param {object} replacement a file with name, size and mtime - * @param {object} controller a controller with onCancel, onSkip, onReplace and onRename methods + * @param {object} data upload object + * @param {object} original file with name, size and mtime + * @param {object} replacement file with name, size and mtime + * @param {object} controller with onCancel, onSkip, onReplace and onRename methods */ fileexists:function(data, original, replacement, controller) { + var self = this; var selection = controller.getSelection(data.originalFiles); if (selection.defaultAction) { controller[selection.defaultAction](data); } else { - $.when(this._getFileExistsTemplate()).then(function($tmpl) { - var dialog_name = 'oc-dialog-fileexists-' + OCdialogs.dialogs_counter + '-content'; - var dialog_id = '#' + dialog_name; - var title = t('files','Replace »{filename}«?',{filename: original.name}); - var original_size= t('files','Size: {size}',{size: original.size}); - var original_mtime = t('files','Last changed: {mtime}',{mtime: original.mtime}); - var replacement_size= t('files','Size: {size}',{size: replacement.size}); - var replacement_mtime = t('files','Last changed: {mtime}',{mtime: replacement.mtime}); - var $dlg = $tmpl.octemplate({ - dialog_name: dialog_name, - title: title, - type: 'fileexists', - - why: t('files','Another file with the same name already exists in "{dir}".',{dir:'somedir'}), - what: t('files','Replacing it will overwrite it\'s contents.'), - original_heading: t('files','Original file'), - original_size: original_size, - original_mtime: original_mtime, - - replacement_heading: t('files','Replace with'), - replacement_size: replacement_size, - replacement_mtime: replacement_mtime, - - new_name_label: t('files','Choose a new name for the target.'), - all_files_label: t('files','Use this action for all files.') - }); - $('body').append($dlg); - + var dialog_name = 'oc-dialog-fileexists-content'; + var dialog_id = '#' + dialog_name; + if (this._fileexistsshown) { + // add row + var conflict = $(dialog_id+ ' .conflict').last().clone(); + conflict.find('.name').text(original.name); + conflict.find('.original .size').text(humanFileSize(original.size)); + conflict.find('.original .mtime').text(formatDate(original.mtime*1000)); + conflict.find('.replacement .size').text(humanFileSize(replacement.size)); + conflict.find('.replacement .mtime').text(formatDate(replacement.lastModifiedDate)); getMimeIcon(original.type,function(path){ - $(dialog_id + ' .original .icon').css('background-image','url('+path+')'); + conflict.find('.original .icon').css('background-image','url('+path+')'); }); getMimeIcon(replacement.type,function(path){ - $(dialog_id + ' .replacement .icon').css('background-image','url('+path+')'); + conflict.find('.replacement .icon').css('background-image','url('+path+')'); }); - $(dialog_id + ' #newname').val(original.name); + $(dialog_id+' .conflict').last().after(conflict); + $(dialog_id).parent().children('.oc-dialog-title').text(t('files','{count} file conflicts',{count:$(dialog_id+ ' .conflict').length})); + + //set more recent mtime bold + if (replacement.lastModifiedDate.getTime() > original.mtime*1000) { + conflict.find('.replacement .mtime').css('font-weight', 'bold'); + } else if (replacement.lastModifiedDate.getTime() < original.mtime*1000) { + conflict.find('.original .mtime').css('font-weight', 'bold'); + } else { + //TODO add to same mtime colletion? + } + + // set bigger size bold + if (replacement.size > original.size) { + conflict.find('.replacement .size').css('font-weight','bold'); + } else if (replacement.size < original.size) { + conflict.find('.original .size').css('font-weight','bold'); + } else { + //TODO add to same size colletion? + } + + //add checkbox toggling actions + conflict.find('.replacement,.original').on('click', function(){ + var checkbox = $(this).find('input[type="checkbox"]'); + checkbox.prop('checkbox', !checkbox.prop('checkbox')); + }).find('input[type="checkbox"]').prop('checkbox',false); + + //TODO show skip action for files with same size and mtime + + $(window).trigger('resize'); + } else { + //create dialog + this._fileexistsshown = true; + $.when(this._getFileExistsTemplate()).then(function($tmpl) { + var title = t('files','One file conflict'); + var original_size = humanFileSize(original.size); + var original_mtime = formatDate(original.mtime*1000); + var replacement_size= humanFileSize(replacement.size); + var replacement_mtime = formatDate(replacement.lastModifiedDate); + var $dlg = $tmpl.octemplate({ + dialog_name: dialog_name, + title: title, + type: 'fileexists', + why: t('files','Which files do you want to keep?'), + what: t('files','If you select both versions, the copied file will have a number added to its name.'), + filename: original.name, + + original_size: original_size, + original_mtime: original_mtime, - $(dialog_id + ' #newname').on('keyup', function(e){ - if ($(dialog_id + ' #newname').val() === original.name) { - $(dialog_id + ' + div .rename').removeClass('primary').hide(); - $(dialog_id + ' + div .replace').addClass('primary').show(); - } else { - $(dialog_id + ' + div .rename').addClass('primary').show(); - $(dialog_id + ' + div .replace').removeClass('primary').hide(); - } - }); + replacement_size: replacement_size, + replacement_mtime: replacement_mtime + }); + $('body').append($dlg); - buttonlist = [{ - text: t('core', 'Cancel'), - classes: 'cancel', - click: function(){ - if ( typeof controller.onCancel !== 'undefined') { - controller.onCancel(data); - } - $(dialog_id).ocdialog('close'); - } - }, - { - text: t('core', 'Skip'), - classes: 'skip', - click: function(){ - if ( typeof controller.onSkip !== 'undefined') { - if($(dialog_id + ' #allfiles').prop('checked')){ - selection.defaultAction = 'onSkip'; - /*selection.defaultAction = function(){ - controller.onSkip(data); - };*/ - } - controller.onSkip(data); - } - // trigger fileupload done with status skip - //data.result[0].status = 'skip'; - //fileupload._trigger('done', data.e, data); - $(dialog_id).ocdialog('close'); + getMimeIcon(original.type,function(path){ + $(dialog_id + ' .original .icon').css('background-image','url('+path+')'); + }); + getMimeIcon(replacement.type,function(path){ + $(dialog_id + ' .replacement .icon').css('background-image','url('+path+')'); + }); + $(dialog_id + ' #newname').val(original.name); + + $(dialog_id + ' #newname').on('keyup', function(e){ + if ($(dialog_id + ' #newname').val() === original.name) { + $(dialog_id + ' + div .rename').removeClass('primary').hide(); + $(dialog_id + ' + div .replace').addClass('primary').show(); + } else { + $(dialog_id + ' + div .rename').addClass('primary').show(); + $(dialog_id + ' + div .replace').removeClass('primary').hide(); } - }, - { - text: t('core', 'Replace'), - classes: 'replace', - click: function(){ - if ( typeof controller.onReplace !== 'undefined') { - if($(dialog_id + ' #allfiles').prop('checked')){ - selection.defaultAction = 'onReplace'; - /*selection.defaultAction = function(){ - controller.onReplace(data); - };*/ + }); + + buttonlist = [{ + text: t('core', 'Cancel'), + classes: 'cancel', + click: function(){ + self._fileexistsshown = false; + if ( typeof controller.onCancel !== 'undefined') { + controller.onCancel(data); } - controller.onReplace(data); + $(dialog_id).ocdialog('close'); } - $(dialog_id).ocdialog('close'); }, - defaultButton: true - }, - { - text: t('core', 'Rename'), - classes: 'rename', - click: function(){ - if ( typeof controller.onRename !== 'undefined') { - //TODO use autorename when repeat is checked - controller.onRename(data, $(dialog_id + ' #newname').val()); + { + text: t('core', 'Continue'), + classes: 'continue', + click: function(){ + self._fileexistsshown = false; + if ( typeof controller.onRename !== 'undefined') { + //TODO use autorename when repeat is checked + controller.onRename(data, $(dialog_id + ' #newname').val()); + } + $(dialog_id).ocdialog('close'); } - $(dialog_id).ocdialog('close'); + }]; + + $(dialog_id).ocdialog({ + width: 500, + closeOnEscape: true, + modal: true, + buttons: buttonlist, + closeButton: null + }); + + $(dialog_id + ' + div .rename').hide(); + $(dialog_id + ' #newname').hide(); + + $(dialog_id + ' #newnamecb').on('change', function(){ + if ($(dialog_id + ' #newnamecb').prop('checked')) { + $(dialog_id + ' #newname').fadeIn(); + } else { + $(dialog_id + ' #newname').fadeOut(); + $(dialog_id + ' #newname').val(original.name); } - }]; + }); + $(dialog_id).css('height','auto'); - $(dialog_id).ocdialog({ - width: 500, - closeOnEscape: true, - modal: true, - buttons: buttonlist, - closeButton: null - }); - OCdialogs.dialogs_counter++; + var conflict = $(dialog_id + ' .conflict').last(); + //set more recent mtime bold + if (replacement.lastModifiedDate.getTime() > original.mtime*1000) { + conflict.find('.replacement .mtime').css('font-weight','bold'); + } else if (replacement.lastModifiedDate.getTime() < original.mtime*1000) { + conflict.find('.original .mtime').css('font-weight','bold'); + } else { + //TODO add to same mtime colletion? + } - $(dialog_id + ' + div .rename').hide(); - $(dialog_id + ' #newname').hide(); - - $(dialog_id + ' #newnamecb').on('change', function(){ - if ($(dialog_id + ' #newnamecb').prop('checked')) { - $(dialog_id + ' #newname').fadeIn(); + // set bigger size bold + if (replacement.size > original.size) { + conflict.find('.replacement .size').css('font-weight','bold'); + } else if (replacement.size < original.size) { + conflict.find('.original .size').css('font-weight','bold'); } else { - $(dialog_id + ' #newname').fadeOut(); - $(dialog_id + ' #newname').val(original.name); + //TODO add to same size colletion? } - }); - - }) - .fail(function() { - alert(t('core', 'Error loading file exists template')); - }); + //add checkbox toggling actions + //add checkbox toggling actions + $(dialog_id).find('.allnewfiles').on('click', function(){ + var checkboxes = $(dialog_id).find('.replacement input[type="checkbox"]'); + checkboxes.prop('checked', $(this).prop('checked')); + }); + $(dialog_id).find('.allexistingfiles').on('click', function(){ + var checkboxes = $(dialog_id).find('.original input[type="checkbox"]'); + checkboxes.prop('checked', $(this).prop('checked')); + }); + conflict.find('.replacement,.original').on('click', function(){ + var checkbox = $(this).find('input[type="checkbox"]'); + checkbox.prop('checked', !checkbox.prop('checked')); + }); + + //update counters + $(dialog_id).on('click', '.replacement,.allnewfiles', function(){ + var count = $(dialog_id).find('.replacement input[type="checkbox"]:checked').length; + if (count === $(dialog_id+ ' .conflict').length) { + $(dialog_id).find('.allnewfiles').prop('checked', true); + $(dialog_id).find('.allnewfiles + .count').text(t('files','(all selected)')); + } else if (count > 0) { + $(dialog_id).find('.allnewfiles').prop('checked', false); + $(dialog_id).find('.allnewfiles + .count').text(t('files','({count} selected)',{count:count})); + } else { + $(dialog_id).find('.allnewfiles').prop('checked', false); + $(dialog_id).find('.allnewfiles + .count').text(''); + } + }); + $(dialog_id).on('click', '.original,.allexistingfiles', function(){ + var count = $(dialog_id).find('.original input[type="checkbox"]:checked').length; + if (count === $(dialog_id+ ' .conflict').length) { + $(dialog_id).find('.allexistingfiles').prop('checked', true); + $(dialog_id).find('.allexistingfiles + .count').text(t('files','(all selected)')); + } else if (count > 0) { + $(dialog_id).find('.allexistingfiles').prop('checked', false); + $(dialog_id).find('.allexistingfiles + .count').text(t('files','({count} selected)',{count:count})); + } else { + $(dialog_id).find('.allexistingfiles').prop('checked', false); + $(dialog_id).find('.allexistingfiles + .count').text(''); + } + }); + }) + .fail(function() { + alert(t('core', 'Error loading file exists template')); + }); + } } }, _getFilePickerTemplate: function() { -- cgit v1.2.3 From 3eed060ec9f680aed4b254f018d832ade5f873c7 Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Tue, 27 Aug 2013 23:53:04 +0200 Subject: backport of #4357 to master --- apps/files/ajax/upload.php | 24 ++++++++++++++---------- apps/files/js/file-upload.js | 26 ++++++++++++-------------- apps/files_sharing/lib/sharedstorage.php | 10 +++++++--- 3 files changed, 33 insertions(+), 27 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index dde5d3c50af..1d03cd89f83 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -105,16 +105,20 @@ if (strpos($dir, '..') === false) { $meta = \OC\Files\Filesystem::getFileInfo($target); // updated max file size after upload $storageStats = \OCA\files\lib\Helper::buildFileStorageStatistics($dir); - - $result[] = array('status' => 'success', - 'mime' => $meta['mimetype'], - 'size' => $meta['size'], - 'id' => $meta['fileid'], - 'name' => basename($target), - 'originalname' => $files['name'][$i], - 'uploadMaxFilesize' => $maxUploadFileSize, - 'maxHumanFilesize' => $maxHumanFileSize - ); + if ($meta === false) { + OCP\JSON::error(array('data' => array_merge(array('message' => $l->t('Upload failed')), $storageStats))); + exit(); + } else { + $result[] = array('status' => 'success', + 'mime' => $meta['mimetype'], + 'size' => $meta['size'], + 'id' => $meta['fileid'], + 'name' => basename($target), + 'originalname' => $files['name'][$i], + 'uploadMaxFilesize' => $maxUploadFileSize, + 'maxHumanFilesize' => $maxHumanFileSize + ); + } } } OCP\JSON::encodedPrint($result); diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index f262f11f065..1e6ab74fb6d 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -102,6 +102,18 @@ $(document).ready(function() { var result=$.parseJSON(response); if(typeof result[0] !== 'undefined' && result[0].status === 'success') { + var filename = result[0].originalname; + + // delete jqXHR reference + if (typeof data.context !== 'undefined' && data.context.data('type') === 'dir') { + var dirName = data.context.data('file'); + delete uploadingFiles[dirName][filename]; + if ($.assocArraySize(uploadingFiles[dirName]) == 0) { + delete uploadingFiles[dirName]; + } + } else { + delete uploadingFiles[filename]; + } var file = result[0]; } else { data.textStatus = 'servererror'; @@ -109,20 +121,6 @@ $(document).ready(function() { var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); fu._trigger('fail', e, data); } - - var filename = result[0].originalname; - - // delete jqXHR reference - if (typeof data.context !== 'undefined' && data.context.data('type') === 'dir') { - var dirName = data.context.data('file'); - delete uploadingFiles[dirName][filename]; - if ($.assocArraySize(uploadingFiles[dirName]) == 0) { - delete uploadingFiles[dirName]; - } - } else { - delete uploadingFiles[filename]; - } - }, /** * called after last upload diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 7384b094cb0..d91acbbb2bd 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -362,9 +362,13 @@ class Shared extends \OC\Files\Storage\Common { case 'xb': case 'a': case 'ab': - if (!$this->isUpdatable($path)) { - return false; - } + $exists = $this->file_exists($path); + if ($exists && !$this->isUpdatable($path)) { + return false; + } + if (!$exists && !$this->isCreatable(dirname($path))) { + return false; + } } $info = array( 'target' => $this->sharedFolder.$path, -- cgit v1.2.3 From f4ec5182bdeaa611d13648b50d24f80501d92acd Mon Sep 17 00:00:00 2001 From: ringmaster Date: Thu, 29 Aug 2013 12:05:20 -0400 Subject: Workaround for IE 9 & 10 for clicking filelist after adding new item --- apps/files/js/file-upload.js | 3 +++ 1 file changed, 3 insertions(+) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 1e6ab74fb6d..a6cb13572d2 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -342,6 +342,9 @@ $(document).ready(function() { } var li=form.parent(); form.remove(); + /* workaround for IE 9&10 click event trap, 2 lines: */ + $('input').first().focus(); + $('#content').focus(); li.append('

'+li.data('text')+'

'); $('#new>a').click(); }); -- cgit v1.2.3 From 307b673b79120d79c406927ee8a5f3ef83c02af2 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Sun, 1 Sep 2013 16:14:46 +0200 Subject: Fixed public upload error that prevents upload Public upload is broken because the file_upload_param variable expected to exist by public.js didn't. This fix sets the variable scope to the window to make it accessible outside. --- apps/files/js/file-upload.js | 1 + 1 file changed, 1 insertion(+) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 3d620c5640b..e9b07518bab 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -348,4 +348,5 @@ $(document).ready(function() { $('#new>a').click(); }); }); + window.file_upload_param = file_upload_param; }); -- cgit v1.2.3 From bbf8acb383bdcb1dcb53f4b9d5a8d894b17401df Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Thu, 5 Sep 2013 10:19:54 +0200 Subject: separate uploading code from progress code, add progress capability detection --- apps/files/js/file-upload.js | 193 +++++++++++++++++++++++-------------------- apps/files/js/filelist.js | 20 ++--- apps/files/js/files.js | 32 +++---- core/js/oc-dialogs.js | 13 ++- 4 files changed, 136 insertions(+), 122 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index be3d7e08aff..bd0ae4db00b 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -14,6 +14,7 @@ * - when only existing -> remember as single skip action * - when only new -> remember as single replace action * - when both -> remember as single autorename action + * - continue -> apply marks, when nothing is marked continue == skip all * - start uploading selection * * on send @@ -96,7 +97,30 @@ * */ +// from https://github.com/New-Bamboo/example-ajax-upload/blob/master/public/index.html +// also see article at http://blog.new-bamboo.co.uk/2012/01/10/ridiculously-simple-ajax-uploads-with-formdata +// Function that will allow us to know if Ajax uploads are supported +function supportAjaxUploadWithProgress() { + return supportFileAPI() && supportAjaxUploadProgressEvents() && supportFormData(); + // Is the File API supported? + function supportFileAPI() { + var fi = document.createElement('INPUT'); + fi.type = 'file'; + return 'files' in fi; + }; + + // Are progress events supported? + function supportAjaxUploadProgressEvents() { + var xhr = new XMLHttpRequest(); + return !! (xhr && ('upload' in xhr) && ('onprogress' in xhr.upload)); + }; + + // Is FormData supported? + function supportFormData() { + return !! window.FormData; + } +} //TODO clean uploads when all progress has completed OC.Upload = { @@ -245,6 +269,7 @@ OC.Upload = { console.log(data); }, checkExistingFiles: function (selection, callbacks){ + // FIXME check filelist before uploading callbacks.onNoConflicts(selection); } }; @@ -327,7 +352,7 @@ $(document).ready(function() { return false; //don't upload anything } - // check existing files whan all is collected + // check existing files when all is collected if ( selection.uploads.length >= selection.filesToUpload ) { //remove our selection hack: @@ -358,11 +383,6 @@ $(document).ready(function() { OC.Upload.checkExistingFiles(selection, callbacks); - //TODO refactor away: - //show cancel button - if($('html.lte9').length === 0 && data.dataType !== 'iframe') { - $('#uploadprogresswrapper input.stop').show(); - } } @@ -389,13 +409,6 @@ $(document).ready(function() { */ start: function(e) { OC.Upload.logStatus('start', e, null); - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return true; - } - $('#uploadprogresswrapper input.stop').show(); - $('#uploadprogressbar').progressbar({value:0}); - $('#uploadprogressbar').fadeIn(); }, fail: function(e, data) { OC.Upload.logStatus('fail', e, data); @@ -414,32 +427,6 @@ $(document).ready(function() { } //var selection = OC.Upload.getSelection(data.originalFiles); //OC.Upload.deleteSelectionUpload(selection, data.files[0].name); - - //if user pressed cancel hide upload progress bar and cancel button - if (data.errorThrown === 'abort') { - $('#uploadprogresswrapper input.stop').fadeOut(); - $('#uploadprogressbar').fadeOut(); - } - }, - progress: function(e, data) { - OC.Upload.logStatus('progress', e, data); - // TODO: show nice progress bar in file row - }, - /** - * - * @param {type} e - * @param {type} data (only has loaded, total and lengthComputable) - * @returns {unresolved} - */ - progressall: function(e, data) { - OC.Upload.logStatus('progressall', e, data); - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return; - } - var progress = (data.loaded/data.total)*100; - //var progress = OC.Upload.progressBytes(); - $('#uploadprogressbar').progressbar('value', progress); }, /** * called for every successful upload @@ -460,33 +447,21 @@ $(document).ready(function() { //var selection = OC.Upload.getSelection(data.originalFiles); if(typeof result[0] !== 'undefined' - && result[0].status === 'success' + && result[0].status === 'existserror' ) { - //if (selection) { - // selection.loadedBytes+=data.loaded; - //} - //OC.Upload.nextUpload(); + //show "file already exists" dialog + var original = result[0]; + var replacement = data.files[0]; + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu); } else { - if (result[0].status === 'existserror') { - //show "file already exists" dialog - var original = result[0]; - var replacement = data.files[0]; - var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu); - } else { - OC.Upload.deleteSelectionUpload(selection, data.files[0].name); - data.textStatus = 'servererror'; - data.errorThrown = t('files', result.data.message); - var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - fu._trigger('fail', e, data); - } + OC.Upload.deleteSelectionUpload(selection, data.files[0].name); + data.textStatus = 'servererror'; + data.errorThrown = t('files', result.data.message); + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + fu._trigger('fail', e, data); } - //if user pressed cancel hide upload chrome - //if (! OC.Upload.isProcessing()) { - // $('#uploadprogresswrapper input.stop').fadeOut(); - // $('#uploadprogressbar').fadeOut(); - //} }, /** @@ -496,36 +471,78 @@ $(document).ready(function() { */ stop: function(e, data) { OC.Upload.logStatus('stop', e, data); - //if(OC.Upload.progressBytes()>=100) { //only hide controls when all selections have ended uploading - - //OC.Upload.cancelUploads(); //cleanup - - // if(data.dataType !== 'iframe') { - // $('#uploadprogresswrapper input.stop').hide(); - // } - - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return; - } - - // $('#uploadprogressbar').progressbar('value', 100); - // $('#uploadprogressbar').fadeOut(); - //} - //if user pressed cancel hide upload chrome - //if (! OC.Upload.isProcessing()) { - // $('#uploadprogresswrapper input.stop').fadeOut(); - // $('#uploadprogressbar').fadeOut(); - //} } }; - - var file_upload_handler = function() { - $('#file_upload_start').fileupload(file_upload_param); - }; if ( document.getElementById('data-upload-form') ) { - $(file_upload_handler); + // initialize jquery fileupload (blueimp) + var fileupload = $('#file_upload_start').fileupload(file_upload_param); + + if(supportAjaxUploadWithProgress()) { + + // add progress handlers + fileupload.on('fileuploadadd', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadadd', e, data); + //show cancel button + //if(data.dataType !== 'iframe') { //FIXME when is iframe used? only for ie? + // $('#uploadprogresswrapper input.stop').show(); + //} + }); + // add progress handlers + fileupload.on('fileuploadstart', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadstart', e, data); + $('#uploadprogresswrapper input.stop').show(); + $('#uploadprogressbar').progressbar({value:0}); + $('#uploadprogressbar').fadeIn(); + }); + fileupload.on('fileuploadprogress', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadprogress', e, data); + //TODO progressbar in row + }); + fileupload.on('fileuploadprogressall', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadprogressall', e, data); + var progress = (data.loaded / data.total) * 100; + $('#uploadprogressbar').progressbar('value', progress); + }); + fileupload.on('fileuploaddone', function(e, data) { + OC.Upload.logStatus('progress handle fileuploaddone', e, data); + //if user pressed cancel hide upload chrome + //if (! OC.Upload.isProcessing()) { + // $('#uploadprogresswrapper input.stop').fadeOut(); + // $('#uploadprogressbar').fadeOut(); + //} + }); + fileupload.on('fileuploadstop', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadstop', e, data); + //if(OC.Upload.progressBytes()>=100) { //only hide controls when all selections have ended uploading + + //OC.Upload.cancelUploads(); //cleanup + + // if(data.dataType !== 'iframe') { + // $('#uploadprogresswrapper input.stop').hide(); + // } + + // $('#uploadprogressbar').progressbar('value', 100); + // $('#uploadprogressbar').fadeOut(); + //} + //if user pressed cancel hide upload chrome + //if (! OC.Upload.isProcessing()) { + // $('#uploadprogresswrapper input.stop').fadeOut(); + // $('#uploadprogressbar').fadeOut(); + //} + }); + fileupload.on('fileuploadfail', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadfail', e, data); + //if user pressed cancel hide upload progress bar and cancel button + if (data.errorThrown === 'abort') { + $('#uploadprogresswrapper input.stop').fadeOut(); + $('#uploadprogressbar').fadeOut(); + } + }); + + } else { + console.log('skipping file progress because your browser is broken'); + } } $.assocArraySize = function(obj) { // http://stackoverflow.com/a/6700/11236 diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 31e2a8300e2..4f20d1940aa 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -517,7 +517,7 @@ $(document).ready(function(){ var file_upload_start = $('#file_upload_start'); file_upload_start.on('fileuploaddrop', function(e, data) { - OC.Upload.logStatus('fileuploaddrop', e, data); + OC.Upload.logStatus('filelist handle fileuploaddrop', e, data); var dropTarget = $(e.originalEvent.target).closest('tr'); if(dropTarget && dropTarget.data('type') === 'dir') { // drag&drop upload to folder @@ -556,7 +556,7 @@ $(document).ready(function(){ }); file_upload_start.on('fileuploadadd', function(e, data) { - OC.Upload.logStatus('fileuploadadd', e, data); + OC.Upload.logStatus('filelist handle fileuploadadd', e, data); // lookup selection for dir var selection = OC.Upload.getSelection(data.originalFiles); @@ -592,10 +592,10 @@ $(document).ready(function(){ }); file_upload_start.on('fileuploadstart', function(e, data) { - OC.Upload.logStatus('fileuploadstart', e, data); + OC.Upload.logStatus('filelist handle fileuploadstart', e, data); }); file_upload_start.on('fileuploaddone', function(e, data) { - OC.Upload.logStatus('fileuploaddone', e, data); + OC.Upload.logStatus('filelist handle fileuploaddone', e, data); var response; if (typeof data.result === 'string') { @@ -672,22 +672,22 @@ $(document).ready(function(){ }); file_upload_start.on('fileuploadalways', function(e, data) { - OC.Upload.logStatus('fileuploadalways', e, data); + OC.Upload.logStatus('filelist handle fileuploadalways', e, data); }); file_upload_start.on('fileuploadsend', function(e, data) { - OC.Upload.logStatus('fileuploadsend', e, data); + OC.Upload.logStatus('filelist handle fileuploadsend', e, data); // TODOD add vis //data.context.element = }); file_upload_start.on('fileuploadprogress', function(e, data) { - OC.Upload.logStatus('fileuploadprogress', e, data); + OC.Upload.logStatus('filelist handle fileuploadprogress', e, data); }); file_upload_start.on('fileuploadprogressall', function(e, data) { - OC.Upload.logStatus('fileuploadprogressall', e, data); + OC.Upload.logStatus('filelist handle fileuploadprogressall', e, data); }); file_upload_start.on('fileuploadstop', function(e, data) { - OC.Upload.logStatus('fileuploadstop', e, data); + OC.Upload.logStatus('filelist handle fileuploadstop', e, data); //if user pressed cancel hide upload chrome if (! OC.Upload.isProcessing()) { @@ -700,7 +700,7 @@ $(document).ready(function(){ } }); file_upload_start.on('fileuploadfail', function(e, data) { - OC.Upload.logStatus('fileuploadfail', e, data); + OC.Upload.logStatus('filelist handle fileuploadfail', e, data); //if user pressed cancel hide upload chrome if (data.errorThrown === 'abort') { diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 9a725fc2077..4a6c9c78900 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -1,6 +1,6 @@ Files={ updateMaxUploadFilesize:function(response) { - if(response == undefined) { + if(response === undefined) { return; } if(response.data !== undefined && response.data.uploadMaxFilesize !== undefined) { @@ -9,7 +9,7 @@ Files={ $('#usedSpacePercent').val(response.data.usedSpacePercent); Files.displayStorageWarnings(); } - if(response[0] == undefined) { + if(response[0] === undefined) { return; } if(response[0].uploadMaxFilesize !== undefined) { @@ -25,7 +25,7 @@ Files={ OC.Notification.show(t('files', '\'.\' is an invalid file name.')); return false; } - if (name.length == 0) { + if (name.length === 0) { OC.Notification.show(t('files', 'File name cannot be empty.')); return false; } @@ -33,7 +33,7 @@ Files={ // check for invalid characters var invalid_characters = ['\\', '/', '<', '>', ':', '"', '|', '?', '*']; for (var i = 0; i < invalid_characters.length; i++) { - if (name.indexOf(invalid_characters[i]) != -1) { + if (name.indexOf(invalid_characters[i]) !== -1) { OC.Notification.show(t('files', "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed.")); return false; } @@ -127,7 +127,7 @@ $(document).ready(function() { var rows = $(this).parent().parent().parent().children('tr'); for (var i = start; i < end; i++) { $(rows).each(function(index) { - if (index == i) { + if (index === i) { var checkbox = $(this).children().children('input:checkbox'); $(checkbox).attr('checked', 'checked'); $(checkbox).parent().parent().addClass('selected'); @@ -145,7 +145,7 @@ $(document).ready(function() { $(checkbox).attr('checked', 'checked'); $(checkbox).parent().parent().toggleClass('selected'); var selectedCount=$('td.filename input:checkbox:checked').length; - if (selectedCount == $('td.filename input:checkbox').length) { + if (selectedCount === $('td.filename input:checkbox').length) { $('#select_all').attr('checked', 'checked'); } } @@ -192,7 +192,7 @@ $(document).ready(function() { var rows = $(this).parent().parent().parent().children('tr'); for (var i = start; i < end; i++) { $(rows).each(function(index) { - if (index == i) { + if (index === i) { var checkbox = $(this).children().children('input:checkbox'); $(checkbox).attr('checked', 'checked'); $(checkbox).parent().parent().addClass('selected'); @@ -205,7 +205,7 @@ $(document).ready(function() { if(!$(this).attr('checked')){ $('#select_all').attr('checked',false); }else{ - if(selectedCount==$('td.filename input:checkbox').length){ + if(selectedCount === $('td.filename input:checkbox').length){ $('#select_all').attr('checked',true); } } @@ -262,9 +262,9 @@ $(document).ready(function() { function resizeBreadcrumbs(firstRun) { var width = $(this).width(); - if (width != lastWidth) { + if (width !== lastWidth) { if ((width < lastWidth || firstRun) && width < breadcrumbsWidth) { - if (hiddenBreadcrumbs == 0) { + if (hiddenBreadcrumbs === 0) { breadcrumbsWidth -= $(breadcrumbs[1]).get(0).offsetWidth; $(breadcrumbs[1]).find('a').hide(); $(breadcrumbs[1]).append('...'); @@ -276,12 +276,12 @@ $(document).ready(function() { breadcrumbsWidth -= $(breadcrumbs[i]).get(0).offsetWidth; $(breadcrumbs[i]).hide(); hiddenBreadcrumbs = i; - i++ + i++; } } else if (width > lastWidth && hiddenBreadcrumbs > 0) { var i = hiddenBreadcrumbs; while (width > breadcrumbsWidth && i > 0) { - if (hiddenBreadcrumbs == 1) { + if (hiddenBreadcrumbs === 1) { breadcrumbsWidth -= $(breadcrumbs[1]).get(0).offsetWidth; $(breadcrumbs[1]).find('span').remove(); $(breadcrumbs[1]).find('a').show(); @@ -382,7 +382,7 @@ scanFiles.scanning=false; function boolOperationFinished(data, callback) { result = jQuery.parseJSON(data.responseText); Files.updateMaxUploadFilesize(result); - if(result.status == 'success'){ + if(result.status === 'success'){ callback.call(); } else { alert(result.data.message); @@ -436,7 +436,7 @@ var createDragShadow = function(event){ }); return dragshadow; -} +}; //options for file drag/drop var dragOptions={ @@ -446,7 +446,7 @@ var dragOptions={ stop: function(event, ui) { $('#fileList tr td.filename').addClass('ui-draggable'); } -} +}; // sane browsers support using the distance option if ( $('html.ie').length === 0) { dragOptions['distance'] = 20; @@ -489,7 +489,7 @@ var folderDropOptions={ }); }, tolerance: 'pointer' -} +}; var crumbDropOptions={ drop: function( event, ui ) { diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index 5ed24417264..08afbfd42f2 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -257,13 +257,7 @@ var OCdialogs = { //TODO add to same size collection? } - //add checkbox toggling actions - conflict.find('.replacement,.original').on('click', function(){ - var checkbox = $(this).find('input[type="checkbox"]'); - checkbox.prop('checkbox', !checkbox.prop('checkbox')); - }).find('input[type="checkbox"]').prop('checkbox',false); - - //TODO show skip action for files with same size and mtime + //TODO show skip action for files with same size and mtime in bottom row }; var selection = controller.getSelection(data.originalFiles); @@ -345,11 +339,14 @@ var OCdialogs = { var checkboxes = $(dialog_id).find('.conflict:not(.template) .original input[type="checkbox"]'); checkboxes.prop('checked', $(this).prop('checked')); }); - $(dialog_id).find('.conflicts').on('click', '.replacement,.original', function() { var checkbox = $(this).find('input[type="checkbox"]'); checkbox.prop('checked', !checkbox.prop('checked')); }); + $(dialog_id).find('.conflicts').on('click', 'input[type="checkbox"]', function() { + var checkbox = $(this); + checkbox.prop('checked', !checkbox.prop('checked')); + }); //update counters $(dialog_id).on('click', '.replacement,.allnewfiles', function() { -- cgit v1.2.3 From 8a7e26b268b8f4be32bb0b54527a83cadbfc28fd Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Thu, 5 Sep 2013 17:46:19 +0200 Subject: cleanup dead code --- apps/files/js/file-upload.js | 68 +++++++++++++++++++++++++------------------- core/js/oc-dialogs.js | 7 +++-- 2 files changed, 42 insertions(+), 33 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index efd3c0d59ed..9af09fcdd92 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -157,25 +157,6 @@ OC.Upload = { } return this._selections[originalFiles.selectionKey]; }, - deleteSelection:function(selectionKey) { - if (this._selections[selectionKey]) { - jQuery.each(this._selections[selectionKey].uploads, function(i, upload) { - upload.abort(); - }); - delete this._selections[selectionKey]; - } else { - console.log('OC.Upload: selection ' + selectionKey + ' does not exist'); - } - }, - deleteSelectionUpload:function(selection, filename) { - if(selection.uploads[filename]) { - selection.uploads[filename].abort(); - return true; - } else { - console.log('OC.Upload: selection ' + selection.selectionKey + ' does not contain upload for ' + filename); - } - return false; - }, cancelUpload:function(dir, filename) { var self = this; var deleted = false; @@ -244,24 +225,48 @@ OC.Upload = { }); return total; }, - onCancel:function(data){ + onCancel:function(data) { //TODO cancel all uploads of this selection var selection = this.getSelection(data.originalFiles); OC.Upload.deleteSelection(selection.selectionKey); //FIXME hide progressbar }, + onContinue:function(conflicts) { + var self = this; + //iterate over all conflicts + jQuery.each(conflicts, function (i, conflict) { + conflict = $(conflict); + var keepOriginal = conflict.find('.original input[type="checkbox"]:checked').length === 1; + var keepReplacement = conflict.find('.replacement input[type="checkbox"]:checked').length === 1; + if (keepOriginal && keepReplacement) { + // when both selected -> autorename + self.onAutorename(conflict.data('data')); + } else if (keepReplacement) { + // when only replacement selected -> overwrite + self.onReplace(conflict.data('data')); + } else { + // when only original seleted -> skip + // when none selected -> skip + self.onSkip(conflict.data('data')); + } + }); + }, onSkip:function(data){ - var selection = this.getSelection(data.originalFiles); - selection.loadedBytes += data.loaded; - this.nextUpload(); + OC.Upload.logStatus('skip', null, data); + //var selection = this.getSelection(data.originalFiles); + //selection.loadedBytes += data.loaded; + //this.nextUpload(); + //TODO trigger skip? what about progress? }, onReplace:function(data){ + OC.Upload.logStatus('replace', null, data); data.data.append('replace', true); data.submit(); }, - onRename:function(data, newName){ - data.data.append('newname', newName); + onAutorename:function(data){ + OC.Upload.logStatus('autorename', null, data); + data.data.append('autorename', true); data.submit(); }, logStatus:function(caption, e, data) { @@ -446,16 +451,19 @@ $(document).ready(function() { var result=$.parseJSON(response); //var selection = OC.Upload.getSelection(data.originalFiles); - if(typeof result[0] !== 'undefined' - && result[0].status === 'existserror' - ) { + if(typeof result[0] === 'undefined') { + data.textStatus = 'servererror'; + data.errorThrown = t('files', 'Could not get result from server.'); + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + fu._trigger('fail', e, data); + } else if (result[0].status === 'existserror') { //show "file already exists" dialog var original = result[0]; var replacement = data.files[0]; var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu); - } else { - OC.Upload.deleteSelectionUpload(selection, data.files[0].name); + } else if (result[0].status !== 'success') { + delete data.jqXHR; data.textStatus = 'servererror'; data.errorThrown = t('files', result.data.message); var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index 08afbfd42f2..77af1a2dde5 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -224,6 +224,8 @@ var OCdialogs = { var conflict = conflicts.find('.conflict.template').clone(); + conflict.data('data',data); + conflict.find('.filename').text(original.name); conflict.find('.original .size').text(humanFileSize(original.size)); conflict.find('.original .mtime').text(formatDate(original.mtime*1000)); @@ -312,9 +314,8 @@ var OCdialogs = { classes: 'continue', click: function(){ self._fileexistsshown = false; - if ( typeof controller.onRename !== 'undefined') { - //TODO use autorename when repeat is checked - controller.onRename(data, $(dialog_id + ' #newname').val()); + if ( typeof controller.onContinue !== 'undefined') { + controller.onContinue($(dialog_id + ' .conflict:not(.template)')); } $(dialog_id).ocdialog('close'); } -- cgit v1.2.3 From 238d92b11cab31061fb5766b9f75d4772d48283e Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Fri, 6 Sep 2013 17:53:58 +0200 Subject: refactor replace and autorename resolution in upload.php --- apps/files/ajax/upload.php | 72 ++++++++++++++++++++------------------------ apps/files/js/file-upload.js | 4 +-- 2 files changed, 35 insertions(+), 41 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index ec313030ed1..60c2454b290 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -78,7 +78,7 @@ foreach ($_FILES['files']['error'] as $error) { } $files = $_FILES['files']; -$error = ''; +$error = false; $maxUploadFileSize = $storageStats['uploadMaxFilesize']; $maxHumanFileSize = OCP\Util::humanFileSize($maxUploadFileSize); @@ -99,57 +99,51 @@ if (strpos($dir, '..') === false) { $fileCount = count($files['name']); for ($i = 0; $i < $fileCount; $i++) { // $path needs to be normalized - this failed within drag'n'drop upload to a sub-folder - if (isset($_POST['newname'])) { - $newName = $_POST['newname']; + if (isset($_POST['resolution']) && $_POST['resolution']==='autorename') { + // append a number in brackets like 'filename (2).ext' + $target = OCP\Files::buildNotExistingFileName(stripslashes($dir), $files['name'][$i]); } else { - $newName = $files['name'][$i]; + $target = \OC\Files\Filesystem::normalizePath(stripslashes($dir).'/'.$files['name'][$i]); } - if (isset($_POST['replace']) && $_POST['replace'] == true) { - $replace = true; + + if ( ! \OC\Files\Filesystem::file_exists($target) + || (isset($_POST['resolution']) && $_POST['resolution']==='replace') + ) { + // upload and overwrite file + if (is_uploaded_file($files['tmp_name'][$i]) and \OC\Files\Filesystem::fromTmpFile($files['tmp_name'][$i], $target)) { + $status = 'success'; + + // updated max file size after upload + $storageStats = \OCA\files\lib\Helper::buildFileStorageStatistics($dir); + + } else { + $error = $l->t('Upload failed. Could not find uploaded file'); + } + } else { - $replace = false; + // file already exists + $status = 'existserror'; } - $target = \OC\Files\Filesystem::normalizePath(stripslashes($dir).'/'.$newName); - if ( ! $replace && \OC\Files\Filesystem::file_exists($target)) { - $meta = \OC\Files\Filesystem::getFileInfo($target); - $result[] = array('status' => 'existserror', - 'type' => $meta['mimetype'], + } + if ($error === false) { + $meta = \OC\Files\Filesystem::getFileInfo($target); + if ($meta === false) { + $error = $l->t('Upload failed. Could not get file info.'); + } else { + $result[] = array('status' => $status, + 'mime' => $meta['mimetype'], 'mtime' => $meta['mtime'], 'size' => $meta['size'], 'id' => $meta['fileid'], 'name' => basename($target), - 'originalname' => $newName, + 'originalname' => $files['tmp_name'][$i], 'uploadMaxFilesize' => $maxUploadFileSize, 'maxHumanFilesize' => $maxHumanFileSize ); - } else { - //$target = OCP\Files::buildNotExistingFileName(stripslashes($dir), $files['name'][$i]); - if (is_uploaded_file($files['tmp_name'][$i]) and \OC\Files\Filesystem::fromTmpFile($files['tmp_name'][$i], $target)) { - $meta = \OC\Files\Filesystem::getFileInfo($target); - - // updated max file size after upload - $storageStats = \OCA\files\lib\Helper::buildFileStorageStatistics($dir); - - if ($meta === false) { - OCP\JSON::error(array('data' => array_merge(array('message' => $l->t('Upload failed')), $storageStats))); - exit(); - } else { - $result[] = array('status' => 'success', - 'mime' => $meta['mimetype'], - 'mtime' => $meta['mtime'], - 'size' => $meta['size'], - 'id' => $meta['fileid'], - 'name' => basename($target), - 'originalname' => $newName, - 'uploadMaxFilesize' => $maxUploadFileSize, - 'maxHumanFilesize' => $maxHumanFileSize - ); - } - } + OCP\JSON::encodedPrint($result); + exit(); } } - OCP\JSON::encodedPrint($result); - exit(); } else { $error = $l->t('Invalid directory.'); } diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 9af09fcdd92..3fcda22e53e 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -261,12 +261,12 @@ OC.Upload = { }, onReplace:function(data){ OC.Upload.logStatus('replace', null, data); - data.data.append('replace', true); + data.data.append('resolution', 'replace'); data.submit(); }, onAutorename:function(data){ OC.Upload.logStatus('autorename', null, data); - data.data.append('autorename', true); + data.data.append('resolution', 'autorename'); data.submit(); }, logStatus:function(caption, e, data) { -- cgit v1.2.3 From e2c0fe829698de9f89bf2cc3f854942f44bffc69 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Fri, 6 Sep 2013 18:16:40 +0200 Subject: fix upload of multiple files --- apps/files/ajax/upload.php | 61 +++++++++++++++++++++++++++----------------- apps/files/js/file-upload.js | 4 +-- 2 files changed, 40 insertions(+), 25 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index 60c2454b290..12724c0c5bc 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -111,41 +111,56 @@ if (strpos($dir, '..') === false) { ) { // upload and overwrite file if (is_uploaded_file($files['tmp_name'][$i]) and \OC\Files\Filesystem::fromTmpFile($files['tmp_name'][$i], $target)) { - $status = 'success'; - + // updated max file size after upload $storageStats = \OCA\files\lib\Helper::buildFileStorageStatistics($dir); + $meta = \OC\Files\Filesystem::getFileInfo($target); + if ($meta === false) { + $error = $l->t('Upload failed. Could not get file info.'); + } else { + $result[] = array('status' => 'success', + 'mime' => $meta['mimetype'], + 'mtime' => $meta['mtime'], + 'size' => $meta['size'], + 'id' => $meta['fileid'], + 'name' => basename($target), + 'originalname' => $files['tmp_name'][$i], + 'uploadMaxFilesize' => $maxUploadFileSize, + 'maxHumanFilesize' => $maxHumanFileSize + ); + } + } else { $error = $l->t('Upload failed. Could not find uploaded file'); } } else { // file already exists - $status = 'existserror'; - } - } - if ($error === false) { - $meta = \OC\Files\Filesystem::getFileInfo($target); - if ($meta === false) { - $error = $l->t('Upload failed. Could not get file info.'); - } else { - $result[] = array('status' => $status, - 'mime' => $meta['mimetype'], - 'mtime' => $meta['mtime'], - 'size' => $meta['size'], - 'id' => $meta['fileid'], - 'name' => basename($target), - 'originalname' => $files['tmp_name'][$i], - 'uploadMaxFilesize' => $maxUploadFileSize, - 'maxHumanFilesize' => $maxHumanFileSize - ); - OCP\JSON::encodedPrint($result); - exit(); + $meta = \OC\Files\Filesystem::getFileInfo($target); + if ($meta === false) { + $error = $l->t('Upload failed. Could not get file info.'); + } else { + $result[] = array('status' => 'existserror', + 'mime' => $meta['mimetype'], + 'mtime' => $meta['mtime'], + 'size' => $meta['size'], + 'id' => $meta['fileid'], + 'name' => basename($target), + 'originalname' => $files['tmp_name'][$i], + 'uploadMaxFilesize' => $maxUploadFileSize, + 'maxHumanFilesize' => $maxHumanFileSize + ); + } } } } else { $error = $l->t('Invalid directory.'); } -OCP\JSON::error(array('data' => array_merge(array('message' => $error), $storageStats))); +if ($error === false) { + OCP\JSON::encodedPrint($result); + exit(); +} else { + OCP\JSON::error(array('data' => array_merge(array('message' => $error), $storageStats))); +} diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 3fcda22e53e..4f93403baf5 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -536,8 +536,8 @@ $(document).ready(function() { //} //if user pressed cancel hide upload chrome //if (! OC.Upload.isProcessing()) { - // $('#uploadprogresswrapper input.stop').fadeOut(); - // $('#uploadprogressbar').fadeOut(); + $('#uploadprogresswrapper input.stop').fadeOut(); + $('#uploadprogressbar').fadeOut(); //} }); fileupload.on('fileuploadfail', function(e, data) { -- cgit v1.2.3 From 673e0c01a79927359319ff15411a33f460d85ac5 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Fri, 6 Sep 2013 22:40:10 +0200 Subject: fix page leaving checks --- apps/files/js/file-upload.js | 130 +++++++++++-------------------------------- apps/files/js/filelist.js | 21 ++++--- core/js/oc-dialogs.js | 11 ++-- 3 files changed, 52 insertions(+), 110 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 4f93403baf5..47d1188b511 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -124,43 +124,12 @@ function supportAjaxUploadWithProgress() { //TODO clean uploads when all progress has completed OC.Upload = { - /** - * map to lookup the selections for a given directory. - * @type Array - */ - _selections: {}, - _selectionCount: 0, - /* - * queue which progress tracker to use for the next upload - * @type Array - */ - _queue: [], - queueUpload:function(data) { - // add to queue - this._queue.push(data); //remember what to upload next - if ( ! this.isProcessing() ) { - this.startUpload(); - } - }, - getSelection:function(originalFiles) { - if (!originalFiles.selectionKey) { - originalFiles.selectionKey = 'selection-' + this._selectionCount++; - this._selections[originalFiles.selectionKey] = { - selectionKey:originalFiles.selectionKey, - files:{}, - totalBytes:0, - loadedBytes:0, - currentFile:0, - uploads:{}, - checked:false - }; - } - return this._selections[originalFiles.selectionKey]; - }, + _uploads: [], cancelUpload:function(dir, filename) { var self = this; var deleted = false; - jQuery.each(this._selections, function(i, selection) { + //FIXME _selections + jQuery.each(this._uploads, function(i, jqXHR) { if (selection.dir === dir && selection.uploads[filename]) { deleted = self.deleteSelectionUpload(selection, filename); return false; // end searching through selections @@ -168,69 +137,34 @@ OC.Upload = { }); return deleted; }, + deleteUpload:function(data) { + delete data.jqXHR; + }, cancelUploads:function() { console.log('canceling uploads'); - var self = this; - jQuery.each(this._selections,function(i, selection){ - self.deleteSelection(selection.selectionKey); + jQuery.each(this._uploads,function(i, jqXHR){ + jqXHR.abort(); }); - this._queue = []; - this._isProcessing = false; - }, - _isProcessing:false, - isProcessing:function(){ - return this._isProcessing; + this._uploads = []; + }, - startUpload:function(){ - if (this._queue.length > 0) { - this._isProcessing = true; - this.nextUpload(); - return true; - } else { - return false; + rememberUpload:function(jqXHR){ + if (jqXHR) { + this._uploads.push(jqXHR); } }, - nextUpload:function(){ - if (this._queue.length > 0) { - var data = this._queue.pop(); - var selection = this.getSelection(data.originalFiles); - selection.uploads[data.files[0]] = data.submit(); - - } else { - //queue is empty, we are done - this._isProcessing = false; - OC.Upload.cancelUploads(); - } - }, - progressBytes: function() { - var total = 0; - var loaded = 0; - jQuery.each(this._selections, function (i, selection) { - total += selection.totalBytes; - loaded += selection.loadedBytes; - }); - return (loaded/total)*100; - }, - loadedBytes: function() { - var loaded = 0; - jQuery.each(this._selections, function (i, selection) { - loaded += selection.loadedBytes; - }); - return loaded; - }, - totalBytes: function() { - var total = 0; - jQuery.each(this._selections, function (i, selection) { - total += selection.totalBytes; + isProcessing:function(){ + var count = 0; + + jQuery.each(this._uploads,function(i, data){ + if (data.state() === 'pending') { + count++; + } }); - return total; + return count > 0; }, onCancel:function(data) { - //TODO cancel all uploads of this selection - - var selection = this.getSelection(data.originalFiles); - OC.Upload.deleteSelection(selection.selectionKey); - //FIXME hide progressbar + this.cancelUploads(); }, onContinue:function(conflicts) { var self = this; @@ -253,19 +187,16 @@ OC.Upload = { }); }, onSkip:function(data){ - OC.Upload.logStatus('skip', null, data); - //var selection = this.getSelection(data.originalFiles); - //selection.loadedBytes += data.loaded; - //this.nextUpload(); - //TODO trigger skip? what about progress? + this.logStatus('skip', null, data); + this.deleteUpload(data); }, onReplace:function(data){ - OC.Upload.logStatus('replace', null, data); + this.logStatus('replace', null, data); data.data.append('resolution', 'replace'); data.submit(); }, onAutorename:function(data){ - OC.Upload.logStatus('autorename', null, data); + this.logStatus('autorename', null, data); data.data.append('resolution', 'autorename'); data.submit(); }, @@ -415,6 +346,9 @@ $(document).ready(function() { start: function(e) { OC.Upload.logStatus('start', e, null); }, + submit: function (e, data) { + OC.Upload.rememberUpload(data); + }, fail: function(e, data) { OC.Upload.logStatus('fail', e, data); if (typeof data.textStatus !== 'undefined' && data.textStatus !== 'success' ) { @@ -432,6 +366,7 @@ $(document).ready(function() { } //var selection = OC.Upload.getSelection(data.originalFiles); //OC.Upload.deleteSelectionUpload(selection, data.files[0].name); + OC.Upload.deleteUpload(data); }, /** * called for every successful upload @@ -449,8 +384,9 @@ $(document).ready(function() { response = data.result[0].body.innerText; } var result=$.parseJSON(response); - //var selection = OC.Upload.getSelection(data.originalFiles); + delete data.jqXHR; + if(typeof result[0] === 'undefined') { data.textStatus = 'servererror'; data.errorThrown = t('files', 'Could not get result from server.'); @@ -463,7 +399,7 @@ $(document).ready(function() { var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu); } else if (result[0].status !== 'success') { - delete data.jqXHR; + //delete data.jqXHR; data.textStatus = 'servererror'; data.errorThrown = t('files', result.data.message); var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 1bb9672f961..a96f555ac0b 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -522,6 +522,9 @@ $(document).ready(function(){ var dropTarget = $(e.originalEvent.target).closest('tr'); if(dropTarget && dropTarget.data('type') === 'dir') { // drag&drop upload to folder + // remember as context + data.context = dropTarget; + var dir = dropTarget.data('file'); // update folder in form @@ -546,19 +549,15 @@ $(document).ready(function(){ OC.Upload.logStatus('filelist handle fileuploadadd', e, data); // lookup selection for dir - var selection = OC.Upload.getSelection(data.originalFiles); + //var selection = OC.Upload.getSelection(data.originalFiles); if(FileList.deleteFiles && FileList.deleteFiles.indexOf(data.files[0].name)!==-1){//finish delete if we are uploading a deleted file FileList.finishDelete(null, true); //delete file before continuing } // add ui visualization to existing folder - if(selection.dropTarget && selection.dropTarget.data('type') === 'dir') { + if(data.context && data.context.data('type') === 'dir') { // add to existing folder - var dirName = selection.dropTarget.data('file'); - - // set dir context - data.context = $('tr').filterAttr('data-type', 'dir').filterAttr('data-file', dirName); // update upload counter ui var uploadtext = data.context.find('.uploadtext'); @@ -578,6 +577,10 @@ $(document).ready(function(){ } }); + file_upload_start.on('fileuploadsend', function(e, data) { + OC.Upload.logStatus('filelist handle fileuploadsend', e, data); + return true; + }); file_upload_start.on('fileuploadstart', function(e, data) { OC.Upload.logStatus('filelist handle fileuploadstart', e, data); }); @@ -608,7 +611,7 @@ $(document).ready(function(){ var img = OC.imagePath('core', 'filetypes/folder.png'); data.context.find('td.filename').attr('style','background-image:url('+img+')'); uploadtext.text(translatedText); - uploadtext.show(); + uploadtext.hide(); } else { uploadtext.text(translatedText); } @@ -648,6 +651,7 @@ $(document).ready(function(){ } //if user pressed cancel hide upload chrome + /* if (! OC.Upload.isProcessing()) { //cleanup uploading to a dir var uploadtext = $('tr .uploadtext'); @@ -656,6 +660,7 @@ $(document).ready(function(){ uploadtext.fadeOut(); uploadtext.attr('currentUploads', 0); } + */ }); file_upload_start.on('fileuploadalways', function(e, data) { @@ -677,7 +682,7 @@ $(document).ready(function(){ OC.Upload.logStatus('filelist handle fileuploadstop', e, data); //if user pressed cancel hide upload chrome - if (! OC.Upload.isProcessing()) { + if (data.errorThrown === 'abort') { //cleanup uploading to a dir var uploadtext = $('tr .uploadtext'); var img = OC.imagePath('core', 'filetypes/folder.png'); diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index fd77f5998b5..bc460798350 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -262,10 +262,10 @@ var OCdialogs = { //TODO show skip action for files with same size and mtime in bottom row }; - var selection = controller.getSelection(data.originalFiles); - if (selection.defaultAction) { - controller[selection.defaultAction](data); - } else { + //var selection = controller.getSelection(data.originalFiles); + //if (selection.defaultAction) { + // controller[selection.defaultAction](data); + //} else { var dialog_name = 'oc-dialog-fileexists-content'; var dialog_id = '#' + dialog_name; if (this._fileexistsshown) { @@ -306,6 +306,7 @@ var OCdialogs = { if ( typeof controller.onCancel !== 'undefined') { controller.onCancel(data); } + $(dialog_id).ocdialog('close'); $(dialog_id).ocdialog('destroy').remove(); } }, @@ -382,7 +383,7 @@ var OCdialogs = { alert(t('core', 'Error loading file exists template')); }); } - } + //} }, _getFilePickerTemplate: function() { var defer = $.Deferred(); -- cgit v1.2.3 From ce035016460d8285d5511e67b389d494eb78c1ce Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Fri, 6 Sep 2013 23:44:40 +0200 Subject: fine ie8 compatability --- apps/files/css/files.css | 4 ++++ apps/files/js/file-upload.js | 6 +++++- core/js/oc-dialogs.js | 16 ++++++++++------ 3 files changed, 19 insertions(+), 7 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 65aa29052e6..ea9c99bb269 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -380,6 +380,10 @@ table.dragshadow td.size { float: left; width: 235px; } +html.lte9 .oc-dialog .fileexists .original { + float: left; + width: 225px; +} .oc-dialog .fileexists .conflicts { overflow-y:scroll; max-height: 225px; diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 47d1188b511..ead397c569e 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -197,7 +197,11 @@ OC.Upload = { }, onAutorename:function(data){ this.logStatus('autorename', null, data); - data.data.append('resolution', 'autorename'); + if (data.data) { + data.data.append('resolution', 'autorename'); + } else { + data.formData.push({name:'resolution',value:'autorename'}); //hack for ie8 + } data.submit(); }, logStatus:function(caption, e, data) { diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index bc460798350..82bf49fc3a7 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -229,8 +229,11 @@ var OCdialogs = { conflict.find('.filename').text(original.name); conflict.find('.original .size').text(humanFileSize(original.size)); conflict.find('.original .mtime').text(formatDate(original.mtime*1000)); - conflict.find('.replacement .size').text(humanFileSize(replacement.size)); - conflict.find('.replacement .mtime').text(formatDate(replacement.lastModifiedDate)); + // ie sucks + if (replacement.size && replacement.lastModifiedDate) { + conflict.find('.replacement .size').text(humanFileSize(replacement.size)); + conflict.find('.replacement .mtime').text(formatDate(replacement.lastModifiedDate)); + } var path = getPathForPreview(original.name); lazyLoadPreview(path, original.type, function(previewpath){ conflict.find('.original .icon').css('background-image','url('+previewpath+')'); @@ -242,18 +245,19 @@ var OCdialogs = { conflicts.append(conflict); //set more recent mtime bold - if (replacement.lastModifiedDate.getTime() > original.mtime*1000) { + // ie sucks + if (replacement.lastModifiedDate && replacement.lastModifiedDate.getTime() > original.mtime*1000) { conflict.find('.replacement .mtime').css('font-weight', 'bold'); - } else if (replacement.lastModifiedDate.getTime() < original.mtime*1000) { + } else if (replacement.lastModifiedDate && replacement.lastModifiedDate.getTime() < original.mtime*1000) { conflict.find('.original .mtime').css('font-weight', 'bold'); } else { //TODO add to same mtime collection? } // set bigger size bold - if (replacement.size > original.size) { + if (replacement.size && replacement.size > original.size) { conflict.find('.replacement .size').css('font-weight', 'bold'); - } else if (replacement.size < original.size) { + } else if (replacement.size && replacement.size < original.size) { conflict.find('.original .size').css('font-weight', 'bold'); } else { //TODO add to same size collection? -- cgit v1.2.3 From 8828fafd362e3559a7448c846896203c563cc08a Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Sun, 8 Sep 2013 10:41:20 +0200 Subject: cleanup comments --- apps/files/js/file-upload.js | 205 ++++++++++++++----------------------------- apps/files/js/filelist.js | 73 ++------------- 2 files changed, 75 insertions(+), 203 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index ead397c569e..d3e644dbdaf 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -1,100 +1,11 @@ /** - * - * and yet another idea how to handle file uploads: - * let the jquery fileupload thing handle as much as possible - * - * use singlefileupload - * on first add of every selection - * - check all files of originalFiles array with files in dir - * - on conflict show dialog - * - skip all -> remember as default action - * - replace all -> remember as default action - * - choose -> show choose dialog - * - mark files to keep - * - when only existing -> remember as single skip action - * - when only new -> remember as single replace action - * - when both -> remember as single autorename action - * - continue -> apply marks, when nothing is marked continue == skip all - * - start uploading selection - * - * on send - * - if single action or default action - * - when skip -> abort upload - * ..- when replace -> add replace=true parameter - * ..- when rename -> add newName=filename parameter - * ..- when autorename -> add autorename=true parameter - * - * on fail - * - if server sent existserror - * - show dialog - * - on skip single -> abort single upload - * - on skip always -> remember as default action - * - on replace single -> replace single upload - * - on replace always -> remember as default action - * - on rename single -> rename single upload, propose autorename - when changed disable remember always checkbox - * - on rename always -> remember autorename as default action - * - resubmit data - * - * on uplad done - * - if last upload -> unset default action - * - * ------------------------------------------------------------- - * - * use put t ocacnel upload before it starts? use chunked uploads? - * - * 1. tracking which file to upload next -> upload queue with data elements added whenever add is called - * 2. tracking progress for each folder individually -> track progress in a progress[dirname] object - * - every new selection increases the total size and number of files for a directory - * - add increases, successful done decreases, skip decreases, cancel decreases - * 3. track selections -> the general skip / overwrite decision is selection based and can change - * - server might send already exists error -> show dialog & remember decision for selection again - * - server sends error, how do we find collection? - * 4. track jqXHR object to prevent browser from navigationg away -> track in a uploads[dirname][filename] object [x] - * - * selections can progress in parrallel but each selection progresses sequentially - * - * -> store everything in context? - * context.folder - * context.element? - * context.progressui? - * context.jqXHR - * context.selection - * context.selection.onExistsAction? - * - * context available in what events? - * build in drop() add dir - * latest in add() add file? add selection! - * progress? -> update progress? - * onsubmit -> context.jqXHR? - * fail() -> - * done() - * - * when versioning app is active -> always overwrite - * - * fileupload scenario: empty folder & d&d 20 files - * queue the 20 files - * check list of files for duplicates -> empty - * start uploading the queue (show progress dialog?) - * - no duplicates -> all good, add files to list - * - server reports duplicate -> show skip, replace or rename dialog (for individual files) - * - * fileupload scenario: files uploaded & d&d 20 files again - * queue the 20 files - * check list of files for duplicates -> find n duplicates -> - * show skip, replace or rename dialog as general option - * - show list of differences with preview (win 8) - * remember action for each file - * start uploading the queue (show progress dialog?) - * - no duplicates -> all good, add files to list - * - server reports duplicate -> use remembered action - * - * dialoge: - * -> skip, replace, choose (or abort) () - * -> choose left or right (with skip) (when only one file in list also show rename option and remember for all option) - * - * progress always based on filesize - * number of files as text, bytes as bar - * + * The file upload code uses several hooks to interact with blueimps jQuery file upload library: + * 1. the core upload handling hooks are added when initializing the plugin, + * 2. if the browser supports progress events they are added in a separate set after the initialization + * 3. every app can add it's own triggers for fileupload + * - files adds d'n'd handlers and also reacts to done events to add new rows to the filelist + * - TODO pictures upload button + * - TODO music upload button */ // from https://github.com/New-Bamboo/example-ajax-upload/blob/master/public/index.html @@ -122,9 +33,20 @@ function supportAjaxUploadWithProgress() { } } -//TODO clean uploads when all progress has completed +/** + * keeps track of uploads in progress and implements callbacks for the conflicts dialog + * @type OC.Upload + */ OC.Upload = { _uploads: [], + /** + * cancels a single upload, + * @deprecated because it was only used when a file currently beeing uploaded was deleted. Now they are added after + * they have been uploaded. + * @param string dir + * @param string filename + * @returns unresolved + */ cancelUpload:function(dir, filename) { var self = this; var deleted = false; @@ -137,22 +59,33 @@ OC.Upload = { }); return deleted; }, + /** + * deletes the jqHXR object from a data selection + * @param data data + */ deleteUpload:function(data) { delete data.jqXHR; }, + /** + * cancels all uploads + */ cancelUploads:function() { console.log('canceling uploads'); jQuery.each(this._uploads,function(i, jqXHR){ jqXHR.abort(); }); this._uploads = []; - }, rememberUpload:function(jqXHR){ if (jqXHR) { this._uploads.push(jqXHR); } }, + /** + * Checks the currently known uploads. + * returns true if any hxr has the state 'pending' + * @returns Boolean + */ isProcessing:function(){ var count = 0; @@ -163,9 +96,18 @@ OC.Upload = { }); return count > 0; }, + /** + * callback for the conflicts dialog + * @param data + */ onCancel:function(data) { this.cancelUploads(); }, + /** + * callback for the conflicts dialog + * calls onSkip, onReplace or onAutorename for each conflict + * @param conflicts list of conflict elements + */ onContinue:function(conflicts) { var self = this; //iterate over all conflicts @@ -186,15 +128,27 @@ OC.Upload = { } }); }, + /** + * handle skipping an upload + * @param data data + */ onSkip:function(data){ this.logStatus('skip', null, data); this.deleteUpload(data); }, + /** + * handle replacing a file on the server with an uploaded file + * @param data data + */ onReplace:function(data){ this.logStatus('replace', null, data); data.data.append('resolution', 'replace'); data.submit(); }, + /** + * handle uploading a file and letting the server decide a new name + * @param data data + */ onAutorename:function(data){ this.logStatus('autorename', null, data); if (data.data) { @@ -208,8 +162,19 @@ OC.Upload = { console.log(caption); console.log(data); }, + /** + * TODO checks the list of existing files prior to uploading and shows a simple dialog to choose + * skip all, replace all or choosw which files to keep + * @param array selection of files to upload + * @param callbacks to call: + * onNoConflicts, + * onSkipConflicts, + * onReplaceConflicts, + * onChooseConflicts, + * onCancel + */ checkExistingFiles: function (selection, callbacks){ - // FIXME check filelist before uploading + // TODO check filelist before uploading and show dialog on conflicts, use callbacks callbacks.onNoConflicts(selection); } }; @@ -220,7 +185,6 @@ $(document).ready(function() { dropZone: $('#content'), // restrict dropZone to content div autoUpload: false, sequentialUploads: true, - //singleFileUploads is on by default, so the data.files array will always have length 1 /** * on first add of every selection @@ -306,7 +270,7 @@ $(document).ready(function() { }); }, onSkipConflicts: function (selection) { - //TODO mark conflicting files as toskip + //TODO mark conflicting files as toskip }, onReplaceConflicts: function (selection) { //TODO mark conflicting files as toreplace @@ -324,22 +288,6 @@ $(document).ready(function() { OC.Upload.checkExistingFiles(selection, callbacks); } - - - - //TODO check filename already exists - /* - if ($('tr[data-file="'+data.files[0].name+'"][data-id]').length > 0) { - data.textStatus = 'alreadyexists'; - data.errorThrown = t('files', '{filename} already exists', - {filename: data.files[0].name} - ); - //TODO show "file already exists" dialog - var fu = that.data('blueimp-fileupload') || that.data('fileupload'); - fu._trigger('fail', e, data); - return false; - } - */ return true; // continue adding files }, @@ -368,8 +316,6 @@ $(document).ready(function() { $('#notification').fadeOut(); }, 5000); } - //var selection = OC.Upload.getSelection(data.originalFiles); - //OC.Upload.deleteSelectionUpload(selection, data.files[0].name); OC.Upload.deleteUpload(data); }, /** @@ -455,30 +401,13 @@ $(document).ready(function() { }); fileupload.on('fileuploaddone', function(e, data) { OC.Upload.logStatus('progress handle fileuploaddone', e, data); - //if user pressed cancel hide upload chrome - //if (! OC.Upload.isProcessing()) { - // $('#uploadprogresswrapper input.stop').fadeOut(); - // $('#uploadprogressbar').fadeOut(); - //} }); fileupload.on('fileuploadstop', function(e, data) { OC.Upload.logStatus('progress handle fileuploadstop', e, data); - //if(OC.Upload.progressBytes()>=100) { //only hide controls when all selections have ended uploading - - //OC.Upload.cancelUploads(); //cleanup - - // if(data.dataType !== 'iframe') { - // $('#uploadprogresswrapper input.stop').hide(); - // } - - // $('#uploadprogressbar').progressbar('value', 100); - // $('#uploadprogressbar').fadeOut(); - //} - //if user pressed cancel hide upload chrome - //if (! OC.Upload.isProcessing()) { + $('#uploadprogresswrapper input.stop').fadeOut(); $('#uploadprogressbar').fadeOut(); - //} + }); fileupload.on('fileuploadfail', function(e, data) { OC.Upload.logStatus('progress handle fileuploadfail', e, data); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index a96f555ac0b..891d45f5805 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -547,11 +547,9 @@ $(document).ready(function(){ }); file_upload_start.on('fileuploadadd', function(e, data) { OC.Upload.logStatus('filelist handle fileuploadadd', e, data); - - // lookup selection for dir - //var selection = OC.Upload.getSelection(data.originalFiles); - - if(FileList.deleteFiles && FileList.deleteFiles.indexOf(data.files[0].name)!==-1){//finish delete if we are uploading a deleted file + + //finish delete if we are uploading a deleted file + if(FileList.deleteFiles && FileList.deleteFiles.indexOf(data.files[0].name)!==-1){ FileList.finishDelete(null, true); //delete file before continuing } @@ -584,6 +582,10 @@ $(document).ready(function(){ file_upload_start.on('fileuploadstart', function(e, data) { OC.Upload.logStatus('filelist handle fileuploadstart', e, data); }); + /* + * when file upload done successfully add row to filelist + * update counter when uploading to sub folder + */ file_upload_start.on('fileuploaddone', function(e, data) { OC.Upload.logStatus('filelist handle fileuploaddone', e, data); @@ -649,18 +651,6 @@ $(document).ready(function(){ }); } } - - //if user pressed cancel hide upload chrome - /* - if (! OC.Upload.isProcessing()) { - //cleanup uploading to a dir - var uploadtext = $('tr .uploadtext'); - var img = OC.imagePath('core', 'filetypes/folder.png'); - uploadtext.parents('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.fadeOut(); - uploadtext.attr('currentUploads', 0); - } - */ }); file_upload_start.on('fileuploadalways', function(e, data) { @@ -668,9 +658,6 @@ $(document).ready(function(){ }); file_upload_start.on('fileuploadsend', function(e, data) { OC.Upload.logStatus('filelist handle fileuploadsend', e, data); - - // TODOD add vis - //data.context.element = }); file_upload_start.on('fileuploadprogress', function(e, data) { OC.Upload.logStatus('filelist handle fileuploadprogress', e, data); @@ -704,51 +691,7 @@ $(document).ready(function(){ uploadtext.attr('currentUploads', 0); } }); - /* - file_upload_start.on('fileuploadfail', function(e, data) { - console.log('fileuploadfail'+((data.files&&data.files.length>0)?' '+data.files[0].name:'')); - - // if we are uploading to a subdirectory - if (data.context && data.context.data('type') === 'dir') { - - // update upload counter ui - var uploadtext = data.context.find('.uploadtext'); - var currentUploads = parseInt(uploadtext.attr('currentUploads')); - currentUploads -= 1; - uploadtext.attr('currentUploads', currentUploads); - if(currentUploads === 0) { - var img = OC.imagePath('core', 'filetypes/folder.png'); - data.context.find('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.text(''); - uploadtext.hide(); - } else { - uploadtext.text(currentUploads + ' ' + t('files', 'files uploading')); - } - - } - - // cleanup files, error notification has been shown by fileupload code - var tr = data.context; - if (typeof tr === 'undefined') { - tr = $('tr').filterAttr('data-file', data.files[0].name); - } - if (tr.attr('data-type') === 'dir') { - - //cleanup uploading to a dir - var uploadtext = tr.find('.uploadtext'); - var img = OC.imagePath('core', 'filetypes/folder.png'); - tr.find('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.text(''); - uploadtext.hide(); //TODO really hide already - - } else { - //TODO add row when sending file - //remove file - tr.fadeOut(); - tr.remove(); - } - }); -*/ + $('#notification').hide(); $('#notification').on('click', '.undo', function(){ if (FileList.deleteFiles) { -- cgit v1.2.3 From 577e3f22b27d8f53a829185d3d749428a37189c5 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Sun, 8 Sep 2013 10:43:52 +0200 Subject: remove unused hooks --- apps/files/js/file-upload.js | 3 --- apps/files/js/filelist.js | 20 -------------------- 2 files changed, 23 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index d3e644dbdaf..d1f9a79f21a 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -399,9 +399,6 @@ $(document).ready(function() { var progress = (data.loaded / data.total) * 100; $('#uploadprogressbar').progressbar('value', progress); }); - fileupload.on('fileuploaddone', function(e, data) { - OC.Upload.logStatus('progress handle fileuploaddone', e, data); - }); fileupload.on('fileuploadstop', function(e, data) { OC.Upload.logStatus('progress handle fileuploadstop', e, data); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 891d45f5805..e3543661914 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -575,13 +575,6 @@ $(document).ready(function(){ } }); - file_upload_start.on('fileuploadsend', function(e, data) { - OC.Upload.logStatus('filelist handle fileuploadsend', e, data); - return true; - }); - file_upload_start.on('fileuploadstart', function(e, data) { - OC.Upload.logStatus('filelist handle fileuploadstart', e, data); - }); /* * when file upload done successfully add row to filelist * update counter when uploading to sub folder @@ -652,19 +645,6 @@ $(document).ready(function(){ } } }); - - file_upload_start.on('fileuploadalways', function(e, data) { - OC.Upload.logStatus('filelist handle fileuploadalways', e, data); - }); - file_upload_start.on('fileuploadsend', function(e, data) { - OC.Upload.logStatus('filelist handle fileuploadsend', e, data); - }); - file_upload_start.on('fileuploadprogress', function(e, data) { - OC.Upload.logStatus('filelist handle fileuploadprogress', e, data); - }); - file_upload_start.on('fileuploadprogressall', function(e, data) { - OC.Upload.logStatus('filelist handle fileuploadprogressall', e, data); - }); file_upload_start.on('fileuploadstop', function(e, data) { OC.Upload.logStatus('filelist handle fileuploadstop', e, data); -- cgit v1.2.3 From 7854cf04eec68da83655a819f081d2f2e12f607b Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Fri, 13 Sep 2013 17:00:07 +0200 Subject: refactor upload js & html to always use only js to fill form data --- apps/files/js/file-upload.js | 20 +++++++++++--------- apps/files/templates/index.php | 12 ++---------- apps/files_sharing/js/public.js | 27 +++++++++++++-------------- 3 files changed, 26 insertions(+), 33 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 970aad1f978..aeb2da90d5f 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -46,6 +46,15 @@ $(document).ready(function() { $('#uploadprogresswrapper input.stop').show(); } }, + submit: function(e, data) { + if ( ! data.formData ) { + // noone set update parameters, we set the minimum + data.formData = { + requesttoken: oc_requesttoken, + dir: $('#dir').val() + }; + } + }, /** * called after the first add, does NOT have the data param * @param e @@ -141,15 +150,8 @@ $(document).ready(function() { $('#uploadprogressbar').fadeOut(); } }; - var file_upload_handler = function() { - $('#file_upload_start').fileupload(file_upload_param); - }; - - - - if ( document.getElementById('data-upload-form') ) { - $(file_upload_handler); - } + $('#file_upload_start').fileupload(file_upload_param); + $.assocArraySize = function(obj) { // http://stackoverflow.com/a/6700/11236 var size = 0, key; diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 29cb457cd5a..e481f89beb8 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -16,26 +16,18 @@
-
= 0):?> - - + -
> diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index 357c6fdf540..acabc9a5c10 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -7,8 +7,6 @@ function fileDownloadPath(dir, file) { return url; } -var form_data; - $(document).ready(function() { $('#data-upload-form').tipsy({gravity:'ne', fade:true}); @@ -50,19 +48,20 @@ $(document).ready(function() { }); } - // Add some form data to the upload handler - file_upload_param.formData = { - MAX_FILE_SIZE: $('#uploadMaxFilesize').val(), - requesttoken: $('#publicUploadRequestToken').val(), - dirToken: $('#dirToken').val(), - appname: 'files_sharing', - subdir: $('input#dir').val() - }; + var file_upload_start = $('#file_upload_start'); + file_upload_start.on('fileuploadadd', function(e, data) { + // Add custom data to the upload handler + data.formData = { + requesttoken: $('#publicUploadRequestToken').val(), + dirToken: $('#dirToken').val(), + subdir: $('input#dir').val() + }; + }); - // Add Uploadprogress Wrapper to controls bar - $('#controls').append($('#additional_controls div#uploadprogresswrapper')); + // Add Uploadprogress Wrapper to controls bar + $('#controls').append($('#additional_controls div#uploadprogresswrapper')); - // Cancel upload trigger - $('#cancel_upload_button').click(Files.cancelUploads); + // Cancel upload trigger + $('#cancel_upload_button').click(Files.cancelUploads); }); -- cgit v1.2.3 From d41e722629c89cecbb7e623005e331ce97dd9fc6 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Mon, 16 Sep 2013 14:10:19 +0200 Subject: refactor upload progress --- apps/files/js/file-upload.js | 308 ++++++++++++++++++++++++------------------- 1 file changed, 173 insertions(+), 135 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index aeb2da90d5f..6a53bebfcc0 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -1,157 +1,196 @@ -$(document).ready(function() { - var file_upload_param = { - dropZone: $('#content'), // restrict dropZone to content div - //singleFileUploads is on by default, so the data.files array will always have length 1 - add: function(e, data) { +/** + * Function that will allow us to know if Ajax uploads are supported + * @link https://github.com/New-Bamboo/example-ajax-upload/blob/master/public/index.html + * also see article @link http://blog.new-bamboo.co.uk/2012/01/10/ridiculously-simple-ajax-uploads-with-formdata + */ +function supportAjaxUploadWithProgress() { + return supportFileAPI() && supportAjaxUploadProgressEvents() && supportFormData(); - if(data.files[0].type === '' && data.files[0].size == 4096) - { - data.textStatus = 'dirorzero'; - data.errorThrown = t('files','Unable to upload your file as it is a directory or has 0 bytes'); - var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - fu._trigger('fail', e, data); - return true; //don't upload this file but go on with next in queue - } + // Is the File API supported? + function supportFileAPI() { + var fi = document.createElement('INPUT'); + fi.type = 'file'; + return 'files' in fi; + }; - var totalSize=0; - $.each(data.originalFiles, function(i,file){ - totalSize+=file.size; - }); + // Are progress events supported? + function supportAjaxUploadProgressEvents() { + var xhr = new XMLHttpRequest(); + return !! (xhr && ('upload' in xhr) && ('onprogress' in xhr.upload)); + }; - if(totalSize>$('#max_upload').val()){ - data.textStatus = 'notenoughspace'; - data.errorThrown = t('files','Not enough space available'); - var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - fu._trigger('fail', e, data); - return false; //don't upload anything - } + // Is FormData supported? + function supportFormData() { + return !! window.FormData; + } +} + +$(document).ready(function() { - // start the actual file upload - var jqXHR = data.submit(); + if ( $('#file_upload_start').length ) { + var file_upload_param = { + dropZone: $('#content'), // restrict dropZone to content div + //singleFileUploads is on by default, so the data.files array will always have length 1 + add: function(e, data) { - // remember jqXHR to show warning to user when he navigates away but an upload is still in progress - if (typeof data.context !== 'undefined' && data.context.data('type') === 'dir') { - var dirName = data.context.data('file'); - if(typeof uploadingFiles[dirName] === 'undefined') { - uploadingFiles[dirName] = {}; + if(data.files[0].type === '' && data.files[0].size == 4096) + { + data.textStatus = 'dirorzero'; + data.errorThrown = t('files','Unable to upload your file as it is a directory or has 0 bytes'); + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + fu._trigger('fail', e, data); + return true; //don't upload this file but go on with next in queue } - uploadingFiles[dirName][data.files[0].name] = jqXHR; - } else { - uploadingFiles[data.files[0].name] = jqXHR; - } - //show cancel button - if($('html.lte9').length === 0 && data.dataType !== 'iframe') { - $('#uploadprogresswrapper input.stop').show(); - } - }, - submit: function(e, data) { - if ( ! data.formData ) { - // noone set update parameters, we set the minimum - data.formData = { - requesttoken: oc_requesttoken, - dir: $('#dir').val() - }; - } - }, - /** - * called after the first add, does NOT have the data param - * @param e - */ - start: function(e) { - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return; - } - $('#uploadprogressbar').progressbar({value:0}); - $('#uploadprogressbar').fadeIn(); - }, - fail: function(e, data) { - if (typeof data.textStatus !== 'undefined' && data.textStatus !== 'success' ) { - if (data.textStatus === 'abort') { - $('#notification').text(t('files', 'Upload cancelled.')); - } else { - // HTTP connection problem - $('#notification').text(data.errorThrown); + var totalSize=0; + $.each(data.originalFiles, function(i,file){ + totalSize+=file.size; + }); + + if(totalSize>$('#max_upload').val()){ + data.textStatus = 'notenoughspace'; + data.errorThrown = t('files','Not enough space available'); + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + fu._trigger('fail', e, data); + return false; //don't upload anything } - $('#notification').fadeIn(); - //hide notification after 5 sec - setTimeout(function() { - $('#notification').fadeOut(); - }, 5000); - } - delete uploadingFiles[data.files[0].name]; - }, - progress: function(e, data) { - // TODO: show nice progress bar in file row - }, - progressall: function(e, data) { - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return; - } - var progress = (data.loaded/data.total)*100; - $('#uploadprogressbar').progressbar('value',progress); - }, - /** - * called for every successful upload - * @param e - * @param data - */ - done:function(e, data) { - // handle different responses (json or body from iframe for ie) - var response; - if (typeof data.result === 'string') { - response = data.result; - } else { - //fetch response from iframe - response = data.result[0].body.innerText; - } - var result=$.parseJSON(response); - if(typeof result[0] !== 'undefined' && result[0].status === 'success') { - var filename = result[0].originalname; + // start the actual file upload + var jqXHR = data.submit(); - // delete jqXHR reference + // remember jqXHR to show warning to user when he navigates away but an upload is still in progress if (typeof data.context !== 'undefined' && data.context.data('type') === 'dir') { var dirName = data.context.data('file'); - delete uploadingFiles[dirName][filename]; - if ($.assocArraySize(uploadingFiles[dirName]) == 0) { - delete uploadingFiles[dirName]; + if(typeof uploadingFiles[dirName] === 'undefined') { + uploadingFiles[dirName] = {}; } + uploadingFiles[dirName][data.files[0].name] = jqXHR; } else { - delete uploadingFiles[filename]; + uploadingFiles[data.files[0].name] = jqXHR; } - var file = result[0]; - } else { - data.textStatus = 'servererror'; - data.errorThrown = t('files', result.data.message); - var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - fu._trigger('fail', e, data); - } - }, - /** - * called after last upload - * @param e - * @param data - */ - stop: function(e, data) { - if(data.dataType !== 'iframe') { - $('#uploadprogresswrapper input.stop').hide(); - } + }, + submit: function(e, data) { + if ( ! data.formData ) { + // noone set update parameters, we set the minimum + data.formData = { + requesttoken: oc_requesttoken, + dir: $('#dir').val() + }; + } + }, + /** + * called after the first add, does NOT have the data param + * @param e + */ + start: function(e) { + }, + fail: function(e, data) { + if (typeof data.textStatus !== 'undefined' && data.textStatus !== 'success' ) { + if (data.textStatus === 'abort') { + $('#notification').text(t('files', 'Upload cancelled.')); + } else { + // HTTP connection problem + $('#notification').text(data.errorThrown); + } + $('#notification').fadeIn(); + //hide notification after 5 sec + setTimeout(function() { + $('#notification').fadeOut(); + }, 5000); + } + delete uploadingFiles[data.files[0].name]; + }, + /** + * called for every successful upload + * @param e + * @param data + */ + done:function(e, data) { + // handle different responses (json or body from iframe for ie) + var response; + if (typeof data.result === 'string') { + response = data.result; + } else { + //fetch response from iframe + response = data.result[0].body.innerText; + } + var result=$.parseJSON(response); + + if(typeof result[0] !== 'undefined' && result[0].status === 'success') { + var filename = result[0].originalname; - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return; + // delete jqXHR reference + if (typeof data.context !== 'undefined' && data.context.data('type') === 'dir') { + var dirName = data.context.data('file'); + delete uploadingFiles[dirName][filename]; + if ($.assocArraySize(uploadingFiles[dirName]) == 0) { + delete uploadingFiles[dirName]; + } + } else { + delete uploadingFiles[filename]; + } + var file = result[0]; + } else { + data.textStatus = 'servererror'; + data.errorThrown = t('files', result.data.message); + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + fu._trigger('fail', e, data); + } + }, + /** + * called after last upload + * @param e + * @param data + */ + stop: function(e, data) { } + }; + + // initialize jquery fileupload (blueimp) + var fileupload = $('#file_upload_start').fileupload(file_upload_param); + window.file_upload_param = fileupload; + + if(supportAjaxUploadWithProgress()) { - $('#uploadprogressbar').progressbar('value',100); - $('#uploadprogressbar').fadeOut(); + // add progress handlers + fileupload.on('fileuploadadd', function(e, data) { + //show cancel button + //if(data.dataType !== 'iframe') { //FIXME when is iframe used? only for ie? + // $('#uploadprogresswrapper input.stop').show(); + //} + }); + // add progress handlers + fileupload.on('fileuploadstart', function(e, data) { + $('#uploadprogresswrapper input.stop').show(); + $('#uploadprogressbar').progressbar({value:0}); + $('#uploadprogressbar').fadeIn(); + }); + fileupload.on('fileuploadprogress', function(e, data) { + //TODO progressbar in row + }); + fileupload.on('fileuploadprogressall', function(e, data) { + var progress = (data.loaded / data.total) * 100; + $('#uploadprogressbar').progressbar('value', progress); + }); + fileupload.on('fileuploadstop', function(e, data) { + $('#uploadprogresswrapper input.stop').fadeOut(); + $('#uploadprogressbar').fadeOut(); + + }); + fileupload.on('fileuploadfail', function(e, data) { + //if user pressed cancel hide upload progress bar and cancel button + if (data.errorThrown === 'abort') { + $('#uploadprogresswrapper input.stop').fadeOut(); + $('#uploadprogressbar').fadeOut(); + } + }); + + } else { + console.log('skipping file progress because your browser is broken'); } - }; - $('#file_upload_start').fileupload(file_upload_param); - + } + $.assocArraySize = function(obj) { // http://stackoverflow.com/a/6700/11236 var size = 0, key; @@ -353,5 +392,4 @@ $(document).ready(function() { $('#new>a').click(); }); }); - window.file_upload_param = file_upload_param; }); -- cgit v1.2.3 From 86c4c83b861febed135707cf3d87047a8a43a043 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Tue, 17 Sep 2013 19:11:18 +0200 Subject: use exists --- apps/files/js/file-upload.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 28270f13938..9d22162f06d 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -373,7 +373,7 @@ $(document).ready(function() { } }; - if ( document.getElementById('data-upload-form') ) { + if ( $('#file_upload_start').exists() ) { // initialize jquery fileupload (blueimp) var fileupload = $('#file_upload_start').fileupload(file_upload_param); window.file_upload_param = fileupload; -- cgit v1.2.3 From 715846626eb931f086fbc952b33031f706b4547a Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Wed, 18 Sep 2013 14:39:39 +0200 Subject: hide excessive logging with a trace flag --- apps/files/js/file-upload.js | 39 +++++++++++++++++++++------------------ apps/files/js/filelist.js | 10 +++++----- 2 files changed, 26 insertions(+), 23 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 9ea3658cc73..8e9bcb885f6 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -72,7 +72,7 @@ OC.Upload = { * cancels all uploads */ cancelUploads:function() { - console.log('canceling uploads'); + this.log('canceling uploads'); jQuery.each(this._uploads,function(i, jqXHR){ jqXHR.abort(); }); @@ -135,7 +135,7 @@ OC.Upload = { * @param data data */ onSkip:function(data){ - this.logStatus('skip', null, data); + this.log('skip', null, data); this.deleteUpload(data); }, /** @@ -143,7 +143,7 @@ OC.Upload = { * @param data data */ onReplace:function(data){ - this.logStatus('replace', null, data); + this.log('replace', null, data); data.data.append('resolution', 'replace'); data.submit(); }, @@ -152,7 +152,7 @@ OC.Upload = { * @param data data */ onAutorename:function(data){ - this.logStatus('autorename', null, data); + this.log('autorename', null, data); if (data.data) { data.data.append('resolution', 'autorename'); } else { @@ -160,9 +160,12 @@ OC.Upload = { } data.submit(); }, - logStatus:function(caption, e, data) { - console.log(caption); - console.log(data); + _trace:false, //TODO implement log handler for JS per class? + log:function(caption, e, data) { + if (this._trace) { + console.log(caption); + console.log(data); + } }, /** * TODO checks the list of existing files prior to uploading and shows a simple dialog to choose @@ -207,7 +210,7 @@ $(document).ready(function() { * @returns {Boolean} */ add: function(e, data) { - OC.Upload.logStatus('add', e, data); + OC.Upload.log('add', e, data); var that = $(this); // we need to collect all data upload objects before starting the upload so we can check their existence @@ -300,7 +303,7 @@ $(document).ready(function() { * @param e */ start: function(e) { - OC.Upload.logStatus('start', e, null); + OC.Upload.log('start', e, null); }, submit: function(e, data) { OC.Upload.rememberUpload(data); @@ -313,7 +316,7 @@ $(document).ready(function() { } }, fail: function(e, data) { - OC.Upload.logStatus('fail', e, data); + OC.Upload.log('fail', e, data); if (typeof data.textStatus !== 'undefined' && data.textStatus !== 'success' ) { if (data.textStatus === 'abort') { $('#notification').text(t('files', 'Upload cancelled.')); @@ -335,7 +338,7 @@ $(document).ready(function() { * @param data */ done:function(e, data) { - OC.Upload.logStatus('done', e, data); + OC.Upload.log('done', e, data); // handle different responses (json or body from iframe for ie) var response; if (typeof data.result === 'string') { @@ -373,7 +376,7 @@ $(document).ready(function() { * @param data */ stop: function(e, data) { - OC.Upload.logStatus('stop', e, data); + OC.Upload.log('stop', e, data); } }; @@ -385,7 +388,7 @@ $(document).ready(function() { // add progress handlers fileupload.on('fileuploadadd', function(e, data) { - OC.Upload.logStatus('progress handle fileuploadadd', e, data); + OC.Upload.log('progress handle fileuploadadd', e, data); //show cancel button //if(data.dataType !== 'iframe') { //FIXME when is iframe used? only for ie? // $('#uploadprogresswrapper input.stop').show(); @@ -393,29 +396,29 @@ $(document).ready(function() { }); // add progress handlers fileupload.on('fileuploadstart', function(e, data) { - OC.Upload.logStatus('progress handle fileuploadstart', e, data); + OC.Upload.log('progress handle fileuploadstart', e, data); $('#uploadprogresswrapper input.stop').show(); $('#uploadprogressbar').progressbar({value:0}); $('#uploadprogressbar').fadeIn(); }); fileupload.on('fileuploadprogress', function(e, data) { - OC.Upload.logStatus('progress handle fileuploadprogress', e, data); + OC.Upload.log('progress handle fileuploadprogress', e, data); //TODO progressbar in row }); fileupload.on('fileuploadprogressall', function(e, data) { - OC.Upload.logStatus('progress handle fileuploadprogressall', e, data); + OC.Upload.log('progress handle fileuploadprogressall', e, data); var progress = (data.loaded / data.total) * 100; $('#uploadprogressbar').progressbar('value', progress); }); fileupload.on('fileuploadstop', function(e, data) { - OC.Upload.logStatus('progress handle fileuploadstop', e, data); + OC.Upload.log('progress handle fileuploadstop', e, data); $('#uploadprogresswrapper input.stop').fadeOut(); $('#uploadprogressbar').fadeOut(); }); fileupload.on('fileuploadfail', function(e, data) { - OC.Upload.logStatus('progress handle fileuploadfail', e, data); + OC.Upload.log('progress handle fileuploadfail', e, data); //if user pressed cancel hide upload progress bar and cancel button if (data.errorThrown === 'abort') { $('#uploadprogresswrapper input.stop').fadeOut(); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index ffdbe5ef01e..39df91c94b4 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -652,7 +652,7 @@ $(document).ready(function(){ var file_upload_start = $('#file_upload_start'); file_upload_start.on('fileuploaddrop', function(e, data) { - OC.Upload.logStatus('filelist handle fileuploaddrop', e, data); + OC.Upload.log('filelist handle fileuploaddrop', e, data); var dropTarget = $(e.originalEvent.target).closest('tr'); if(dropTarget && dropTarget.data('type') === 'dir') { // drag&drop upload to folder @@ -681,7 +681,7 @@ $(document).ready(function(){ }); file_upload_start.on('fileuploadadd', function(e, data) { - OC.Upload.logStatus('filelist handle fileuploadadd', e, data); + OC.Upload.log('filelist handle fileuploadadd', e, data); //finish delete if we are uploading a deleted file if(FileList.deleteFiles && FileList.deleteFiles.indexOf(data.files[0].name)!==-1){ @@ -715,7 +715,7 @@ $(document).ready(function(){ * update counter when uploading to sub folder */ file_upload_start.on('fileuploaddone', function(e, data) { - OC.Upload.logStatus('filelist handle fileuploaddone', e, data); + OC.Upload.log('filelist handle fileuploaddone', e, data); var response; if (typeof data.result === 'string') { @@ -781,7 +781,7 @@ $(document).ready(function(){ } }); file_upload_start.on('fileuploadstop', function(e, data) { - OC.Upload.logStatus('filelist handle fileuploadstop', e, data); + OC.Upload.log('filelist handle fileuploadstop', e, data); //if user pressed cancel hide upload chrome if (data.errorThrown === 'abort') { @@ -794,7 +794,7 @@ $(document).ready(function(){ } }); file_upload_start.on('fileuploadfail', function(e, data) { - OC.Upload.logStatus('filelist handle fileuploadfail', e, data); + OC.Upload.log('filelist handle fileuploadfail', e, data); //if user pressed cancel hide upload chrome if (data.errorThrown === 'abort') { -- cgit v1.2.3 From 89ed0007c021f27d1a867682005e0c36bcad433a Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Thu, 19 Sep 2013 11:11:22 +0200 Subject: jsdoc types should go into {} --- apps/files/js/file-upload.js | 52 ++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 8e9bcb885f6..3cf43dff506 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -37,7 +37,7 @@ function supportAjaxUploadWithProgress() { /** * keeps track of uploads in progress and implements callbacks for the conflicts dialog - * @type OC.Upload + * @type {OC.Upload} */ OC.Upload = { _uploads: [], @@ -45,9 +45,9 @@ OC.Upload = { * cancels a single upload, * @deprecated because it was only used when a file currently beeing uploaded was deleted. Now they are added after * they have been uploaded. - * @param string dir - * @param string filename - * @returns unresolved + * @param {string} dir + * @param {string} filename + * @returns {unresolved} */ cancelUpload:function(dir, filename) { var self = this; @@ -63,7 +63,7 @@ OC.Upload = { }, /** * deletes the jqHXR object from a data selection - * @param data data + * @param {object} data */ deleteUpload:function(data) { delete data.jqXHR; @@ -86,7 +86,7 @@ OC.Upload = { /** * Checks the currently known uploads. * returns true if any hxr has the state 'pending' - * @returns Boolean + * @returns {boolean} */ isProcessing:function(){ var count = 0; @@ -100,7 +100,7 @@ OC.Upload = { }, /** * callback for the conflicts dialog - * @param data + * @param {object} data */ onCancel:function(data) { this.cancelUploads(); @@ -108,7 +108,7 @@ OC.Upload = { /** * callback for the conflicts dialog * calls onSkip, onReplace or onAutorename for each conflict - * @param conflicts list of conflict elements + * @param {object} conflicts - list of conflict elements */ onContinue:function(conflicts) { var self = this; @@ -132,7 +132,7 @@ OC.Upload = { }, /** * handle skipping an upload - * @param data data + * @param {object} data */ onSkip:function(data){ this.log('skip', null, data); @@ -140,7 +140,7 @@ OC.Upload = { }, /** * handle replacing a file on the server with an uploaded file - * @param data data + * @param {object} data */ onReplace:function(data){ this.log('replace', null, data); @@ -149,7 +149,7 @@ OC.Upload = { }, /** * handle uploading a file and letting the server decide a new name - * @param data data + * @param {object} data */ onAutorename:function(data){ this.log('autorename', null, data); @@ -170,13 +170,13 @@ OC.Upload = { /** * TODO checks the list of existing files prior to uploading and shows a simple dialog to choose * skip all, replace all or choosw which files to keep - * @param array selection of files to upload - * @param callbacks to call: - * onNoConflicts, - * onSkipConflicts, - * onReplaceConflicts, - * onChooseConflicts, - * onCancel + * @param {array} selection of files to upload + * @param {object} callbacks - object with several callback methods + * @param {function} callbacks.onNoConflicts + * @param {function} callbacks.onSkipConflicts + * @param {function} callbacks.onReplaceConflicts + * @param {function} callbacks.onChooseConflicts + * @param {function} callbacks.onCancel */ checkExistingFiles: function (selection, callbacks){ // TODO check filelist before uploading and show dialog on conflicts, use callbacks @@ -205,9 +205,9 @@ $(document).ready(function() { * - when only new -> remember as single replace action * - when both -> remember as single autorename action * - start uploading selection - * @param {type} e - * @param {type} data - * @returns {Boolean} + * @param {object} e + * @param {object} data + * @returns {boolean} */ add: function(e, data) { OC.Upload.log('add', e, data); @@ -300,7 +300,7 @@ $(document).ready(function() { }, /** * called after the first add, does NOT have the data param - * @param e + * @param {object} e */ start: function(e) { OC.Upload.log('start', e, null); @@ -334,8 +334,8 @@ $(document).ready(function() { }, /** * called for every successful upload - * @param e - * @param data + * @param {object} e + * @param {object} data */ done:function(e, data) { OC.Upload.log('done', e, data); @@ -372,8 +372,8 @@ $(document).ready(function() { }, /** * called after last upload - * @param e - * @param data + * @param {object} e + * @param {object} data */ stop: function(e, data) { OC.Upload.log('stop', e, data); -- cgit v1.2.3 From c30c153ea517403ee479be739a503bd91bab272e Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Thu, 19 Sep 2013 11:13:11 +0200 Subject: fixing typos and l10n --- apps/files/js/file-upload.js | 2 +- apps/files/js/files.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 8e9bcb885f6..2c42f29445f 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -169,7 +169,7 @@ OC.Upload = { }, /** * TODO checks the list of existing files prior to uploading and shows a simple dialog to choose - * skip all, replace all or choosw which files to keep + * skip all, replace all or choose which files to keep * @param array selection of files to upload * @param callbacks to call: * onNoConflicts, diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 76f19b87cbc..ccb40e7216f 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -499,7 +499,7 @@ var folderDropOptions={ $('#notification').fadeIn(); } } else { - OC.dialogs.alert(t('Error moving file'), t('core', 'Error')); + OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error')); } }); }); @@ -537,7 +537,7 @@ var crumbDropOptions={ $('#notification').fadeIn(); } } else { - OC.dialogs.alert(t('Error moving file'), t('core', 'Error')); + OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error')); } }); }); -- cgit v1.2.3 From ae97fad6322c94765ce7bed0b7c2278d5aa0e701 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Thu, 19 Sep 2013 11:32:56 +0200 Subject: fix double translation of error message --- apps/files/js/file-upload.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'apps/files/js/file-upload.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 3cf43dff506..cca256a5ab0 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -365,7 +365,7 @@ $(document).ready(function() { } else if (result[0].status !== 'success') { //delete data.jqXHR; data.textStatus = 'servererror'; - data.errorThrown = t('files', result.data.message); + data.errorThrown = result.data.message; // error message has been translated on server var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); fu._trigger('fail', e, data); } -- cgit v1.2.3