##// END OF EJS Templates
Fix file upload for IE9 (#6719)....
Jean-Philippe Lang -
r15544:2f48c9a83a29
parent child
Show More
@@ -1,211 +1,212
1 /* Redmine - project management software
1 /* Redmine - project management software
2 Copyright (C) 2006-2016 Jean-Philippe Lang */
2 Copyright (C) 2006-2016 Jean-Philippe Lang */
3
3
4 function addFile(inputEl, file, eagerUpload) {
4 function addFile(inputEl, file, eagerUpload) {
5 var attachmentsFields = $(inputEl).closest('.attachments_form').find('.attachments_fields');
5 var attachmentsFields = $(inputEl).closest('.attachments_form').find('.attachments_fields');
6 var addAttachment = $(inputEl).closest('.attachments_form').find('.add_attachment');
6 var addAttachment = $(inputEl).closest('.attachments_form').find('.add_attachment');
7 var maxFiles = ($(inputEl).prop('multiple') == true ? 10 : 1);
7 var maxFiles = ($(inputEl).attr('multiple') == 'multiple' ? 10 : 1);
8
8
9 if (attachmentsFields.children().length < maxFiles) {
9 if (attachmentsFields.children().length < maxFiles) {
10 var attachmentId = addFile.nextAttachmentId++;
10 var attachmentId = addFile.nextAttachmentId++;
11 var fileSpan = $('<span>', { id: 'attachments_' + attachmentId });
11 var fileSpan = $('<span>', { id: 'attachments_' + attachmentId });
12 var param = $(inputEl).data('param');
12 var param = $(inputEl).data('param');
13 if (!param) {param = 'attachments'};
13 if (!param) {param = 'attachments'};
14
14
15 fileSpan.append(
15 fileSpan.append(
16 $('<input>', { type: 'text', 'class': 'filename readonly', name: param +'[' + attachmentId + '][filename]', readonly: 'readonly'} ).val(file.name),
16 $('<input>', { type: 'text', 'class': 'filename readonly', name: param +'[' + attachmentId + '][filename]', readonly: 'readonly'} ).val(file.name),
17 $('<input>', { type: 'text', 'class': 'description', name: param + '[' + attachmentId + '][description]', maxlength: 255, placeholder: $(inputEl).data('description-placeholder') } ).toggle(!eagerUpload),
17 $('<input>', { type: 'text', 'class': 'description', name: param + '[' + attachmentId + '][description]', maxlength: 255, placeholder: $(inputEl).data('description-placeholder') } ).toggle(!eagerUpload),
18 $('<input>', { type: 'hidden', 'class': 'token', name: param + '[' + attachmentId + '][token]'} ),
18 $('<input>', { type: 'hidden', 'class': 'token', name: param + '[' + attachmentId + '][token]'} ),
19 $('<a>&nbsp</a>').attr({ href: "#", 'class': 'remove-upload' }).click(removeFile).toggle(!eagerUpload)
19 $('<a>&nbsp</a>').attr({ href: "#", 'class': 'remove-upload' }).click(removeFile).toggle(!eagerUpload)
20 ).appendTo(attachmentsFields);
20 ).appendTo(attachmentsFields);
21
21
22 if ($(inputEl).data('description') == 0) {
22 if ($(inputEl).data('description') == 0) {
23 fileSpan.find('input.description').remove();
23 fileSpan.find('input.description').remove();
24 }
24 }
25
25
26 if(eagerUpload) {
26 if(eagerUpload) {
27 ajaxUpload(file, attachmentId, fileSpan, inputEl);
27 ajaxUpload(file, attachmentId, fileSpan, inputEl);
28 }
28 }
29
29
30 addAttachment.toggle(attachmentsFields.children().length < maxFiles);
30 addAttachment.toggle(attachmentsFields.children().length < maxFiles);
31 return attachmentId;
31 return attachmentId;
32 }
32 }
33 return null;
33 return null;
34 }
34 }
35
35
36 addFile.nextAttachmentId = 1;
36 addFile.nextAttachmentId = 1;
37
37
38 function ajaxUpload(file, attachmentId, fileSpan, inputEl) {
38 function ajaxUpload(file, attachmentId, fileSpan, inputEl) {
39
39
40 function onLoadstart(e) {
40 function onLoadstart(e) {
41 fileSpan.removeClass('ajax-waiting');
41 fileSpan.removeClass('ajax-waiting');
42 fileSpan.addClass('ajax-loading');
42 fileSpan.addClass('ajax-loading');
43 $('input:submit', $(this).parents('form')).attr('disabled', 'disabled');
43 $('input:submit', $(this).parents('form')).attr('disabled', 'disabled');
44 }
44 }
45
45
46 function onProgress(e) {
46 function onProgress(e) {
47 if(e.lengthComputable) {
47 if(e.lengthComputable) {
48 this.progressbar( 'value', e.loaded * 100 / e.total );
48 this.progressbar( 'value', e.loaded * 100 / e.total );
49 }
49 }
50 }
50 }
51
51
52 function actualUpload(file, attachmentId, fileSpan, inputEl) {
52 function actualUpload(file, attachmentId, fileSpan, inputEl) {
53
53
54 ajaxUpload.uploading++;
54 ajaxUpload.uploading++;
55
55
56 uploadBlob(file, $(inputEl).data('upload-path'), attachmentId, {
56 uploadBlob(file, $(inputEl).data('upload-path'), attachmentId, {
57 loadstartEventHandler: onLoadstart.bind(progressSpan),
57 loadstartEventHandler: onLoadstart.bind(progressSpan),
58 progressEventHandler: onProgress.bind(progressSpan)
58 progressEventHandler: onProgress.bind(progressSpan)
59 })
59 })
60 .done(function(result) {
60 .done(function(result) {
61 progressSpan.progressbar( 'value', 100 ).remove();
61 progressSpan.progressbar( 'value', 100 ).remove();
62 fileSpan.find('input.description, a').css('display', 'inline-block');
62 fileSpan.find('input.description, a').css('display', 'inline-block');
63 })
63 })
64 .fail(function(result) {
64 .fail(function(result) {
65 progressSpan.text(result.statusText);
65 progressSpan.text(result.statusText);
66 }).always(function() {
66 }).always(function() {
67 ajaxUpload.uploading--;
67 ajaxUpload.uploading--;
68 fileSpan.removeClass('ajax-loading');
68 fileSpan.removeClass('ajax-loading');
69 var form = fileSpan.parents('form');
69 var form = fileSpan.parents('form');
70 if (form.queue('upload').length == 0 && ajaxUpload.uploading == 0) {
70 if (form.queue('upload').length == 0 && ajaxUpload.uploading == 0) {
71 $('input:submit', form).removeAttr('disabled');
71 $('input:submit', form).removeAttr('disabled');
72 }
72 }
73 form.dequeue('upload');
73 form.dequeue('upload');
74 });
74 });
75 }
75 }
76
76
77 var progressSpan = $('<div>').insertAfter(fileSpan.find('input.filename'));
77 var progressSpan = $('<div>').insertAfter(fileSpan.find('input.filename'));
78 progressSpan.progressbar();
78 progressSpan.progressbar();
79 fileSpan.addClass('ajax-waiting');
79 fileSpan.addClass('ajax-waiting');
80
80
81 var maxSyncUpload = $(inputEl).data('max-concurrent-uploads');
81 var maxSyncUpload = $(inputEl).data('max-concurrent-uploads');
82
82
83 if(maxSyncUpload == null || maxSyncUpload <= 0 || ajaxUpload.uploading < maxSyncUpload)
83 if(maxSyncUpload == null || maxSyncUpload <= 0 || ajaxUpload.uploading < maxSyncUpload)
84 actualUpload(file, attachmentId, fileSpan, inputEl);
84 actualUpload(file, attachmentId, fileSpan, inputEl);
85 else
85 else
86 $(inputEl).parents('form').queue('upload', actualUpload.bind(this, file, attachmentId, fileSpan, inputEl));
86 $(inputEl).parents('form').queue('upload', actualUpload.bind(this, file, attachmentId, fileSpan, inputEl));
87 }
87 }
88
88
89 ajaxUpload.uploading = 0;
89 ajaxUpload.uploading = 0;
90
90
91 function removeFile() {
91 function removeFile() {
92 $(this).closest('.attachments_form').find('.add_attachment').show();
92 $(this).parent('span').remove();
93 $(this).parent('span').remove();
93 return false;
94 return false;
94 }
95 }
95
96
96 function uploadBlob(blob, uploadUrl, attachmentId, options) {
97 function uploadBlob(blob, uploadUrl, attachmentId, options) {
97
98
98 var actualOptions = $.extend({
99 var actualOptions = $.extend({
99 loadstartEventHandler: $.noop,
100 loadstartEventHandler: $.noop,
100 progressEventHandler: $.noop
101 progressEventHandler: $.noop
101 }, options);
102 }, options);
102
103
103 uploadUrl = uploadUrl + '?attachment_id=' + attachmentId;
104 uploadUrl = uploadUrl + '?attachment_id=' + attachmentId;
104 if (blob instanceof window.File) {
105 if (blob instanceof window.File) {
105 uploadUrl += '&filename=' + encodeURIComponent(blob.name);
106 uploadUrl += '&filename=' + encodeURIComponent(blob.name);
106 uploadUrl += '&content_type=' + encodeURIComponent(blob.type);
107 uploadUrl += '&content_type=' + encodeURIComponent(blob.type);
107 }
108 }
108
109
109 return $.ajax(uploadUrl, {
110 return $.ajax(uploadUrl, {
110 type: 'POST',
111 type: 'POST',
111 contentType: 'application/octet-stream',
112 contentType: 'application/octet-stream',
112 beforeSend: function(jqXhr, settings) {
113 beforeSend: function(jqXhr, settings) {
113 jqXhr.setRequestHeader('Accept', 'application/js');
114 jqXhr.setRequestHeader('Accept', 'application/js');
114 // attach proper File object
115 // attach proper File object
115 settings.data = blob;
116 settings.data = blob;
116 },
117 },
117 xhr: function() {
118 xhr: function() {
118 var xhr = $.ajaxSettings.xhr();
119 var xhr = $.ajaxSettings.xhr();
119 xhr.upload.onloadstart = actualOptions.loadstartEventHandler;
120 xhr.upload.onloadstart = actualOptions.loadstartEventHandler;
120 xhr.upload.onprogress = actualOptions.progressEventHandler;
121 xhr.upload.onprogress = actualOptions.progressEventHandler;
121 return xhr;
122 return xhr;
122 },
123 },
123 data: blob,
124 data: blob,
124 cache: false,
125 cache: false,
125 processData: false
126 processData: false
126 });
127 });
127 }
128 }
128
129
129 function addInputFiles(inputEl) {
130 function addInputFiles(inputEl) {
130 var attachmentsFields = $(inputEl).closest('.attachments_form').find('.attachments_fields');
131 var attachmentsFields = $(inputEl).closest('.attachments_form').find('.attachments_fields');
131 var addAttachment = $(inputEl).closest('.attachments_form').find('.add_attachment');
132 var addAttachment = $(inputEl).closest('.attachments_form').find('.add_attachment');
132 var clearedFileInput = $(inputEl).clone().val('');
133 var clearedFileInput = $(inputEl).clone().val('');
133 var sizeExceeded = false;
134 var sizeExceeded = false;
134 var param = $(inputEl).data('param');
135 var param = $(inputEl).data('param');
135 if (!param) {param = 'attachments'};
136 if (!param) {param = 'attachments'};
136
137
137 if ($.ajaxSettings.xhr().upload && inputEl.files) {
138 if ($.ajaxSettings.xhr().upload && inputEl.files) {
138 // upload files using ajax
139 // upload files using ajax
139 sizeExceeded = uploadAndAttachFiles(inputEl.files, inputEl);
140 sizeExceeded = uploadAndAttachFiles(inputEl.files, inputEl);
140 $(inputEl).remove();
141 $(inputEl).remove();
141 } else {
142 } else {
142 // browser not supporting the file API, upload on form submission
143 // browser not supporting the file API, upload on form submission
143 var attachmentId;
144 var attachmentId;
144 var aFilename = inputEl.value.split(/\/|\\/);
145 var aFilename = inputEl.value.split(/\/|\\/);
145 attachmentId = addFile(inputEl, { name: aFilename[ aFilename.length - 1 ] }, false);
146 attachmentId = addFile(inputEl, { name: aFilename[ aFilename.length - 1 ] }, false);
146 if (attachmentId) {
147 if (attachmentId) {
147 $(inputEl).attr({ name: param + '[' + attachmentId + '][file]', style: 'display:none;' }).appendTo('#attachments_' + attachmentId);
148 $(inputEl).attr({ name: param + '[' + attachmentId + '][file]', style: 'display:none;' }).appendTo('#attachments_' + attachmentId);
148 }
149 }
149 }
150 }
150
151
151 clearedFileInput.prependTo(addAttachment);
152 clearedFileInput.prependTo(addAttachment);
152 }
153 }
153
154
154 function uploadAndAttachFiles(files, inputEl) {
155 function uploadAndAttachFiles(files, inputEl) {
155
156
156 var maxFileSize = $(inputEl).data('max-file-size');
157 var maxFileSize = $(inputEl).data('max-file-size');
157 var maxFileSizeExceeded = $(inputEl).data('max-file-size-message');
158 var maxFileSizeExceeded = $(inputEl).data('max-file-size-message');
158
159
159 var sizeExceeded = false;
160 var sizeExceeded = false;
160 $.each(files, function() {
161 $.each(files, function() {
161 if (this.size && maxFileSize != null && this.size > parseInt(maxFileSize)) {sizeExceeded=true;}
162 if (this.size && maxFileSize != null && this.size > parseInt(maxFileSize)) {sizeExceeded=true;}
162 });
163 });
163 if (sizeExceeded) {
164 if (sizeExceeded) {
164 window.alert(maxFileSizeExceeded);
165 window.alert(maxFileSizeExceeded);
165 } else {
166 } else {
166 $.each(files, function() {addFile(inputEl, this, true);});
167 $.each(files, function() {addFile(inputEl, this, true);});
167 }
168 }
168 return sizeExceeded;
169 return sizeExceeded;
169 }
170 }
170
171
171 function handleFileDropEvent(e) {
172 function handleFileDropEvent(e) {
172
173
173 $(this).removeClass('fileover');
174 $(this).removeClass('fileover');
174 blockEventPropagation(e);
175 blockEventPropagation(e);
175
176
176 if ($.inArray('Files', e.dataTransfer.types) > -1) {
177 if ($.inArray('Files', e.dataTransfer.types) > -1) {
177 uploadAndAttachFiles(e.dataTransfer.files, $('input:file.filedrop').first());
178 uploadAndAttachFiles(e.dataTransfer.files, $('input:file.filedrop').first());
178 }
179 }
179 }
180 }
180
181
181 function dragOverHandler(e) {
182 function dragOverHandler(e) {
182 $(this).addClass('fileover');
183 $(this).addClass('fileover');
183 blockEventPropagation(e);
184 blockEventPropagation(e);
184 }
185 }
185
186
186 function dragOutHandler(e) {
187 function dragOutHandler(e) {
187 $(this).removeClass('fileover');
188 $(this).removeClass('fileover');
188 blockEventPropagation(e);
189 blockEventPropagation(e);
189 }
190 }
190
191
191 function setupFileDrop() {
192 function setupFileDrop() {
192 if (window.File && window.FileList && window.ProgressEvent && window.FormData) {
193 if (window.File && window.FileList && window.ProgressEvent && window.FormData) {
193
194
194 $.event.fixHooks.drop = { props: [ 'dataTransfer' ] };
195 $.event.fixHooks.drop = { props: [ 'dataTransfer' ] };
195
196
196 $('form div.box:not(.filedroplistner)').has('input:file.filedrop').each(function() {
197 $('form div.box:not(.filedroplistner)').has('input:file.filedrop').each(function() {
197 $(this).on({
198 $(this).on({
198 dragover: dragOverHandler,
199 dragover: dragOverHandler,
199 dragleave: dragOutHandler,
200 dragleave: dragOutHandler,
200 drop: handleFileDropEvent
201 drop: handleFileDropEvent
201 }).addClass('filedroplistner');
202 }).addClass('filedroplistner');
202 });
203 });
203 }
204 }
204 }
205 }
205
206
206 $(document).ready(setupFileDrop);
207 $(document).ready(setupFileDrop);
207 $(document).ready(function(){
208 $(document).ready(function(){
208 $("input.deleted_attachment").change(function(){
209 $("input.deleted_attachment").change(function(){
209 $(this).parents('.existing-attachment').toggleClass('deleted', $(this).is(":checked"));
210 $(this).parents('.existing-attachment').toggleClass('deleted', $(this).is(":checked"));
210 }).change();
211 }).change();
211 });
212 });
General Comments 0
You need to be logged in to leave comments. Login now