98e5c67c5bc26bec39b64d786a08c33cd2e4a2f8
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Resources / Public / JavaScript / DragUploader.js
1 /*
2 * This file is part of the TYPO3 CMS project.
3 *
4 * It is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License, either version 2
6 * of the License, or any later version.
7 *
8 * For the full copyright and license information, please read the
9 * LICENSE.txt file that was distributed with this source code.
10 *
11 * The TYPO3 project - inspiring people to share!
12 */
13
14 /**
15 * JavaScript RequireJS module called "TYPO3/CMS/Backend/DragUploader"
16 *
17 */
18 define('TYPO3/CMS/Backend/DragUploader', ['jquery'], function($) {
19
20 /*
21 * part 1: a generic jQuery plugin "$.dragUploader"
22 */
23
24 // register the constructor
25 var DragUploaderPlugin = function(element) {
26 var me = this;
27 me.$body = $('body');
28 me.$element = $(element);
29 me.$trigger = $(me.$element.data('dropzone-trigger'));
30 me.$dropzone = $('<div />').addClass('t3-dropzone').hide();
31 if (me.$element.data('file-irre-object') && me.$element.nextAll(me.$element.data('dropzone-target')).length !== 0) {
32 me.dropZoneInsertBefore = true;
33 me.$dropzone.insertBefore(me.$element.data('dropzone-target'));
34 } else {
35 me.dropZoneInsertBefore = false;
36 me.$dropzone.insertAfter(me.$element.data('dropzone-target'));
37 }
38 me.$dropzoneMask = $('<div />').addClass('t3-dropzone-mask').appendTo(me.$dropzone);
39 me.$fileInput = $('<input type="file" multiple name="files[]" />').addClass('t3-upload-file-picker').appendTo(me.$body);
40 me.$fileList = $(me.$element.data('progress-container'));
41 me.fileListColumnCount = $('thead tr:first td', me.$fileList).length;
42 me.filesExtensionsAllowed = me.$element.data('file-allowed');
43 me.fileDenyPattern = me.$element.data('file-deny-pattern') ? new RegExp(me.$element.data('file-deny-pattern'), 'i') : false;
44 me.maxFileSize = parseInt(me.$element.data('max-file-size'));
45 me.target = me.$element.data('target-folder');
46
47 me.browserCapabilities = {
48 fileReader: typeof FileReader != 'undefined',
49 DnD: 'draggable' in document.createElement('span'),
50 FormData: !!window.FormData,
51 Progress: "upload" in new XMLHttpRequest
52 };
53
54 me.showDropzone = function() {
55 me.$dropzone.show();
56 };
57
58 me.hideDropzone = function(event) {
59 event.stopPropagation();
60 event.preventDefault();
61 me.$dropzone.hide();
62 };
63
64 me.dragFileIntoDocument = function(event) {
65 event.stopPropagation();
66 event.preventDefault();
67 me.$body.addClass('t3-drop-in-progress');
68 me.showDropzone();
69 return false;
70 };
71
72 me.dragAborted = function(event) {
73 event.stopPropagation();
74 event.preventDefault();
75 me.$body.removeClass('t3-drop-in-progress');
76 return false;
77 };
78
79 me.ignoreDrop = function(event) {
80 // stops the browser from redirecting.
81 event.stopPropagation();
82 event.preventDefault();
83 me.dragAborted(event);
84 return false;
85 };
86
87 me.handleDrop = function (event) {
88 me.ignoreDrop(event);
89 me.processFiles(event.originalEvent.dataTransfer.files);
90 me.$dropzone.removeClass('t3-dropzone-drop-ok');
91 };
92
93 me.processFiles = function (files) {
94 me.queueLength = 0;
95
96 // ask user if we should override files
97 var override = confirm(TYPO3.l10n.localize('file_upload.overwriteExistingFiles'));
98 if (!me.$fileList.is(':visible')) {
99 me.$fileList.show();
100 }
101 // Add each file to queue and start upload
102 $.each(files, function(i, file) {
103 me.queueLength++;
104 new FileQueueItem(me, file, override);
105 });
106 };
107
108 me.fileInDropzone = function(event) {
109 me.$dropzone.addClass('t3-dropzone-drop-ok');
110 };
111
112 me.fileOutOfDropzone = function(event) {
113 me.$dropzone.removeClass('t3-dropzone-drop-ok');
114 };
115
116 // bind file picker to default upload button
117 me.bindUploadButton = function(button) {
118 button.click(function(event) {
119 event.preventDefault();
120 me.$fileInput.click();
121 me.showDropzone();
122 });
123 };
124
125 if (me.browserCapabilities.DnD) {
126 me.$element.show();
127 me.$body.on('dragover', me.dragFileIntoDocument);
128 me.$body.on('dragend', me.dragAborted);
129 me.$body.on('drop', me.ignoreDrop);
130
131 me.$dropzone.on('dragenter', me.fileInDropzone);
132 me.$dropzoneMask.on('dragenter', me.fileInDropzone);
133 me.$dropzoneMask.on('dragleave', me.fileOutOfDropzone);
134 me.$dropzoneMask.on('drop', me.handleDrop);
135
136 me.$dropzone.prepend('<h4>'+TYPO3.l10n.localize('file_upload.dropzonehint.title')+'</h4><p>'+TYPO3.l10n.localize('file_upload.dropzonehint.message')+'</p>')
137 .click(function(){me.$fileInput.click()});
138 $('<span />').addClass('t3-icon t3-icon-actions t3-icon-actions-close t3-dropzone-close').html('&nbsp;').click(me.hideDropzone).appendTo(me.$dropzone);
139
140 // no filelist then create own progress table
141 if (me.$fileList.length === 0) {
142 me.$fileList = $('<table />').attr('id', 'typo3-filelist').addClass('table table-striped table-hover t3-upload-queue').html('<tbody></tbody>').hide();
143 if (me.dropZoneInsertBefore) {
144 me.$fileList.insertAfter(me.$dropzone);
145 } else {
146 me.$fileList.insertBefore(me.$dropzone);
147 }
148 me.fileListColumnCount = 7;
149 }
150
151 me.$fileInput.on('change', function() {
152 me.processFiles(this.files);
153 });
154
155 me.bindUploadButton(me.$trigger.length ? me.$trigger : me.$element);
156 }
157
158 me.decrementQueueLength = function() {
159 if (me.queueLength > 0) {
160 me.queueLength--;
161 if (me.queueLength == 0) {
162 $.ajax({
163 url: TYPO3.settings.ajaxUrls['DocumentTemplate::getFlashMessages'],
164 cache: false,
165 success: function(data) {
166 var messages = $('#typo3-messages');
167 if (messages.length == 0) {
168 $('#typo3-inner-docbody').prepend(data);
169 } else {
170 messages.replaceWith(data);
171 }
172 }
173 });
174 }
175 }
176 }
177 };
178
179 var FileQueueItem = function(dragUploader, file, override) {
180
181 var me = this;
182 me.dragUploader = dragUploader;
183 me.file = file;
184 me.override = override;
185
186 me.$row = $('<tr />').addClass('t3-upload-queue-item uploading');
187 me.$iconCol = $('<td />').addClass('col-icon').appendTo(me.$row);
188 me.$fileName = $('<td />').text(file.name).appendTo(me.$row);
189 me.$progress = $('<td />').addClass('t3-upload-queue-progress')
190 .attr('colspan', me.dragUploader.fileListColumnCount-2).appendTo(me.$row);
191 me.$progressContainer = $('<div />').addClass('t3-upload-queue-progress').appendTo(me.$progress);
192 me.$progressBar = $('<div />').addClass('t3-upload-queue-progress-bar').appendTo(me.$progressContainer);
193 me.$progressPercentage = $('<span />').addClass('t3-upload-queue-progress-percentage').appendTo(me.$progressContainer);
194 me.$progressMessage = $('<span />').addClass('t3-upload-queue-progress-message').appendTo(me.$progressContainer);
195
196 me.updateMessage = function(message) {
197 me.$progressMessage.text(message);
198 };
199
200 me.removeProgress = function() {
201 if (me.$progress) {
202 me.$progress.remove();
203 }
204 };
205
206 me.uploadStart = function() {
207 me.$progressPercentage.text('(0%)');
208 me.$progressBar.width('1%');
209 me.dragUploader.$trigger.trigger('uploadStart', [me]);
210 };
211
212 me.uploadError = function(response) {
213 me.updateMessage(TYPO3.l10n.localize('file_upload.uploadFailed').replace(/\{0\}/g, me.file.name));
214 var error = $(response.responseText);
215 if (error.is('t3err')) {
216 me.$progressPercentage.text(error.text());
217 } else {
218 me.$progressPercentage.text('(' + response.statusText + ')');
219 }
220 me.$row.addClass('error');
221 dragUploader.decrementQueueLength();
222 me.dragUploader.$trigger.trigger('uploadError', [me, response]);
223 };
224
225 me.updateProgress = function(event) {
226 var percentage = Math.round((event.loaded / event.total) * 100) + '%'
227 me.$progressBar.width(percentage);
228 me.$progressPercentage.text(percentage);
229 me.dragUploader.$trigger.trigger('updateProgress', [me, percentage, event]);
230 };
231
232 me.uploadComplete = function(data) {
233 if (data.result.upload) {
234 me.dragUploader.decrementQueueLength();
235 me.$row.removeClass('uploading');
236 me.$fileName.text(data.result.upload[0].name);
237 me.$progressPercentage.text('');
238 me.$progressMessage.text('100%');
239 me.$progressBar.width('100%');
240
241 // replace file icon
242 if (data.result.upload[0].iconClasses) {
243 me.$iconCol.html('<span class="' + data.result.upload[0].iconClasses + '">&nbsp;</span>');
244 }
245
246 if (me.dragUploader.$element.data('file-irre-object')) {
247 inline.delayedImportElement(
248 me.dragUploader.$element.data('file-irre-object'),
249 'sys_file',
250 data.result.upload[0].uid,
251 'file'
252 );
253 setTimeout(function() {
254 me.$row.remove();
255 if ($('tr', me.dragUploader.$fileList).length === 0) {
256 me.dragUploader.$fileList.hide();
257 me.dragUploader.$trigger.trigger('uploadComplete', [me, data]);
258 }
259 }, 3000);
260
261
262 } else {
263 setTimeout(function() {
264 me.showFileInfo(data.result.upload[0]);
265 me.dragUploader.$trigger.trigger('uploadComplete', [me, data]);
266 }, 3000);
267 }
268 }
269 };
270
271 me.showFileInfo = function(fileInfo) {
272 me.removeProgress();
273 // add spacing cells when clibboard and/or extended view is enabled
274 for (i = 7; i < me.dragUploader.fileListColumnCount; i++) {
275 $('<td />').text('').appendTo(me.$row);
276 }
277 $('<td />').text(fileInfo.extension.toUpperCase()).appendTo(me.$row);
278 $('<td />').text(fileInfo.date).appendTo(me.$row);
279 $('<td />').text(me.fileSizeAsString(fileInfo.size)).appendTo(me.$row);
280 var permissions = '';
281 if (fileInfo.permissions.read) {
282 permissions += '<span class="typo3-red"><strong>R</strong></span>';
283 }
284 if (fileInfo.permissions.write) {
285 permissions += '<span class="typo3-red"><strong>W</strong></span>';
286 }
287 $('<td />').html(permissions).appendTo(me.$row);
288 $('<td />').text('-').appendTo(me.$row);
289 };
290
291 me.fileSizeAsString = function(size) {
292 var string = "";
293 var sizeKB = size / 1024;
294 if (parseInt(sizeKB) > 1024) {
295 var sizeMB = sizeKB / 1024;
296 string = sizeMB.toFixed(1) + " MB";
297 } else {
298 string = sizeKB.toFixed(1) + " KB";
299 }
300 return string;
301 };
302
303 me.checkAllowedExtensions = function() {
304 if (!me.dragUploader.filesExtensionsAllowed) {
305 return true;
306 }
307 var extension = me.file.name.split('.').pop();
308 var allowed = me.dragUploader.filesExtensionsAllowed.split(',');
309 if ($.inArray(extension.toLowerCase(), allowed) !== -1) {
310 return true;
311 }
312 return false;
313 }
314
315 // position queue item in file list
316 if ($('tbody tr.t3-upload-queue-item', me.dragUploader.$fileList).length === 0) {
317 me.$row.prependTo($('tbody', me.dragUploader.$fileList));
318 me.$row.addClass('last');
319 } else {
320 me.$row.insertBefore($('tbody tr.t3-upload-queue-item:first', me.dragUploader.$fileList));
321 }
322
323 // set dummy file icon
324 me.$iconCol.html('<span class="t3-icon t3-icon-mimetypes t3-icon-other-other">&nbsp;</span>')
325
326 // check file size
327 if (me.file.size > me.dragUploader.maxFileSize) {
328 me.updateMessage(TYPO3.l10n.localize('file_upload.maxFileSizeExceeded')
329 .replace(/\{0\}/g, me.file.name)
330 .replace(/\{1\}/g, me.fileSizeAsString(me.dragUploader.maxFileSize)));
331 me.$row.addClass('error');
332
333 // check filename/extension against deny pattern
334 } else if (me.dragUploader.fileDenyPattern && me.file.name.match(me.dragUploader.fileDenyPattern)) {
335 me.updateMessage(TYPO3.l10n.localize('file_upload.fileNotAllowed').replace(/\{0\}/g, me.file.name));
336 me.$row.addClass('error');
337
338 } else if (!me.checkAllowedExtensions()) {
339 me.updateMessage(TYPO3.l10n.localize('file_upload.fileExtensionExpected')
340 .replace(/\{0\}/g, me.dragUploader.filesExtensionsAllowed)
341 );
342 me.$row.addClass('error');
343 } else {
344 me.updateMessage('- ' + me.fileSizeAsString(me.file.size));
345
346 var formData = new FormData();
347 formData.append('file[upload][1][target]', me.dragUploader.target);
348 formData.append('file[upload][1][data]', '1');
349 if(me.override) {
350 formData.append('overwriteExistingFiles', '1');
351 }
352 formData.append('redirect', '');
353 formData.append('upload_1', me.file);
354
355 var s = $.extend(true, {}, $.ajaxSettings, {
356 url: TYPO3.settings.ajaxUrls['TYPO3_tcefile::process'],
357 contentType: false,
358 processData: false,
359 data: formData,
360 cache: false,
361 type: 'POST',
362 success: me.uploadComplete,
363 error: me.uploadError
364 });
365
366 s.xhr = function() {
367 var xhr = $.ajaxSettings.xhr();
368 xhr.upload.addEventListener('progress', me.updateProgress);
369 return xhr;
370 };
371
372 // start upload
373 me.upload = $.ajax(s);
374 }
375
376 }
377
378 /**
379 * part 2: The main module of this file
380 * - initialize the DragUploader module and register
381 * the jQuery plugin in the jQuery global object
382 * when initializing the DragUploader module
383 */
384 var DragUploader = {};
385
386 DragUploader.options = {
387 };
388
389 DragUploader.initialize = function() {
390 var
391 me = this
392 ,opts = me.options;
393
394 // register the jQuery plugin "DragUploaderPlugin"
395 $.fn.dragUploader = function(option) {
396 return this.each(function() {
397 var $this = $(this)
398 , data = $this.data('DragUploaderPlugin');
399 if (!data) {
400 $this.data('DragUploaderPlugin', (data = new DragUploaderPlugin(this)));
401 }
402 if (typeof option == 'string') {
403 data[option]();
404 }
405 })
406 };
407
408 $('.t3-drag-uploader').dragUploader();
409
410 };
411
412
413
414 /**
415 * part 3: initialize the RequireJS module, require possible post-initialize hooks,
416 * and return the main object
417 */
418 var initialize = function() {
419
420 DragUploader.initialize();
421
422 // load required modules to hook in the post initialize function
423 if (undefined !== TYPO3.settings && undefined !== TYPO3.settings.RequireJS && undefined !== TYPO3.settings.RequireJS.PostInitializationModules && undefined !== TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader']) {
424 $.each(TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader'], function(pos, moduleName) {
425 require([moduleName]);
426 });
427 }
428
429 // return the object in the global space
430 return DragUploader;
431 };
432
433 // call the main initialize function and execute the hooks
434 return initialize();
435
436 });