typo3-typescript-sources 14.3.7 → 14.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/.github/workflows/sync-and-publish.yml +240 -191
  2. package/.last-synced-tag +1 -1
  3. package/README.md +13 -5
  4. package/adminpanel/admin-panel.ts +22 -22
  5. package/adminpanel/modules/cache.ts +3 -3
  6. package/backend/action-dispatcher.ts +8 -3
  7. package/backend/bookmark/bookmark-store.ts +5 -5
  8. package/backend/clipboard-panel.ts +2 -2
  9. package/backend/code-editor/element/code-mirror-element.ts +1 -1
  10. package/backend/code-editor/language/typoscript.ts +3 -3
  11. package/backend/code-editor/stream-parser/typoscript.ts +2 -2
  12. package/backend/color-picker.ts +2 -2
  13. package/backend/column-selector-button.ts +11 -8
  14. package/backend/context-help.ts +3 -3
  15. package/backend/context-menu.ts +8 -3
  16. package/backend/drag-tooltip.ts +9 -4
  17. package/backend/drag-uploader.ts +81 -89
  18. package/backend/element/draggable-resizable-element.ts +18 -12
  19. package/backend/element/icon-element.ts +2 -2
  20. package/backend/element/thumbnail-element.ts +2 -2
  21. package/backend/element-browser.ts +1 -1
  22. package/backend/event/consumer-scope.ts +2 -2
  23. package/backend/event/interaction-request-assignment.ts +1 -1
  24. package/backend/event/interaction-request-map.ts +3 -3
  25. package/backend/form-engine/container/flex-form-container-container.ts +2 -2
  26. package/backend/form-engine/container/flex-form-section-container.ts +1 -1
  27. package/backend/form-engine/container/inline-control-container.ts +14 -14
  28. package/backend/form-engine/element/abstract-sortable-select-items.ts +3 -3
  29. package/backend/form-engine/element/color-element.ts +1 -1
  30. package/backend/form-engine/element/datetime-element.ts +1 -1
  31. package/backend/form-engine/element/extra/char-counter.ts +2 -2
  32. package/backend/form-engine/element/extra/select-box-filter.ts +1 -1
  33. package/backend/form-engine/element/mfa-info-element.ts +2 -2
  34. package/backend/form-engine/element/online-media-form-element.ts +8 -4
  35. package/backend/form-engine/element/select-multiple-side-by-side-element.ts +2 -2
  36. package/backend/form-engine/element/select-tree.ts +3 -3
  37. package/backend/form-engine/element/slug-element.ts +7 -7
  38. package/backend/form-engine/element/suggest/result-container.ts +1 -1
  39. package/backend/form-engine/element/table-wizard-element.ts +4 -4
  40. package/backend/form-engine/inline-relation/ajax-dispatcher.ts +3 -3
  41. package/backend/form-engine-validation.ts +11 -5
  42. package/backend/form-engine.ts +19 -23
  43. package/backend/global-event-handler.ts +1 -1
  44. package/backend/hotkeys/hotkey-struct.ts +1 -1
  45. package/backend/hotkeys.ts +3 -3
  46. package/backend/image-manipulation.ts +19 -19
  47. package/backend/layout-module/column-restriction.ts +61 -0
  48. package/backend/layout-module/drag-drop.ts +16 -4
  49. package/backend/layout-module/paste.ts +8 -1
  50. package/backend/live-search/element/result/item/action/action-container.ts +1 -1
  51. package/backend/live-search/element/result/item/item-container.ts +4 -4
  52. package/backend/live-search/element/result/result-container.ts +5 -0
  53. package/backend/live-search/element/result/result-pagination.ts +1 -1
  54. package/backend/live-search/live-search.ts +2 -2
  55. package/backend/localization/label-provider.ts +4 -4
  56. package/backend/localization/localization-wizard.ts +4 -4
  57. package/backend/localization/steps/content-record-selection-step.ts +8 -8
  58. package/backend/localization/steps/handler-selection-step.ts +4 -4
  59. package/backend/localization/steps/mode-step.ts +4 -4
  60. package/backend/localization/steps/source-language-step.ts +4 -4
  61. package/backend/localization/steps/target-language-step.ts +4 -4
  62. package/backend/login-refresh.ts +1 -9
  63. package/backend/login.ts +2 -2
  64. package/backend/modal.ts +38 -5
  65. package/backend/module/iframe.ts +10 -0
  66. package/backend/module/router.ts +10 -0
  67. package/backend/module-menu.ts +3 -3
  68. package/backend/multi-record-selection-action.ts +1 -1
  69. package/backend/multi-record-selection-delete-action.ts +1 -1
  70. package/backend/multi-record-selection.ts +14 -14
  71. package/backend/multi-step-wizard.ts +9 -2
  72. package/backend/new-multiple-pages.ts +2 -2
  73. package/backend/new-record-wizard.ts +15 -15
  74. package/backend/notification.ts +90 -6
  75. package/backend/online-media.ts +1 -3
  76. package/backend/page-wizard/page-wizard.ts +2 -2
  77. package/backend/page-wizard/steps/doktype-step.ts +5 -5
  78. package/backend/pagetsconfig/pagetsconfig-includes.ts +1 -1
  79. package/backend/popover.ts +5 -5
  80. package/backend/record-download-button.ts +5 -6
  81. package/backend/recordlist.ts +11 -6
  82. package/backend/security/element/csp-reports.ts +8 -8
  83. package/backend/setup-module.ts +2 -2
  84. package/backend/sortable-table.ts +10 -4
  85. package/backend/storage/abstract-client-storage.ts +2 -2
  86. package/backend/storage/module-state-storage.ts +2 -2
  87. package/backend/tests/backend/utility/dom-helper-test.ts +2 -2
  88. package/backend/tests/fixtures/modal-autofocus.html +1 -0
  89. package/backend/tests/form-engine/element/extra/select-box-filter-test.ts +3 -3
  90. package/backend/tests/form-engine-validation-test.ts +1 -1
  91. package/backend/tests/modal-test.ts +91 -0
  92. package/backend/tests/notification-test.ts +176 -0
  93. package/backend/tests/tree/tree-test.ts +41 -0
  94. package/backend/tree/file-storage-browser.ts +1 -1
  95. package/backend/tree/file-storage-tree-container.ts +4 -4
  96. package/backend/tree/page-browser.ts +1 -1
  97. package/backend/tree/page-position-select.ts +1 -1
  98. package/backend/tree/page-tree-element.ts +9 -2
  99. package/backend/tree/page-tree.ts +7 -1
  100. package/backend/tree/tree-module-state.ts +5 -5
  101. package/backend/tree/tree.ts +15 -8
  102. package/backend/user-pass-login.ts +2 -2
  103. package/backend/utility/collapse-state-persister.ts +1 -1
  104. package/backend/utility/collapse-state-search.ts +6 -6
  105. package/backend/utility/dom-helper.ts +3 -3
  106. package/backend/utility.ts +6 -6
  107. package/backend/viewport/content-navigation.ts +5 -0
  108. package/backend/viewport/navigation-container.ts +1 -1
  109. package/backend/viewport/toolbar.ts +3 -3
  110. package/backend/window-manager.ts +2 -2
  111. package/backend/wizard/events/auto-advance-event.ts +6 -0
  112. package/backend/wizard/events/before-next-step-event.ts +6 -0
  113. package/backend/wizard/events/step-summary-event.ts +6 -0
  114. package/backend/wizard/move-content-element.ts +1 -1
  115. package/backend/wizard/wizard.ts +8 -10
  116. package/belog/backend-log.ts +2 -2
  117. package/beuser/permissions.ts +2 -2
  118. package/core/ajax/ajax-response.ts +1 -1
  119. package/core/ajax/input-transformer.ts +1 -1
  120. package/core/event/debounce-event.ts +4 -4
  121. package/core/event/event-interface.ts +3 -3
  122. package/core/event/regular-event.ts +7 -7
  123. package/core/event/request-animation-frame-event.ts +4 -4
  124. package/core/event/throttle-event.ts +4 -4
  125. package/core/java-script-item-processor.ts +6 -0
  126. package/core/referrer-refresh.ts +2 -2
  127. package/core/security-utility.ts +1 -1
  128. package/dashboard/dashboard-wizard.ts +2 -2
  129. package/dashboard/dashboard.ts +8 -13
  130. package/dashboard/wizard/steps/preset-step.ts +3 -3
  131. package/extensionmanager/main.ts +7 -7
  132. package/filelist/browse-files.ts +1 -1
  133. package/filelist/browse-folders.ts +2 -2
  134. package/filelist/file-list-dragdrop.ts +5 -5
  135. package/filelist/file-list-rename-handler.ts +3 -6
  136. package/filelist/file-list-replace-handler.ts +3 -6
  137. package/filelist/file-list-transfer-handler.ts +2 -2
  138. package/filelist/file-list.ts +8 -9
  139. package/form/backend/form-editor/component/date-editor.ts +4 -0
  140. package/form/backend/form-editor/component/form-element-selector.ts +4 -0
  141. package/form/backend/form-editor/inspector-component.ts +6 -6
  142. package/form/backend/form-editor/mediator.ts +36 -36
  143. package/form/backend/form-editor/modals-component.ts +115 -6
  144. package/form/backend/form-editor/stage-component.ts +4 -4
  145. package/form/backend/form-editor/view-model.ts +7 -7
  146. package/form/backend/form-manager/view-model.ts +5 -5
  147. package/form/backend/form-wizard/form-wizard.ts +2 -2
  148. package/form/backend/form-wizard/steps/storage-step.ts +1 -1
  149. package/impexp/import-export.ts +1 -1
  150. package/info/translation-status.ts +2 -2
  151. package/install/ajax/ajax-queue.ts +1 -1
  152. package/install/module/abstract-interactable-module.ts +2 -2
  153. package/install/module/environment/image-processing.ts +4 -4
  154. package/install/module/environment/mail-test.ts +2 -2
  155. package/install/module/maintenance/clear-tables.ts +3 -3
  156. package/install/module/maintenance/clear-typo3temp-files.ts +8 -8
  157. package/install/module/maintenance/create-admin.ts +3 -3
  158. package/install/module/maintenance/database-analyzer.ts +9 -7
  159. package/install/module/maintenance/language-packs.ts +4 -4
  160. package/install/module/maintenance/reference-index.ts +1 -1
  161. package/install/module/settings/change-install-tool-password.ts +1 -1
  162. package/install/module/settings/extension-configuration.ts +5 -5
  163. package/install/module/settings/features.ts +1 -1
  164. package/install/module/settings/local-configuration.ts +5 -5
  165. package/install/module/settings/presets.ts +2 -2
  166. package/install/module/settings/system-maintainer.ts +1 -1
  167. package/install/module/upgrade/extension-scanner.ts +5 -5
  168. package/install/module/upgrade/tca-migrations-check.ts +1 -1
  169. package/install/module/upgrade/upgrade-docs.ts +16 -16
  170. package/install/module/upgrade/upgrade-wizards.ts +7 -8
  171. package/install/renderable/language-packs.ts +8 -2
  172. package/lowlevel/query-generator.ts +2 -2
  173. package/package.json +2 -2
  174. package/redirects/event-handler.ts +6 -0
  175. package/rte-ckeditor/ckeditor5.ts +7 -3
  176. package/rte-ckeditor/legacy/ckeditor5-bundle.ts +0 -1
  177. package/rte-ckeditor/plugin/typo3-link.ts +7 -9
  178. package/rte-ckeditor/rte-link-browser.ts +1 -1
  179. package/rte-ckeditor/tests/ckeditor5-test.ts +68 -0
  180. package/scheduler/form-engine/element/timing-options-element.ts +1 -1
  181. package/scheduler/scheduler-add-group.ts +4 -12
  182. package/scheduler/scheduler.ts +2 -2
  183. package/styleguide/checkbox-demo.ts +1 -1
  184. package/tstemplate/template-analyzer.ts +1 -1
  185. package/viewpage/main.ts +1 -1
  186. package/workspaces/backend.ts +5 -5
  187. package/workspaces/preview.ts +41 -2
  188. package/workspaces/renderable/record-table.ts +95 -78
  189. package/workspaces/renderable/send-to-stage-form.ts +1 -1
@@ -258,7 +258,7 @@ export default class DragUploader {
258
258
  if (node.matches('.t3js-drag-uploader')) {
259
259
  new DragUploader(node);
260
260
  } else {
261
- node.querySelectorAll('.t3js-drag-uploader').forEach((element: HTMLElement): void => {
261
+ node.querySelectorAll<HTMLElement>('.t3js-drag-uploader').forEach((element): void => {
262
262
  new DragUploader(element);
263
263
  });
264
264
  }
@@ -269,7 +269,7 @@ export default class DragUploader {
269
269
  observer.observe(document, { childList: true, subtree: true });
270
270
 
271
271
  DocumentService.ready().then((): void => {
272
- document.querySelectorAll('.t3js-drag-uploader').forEach((element: HTMLElement): void => {
272
+ document.querySelectorAll<HTMLElement>('.t3js-drag-uploader').forEach((element): void => {
273
273
  new DragUploader(element);
274
274
  });
275
275
  });
@@ -371,7 +371,21 @@ export default class DragUploader {
371
371
  * @param {FileList} files
372
372
  */
373
373
  public processFiles(files: FileList): void {
374
- this.queueLength = files.length;
374
+ const validFiles: File[] = [];
375
+ for (const file of Array.from(files)) {
376
+ const error = this.validateFile(file);
377
+ if (error === null) {
378
+ validFiles.push(file);
379
+ } else {
380
+ Notification.error(coreLabels.get('file_upload.notUploaded.title'), error);
381
+ }
382
+ }
383
+ this.fileInput.value = '';
384
+ if (validFiles.length === 0) {
385
+ return;
386
+ }
387
+
388
+ this.queueLength = validFiles.length;
375
389
 
376
390
  if (this.fileList.parentElement.hasAttribute('hidden')) {
377
391
  // Show the filelist (table)
@@ -385,11 +399,11 @@ export default class DragUploader {
385
399
  this.overallProgressBar = document.createElement('typo3-backend-progress-bar');
386
400
  document.body.appendChild(this.overallProgressBar);
387
401
  this.overallProgressBar.start();
388
- this.percentagePerFile = 1 / files.length;
402
+ this.percentagePerFile = 1 / validFiles.length;
389
403
 
390
404
  // Check for each file if is already exist before adding it to the queue
391
405
  const ajaxCalls: Promise<void>[] = [];
392
- Array.from(files).forEach((file: File) => {
406
+ validFiles.forEach((file) => {
393
407
  const request = new AjaxRequest(TYPO3.settings.ajaxUrls.file_exists).withQueryArguments({
394
408
  fileName: file.name,
395
409
  fileTarget: this.target,
@@ -418,8 +432,6 @@ export default class DragUploader {
418
432
  this.overallProgressBar.done();
419
433
  }
420
434
  });
421
-
422
- this.fileInput.value = '';
423
435
  }
424
436
 
425
437
  public fileInDropzone = (): void => {
@@ -594,7 +606,7 @@ export default class DragUploader {
594
606
  this.askForOverride[index].action = <Action>select.value;
595
607
  }
596
608
  } else {
597
- modal.querySelectorAll('.t3js-actions').forEach((select: HTMLSelectElement) => select.disabled = false);
609
+ modal.querySelectorAll<HTMLSelectElement>('.t3js-actions').forEach((select) => select.disabled = false);
598
610
  }
599
611
  }).delegateTo(modal, '.t3js-actions-all');
600
612
 
@@ -631,6 +643,37 @@ export default class DragUploader {
631
643
  document.dispatchEvent(new CustomEvent(uploadFinishedEventName));
632
644
  });
633
645
  }
646
+
647
+ /**
648
+ * @returns the reason why the file cannot be uploaded, null if it can be uploaded
649
+ */
650
+ private validateFile(file: File): string | null {
651
+ const extension = file.name.split('.').pop().toLowerCase();
652
+ if (this.maxFileSize > 0 && file.size > this.maxFileSize) {
653
+ return coreLabels.get('file_upload.maxFileSizeExceeded', {
654
+ '0': file.name,
655
+ '1': FormatUtility.fileSizeAsString(this.maxFileSize),
656
+ });
657
+ }
658
+ if (this.fileDenyPattern && file.name.match(this.fileDenyPattern)) {
659
+ return coreLabels.get('file_upload.fileNotAllowed', {
660
+ '0': file.name,
661
+ });
662
+ }
663
+ if (this.filesExtensionsAllowed && !this.filesExtensionsAllowed.split(',').includes(extension)) {
664
+ return coreLabels.get('file_upload.notUploaded.extensionExpected', {
665
+ '0': file.name,
666
+ '1': this.filesExtensionsAllowed.split(',').join(', '),
667
+ });
668
+ }
669
+ if (this.filesExtensionsDisallowed && this.filesExtensionsDisallowed.split(',').includes(extension)) {
670
+ return coreLabels.get('file_upload.notUploaded.extensionDisallowed', {
671
+ '0': file.name,
672
+ '1': extension,
673
+ });
674
+ }
675
+ return null;
676
+ }
634
677
  }
635
678
 
636
679
  class FileQueueItem {
@@ -697,72 +740,41 @@ class FileQueueItem {
697
740
  // set dummy file icon
698
741
  this.iconCol.innerHTML = '<typo3-backend-icon identifier="mimetypes-other-other" />';
699
742
 
700
- // check file size
701
- if (this.dragUploader.maxFileSize > 0 && this.file.size > this.dragUploader.maxFileSize) {
702
- this.updateMessage(coreLabels.get('file_upload.maxFileSizeExceeded', {
703
- '0': this.file.name,
704
- '1': FormatUtility.fileSizeAsString(this.dragUploader.maxFileSize),
705
- }));
706
- this.progressBar.value = 100;
707
- this.progressBar.severity = SeverityEnum.error;
708
-
709
- // check filename/extension against deny pattern
710
- } else if (this.dragUploader.fileDenyPattern && this.file.name.match(this.dragUploader.fileDenyPattern)) {
711
- this.updateMessage(coreLabels.get('file_upload.fileNotAllowed', {
712
- '0': this.file.name,
713
- }));
714
- this.progressBar.value = 100;
715
- this.progressBar.severity = SeverityEnum.error;
716
-
717
- } else if (this.isProhibitedByAllowedExtensionsList()) {
718
- this.updateMessage(coreLabels.get('file_upload.fileExtensionExpected', {
719
- '0': this.dragUploader.filesExtensionsAllowed,
720
- }));
721
- this.progressBar.value = 100;
722
- this.progressBar.severity = SeverityEnum.error;
723
- } else if (this.isProhibitedByDisallowedExtensionsList()) {
724
- this.updateMessage(coreLabels.get('file_upload.fileExtensionDisallowed', {
725
- '0': this.dragUploader.filesExtensionsDisallowed,
726
- }));
727
- this.progressBar.value = 100;
728
- this.progressBar.severity = SeverityEnum.error;
729
- } else {
730
- this.updateMessage('- ' + FormatUtility.fileSizeAsString(this.file.size));
731
-
732
- const formData = new FormData();
733
- formData.append('data[upload][1][target]', this.dragUploader.target);
734
- formData.append('data[upload][1][data]', '1');
735
- formData.append('overwriteExistingFiles', this.override);
736
- formData.append('redirect', '');
737
- formData.append('upload_1', this.file);
738
-
739
- // We use XMLHttpRequest as we need the `progress` event which isn't supported by fetch()
740
- const xhr = new XMLHttpRequest();
741
- xhr.onreadystatechange = (): void => {
742
- if (xhr.readyState === XMLHttpRequest.DONE) {
743
- if (xhr.status === 200) {
744
- try {
745
- const response = JSON.parse(xhr.responseText);
746
- if (!response.hasErrors) {
747
- this.uploadSuccess(response);
748
- } else {
749
- this.uploadError(xhr);
750
- }
751
- }
752
- catch {
753
- // In case JSON can not be parsed, the upload failed due to server errors,
754
- // e.g. "POST Content-Length exceeds limit". Just handle as upload error.
743
+ this.updateMessage('- ' + FormatUtility.fileSizeAsString(this.file.size));
744
+
745
+ const formData = new FormData();
746
+ formData.append('data[upload][1][target]', this.dragUploader.target);
747
+ formData.append('data[upload][1][data]', '1');
748
+ formData.append('overwriteExistingFiles', this.override);
749
+ formData.append('redirect', '');
750
+ formData.append('upload_1', this.file);
751
+
752
+ // We use XMLHttpRequest as we need the `progress` event which isn't supported by fetch()
753
+ const xhr = new XMLHttpRequest();
754
+ xhr.onreadystatechange = (): void => {
755
+ if (xhr.readyState === XMLHttpRequest.DONE) {
756
+ if (xhr.status === 200) {
757
+ try {
758
+ const response = JSON.parse(xhr.responseText);
759
+ if (!response.hasErrors) {
760
+ this.uploadSuccess(response);
761
+ } else {
755
762
  this.uploadError(xhr);
756
763
  }
757
- } else {
764
+ }
765
+ catch {
766
+ // In case JSON can not be parsed, the upload failed due to server errors,
767
+ // e.g. "POST Content-Length exceeds limit". Just handle as upload error.
758
768
  this.uploadError(xhr);
759
769
  }
770
+ } else {
771
+ this.uploadError(xhr);
760
772
  }
761
- };
762
- xhr.upload.addEventListener('progress', (e: ProgressEvent) => this.updateProgress(e));
763
- xhr.open('POST', TYPO3.settings.ajaxUrls.file_process);
764
- xhr.send(formData);
765
- }
773
+ }
774
+ };
775
+ xhr.upload.addEventListener('progress', (e: ProgressEvent) => this.updateProgress(e));
776
+ xhr.open('POST', TYPO3.settings.ajaxUrls.file_process);
777
+ xhr.send(formData);
766
778
  }
767
779
 
768
780
  /**
@@ -925,26 +937,6 @@ class FileQueueItem {
925
937
  this.row.append(document.createElement('td'));
926
938
  }
927
939
  }
928
-
929
- public isProhibitedByAllowedExtensionsList(): boolean {
930
- if (!this.dragUploader.filesExtensionsAllowed) {
931
- return false;
932
- }
933
- const extension = this.file.name.split('.').pop();
934
- const allowed = this.dragUploader.filesExtensionsAllowed.split(',');
935
-
936
- return !allowed.includes(extension.toLowerCase());
937
- }
938
-
939
- public isProhibitedByDisallowedExtensionsList(): boolean {
940
- if (!this.dragUploader.filesExtensionsDisallowed) {
941
- return false;
942
- }
943
- const extension = this.file.name.split('.').pop();
944
- const disallowed = this.dragUploader.filesExtensionsDisallowed.split(',');
945
-
946
- return disallowed.includes(extension.toLowerCase());
947
- }
948
940
  }
949
941
 
950
942
  DragUploader.init();
@@ -94,22 +94,22 @@ export class DraggableResizableElement extends LitElement {
94
94
 
95
95
  public override connectedCallback(): void {
96
96
  super.connectedCallback();
97
- this.pointerEventNames.pointerDown.forEach((name: string): void =>
98
- document.addEventListener(name, this.handleStart, true));
99
- this.pointerEventNames.pointerMove.forEach((name: string): void =>
100
- document.addEventListener(name, this.handleUpdate, true));
101
- this.pointerEventNames.pointerUp.forEach((name: string): void =>
102
- document.addEventListener(name, this.handleFinish, true));
97
+ this.pointerEventNames.pointerDown.forEach((name): void =>
98
+ document.addEventListener(name, this.handleStart as EventListener, true));
99
+ this.pointerEventNames.pointerMove.forEach((name): void =>
100
+ document.addEventListener(name, this.handleUpdate as EventListener, true));
101
+ this.pointerEventNames.pointerUp.forEach((name): void =>
102
+ document.addEventListener(name, this.handleFinish as EventListener, true));
103
103
  }
104
104
 
105
105
  public override disconnectedCallback(): void {
106
106
  super.disconnectedCallback();
107
- this.pointerEventNames.pointerDown.forEach((name: string): void =>
108
- document.removeEventListener(name, this.handleStart, true));
109
- this.pointerEventNames.pointerMove.forEach((name: string): void =>
110
- document.removeEventListener(name, this.handleUpdate, true));
111
- this.pointerEventNames.pointerUp.forEach((name: string): void =>
112
- document.removeEventListener(name, this.handleFinish, true));
107
+ this.pointerEventNames.pointerDown.forEach((name): void =>
108
+ document.removeEventListener(name, this.handleStart as EventListener, true));
109
+ this.pointerEventNames.pointerMove.forEach((name): void =>
110
+ document.removeEventListener(name, this.handleUpdate as EventListener, true));
111
+ this.pointerEventNames.pointerUp.forEach((name): void =>
112
+ document.removeEventListener(name, this.handleFinish as EventListener, true));
113
113
  }
114
114
 
115
115
  protected override render(): TemplateResult {
@@ -246,4 +246,10 @@ declare global {
246
246
  interface HTMLElementTagNameMap {
247
247
  'typo3-backend-draggable-resizable': DraggableResizableElement;
248
248
  }
249
+
250
+ interface HTMLElementEventMap {
251
+ 'draggable-resizable-started': DraggableResizableEvent;
252
+ 'draggable-resizable-updated': DraggableResizableEvent;
253
+ 'draggable-resizable-finished': DraggableResizableEvent;
254
+ }
249
255
  }
@@ -48,10 +48,10 @@ export class IconElement extends LitElement {
48
48
  @property({ type: String }) raw?: string = null;
49
49
 
50
50
  private readonly iconTask = new Task(this, {
51
- task: async ([identifier, size, overlay, state, markup]: [string, Sizes, States, string, MarkupIdentifiers], { signal }): Promise<string> => {
51
+ task: async ([identifier, size, overlay, state, markup], { signal }): Promise<string> => {
52
52
  return await Icons.getIcon(identifier, size, overlay, state, markup, signal);
53
53
  },
54
- args: () => [this.identifier, this.size, this.overlay, this.state, this.markup]
54
+ args: () => [this.identifier, this.size, this.overlay, this.state, this.markup] as const
55
55
  });
56
56
 
57
57
  protected override render(): TemplateResult | symbol {
@@ -34,7 +34,7 @@ export class ThumbnailElement extends LitElement {
34
34
  @property({ type: Number, reflect: true }) height: number;
35
35
 
36
36
  private readonly thumbnailTask = new Task(this, {
37
- task: async ([url, size, keepAspectRatio, width, height]: [string, ThumbnailSizeType, boolean, number, number]): Promise<TemplateResult> => {
37
+ task: async ([url, size, keepAspectRatio, width, height]): Promise<TemplateResult> => {
38
38
  const thumbnailUrl = new URL(url, window.origin);
39
39
  thumbnailUrl.searchParams.set('size', size);
40
40
  thumbnailUrl.searchParams.set('keepAspectRatio', keepAspectRatio ? '1' : '0');
@@ -60,7 +60,7 @@ export class ThumbnailElement extends LitElement {
60
60
  }));
61
61
  return html`${img}`;
62
62
  },
63
- args: () => [this.url, this.size, this.keepAspectRatio, this.width, this.height]
63
+ args: () => [this.url, this.size, this.keepAspectRatio, this.width, this.height] as const
64
64
  });
65
65
 
66
66
  protected override createRenderRoot(): HTMLElement | ShadowRoot {
@@ -79,7 +79,7 @@ class ElementBrowser {
79
79
  const isInModalFrame = typeof window.frames !== 'undefined'
80
80
  && typeof window.frames.frameElement !== 'undefined'
81
81
  && window.frames.frameElement.classList.contains('t3js-modal-iframe');
82
- const otherModalFrames = Array.from(top.frames || []).filter((item: Window) => {
82
+ const otherModalFrames = Array.from(top.frames || []).filter((item) => {
83
83
  try {
84
84
  return typeof item.frameElement !== 'undefined'
85
85
  && item.frameElement.classList.contains('t3js-modal-iframe')
@@ -33,14 +33,14 @@ class ConsumerScope {
33
33
 
34
34
  public detach(consumer: Consumable): void {
35
35
  this.consumers = this.consumers.filter(
36
- (currentConsumer: Consumable) => currentConsumer !== consumer,
36
+ (currentConsumer) => currentConsumer !== consumer,
37
37
  );
38
38
  }
39
39
 
40
40
  public async invoke(request: InteractionRequest): Promise<void> {
41
41
  const promises: Promise<void>[] = [];
42
42
  this.consumers.forEach(
43
- (consumer: Consumable) => {
43
+ (consumer) => {
44
44
  const promise: Promise<void> = consumer.consume.call(consumer, request);
45
45
  if (promise) {
46
46
  promises.push(promise);
@@ -20,5 +20,5 @@ export interface PromiseControls<T> {
20
20
 
21
21
  export interface InteractionRequestAssignment {
22
22
  request: InteractionRequest;
23
- deferreds: PromiseControls<unknown>[];
23
+ deferreds: PromiseControls<never>[];
24
24
  }
@@ -17,7 +17,7 @@ import type { InteractionRequestAssignment, PromiseControls } from './interactio
17
17
  class InteractionRequestMap {
18
18
  private assignments: InteractionRequestAssignment[] = [];
19
19
 
20
- public attachFor(request: InteractionRequest, deferred: PromiseControls<unknown>): void {
20
+ public attachFor(request: InteractionRequest, deferred: PromiseControls<never>): void {
21
21
  let targetAssignment: InteractionRequestAssignment = this.getFor(request);
22
22
  if (targetAssignment === null) {
23
23
  targetAssignment = { request, deferreds: [] };
@@ -29,14 +29,14 @@ class InteractionRequestMap {
29
29
  public detachFor(request: InteractionRequest): void {
30
30
  const targetAssignment = this.getFor(request);
31
31
  this.assignments = this.assignments.filter(
32
- (assignment: InteractionRequestAssignment) => assignment === targetAssignment,
32
+ (assignment) => assignment === targetAssignment,
33
33
  );
34
34
  }
35
35
 
36
36
  public getFor(triggerEvent: InteractionRequest): InteractionRequestAssignment | null {
37
37
  let targetAssignment: InteractionRequestAssignment = null;
38
38
  this.assignments.some(
39
- (assignment: InteractionRequestAssignment) => {
39
+ (assignment) => {
40
40
  if (assignment.request === triggerEvent) {
41
41
  targetAssignment = assignment;
42
42
  return true;
@@ -135,7 +135,7 @@ class FlexFormContainerContainer {
135
135
  }
136
136
 
137
137
  private registerPanelToggle(): void {
138
- ['hide.bs.collapse', 'show.bs.collapse'].forEach((eventName: string): void => {
138
+ ['hide.bs.collapse', 'show.bs.collapse'].forEach((eventName): void => {
139
139
  new RegularEvent(eventName, (e: Event): void => {
140
140
  if (e.target !== this.containerContent) {
141
141
  return;
@@ -147,7 +147,7 @@ class FlexFormContainerContainer {
147
147
  }
148
148
 
149
149
  private registerPreviewUpdate(): void {
150
- ['input', 'change'].forEach((eventName: string): void => {
150
+ ['input', 'change'].forEach((eventName): void => {
151
151
  new RegularEvent(eventName, (): void => {
152
152
  this.generatePreview();
153
153
  }).delegateTo(this.containerContent, 'input[type="text"], textarea');
@@ -98,7 +98,7 @@ class FlexFormSectionContainer {
98
98
 
99
99
  private readonly updateSorting = (e: Sortable.SortableEvent): void => {
100
100
  const actionFields: NodeListOf<HTMLInputElement> = this.container.querySelectorAll(Selectors.actionFieldSelector);
101
- actionFields.forEach((element: HTMLInputElement, key: number): void => {
101
+ actionFields.forEach((element, key): void => {
102
102
  element.value = key.toString();
103
103
  });
104
104
 
@@ -161,7 +161,7 @@ class InlineControlContainer extends HTMLElement {
161
161
  }
162
162
 
163
163
  private static getValuesFromHashMap(hashmap: UniqueDefinitionCollection): Array<any> {
164
- return Object.keys(hashmap).map((key: string) => hashmap[key]);
164
+ return Object.keys(hashmap).map((key) => hashmap[key]);
165
165
  }
166
166
 
167
167
  private static selectOptionValueExists(selectElement: HTMLSelectElement, value: string): boolean {
@@ -449,10 +449,10 @@ class InlineControlContainer extends HTMLElement {
449
449
  * @param {string} afterUid
450
450
  */
451
451
  private async importRecord(params: Array<any>, afterUid?: string): Promise<void> {
452
- return this.ajaxDispatcher.send(
452
+ return this.ajaxDispatcher.send<InlineResponseInterface>(
453
453
  this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint(this.endpoints.create)),
454
454
  params,
455
- ).then(async (response: InlineResponseInterface): Promise<void> => {
455
+ ).then(async (response): Promise<void> => {
456
456
  if (this.isBelowMax()) {
457
457
  this.createRecord(
458
458
  response.compilerInput.uid,
@@ -553,10 +553,10 @@ class InlineControlContainer extends HTMLElement {
553
553
  return;
554
554
  }
555
555
 
556
- this.ajaxDispatcher.send(
556
+ this.ajaxDispatcher.send<InlineResponseInterface>(
557
557
  this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint(this.endpoints.synchronizelocalize)),
558
558
  [this.objectGroup, targetElement.dataset.type],
559
- ).then(async (response: InlineResponseInterface): Promise<void> => {
559
+ ).then(async (response): Promise<void> => {
560
560
  this.recordsContainer.insertAdjacentHTML('beforeend', response.data);
561
561
 
562
562
  const objectIdPrefix = this.objectGroup + Separators.structureSeparator;
@@ -617,9 +617,9 @@ class InlineControlContainer extends HTMLElement {
617
617
 
618
618
  if (!isLoading) {
619
619
  const ajaxRequest = this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint(this.endpoints.details));
620
- const request = this.ajaxDispatcher.send(ajaxRequest, [objectId]);
620
+ const request = this.ajaxDispatcher.send<InlineResponseInterface>(ajaxRequest, [objectId]);
621
621
 
622
- request.then(async (response: InlineResponseInterface): Promise<void> => {
622
+ request.then(async (response): Promise<void> => {
623
623
  delete this.requestQueue[objectId];
624
624
  delete this.progressQueue[objectId];
625
625
 
@@ -762,7 +762,7 @@ class InlineControlContainer extends HTMLElement {
762
762
  const currentRecordContainer = this.getRecordContainer(objectId);
763
763
  const recordUid = currentRecordContainer.dataset.objectUid;
764
764
  const recordListContainer = this.recordsContainer;
765
- const records = Array.from(recordListContainer.children).map((child: HTMLElement) => child.dataset.objectUid);
765
+ const records = Array.from(recordListContainer.children as HTMLCollectionOf<HTMLElement>).map((child) => child.dataset.objectUid);
766
766
  const position = records.indexOf(recordUid);
767
767
  let isChanged = false;
768
768
 
@@ -795,8 +795,8 @@ class InlineControlContainer extends HTMLElement {
795
795
  }
796
796
 
797
797
  const recordListContainer = this.recordsContainer;
798
- const records = Array.from(recordListContainer.querySelectorAll(selector`[data-object-parent-group="${this.objectGroup}"][data-placeholder-record="0"]`))
799
- .map((child: HTMLElement) => child.dataset.objectUid);
798
+ const records = Array.from(recordListContainer.querySelectorAll<HTMLElement>(selector`[data-object-parent-group="${this.objectGroup}"][data-placeholder-record="0"]`))
799
+ .map((child) => child.dataset.objectUid);
800
800
 
801
801
  (<HTMLInputElement>formField).value = records.join(',');
802
802
  FormEngine.markFieldAsChanged(formField);
@@ -841,12 +841,12 @@ class InlineControlContainer extends HTMLElement {
841
841
  * @param {boolean} visible
842
842
  */
843
843
  private toggleContainerControls(visible: boolean): void {
844
- const controlContainer = this.querySelectorAll(
844
+ const controlContainer = this.querySelectorAll<HTMLElement>(
845
845
  ':scope > ' + Selectors.controlContainer
846
846
  );
847
- controlContainer.forEach((container: HTMLElement): void => {
847
+ controlContainer.forEach((container): void => {
848
848
  const controlContainerButtons = container.querySelectorAll<HTMLButtonElement | HTMLAnchorElement>('button, a');
849
- controlContainerButtons.forEach((button: HTMLButtonElement | HTMLAnchorElement): void => {
849
+ controlContainerButtons.forEach((button): void => {
850
850
  button.hidden = !visible;
851
851
  });
852
852
  });
@@ -912,7 +912,7 @@ class InlineControlContainer extends HTMLElement {
912
912
  return;
913
913
  }
914
914
 
915
- records.forEach((recordUid: string, index: number): void => {
915
+ records.forEach((recordUid, index): void => {
916
916
  const recordContainer = this.getRecordContainer(objectId + Separators.structureSeparator + recordUid);
917
917
  if (recordContainer === null) {
918
918
  return;
@@ -25,7 +25,7 @@ export abstract class AbstractSortableSelectItems {
25
25
  * @param {HTMLSelectElement} fieldElement
26
26
  */
27
27
  private static moveOptionToTop(fieldElement: HTMLSelectElement): void {
28
- Array.from(fieldElement.querySelectorAll(':checked')).reverse().forEach((optionEl: HTMLOptionElement): void => {
28
+ Array.from(fieldElement.querySelectorAll<HTMLOptionElement>(':checked')).reverse().forEach((optionEl): void => {
29
29
  fieldElement.insertBefore(optionEl, fieldElement.firstElementChild);
30
30
  });
31
31
  }
@@ -36,7 +36,7 @@ export abstract class AbstractSortableSelectItems {
36
36
  * @param {HTMLSelectElement} fieldElement
37
37
  */
38
38
  private static moveOptionToBottom(fieldElement: HTMLSelectElement): void {
39
- fieldElement.querySelectorAll(':checked').forEach((optionEl: HTMLOptionElement): void => {
39
+ fieldElement.querySelectorAll<HTMLOptionElement>(':checked').forEach((optionEl): void => {
40
40
  fieldElement.insertBefore(optionEl, null);
41
41
  });
42
42
  }
@@ -85,7 +85,7 @@ export abstract class AbstractSortableSelectItems {
85
85
  */
86
86
  private static removeOption(fieldElement: HTMLSelectElement, availableFieldElement: HTMLSelectElement): void {
87
87
  const previousSelectIndex = fieldElement.selectedIndex;
88
- fieldElement.querySelectorAll(':checked').forEach((option: HTMLOptionElement): void => {
88
+ fieldElement.querySelectorAll<HTMLOptionElement>(':checked').forEach((option): void => {
89
89
  const originalOption = <HTMLOptionElement>availableFieldElement.querySelector(selector`option[value="${option.value}"]`);
90
90
  if (originalOption !== null) {
91
91
  originalOption.classList.remove('hidden');
@@ -72,7 +72,7 @@ class ColorElement extends HTMLElement {
72
72
  FormEngineValidation.validateField(e.target as HTMLInputElement);
73
73
  FormEngine.markFieldAsChanged(e.target as HTMLInputElement);
74
74
 
75
- document.querySelectorAll('.module-docheader-bar .btn').forEach((btn: HTMLButtonElement): void => {
75
+ document.querySelectorAll<HTMLButtonElement>('.module-docheader-bar .btn').forEach((btn): void => {
76
76
  btn.classList.remove('disabled');
77
77
  btn.disabled = false;
78
78
  });
@@ -56,7 +56,7 @@ class DatetimeElement extends HTMLElement {
56
56
  FormEngineValidation.validateField(e.target as HTMLInputElement);
57
57
  FormEngine.markFieldAsChanged(e.target as HTMLInputElement);
58
58
 
59
- document.querySelectorAll('.module-docheader-bar .btn').forEach((btn: HTMLButtonElement): void => {
59
+ document.querySelectorAll<HTMLButtonElement>('.module-docheader-bar .btn').forEach((btn): void => {
60
60
  btn.classList.remove('disabled');
61
61
  btn.disabled = false;
62
62
  });
@@ -73,11 +73,11 @@ export class CharCounter extends LitElement {
73
73
  this.targetElement.removeEventListener('blur', this.onBlur);
74
74
  }
75
75
 
76
- private readonly onInput = (e: InputEvent): void => {
76
+ private readonly onInput = (e: Event): void => {
77
77
  this.determineRemainingCharacters(e.target as HTMLInputElement|HTMLTextAreaElement);
78
78
  };
79
79
 
80
- private readonly onFocus = (e: FocusEvent): void => {
80
+ private readonly onFocus = (e: Event): void => {
81
81
  this.determineRemainingCharacters(e.target as HTMLInputElement|HTMLTextAreaElement);
82
82
  this.hidden = false;
83
83
  };
@@ -82,7 +82,7 @@ class SelectBoxFilter {
82
82
  // A plain substring match, no RegExp involved, therefore nothing to escape either
83
83
  const foldedFilterText = TextNormalizer.foldCaseAndDiacritics(TextNormalizer.normalizeInvisibleCharacters(filterText));
84
84
 
85
- this.availableOptions.forEach((option: HTMLOptionElement, index: number): void => {
85
+ this.availableOptions.forEach((option, index): void => {
86
86
  option.hidden = filterText.length > 0 && !this.foldedLabels[index].includes(foldedFilterText);
87
87
  SelectBoxFilter.toggleOptGroup(option);
88
88
  });
@@ -71,7 +71,7 @@ class MfaInfoElement {
71
71
  this.prepareDeactivateRequest(this.deactivteMfaButton);
72
72
  }).bindTo(this.deactivteMfaButton);
73
73
 
74
- this.deactivteProviderButtons.forEach((buttonElement: HTMLButtonElement): void => {
74
+ this.deactivteProviderButtons.forEach((buttonElement): void => {
75
75
  new RegularEvent('click', (e: Event): void => {
76
76
  e.preventDefault();
77
77
  this.prepareDeactivateRequest(buttonElement);
@@ -120,7 +120,7 @@ class MfaInfoElement {
120
120
  }).then(async (response: AjaxResponse): Promise<void> => {
121
121
  const data: Response = await response.resolve();
122
122
  if (data.status.length > 0) {
123
- data.status.forEach((status: Status): void => {
123
+ data.status.forEach((status): void => {
124
124
  if (data.success) {
125
125
  Notification.success(status.title, status.message);
126
126
  } else {
@@ -30,14 +30,14 @@ export class OnlineMediaFormElement extends LitElement {
30
30
 
31
31
  protected override render(): TemplateResult {
32
32
  return html`
33
- <form @submit="${ this.dispatchSubmitEvent }">
33
+ <form id="online-media-form" @submit="${ this.dispatchSubmitEvent }">
34
34
  <div class="form-control-wrap">
35
- <input type="text" class="form-control" name="online-media-url" placeholder="${this.placeholder}" required>
35
+ <input type="text" class="form-control" name="online-media-url" placeholder="${this.placeholder}" required autofocus>
36
36
  <div class="form-text">
37
37
  ${this.allowedExtensionsHelpText}<br>
38
38
  <ul class="badge-list">
39
- ${this.allowedExtensions.split(',').map((ext: string) => html`
40
- <li><span class="badge badge-secondary">${ext.trim().toUpperCase()}</span></li>
39
+ ${this.allowedExtensions.split(',').map((ext) => html`
40
+ <li><span class="badge badge-secondary">${ext.trim().toLowerCase()}</span></li>
41
41
  `)}
42
42
  </ul>
43
43
  </div>
@@ -62,4 +62,8 @@ declare global {
62
62
  interface HTMLElementTagNameMap {
63
63
  'typo3-backend-formengine-online-media-form': OnlineMediaFormElement;
64
64
  }
65
+
66
+ interface HTMLElementEventMap {
67
+ 'typo3:formengine:online-media-added': CustomEvent<{ file: number }>;
68
+ }
65
69
  }
@@ -52,9 +52,9 @@ class SelectMultipleSideBySideElement extends AbstractSortableSelectItems {
52
52
  const fieldName = element.dataset.relatedfieldname;
53
53
  if (fieldName) {
54
54
  const exclusiveValues = Utility.trimExplode(',', element.dataset?.exclusivevalues ?? '');
55
- const selectedOptions = element.querySelectorAll('option:checked'); // Yep, :checked finds selected options
55
+ const selectedOptions = element.querySelectorAll<HTMLOptionElement>('option:checked'); // Yep, :checked finds selected options
56
56
  if (selectedOptions.length > 0) {
57
- selectedOptions.forEach((optionElement: HTMLOptionElement): void => {
57
+ selectedOptions.forEach((optionElement): void => {
58
58
  FormEngine.setSelectOptionFromExternalSource(
59
59
  fieldName,
60
60
  optionElement.value,