[TASK] Migrate Severity to TypeScript
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Resources / Private / TypeScript / DragUploader.ts
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 * Module: TYPO3/CMS/Backend/DragUploader
16 */
17
18 import * as $ from 'jquery';
19 import moment = require('moment');
20 import NProgress = require('nprogress');
21 import Modal = require('TYPO3/CMS/Backend/Modal');
22 import Notification = require('TYPO3/CMS/Backend/Notification');
23 import Severity = require('./Severity');
24
25 /**
26 * Possible actions for conflicts w/ existing files
27 */
28 enum Action {
29 OVERRIDE = 'replace',
30 RENAME = 'rename',
31 SKIP = 'cancel',
32 USE_EXISTING = 'useExisting'
33 }
34
35 /**
36 * Properties of a file as returned from the AJAX action; essential, this is a serialized instance of
37 * \TYPO3\CMS\Core\Resource\File plus some extra properties (see FileController::flattenResultDataValue())
38 */
39 interface UploadedFile {
40 name: string;
41 id: number;
42 uid: number;
43 icon: string;
44 extension: string;
45 permissions: {read: boolean; write: boolean};
46 size: number;
47 // formatted as ddmmyy
48 date: string;
49
50 mtime: Date;
51 thumbUrl: string;
52 type: string;
53 }
54
55 class DragUploaderPlugin {
56 public irreObjectUid: number;
57 public $fileList: JQuery;
58 public fileListColumnCount: number;
59 public filesExtensionsAllowed: string;
60 public fileDenyPattern: RegExp | null;
61 public maxFileSize: number;
62 public $trigger: JQuery;
63 public target: string;
64
65 /**
66 * Array of files which are asked for being overridden
67 */
68 private askForOverride: Array<{original: UploadedFile, uploaded: File, action: Action}> = [];
69
70 private percentagePerFile: number = 1;
71
72 private $body: JQuery;
73 private $element: JQuery;
74 private $dropzone: JQuery;
75 private $dropzoneMask: JQuery;
76 private fileInput: HTMLInputElement;
77 private browserCapabilities: { fileReader: boolean; DnD: boolean; Progress: boolean };
78 private dropZoneInsertBefore: boolean;
79 private queueLength: number;
80
81 constructor(element: HTMLElement) {
82 this.$body = $('body');
83 this.$element = $(element);
84 const hasTrigger = this.$element.data('dropzoneTrigger') !== undefined;
85 this.$trigger = $(this.$element.data('dropzoneTrigger'));
86 this.$dropzone = $('<div />').addClass('dropzone').hide();
87 this.irreObjectUid = this.$element.data('fileIrreObject');
88
89 const dropZoneEscapedTarget = this.$element.data('dropzoneTarget');
90 if (this.irreObjectUid && this.$element.nextAll(dropZoneEscapedTarget).length !== 0) {
91 this.dropZoneInsertBefore = true;
92 this.$dropzone.insertBefore(dropZoneEscapedTarget);
93 } else {
94 this.dropZoneInsertBefore = false;
95 this.$dropzone.insertAfter(dropZoneEscapedTarget);
96 }
97 this.$dropzoneMask = $('<div />').addClass('dropzone-mask').appendTo(this.$dropzone);
98 this.fileInput = <HTMLInputElement>document.createElement('input');
99 this.fileInput.setAttribute('type', 'file');
100 this.fileInput.setAttribute('multiple', 'multiple');
101 this.fileInput.setAttribute('name', 'files[]');
102 this.fileInput.classList.add('upload-file-picker');
103 this.$body.append(this.fileInput);
104
105 this.$fileList = $(this.$element.data('progress-container'));
106 this.fileListColumnCount = $('thead tr:first th', this.$fileList).length;
107 this.filesExtensionsAllowed = this.$element.data('file-allowed');
108 this.fileDenyPattern = this.$element.data('file-deny-pattern') ? new RegExp(this.$element.data('file-deny-pattern'), 'i') : null;
109 this.maxFileSize = parseInt(this.$element.data('max-file-size'), 10);
110 this.target = this.$element.data('target-folder');
111
112 this.browserCapabilities = {
113 fileReader: typeof FileReader !== 'undefined',
114 DnD: 'draggable' in document.createElement('span'),
115 Progress: 'upload' in new XMLHttpRequest
116 };
117
118
119 if (!this.browserCapabilities.DnD) {
120 console.warn('Browser has no Drag and drop capabilities; cannot initialize DragUploader');
121 return;
122 }
123
124 this.$body.on('dragover', this.dragFileIntoDocument);
125 this.$body.on('dragend', this.dragAborted);
126 this.$body.on('drop', this.ignoreDrop);
127
128 this.$dropzone.on('dragenter', this.fileInDropzone);
129 this.$dropzoneMask.on('dragenter', this.fileInDropzone);
130 this.$dropzoneMask.on('dragleave', this.fileOutOfDropzone);
131 this.$dropzoneMask.on('drop', (ev: JQueryEventObject) => this.handleDrop(<JQueryTypedEvent<DragEvent>>ev));
132
133 this.$dropzone.prepend(
134 '<div class="dropzone-hint">' +
135 '<div class="dropzone-hint-media">' +
136 '<div class="dropzone-hint-icon"></div>' +
137 '</div>' +
138 '<div class="dropzone-hint-body">' +
139 '<h3 class="dropzone-hint-title">' +
140 TYPO3.lang['file_upload.dropzonehint.title'] +
141 '</h3>' +
142 '<p class="dropzone-hint-message">' +
143 TYPO3.lang['file_upload.dropzonehint.message'] +
144 '</p>' +
145 '</div>' +
146 '</div>'
147 ).click(() => {
148 this.fileInput.click();
149 });
150 $('<span />').addClass('dropzone-close').click(this.hideDropzone).appendTo(this.$dropzone);
151
152 // no filelist then create own progress table
153 if (this.$fileList.length === 0) {
154 this.$fileList = $('<table />')
155 .attr('id', 'typo3-filelist')
156 .addClass('table table-striped table-hover upload-queue')
157 .html('<tbody></tbody>').hide();
158
159 if (this.dropZoneInsertBefore) {
160 this.$fileList.insertAfter(this.$dropzone);
161 } else {
162 this.$fileList.insertBefore(this.$dropzone);
163 }
164 this.fileListColumnCount = 7;
165 }
166
167 this.fileInput.addEventListener('change', () => {
168 this.processFiles(Array.apply(null, this.fileInput.files));
169 });
170
171 this.bindUploadButton(hasTrigger === true ? this.$trigger : this.$element);
172 }
173
174 public showDropzone(): void {
175 this.$dropzone.show();
176 }
177
178 /**
179 *
180 * @param {Event} event
181 */
182 public hideDropzone(event: Event): void {
183 event.stopPropagation();
184 event.preventDefault();
185 this.$dropzone.hide();
186 }
187
188 /**
189 * @param {Event} event
190 * @returns {boolean}
191 */
192 public dragFileIntoDocument = (event: Event): boolean => {
193 event.stopPropagation();
194 event.preventDefault();
195 $(event.currentTarget).addClass('drop-in-progress');
196 this.showDropzone();
197 return false;
198 }
199
200 /**
201 *
202 * @param {Event} event
203 * @returns {Boolean}
204 */
205 public dragAborted = (event: Event): boolean => {
206 event.stopPropagation();
207 event.preventDefault();
208 $(event.currentTarget).removeClass('drop-in-progress');
209 return false;
210 }
211
212 public ignoreDrop = (event: Event): boolean => {
213 // stops the browser from redirecting.
214 event.stopPropagation();
215 event.preventDefault();
216 this.dragAborted(event);
217 return false;
218 }
219
220 public handleDrop = (event: JQueryTypedEvent<DragEvent>): void => {
221 this.ignoreDrop(event);
222 this.processFiles(event.originalEvent.dataTransfer.files);
223 this.$dropzone.removeClass('drop-status-ok');
224 }
225
226 /**
227 * @param {FileList} files
228 */
229 public processFiles(files: FileList): void {
230 this.queueLength = files.length;
231
232 if (!this.$fileList.is(':visible')) {
233 this.$fileList.show();
234 }
235
236 NProgress.start();
237 this.percentagePerFile = 1 / files.length;
238
239 // Check for each file if is already exist before adding it to the queue
240 const ajaxCalls: JQueryXHR[] = [];
241 $.each(files, (i: string, file) => {
242 ajaxCalls[parseInt(i, 10)] = $.ajax({
243 url: TYPO3.settings.ajaxUrls.file_exists,
244 data: {
245 fileName: file.name,
246 fileTarget: this.target
247 },
248 cache: false,
249 success: (response: any) => {
250 const fileExists = typeof response.uid !== 'undefined';
251 if (fileExists) {
252 this.askForOverride.push({
253 original: response,
254 uploaded: file,
255 action: this.irreObjectUid ? Action.USE_EXISTING : Action.SKIP
256 });
257 NProgress.inc(this.percentagePerFile);
258 } else {
259 // Unused var _ is necessary as "no-unused-expression" is active
260 const _ = new FileQueueItem(this, file, Action.SKIP);
261 }
262 }
263 });
264 });
265
266 $.when.apply($, ajaxCalls).done(() => {
267 this.drawOverrideModal();
268 NProgress.done();
269 });
270
271 this.fileInput.value = '';
272 }
273
274 public fileInDropzone = (): void => {
275 this.$dropzone.addClass('drop-status-ok');
276 }
277
278 public fileOutOfDropzone = (): void => {
279 this.$dropzone.removeClass('drop-status-ok');
280 }
281
282 /**
283 * Bind file picker to default upload button
284 *
285 * @param {Object} button
286 */
287 public bindUploadButton(button: JQuery): void {
288 button.click((event: Event) => {
289 event.preventDefault();
290 this.fileInput.click();
291 this.showDropzone();
292 });
293 }
294
295 /**
296 * Decrements the queue and renders a flash message if queue is empty
297 */
298 public decrementQueueLength(): void {
299 if (this.queueLength > 0) {
300 this.queueLength--;
301 if (this.queueLength === 0) {
302 $.ajax({
303 url: TYPO3.settings.ajaxUrls.flashmessages_render,
304 cache: false,
305 success: (data) => {
306 $.each(data, (index: number, flashMessage: {title: string, message: string, severity: number}) => {
307 Notification.showMessage(flashMessage.title, flashMessage.message, flashMessage.severity);
308 });
309 }
310 });
311 }
312 }
313 }
314
315 /**
316 * Renders the modal for existing files
317 */
318 public drawOverrideModal(): void {
319 const amountOfItems = Object.keys(this.askForOverride).length;
320 if (amountOfItems === 0) {
321 return;
322 }
323 const $modalContent = $('<div/>').append(
324 $('<p/>').text(TYPO3.lang['file_upload.existingfiles.description']),
325 $('<table/>', {class: 'table'}).append(
326 $('<thead/>').append(
327 $('<tr />').append(
328 $('<th/>'),
329 $('<th/>').text(TYPO3.lang['file_upload.header.originalFile']),
330 $('<th/>').text(TYPO3.lang['file_upload.header.uploadedFile']),
331 $('<th/>').text(TYPO3.lang['file_upload.header.action'])
332 )
333 )
334 )
335 );
336
337 for (let i = 0; i < amountOfItems; ++i) {
338 const $record = $('<tr />').append(
339 $('<td />').append(
340 (this.askForOverride[i].original.thumbUrl !== ''
341 ? $('<img />', {src: this.askForOverride[i].original.thumbUrl, height: 40})
342 : $(this.askForOverride[i].original.icon)
343 )
344 ),
345 $('<td />').html(
346 this.askForOverride[i].uploaded.name + ' (' + (DragUploader.fileSizeAsString(this.askForOverride[i].uploaded.size)) + ')' +
347 '<br>' + moment(this.askForOverride[i].uploaded.lastModifiedDate, 'x').format('YYYY-MM-DD HH:mm')
348 ),
349 $('<td />').html(
350 this.askForOverride[i].uploaded.name + ' (' + (DragUploader.fileSizeAsString(this.askForOverride[i].original.size)) + ')' +
351 '<br>' + moment(this.askForOverride[i].original.mtime, 'X').format('YYYY-MM-DD HH:mm')
352 ),
353 $('<td />').append(
354 $('<select />', {class: 'form-control t3js-actions', 'data-override': i}).append(
355 (this.irreObjectUid ? $('<option/>').val(Action.USE_EXISTING).text(TYPO3.lang['file_upload.actions.use_existing']) : ''),
356 $('<option />').val(Action.SKIP).text(TYPO3.lang['file_upload.actions.skip']),
357 $('<option />').val(Action.RENAME).text(TYPO3.lang['file_upload.actions.rename']),
358 $('<option />').val(Action.OVERRIDE).text(TYPO3.lang['file_upload.actions.override'])
359 )
360 )
361 );
362 $modalContent.find('table').append('<tbody />').append($record);
363 }
364
365 const $modal = Modal.confirm(
366 TYPO3.lang['file_upload.existingfiles.title'], $modalContent, Severity.warning,
367 [
368 {
369 text: $(this).data('button-close-text') || TYPO3.lang['file_upload.button.cancel'] || 'Cancel',
370 active: true,
371 btnClass: 'btn-default',
372 name: 'cancel'
373 },
374 {
375 text: $(this).data('button-ok-text') || TYPO3.lang['file_upload.button.continue'] || 'Continue with selected actions',
376 btnClass: 'btn-warning',
377 name: 'continue'
378 }
379 ],
380 ['modal-inner-scroll']
381 );
382 $modal.find('.modal-dialog').addClass('modal-lg');
383
384 $modal.find('.modal-footer').prepend(
385 $('<span/>').addClass('form-inline').append(
386 $('<label/>').text(TYPO3.lang['file_upload.actions.all.label']),
387 $('<select/>', {class: 'form-control t3js-actions-all'}).append(
388 $('<option/>').val('').text(TYPO3.lang['file_upload.actions.all.empty']),
389 (this.irreObjectUid ? $('<option/>').val(Action.USE_EXISTING).text(TYPO3.lang['file_upload.actions.all.use_existing']) : ''),
390 $('<option/>').val(Action.SKIP).text(TYPO3.lang['file_upload.actions.all.skip']),
391 $('<option/>').val(Action.RENAME).text(TYPO3.lang['file_upload.actions.all.rename']),
392 $('<option/>').val(Action.OVERRIDE).text(TYPO3.lang['file_upload.actions.all.override'])
393 )
394 )
395 );
396
397 const uploader = this;
398 $modal.on('change', '.t3js-actions-all', function(this: HTMLInputElement): void {
399 const $this = $(this),
400 value = $this.val();
401
402 if (value !== '') {
403 // mass action was selected, apply action to every file
404 $modal.find('.t3js-actions').each((i, select) => {
405 const $select = $(select),
406 index = parseInt($select.data('override'), 10);
407 $select.val(value).prop('disabled', 'disabled');
408 uploader.askForOverride[index].action = <Action>$select.val();
409 });
410 } else {
411 $modal.find('.t3js-actions').removeProp('disabled');
412 }
413 }).on('change', '.t3js-actions', function(this: HTMLInputElement): void {
414 const $this = $(this),
415 index = parseInt($this.data('override'), 10);
416 uploader.askForOverride[index].action = <Action>$this.val();
417 }).on('button.clicked', function(this: HTMLInputElement, e: Event): void {
418 if ((<HTMLInputElement>(e.target)).name === 'cancel') {
419 uploader.askForOverride = [];
420 Modal.dismiss();
421 } else if ((<HTMLInputElement>(e.target)).name === 'continue') {
422 $.each(uploader.askForOverride, (key, fileInfo) => {
423 if (fileInfo.action === Action.USE_EXISTING) {
424 DragUploader.addFileToIrre(
425 uploader.irreObjectUid,
426 fileInfo.original
427 );
428 } else if (fileInfo.action !== Action.SKIP) {
429 // Unused var _ is necessary as "no-unused-expression" is active
430 const _ = new FileQueueItem(uploader, fileInfo.uploaded, fileInfo.action);
431 }
432 });
433 uploader.askForOverride = [];
434 Modal.dismiss();
435 }
436 }).on('hidden.bs.modal', () => {
437 this.askForOverride = [];
438 });
439 }
440 }
441
442 class FileQueueItem {
443 private $row: JQuery;
444 private $iconCol: JQuery;
445 private $fileName: JQuery;
446 private $progress: JQuery;
447 private $progressContainer: JQuery;
448 private $progressBar: JQuery;
449 private $progressPercentage: JQuery;
450 private $progressMessage: JQuery;
451 private dragUploader: DragUploaderPlugin;
452 private file: File;
453 private override: Action;
454 private upload: XMLHttpRequest;
455
456 constructor(dragUploader: DragUploaderPlugin, file: File, override: Action) {
457 this.dragUploader = dragUploader;
458 this.file = file;
459 this.override = override;
460
461 this.$row = $('<tr />').addClass('upload-queue-item uploading');
462 this.$iconCol = $('<td />').addClass('col-icon').appendTo(this.$row);
463 this.$fileName = $('<td />').text(file.name).appendTo(this.$row);
464 this.$progress = $('<td />').attr('colspan', this.dragUploader.fileListColumnCount - 2).appendTo(this.$row);
465 this.$progressContainer = $('<div />').addClass('upload-queue-progress').appendTo(this.$progress);
466 this.$progressBar = $('<div />').addClass('upload-queue-progress-bar').appendTo(this.$progressContainer);
467 this.$progressPercentage = $('<span />').addClass('upload-queue-progress-percentage').appendTo(this.$progressContainer);
468 this.$progressMessage = $('<span />').addClass('upload-queue-progress-message').appendTo(this.$progressContainer);
469
470
471 // position queue item in filelist
472 if ($('tbody tr.upload-queue-item', this.dragUploader.$fileList).length === 0) {
473 this.$row.prependTo($('tbody', this.dragUploader.$fileList));
474 this.$row.addClass('last');
475 } else {
476 this.$row.insertBefore($('tbody tr.upload-queue-item:first', this.dragUploader.$fileList));
477 }
478
479 // set dummy file icon
480 this.$iconCol.html('<span class="t3-icon t3-icon-mimetypes t3-icon-other-other">&nbsp;</span>');
481
482 // check file size
483 if (this.dragUploader.maxFileSize > 0 && this.file.size > this.dragUploader.maxFileSize) {
484 this.updateMessage(TYPO3.lang['file_upload.maxFileSizeExceeded']
485 .replace(/\{0\}/g, this.file.name)
486 .replace(/\{1\}/g, DragUploader.fileSizeAsString(this.dragUploader.maxFileSize)));
487 this.$row.addClass('error');
488
489 // check filename/extension against deny pattern
490 } else if (this.dragUploader.fileDenyPattern && this.file.name.match(this.dragUploader.fileDenyPattern)) {
491 this.updateMessage(TYPO3.lang['file_upload.fileNotAllowed'].replace(/\{0\}/g, this.file.name));
492 this.$row.addClass('error');
493
494 } else if (!this.checkAllowedExtensions()) {
495 this.updateMessage(TYPO3.lang['file_upload.fileExtensionExpected']
496 .replace(/\{0\}/g, this.dragUploader.filesExtensionsAllowed)
497 );
498 this.$row.addClass('error');
499 } else {
500 this.updateMessage('- ' + DragUploader.fileSizeAsString(this.file.size));
501
502 const formData = new FormData();
503 formData.append('data[upload][1][target]', this.dragUploader.target);
504 formData.append('data[upload][1][data]', '1');
505 formData.append('overwriteExistingFiles', this.override);
506 formData.append('redirect', '');
507 formData.append('upload_1', this.file);
508
509 const s = $.extend(true, {}, $.ajaxSettings, {
510 url: TYPO3.settings.ajaxUrls.file_process,
511 contentType: false,
512 processData: false,
513 data: formData,
514 cache: false,
515 type: 'POST',
516 success: (data: {upload?: UploadedFile[]}) => this.uploadSuccess(data),
517 error: (response: XMLHttpRequest) => this.uploadError(response)
518 });
519
520 s.xhr = () => {
521 const xhr = $.ajaxSettings.xhr();
522 xhr.upload.addEventListener('progress', (e: ProgressEvent) => this.updateProgress(e));
523 return xhr;
524 };
525
526 // start upload
527 this.upload = $.ajax(s);
528 }
529 }
530
531 /**
532 * @param {string} message
533 */
534 public updateMessage(message: string): void {
535 this.$progressMessage.text(message);
536 }
537
538 /**
539 * Remove the progress bar
540 */
541 public removeProgress(): void {
542 if (this.$progress) {
543 this.$progress.remove();
544 }
545 }
546
547 public uploadStart(): void {
548 this.$progressPercentage.text('(0%)');
549 this.$progressBar.width('1%');
550 this.dragUploader.$trigger.trigger('uploadStart', [this]);
551 }
552
553 /**
554 * @param {XMLHttpRequest} response
555 */
556 public uploadError(response: XMLHttpRequest): void {
557 this.updateMessage(TYPO3.lang['file_upload.uploadFailed'].replace(/\{0\}/g, this.file.name));
558 const error = $(response.responseText);
559 if (error.is('t3err')) {
560 this.$progressPercentage.text(error.text());
561 } else {
562 this.$progressPercentage.text('(' + response.statusText + ')');
563 }
564 this.$row.addClass('error');
565 this.dragUploader.decrementQueueLength();
566 this.dragUploader.$trigger.trigger('uploadError', [this, response]);
567 }
568
569 /**
570 * @param {ProgressEvent} event
571 */
572 public updateProgress(event: ProgressEvent): void {
573 const percentage = Math.round((event.loaded / event.total) * 100) + '%';
574 this.$progressBar.outerWidth(percentage);
575 this.$progressPercentage.text(percentage);
576 this.dragUploader.$trigger.trigger('updateProgress', [this, percentage, event]);
577 }
578
579 /**
580 * @param {{upload?: UploadedFile[]}} data
581 */
582 public uploadSuccess(data: {upload?: UploadedFile[]}): void {
583 if (data.upload) {
584 this.dragUploader.decrementQueueLength();
585 this.$row.removeClass('uploading');
586 this.$fileName.text(data.upload[0].name);
587 this.$progressPercentage.text('');
588 this.$progressMessage.text('100%');
589 this.$progressBar.outerWidth('100%');
590
591 // replace file icon
592 if (data.upload[0].icon) {
593 this.$iconCol
594 .html(
595 '<a href="#" class="t3js-contextmenutrigger" data-uid="'
596 + data.upload[0].id + '" data-table="sys_file">'
597 + data.upload[0].icon + '&nbsp;</span></a>'
598 );
599 }
600
601 if (this.dragUploader.irreObjectUid) {
602 DragUploader.addFileToIrre(
603 this.dragUploader.irreObjectUid,
604 data.upload[0]
605 );
606 setTimeout(
607 () => {
608 this.$row.remove();
609 if ($('tr', this.dragUploader.$fileList).length === 0) {
610 this.dragUploader.$fileList.hide();
611 this.dragUploader.$trigger.trigger('uploadSuccess', [this, data]);
612 }
613 },
614 3000);
615 } else {
616 setTimeout(
617 () => {
618 this.showFileInfo(data.upload[0]);
619 this.dragUploader.$trigger.trigger('uploadSuccess', [this, data]);
620 },
621 3000);
622 }
623 }
624 }
625
626 /**
627 * @param {UploadedFile} fileInfo
628 */
629 public showFileInfo(fileInfo: UploadedFile): void {
630 this.removeProgress();
631 // add spacing cells when clibboard and/or extended view is enabled
632 for (let i = 7; i < this.dragUploader.fileListColumnCount; i++) {
633 $('<td />').text('').appendTo(this.$row);
634 }
635 $('<td />').text(fileInfo.extension.toUpperCase()).appendTo(this.$row);
636 $('<td />').text(fileInfo.date).appendTo(this.$row);
637 $('<td />').text(DragUploader.fileSizeAsString(fileInfo.size)).appendTo(this.$row);
638 let permissions = '';
639 if (fileInfo.permissions.read) {
640 permissions += '<strong class="text-danger">' + TYPO3.lang['permissions.read'] + '</strong>';
641 }
642 if (fileInfo.permissions.write) {
643 permissions += '<strong class="text-danger">' + TYPO3.lang['permissions.write'] + '</strong>';
644 }
645 $('<td />').html(permissions).appendTo(this.$row);
646 $('<td />').text('-').appendTo(this.$row);
647 }
648
649 public checkAllowedExtensions(): boolean {
650 if (!this.dragUploader.filesExtensionsAllowed) {
651 return true;
652 }
653 const extension = this.file.name.split('.').pop();
654 const allowed = this.dragUploader.filesExtensionsAllowed.split(',');
655
656 return $.inArray(extension.toLowerCase(), allowed) !== -1;
657 }
658 }
659
660 class DragUploader {
661 private static options: DragUploaderOptions;
662 fileListColumnCount: number;
663 filesExtensionsAllowed: string;
664 fileDenyPattern: string;
665
666 public static fileSizeAsString(size: number): string {
667 const sizeKB: number = size / 1024;
668 let str = '';
669
670 if (sizeKB > 1024) {
671 str = (sizeKB / 1024).toFixed(1) + ' MB';
672 } else {
673 str = sizeKB.toFixed(1) + ' KB';
674 }
675 return str;
676 }
677
678 /**
679 * @param {number} irre_object
680 * @param {UploadedFile} file
681 */
682 public static addFileToIrre(irre_object: number, file: UploadedFile): void {
683 window.inline.delayedImportElement(
684 irre_object,
685 'sys_file',
686 file.uid,
687 'file'
688 );
689 }
690
691 public static init(): void {
692 const me = this;
693 const opts = me.options;
694
695 // register the jQuery plugin "DragUploaderPlugin"
696 $.fn.extend({
697 dragUploader: function (options?: DragUploaderOptions | string): JQuery {
698 return this.each((index: number, elem: HTMLElement): void => {
699 const $this = $(elem);
700 let data = $this.data('DragUploaderPlugin');
701 if (!data) {
702 $this.data('DragUploaderPlugin', (data = new DragUploaderPlugin(elem)));
703 }
704 if (typeof options === 'string') {
705 data[options]();
706 }
707 });
708 }
709 });
710
711 $(() => {
712 $('.t3js-drag-uploader').dragUploader(opts);
713 });
714 }
715
716 }
717
718 /**
719 * Function to apply the example plugin to the selected elements of a jQuery result.
720 */
721 interface DragUploaderFunction {
722 /**
723 * Apply the example plugin to the elements selected in the jQuery result.
724 *
725 * @param options Options to use for this application of the example plugin.
726 * @returns jQuery result.
727 */
728 (options: DragUploaderOptions): JQuery;
729 }
730
731 export const initialize = function(): void {
732 DragUploader.init();
733
734 // load required modules to hook in the post initialize function
735 if (
736 'undefined' !== typeof TYPO3.settings
737 && 'undefined' !== typeof TYPO3.settings.RequireJS
738 && 'undefined' !== typeof TYPO3.settings.RequireJS.PostInitializationModules
739 && 'undefined' !== typeof TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader']
740 ) {
741 $.each(
742 TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader'], (pos, moduleName) => {
743 require([moduleName]);
744 }
745 );
746 }
747 };
748
749 initialize();