summaryrefslogtreecommitdiffstats
path: root/public/javascripts
diff options
context:
space:
mode:
authorJean-Philippe Lang <jp_lang@yahoo.fr>2012-12-10 20:09:41 +0000
committerJean-Philippe Lang <jp_lang@yahoo.fr>2012-12-10 20:09:41 +0000
commitef25210aca9278e51f81bd15e85a3143c667ff17 (patch)
tree312d20360571770a231f4fd307c12d0c535d60f2 /public/javascripts
parent2304f5d42c2bb1829b1cf9055c2848db116742d3 (diff)
downloadredmine-ef25210aca9278e51f81bd15e85a3143c667ff17.tar.gz
redmine-ef25210aca9278e51f81bd15e85a3143c667ff17.zip
Merged ajax_upload branch (#3957).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10977 e93f8b46-1217-0410-a6f0-8f06a7374b81
Diffstat (limited to 'public/javascripts')
-rw-r--r--public/javascripts/application.js43
-rw-r--r--public/javascripts/attachments.js189
2 files changed, 196 insertions, 36 deletions
diff --git a/public/javascripts/application.js b/public/javascripts/application.js
index 35b24ad05..e5886a666 100644
--- a/public/javascripts/application.js
+++ b/public/javascripts/application.js
@@ -290,40 +290,6 @@ function submit_query_form(id) {
$('#'+id).submit();
}
-var fileFieldCount = 1;
-function addFileField() {
- var fields = $('#attachments_fields');
- if (fields.children().length >= 10) return false;
- fileFieldCount++;
- var s = fields.children('span').first().clone();
- s.children('input.file').attr('name', "attachments[" + fileFieldCount + "][file]").val('');
- s.children('input.description').attr('name', "attachments[" + fileFieldCount + "][description]").val('');
- fields.append(s);
-}
-
-function removeFileField(el) {
- var fields = $('#attachments_fields');
- var s = $(el).parents('span').first();
- if (fields.children().length > 1) {
- s.remove();
- } else {
- s.children('input.file').val('');
- s.children('input.description').val('');
- }
-}
-
-function checkFileSize(el, maxSize, message) {
- var files = el.files;
- if (files) {
- for (var i=0; i<files.length; i++) {
- if (files[i].size > maxSize) {
- alert(message);
- el.value = "";
- }
- }
- }
-}
-
function showTab(name) {
$('div#content .tab-content').hide();
$('div.tabs a').removeClass('selected');
@@ -579,8 +545,8 @@ function warnLeavingUnsaved(message) {
};
$(document).ready(function(){
- $('#ajax-indicator').bind('ajaxSend', function(){
- if ($('.ajax-loading').length == 0) {
+ $('#ajax-indicator').bind('ajaxSend', function(event, xhr, settings){
+ if ($('.ajax-loading').length == 0 && settings.contentType != 'application/octet-stream') {
$('#ajax-indicator').show();
}
});
@@ -607,5 +573,10 @@ function addFormObserversForDoubleSubmit() {
});
}
+function blockEventPropagation(event) {
+ event.stopPropagation();
+ event.preventDefault();
+}
+
$(document).ready(hideOnLoad);
$(document).ready(addFormObserversForDoubleSubmit);
diff --git a/public/javascripts/attachments.js b/public/javascripts/attachments.js
new file mode 100644
index 000000000..fa5722723
--- /dev/null
+++ b/public/javascripts/attachments.js
@@ -0,0 +1,189 @@
+/* Redmine - project management software
+ Copyright (C) 2006-2012 Jean-Philippe Lang */
+
+function addFile(inputEl, file, eagerUpload) {
+
+ if ($('#attachments_fields').children().length < 10) {
+
+ var attachmentId = addFile.nextAttachmentId++;
+
+ var fileSpan = $('<span>', { id: 'attachments_' + attachmentId });
+
+ fileSpan.append(
+ $('<input>', { type: 'text', 'class': 'filename readonly', name: 'attachments[' + attachmentId + '][filename]', readonly: 'readonly'} ).val(file.name),
+ $('<input>', { type: 'text', 'class': 'description', name: 'attachments[' + attachmentId + '][description]', maxlength: 255, placeholder: $(inputEl).data('description-placeholder') } ).toggle(!eagerUpload),
+ $('<a>&nbsp</a>').attr({ href: "#", 'class': 'remove-upload' }).click(removeFile).toggle(!eagerUpload)
+ ).appendTo('#attachments_fields');
+
+ if(eagerUpload) {
+ ajaxUpload(file, attachmentId, fileSpan, inputEl);
+ }
+
+ return attachmentId;
+ }
+ return null;
+}
+
+addFile.nextAttachmentId = 1;
+
+function ajaxUpload(file, attachmentId, fileSpan, inputEl) {
+
+ function onLoadstart(e) {
+ fileSpan.removeClass('ajax-waiting');
+ fileSpan.addClass('ajax-loading');
+ $('input:submit', $(this).parents('form')).attr('disabled', 'disabled');
+ }
+
+ function onProgress(e) {
+ if(e.lengthComputable) {
+ this.progressbar( 'value', e.loaded * 100 / e.total );
+ }
+ }
+
+ function actualUpload(file, attachmentId, fileSpan, inputEl) {
+
+ ajaxUpload.uploading++;
+
+ uploadBlob(file, $(inputEl).data('upload-path'), attachmentId, {
+ loadstartEventHandler: onLoadstart.bind(progressSpan),
+ progressEventHandler: onProgress.bind(progressSpan)
+ })
+ .done(function(result) {
+ progressSpan.progressbar( 'value', 100 ).remove();
+ fileSpan.find('input.description, a').css('display', 'inline-block');
+ })
+ .fail(function(result) {
+ progressSpan.text(result.statusText);
+ }).always(function() {
+ ajaxUpload.uploading--;
+ fileSpan.removeClass('ajax-loading');
+ var form = fileSpan.parents('form');
+ if (form.queue('upload').length == 0 && ajaxUpload.uploading == 0) {
+ $('input:submit', form).removeAttr('disabled');
+ }
+ form.dequeue('upload');
+ });
+ }
+
+ var progressSpan = $('<div>').insertAfter(fileSpan.find('input.filename'));
+ progressSpan.progressbar();
+ fileSpan.addClass('ajax-waiting');
+
+ var maxSyncUpload = $(inputEl).data('max-concurrent-uploads');
+
+ if(maxSyncUpload == null || maxSyncUpload <= 0 || ajaxUpload.uploading < maxSyncUpload)
+ actualUpload(file, attachmentId, fileSpan, inputEl);
+ else
+ $(inputEl).parents('form').queue('upload', actualUpload.bind(this, file, attachmentId, fileSpan, inputEl));
+}
+
+ajaxUpload.uploading = 0;
+
+function removeFile() {
+ $(this).parent('span').remove();
+ return false;
+}
+
+function uploadBlob(blob, uploadUrl, attachmentId, options) {
+
+ var actualOptions = $.extend({
+ loadstartEventHandler: $.noop,
+ progressEventHandler: $.noop
+ }, options);
+
+ uploadUrl = uploadUrl + '?attachment_id=' + attachmentId;
+ if (blob instanceof window.File) {
+ uploadUrl += '&filename=' + encodeURIComponent(blob.name);
+ }
+
+ return $.ajax(uploadUrl, {
+ type: 'POST',
+ contentType: 'application/octet-stream',
+ beforeSend: function(jqXhr) {
+ jqXhr.setRequestHeader('Accept', 'application/js');
+ },
+ xhr: function() {
+ var xhr = $.ajaxSettings.xhr();
+ xhr.upload.onloadstart = actualOptions.loadstartEventHandler;
+ xhr.upload.onprogress = actualOptions.progressEventHandler;
+ return xhr;
+ },
+ data: blob,
+ cache: false,
+ processData: false
+ });
+}
+
+function addInputFiles(inputEl) {
+ var clearedFileInput = $(inputEl).clone().val('');
+
+ if (inputEl.files) {
+ // upload files using ajax
+ uploadAndAttachFiles(inputEl.files, inputEl);
+ $(inputEl).remove();
+ } else {
+ // browser not supporting the file API, upload on form submission
+ var attachmentId;
+ var aFilename = inputEl.value.split(/\/|\\/);
+ attachmentId = addFile(inputEl, { name: aFilename[ aFilename.length - 1 ] }, false);
+ if (attachmentId) {
+ $(inputEl).attr({ name: 'attachments[' + attachmentId + '][file]', style: 'display:none;' }).appendTo('#attachments_' + attachmentId);
+ }
+ }
+
+ clearedFileInput.insertAfter('#attachments_fields');
+}
+
+function uploadAndAttachFiles(files, inputEl) {
+
+ var maxFileSize = $(inputEl).data('max-file-size');
+ var maxFileSizeExceeded = $(inputEl).data('max-file-size-message');
+
+ var sizeExceeded = false;
+ $.each(files, function() {
+ if (this.size && maxFileSize && this.size > parseInt(maxFileSize)) {sizeExceeded=true;}
+ });
+ if (sizeExceeded) {
+ window.alert(maxFileSizeExceeded);
+ } else {
+ $.each(files, function() {addFile(inputEl, this, true);});
+ }
+}
+
+function handleFileDropEvent(e) {
+
+ $(this).removeClass('fileover');
+ blockEventPropagation(e);
+
+ if ($.inArray('Files', e.dataTransfer.types) > -1) {
+
+ uploadAndAttachFiles(e.dataTransfer.files, $('input:file[name=attachments_files]'));
+ }
+}
+
+function dragOverHandler(e) {
+ $(this).addClass('fileover');
+ blockEventPropagation(e);
+}
+
+function dragOutHandler(e) {
+ $(this).removeClass('fileover');
+ blockEventPropagation(e);
+}
+
+function setupFileDrop() {
+ if (window.File && window.FileList && window.ProgressEvent && window.FormData) {
+
+ $.event.fixHooks.drop = { props: [ 'dataTransfer' ] };
+
+ $('form div.box').has('input:file').each(function() {
+ $(this).on({
+ dragover: dragOverHandler,
+ dragleave: dragOutHandler,
+ drop: handleFileDropEvent
+ });
+ });
+ }
+}
+
+$(document).ready(setupFileDrop);