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