Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
Various display behaviour tweaks to file upload dialog.
Browse files Browse the repository at this point in the history
    - when there is an error adding a file, just show an error and do not add it
    - show number of queued and uploading files
    - change drop zone style when files are dragged over it
    - reset dialog when it is reopened
    - close dialog when any of the cancel buttons are pressed
    - disable adding more files when upload has started
  • Loading branch information
davidmason committed Apr 29, 2014
1 parent 7b6ca7c commit 3abb78b
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 35 deletions.
Expand Up @@ -54,7 +54,8 @@
$.blueimp.fileupload.prototype._specialOptions.push(
'filesContainer',
'uploadTemplateId',
'downloadTemplateId'
'downloadTemplateId',
'errorTemplateId'
);

// The UI version extends the file upload widget
Expand All @@ -70,6 +71,8 @@
uploadTemplateId: 'template-upload',
// The ID of the download template:
downloadTemplateId: 'template-download',
// The ID of the template for rendering an error
errorTemplateId: 'template-error',
// The container for the list of files. If undefined, it is set to
// an element with class "files" inside of the widget element:
filesContainer: undefined,
Expand All @@ -87,6 +90,11 @@
.not('.processing').length;
},

getNumberOfUploadedFiles: function () {
return this.filesContainer.children('.template-download')
.not('.processing').length;
},

// Callback to retrieve the list of files from the server response:
getFilesFromResponse: function (data) {
if (data.result && $.isArray(data.result.files)) {
Expand All @@ -106,40 +114,36 @@
that = $this.data('blueimp-fileupload') ||
$this.data('fileupload'),
options = that.options;
data.context = that._renderUpload(data.files)
.data('data', data)
.addClass('processing');
options.filesContainer[
options.prependFiles ? 'prepend' : 'append'
](data.context);
that._forceReflow(data.context);
that._transition(data.context);

data.process(function () {
return $this.fileupload('process', data);
}).always(function () {
data.context.each(function (index) {
$(this).find('.size').text(
that._formatFileSize(data.files[index].size)
);
}).removeClass('processing');
that._renderPreviews(data);
}).done(function () {
data.context.find('.start').prop('disabled', false);
if ((that._trigger('added', e, data) !== false) &&
(options.autoUpload || data.autoUpload) &&
data.autoUpload !== false) {
data.submit();
}
}).fail(function () {
if (data.files.error) {
data.context.each(function (index) {
var error = data.files[index].error;
if (error) {
$(this).find('.fileupload-error').text(error)
.removeClass('is-invisible');
var successFiles = [];
$.each(data.files, function (index, file) {
var error = file.error;
if (error) {
// TODO proper i18n for these strings
if (error === 'File type not allowed') {
data.files[index].error = '"' + file.name + '" is not a supported file type.'
}
});
}
that._renderError(file).appendTo(options.errorList);
} else {
successFiles.push(file);
}
});

data.files = successFiles;

// render successful files
data.context = that._renderUpload(data.files)
.data('data', data);
data.context.find('.start').prop('disabled', false);
options.filesContainer[
options.prependFiles ? 'prepend' : 'append'
](data.context);

that._forceReflow(data.context);
that._transition(data.context);
});
},
// Callback for the start of each file upload request:
Expand Down Expand Up @@ -519,6 +523,10 @@
).find('a[download]').each(this._enableDragToDesktop).end();
},

_renderError: function (error) {
return $(this.options.errorTemplate(error));
},

_startHandler: function (e) {
e.preventDefault();
var button = $(e.currentTarget),
Expand Down Expand Up @@ -581,10 +589,16 @@

_initButtonBarEventHandlers: function () {
var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
filesList = this.options.filesContainer;
filesList = this.options.filesContainer,
dropZone = this.element.find('.drag-drop');
this._on(fileUploadButtonBar.find('.start'), {
click: function (e) {
e.preventDefault();
$(e.target).prop('disabled', true)
.text('Uploading...');
dropZone.addClass('is-hidden');
// TODO change upload progress message
this.options.updateUploadCountIndicator(this.options);
filesList.find('.start').click();
}
});
Expand Down Expand Up @@ -666,6 +680,9 @@
if (options.downloadTemplateId) {
options.downloadTemplate = tmpl(options.downloadTemplateId);
}
if (options.errorTemplateId) {
options.errorTemplate = tmpl(options.errorTemplateId);
}
}
},

Expand Down
Expand Up @@ -958,6 +958,13 @@
fileSet,
i,
j = 0;
if (options.beforeAdd) {
if (typeof options.beforeAdd === "function") {
options.beforeAdd(e, $.extend({}, this.options, data));
} else {
console.log('options.beforeAdd should be type "function", but is type "' + typeof options.beforeAdd + '"');
}
}
if (limitSize && (!filesLength || files[0].size === undefined)) {
limitSize = undefined;
}
Expand Down Expand Up @@ -1012,6 +1019,13 @@
);
return result;
});
if (options.afterAdd) {
if (typeof options.afterAdd === "function") {
options.afterAdd(e, $.extend({}, this.options, data));
} else {
console.log('options.afterAdd should be type "function", but is type "' + typeof options.beforeAdd + '"');
}
}
return result;
},

Expand Down
Expand Up @@ -15,17 +15,108 @@ $(function () {
'use strict';

$('.fileupload').each(function() {
var uploadForm = $(this),
var $doc = $(document),
uploadForm = $(this),
dropZone = uploadForm.find('.drag-drop'),
container = uploadForm.closest('.modal');
filesList = uploadForm.find('.files'),
errorList = uploadForm.find('.js-errors'),
startButton = uploadForm.find('.fileupload-main-start'),
doneButton = uploadForm.find('.fileupload-done'),
cancelButton = uploadForm.find('.fileupload-cancel'),
closeButton = uploadForm.find('.fileupload-close'),
closeButtons = doneButton.add(cancelButton).add(closeButton),
container = uploadForm.closest('.modal'),
revealButtonId = container.attr('id') + '-toggle-button',
revealButton = $('#' + revealButtonId),
countIndicator = uploadForm.find('.js-file-count'),
resetUploadForm = (function resetUploadForm () {
errorList.empty();
// individual items should clean up any resources they use
filesList.find('.cancel').click();
// remove items that don't have a cancel button
filesList.empty();
startButton.removeClass('is-hidden')
.attr('disabled', true)
.text('Upload Documents');
doneButton.addClass('is-hidden').prop('disabled', true);
cancelButton.removeClass('is-hidden').prop('disabled', false);
dropZone.removeClass('is-hidden');
}),
updateCountIndicator = (function updateCountIndicator (options) {
var numberOfFiles = options.getNumberOfFiles(),
noFiles = numberOfFiles === 0;
// FIXME i18n on this string
countIndicator.text((noFiles ? 'No' : numberOfFiles) + ' document' + (numberOfFiles === 1 ? '' : 's') + ' queued');
// start button should only be enabled if there are files to upload
startButton.attr('disabled', noFiles);
}),
updateUploadCountIndicator = (function updateUploadCountIndicator (options) {
var totalFiles = options.getNumberOfFiles(),
uploadedFiles = options.getNumberOfUploadedFiles();
countIndicator.text('Uploaded ' + uploadedFiles + ' of ' + totalFiles + ' files');
if (uploadedFiles === totalFiles) {
startButton.addClass('is-hidden').prop('disabled', true);
doneButton.removeClass('is-hidden').prop('disabled', false);
cancelButton.addClass('is-hidden').prop('disabled', true);
}
});

// move the container to the end of the body so it is on top of everything
container.appendTo('body');

revealButton.bind('click', resetUploadForm);

closeButtons.bind('click', function (e) {
zanata.modal.hide('#' + container.attr('id'));
});

// prevent default file drop behaviour on the page
$doc.bind('drop dragover', function (e) {
e.preventDefault();
});


dropZone.bind('drop dragleave dragend', function (e) {
e.preventDefault();
dropZone.removeClass('is-active');
});
dropZone.bind('dragover', function (e) {
e.preventDefault();
dropZone.addClass('is-active');
});

uploadForm.fileupload({
sequentialUploads: true,
dropZone: dropZone,
acceptFileTypes: /(\.|\/)(pot|dtd|txt|idml|html?|od[tpsg])$/i
beforeAdd: (function beforeAdd (e, data) {
errorList.empty();
startButton.attr('disabled', true);
}),
afterAdd: (function afterAdd (e, data) {
updateCountIndicator(data);
}),
errorList: errorList,
// TODO this might need to be passed in as a composite interface attribute and stored in a data- attribute
acceptFileTypes: /(\.|\/)(pot|dtd|txt|idml|html?|od[tpsg])$/i,
failed: (function updateCount (e, data) {
// update count when removing files
var $this = $(this),
that = $this.data('blueimp-fileupload') ||
$this.data('fileupload'),
options = that.options;
// FIXME may want to make sure this doesn't trigger update while
// uploading. Alternative: make this function aware of
// current uploading state.
updateCountIndicator(options);
}),
completed: (function completed (e, data) {
var $this = $(this),
that = $this.data('blueimp-fileupload') ||
$this.data('fileupload'),
options = that.options;
updateUploadCountIndicator(options);
}),
updateUploadCountIndicator: updateUploadCountIndicator
});

// FIXME may be unnecessary. If necessary, it could just go in the above options
Expand Down

0 comments on commit 3abb78b

Please sign in to comment.