##// END OF EJS Templates
Save the selected file just in case uploading stuff fails/is not supported....
Jean-Philippe Lang -
r10781:39e7bab2b9ae
parent child
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,30 +1,31
1 <span id="attachments_fields">
1 <span id="attachments_fields">
2 <% if defined?(container) && container && container.saved_attachments %>
2 <% if defined?(container) && container && container.saved_attachments %>
3 <% container.saved_attachments.each_with_index do |attachment, i| %>
3 <% container.saved_attachments.each_with_index do |attachment, i| %>
4 <span id="attachments_p<%= i %>">
4 <span id="attachments_p<%= i %>">
5 <%= text_field_tag("attachments[p#{i}][filename]", attachment.filename, :class => 'filename') +
5 <%= text_field_tag("attachments[p#{i}][filename]", attachment.filename, :class => 'filename') +
6 text_field_tag("attachments[p#{i}][description]", attachment.description, :maxlength => 255, :placeholder => l(:label_optional_description), :class => 'description') +
6 text_field_tag("attachments[p#{i}][description]", attachment.description, :maxlength => 255, :placeholder => l(:label_optional_description), :class => 'description') +
7 link_to('&nbsp;'.html_safe, attachment_path(attachment, :attachment_id => "p#{i}", :format => 'js'), :method => 'delete', :remote => true, :class => 'remove-upload') %>
7 link_to('&nbsp;'.html_safe, attachment_path(attachment, :attachment_id => "p#{i}", :format => 'js'), :method => 'delete', :remote => true, :class => 'remove-upload') %>
8 <%= hidden_field_tag "attachments[p#{i}][token]", "#{attachment.token}" %>
8 <%= hidden_field_tag "attachments[p#{i}][token]", "#{attachment.token}" %>
9 </span>
9 </span>
10 <% end %>
10 <% end %>
11 <% end %>
11 <% end %>
12 </span>
12 </span>
13 <span class="add_attachment">
13 <span class="add_attachment">
14 <%= file_field_tag 'attachments_files',
14 <%= file_field_tag 'attachments[dummy][file]',
15 :id => nil,
15 :id => nil,
16 :class => 'file_selector',
16 :multiple => true,
17 :multiple => true,
17 :onchange => 'addInputFiles(this);',
18 :onchange => 'addInputFiles(this);',
18 :data => {
19 :data => {
19 :max_file_size => Setting.attachment_max_size.to_i.kilobytes,
20 :max_file_size => Setting.attachment_max_size.to_i.kilobytes,
20 :max_file_size_message => l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)),
21 :max_file_size_message => l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)),
21 :max_concurrent_uploads => Redmine::Configuration['max_concurrent_ajax_uploads'].to_i,
22 :max_concurrent_uploads => Redmine::Configuration['max_concurrent_ajax_uploads'].to_i,
22 :upload_path => uploads_path(:format => 'js'),
23 :upload_path => uploads_path(:format => 'js'),
23 :description_placeholder => l(:label_optional_description)
24 :description_placeholder => l(:label_optional_description)
24 } %>
25 } %>
25 (<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)
26 (<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)
26 </span>
27 </span>
27
28
28 <% content_for :header_tags do %>
29 <% content_for :header_tags do %>
29 <%= javascript_include_tag 'attachments' %>
30 <%= javascript_include_tag 'attachments' %>
30 <% end %>
31 <% end %>
@@ -1,189 +1,188
1 /* Redmine - project management software
1 /* Redmine - project management software
2 Copyright (C) 2006-2012 Jean-Philippe Lang */
2 Copyright (C) 2006-2012 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>&nbsp</a>').attr({ href: "#", 'class': 'remove-upload' }).click(removeFile).toggle(!eagerUpload)
15 $('<a>&nbsp</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) {
103 jqXhr.setRequestHeader('Accept', 'application/js');
103 jqXhr.setRequestHeader('Accept', 'application/js');
104 },
104 },
105 xhr: function() {
105 xhr: function() {
106 var xhr = $.ajaxSettings.xhr();
106 var xhr = $.ajaxSettings.xhr();
107 xhr.upload.onloadstart = actualOptions.loadstartEventHandler;
107 xhr.upload.onloadstart = actualOptions.loadstartEventHandler;
108 xhr.upload.onprogress = actualOptions.progressEventHandler;
108 xhr.upload.onprogress = actualOptions.progressEventHandler;
109 return xhr;
109 return xhr;
110 },
110 },
111 data: blob,
111 data: blob,
112 cache: false,
112 cache: false,
113 processData: false
113 processData: false
114 });
114 });
115 }
115 }
116
116
117 function addInputFiles(inputEl) {
117 function addInputFiles(inputEl) {
118 var clearedFileInput = $(inputEl).clone().val('');
118 var clearedFileInput = $(inputEl).clone().val('');
119
119
120 if (inputEl.files) {
120 if (inputEl.files) {
121 // upload files using ajax
121 // upload files using ajax
122 uploadAndAttachFiles(inputEl.files, inputEl);
122 uploadAndAttachFiles(inputEl.files, inputEl);
123 $(inputEl).remove();
123 $(inputEl).remove();
124 } else {
124 } else {
125 // browser not supporting the file API, upload on form submission
125 // browser not supporting the file API, upload on form submission
126 var attachmentId;
126 var attachmentId;
127 var aFilename = inputEl.value.split(/\/|\\/);
127 var aFilename = inputEl.value.split(/\/|\\/);
128 attachmentId = addFile(inputEl, { name: aFilename[ aFilename.length - 1 ] }, false);
128 attachmentId = addFile(inputEl, { name: aFilename[ aFilename.length - 1 ] }, false);
129 if (attachmentId) {
129 if (attachmentId) {
130 $(inputEl).attr({ name: 'attachments[' + attachmentId + '][file]', style: 'display:none;' }).appendTo('#attachments_' + attachmentId);
130 $(inputEl).attr({ name: 'attachments[' + attachmentId + '][file]', style: 'display:none;' }).appendTo('#attachments_' + attachmentId);
131 }
131 }
132 }
132 }
133
133
134 clearedFileInput.insertAfter('#attachments_fields');
134 clearedFileInput.insertAfter('#attachments_fields');
135 }
135 }
136
136
137 function uploadAndAttachFiles(files, inputEl) {
137 function uploadAndAttachFiles(files, inputEl) {
138
138
139 var maxFileSize = $(inputEl).data('max-file-size');
139 var maxFileSize = $(inputEl).data('max-file-size');
140 var maxFileSizeExceeded = $(inputEl).data('max-file-size-message');
140 var maxFileSizeExceeded = $(inputEl).data('max-file-size-message');
141
141
142 var sizeExceeded = false;
142 var sizeExceeded = false;
143 $.each(files, function() {
143 $.each(files, function() {
144 if (this.size && maxFileSize && this.size > parseInt(maxFileSize)) {sizeExceeded=true;}
144 if (this.size && maxFileSize && this.size > parseInt(maxFileSize)) {sizeExceeded=true;}
145 });
145 });
146 if (sizeExceeded) {
146 if (sizeExceeded) {
147 window.alert(maxFileSizeExceeded);
147 window.alert(maxFileSizeExceeded);
148 } else {
148 } else {
149 $.each(files, function() {addFile(inputEl, this, true);});
149 $.each(files, function() {addFile(inputEl, this, true);});
150 }
150 }
151 }
151 }
152
152
153 function handleFileDropEvent(e) {
153 function handleFileDropEvent(e) {
154
154
155 $(this).removeClass('fileover');
155 $(this).removeClass('fileover');
156 blockEventPropagation(e);
156 blockEventPropagation(e);
157
157
158 if ($.inArray('Files', e.dataTransfer.types) > -1) {
158 if ($.inArray('Files', e.dataTransfer.types) > -1) {
159
159 uploadAndAttachFiles(e.dataTransfer.files, $('input:file.file_selector'));
160 uploadAndAttachFiles(e.dataTransfer.files, $('input:file[name=attachments_files]'));
161 }
160 }
162 }
161 }
163
162
164 function dragOverHandler(e) {
163 function dragOverHandler(e) {
165 $(this).addClass('fileover');
164 $(this).addClass('fileover');
166 blockEventPropagation(e);
165 blockEventPropagation(e);
167 }
166 }
168
167
169 function dragOutHandler(e) {
168 function dragOutHandler(e) {
170 $(this).removeClass('fileover');
169 $(this).removeClass('fileover');
171 blockEventPropagation(e);
170 blockEventPropagation(e);
172 }
171 }
173
172
174 function setupFileDrop() {
173 function setupFileDrop() {
175 if (window.File && window.FileList && window.ProgressEvent && window.FormData) {
174 if (window.File && window.FileList && window.ProgressEvent && window.FormData) {
176
175
177 $.event.fixHooks.drop = { props: [ 'dataTransfer' ] };
176 $.event.fixHooks.drop = { props: [ 'dataTransfer' ] };
178
177
179 $('form div.box').has('input:file').each(function() {
178 $('form div.box').has('input:file').each(function() {
180 $(this).on({
179 $(this).on({
181 dragover: dragOverHandler,
180 dragover: dragOverHandler,
182 dragleave: dragOutHandler,
181 dragleave: dragOutHandler,
183 drop: handleFileDropEvent
182 drop: handleFileDropEvent
184 });
183 });
185 });
184 });
186 }
185 }
187 }
186 }
188
187
189 $(document).ready(setupFileDrop);
188 $(document).ready(setupFileDrop);
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now