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