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