diff options
author | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2012-12-10 20:09:41 +0000 |
---|---|---|
committer | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2012-12-10 20:09:41 +0000 |
commit | ef25210aca9278e51f81bd15e85a3143c667ff17 (patch) | |
tree | 312d20360571770a231f4fd307c12d0c535d60f2 /public/javascripts | |
parent | 2304f5d42c2bb1829b1cf9055c2848db116742d3 (diff) | |
download | redmine-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.js | 43 | ||||
-rw-r--r-- | public/javascripts/attachments.js | 189 |
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> </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); |