@@ -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). |
|
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> </a>').attr({ href: "#", 'class': 'remove-upload' }).click(removeFile).toggle(!eagerUpload) |
|
19 | $('<a> </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