From 03e4a4a5f3ca1ce57f62bc3295b5e6094266dd93 Mon Sep 17 00:00:00 2001 From: Benjamin Kott <benjamin.kott@outlook.com> Date: Thu, 9 Mar 2023 08:25:07 +0100 Subject: [PATCH] [TASK] Cleanup eslint configuration We renamed the eslint configuration to follow the recommended standards so developers IDEs will pick up the configuration more easily. The ruleset was adapted to follow a wider range of recommendations. - eslint:recommended - @typescript-eslint/recommended - lit/recommended - wc/recommended In addition we now also force more kinds of spacing rules to avoid IDEs having different opinions on how to format for example includes. The only recommendation we don't want to follow is - @typescript-eslint/no-inferrable-types. We don't want to disallow being explicit. Instead of turning off rules we cannot fully fulfill as of now, we are now changing the configuration to warning. This will help developers spotting mistakes more easy when writing code. Rules that are currently in "warn" mode: - @typescript-eslint/ban-types - @typescript-eslint/no-explicit-any - @typescript-eslint/no-this-alias To only check for errors run in the Build directory: - npx eslint ./Sources/TypeScript/ --quiet We are removing the custom member ordering and preferring the default. @typescript-eslint/member-ordering Resolves: #100126 Releases: main Change-Id: I6f09f82c0bdcde1aa3edec66b3b6ab028d5bb242 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/78040 Tested-by: core-ci <typo3@b13.com> Reviewed-by: Benni Mack <benni@typo3.org> Reviewed-by: Oliver Hader <oliver.hader@typo3.org> Tested-by: Benni Mack <benni@typo3.org> Tested-by: Oliver Hader <oliver.hader@typo3.org> --- .editorconfig | 5 + Build/.eslintrc.json | 87 +++++++ Build/Gruntfile.js | 2 +- .../TypeScript/adminpanel/admin-panel.ts | 7 +- .../TypeScript/adminpanel/modules/cache.ts | 5 +- .../TypeScript/adminpanel/modules/preview.ts | 31 +-- .../backend/action-button/abstract-action.ts | 6 +- .../backend/action-button/deferred-action.ts | 8 +- .../backend/action-button/immediate-action.ts | 6 +- .../TypeScript/backend/action-dispatcher.ts | 22 +- .../TypeScript/backend/ajax-data-handler.ts | 30 +-- .../TypeScript/backend/broadcast-message.ts | 20 +- .../TypeScript/backend/broadcast-service.ts | 12 +- .../Sources/TypeScript/backend/clear-cache.ts | 14 +- .../TypeScript/backend/clipboard-panel.ts | 40 ++-- .../TypeScript/backend/color-picker.ts | 2 +- .../backend/column-selector-button.ts | 74 +++--- .../TypeScript/backend/context-help.ts | 4 +- .../backend/context-menu-actions.ts | 6 +- .../TypeScript/backend/context-menu.ts | 65 +++--- .../TypeScript/backend/copy-to-clipboard.ts | 18 +- .../TypeScript/backend/date-time-picker.ts | 18 +- .../TypeScript/backend/document-header.ts | 6 +- .../backend/document-save-actions.ts | 16 +- .../TypeScript/backend/drag-uploader.ts | 89 +++---- .../TypeScript/backend/element-browser.ts | 4 +- .../backend/element/editable-page-title.ts | 32 +-- .../backend/element/icon-element.ts | 46 ++-- .../element/immediate-action-element.ts | 24 +- .../backend/element/spinner-element.ts | 11 +- .../backend/element/table-wizard-element.ts | 51 ++-- .../backend/event/event-dispatcher.ts | 2 +- .../backend/event/interaction-request-map.ts | 2 +- .../backend/event/interaction-request.ts | 11 +- .../TypeScript/backend/file-link-handler.ts | 2 +- .../form-engine-link-browser-adapter.ts | 8 +- .../TypeScript/backend/form-engine-review.ts | 24 +- .../TypeScript/backend/form-engine-suggest.ts | 4 +- .../backend/form-engine-validation.ts | 57 ++--- .../Sources/TypeScript/backend/form-engine.ts | 50 ++-- .../container/files-control-container.ts | 39 ++-- .../flex-form-container-container.ts | 14 +- .../container/flex-form-section-container.ts | 30 +-- .../container/inline-control-container.ts | 68 +++--- .../container/site-language-container.ts | 34 +-- .../element/abstract-sortable-select-items.ts | 6 +- .../form-engine/element/category-element.ts | 14 +- .../form-engine/element/color-element.ts | 4 +- .../form-engine/element/datetime-element.ts | 4 +- .../element/extra/select-box-filter.ts | 12 +- .../form-engine/element/folder-element.ts | 2 +- .../form-engine/element/group-element.ts | 2 +- .../form-engine/element/json-element.ts | 4 +- .../form-engine/element/mfa-info-element.ts | 6 +- .../form-engine/element/modifier/tabbable.ts | 2 +- .../element/select-check-box-element.ts | 22 +- .../select-multiple-side-by-side-element.ts | 2 +- .../element/select-single-element.ts | 6 +- .../element/select-tree-element.ts | 20 +- .../element/select-tree-toolbar.ts | 10 +- .../form-engine/element/select-tree.ts | 17 +- .../form-engine/element/slug-element.ts | 14 +- .../element/suggest/result-container.ts | 14 +- .../element/suggest/result-item.ts | 16 +- .../form-engine/element/text-element.ts | 4 +- .../form-engine/element/text-table-element.ts | 4 +- .../form-engine/field-control/add-record.ts | 2 +- .../form-engine/field-control/edit-popup.ts | 6 +- .../field-control/insert-clipboard.ts | 4 +- .../form-engine/field-control/link-popup.ts | 2 +- .../form-engine/field-control/list-module.ts | 2 +- .../field-control/password-generator.ts | 6 +- .../field-control/reset-selection.ts | 4 +- .../form-engine/field-control/table-wizard.ts | 2 +- .../form-engine/field-wizard/value-picker.ts | 4 +- .../form-engine/field-wizard/value-slider.ts | 4 +- .../inline-relation/ajax-dispatcher.ts | 12 +- .../inline-response-interface.ts | 2 +- .../backend/form-engine/request-update.ts | 16 +- .../backend/global-event-handler.ts | 2 +- .../Sources/TypeScript/backend/grid-editor.ts | 88 ++++--- .../Sources/TypeScript/backend/hashing/md5.ts | 27 ++- Build/Sources/TypeScript/backend/icons.ts | 16 +- .../TypeScript/backend/image-manipulation.ts | 72 +++--- .../Sources/TypeScript/backend/info-window.ts | 2 +- .../TypeScript/backend/input/clearable.ts | 18 +- .../backend/layout-module/drag-drop.ts | 18 +- .../TypeScript/backend/layout-module/paste.ts | 32 +-- .../TypeScript/backend/link-browser.ts | 11 +- .../live-search/element/backend-search.ts | 4 +- .../element/provider/default-result-item.ts | 12 +- .../provider/page-provider-result-item.ts | 12 +- .../result/item/action/action-container.ts | 12 +- .../element/result/item/action/action.ts | 12 +- .../element/result/item/item-container.ts | 16 +- .../live-search/element/result/item/item.ts | 10 +- .../element/result/result-container.ts | 16 +- .../element/result/result-detail-container.ts | 8 +- .../live-search/element/search-option-item.ts | 14 +- .../backend/live-search/element/show-all.ts | 6 +- .../live-search/live-search-shortcut.ts | 10 +- .../result-types/default-result-type.ts | 6 +- .../result-types/page-result-type.ts | 4 +- .../TypeScript/backend/localization.ts | 18 +- .../TypeScript/backend/login-refresh.ts | 38 +-- Build/Sources/TypeScript/backend/login.ts | 2 +- Build/Sources/TypeScript/backend/modal.ts | 102 ++++---- .../Sources/TypeScript/backend/module-menu.ts | 22 +- .../TypeScript/backend/module/iframe.ts | 14 +- .../TypeScript/backend/module/router.ts | 28 +-- .../backend/multi-record-selection.ts | 46 ++-- .../TypeScript/backend/multi-step-wizard.ts | 60 +++-- .../new-content-element-wizard-button.ts | 2 +- .../backend/new-content-element-wizard.ts | 45 ++-- .../TypeScript/backend/new-multiple-pages.ts | 2 +- .../TypeScript/backend/notification.ts | 26 +-- .../TypeScript/backend/online-media.ts | 8 +- .../TypeScript/backend/page-actions.ts | 2 +- .../TypeScript/backend/page-link-handler.ts | 6 +- .../backend/page-tree/page-tree-element.ts | 48 ++-- .../TypeScript/backend/page-tree/page-tree.ts | 16 +- .../pagetsconfig/pagetsconfig-includes.ts | 14 +- Build/Sources/TypeScript/backend/popover.ts | 4 +- .../backend/record-download-button.ts | 20 +- .../TypeScript/backend/record-link-handler.ts | 2 +- .../Sources/TypeScript/backend/recordlist.ts | 66 +++--- Build/Sources/TypeScript/backend/severity.ts | 8 +- .../storage/abstract-client-storage.ts | 2 +- .../backend/storage/module-state-storage.ts | 8 +- .../TypeScript/backend/storage/persistent.ts | 6 +- Build/Sources/TypeScript/backend/svg-tree.ts | 102 ++++---- .../Sources/TypeScript/backend/switch-user.ts | 16 +- Build/Sources/TypeScript/backend/tabs.ts | 42 ++-- .../backend/tests/backend-exception-test.ts | 2 +- .../element/immediate-action-element-test.ts | 10 +- .../tests/form-engine-validation-test.ts | 5 +- .../backend/tests/grid-editor-test.ts | 2 +- .../TypeScript/backend/tests/icons-test.ts | 3 +- .../backend/tests/notification-test.ts | 10 +- .../TypeScript/backend/tests/popover-test.ts | 4 +- Build/Sources/TypeScript/backend/toolbar.ts | 2 +- .../backend/toolbar/clear-cache-menu.ts | 8 +- .../TypeScript/backend/toolbar/live-search.ts | 18 +- .../backend/toolbar/shortcut-menu.ts | 16 +- .../toolbar/system-information-menu.ts | 20 +- Build/Sources/TypeScript/backend/tooltip.ts | 16 +- .../TypeScript/backend/tree/drag-drop.ts | 24 +- .../backend/tree/file-storage-browser.ts | 28 +-- .../tree/file-storage-tree-container.ts | 43 ++-- .../backend/tree/file-storage-tree.ts | 10 +- .../TypeScript/backend/tree/page-browser.ts | 40 ++-- .../TypeScript/backend/tree/tree-node.ts | 2 +- .../TypeScript/backend/url-link-handler.ts | 2 +- .../TypeScript/backend/user-pass-login.ts | 70 +++--- Build/Sources/TypeScript/backend/utility.ts | 2 +- .../backend/utility/collapse-state-search.ts | 6 +- .../backend/utility/message-utility.ts | 2 +- .../backend/viewport/content-container.ts | 4 +- .../TypeScript/backend/viewport/loader.ts | 4 +- .../backend/viewport/navigation-container.ts | 18 +- .../backend/viewport/resizable-navigation.ts | 42 ++-- .../TypeScript/backend/viewport/toolbar.ts | 2 +- .../TypeScript/backend/viewport/topbar.ts | 4 +- .../TypeScript/backend/window-manager.ts | 9 +- Build/Sources/TypeScript/backend/wizard.ts | 48 ++-- Build/Sources/TypeScript/belog/backend-log.ts | 4 +- .../Sources/TypeScript/beuser/permissions.ts | 30 +-- .../TypeScript/core/ajax/ajax-request.ts | 14 +- .../TypeScript/core/ajax/input-transformer.ts | 6 +- .../core/ajax/simple-response-interface.ts | 2 +- .../core/authentication/mfa-provider/totp.ts | 18 +- .../TypeScript/core/event/debounce-event.ts | 4 +- .../TypeScript/core/event/regular-event.ts | 2 +- .../event/request-animation-frame-event.ts | 11 +- .../TypeScript/core/event/throttle-event.ts | 4 +- .../core/java-script-item-handler.ts | 4 +- .../core/java-script-item-processor.ts | 22 +- Build/Sources/TypeScript/core/lit-helper.ts | 8 +- .../TypeScript/core/security-utility.ts | 2 +- .../core/tests/ajax/ajax-request-test.ts | 76 +++--- .../core/tests/ajax/input-transformer-test.ts | 10 +- .../core/tests/security-utility-test.ts | 4 +- .../TypeScript/dashboard/chart-initializer.ts | 10 +- .../TypeScript/dashboard/dashboard-delete.ts | 2 +- .../TypeScript/dashboard/dashboard-modal.ts | 4 +- Build/Sources/TypeScript/dashboard/grid.ts | 6 +- .../dashboard/widget-content-collector.ts | 9 +- .../TypeScript/dashboard/widget-refresh.ts | 8 +- .../TypeScript/dashboard/widget-remover.ts | 2 +- .../TypeScript/dashboard/widget-selector.ts | 6 +- .../extensionmanager/distribution-image.ts | 8 +- .../TypeScript/extensionmanager/main.ts | 17 +- .../TypeScript/extensionmanager/repository.ts | 22 +- .../TypeScript/extensionmanager/update.ts | 2 +- .../extensionmanager/upload-form.ts | 2 +- .../TypeScript/filelist/browse-files.ts | 40 ++-- .../TypeScript/filelist/browse-folders.ts | 22 +- .../filelist/context-menu-actions.ts | 2 +- .../TypeScript/filelist/create-folder.ts | 4 +- .../TypeScript/filelist/file-delete.ts | 2 +- .../TypeScript/filelist/file-list-actions.ts | 2 +- .../TypeScript/filelist/file-list-dragdrop.ts | 2 +- .../filelist/file-list-transfer-handler.ts | 4 +- .../Sources/TypeScript/filelist/file-list.ts | 156 ++++++------- .../TypeScript/filelist/rename-file.ts | 10 +- .../Sources/TypeScript/form/backend/helper.ts | 4 +- .../TypeScript/install/ajax/ajax-queue.ts | 8 +- Build/Sources/TypeScript/install/install.ts | 1 - Build/Sources/TypeScript/install/installer.ts | 153 ++++++------ .../module/abstract-interactable-module.ts | 2 +- .../module/environment/environment-check.ts | 16 +- .../module/environment/folder-structure.ts | 24 +- .../module/environment/image-processing.ts | 27 +-- .../install/module/environment/mail-test.ts | 21 +- .../install/module/environment/php-info.ts | 10 +- .../module/environment/system-information.ts | 10 +- .../install/module/maintenance/cache.ts | 11 +- .../module/maintenance/clear-tables.ts | 15 +- .../maintenance/clear-typo3temp-files.ts | 13 +- .../module/maintenance/create-admin.ts | 13 +- .../module/maintenance/database-analyzer.ts | 23 +- .../module/maintenance/dump-autoload.ts | 11 +- .../module/maintenance/language-packs.ts | 69 +++--- .../maintenance/reset-backend-user-uc.ts | 11 +- .../settings/change-install-tool-password.ts | 13 +- .../settings/extension-configuration.ts | 49 ++-- .../install/module/settings/features.ts | 20 +- .../module/settings/local-configuration.ts | 25 +- .../install/module/settings/presets.ts | 19 +- .../module/settings/system-maintainer.ts | 40 +++- .../install/module/upgrade/core-update.ts | 34 ++- .../module/upgrade/extension-compat-tester.ts | 19 +- .../module/upgrade/extension-scanner.ts | 18 +- .../module/upgrade/tca-ext-tables-check.ts | 21 +- .../module/upgrade/tca-migrations-check.ts | 17 +- .../install/module/upgrade/upgrade-docs.ts | 56 ++--- .../install/module/upgrade/upgrade-wizards.ts | 135 +++++++---- .../install/renderable/clearable.ts | 18 +- .../install/renderable/flash-message.ts | 2 +- .../TypeScript/install/renderable/info-box.ts | 2 +- .../install/renderable/progress-bar.ts | 2 +- Build/Sources/TypeScript/install/router.ts | 108 ++++----- .../TypeScript/linkvalidator/linkvalidator.ts | 14 +- .../TypeScript/lowlevel/query-generator.ts | 2 +- .../TypeScript/lowlevel/reference-index.ts | 2 +- .../opendocs/toolbar/opendocs-menu.ts | 38 ++- Build/Sources/TypeScript/recycler/recycler.ts | 87 +++---- .../TypeScript/redirects/event-handler.ts | 38 ++- .../redirects/form-engine-evaluation.ts | 2 +- .../TypeScript/rte_ckeditor/ckeditor5.ts | 16 +- .../rte_ckeditor/plugin/typo3-link.ts | 9 +- .../rte_ckeditor/plugin/whitespace.ts | 2 +- .../rte_ckeditor/rte-link-browser.ts | 15 +- .../Sources/TypeScript/scheduler/scheduler.ts | 44 ++-- .../Sources/TypeScript/setup/setup-module.ts | 30 +-- .../t3editor/element/code-mirror-element.ts | 65 +++--- .../t3editor/language/typoscript.ts | 32 ++- .../TypeScript/tstemplate/constant-editor.ts | 4 +- .../tstemplate/template-analyzer.ts | 14 +- Build/Sources/TypeScript/viewpage/main.ts | 14 +- .../Sources/TypeScript/workspaces/backend.ts | 220 ++++++++++-------- .../Sources/TypeScript/workspaces/preview.ts | 32 +-- .../workspaces/toolbar/workspaces-menu.ts | 37 ++- .../TypeScript/workspaces/workspaces.ts | 18 +- Build/eslintrc.json | 94 -------- Build/types/JQuery/draguploader.d.ts | 4 + Build/types/JQuery/general.d.ts | 7 + Build/types/JQuery/minicolors.d.ts | 4 + Build/types/JQuery/paging.d.ts | 3 + Build/types/TYPO3/index.d.ts | 23 -- Build/types/tablesort.d.ts | 8 +- .../Public/JavaScript/admin-panel.js | 2 +- .../Public/JavaScript/modules/cache.js | 2 +- .../Public/JavaScript/modules/preview.js | 2 +- .../Public/JavaScript/action-dispatcher.js | 2 +- .../Public/JavaScript/ajax-data-handler.js | 2 +- .../Public/JavaScript/broadcast-message.js | 2 +- .../Public/JavaScript/clear-cache.js | 2 +- .../JavaScript/column-selector-button.js | 2 +- .../Public/JavaScript/context-menu-actions.js | 2 +- .../Public/JavaScript/context-menu.js | 2 +- .../Public/JavaScript/copy-to-clipboard.js | 2 +- .../JavaScript/document-save-actions.js | 2 +- .../Public/JavaScript/drag-uploader.js | 2 +- .../JavaScript/element/editable-page-title.js | 4 +- .../element/immediate-action-element.js | 2 +- .../element/table-wizard-element.js | 14 +- .../Public/JavaScript/file-link-handler.js | 2 +- .../Public/JavaScript/form-engine-review.js | 2 +- .../JavaScript/form-engine-validation.js | 2 +- .../Public/JavaScript/form-engine.js | 2 +- .../container/files-control-container.js | 2 +- .../flex-form-container-container.js | 2 +- .../container/flex-form-section-container.js | 2 +- .../container/inline-control-container.js | 2 +- .../container/site-language-container.js | 2 +- .../element/abstract-sortable-select-items.js | 2 +- .../element/select-single-element.js | 2 +- .../element/select-tree-element.js | 2 +- .../form-engine/element/select-tree.js | 2 +- .../element/suggest/result-container.js | 4 +- .../field-control/insert-clipboard.js | 2 +- .../field-control/reset-selection.js | 2 +- .../JavaScript/form-engine/request-update.js | 2 +- .../Public/JavaScript/grid-editor.js | 2 +- .../Public/JavaScript/hashing/md5.js | 2 +- .../Public/JavaScript/image-manipulation.js | 2 +- .../Public/JavaScript/input/clearable.js | 2 +- .../JavaScript/layout-module/drag-drop.js | 2 +- .../Public/JavaScript/layout-module/paste.js | 2 +- .../element/result/item/item-container.js | 2 +- .../live-search/live-search-shortcut.js | 2 +- .../Public/JavaScript/login-refresh.js | 2 +- .../Resources/Public/JavaScript/modal.js | 6 +- .../Public/JavaScript/module-menu.js | 2 +- .../Public/JavaScript/module/router.js | 2 +- .../JavaScript/multi-record-selection.js | 2 +- .../Public/JavaScript/multi-step-wizard.js | 2 +- .../JavaScript/new-content-element-wizard.js | 2 +- .../Public/JavaScript/new-multiple-pages.js | 2 +- .../Public/JavaScript/notification.js | 2 +- .../Public/JavaScript/page-actions.js | 2 +- .../Public/JavaScript/page-link-handler.js | 2 +- .../JavaScript/page-tree/page-tree-element.js | 4 +- .../Public/JavaScript/page-tree/page-tree.js | 2 +- .../JavaScript/record-download-button.js | 2 +- .../Public/JavaScript/record-link-handler.js | 2 +- .../Resources/Public/JavaScript/recordlist.js | 2 +- .../Resources/Public/JavaScript/severity.js | 2 +- .../storage/module-state-storage.js | 2 +- .../Resources/Public/JavaScript/svg-tree.js | 4 +- .../Public/JavaScript/switch-user.js | 2 +- .../Resources/Public/JavaScript/tabs.js | 2 +- .../Public/JavaScript/toolbar/live-search.js | 2 +- .../Resources/Public/JavaScript/tooltip.js | 2 +- .../Public/JavaScript/tree/drag-drop.js | 2 +- .../JavaScript/tree/file-storage-browser.js | 2 +- .../tree/file-storage-tree-container.js | 4 +- .../JavaScript/tree/file-storage-tree.js | 2 +- .../Public/JavaScript/tree/page-browser.js | 2 +- .../Public/JavaScript/url-link-handler.js | 2 +- .../utility/collapse-state-search.js | 2 +- .../viewport/navigation-container.js | 2 +- .../viewport/resizable-navigation.js | 4 +- .../Public/JavaScript/window-manager.js | 2 +- .../Resources/Public/JavaScript/wizard.js | 2 +- .../Tests/JavaScript/notification-test.js | 2 +- .../Public/JavaScript/permissions.js | 2 +- .../JavaScript/ajax/input-transformer.js | 2 +- .../authentication/mfa-provider/totp.js | 4 +- .../event/request-animation-frame-event.js | 2 +- .../JavaScript/java-script-item-processor.js | 2 +- .../Public/JavaScript/security-utility.js | 2 +- .../Recycler/RecyclerModuleCest.php | 1 + .../JavaScript/ajax/ajax-request-test.js | 2 +- .../Tests/JavaScript/security-utility-test.js | 2 +- .../Public/JavaScript/chart-initializer.js | 2 +- .../Resources/Public/JavaScript/grid.js | 2 +- .../JavaScript/widget-content-collector.js | 2 +- .../Public/JavaScript/widget-refresh.js | 2 +- .../Resources/Public/JavaScript/main.js | 2 +- .../Resources/Public/JavaScript/repository.js | 2 +- .../Public/JavaScript/browse-files.js | 2 +- .../Public/JavaScript/file-list-dragdrop.js | 2 +- .../Resources/Public/JavaScript/file-list.js | 2 +- .../Resources/Public/JavaScript/installer.js | 2 +- .../module/environment/environment-check.js | 2 +- .../module/environment/folder-structure.js | 2 +- .../module/environment/image-processing.js | 2 +- .../module/environment/mail-test.js | 2 +- .../module/maintenance/language-packs.js | 2 +- .../settings/extension-configuration.js | 2 +- .../JavaScript/module/settings/features.js | 2 +- .../JavaScript/module/settings/presets.js | 2 +- .../module/settings/system-maintainer.js | 2 +- .../JavaScript/module/upgrade/core-update.js | 2 +- .../module/upgrade/extension-compat-tester.js | 2 +- .../module/upgrade/extension-scanner.js | 2 +- .../module/upgrade/tca-ext-tables-check.js | 2 +- .../module/upgrade/tca-migrations-check.js | 2 +- .../JavaScript/module/upgrade/upgrade-docs.js | 2 +- .../module/upgrade/upgrade-wizards.js | 2 +- .../Public/JavaScript/renderable/clearable.js | 2 +- .../JavaScript/renderable/flash-message.js | 2 +- .../Public/JavaScript/renderable/info-box.js | 2 +- .../JavaScript/renderable/progress-bar.js | 2 +- .../Resources/Public/JavaScript/router.js | 2 +- .../Public/JavaScript/linkvalidator.js | 2 +- .../JavaScript/toolbar/opendocs-menu.js | 2 +- .../Resources/Public/JavaScript/recycler.js | 2 +- .../Public/JavaScript/event-handler.js | 2 +- .../Resources/Public/JavaScript/ckeditor5.js | 2 +- .../Public/JavaScript/rte-link-browser.js | 2 +- .../Resources/Public/JavaScript/scheduler.js | 2 +- .../JavaScript/element/code-mirror-element.js | 2 +- .../Public/JavaScript/language/typoscript.js | 2 +- .../Resources/Public/JavaScript/main.js | 2 +- .../Resources/Public/JavaScript/backend.js | 2 +- .../JavaScript/toolbar/workspaces-menu.js | 2 +- 399 files changed, 2891 insertions(+), 2911 deletions(-) create mode 100644 Build/.eslintrc.json delete mode 100644 Build/eslintrc.json create mode 100644 Build/types/JQuery/draguploader.d.ts create mode 100644 Build/types/JQuery/general.d.ts create mode 100644 Build/types/JQuery/minicolors.d.ts create mode 100644 Build/types/JQuery/paging.d.ts diff --git a/.editorconfig b/.editorconfig index 293886f83b3b..4229670aa303 100644 --- a/.editorconfig +++ b/.editorconfig @@ -38,6 +38,11 @@ indent_size = 2 indent_size = 2 indent_style = tab +#.eslintrc.json +[.eslintrc.json] +indent_size = 2 +indent_style = space + # stylelint [.stylelintrc] indent_size = 2 diff --git a/Build/.eslintrc.json b/Build/.eslintrc.json new file mode 100644 index 000000000000..1fedd461cdf1 --- /dev/null +++ b/Build/.eslintrc.json @@ -0,0 +1,87 @@ +{ + "root": true, + "env": { + "browser": true, + "es6": true + }, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": true + }, + "plugins": [ + "@typescript-eslint", + "lit", + "wc" + ], + "settings": { + "wc": { + "elementBaseClasses": [ + "LitElement" + ] + } + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:wc/recommended", + "plugin:lit/recommended" + ], + "rules": { + "@typescript-eslint/indent": ["error", 2], + "@typescript-eslint/no-inferrable-types": "off", // we want to keep explicit type casting + "@typescript-eslint/ban-types": "warn", + "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/no-this-alias": "warn", + "@typescript-eslint/member-ordering": "error", + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "class", + "format": ["PascalCase"] + }, + { + "selector": "typeLike", + "format": ["PascalCase"] + } + ], + "curly": "error", + "default-case": "error", + "dot-notation": "error", + "eol-last": "error", + "guard-for-in": "error", + "lit/no-duplicate-template-bindings": "error", + "lit/no-native-attributes": "warn", + "lit/no-invalid-escape-sequences": "error", + "lit/no-legacy-imports": "error", + "lit/no-useless-template-literals": "error", + "lit/prefer-nothing": "error", + "no-bitwise": "off", + "no-caller": "error", + "no-debugger": "error", + "no-empty": "error", + "no-empty-function": ["error", { + "allow": ["constructors"] + }], + "no-eval": "error", + "no-fallthrough": "error", + "no-new-wrappers": "error", + "no-unused-labels": "error", + "no-multi-spaces": "error", + "no-var": "error", + "no-case-declarations": "off", + "object-curly-spacing": [ + "error", + "always" + ], + "quotes": [ + "error", + "single" + ], + "radix": "error", + "semi": "off", + "space-infix-ops": "error", + "wc/no-constructor-params": "error", + "wc/no-typos": "error", + "wc/require-listener-teardown": "error" + } +} diff --git a/Build/Gruntfile.js b/Build/Gruntfile.js index 0690738a684a..0537c05f5c44 100644 --- a/Build/Gruntfile.js +++ b/Build/Gruntfile.js @@ -266,7 +266,7 @@ module.exports = function (grunt) { options: { cache: true, cacheLocation: './.cache/eslintcache/', - overrideConfigFile: 'eslintrc.json' + overrideConfigFile: '.eslintrc.json' }, files: { src: [ diff --git a/Build/Sources/TypeScript/adminpanel/admin-panel.ts b/Build/Sources/TypeScript/adminpanel/admin-panel.ts index 4eaf91f9b504..639eb14a5d1b 100644 --- a/Build/Sources/TypeScript/adminpanel/admin-panel.ts +++ b/Build/Sources/TypeScript/adminpanel/admin-panel.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line @typescript-eslint/no-namespace namespace TYPO3 { export const AdminPanelSelectors = { adminPanelRole: 'form[data-typo3-role=typo3-adminPanel]', @@ -130,7 +131,7 @@ namespace TYPO3 { this .querySelectorAll('.typo3-adminPanel-table th, .typo3-adminPanel-table td') .forEach((elm: HTMLElement) => { - elm.addEventListener('click', () => { + elm.addEventListener('click', () => { elm.focus(); try { document.execCommand('copy'); @@ -197,7 +198,7 @@ namespace TYPO3 { * reloaded */ private getCleanReloadUrl(): string { - let urlParams: string[] = []; + const urlParams: string[] = []; location.search.substr(1).split('&').forEach((item: string): void => { if (item && !item.includes('ADMCMD_')) { urlParams.push(item); @@ -210,7 +211,7 @@ namespace TYPO3 { private addBackdropListener(): void { this.querySelectorAll('.' + AdminPanelClasses.backdrop) - .forEach((elm: HTMLElement) => { + .forEach((elm: HTMLElement) => { elm.addEventListener('click', () => { this.removeBackdrop(); this diff --git a/Build/Sources/TypeScript/adminpanel/modules/cache.ts b/Build/Sources/TypeScript/adminpanel/modules/cache.ts index 01d7b8789db2..ce05a773d7e0 100644 --- a/Build/Sources/TypeScript/adminpanel/modules/cache.ts +++ b/Build/Sources/TypeScript/adminpanel/modules/cache.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line @typescript-eslint/no-namespace namespace TYPO3 { export class Cache { private buttons: NodeList; @@ -7,8 +8,8 @@ namespace TYPO3 { this.buttons.forEach((element: HTMLElement): void => { element.addEventListener('click', (): void => { - let url = element.dataset.typo3AjaxUrl; - let request = new XMLHttpRequest(); + const url = element.dataset.typo3AjaxUrl; + const request = new XMLHttpRequest(); request.open('GET', url); request.send(); request.onload = (): void => { diff --git a/Build/Sources/TypeScript/adminpanel/modules/preview.ts b/Build/Sources/TypeScript/adminpanel/modules/preview.ts index fba9713d095f..5be3d68d8654 100644 --- a/Build/Sources/TypeScript/adminpanel/modules/preview.ts +++ b/Build/Sources/TypeScript/adminpanel/modules/preview.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line @typescript-eslint/no-namespace namespace TYPO3 { export class Preview { private readonly dateField: HTMLInputElement = null; @@ -24,36 +25,36 @@ namespace TYPO3 { this.timeField.valueAsDate = initialDate; } - this.toggleField.addEventListener('change', this.toggleDisplay) + this.toggleField.addEventListener('change', this.toggleDisplay); this.dateField.addEventListener('change', this.updateDateField); this.timeField.addEventListener('change', this.updateDateField); } private toggleDisplay = (): void => { - let toggleVal = this.toggleField.checked; - let groupElement = <HTMLDivElement>document.getElementById('typo3-adminPanel-preview_simulateDate'); + const toggleVal = this.toggleField.checked; + const groupElement = <HTMLDivElement>document.getElementById('typo3-adminPanel-preview_simulateDate'); if (toggleVal) { - groupElement.classList.remove('typo3-adminPanel-group-disable') - this.dateField.disabled = false - this.timeField.disabled = false - this.updateDateField() + groupElement.classList.remove('typo3-adminPanel-group-disable'); + this.dateField.disabled = false; + this.timeField.disabled = false; + this.updateDateField(); } else { - groupElement.classList.add('typo3-adminPanel-group-disable') - this.dateField.disabled = true - this.timeField.disabled = true - this.targetField.value = '' + groupElement.classList.add('typo3-adminPanel-group-disable'); + this.dateField.disabled = true; + this.timeField.disabled = true; + this.targetField.value = ''; } - } + }; private updateDateField = (): void => { let dateVal = this.dateField.value; let timeVal = this.timeField.value; if (!dateVal && timeVal) { - let tempDate = new Date(); + const tempDate = new Date(); dateVal = tempDate.getFullYear() + '-' + (tempDate.getMonth() + 1) + '-' + tempDate.getDate(); } if (dateVal && !timeVal) { - timeVal = '00:00'; + timeVal = '00:00'; } if (!dateVal && !timeVal) { @@ -64,7 +65,7 @@ namespace TYPO3 { this.targetField.value = (date.valueOf() / 1000).toString(); } - } + }; } } diff --git a/Build/Sources/TypeScript/backend/action-button/abstract-action.ts b/Build/Sources/TypeScript/backend/action-button/abstract-action.ts index d2e6424b4108..1bd0a5775080 100644 --- a/Build/Sources/TypeScript/backend/action-button/abstract-action.ts +++ b/Build/Sources/TypeScript/backend/action-button/abstract-action.ts @@ -12,11 +12,11 @@ */ export abstract class AbstractAction { - protected callback: (promise?: Promise<any>) => void|Promise<any>; + protected callback: (promise?: Promise<void>) => void|Promise<void>; - constructor(callback: (promise?: Promise<any>) => void|Promise<any>) { + constructor(callback: (promise?: Promise<void>) => void|Promise<void>) { this.callback = callback; } - public abstract execute(el: HTMLElement): Promise<any>; + public abstract execute(el: HTMLElement): Promise<void>; } diff --git a/Build/Sources/TypeScript/backend/action-button/deferred-action.ts b/Build/Sources/TypeScript/backend/action-button/deferred-action.ts index be5fcfe7688f..eeb0ae7617ce 100644 --- a/Build/Sources/TypeScript/backend/action-button/deferred-action.ts +++ b/Build/Sources/TypeScript/backend/action-button/deferred-action.ts @@ -11,16 +11,16 @@ * The TYPO3 project - inspiring people to share! */ -import {AbstractAction} from './abstract-action'; +import { AbstractAction } from './abstract-action'; import Icons from '../icons'; /** * Action used when an operation execution time is unknown. */ class DeferredAction extends AbstractAction { - protected callback: () => Promise<any>; + protected callback: () => Promise<void>; - public async execute(el: HTMLAnchorElement|HTMLButtonElement): Promise<any> { + public async execute(el: HTMLAnchorElement|HTMLButtonElement): Promise<void> { el.dataset.actionLabel = el.innerText; el.classList.add('disabled'); @@ -30,7 +30,7 @@ class DeferredAction extends AbstractAction { return await this.executeCallback(el); } - private async executeCallback(el: HTMLElement): Promise<any> { + private async executeCallback(el: HTMLElement): Promise<void> { return await Promise.resolve(this.callback()).finally(() => { el.innerText = el.dataset.actionLabel; el.classList.remove('disabled'); diff --git a/Build/Sources/TypeScript/backend/action-button/immediate-action.ts b/Build/Sources/TypeScript/backend/action-button/immediate-action.ts index 4de8800c5e64..20e93cd4a739 100644 --- a/Build/Sources/TypeScript/backend/action-button/immediate-action.ts +++ b/Build/Sources/TypeScript/backend/action-button/immediate-action.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {AbstractAction} from './abstract-action'; +import { AbstractAction } from './abstract-action'; /** * Action used when an operation is executed immediately. @@ -19,11 +19,11 @@ import {AbstractAction} from './abstract-action'; class ImmediateAction extends AbstractAction { protected callback: () => void; - public execute(): Promise<any> { + public execute(): Promise<void> { return this.executeCallback(); } - private async executeCallback(): Promise<any> { + private async executeCallback(): Promise<void> { return Promise.resolve(this.callback()); } } diff --git a/Build/Sources/TypeScript/backend/action-dispatcher.ts b/Build/Sources/TypeScript/backend/action-dispatcher.ts index 263f3bf41b0d..005bef18b2a3 100644 --- a/Build/Sources/TypeScript/backend/action-dispatcher.ts +++ b/Build/Sources/TypeScript/backend/action-dispatcher.ts @@ -36,6 +36,11 @@ declare type ActionDispatchArgument = string | HTMLElement | Event; class ActionDispatcher { private delegates: {[key: string]: Function} = {}; + public constructor() { + this.createDelegates(); + documentService.ready().then((): void => this.registerEvents()); + } + private static resolveArguments(element: HTMLElement): null | string[] { if (element.dataset.dispatchArgs) { // `"` is the only literal of a PHP `json_encode` that needs to be substituted @@ -50,24 +55,7 @@ class ActionDispatcher { return null; } - private static enrichItems(items: any[], evt: Event, target: HTMLElement): any[] { - return items.map((item: any) => { - if (!(item instanceof Object) || !item.$event) { - return item; - } - if (item.$target) { - return target; - } - if (item.$event) { - return evt; - } - }); - } - public constructor() { - this.createDelegates(); - documentService.ready().then((): void => this.registerEvents()); - } private createDelegates(): void { this.delegates = { diff --git a/Build/Sources/TypeScript/backend/ajax-data-handler.ts b/Build/Sources/TypeScript/backend/ajax-data-handler.ts index 729db9a7c404..3c3d9cb6f564 100644 --- a/Build/Sources/TypeScript/backend/ajax-data-handler.ts +++ b/Build/Sources/TypeScript/backend/ajax-data-handler.ts @@ -11,11 +11,11 @@ * The TYPO3 project - inspiring people to share! */ -import {BroadcastMessage} from '@typo3/backend/broadcast-message'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { BroadcastMessage } from '@typo3/backend/broadcast-message'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import DocumentService from '@typo3/core/document-service'; -import {SeverityEnum} from './enum/severity'; +import { SeverityEnum } from './enum/severity'; import ResponseInterface from './ajax-data-handler/response-interface'; import $ from 'jquery'; import BroadcastService from '@typo3/backend/broadcast-service'; @@ -42,6 +42,12 @@ interface AfterProcessEventDict { * through \TYPO3\CMS\Backend\Controller\SimpleDataHandlerController->processAjaxRequest (record_process route) */ class AjaxDataHandler { + constructor() { + DocumentService.ready().then((): void => { + this.initialize(); + }); + } + /** * Refresh the page tree */ @@ -54,7 +60,7 @@ class AjaxDataHandler { * returns a jQuery Promise to work with * * @param {string | object} params - * @returns {Promise<any>} + * @returns {Promise<ResponseInterface>} */ private static call(params: string | object): Promise<ResponseInterface> { return (new AjaxRequest(TYPO3.settings.ajaxUrls.record_process)).withQueryArguments(params).get().then(async (response: AjaxResponse): Promise<ResponseInterface> => { @@ -62,20 +68,14 @@ class AjaxDataHandler { }); } - constructor() { - DocumentService.ready().then((): void => { - this.initialize(); - }); - } - /** * Generic function to call from the outside the script and validate directly showing errors * * @param {string | object} parameters * @param {AfterProcessEventDict} eventDict Dictionary used as event detail. This is private API yet. - * @returns {Promise<any>} + * @returns {Promise<ResponseInterface>} */ - public process(parameters: string | object, eventDict?: AfterProcessEventDict): Promise<any> { + public process(parameters: string | object, eventDict?: AfterProcessEventDict): Promise<ResponseInterface> { const promise = AjaxDataHandler.call(parameters); return promise.then((result: ResponseInterface): ResponseInterface => { if (result.hasErrors) { @@ -83,7 +83,7 @@ class AjaxDataHandler { } if (eventDict) { - const payload = {...eventDict, hasErrors: result.hasErrors}; + const payload = { ...eventDict, hasErrors: result.hasErrors }; const message = new BroadcastMessage( 'datahandler', 'process', @@ -219,7 +219,7 @@ class AjaxDataHandler { const uid = $rowElements.data('uid'); // make the AJAX call to toggle the visibility - const eventData = {component: 'datahandler', action: 'delete', table, uid}; + const eventData = { component: 'datahandler', action: 'delete', table, uid }; this.process(params, eventData).then((result: ResponseInterface): void => { // revert to the old class Icons.getIcon('actions-edit-delete', Icons.sizes.small).then((icon: string): void => { @@ -258,7 +258,7 @@ class AjaxDataHandler { * @param {Object} result */ private handleErrors(result: ResponseInterface): void { - for (let message of result.messages) { + for (const message of result.messages) { Notification.error(message.title, message.message); } } diff --git a/Build/Sources/TypeScript/backend/broadcast-message.ts b/Build/Sources/TypeScript/backend/broadcast-message.ts index c774eb946254..107a2f1cb890 100644 --- a/Build/Sources/TypeScript/backend/broadcast-message.ts +++ b/Build/Sources/TypeScript/backend/broadcast-message.ts @@ -19,8 +19,17 @@ export class BroadcastMessage { readonly eventName: string; readonly payload: any; + constructor(componentName: string, eventName: string, payload: any) { + if (!componentName || !eventName) { + throw new Error('Properties componentName and eventName have to be defined'); + } + this.componentName = componentName; + this.eventName = eventName; + this.payload = payload || {}; + } + public static fromData(data: any): BroadcastMessage { - let payload = Object.assign({}, data); + const payload = Object.assign({}, data); delete payload.componentName; delete payload.eventName; return new BroadcastMessage( @@ -30,15 +39,6 @@ export class BroadcastMessage { ); } - constructor(componentName: string, eventName: string, payload: any) { - if (!componentName || !eventName) { - throw new Error('Properties componentName and eventName have to be defined'); - } - this.componentName = componentName; - this.eventName = eventName; - this.payload = payload || {}; - } - public createCustomEvent(scope: string = 'typo3'): CustomEvent { return new CustomEvent( [scope, this.componentName, this.eventName].join(':'), diff --git a/Build/Sources/TypeScript/backend/broadcast-service.ts b/Build/Sources/TypeScript/backend/broadcast-service.ts index 3ecd0d6564a0..f1fae56109c7 100644 --- a/Build/Sources/TypeScript/backend/broadcast-service.ts +++ b/Build/Sources/TypeScript/backend/broadcast-service.ts @@ -12,8 +12,8 @@ */ import 'broadcastchannel'; -import {BroadcastMessage} from '@typo3/backend/broadcast-message'; -import {MessageUtility} from '@typo3/backend/utility/message-utility'; +import { BroadcastMessage } from '@typo3/backend/broadcast-message'; +import { MessageUtility } from '@typo3/backend/utility/message-utility'; /** * @module @typo3/backend/broadcast-service @@ -21,6 +21,10 @@ import {MessageUtility} from '@typo3/backend/utility/message-utility'; class BroadcastService { private readonly channel: BroadcastChannel; + public constructor() { + this.channel = new BroadcastChannel('typo3'); + } + public get isListening(): boolean { return typeof this.channel.onmessage === 'function'; } @@ -33,10 +37,6 @@ class BroadcastService { document.dispatchEvent(message.createCustomEvent('typo3')); } - public constructor() { - this.channel = new BroadcastChannel('typo3'); - } - public listen(): void { if (this.isListening) { return; diff --git a/Build/Sources/TypeScript/backend/clear-cache.ts b/Build/Sources/TypeScript/backend/clear-cache.ts index d46cf2b6b7d4..aa6f50cc7ec5 100644 --- a/Build/Sources/TypeScript/backend/clear-cache.ts +++ b/Build/Sources/TypeScript/backend/clear-cache.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import Notification from '@typo3/backend/notification'; import Icons from '@typo3/backend/icons'; import RegularEvent from '@typo3/core/event/regular-event'; @@ -26,6 +26,10 @@ enum Identifiers { * Module: @typo3/backend/clear-cache */ class ClearCache { + constructor() { + this.registerClickHandler(); + } + private static setDisabled(element: HTMLButtonElement, isDisabled: boolean): void { element.disabled = isDisabled; element.classList.toggle('disabled', isDisabled); @@ -38,7 +42,7 @@ class ClearCache { * @return Promise<AjaxResponse> */ private static sendClearCacheRequest(pageId: number): Promise<AjaxResponse> { - const request = new AjaxRequest(TYPO3.settings.ajaxUrls.web_list_clearpagecache).withQueryArguments({id: pageId}).get({cache: 'no-cache'}); + const request = new AjaxRequest(TYPO3.settings.ajaxUrls.web_list_clearpagecache).withQueryArguments({ id: pageId }).get({ cache: 'no-cache' }); request.then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { @@ -55,10 +59,6 @@ class ClearCache { return request; } - constructor() { - this.registerClickHandler(); - } - private registerClickHandler(): void { const trigger = document.querySelector(`${Identifiers.clearCache}:not([disabled])`); if (trigger !== null) { @@ -79,7 +79,7 @@ class ClearCache { me.querySelector(Identifiers.icon).outerHTML = icon; }); ClearCache.setDisabled(me, false); - }) + }); }).bindTo(trigger); } } diff --git a/Build/Sources/TypeScript/backend/clipboard-panel.ts b/Build/Sources/TypeScript/backend/clipboard-panel.ts index 6f130accf6f6..1db50227b95a 100644 --- a/Build/Sources/TypeScript/backend/clipboard-panel.ts +++ b/Build/Sources/TypeScript/backend/clipboard-panel.ts @@ -11,13 +11,13 @@ * The TYPO3 project - inspiring people to share! */ -import {html, LitElement, nothing, TemplateResult} from 'lit'; -import {customElement, property} from 'lit/decorators'; -import {until} from 'lit/directives/until'; -import {unsafeHTML} from 'lit/directives/unsafe-html'; -import {classMap} from 'lit/directives/class-map'; +import { html, LitElement, nothing, TemplateResult } from 'lit'; +import { customElement, property } from 'lit/decorators'; +import { until } from 'lit/directives/until'; +import { unsafeHTML } from 'lit/directives/unsafe-html'; +import { classMap } from 'lit/directives/class-map'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import Notification from '@typo3/backend/notification'; import '@typo3/backend/element/spinner-element'; import '@typo3/backend/element/icon-element'; @@ -32,7 +32,7 @@ interface ClipboardData { copyMode: CopyMode; elementCount: number; tabs: Array<ClipboardTab>; - labels: any; + labels: Record<string, string>; } interface ClipboardTab { @@ -64,8 +64,8 @@ interface DispatchArgs { */ @customElement('typo3-backend-clipboard-panel') export class ClipboardPanel extends LitElement { - @property({type: String, attribute: 'return-url'}) returnUrl: string = ''; - @property({type: String}) table: string = ''; + @property({ type: String, attribute: 'return-url' }) returnUrl: string = ''; + @property({ type: String }) table: string = ''; private static renderLoader(): TemplateResult { return html` @@ -91,8 +91,8 @@ export class ClipboardPanel extends LitElement { private renderPanel(): Promise<TemplateResult> { return (new AjaxRequest(top.TYPO3.settings.Clipboard.moduleUrl)) - .withQueryArguments({action: 'getClipboardData'}) - .post({table: this.table}) + .withQueryArguments({ action: 'getClipboardData' }) + .post({ table: this.table }) .then(async (response: AjaxResponse): Promise<TemplateResult> => { const resolvedBody = await response.resolve(); if (resolvedBody.success === true && resolvedBody.data) { @@ -105,7 +105,7 @@ export class ClipboardPanel extends LitElement { <div class="table-fit"> <table class="table"> <tbody> - ${clipboardData.tabs.map((tab: any): TemplateResult => this.renderTab(tab, clipboardData))} + ${clipboardData.tabs.map((tab: ClipboardTab): TemplateResult => this.renderTab(tab, clipboardData))} </tbody> </tabel> </div> @@ -128,7 +128,7 @@ export class ClipboardPanel extends LitElement { return html` <tr> <td colspan="2" class="nowrap"> - <button type="button" class="btn btn-link" title="${tab.description}" data-action="setP" @click="${(event: PointerEvent) => this.updateClipboard(event, {CB: {'setP': tab.identifier}})}"> + <button type="button" class="btn btn-link" title="${tab.description}" data-action="setP" @click="${(event: PointerEvent) => this.updateClipboard(event, { CB: { 'setP': tab.identifier } })}"> ${clipboardData.current === tab.identifier ? html` <typo3-backend-icon identifier="actions-check-circle-alt" alternativeMarkupIdentifier="inline" size="small" class="icon icon-size-small"></typo3-backend-icon> ${tab.title} @@ -144,19 +144,19 @@ export class ClipboardPanel extends LitElement { <td class="col-control nowrap"> ${clipboardData.current !== tab.identifier ? nothing : html` <div class="btn-group"> - <input type="radio" class="btn-check" id="clipboard-copymode-copy" data-action="setCopyMode" ?checked=${clipboardData.copyMode === CopyMode.copy} @click="${(event: PointerEvent) => this.updateClipboard(event, {CB: {'setCopyMode': '1'}})}"> + <input type="radio" class="btn-check" id="clipboard-copymode-copy" data-action="setCopyMode" ?checked=${clipboardData.copyMode === CopyMode.copy} @click="${(event: PointerEvent) => this.updateClipboard(event, { CB: { 'setCopyMode': '1' } })}"> <label class="btn btn-default btn-sm" for="clipboard-copymode-copy"> <typo3-backend-icon identifier="actions-edit-copy" alternativeMarkupIdentifier="inline" size="small" class="icon icon-size-small"></typo3-backend-icon> ${clipboardData.labels.copyElements} </label> - <input type="radio" class="btn-check" id="clipboard-copymode-move" data-action="setCopyMode" ?checked=${clipboardData.copyMode !== CopyMode.copy} @click="${(event: PointerEvent) => this.updateClipboard(event, {CB: {'setCopyMode': '0'}})}"> + <input type="radio" class="btn-check" id="clipboard-copymode-move" data-action="setCopyMode" ?checked=${clipboardData.copyMode !== CopyMode.copy} @click="${(event: PointerEvent) => this.updateClipboard(event, { CB: { 'setCopyMode': '0' } })}"> <label class="btn btn-default btn-sm" for="clipboard-copymode-move"> <typo3-backend-icon identifier="actions-cut" alternativeMarkupIdentifier="inline" size="small" class="icon icon-size-small"></typo3-backend-icon> ${clipboardData.labels.moveElements} </label> </div> ${!clipboardData.elementCount ? nothing : html` - <button type="button" class="btn btn-default btn-sm" title="${clipboardData.labels.removeAll}" data-action="removeAll" @click="${(event: PointerEvent) => this.updateClipboard(event, {CB: {'removeAll': tab.identifier}})}"> + <button type="button" class="btn btn-default btn-sm" title="${clipboardData.labels.removeAll}" data-action="removeAll" @click="${(event: PointerEvent) => this.updateClipboard(event, { CB: { 'removeAll': tab.identifier } })}"> <typo3-backend-icon identifier="actions-minus" alternativeMarkupIdentifier="inline" size="small" class="icon icon-size-small"></typo3-backend-icon> ${clipboardData.labels.removeAll} </button>`} @@ -170,7 +170,7 @@ export class ClipboardPanel extends LitElement { private renderTabItem(tabItem: ClipboardTabItem, tabIdentifier: string, clipboardData: ClipboardData): TemplateResult { return html` <tr> - <td class="col-icon nowrap ${classMap({'ps-4': !tabItem.identifier})}"> + <td class="col-icon nowrap ${classMap({ 'ps-4': !tabItem.identifier })}"> ${unsafeHTML(tabItem.icon)} </td> <td class="nowrap" style="width: 95%"> @@ -188,7 +188,7 @@ export class ClipboardPanel extends LitElement { </button> `} ${!tabItem.identifier ? nothing : html` - <button type="button" class="btn btn-default btn-sm" title="${clipboardData.labels.removeItem}" data-action="remove" @click="${(event: PointerEvent) => this.updateClipboard(event,{CB: {'remove': tabItem.identifier}})}"> + <button type="button" class="btn btn-default btn-sm" title="${clipboardData.labels.removeItem}" data-action="remove" @click="${(event: PointerEvent) => this.updateClipboard(event,{ CB: { 'remove': tabItem.identifier } })}"> <span> <typo3-backend-icon identifier="actions-minus" alternativeMarkupIdentifier="inline" size="small" class="icon icon-size-small"></typo3-backend-icon> ${clipboardData.labels.removeItem} @@ -212,7 +212,7 @@ export class ClipboardPanel extends LitElement { // other components react on the updated clipboard state. if (target.dataset.action) { target.dispatchEvent(new CustomEvent('typo3:clipboard:' + target.dataset.action, { - detail: {payload: payload, response: resolvedBody}, + detail: { payload: payload, response: resolvedBody }, bubbles: true, cancelable: false })); @@ -230,7 +230,7 @@ export class ClipboardPanel extends LitElement { private reloadModule (): void { if (this.returnUrl) { - this.ownerDocument.location.href = this.returnUrl + this.ownerDocument.location.href = this.returnUrl; } else { this.ownerDocument.location.reload(); } diff --git a/Build/Sources/TypeScript/backend/color-picker.ts b/Build/Sources/TypeScript/backend/color-picker.ts index 35660382834f..4aa331f8dc95 100644 --- a/Build/Sources/TypeScript/backend/color-picker.ts +++ b/Build/Sources/TypeScript/backend/color-picker.ts @@ -40,7 +40,7 @@ class ColorPicker { } // Initialize color picker - ($(element) as any).minicolors({ + $(element).minicolors({ format: 'hex', position: 'bottom left', theme: 'bootstrap', diff --git a/Build/Sources/TypeScript/backend/column-selector-button.ts b/Build/Sources/TypeScript/backend/column-selector-button.ts index d803204d5ea0..dffd6a198ba7 100644 --- a/Build/Sources/TypeScript/backend/column-selector-button.ts +++ b/Build/Sources/TypeScript/backend/column-selector-button.ts @@ -11,14 +11,14 @@ * The TYPO3 project - inspiring people to share! */ -import {html, css, TemplateResult, LitElement} from 'lit'; -import {customElement, property} from 'lit/decorators'; -import {SeverityEnum} from '@typo3/backend/enum/severity'; +import { html, css, TemplateResult, LitElement } from 'lit'; +import { customElement, property } from 'lit/decorators'; +import { SeverityEnum } from '@typo3/backend/enum/severity'; import Severity from '@typo3/backend/severity'; -import {default as Modal, ModalElement} from '@typo3/backend/modal'; -import {lll} from '@typo3/core/lit-helper'; +import { default as Modal, ModalElement } from '@typo3/backend/modal'; +import { lll } from '@typo3/core/lit-helper'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import Notification from '@typo3/backend/notification'; enum Selectors { @@ -51,15 +51,29 @@ enum SelectorActions { * </typo3-backend-column-selector-button> */ @customElement('typo3-backend-column-selector-button') -class ColumnSelectorButton extends LitElement { +export class ColumnSelectorButton extends LitElement { static styles = [css`:host { cursor: pointer; appearance: button; }`]; - @property({type: String}) url: string; - @property({type: String}) target: string; - @property({type: String}) title: string = 'Show columns'; - @property({type: String}) ok: string = lll('button.ok') || 'Update'; - @property({type: String}) close: string = lll('button.close') || 'Close'; - @property({type: String}) error: string = 'Could not update columns'; + @property({ type: String }) url: string; + @property({ type: String }) target: string; + @property({ type: String }) title: string = 'Show columns'; + @property({ type: String }) ok: string = lll('button.ok') || 'Update'; + @property({ type: String }) close: string = lll('button.close') || 'Close'; + @property({ type: String }) error: string = 'Could not update columns'; + + public constructor() { + super(); + this.addEventListener('click', (e: Event): void => { + e.preventDefault(); + this.showColumnSelectorModal(); + }); + this.addEventListener('keydown', (e: KeyboardEvent): void => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + this.showColumnSelectorModal(); + } + }); + } /** * Toggle selector actions state (enabled or disabled) depending @@ -78,23 +92,23 @@ class ColumnSelectorButton extends LitElement { selectNone: HTMLButtonElement, initialize: boolean = false ) { - selectAll.classList.add('disabled') - for (let i=0; i < columns.length; i++) { + selectAll.classList.add('disabled'); + for (let i = 0; i < columns.length; i++) { if (!columns[i].disabled && !columns[i].checked && (initialize || !ColumnSelectorButton.isColumnHidden(columns[i])) ) { - selectAll.classList.remove('disabled') + selectAll.classList.remove('disabled'); break; } } - selectNone.classList.add('disabled') - for (let i=0; i < columns.length; i++) { + selectNone.classList.add('disabled'); + for (let i = 0; i < columns.length; i++) { if (!columns[i].disabled && columns[i].checked && (initialize || !ColumnSelectorButton.isColumnHidden(columns[i])) ) { - selectNone.classList.remove('disabled') + selectNone.classList.remove('disabled'); break; } } @@ -135,20 +149,6 @@ class ColumnSelectorButton extends LitElement { }); } - public constructor() { - super(); - this.addEventListener('click', (e: Event): void => { - e.preventDefault(); - this.showColumnSelectorModal(); - }); - this.addEventListener('keydown', (e: KeyboardEvent): void => { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - this.showColumnSelectorModal(); - } - }) - } - public connectedCallback(): void { if (!this.hasAttribute('role')) { this.setAttribute('role', 'button'); @@ -201,11 +201,11 @@ class ColumnSelectorButton extends LitElement { } (new AjaxRequest(TYPO3.settings.ajaxUrls.show_columns)) .post(new FormData(form)) - .then(async (response: AjaxResponse): Promise<any> => { + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { // @todo This does not jump to the anchor (#t3-table-some_table) after the reload!!! - this.ownerDocument.location.href = this.target + this.ownerDocument.location.href = this.target; this.ownerDocument.location.reload(); } else { Notification.error(data.message || 'No update was performed'); @@ -214,7 +214,7 @@ class ColumnSelectorButton extends LitElement { }) .catch(() => { this.abortSelection(); - }) + }); } private handleModalContentLoaded(currentModal: HTMLElement): void { @@ -224,7 +224,7 @@ class ColumnSelectorButton extends LitElement { return; } // Prevent the form from being submitted as the form data will be send via an ajax request - form.addEventListener('submit', (e: Event): void => { e.preventDefault() }); + form.addEventListener('submit', (e: Event): void => { e.preventDefault(); }); const columns: NodeListOf<HTMLInputElement> = currentModal.querySelectorAll(Selectors.columnsSelector); const columnsFilter: HTMLInputElement = currentModal.querySelector(Selectors.columnsFilterSelector); diff --git a/Build/Sources/TypeScript/backend/context-help.ts b/Build/Sources/TypeScript/backend/context-help.ts index 3edd127af497..a952529adb70 100644 --- a/Build/Sources/TypeScript/backend/context-help.ts +++ b/Build/Sources/TypeScript/backend/context-help.ts @@ -12,7 +12,7 @@ */ import 'bootstrap'; -import {Popover as BootstrapPopover} from 'bootstrap'; +import { Popover as BootstrapPopover } from 'bootstrap'; import Popover from './popover'; import RegularEvent from '@typo3/core/event/regular-event'; @@ -44,7 +44,7 @@ class ContextHelp { const me = e.target as HTMLElement; const description = me.dataset.description; - if (!!description) { + if (description) { const options = <BootstrapPopover.Options>{ title: me.dataset.title || '', content: description, diff --git a/Build/Sources/TypeScript/backend/context-menu-actions.ts b/Build/Sources/TypeScript/backend/context-menu-actions.ts index 0eb84a5b3cee..29f966e8966a 100644 --- a/Build/Sources/TypeScript/backend/context-menu-actions.ts +++ b/Build/Sources/TypeScript/backend/context-menu-actions.ts @@ -35,8 +35,8 @@ class ContextMenuActions { } public static editRecord(table: string, uid: number, dataset: DOMStringMap): void { - let overrideVals = '', - pageLanguageId = dataset.pagesLanguageUid; + const pageLanguageId = dataset.pagesLanguageUid; + let overrideVals = ''; if (pageLanguageId) { // Disallow manual adjustment of the language field for pages @@ -241,7 +241,7 @@ class ContextMenuActions { */ public static clearCache(table: string, uid: number): void { (new AjaxRequest(TYPO3.settings.ajaxUrls.web_list_clearpagecache)).withQueryArguments({ id: uid }).get({ cache: 'no-cache' }).then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { Notification.success(data.title, data.message, 1); diff --git a/Build/Sources/TypeScript/backend/context-menu.ts b/Build/Sources/TypeScript/backend/context-menu.ts index a4afeb98eacf..c9d5abb0f985 100644 --- a/Build/Sources/TypeScript/backend/context-menu.ts +++ b/Build/Sources/TypeScript/backend/context-menu.ts @@ -12,7 +12,7 @@ */ import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import ContextMenuActions from './context-menu-actions'; import DebounceEvent from '@typo3/core/event/debounce-event'; @@ -33,7 +33,7 @@ interface MenuItem { type: string; icon: string; label: string; - additionalAttributes?: { [key: string]: string }; + additionalAttributes?: Record<string, string>; childItems?: MenuItems; callbackAction?: string; } @@ -47,16 +47,29 @@ interface MenuItems { * Container used to load the context menu via AJAX to render the result in a layer next to the mouse cursor */ class ContextMenu { - private mousePos: MousePosition = {X: null, Y: null}; - private record: ActiveRecord = {uid: null, table: null}; + private mousePos: MousePosition = { X: null, Y: null }; + private record: ActiveRecord = { uid: null, table: null }; private eventSources: Element[] = []; + constructor() { + document.addEventListener('click', (event: PointerEvent) => { + this.handleTriggerEvent(event); + }); + + document.addEventListener('contextmenu', (event: PointerEvent) => { + this.handleTriggerEvent(event); + }); + + // register mouse movement inside the document + new ThrottleEvent('mousemove', this.storeMousePositionEvent.bind(this), 50).bindTo(document); + } + /** * @param {MenuItem} item * @returns {string} */ private static drawActionItem(item: MenuItem): string { - const attributes: { [key: string]: string } = item.additionalAttributes || {}; + const attributes: Record<string, string> = item.additionalAttributes || {}; let attributesString = ''; for (const attribute of Object.entries(attributes)) { const [k, v] = attribute; @@ -78,19 +91,6 @@ class ContextMenu { return isInXBoundary && isInYBoundary; } - constructor() { - document.addEventListener('click', (event: PointerEvent) => { - this.handleTriggerEvent(event); - }); - - document.addEventListener('contextmenu', (event: PointerEvent) => { - this.handleTriggerEvent(event); - }); - - // register mouse movement inside the document - new ThrottleEvent('mousemove', this.storeMousePositionEvent.bind(this), 50).bindTo(document); - } - /** * Main function, called from most context menu links * @@ -104,7 +104,7 @@ class ContextMenu { public show(table: string, uid: number|string, context: string, unusedParam1: string, unusedParam2: string, eventSource: Element = null): void { this.hideAll(); - this.record = {table: table, uid: uid}; + this.record = { table: table, uid: uid }; // fix: [tabindex=-1] is not focusable!!! const focusableSource = eventSource.matches('a, button, [tabindex]') ? eventSource : eventSource.closest('a, button, [tabindex]'); this.eventSources.push(focusableSource); @@ -134,13 +134,12 @@ class ContextMenu { document.querySelectorAll('.context-menu').forEach((contextMenu: Element): void => { // Explicitly update cursor position if element is entered to avoid timing issues - new RegularEvent('mouseenter', (e: MouseEvent): void => { - const target: HTMLElement = e.target as HTMLElement; - this.storeMousePositionEvent(e); + new RegularEvent('mouseenter', (event: MouseEvent): void => { + this.storeMousePositionEvent(event); }).bindTo(contextMenu); - new DebounceEvent('mouseleave', (e: MouseEvent) => { - const target: HTMLElement = e.target as HTMLElement; + new DebounceEvent('mouseleave', (event: MouseEvent) => { + const target: HTMLElement = event.target as HTMLElement; const childMenu: HTMLElement | null = document.querySelector('[data-parent="#' + target.id + '"]'); const hideThisMenu = @@ -191,7 +190,7 @@ class ContextMenu { private handleContextMenuEvent(event: PointerEvent, element: HTMLElement): void { - const contextTrigger: String = element.dataset.contextmenuTrigger; + const contextTrigger: string = element.dataset.contextmenuTrigger; if (contextTrigger === 'click' || contextTrigger === event.type) { event.preventDefault(); this.show( @@ -234,7 +233,7 @@ class ContextMenu { */ private fetch(parameters: string): void { const url = TYPO3.settings.ajaxUrls.contextmenu; - (new AjaxRequest(url)).withQueryArguments(parameters).get().then(async (response: AjaxResponse): Promise<any> => { + (new AjaxRequest(url)).withQueryArguments(parameters).get().then(async (response: AjaxResponse): Promise<void> => { const data: MenuItems = await response.resolve(); if (typeof response !== 'undefined' && Object.keys(response).length > 0) { this.populateData(data, 0); @@ -285,13 +284,13 @@ class ContextMenu { }, }); if (me.dataset.callbackModule) { - import(callbackModule + '.js').then(({default: callbackModuleCallback}: {default: any}): void => { + import(callbackModule + '.js').then(({ default: callbackModuleCallback }: {default: any}): void => { callbackModuleCallback[callbackAction].bind(thisProxy)(this.record.table, this.record.uid, dataAttributesToPass); }); } else if (ContextMenuActions && typeof (ContextMenuActions as any)[callbackAction] === 'function') { (ContextMenuActions as any)[callbackAction].bind(thisProxy)(this.record.table, this.record.uid, dataAttributesToPass); } else { - console.log('action: ' + callbackAction + ' not found'); + console.error('action: ' + callbackAction + ' not found'); } this.hideAll(); }); @@ -362,14 +361,14 @@ class ContextMenu { } private setFocusToFirstItem(currentItem: HTMLElement): void { - let firstItem = this.getFirstItem(currentItem); + const firstItem = this.getFirstItem(currentItem); if (firstItem) { firstItem.focus(); } } private setFocusToLastItem(currentItem: HTMLElement): void { - let lastItem = this.getLastItem(currentItem); + const lastItem = this.getLastItem(currentItem); if (lastItem) { lastItem.focus(); } @@ -465,7 +464,7 @@ class ContextMenu { } } - return {left: x + 'px', top: y + 'px'}; + return { left: x + 'px', top: y + 'px' }; } /** @@ -505,8 +504,8 @@ class ContextMenu { * in the context menu object */ private storeMousePositionEvent = (event: MouseEvent): void => { - this.mousePos = {X: event.pageX, Y: event.pageY}; - } + this.mousePos = { X: event.pageX, Y: event.pageY }; + }; /** * @param {string} obj diff --git a/Build/Sources/TypeScript/backend/copy-to-clipboard.ts b/Build/Sources/TypeScript/backend/copy-to-clipboard.ts index 847f7cbe8633..ef4d19e5d185 100644 --- a/Build/Sources/TypeScript/backend/copy-to-clipboard.ts +++ b/Build/Sources/TypeScript/backend/copy-to-clipboard.ts @@ -11,10 +11,10 @@ * The TYPO3 project - inspiring people to share! */ -import {html, css, TemplateResult, LitElement} from 'lit'; -import {customElement, property} from 'lit/decorators'; +import { html, css, TemplateResult, LitElement } from 'lit'; +import { customElement, property } from 'lit/decorators'; import Notification from '@typo3/backend/notification'; -import {lll} from '@typo3/core/lit-helper'; +import { lll } from '@typo3/core/lit-helper'; /** * Module: @typo3/backend/copy-to-clipboard @@ -28,22 +28,22 @@ import {lll} from '@typo3/core/lit-helper'; * </typo3-copy-to-clipboard> */ @customElement('typo3-copy-to-clipboard') -class CopyToClipboard extends LitElement { +export class CopyToClipboard extends LitElement { static styles = [css`:host { cursor: pointer; appearance: button; }`]; - @property({type: String}) text: string; + @property({ type: String }) text: string; public constructor() { super(); this.addEventListener('click', (e: Event): void => { e.preventDefault(); - this.copyToClipboard() + this.copyToClipboard(); }); this.addEventListener('keydown', (e: KeyboardEvent): void => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); - this.copyToClipboard() + this.copyToClipboard(); } - }) + }); } public connectedCallback(): void { @@ -61,7 +61,7 @@ class CopyToClipboard extends LitElement { private copyToClipboard(): void { if (typeof this.text !== 'string' || !this.text.length) { - console.warn('No text for copy to clipboard given.') + console.warn('No text for copy to clipboard given.'); Notification.error(lll('copyToClipboard.error')); return; } diff --git a/Build/Sources/TypeScript/backend/date-time-picker.ts b/Build/Sources/TypeScript/backend/date-time-picker.ts index 458ac814b24d..34a57b5b2d55 100644 --- a/Build/Sources/TypeScript/backend/date-time-picker.ts +++ b/Build/Sources/TypeScript/backend/date-time-picker.ts @@ -12,7 +12,7 @@ */ import flatpickr from 'flatpickr/flatpickr.min'; -import {DateTime} from 'luxon'; +import { DateTime } from 'luxon'; import PersistentStorage from './storage/persistent'; import ThrottleEvent from '@typo3/core/event/throttle-event'; @@ -46,7 +46,7 @@ class DateTimePicker { day: 1 }); } - return date.toISO({suppressMilliseconds: true}); + return date.toISO({ suppressMilliseconds: true }); } /** @@ -85,7 +85,7 @@ class DateTimePicker { options.locale = locale; options.onOpen = [ (): void => { - scrollEvent.bindTo(document.querySelector('.t3js-module-body')) + scrollEvent.bindTo(document.querySelector('.t3js-module-body')); } ]; options.onClose = (): void => { @@ -97,9 +97,9 @@ class DateTimePicker { inputElement.addEventListener('input', (): void => { // Update selected date in picker - const value = dateTimePicker._input.value - const parsedDate = dateTimePicker.parseDate(value) - const formattedDate = dateTimePicker.formatDate(parsedDate, dateTimePicker.config.dateFormat) + const value = dateTimePicker._input.value; + const parsedDate = dateTimePicker.parseDate(value); + const formattedDate = dateTimePicker.formatDate(parsedDate, dateTimePicker.config.dateFormat); if (value === formattedDate) { dateTimePicker.setDate(value); @@ -120,11 +120,11 @@ class DateTimePicker { if (target.value !== '') { const type = target.dataset.dateType; - const date = DateTime.fromFormat(target.value, target._flatpickr.config.dateFormat, {zone: 'utc'}); + const date = DateTime.fromFormat(target.value, target._flatpickr.config.dateFormat, { zone: 'utc' }); if (date.isValid) { hiddenField.value = DateTimePicker.formatDateForHiddenField(date, type); } else { - target.value = DateTimePicker.formatDateForHiddenField(DateTime.fromISO(hiddenField.value, {zone: 'utc'}), type); + target.value = DateTimePicker.formatDateForHiddenField(DateTime.fromISO(hiddenField.value, { zone: 'utc' }), type); } } else { hiddenField.value = ''; @@ -150,7 +150,7 @@ class DateTimePicker { const bounds = activeFlatpickrElement.getBoundingClientRect(); const additionalOffset = 2; const calendarHeight = activeFlatpickrElement._flatpickr.calendarContainer.offsetHeight; - const distanceFromBottom = window.innerHeight - bounds.bottom + const distanceFromBottom = window.innerHeight - bounds.bottom; const showOnTop = distanceFromBottom < calendarHeight && bounds.top > calendarHeight; let newPosition; diff --git a/Build/Sources/TypeScript/backend/document-header.ts b/Build/Sources/TypeScript/backend/document-header.ts index ef8534419e5b..1ba5b7d5f531 100644 --- a/Build/Sources/TypeScript/backend/document-header.ts +++ b/Build/Sources/TypeScript/backend/document-header.ts @@ -16,7 +16,7 @@ import ThrottleEvent from '@typo3/core/event/throttle-event'; /** * Module: @typo3/backend/document-header - * Folds docHeader when scrolling down, and reveals when scrollup up + * Folds docHeader when scrolling down, and reveals when scrolling up */ class DocumentHeader { private documentHeader: HTMLElement = null; @@ -26,7 +26,7 @@ class DocumentHeader { private lastPosition: number = 0; private currentPosition: number = 0; private changedPosition: number = 0; - private settings: any = { + private readonly settings = { margin: 24, offset: 100, selectors: { @@ -72,7 +72,7 @@ class DocumentHeader { this.documentHeader.classList.add('module-docheader-folded'); } this.lastPosition = this.currentPosition; - } + }; } export default new DocumentHeader(); diff --git a/Build/Sources/TypeScript/backend/document-save-actions.ts b/Build/Sources/TypeScript/backend/document-save-actions.ts index 75afc1822266..0d5b67076b36 100644 --- a/Build/Sources/TypeScript/backend/document-save-actions.ts +++ b/Build/Sources/TypeScript/backend/document-save-actions.ts @@ -19,6 +19,12 @@ class DocumentSaveActions { private static instance: DocumentSaveActions = null; private preSubmitCallbacks: Array<Function> = []; + private constructor() { + DocumentService.ready().then((): void => { + this.initializeSaveHandling(); + }); + } + public static getInstance(): DocumentSaveActions { if (DocumentSaveActions.instance === null) { DocumentSaveActions.instance = new DocumentSaveActions(); @@ -27,12 +33,6 @@ class DocumentSaveActions { return DocumentSaveActions.instance; } - private constructor() { - DocumentService.ready().then((): void => { - this.initializeSaveHandling(); - }); - } - /** * Adds a callback being executed before submit * @@ -72,7 +72,7 @@ class DocumentSaveActions { const $elem = $('<input />').attr('type', 'hidden').attr('name', name).attr('value', value); // Run any preSubmit callbacks - for (let callback of this.preSubmitCallbacks) { + for (const callback of this.preSubmitCallbacks) { callback(e); if (e.isPropagationStopped()) { @@ -101,7 +101,7 @@ class DocumentSaveActions { Icons.getIcon('spinner-circle-dark', Icons.sizes.small).then((markup: string): void => { $affectedButton.find('.t3js-icon').replaceWith(markup); - }).catch((e) => { + }).catch(() => { // Catch error in case the promise was not resolved // e.g. loading a new page }); diff --git a/Build/Sources/TypeScript/backend/drag-uploader.ts b/Build/Sources/TypeScript/backend/drag-uploader.ts index ff063f0549d6..9a84694f4b06 100644 --- a/Build/Sources/TypeScript/backend/drag-uploader.ts +++ b/Build/Sources/TypeScript/backend/drag-uploader.ts @@ -159,7 +159,7 @@ class DragUploaderPlugin { return; } - this.$body.on('dragstart', (e: JQueryEventObject): void => { + this.$body.on('dragstart', (): void => { this.dragStartedInDocument = true; }); this.$body.on('dragover', this.dragFileIntoDocument); @@ -198,7 +198,7 @@ class DragUploaderPlugin { .attr('id', 'typo3-filelist') .addClass('table table-striped table-hover upload-queue') .html('<tbody></tbody>'); - let $tableContainer = $('<div/>', { 'class': 'table-fit' }).hide() + const $tableContainer = $('<div/>', { 'class': 'table-fit' }).hide() .append(this.$fileList); if (this.dropZoneInsertBefore) { @@ -212,7 +212,7 @@ class DragUploaderPlugin { this.fileInput.addEventListener('change', (event: Event) => { this.hideDropzone(event); - this.processFiles(Array.apply(null, this.fileInput.files)); + this.processFiles(this.fileInput.files); }); // Allow the user to hide the dropzone with the "Escape" key @@ -240,7 +240,7 @@ class DragUploaderPlugin { this.$dropzone.removeClass('drop-status-ok'); // User manually hides the dropzone, so we can reset the flag this.manuallyTriggered = false; - } + }; /** * @param {Event} event @@ -260,7 +260,7 @@ class DragUploaderPlugin { this.showDropzone(); } return false; - } + }; /** * @@ -273,7 +273,7 @@ class DragUploaderPlugin { $(event.currentTarget).removeClass('drop-in-progress'); this.dragStartedInDocument = false; return false; - } + }; public ignoreDrop = (event: Event): boolean => { // stops the browser from redirecting. @@ -281,13 +281,13 @@ class DragUploaderPlugin { event.preventDefault(); this.dragAborted(event); return false; - } + }; public handleDrop = (event: JQueryTypedEvent<DragEvent>): void => { this.ignoreDrop(event); this.hideDropzone(event); this.processFiles(event.originalEvent.dataTransfer.files); - } + }; /** * @param {FileList} files @@ -340,7 +340,7 @@ class DragUploaderPlugin { public fileInDropzone = (): void => { this.$dropzone.addClass('drop-status-ok'); - } + }; public fileOutOfDropzone = (): void => { this.$dropzone.removeClass('drop-status-ok'); @@ -348,7 +348,7 @@ class DragUploaderPlugin { if (!this.manuallyTriggered) { this.$dropzone.hide(); } - } + }; /** * Bind file picker to default upload button @@ -374,7 +374,7 @@ class DragUploaderPlugin { if (this.queueLength === 0) { const timeout: number = messages && messages.length ? 5000 : 0; if (timeout) { - for (let flashMessage of messages) { + for (const flashMessage of messages) { Notification.showMessage(flashMessage.title, flashMessage.message, flashMessage.severity); } } @@ -393,12 +393,12 @@ class DragUploaderPlugin { { label: TYPO3.lang['file_upload.reload.filelist.actions.reload'], action: new ImmediateAction((): void => { - top.list_frame.document.location.href = this.reloadUrl + top.list_frame.document.location.href = this.reloadUrl; }) } ] ); - }, timeout) + }, timeout); } } } @@ -495,48 +495,47 @@ class DragUploaderPlugin { } }); - const uploader = this; const $modal = $(modal); - $modal.on('change', '.t3js-actions-all', function (this: HTMLInputElement): void { - const $this = $(this), + $modal.on('change', '.t3js-actions-all', (e: JQueryEventObject): void => { + const $this = $(e.currentTarget), value = $this.val(); if (value !== '') { // mass action was selected, apply action to every file - for (let select of modal.querySelectorAll('.t3js-actions') as NodeListOf<HTMLSelectElement>) { + for (const select of modal.querySelectorAll('.t3js-actions') as NodeListOf<HTMLSelectElement>) { const index = parseInt(select.dataset.override, 10); select.value = value; select.disabled = true; - uploader.askForOverride[index].action = <Action>select.value; + this.askForOverride[index].action = <Action>select.value; } } else { $modal.find('.t3js-actions').removeProp('disabled'); } }); - $modal.on('change', '.t3js-actions', function (this: HTMLInputElement): void { - const $this = $(this), + $modal.on('change', '.t3js-actions', (e: JQueryEventObject): void => { + const $this = $(e.currentTarget), index = parseInt($this.data('override'), 10); - uploader.askForOverride[index].action = <Action>$this.val(); + this.askForOverride[index].action = <Action>$this.val(); }); - modal.addEventListener('button.clicked', function (e: Event): void { + modal.addEventListener('button.clicked', (e: Event): void => { const button = e.target as HTMLButtonElement; if (button.name === 'cancel') { - uploader.askForOverride = []; + this.askForOverride = []; Modal.dismiss(); } else if (button.name === 'continue') { - for (let fileInfo of uploader.askForOverride) { + for (const fileInfo of this.askForOverride) { if (fileInfo.action === Action.USE_EXISTING) { DragUploader.addFileToIrre( - uploader.irreObjectUid, + this.irreObjectUid, fileInfo.original, ); } else if (fileInfo.action !== Action.SKIP) { - new FileQueueItem(uploader, fileInfo.uploaded, fileInfo.action); + new FileQueueItem(this, fileInfo.uploaded, fileInfo.action); } } - uploader.askForOverride = []; + this.askForOverride = []; modal.hideModal(); } }); @@ -689,7 +688,7 @@ class FileQueueItem { const messages = jsonResponse.messages as FlashMessage[]; this.$progressPercentage.text(''); if (messages && messages.length) { - for (let flashMessage of messages) { + for (const flashMessage of messages) { Notification.showMessage(flashMessage.title, flashMessage.message, flashMessage.severity, 10); } } @@ -734,7 +733,7 @@ class FileQueueItem { if (checkbox) { checkbox.removeAttribute('disabled'); checkbox.setAttribute('name', 'CBC[_FILE|' + Md5.hash(combinedIdentifier) + ']'); - checkbox.setAttribute('value', combinedIdentifier) + checkbox.setAttribute('value', combinedIdentifier); } } @@ -815,10 +814,10 @@ class FileQueueItem { } class DragUploader { + private static options: DragUploaderOptions; public fileListColumnCount: number; public filesExtensionsAllowed: string; public fileDenyPattern: string; - private static options: DragUploaderOptions; public static fileSizeAsString(size: number): string { const sizeKB: number = size / 1024; @@ -847,17 +846,16 @@ class DragUploader { } public static init(): void { - const me = this; - const opts = me.options; + const options = this.options; // register the jQuery plugin "DragUploaderPlugin" $.fn.extend({ dragUploader: function (options?: DragUploaderOptions | string): JQuery { - return this.each((index: number, elem: HTMLElement): void => { - const $this = $(elem); - let data = $this.data('DragUploaderPlugin'); + return this.each((index: number, element: HTMLElement): void => { + const $element = $(element); + let data = $element.data('DragUploaderPlugin'); if (!data) { - $this.data('DragUploaderPlugin', (data = new DragUploaderPlugin(elem))); + $element.data('DragUploaderPlugin', (data = new DragUploaderPlugin(element))); } if (typeof options === 'string') { data[options](); @@ -867,30 +865,17 @@ class DragUploader { }); DocumentService.ready().then((): void => { - $('.t3js-drag-uploader').dragUploader(opts); + $('.t3js-drag-uploader').dragUploader(options); }); // @todo Refactor the FormEngine integration of the uploader to instance new uploaders via event handlers const observer = new MutationObserver((): void => { - $('.t3js-drag-uploader').dragUploader(opts); + $('.t3js-drag-uploader').dragUploader(options); }); observer.observe(document, { childList: true, subtree: true }); } } -/** - * Function to apply the example plugin to the selected elements of a jQuery result. - */ -interface DragUploaderFunction { - /** - * Apply the example plugin to the elements selected in the jQuery result. - * - * @param options Options to use for this application of the example plugin. - * @returns jQuery result. - */ - (options: DragUploaderOptions): JQuery; -} - export const initialize = function (): void { DragUploader.init(); @@ -901,7 +886,7 @@ export const initialize = function (): void { && 'undefined' !== typeof TYPO3.settings.RequireJS.PostInitializationModules && 'undefined' !== typeof TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader'] ) { - for (let moduleName of TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader']) { + for (const moduleName of TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader']) { window.require([moduleName]); } } diff --git a/Build/Sources/TypeScript/backend/element-browser.ts b/Build/Sources/TypeScript/backend/element-browser.ts index e4cd88e5e24b..74383c491e29 100644 --- a/Build/Sources/TypeScript/backend/element-browser.ts +++ b/Build/Sources/TypeScript/backend/element-browser.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {MessageUtility} from '@typo3/backend/utility/message-utility'; +import { MessageUtility } from '@typo3/backend/utility/message-utility'; import DocumentService from '@typo3/core/document-service'; import Modal from '@typo3/backend/modal'; @@ -149,7 +149,7 @@ class ElementBrowser { } Modal.dismiss(); close(); - } + }; private addElement(label: string, value: string, close: boolean): void { diff --git a/Build/Sources/TypeScript/backend/element/editable-page-title.ts b/Build/Sources/TypeScript/backend/element/editable-page-title.ts index 2ccd0a137bbd..8f7ad6bebd7d 100644 --- a/Build/Sources/TypeScript/backend/element/editable-page-title.ts +++ b/Build/Sources/TypeScript/backend/element/editable-page-title.ts @@ -11,14 +11,14 @@ * The TYPO3 project - inspiring people to share! */ -import {lll} from '@typo3/core/lit-helper'; -import {html, css, LitElement, TemplateResult, nothing} from 'lit'; -import {customElement, property, state} from 'lit/decorators'; +import { lll } from '@typo3/core/lit-helper'; +import { html, css, LitElement, TemplateResult, nothing } from 'lit'; +import { customElement, property, state } from 'lit/decorators'; import './icon-element'; import AjaxDataHandler from '../ajax-data-handler'; @customElement('typo3-backend-editable-page-title') -class EditablePageTitle extends LitElement { +export class EditablePageTitle extends LitElement { static styles = css` :host { display: block; @@ -123,10 +123,10 @@ class EditablePageTitle extends LitElement { right: 0; } `; - @property({type: String}) pageTitle: string = ''; - @property({type: Number}) pageId: number = 0; - @property({type: Number}) localizedPageId: number = 0; - @property({type: Boolean}) editable: boolean = false; + @property({ type: String }) pageTitle: string = ''; + @property({ type: Number }) pageId: number = 0; + @property({ type: Number }) localizedPageId: number = 0; + @property({ type: Boolean }) editable: boolean = false; @state() _isEditing: boolean = false; @state() _isSubmitting: boolean = false; @@ -186,21 +186,21 @@ class EditablePageTitle extends LitElement { this._isSubmitting = true; - let parameters: { [k: string]: any } = {}; - let recordUid; + let recordUid = this.pageId; if (this.localizedPageId > 0) { recordUid = this.localizedPageId; - } else { - recordUid = this.pageId; } - parameters.data = { - pages: { - [recordUid]: { - title: newPageTitle + const parameters = { + data: { + pages: { + [recordUid]: { + title: newPageTitle + } } } }; + AjaxDataHandler.process(parameters).then((): void => { this.pageTitle = newPageTitle; top.document.dispatchEvent(new CustomEvent('typo3:pagetree:refresh')); diff --git a/Build/Sources/TypeScript/backend/element/icon-element.ts b/Build/Sources/TypeScript/backend/element/icon-element.ts index df90b0fd2141..a36a87983e68 100644 --- a/Build/Sources/TypeScript/backend/element/icon-element.ts +++ b/Build/Sources/TypeScript/backend/element/icon-element.ts @@ -11,11 +11,11 @@ * The TYPO3 project - inspiring people to share! */ -import {html, css, unsafeCSS, LitElement, TemplateResult, CSSResult, nothing} from 'lit'; -import {customElement, property} from 'lit/decorators'; -import {unsafeHTML} from 'lit/directives/unsafe-html'; -import {until} from 'lit/directives/until'; -import {Sizes, States, MarkupIdentifiers} from '../enum/icon-types'; +import { html, css, unsafeCSS, LitElement, TemplateResult, CSSResult, nothing } from 'lit'; +import { customElement, property } from 'lit/decorators'; +import { unsafeHTML } from 'lit/directives/unsafe-html'; +import { until } from 'lit/directives/until'; +import { Sizes, States, MarkupIdentifiers } from '../enum/icon-types'; import Icons from '../icons'; import '@typo3/backend/element/spinner-element'; @@ -44,24 +44,6 @@ const iconSize = (identifier: CSSResult) => css` */ @customElement('typo3-backend-icon') export class IconElement extends LitElement { - @property({type: String}) identifier: string; - @property({type: String, reflect: true}) size: Sizes = null; - @property({type: String}) state: States = States.default; - @property({type: String}) overlay: string = null; - @property({type: String}) markup: MarkupIdentifiers = MarkupIdentifiers.inline; - - /** - * @internal Usage of `raw` attribute is discouraged due to security implications. - * - * The `raw` attribute value will be rendered unescaped into DOM as raw html (.innerHTML = raw). - * That means it is the responsibility of the callee to ensure the HTML string does not contain - * user supplied strings. - * This attribute should therefore only be used to preserve backwards compatibility, - * and must not be used in new code or with user supplied strings. - * Use `identifier` attribute if ever possible instead. - */ - @property({type: String}) raw?: string = null; - // @todo the css of the @typo3/icons should be included instead static styles = [ css` @@ -153,6 +135,24 @@ export class IconElement extends LitElement { iconSize(unsafeCSS(Sizes.mega)), ]; + @property({ type: String }) identifier: string; + @property({ type: String, reflect: true }) size: Sizes = null; + @property({ type: String }) state: States = States.default; + @property({ type: String }) overlay: string = null; + @property({ type: String }) markup: MarkupIdentifiers = MarkupIdentifiers.inline; + + /** + * @internal Usage of `raw` attribute is discouraged due to security implications. + * + * The `raw` attribute value will be rendered unescaped into DOM as raw html (.innerHTML = raw). + * That means it is the responsibility of the callee to ensure the HTML string does not contain + * user supplied strings. + * This attribute should therefore only be used to preserve backwards compatibility, + * and must not be used in new code or with user supplied strings. + * Use `identifier` attribute if ever possible instead. + */ + @property({ type: String }) raw?: string = null; + public render(): TemplateResult | symbol { if (this.raw) { return html`${unsafeHTML(this.raw)}`; diff --git a/Build/Sources/TypeScript/backend/element/immediate-action-element.ts b/Build/Sources/TypeScript/backend/element/immediate-action-element.ts index 6a3a6ece0124..62b9b26f4957 100644 --- a/Build/Sources/TypeScript/backend/element/immediate-action-element.ts +++ b/Build/Sources/TypeScript/backend/element/immediate-action-element.ts @@ -12,7 +12,7 @@ */ import Utility from '@typo3/backend/utility'; -import {EventDispatcher} from '@typo3/backend/event/event-dispatcher'; +import { EventDispatcher } from '@typo3/backend/event/event-dispatcher'; /** * Module: @typo3/backend/element/immediate-action-element @@ -27,16 +27,23 @@ export class ImmediateActionElement extends HTMLElement { private action: string; private args: any[] = []; + /** + * Observed attributes handled by `attributeChangedCallback`. + */ + public static get observedAttributes(): string[] { + return ['action', 'args', 'args-list']; + } + private static async getDelegate(action: string): Promise<Function> { switch (action) { case 'TYPO3.ModuleMenu.App.refreshMenu': - const {default: moduleMenuApp} = await import('@typo3/backend/module-menu'); + const { default: moduleMenuApp } = await import('@typo3/backend/module-menu'); return moduleMenuApp.App.refreshMenu.bind(moduleMenuApp.App); case 'TYPO3.Backend.Topbar.refresh': - const {default: viewportObject} = await import('@typo3/backend/viewport'); + const { default: viewportObject } = await import('@typo3/backend/viewport'); return viewportObject.Topbar.refresh.bind(viewportObject.Topbar); case 'TYPO3.WindowManager.localOpen': - const {default: windowManager} = await import('@typo3/backend/window-manager'); + const { default: windowManager } = await import('@typo3/backend/window-manager'); return windowManager.localOpen.bind(windowManager); case 'TYPO3.Backend.Storage.ModuleStateStorage.update': return (await import('@typo3/backend/storage/module-state-storage')).ModuleStateStorage.update; @@ -49,13 +56,6 @@ export class ImmediateActionElement extends HTMLElement { } } - /** - * Observed attributes handled by `attributeChangedCallback`. - */ - public static get observedAttributes(): string[] { - return ['action', 'args', 'args-list']; - } - /** * Custom element life-cycle callback initializing attributes. */ @@ -82,7 +82,7 @@ export class ImmediateActionElement extends HTMLElement { if (!this.action) { throw new Error('Missing mandatory action attribute'); } - ImmediateActionElement.getDelegate(this.action).then((callback: Function): void => callback.apply(null, this.args)); + ImmediateActionElement.getDelegate(this.action).then((callback: Function): void => callback(...this.args)); } } diff --git a/Build/Sources/TypeScript/backend/element/spinner-element.ts b/Build/Sources/TypeScript/backend/element/spinner-element.ts index cd94f4e5c8e7..cb428d3a87f5 100644 --- a/Build/Sources/TypeScript/backend/element/spinner-element.ts +++ b/Build/Sources/TypeScript/backend/element/spinner-element.ts @@ -11,9 +11,9 @@ * The TYPO3 project - inspiring people to share! */ -import {html, css, LitElement, TemplateResult} from 'lit'; -import {customElement, property} from 'lit/decorators'; -import {Sizes} from '../enum/icon-types'; +import { html, css, LitElement, TemplateResult } from 'lit'; +import { customElement, property } from 'lit/decorators'; +import { Sizes } from '../enum/icon-types'; enum Variant { light = 'light', @@ -29,9 +29,6 @@ enum Variant { */ @customElement('typo3-backend-spinner') export class SpinnerElement extends LitElement { - @property({type: String}) size: Sizes = Sizes.default; - @property({type: String}) variant: Variant = Variant.dark; - static styles = css` :host { display: flex; @@ -77,6 +74,8 @@ export class SpinnerElement extends LitElement { font-size: 64px; } `; + @property({ type: String }) size: Sizes = Sizes.default; + @property({ type: String }) variant: Variant = Variant.dark; public render(): TemplateResult { return html` diff --git a/Build/Sources/TypeScript/backend/element/table-wizard-element.ts b/Build/Sources/TypeScript/backend/element/table-wizard-element.ts index 946b76f388ef..124590c75e06 100644 --- a/Build/Sources/TypeScript/backend/element/table-wizard-element.ts +++ b/Build/Sources/TypeScript/backend/element/table-wizard-element.ts @@ -33,13 +33,16 @@ import { SeverityEnum } from '@typo3/backend/enum/severity'; export class TableWizardElement extends LitElement { @property({ type: String }) type: string = 'textarea'; @property({ type: String }) selectorData: string = ''; - @property({ type: String}) delimiter: string = '|'; - @property({ type: String}) enclosure: string = ''; + @property({ type: String }) delimiter: string = '|'; + @property({ type: String }) enclosure: string = ''; @property({ type: Number, attribute: 'append-rows' }) appendRows: number = 1; - @property({ type: Object }) l10n: any = {}; private table: string[][] = []; + private get firstRow(): string[] { + return this.table[0] || []; + } + public connectedCallback(): void { super.connectedCallback(); @@ -49,10 +52,6 @@ export class TableWizardElement extends LitElement { this.readTableFromTextarea(); } - private get firstRow(): string[] { - return this.table[0] || []; - } - public createRenderRoot(): HTMLElement | ShadowRoot { // @todo Switch to Shadow DOM once Bootstrap CSS style can be applied correctly // const renderRoot = this.attachShadow({mode: 'open'}); @@ -73,8 +72,8 @@ export class TableWizardElement extends LitElement { } private readTableFromTextarea(): void { - let textarea: HTMLTextAreaElement = document.querySelector(this.selectorData); - let table: string[][] = []; + const textarea: HTMLTextAreaElement = document.querySelector(this.selectorData); + const table: string[][] = []; textarea.value.split('\n').forEach((row: string) => { if (row !== '') { @@ -82,8 +81,8 @@ export class TableWizardElement extends LitElement { row = row.replace(new RegExp(this.enclosure, 'g'), ''); } - let cols = row.split(this.delimiter) - table.push(cols) + const cols = row.split(this.delimiter); + table.push(cols); } }); @@ -91,19 +90,19 @@ export class TableWizardElement extends LitElement { } private writeTableSyntaxToTextarea(): void { - let textarea: HTMLTextAreaElement = document.querySelector(this.selectorData); + const textarea: HTMLTextAreaElement = document.querySelector(this.selectorData); let text = ''; this.table.forEach((row) => { - let count = row.length; + const count = row.length; text += row.reduce((result, word, index) => { // Do not add delimiter at the end of each row - let delimiter = (count - 1) === index ? '' : this.delimiter; + const delimiter = (count - 1) === index ? '' : this.delimiter; return result + this.enclosure + word + this.enclosure + delimiter; }, '') + '\n'; }); textarea.value = text; - textarea.dispatchEvent(new CustomEvent('change', {bubbles: true})); + textarea.dispatchEvent(new CustomEvent('change', { bubbles: true })); } private modifyTable(evt: Event, rowIndex: number, colIndex: number): void { @@ -113,11 +112,11 @@ export class TableWizardElement extends LitElement { this.requestUpdate(); } - private toggleType(evt: Event): void { + private toggleType(): void { this.type = this.type === 'input' ? 'textarea' : 'input'; } - private moveColumn(evt: Event, col: number, target: number): void { + private moveColumn(col: number, target: number): void { this.table = this.table.map((row: string[]): string[] => { const temp = row.splice(col, 1); row.splice(target, 0, ...temp); @@ -153,8 +152,8 @@ export class TableWizardElement extends LitElement { } private appendRow(evt: Event, row: number): void { - let columns = this.firstRow.concat().fill(''); - let rows = (new Array(this.appendRows)).fill(columns); + const columns = this.firstRow.concat().fill(''); + const rows = (new Array(this.appendRows)).fill(columns); this.table.splice(row + 1, 0, ...rows); this.writeTableSyntaxToTextarea(); this.requestUpdate(); @@ -220,7 +219,7 @@ export class TableWizardElement extends LitElement { return html` <span class="btn-group"> <button class="btn btn-default" type="button" title="${lll('table_smallFields')}" - @click="${(evt: Event) => this.toggleType(evt)}"> + @click="${() => this.toggleType()}"> <typo3-backend-icon identifier="${this.type === 'input' ? 'actions-chevron-expand' : 'actions-chevron-contract'}" size="small"></typo3-backend-icon> </button> <button class="btn btn-default" type="button" title="${lll('table_setCount')}" @@ -228,7 +227,7 @@ export class TableWizardElement extends LitElement { <typo3-backend-icon identifier="actions-plus" size="small"></typo3-backend-icon> </button> <button class="btn btn-default" type="button" title="${lll('table_showCode')}" - @click="${(evt: Event) => this.showTableSyntax(evt)}"> + @click="${() => this.showTableSyntax()}"> <typo3-backend-icon identifier="actions-code" size="small"></typo3-backend-icon> </button> </span> @@ -249,11 +248,11 @@ export class TableWizardElement extends LitElement { return html` <span class="btn-group"> <button class="btn btn-default" type="button" title="${leftButton.title}" - @click="${(evt: Event) => this.moveColumn(evt, col, leftButton.target)}"> + @click="${() => this.moveColumn(col, leftButton.target)}"> <typo3-backend-icon identifier="actions-chevron-${leftButton.class}" size="small"></typo3-backend-icon> </button> <button class="btn btn-default" type="button" title="${rightButton.title}" - @click="${(evt: Event) => this.moveColumn(evt, col, rightButton.target)}"> + @click="${() => this.moveColumn(col, rightButton.target)}"> <typo3-backend-icon identifier="actions-chevron-${rightButton.class}" size="small"></typo3-backend-icon> </button> <button class="btn btn-default" type="button" title="${lll('table_removeColumn')}" @@ -362,7 +361,7 @@ export class TableWizardElement extends LitElement { }); } - private showTableSyntax(evt: Event): void { + private showTableSyntax(): void { const modal = Modal.advanced({ content: '', // Callback is used to fill in content @@ -383,7 +382,7 @@ export class TableWizardElement extends LitElement { name: 'apply', trigger: (): void => { // Apply table changes - let textarea: HTMLTextAreaElement = document.querySelector(this.selectorData); + const textarea: HTMLTextAreaElement = document.querySelector(this.selectorData); textarea.value = modal.querySelector('textarea').value; this.readTableFromTextarea(); this.requestUpdate(); @@ -393,7 +392,7 @@ export class TableWizardElement extends LitElement { } ], callback: (currentModal: HTMLElement): void => { - let textarea: HTMLTextAreaElement = document.querySelector(this.selectorData); + const textarea: HTMLTextAreaElement = document.querySelector(this.selectorData); render( html`<textarea style="width: 100%;">${textarea.value}</textarea>`, diff --git a/Build/Sources/TypeScript/backend/event/event-dispatcher.ts b/Build/Sources/TypeScript/backend/event/event-dispatcher.ts index 97a416fc65d6..89d80959e6ea 100644 --- a/Build/Sources/TypeScript/backend/event/event-dispatcher.ts +++ b/Build/Sources/TypeScript/backend/event/event-dispatcher.ts @@ -16,7 +16,7 @@ */ export class EventDispatcher { static dispatchCustomEvent(name: string, detail: any = null, useTop: boolean = false): void { - const event = new CustomEvent(name, {detail: detail}); + const event = new CustomEvent(name, { detail: detail }); if (!useTop) { document.dispatchEvent(event); } else if (typeof top !== 'undefined') { diff --git a/Build/Sources/TypeScript/backend/event/interaction-request-map.ts b/Build/Sources/TypeScript/backend/event/interaction-request-map.ts index 7f6d9de6b6f5..97c5b06bd105 100644 --- a/Build/Sources/TypeScript/backend/event/interaction-request-map.ts +++ b/Build/Sources/TypeScript/backend/event/interaction-request-map.ts @@ -20,7 +20,7 @@ class InteractionRequestMap { public attachFor(request: InteractionRequest, deferred: any): void { let targetAssignment = this.getFor(request); if (targetAssignment === null) { - targetAssignment = {request, deferreds: []} as InteractionRequestAssignment; + targetAssignment = { request, deferreds: [] } as InteractionRequestAssignment; this.assignments.push(targetAssignment); } targetAssignment.deferreds.push(deferred); diff --git a/Build/Sources/TypeScript/backend/event/interaction-request.ts b/Build/Sources/TypeScript/backend/event/interaction-request.ts index d594fcb8eaf3..53c68b0865fe 100644 --- a/Build/Sources/TypeScript/backend/event/interaction-request.ts +++ b/Build/Sources/TypeScript/backend/event/interaction-request.ts @@ -17,7 +17,13 @@ class InteractionRequest { protected processed: boolean = false; protected processedData: any = null; + constructor(type: string, parentRequest: InteractionRequest = null) { + this.type = type; + this.parentRequest = parentRequest; + } + public get outerMostRequest(): InteractionRequest { + // eslint-disable-next-line @typescript-eslint/no-this-alias let request: InteractionRequest = this; while (request.parentRequest instanceof InteractionRequest) { request = request.parentRequest; @@ -25,11 +31,6 @@ class InteractionRequest { return request; } - constructor(type: string, parentRequest: InteractionRequest = null) { - this.type = type; - this.parentRequest = parentRequest; - } - public isProcessed(): boolean { return this.processed; } diff --git a/Build/Sources/TypeScript/backend/file-link-handler.ts b/Build/Sources/TypeScript/backend/file-link-handler.ts index 386fc6789255..e924177926b2 100644 --- a/Build/Sources/TypeScript/backend/file-link-handler.ts +++ b/Build/Sources/TypeScript/backend/file-link-handler.ts @@ -27,7 +27,7 @@ class FileLinkHandler { }).delegateTo(document, 'a.t3js-fileLink'); // Link to current page - new RegularEvent('click', (evt: MouseEvent, targetEl: HTMLElement): void => { + new RegularEvent('click', (evt: MouseEvent): void => { evt.preventDefault(); LinkBrowser.finalizeFunction(document.body.dataset.currentLink); }).delegateTo(document, 'input.t3js-linkCurrent'); diff --git a/Build/Sources/TypeScript/backend/form-engine-link-browser-adapter.ts b/Build/Sources/TypeScript/backend/form-engine-link-browser-adapter.ts index 24ed469115b5..6d2e4fc8072e 100644 --- a/Build/Sources/TypeScript/backend/form-engine-link-browser-adapter.ts +++ b/Build/Sources/TypeScript/backend/form-engine-link-browser-adapter.ts @@ -15,10 +15,10 @@ * Module: @typo3/backend/form-engine-link-browser-adapter * LinkBrowser communication with parent window */ -import LinkBrowser from '@typo3/backend/link-browser' +import LinkBrowser from '@typo3/backend/link-browser'; import Modal from '@typo3/backend/modal'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; interface OnFieldChangeItem { name: string; @@ -76,11 +76,11 @@ export default (function() { (new AjaxRequest(TYPO3.settings.ajaxUrls.link_browser_encodetypolink)) .withQueryArguments(attributeValues) .get() - .then(async (response: AjaxResponse): Promise<any> => { + .then(async (response: AjaxResponse): Promise<void> => { const data: Response = await response.resolve(); if (data.typoLink) { field.value = data.typoLink; - field.dispatchEvent(new Event('change', {bubbles: true, cancelable: true})); + field.dispatchEvent(new Event('change', { bubbles: true, cancelable: true })); if (FormEngineLinkBrowserAdapter.onFieldChangeItems instanceof Array) { // @todo us `CustomEvent` or broadcast channel as alternative diff --git a/Build/Sources/TypeScript/backend/form-engine-review.ts b/Build/Sources/TypeScript/backend/form-engine-review.ts index 356bc3d23668..f4c97af2af87 100644 --- a/Build/Sources/TypeScript/backend/form-engine-review.ts +++ b/Build/Sources/TypeScript/backend/form-engine-review.ts @@ -17,7 +17,7 @@ import $ from 'jquery'; import FormEngine from '@typo3/backend/form-engine'; import '@typo3/backend/element/icon-element'; import Popover from './popover'; -import {Popover as BootstrapPopover} from 'bootstrap'; +import { Popover as BootstrapPopover } from 'bootstrap'; /** * Module: @typo3/backend/form-engine-review @@ -36,6 +36,13 @@ class FormEngineReview { */ private readonly labelSelector: string = '.t3js-formengine-label'; + /** + * The constructor, set the class properties default values + */ + constructor() { + this.initialize(); + } + /** * Fetches all fields that have a failed validation * @@ -67,13 +74,6 @@ class FormEngineReview { leastButtonBar.prepend(button); } - /** - * The constructor, set the class properties default values - */ - constructor() { - this.initialize(); - } - /** * Initialize the events */ @@ -99,7 +99,7 @@ class FormEngineReview { } if ($invalidFields.length > 0) { - const $list: any = $('<div />', {class: 'list-group'}); + const $list: any = $('<div />', { class: 'list-group' }); $invalidFields.each(function(this: Element): void { const $field: any = $(this); @@ -123,7 +123,7 @@ class FormEngineReview { toggleButton.classList.add('hidden'); Popover.hide(toggleButton); } - } + }; /** * Finds the field in the form and focuses it @@ -133,15 +133,13 @@ class FormEngineReview { public switchToField = (e: Event, $referenceField: JQuery): void => { e.preventDefault(); - const listItem: HTMLElement = e.currentTarget as HTMLElement; - // iterate possibly nested tab panels $referenceField.parents('[id][role="tabpanel"]').each(function(this: Element): void { $('[aria-controls="' + $(this).attr('id') + '"]').tab('show'); }); $referenceField.focus(); - } + }; } // create an instance and return it diff --git a/Build/Sources/TypeScript/backend/form-engine-suggest.ts b/Build/Sources/TypeScript/backend/form-engine-suggest.ts index aac807e4a8c7..87e332b163d6 100644 --- a/Build/Sources/TypeScript/backend/form-engine-suggest.ts +++ b/Build/Sources/TypeScript/backend/form-engine-suggest.ts @@ -17,7 +17,7 @@ import FormEngine from '@typo3/backend/form-engine'; import RegularEvent from '@typo3/core/event/regular-event'; import DebounceEvent from '@typo3/core/event/debounce-event'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; class FormEngineSuggest { private readonly element: HTMLInputElement; @@ -124,7 +124,7 @@ class FormEngineSuggest { this.resultContainer.hidden = true; } - } + }; } export default FormEngineSuggest; diff --git a/Build/Sources/TypeScript/backend/form-engine-validation.ts b/Build/Sources/TypeScript/backend/form-engine-validation.ts index b73ca8337c18..1f8f18292215 100644 --- a/Build/Sources/TypeScript/backend/form-engine-validation.ts +++ b/Build/Sources/TypeScript/backend/form-engine-validation.ts @@ -17,7 +17,7 @@ * @internal */ import $ from 'jquery'; -import {DateTime} from 'luxon'; +import { DateTime } from 'luxon'; import Md5 from '@typo3/backend/hashing/md5'; import DocumentSaveActions from '@typo3/backend/document-save-actions'; import Modal from '@typo3/backend/modal'; @@ -136,15 +136,10 @@ export default (function() { if (!customEvaluations.has(name)) { customEvaluations.set(name, handler); } - } + }; /** * Format field value - * - * @param {String} type - * @param {String|Number} value - * @param {Object} config - * @returns {String} */ FormEngineValidation.formatValue = function(type: string, value: string|number, config: Object): string { let theString = ''; @@ -154,11 +149,10 @@ export default (function() { case 'date': // poor man’s ISO-8601 detection: if we have a "-" in it, it apparently is not an integer. if (value.toString().indexOf('-') > 0) { - const date = DateTime.fromISO(value.toString(), {zone: 'utc'}); + const date = DateTime.fromISO(value.toString(), { zone: 'utc' }); theString = date.toFormat('dd-MM-yyyy'); } else { - // @ts-ignore - parsedInt = value * 1; + parsedInt = parseInt(value.toString(), 10); if (!parsedInt) { return ''; } @@ -180,14 +174,14 @@ export default (function() { case 'timesec': let dateValue; if (value.toString().indexOf('-') > 0) { - dateValue = DateTime.fromISO(value.toString(), {zone: 'utc'}); + dateValue = DateTime.fromISO(value.toString(), { zone: 'utc' }); } else { // eslint-disable-next-line radix parsedInt = typeof value === 'number' ? value : parseInt(value); if (!parsedInt && value.toString() !== '0') { return ''; } - dateValue = DateTime.fromSeconds(parsedInt, {zone: 'utc'}); + dateValue = DateTime.fromSeconds(parsedInt, { zone: 'utc' }); } if (type === 'timesec') { theString = dateValue.toFormat('HH:mm:ss'); @@ -199,8 +193,7 @@ export default (function() { theString = (value) ? FormEngineValidation.passwordDummy : ''; break; default: - // @ts-ignore - theString = value; + theString = value.toString(); } return theString; }; @@ -249,7 +242,7 @@ export default (function() { */ FormEngineValidation.validateField = function(_field: HTMLInputElement|HTMLTextAreaElement|HTMLSelectElement|JQuery, value?: string): string { - const field = <HTMLInputElement|HTMLTextAreaElement|HTMLSelectElement>(_field instanceof $ ? (<JQuery>_field).get(0) : _field) + const field = <HTMLInputElement|HTMLTextAreaElement|HTMLSelectElement>(_field instanceof $ ? (<JQuery>_field).get(0) : _field); value = value || field.value || ''; @@ -261,7 +254,7 @@ export default (function() { let markParent = false; let selected = 0; // keep the original value, validateField should not alter it - let returnValue: string = value; + const returnValue: string = value; let $relatedField: JQuery; let minItems: number; let maxItems: number; @@ -270,7 +263,7 @@ export default (function() { value = value.trimStart(); } - for (let rule of rules) { + for (const rule of rules) { if (markParent) { // abort any further validation as validating the field already failed break; @@ -289,8 +282,7 @@ export default (function() { if ($relatedField.length) { selected = FormEngineValidation.trimExplode(',', $relatedField.val()).length; } else { - // @ts-ignore - selected = field.value; + selected = parseInt(field.value, 10); } if (typeof rule.minItems !== 'undefined') { minItems = rule.minItems * 1; @@ -307,15 +299,13 @@ export default (function() { } if (typeof rule.lower !== 'undefined') { const minValue = rule.lower * 1; - // @ts-ignore - if (!isNaN(minValue) && value < minValue) { + if (!isNaN(minValue) && parseInt(value, 10) < minValue) { markParent = true; } } if (typeof rule.upper !== 'undefined') { const maxValue = rule.upper * 1; - // @ts-ignore - if (!isNaN(maxValue) && value > maxValue) { + if (!isNaN(maxValue) && parseInt(value, 10) > maxValue) { markParent = true; } } @@ -421,7 +411,6 @@ export default (function() { FormEngineValidation.processValue = function(command: string, value: string, config: {is_in: string}): string { let newString = ''; let theValue = ''; - let theCmd = ''; let a = 0; let returnValue = value; switch (command) { @@ -462,8 +451,7 @@ export default (function() { if (config.is_in) { theValue = '' + value; // Escape special characters, see https://stackoverflow.com/a/6969486/4828813 - // eslint-disable-next-line @typescript-eslint/quotes - config.is_in = config.is_in.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + config.is_in = config.is_in.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); const re = new RegExp('[^' + config.is_in + ']+', 'g'); newString = theValue.replace(re, ''); } else { @@ -500,26 +488,22 @@ export default (function() { break; case 'datetime': if (value !== '') { - theCmd = value.substr(0, 1); returnValue = FormEngineValidation.parseDateTime(value); } break; case 'date': if (value !== '') { - theCmd = value.substr(0, 1); returnValue = FormEngineValidation.parseDate(value); } break; case 'time': case 'timesec': if (value !== '') { - theCmd = value.substr(0, 1); returnValue = FormEngineValidation.parseTime(value, command); } break; case 'year': if (value !== '') { - theCmd = value.substr(0, 1); returnValue = FormEngineValidation.parseYear(value); } break; @@ -550,7 +534,7 @@ export default (function() { } const sectionElement = section || document; - for (let field of sectionElement.querySelectorAll(FormEngineValidation.rulesSelector)) { + for (const field of sectionElement.querySelectorAll(FormEngineValidation.rulesSelector)) { const $field = $(field); if (!$field.closest('.t3js-flex-section-deleted, .t3js-inline-record-deleted, .t3js-file-reference-deleted').length) { @@ -624,13 +608,12 @@ export default (function() { */ FormEngineValidation.parseInt = function(value: number|string|boolean): number { const theVal = '' + value; - let returnValue; if (!value) { return 0; } - returnValue = parseInt(theVal, 10); + const returnValue = parseInt(theVal, 10); if (isNaN(returnValue)) { return 0; } @@ -646,7 +629,7 @@ export default (function() { */ FormEngineValidation.parseDouble = function(value: number|string|boolean, precision: number = 2): string { let theVal = '' + value; - theVal = theVal.replace(/[^0-9,\.-]/g, ''); + theVal = theVal.replace(/[^0-9,.-]/g, ''); const negative = theVal.substring(0, 1) === '-'; theVal = theVal.replace(/-/g, ''); theVal = theVal.replace(/,/g, '.'); @@ -689,7 +672,7 @@ export default (function() { * @returns {*} */ FormEngineValidation.parseDate = function(value: string): number { - FormEngineValidation.lastDate = DateTime.fromFormat(value, 'dd-MM-yyyy', {zone: 'utc'}).toUnixInteger(); + FormEngineValidation.lastDate = DateTime.fromFormat(value, 'dd-MM-yyyy', { zone: 'utc' }).toUnixInteger(); return FormEngineValidation.lastDate; }; @@ -703,7 +686,7 @@ export default (function() { */ FormEngineValidation.parseTime = function(value: string, type: string): number { const format = type === 'timesec' ? 'HH:mm:ss' : 'HH:mm'; - FormEngineValidation.lastTime = DateTime.fromFormat(value, format, {zone: 'utc'}).set({ + FormEngineValidation.lastTime = DateTime.fromFormat(value, format, { zone: 'utc' }).set({ year: 1970, month: 1, day: 1 @@ -847,7 +830,7 @@ export default (function() { e.stopImmediatePropagation(); } }); - } + }; return FormEngineValidation; })(); diff --git a/Build/Sources/TypeScript/backend/form-engine.ts b/Build/Sources/TypeScript/backend/form-engine.ts index cda9718dabf7..f428d163b1e4 100644 --- a/Build/Sources/TypeScript/backend/form-engine.ts +++ b/Build/Sources/TypeScript/backend/form-engine.ts @@ -24,7 +24,7 @@ import DocumentService from '@typo3/core/document-service'; import $ from 'jquery'; import FormEngineValidation from '@typo3/backend/form-engine-validation'; import Icons from '@typo3/backend/icons'; -import {default as Modal, ModalElement} from '@typo3/backend/modal'; +import { default as Modal, ModalElement } from '@typo3/backend/modal'; import * as MessageUtility from '@typo3/backend/utility/message-utility'; import Severity from '@typo3/backend/severity'; import * as BackendExceptionModule from '@typo3/backend/backend-exception'; @@ -54,7 +54,7 @@ export default (function() { const onFieldChangeHandlers: Map<string, Function> = new Map(); // @see \TYPO3\CMS\Backend\Form\Behavior\UpdateValueOnFieldChange - onFieldChangeHandlers.set('typo3-backend-form-update-value', (data: {elementName: string}, evt: Event) => { + onFieldChangeHandlers.set('typo3-backend-form-update-value', (data: {elementName: string}) => { const valueField = document.querySelector('[name="' + CSS.escape(data.elementName) + '"]'); const humanReadableField = document.querySelector('[data-formengine-input-name="' + CSS.escape(data.elementName) + '"]'); FormEngineValidation.updateInputField(data.elementName); @@ -67,7 +67,7 @@ export default (function() { } }); // @see \TYPO3\CMS\Backend\Form\Behavior\ReloadOnFieldChange - onFieldChangeHandlers.set('typo3-backend-form-reload', (data: {confirmation: boolean}, evt: Event) => { + onFieldChangeHandlers.set('typo3-backend-form-reload', (data: {confirmation: boolean}) => { if (!data.confirmation) { FormEngine.saveDocument(); return; @@ -104,7 +104,7 @@ export default (function() { const mask = Math.pow(2, data.position); const unmask = Math.pow(2, data.total) - mask - 1; elementRef.value = active ? (elementRef.value | mask) : (elementRef.value & unmask); - elementRef.dispatchEvent(new Event('change', {bubbles: true, cancelable: true})); + elementRef.dispatchEvent(new Event('change', { bubbles: true, cancelable: true })); }); /** @@ -162,13 +162,12 @@ export default (function() { let fieldEl, $fieldEl, - originalFieldEl, isMultiple = false, isList = false; $fieldEl = FormEngine.getFieldElement(fieldName); fieldEl = $fieldEl.get(0); - originalFieldEl = $fieldEl.get(0); + const originalFieldEl = $fieldEl.get(0); if (originalFieldEl === null || value === '--div--' || originalFieldEl instanceof HTMLOptGroupElement) { return; @@ -190,7 +189,7 @@ export default (function() { // If multiple values are not allowed, clear anything that is in the control already if (!isMultiple) { - for (let el of fieldEl.querySelectorAll('option') as NodeListOf<HTMLOptionElement>) { + for (const el of fieldEl.querySelectorAll('option') as NodeListOf<HTMLOptionElement>) { const $option = $availableFieldEl.find('option[value="' + $.escapeSelector($(el).attr('value')) + '"]'); if ($option.length) { $option.removeClass('hidden').prop('disabled', false); @@ -233,7 +232,7 @@ export default (function() { // check if there is a "_mul" field (a field on the right) and if the field was already added const $multipleFieldEl = FormEngine.getFieldElement(fieldName, '_mul', true); if ($multipleFieldEl.length == 0 || $multipleFieldEl.val() == 0) { - for (let optionEl of fieldEl.querySelectorAll('option') as NodeListOf<HTMLOptionElement>) { + for (const optionEl of fieldEl.querySelectorAll('option') as NodeListOf<HTMLOptionElement>) { if (optionEl.value == value) { addNewValue = false; break; @@ -258,7 +257,7 @@ export default (function() { if (addNewValue) { // finally add the option const $option = $('<option></option>'); - $option.attr({value: value, title: title}).text(label); + $option.attr({ value: value, title: title }).text(label); $option.appendTo($fieldEl); // set the hidden field @@ -302,7 +301,7 @@ export default (function() { // make a comma separated list, if it is a multi-select // set the values to the final hidden field originalFieldEl.value = selectedValues.join(','); - originalFieldEl.dispatchEvent(new Event('change', {bubbles: true, cancelable: true})); + originalFieldEl.dispatchEvent(new Event('change', { bubbles: true, cancelable: true })); }; /** @@ -416,7 +415,7 @@ export default (function() { }).on('change', '.t3js-form-field-eval-null-placeholder-checkbox input[type="checkbox"]', (e: JQueryEventObject) => { FormEngine.toggleCheckboxField($(e.currentTarget)); FormEngineValidation.markFieldAsChanged($(e.currentTarget)); - }).on('change', function(event: Event) { + }).on('change', () => { $('.module-docheader-bar .btn').removeClass('disabled').prop('disabled', false); }).on('click', '.t3js-element-browser', function(e: Event) { e.preventDefault(); @@ -488,7 +487,7 @@ export default (function() { } else if (FormEngine.hasChange()) { FormEngine.preventExitIfNotSaved(function(response: boolean) { outerMostRequest.setProcessedData( - {response: response} + { response: response } ); handleConsumeResponse(outerMostRequest, response); }); @@ -540,8 +539,8 @@ export default (function() { $wrapper.addClass('t3js-charcounter-wrapper'); $parent.append($wrapper); } - $wrapper.append($('<div />', {'class': 't3js-charcounter'}).append( - $('<span />', {'class': maxlengthProperties.labelClass}).text(TYPO3.lang['FormEngine.remainingCharacters'].replace('{0}', maxlengthProperties.remainingCharacters)) + $wrapper.append($('<div />', { 'class': 't3js-charcounter' }).append( + $('<span />', { 'class': maxlengthProperties.labelClass }).text(TYPO3.lang['FormEngine.remainingCharacters'].replace('{0}', maxlengthProperties.remainingCharacters)) )); }).on('blur', (event: JQueryEventObject) => { const $field = $(event.currentTarget), @@ -553,7 +552,7 @@ export default (function() { maxlengthProperties = FormEngine.getCharacterCounterProperties($field); // change class and value - $parent.find('.t3js-charcounter span').removeClass().addClass(maxlengthProperties.labelClass).text(TYPO3.lang['FormEngine.remainingCharacters'].replace('{0}', maxlengthProperties.remainingCharacters)) + $parent.find('.t3js-charcounter span').removeClass().addClass(maxlengthProperties.labelClass).text(TYPO3.lang['FormEngine.remainingCharacters'].replace('{0}', maxlengthProperties.remainingCharacters)); }); $maxlengthElements.addClass('t3js-charcounter-initialized'); }; @@ -566,10 +565,9 @@ export default (function() { */ FormEngine.getCharacterCounterProperties = function($field: JQuery): {remainingCharacters: number, labelClass: string} { const fieldText = $field.val(), - maxlength = $field.attr('maxlength'), + maxlength = parseInt($field.attr('maxlength'), 10), currentFieldLength = fieldText.length, numberOfLineBreaks = (fieldText.match(/\n/g) || []).length, // count line breaks - // @ts-ignore remainingCharacters = maxlength - currentFieldLength - numberOfLineBreaks, threshold = 15; // hard limit of remaining characters when the label class changes let labelClass = ''; @@ -761,7 +759,7 @@ export default (function() { /** * @param {boolean} response */ - FormEngine.preventExitIfNotSavedCallback = function(response: boolean): void { + FormEngine.preventExitIfNotSavedCallback = (): void => { FormEngine.closeDocument(); }; @@ -910,7 +908,7 @@ export default (function() { console.warn('Handler for onFieldChange name `' + name + '` has been overridden.'); } onFieldChangeHandlers.set(name, handler); - } + }; FormEngine.closeModalsRecursive = function() { if (typeof Modal.currentModal !== 'undefined' && Modal.currentModal !== null) { @@ -919,7 +917,7 @@ export default (function() { }); Modal.currentModal.hideModal(); } - } + }; /** * Preview action @@ -935,7 +933,7 @@ export default (function() { callback = callback || FormEngine.previewActionCallback; const previewUrl = (event.currentTarget as HTMLAnchorElement).href; - const isNew = (event.target as HTMLAnchorElement).dataset.hasOwnProperty('isNew'); + const isNew = ('isNew' in (event.target as HTMLAnchorElement).dataset); const $actionElement = $('<input />').attr('type', 'hidden').attr('name', '_savedokview').attr('value', '1'); if (FormEngine.hasChange()) { FormEngine.showPreviewModal(previewUrl, isNew, $actionElement, callback); @@ -1020,7 +1018,7 @@ export default (function() { content = ( TYPO3.lang['label.confirm.view_record_changed.content'] || 'You currently have unsaved changes. You can either discard these changes or save and view them.' - ) + ); } const modal = Modal.confirm(title, content, Severity.info, modalButtons); modal.addEventListener('button.clicked', function (event: Event) { @@ -1042,7 +1040,7 @@ export default (function() { callback = callback || FormEngine.newActionCallback; const $actionElement = $('<input />').attr('type', 'hidden').attr('name', '_savedoknew').attr('value', '1'); - const isNew = (event.target as HTMLElement).dataset.hasOwnProperty('isNew'); + const isNew = ('isNew' in (event.target as HTMLElement).dataset); if (FormEngine.hasChange()) { FormEngine.showNewModal(isNew, $actionElement, callback); } else { @@ -1136,7 +1134,7 @@ export default (function() { callback = callback || FormEngine.duplicateActionCallback; const $actionElement = $('<input />').attr('type', 'hidden').attr('name', '_duplicatedoc').attr('value', '1'); - const isNew = (event.target as HTMLElement).dataset.hasOwnProperty('isNew'); + const isNew = ('isNew' in (event.target as HTMLElement).dataset); if (FormEngine.hasChange()) { FormEngine.showDuplicateModal(isNew, $actionElement, callback); } else { @@ -1293,7 +1291,7 @@ export default (function() { optGroup.disabled = false; optGroup.classList.remove('hidden'); } - } + }; /** * Close current open document @@ -1345,7 +1343,7 @@ export default (function() { // load required modules to hook in the post initialize function if (undefined !== TYPO3.settings.RequireJS && undefined !== TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/FormEngine']) { - for (let moduleName of TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/FormEngine']) { + for (const moduleName of TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/FormEngine']) { window.require([moduleName]); } } diff --git a/Build/Sources/TypeScript/backend/form-engine/container/files-control-container.ts b/Build/Sources/TypeScript/backend/form-engine/container/files-control-container.ts index f3dc1945a14a..854811a5174f 100644 --- a/Build/Sources/TypeScript/backend/form-engine/container/files-control-container.ts +++ b/Build/Sources/TypeScript/backend/form-engine/container/files-control-container.ts @@ -12,16 +12,16 @@ */ import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {MessageUtility} from '../../utility/message-utility'; -import {AjaxDispatcher} from './../inline-relation/ajax-dispatcher'; -import {InlineResponseInterface} from './../inline-relation/inline-response-interface'; +import { MessageUtility } from '../../utility/message-utility'; +import { AjaxDispatcher } from './../inline-relation/ajax-dispatcher'; +import { InlineResponseInterface } from './../inline-relation/inline-response-interface'; import NProgress from 'nprogress'; import Sortable from 'sortablejs'; import FormEngine from '@typo3/backend/form-engine'; import FormEngineValidation from '@typo3/backend/form-engine-validation'; import Icons from '../../icons'; import InfoWindow from '../../info-window'; -import Modal, {ModalElement} from '../../modal'; +import Modal, { ModalElement } from '../../modal'; import RegularEvent from '@typo3/core/event/regular-event'; import Severity from '../../severity'; import Utility from '../../utility'; @@ -220,9 +220,9 @@ class FilesControlContainer extends HTMLElement { } }); } - } + }; - private createRecord(uid: string, markup: string, afterUid: string = null, selectedValue: string = null): void { + private createRecord(uid: string, markup: string, afterUid: string = null): void { let objectId = this.container.dataset.objectGroup; if (afterUid !== null) { objectId += Separators.structureSeparator + afterUid; @@ -230,10 +230,10 @@ class FilesControlContainer extends HTMLElement { if (afterUid !== null) { FilesControlContainer.getFileReferenceContainer(objectId).insertAdjacentHTML('afterend', markup); - this.memorizeAddRecord(uid, afterUid, selectedValue); + this.memorizeAddRecord(uid, afterUid); } else { document.getElementById(this.container.getAttribute('id') + '_records').insertAdjacentHTML('beforeend', markup); - this.memorizeAddRecord(uid, null, selectedValue); + this.memorizeAddRecord(uid, null); } } @@ -246,8 +246,7 @@ class FilesControlContainer extends HTMLElement { this.createRecord( response.compilerInput.uid, response.data, - typeof afterUid !== 'undefined' ? afterUid : null, - typeof response.compilerInput.childChildUid !== 'undefined' ? response.compilerInput.childChildUid : null, + typeof afterUid !== 'undefined' ? afterUid : null ); } }); @@ -335,21 +334,21 @@ class FilesControlContainer extends HTMLElement { me.ajaxDispatcher.send( me.ajaxDispatcher.newRequest(me.ajaxDispatcher.getEndpoint('file_reference_synchronizelocalize')), [me.container.dataset.objectGroup, this.dataset.type], - ).then(async (response: InlineResponseInterface): Promise<any> => { + ).then(async (response: InlineResponseInterface): Promise<void> => { document.getElementById(me.container.getAttribute('id') + '_records').insertAdjacentHTML('beforeend', response.data); const objectIdPrefix = me.container.dataset.objectGroup + Separators.structureSeparator; - for (let itemUid of response.compilerInput.delete) { + for (const itemUid of response.compilerInput.delete) { me.deleteRecord(objectIdPrefix + itemUid, true); } - for (let item of Object.values(response.compilerInput.localize)) { + for (const item of Object.values(response.compilerInput.localize)) { if (typeof item.remove !== 'undefined') { const removableRecordContainer = FilesControlContainer.getFileReferenceContainer(objectIdPrefix + item.remove); removableRecordContainer.parentElement.removeChild(removableRecordContainer); } - me.memorizeAddRecord(item.uid, null, item.selectedValue); + me.memorizeAddRecord(item.uid, null); } }); }).delegateTo(this.container, Selectors.synchronizeLocalizeRecordButtonSelector); @@ -368,7 +367,7 @@ class FilesControlContainer extends HTMLElement { const ajaxRequest = this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint('file_reference_details')); const request = this.ajaxDispatcher.send(ajaxRequest, [objectId]); - request.then(async (response: InlineResponseInterface): Promise<any> => { + request.then(async (response: InlineResponseInterface): Promise<void> => { delete this.requestQueue[objectId]; delete this.progessQueue[objectId]; @@ -426,7 +425,7 @@ class FilesControlContainer extends HTMLElement { ); } - private memorizeAddRecord(newUid: string, afterUid: string = null, selectedValue: string = null): void { + private memorizeAddRecord(newUid: string, afterUid: string = null): void { const formField = this.getFormFieldForElements(); if (formField === null) { return; @@ -469,7 +468,7 @@ class FilesControlContainer extends HTMLElement { return []; } - let records = Utility.trimExplode(',', (<HTMLInputElement>formField).value); + const records = Utility.trimExplode(',', (<HTMLInputElement>formField).value); const indexOfRemoveUid = records.indexOf(objectUid); if (indexOfRemoveUid > -1) { delete records[indexOfRemoveUid]; @@ -489,7 +488,7 @@ class FilesControlContainer extends HTMLElement { const objectUid = fileReferenceContainer.dataset.objectUid; const recordListContainer = <HTMLDivElement>document.getElementById(this.container.getAttribute('id') + '_records'); const records = Array.from(recordListContainer.children).map((child: HTMLElement) => child.dataset.objectUid); - let position = records.indexOf(objectUid); + const position = records.indexOf(objectUid); let isChanged = false; if (direction === SortDirections.UP && position > 0) { @@ -578,7 +577,7 @@ class FilesControlContainer extends HTMLElement { progress = this.progessQueue[objectId]; } else { progress = NProgress; - progress.configure({parent: headerIdentifier, showSpinner: false}); + progress.configure({ parent: headerIdentifier, showSpinner: false }); this.progessQueue[objectId] = progress; } @@ -591,7 +590,7 @@ class FilesControlContainer extends HTMLElement { if (formField !== null) { const records = Utility.trimExplode(',', (<HTMLInputElement>formField).value); - for (let recordUid of records) { + for (const recordUid of records) { if (recordUid === excludeUid) { continue; } diff --git a/Build/Sources/TypeScript/backend/form-engine/container/flex-form-container-container.ts b/Build/Sources/TypeScript/backend/form-engine/container/flex-form-container-container.ts index e0a07568c723..b8f051bf1ece 100644 --- a/Build/Sources/TypeScript/backend/form-engine/container/flex-form-container-container.ts +++ b/Build/Sources/TypeScript/backend/form-engine/container/flex-form-container-container.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {Collapse} from 'bootstrap'; +import { Collapse } from 'bootstrap'; import SecurityUtility from '@typo3/core/security-utility'; import FlexFormSectionContainer from './flex-form-section-container'; import Modal from '@typo3/backend/modal'; @@ -44,10 +44,6 @@ class FlexFormContainerContainer { private readonly panelButton: HTMLElement; private readonly toggleField: HTMLInputElement; - private static getCollapseInstance(container: HTMLElement): Collapse { - return Collapse.getInstance(container) ?? new Collapse(container, {toggle: false}) - } - constructor(parentContainer: FlexFormSectionContainer, container: HTMLElement) { this.securityUtility = new SecurityUtility(); this.parentContainer = parentContainer; @@ -63,11 +59,15 @@ class FlexFormContainerContainer { this.generatePreview(); } + private static getCollapseInstance(container: HTMLElement): Collapse { + return Collapse.getInstance(container) ?? new Collapse(container, { toggle: false }); + } + public getStatus(): ContainerStatus { return { id: this.containerId, collapsed: this.panelButton.getAttribute('aria-expanded') === 'false', - } + }; } private registerEvents(): void { @@ -144,7 +144,7 @@ class FlexFormContainerContainer { let previewContent = ''; if (this.getStatus().collapsed) { const formFields: NodeListOf<HTMLInputElement|HTMLTextAreaElement> = this.containerContent.querySelectorAll('input[type="text"], textarea'); - for (let field of formFields) { + for (const field of formFields) { let content = this.securityUtility.stripHtml(field.value); if (content.length > 50) { content = content.substring(0, 50) + '...'; diff --git a/Build/Sources/TypeScript/backend/form-engine/container/flex-form-section-container.ts b/Build/Sources/TypeScript/backend/form-engine/container/flex-form-section-container.ts index af49d4c6d2d1..9b9403db7f6c 100644 --- a/Build/Sources/TypeScript/backend/form-engine/container/flex-form-section-container.ts +++ b/Build/Sources/TypeScript/backend/form-engine/container/flex-form-section-container.ts @@ -11,15 +11,15 @@ * The TYPO3 project - inspiring people to share! */ -import {Collapse} from 'bootstrap'; +import { Collapse } from 'bootstrap'; import Sortable from 'sortablejs'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import DocumentService from '@typo3/core/document-service'; import FlexFormContainerContainer from './flex-form-container-container'; import FormEngine from '@typo3/backend/form-engine'; import RegularEvent from '@typo3/core/event/regular-event'; -import {JavaScriptItemProcessor} from '@typo3/core/java-script-item-processor'; +import { JavaScriptItemProcessor } from '@typo3/core/java-script-item-processor'; enum Selectors { toggleAllSelector = '.t3-form-flexsection-toggle', @@ -37,10 +37,6 @@ class FlexFormSectionContainer { private allowRestructure: boolean = false; private flexformContainerContainers: FlexFormContainerContainer[] = []; - private static getCollapseInstance(container: HTMLElement): Collapse { - return Collapse.getInstance(container) ?? new Collapse(container, {toggle: false}) - } - /** * @param {string} elementId */ @@ -57,6 +53,10 @@ class FlexFormSectionContainer { }); } + private static getCollapseInstance(container: HTMLElement): Collapse { + return Collapse.getInstance(container) ?? new Collapse(container, { toggle: false }); + } + public getContainer(): HTMLElement { return this.container; } @@ -78,7 +78,7 @@ class FlexFormSectionContainer { private registerContainers(): void { const sectionContainerContainers: NodeListOf<HTMLElement> = this.container.querySelectorAll(Selectors.sectionContainerSelector); - for (let sectionContainerContainer of sectionContainerContainers) { + for (const sectionContainerContainer of sectionContainerContainers) { this.flexformContainerContainers.push(new FlexFormContainerContainer(this, sectionContainerContainer)); } @@ -106,7 +106,7 @@ class FlexFormSectionContainer { this.updateToggleAllState(); this.flexformContainerContainers.splice(e.newIndex, 0, this.flexformContainerContainers.splice(e.oldIndex, 1)[0]); document.dispatchEvent(new Event('formengine:flexform:sorting-changed')); - } + }; private registerToggleAll(): void { new RegularEvent('click', (e: Event): void => { @@ -114,7 +114,7 @@ class FlexFormSectionContainer { const showAll = trigger.dataset.expandAll === 'true'; const collapsibles: NodeListOf<HTMLElement> = this.container.querySelectorAll(Selectors.sectionContentContainerSelector); - for (let collapsible of collapsibles) { + for (const collapsible of collapsibles) { if (showAll) { FlexFormSectionContainer.getCollapseInstance(collapsible).show(); } else { @@ -143,7 +143,7 @@ class FlexFormSectionContainer { flexFormSheetName: dataset.flexformsheetname, flexFormFieldName: dataset.flexformfieldname, flexFormContainerName: dataset.flexformcontainername, - }).then(async (response: AjaxResponse): Promise<any> => { + }).then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); const createdContainer = new DOMParser().parseFromString(data.html, 'text/html').body.firstElementChild as HTMLElement; @@ -159,14 +159,14 @@ class FlexFormSectionContainer { // @todo deprecate or remove with TYPO3 v12.0 if (data.scriptCall && data.scriptCall.length > 0) { - for (let value of data.scriptCall) { + for (const value of data.scriptCall) { // eslint-disable-next-line no-eval eval(value); } } if (data.stylesheetFiles && data.stylesheetFiles.length > 0) { - for (let stylesheetFile of data.stylesheetFiles) { - let element = document.createElement('link'); + for (const stylesheetFile of data.stylesheetFiles) { + const element = document.createElement('link'); element.rel = 'stylesheet'; element.type = 'text/css'; element.href = stylesheetFile; @@ -207,7 +207,7 @@ class FlexFormSectionContainer { private updateToggleAllState(): void { if (this.flexformContainerContainers.length > 0) { const firstContainer = this.flexformContainerContainers.find(Boolean); - this.getToggleAllButton().dataset.expandAll = firstContainer.getStatus().collapsed === true ? 'true' : 'false' + this.getToggleAllButton().dataset.expandAll = firstContainer.getStatus().collapsed === true ? 'true' : 'false'; } } } diff --git a/Build/Sources/TypeScript/backend/form-engine/container/inline-control-container.ts b/Build/Sources/TypeScript/backend/form-engine/container/inline-control-container.ts index 5978971150d8..7fd825bf10ec 100644 --- a/Build/Sources/TypeScript/backend/form-engine/container/inline-control-container.ts +++ b/Build/Sources/TypeScript/backend/form-engine/container/inline-control-container.ts @@ -12,9 +12,9 @@ */ import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {MessageUtility} from '../../utility/message-utility'; -import {AjaxDispatcher} from './../inline-relation/ajax-dispatcher'; -import {InlineResponseInterface} from './../inline-relation/inline-response-interface'; +import { MessageUtility } from '../../utility/message-utility'; +import { AjaxDispatcher } from './../inline-relation/ajax-dispatcher'; +import { InlineResponseInterface } from './../inline-relation/inline-response-interface'; import DocumentService from '@typo3/core/document-service'; import NProgress from 'nprogress'; import Sortable from 'sortablejs'; @@ -76,7 +76,7 @@ interface UniqueDefinition { elTable: string; field: string; max: number; - possible: { [key: string]: string }; + possible: Record<string, string>; selector: string; table: string; type: string; @@ -100,6 +100,18 @@ class InlineControlContainer { private progessQueue: ProgressQueue = {}; private noTitleString: string = (TYPO3.lang ? TYPO3.lang['FormEngine.noRecordTitle'] : '[No title]'); + /** + * @param {string} elementId + */ + constructor(elementId: string) { + DocumentService.ready().then((document: Document): void => { + this.container = <HTMLElement>document.getElementById(elementId); + this.ajaxDispatcher = new AjaxDispatcher(this.container.dataset.objectGroup); + + this.registerEvents(); + }); + } + /** * @param {string} objectId * @return HTMLDivElement @@ -212,7 +224,7 @@ class InlineControlContainer { const options: NodeListOf<HTMLOptionElement> = selectElement.querySelectorAll('option'); let index: number = -1; - for (let possibleValue of Object.keys(unique.possible)) { + for (const possibleValue of Object.keys(unique.possible)) { if (possibleValue === value) { break; } @@ -239,18 +251,6 @@ class InlineControlContainer { selectElement.insertBefore(readdOption, selectElement.options[index]); } - /** - * @param {string} elementId - */ - constructor(elementId: string) { - DocumentService.ready().then((document: Document): void => { - this.container = <HTMLElement>document.getElementById(elementId); - this.ajaxDispatcher = new AjaxDispatcher(this.container.dataset.objectGroup); - - this.registerEvents(); - }); - } - private registerEvents(): void { this.registerInfoButton(); this.registerSort(); @@ -367,7 +367,7 @@ class InlineControlContainer { } }); } - } + }; /** * @param {string} uid @@ -498,15 +498,15 @@ class InlineControlContainer { me.ajaxDispatcher.send( me.ajaxDispatcher.newRequest(me.ajaxDispatcher.getEndpoint('record_inline_synchronizelocalize')), [me.container.dataset.objectGroup, this.dataset.type], - ).then(async (response: InlineResponseInterface): Promise<any> => { + ).then(async (response: InlineResponseInterface): Promise<void> => { document.getElementById(me.container.getAttribute('id') + '_records').insertAdjacentHTML('beforeend', response.data); const objectIdPrefix = me.container.dataset.objectGroup + Separators.structureSeparator; - for (let itemUid of response.compilerInput.delete) { + for (const itemUid of response.compilerInput.delete) { me.deleteRecord(objectIdPrefix + itemUid, true); } - for (let item of Object.values(response.compilerInput.localize)) { + for (const item of Object.values(response.compilerInput.localize)) { if (typeof item.remove !== 'undefined') { const removableRecordContainer = InlineControlContainer.getInlineRecordContainer(objectIdPrefix + item.remove); removableRecordContainer.parentElement.removeChild(removableRecordContainer); @@ -565,7 +565,7 @@ class InlineControlContainer { const ajaxRequest = this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint('record_inline_details')); const request = this.ajaxDispatcher.send(ajaxRequest, [objectId]); - request.then(async (response: InlineResponseInterface): Promise<any> => { + request.then(async (response: InlineResponseInterface): Promise<void> => { delete this.requestQueue[objectId]; delete this.progessQueue[objectId]; @@ -687,7 +687,7 @@ class InlineControlContainer { return []; } - let records = Utility.trimExplode(',', (<HTMLInputElement>formField).value); + const records = Utility.trimExplode(',', (<HTMLInputElement>formField).value); const indexOfRemoveUid = records.indexOf(objectUid); if (indexOfRemoveUid > -1) { delete records[indexOfRemoveUid]; @@ -711,7 +711,7 @@ class InlineControlContainer { const recordUid = currentRecordContainer.dataset.objectUid; const recordListContainer = <HTMLDivElement>document.getElementById(this.container.getAttribute('id') + '_records'); const records = Array.from(recordListContainer.children).map((child: HTMLElement) => child.dataset.objectUid); - let position = records.indexOf(recordUid); + const position = records.indexOf(recordUid); let isChanged = false; if (direction === SortDirections.UP && position > 0) { @@ -798,7 +798,7 @@ class InlineControlContainer { return; } controlContainer.forEach((container: HTMLElement): void => { - let controlContainerButtons = container.querySelectorAll('button, a'); + const controlContainerButtons = container.querySelectorAll('button, a'); controlContainerButtons.forEach((button: HTMLElement): void => { button.style.display = visible ? null : 'none'; }); @@ -817,7 +817,7 @@ class InlineControlContainer { progress = this.progessQueue[objectId]; } else { progress = NProgress; - progress.configure({parent: headerIdentifier, showSpinner: false}); + progress.configure({ parent: headerIdentifier, showSpinner: false }); this.progessQueue[objectId] = progress; } @@ -833,7 +833,7 @@ class InlineControlContainer { if (formField !== null) { const records = Utility.trimExplode(',', (<HTMLInputElement>formField).value); - for (let recordUid of records) { + for (const recordUid of records) { if (recordUid === excludeUid) { continue; } @@ -987,14 +987,14 @@ class InlineControlContainer { return; } - let uniqueValueField = <HTMLSelectElement>recordContainer.querySelector( + const uniqueValueField = <HTMLSelectElement>recordContainer.querySelector( '[name="data[' + unique.table + '][' + recordContainer.dataset.objectUid + '][' + unique.field + ']"]', ); const values = InlineControlContainer.getValuesFromHashMap(unique.used); if (uniqueValueField !== null) { const selectedValue = uniqueValueField.options[uniqueValueField.selectedIndex].value; - for (let value of values) { + for (const value of values) { if (value !== selectedValue) { InlineControlContainer.removeSelectOptionByValue(uniqueValueField, value); } @@ -1026,7 +1026,7 @@ class InlineControlContainer { if (selectorElement !== null) { // remove all items from the new select-item which are already used in other children if (uniqueValueField !== null) { - for (let value of values) { + for (const value of values) { InlineControlContainer.removeSelectOptionByValue(uniqueValueField, value); } // set the selected item automatically to the first of the remaining items if no selector is used @@ -1037,7 +1037,7 @@ class InlineControlContainer { this.handleChangedField(uniqueValueField, this.container.dataset.objectGroup + '[' + recordUid + ']'); } } - for (let value of values) { + for (const value of values) { InlineControlContainer.removeSelectOptionByValue(uniqueValueField, value); } if (typeof unique.used.length !== 'undefined') { @@ -1051,7 +1051,7 @@ class InlineControlContainer { // remove the newly used item from each select-field of the child records if (formField !== null && InlineControlContainer.selectOptionValueExists(selectorElement, selectedValue)) { const records = Utility.trimExplode(',', (<HTMLInputElement>formField).value); - for (let record of records) { + for (const record of records) { uniqueValueField = <HTMLSelectElement>document.querySelector( '[name="data[' + unique.table + '][' + record + '][' + unique.field + ']"]', ); @@ -1111,7 +1111,7 @@ class InlineControlContainer { const records = Utility.trimExplode(',', formField.value); let uniqueValueField; - for (let record of records) { + for (const record of records) { uniqueValueField = <HTMLSelectElement>document.querySelector( '[name="data[' + unique.table + '][' + record + '][' + unique.field + ']"]', ); @@ -1137,7 +1137,7 @@ class InlineControlContainer { const recordObjectId = this.container.dataset.objectGroup + Separators.structureSeparator + recordUid; const recordContainer = InlineControlContainer.getInlineRecordContainer(recordObjectId); - let uniqueValueField = <HTMLSelectElement>recordContainer.querySelector( + const uniqueValueField = <HTMLSelectElement>recordContainer.querySelector( '[name="data[' + unique.table + '][' + recordContainer.dataset.objectUid + '][' + unique.field + ']"]', ); if (unique.type === 'select') { diff --git a/Build/Sources/TypeScript/backend/form-engine/container/site-language-container.ts b/Build/Sources/TypeScript/backend/form-engine/container/site-language-container.ts index 4bcbe135fe51..ce40a6a87e37 100644 --- a/Build/Sources/TypeScript/backend/form-engine/container/site-language-container.ts +++ b/Build/Sources/TypeScript/backend/form-engine/container/site-language-container.ts @@ -12,13 +12,13 @@ */ import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {MessageUtility} from '../../utility/message-utility'; -import {AjaxDispatcher} from './../inline-relation/ajax-dispatcher'; -import {InlineResponseInterface} from './../inline-relation/inline-response-interface'; +import { MessageUtility } from '../../utility/message-utility'; +import { AjaxDispatcher } from './../inline-relation/ajax-dispatcher'; +import { InlineResponseInterface } from './../inline-relation/inline-response-interface'; import NProgress from 'nprogress'; import FormEngine from '@typo3/backend/form-engine'; import FormEngineValidation from '@typo3/backend/form-engine-validation'; -import {default as Modal, ModalElement} from '@typo3/backend/modal'; +import { default as Modal, ModalElement } from '@typo3/backend/modal'; import Notification from '../../notification'; import RegularEvent from '@typo3/core/event/regular-event'; import Severity from '../../severity'; @@ -55,7 +55,7 @@ interface UniqueDefinition { elTable: string; field: string; max: number; - possible: { [key: string]: string }; + possible: Record<string, string>; table: string; used: UniqueDefinitionCollection; } @@ -115,7 +115,7 @@ class SiteLanguageContainer extends HTMLElement { const options: NodeListOf<HTMLOptionElement> = selectElement.querySelectorAll('option'); let index: number = -1; - for (let possibleValue of Object.keys(unique.possible)) { + for (const possibleValue of Object.keys(unique.possible)) { if (possibleValue === value) { break; } @@ -273,7 +273,7 @@ class SiteLanguageContainer extends HTMLElement { } }); } - } + }; private createRecord(uid: string, markup: string, afterUid: string = null, selectedValue: string = null): void { let objectId = this.container.dataset.objectGroup; @@ -314,7 +314,7 @@ class SiteLanguageContainer extends HTMLElement { const ajaxRequest = this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint('site_configuration_inline_details')); const request = this.ajaxDispatcher.send(ajaxRequest, [objectId]); - request.then(async (response: InlineResponseInterface): Promise<any> => { + request.then(async (response: InlineResponseInterface): Promise<void> => { delete this.requestQueue[objectId]; delete this.progessQueue[objectId]; @@ -386,7 +386,7 @@ class SiteLanguageContainer extends HTMLElement { return []; } - let records = Utility.trimExplode(',', (<HTMLInputElement>formField).value); + const records = Utility.trimExplode(',', (<HTMLInputElement>formField).value); const indexOfRemoveUid = records.indexOf(objectUid); if (indexOfRemoveUid > -1) { delete records[indexOfRemoveUid]; @@ -431,7 +431,7 @@ class SiteLanguageContainer extends HTMLElement { progress = this.progessQueue[objectId]; } else { progress = NProgress; - progress.configure({parent: headerIdentifier, showSpinner: false}); + progress.configure({ parent: headerIdentifier, showSpinner: false }); this.progessQueue[objectId] = progress; } @@ -456,13 +456,13 @@ class SiteLanguageContainer extends HTMLElement { const unique: UniqueDefinition = TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup]; const values = SiteLanguageContainer.getValuesFromHashMap(unique.used); - let uniqueValueField = <HTMLSelectElement>recordContainer.querySelector( + const uniqueValueField = <HTMLSelectElement>recordContainer.querySelector( '[name="data[' + unique.table + '][' + recordContainer.dataset.objectUid + '][' + unique.field + ']"]', ); if (uniqueValueField !== null) { const selectedValue = uniqueValueField.options[uniqueValueField.selectedIndex].value; - for (let value of values) { + for (const value of values) { if (value !== selectedValue) { SiteLanguageContainer.removeSelectOptionByValue(uniqueValueField, value); } @@ -486,11 +486,11 @@ class SiteLanguageContainer extends HTMLElement { if (selectorElement !== null) { // remove all items from the new select-item which are already used in other children if (uniqueValueField !== null) { - for (let value of values) { + for (const value of values) { SiteLanguageContainer.removeSelectOptionByValue(uniqueValueField, value); } } - for (let value of values) { + for (const value of values) { SiteLanguageContainer.removeSelectOptionByValue(uniqueValueField, value); } if (typeof unique.used.length !== 'undefined') { @@ -504,7 +504,7 @@ class SiteLanguageContainer extends HTMLElement { // remove the newly used item from each select-field of the child records if (formField !== null && SiteLanguageContainer.selectOptionValueExists(selectorElement, selectedValue)) { const records = Utility.trimExplode(',', (<HTMLInputElement>formField).value); - for (let record of records) { + for (const record of records) { uniqueValueField = <HTMLSelectElement>document.querySelector( '[name="data[' + unique.table + '][' + record + '][' + unique.field + ']"]', ); @@ -530,7 +530,7 @@ class SiteLanguageContainer extends HTMLElement { const recordObjectId = this.container.dataset.objectGroup + Separators.structureSeparator + recordUid; const recordContainer = SiteLanguageContainer.getInlineRecordContainer(recordObjectId); - let uniqueValueField = <HTMLSelectElement>recordContainer.querySelector( + const uniqueValueField = <HTMLSelectElement>recordContainer.querySelector( '[name="data[' + unique.table + '][' + recordContainer.dataset.objectUid + '][' + unique.field + ']"]', ); let uniqueValue; @@ -544,7 +544,7 @@ class SiteLanguageContainer extends HTMLElement { // 9223372036854775807 is the PHP_INT_MAX placeholder, used to allow creation of new records. // This option however should never be displayed in the selector box at is therefore checked. - if (!isNaN(parseInt(uniqueValue, 10)) && parseInt(uniqueValue, 10) !== 9223372036854775807) { + if (uniqueValue !== '9223372036854775807') { const selectorElement: HTMLSelectElement = <HTMLSelectElement>document.getElementById( this.container.dataset.objectGroup + '_selector', ); diff --git a/Build/Sources/TypeScript/backend/form-engine/element/abstract-sortable-select-items.ts b/Build/Sources/TypeScript/backend/form-engine/element/abstract-sortable-select-items.ts index 79eb0ab2b182..41b877e04434 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/abstract-sortable-select-items.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/abstract-sortable-select-items.ts @@ -48,7 +48,7 @@ export abstract class AbstractSortableSelectItems { private static moveOptionUp(fieldElement: HTMLSelectElement): void { const allChildren = Array.from(fieldElement.children); const selectedOptions = Array.from(fieldElement.querySelectorAll(':checked')); - for (let optionEl of selectedOptions) { + for (const optionEl of selectedOptions) { if (allChildren.indexOf(optionEl) === 0 && optionEl.previousElementSibling === null) { break; } @@ -66,7 +66,7 @@ export abstract class AbstractSortableSelectItems { private static moveOptionDown(fieldElement: HTMLSelectElement): void { const allChildren = Array.from(fieldElement.children).reverse(); const selectedOptions = Array.from(fieldElement.querySelectorAll(':checked')).reverse(); - for (let optionEl of selectedOptions) { + for (const optionEl of selectedOptions) { if (allChildren.indexOf(optionEl) === 0 && optionEl.nextElementSibling === null) { break; } @@ -140,5 +140,5 @@ export abstract class AbstractSortableSelectItems { FormEngineValidation.markFieldAsChanged(relatedAvailableValuesField); FormEngineValidation.validateField(relatedAvailableValuesField); }); - } + }; } diff --git a/Build/Sources/TypeScript/backend/form-engine/element/category-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/category-element.ts index a1bb03a0a840..8b139add55f8 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/category-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/category-element.ts @@ -11,12 +11,12 @@ * The TYPO3 project - inspiring people to share! */ -import type {SelectTree} from './select-tree'; -import type {SelectTreeToolbar} from './select-tree-toolbar'; +import type { SelectTree } from './select-tree'; +import type { SelectTreeToolbar } from './select-tree-toolbar'; import './select-tree'; import './select-tree-toolbar'; import '@typo3/backend/element/icon-element'; -import {TreeNode} from '@typo3/backend/tree/tree-node'; +import { TreeNode } from '@typo3/backend/tree/tree-node'; /** * Module: @typo3/backend/form-engine/element/category-element @@ -113,8 +113,8 @@ class CategoryElement extends HTMLElement{ // check all nodes again, to ensure correct display of indeterminate state this.calculateIndeterminate(this.tree.nodes); this.saveCheckboxes(); - this.tree.setup.input.dispatchEvent(new Event('change', {bubbles: true, cancelable: true})); - } + this.tree.setup.input.dispatchEvent(new Event('change', { bubbles: true, cancelable: true })); + }; /** * Resets the node.indeterminate for the whole tree. @@ -127,14 +127,14 @@ class CategoryElement extends HTMLElement{ return node; }); this.calculateIndeterminate(this.tree.nodes); - } + }; /** * Sets a comma-separated list of selected nodes identifiers to configured input */ private saveCheckboxes = (): void => { this.recordField.value = this.tree.getSelectedNodes().map((node: TreeNode): string => node.identifier).join(','); - } + }; /** * Updates the indeterminate state for ancestors of the current node diff --git a/Build/Sources/TypeScript/backend/form-engine/element/color-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/color-element.ts index 15609581342b..4fa5d92da68d 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/color-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/color-element.ts @@ -38,8 +38,8 @@ class ColorElement extends HTMLElement { } this.registerEventHandler(); - import('@typo3/backend/color-picker').then(({default: ColorPicker}): void => { - ColorPicker.initialize(this.element) + import('@typo3/backend/color-picker').then(({ default: ColorPicker }): void => { + ColorPicker.initialize(this.element); }); } diff --git a/Build/Sources/TypeScript/backend/form-engine/element/datetime-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/datetime-element.ts index e9dcc9903b43..81e95b726cf2 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/datetime-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/datetime-element.ts @@ -38,8 +38,8 @@ class DatetimeElement extends HTMLElement { } this.registerEventHandler(); - import('../../date-time-picker').then(({default: DateTimePicker}): void => { - DateTimePicker.initialize(this.element) + import('../../date-time-picker').then(({ default: DateTimePicker }): void => { + DateTimePicker.initialize(this.element); }); } diff --git a/Build/Sources/TypeScript/backend/form-engine/element/extra/select-box-filter.ts b/Build/Sources/TypeScript/backend/form-engine/element/extra/select-box-filter.ts index 0dffe5963f1e..b55c9a56562b 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/extra/select-box-filter.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/extra/select-box-filter.ts @@ -27,6 +27,12 @@ class SelectBoxFilter { private filterText: string = ''; private availableOptions: NodeListOf<HTMLOptionElement> = null; + constructor(selectElement: HTMLSelectElement) { + this.selectElement = selectElement; + + this.initializeEvents(); + } + private static toggleOptGroup(option: HTMLOptionElement): void { const optGroup = <HTMLOptGroupElement>option.parentElement; if (!(optGroup instanceof HTMLOptGroupElement)) { @@ -41,12 +47,6 @@ class SelectBoxFilter { } } - constructor(selectElement: HTMLSelectElement) { - this.selectElement = selectElement; - - this.initializeEvents(); - } - private initializeEvents(): void { const wizardsElement = this.selectElement.closest('.form-wizards-element'); if (wizardsElement === null) { diff --git a/Build/Sources/TypeScript/backend/form-engine/element/folder-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/folder-element.ts index d3f613cb6ed2..eaae8427c04c 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/folder-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/folder-element.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {AbstractSortableSelectItems} from '@typo3/backend/form-engine/element/abstract-sortable-select-items'; +import { AbstractSortableSelectItems } from '@typo3/backend/form-engine/element/abstract-sortable-select-items'; class FolderSortableSelectItems extends AbstractSortableSelectItems { public registerEventHandler(element: HTMLSelectElement): void { diff --git a/Build/Sources/TypeScript/backend/form-engine/element/group-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/group-element.ts index 3dc4e87fedb8..cb807ca30753 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/group-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/group-element.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {AbstractSortableSelectItems} from './abstract-sortable-select-items'; +import { AbstractSortableSelectItems } from './abstract-sortable-select-items'; import DocumentService from '@typo3/core/document-service'; import FormEngineSuggest from '../../form-engine-suggest'; diff --git a/Build/Sources/TypeScript/backend/form-engine/element/json-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/json-element.ts index 0a3b678e48e1..842e235d29ce 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/json-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/json-element.ts @@ -11,8 +11,8 @@ * The TYPO3 project - inspiring people to share! */ -import {Resizable} from './modifier/resizable'; -import {Tabbable} from './modifier/tabbable'; +import { Resizable } from './modifier/resizable'; +import { Tabbable } from './modifier/tabbable'; /** * Module: @typo3/backend/form-engine/element/json-element diff --git a/Build/Sources/TypeScript/backend/form-engine/element/mfa-info-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/mfa-info-element.ts index 00e8a36cbe7c..8f86bd1da3cf 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/mfa-info-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/mfa-info-element.ts @@ -11,13 +11,13 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import DocumentService from '@typo3/core/document-service'; import RegularEvent from '@typo3/core/event/regular-event'; import Notification from '@typo3/backend/notification'; import Modal from '@typo3/backend/modal'; -import {SeverityEnum} from '@typo3/backend/enum/severity'; +import { SeverityEnum } from '@typo3/backend/enum/severity'; interface FieldOptions { userId: number, @@ -115,7 +115,7 @@ class MfaInfoElement { provider: provider, userId: this.options.userId, tableName: this.options.tableName - }).then(async (response: AjaxResponse): Promise<any> => { + }).then(async (response: AjaxResponse): Promise<void> => { const data: Response = await response.resolve(); if (data.status.length > 0) { data.status.forEach((status: Status): void => { diff --git a/Build/Sources/TypeScript/backend/form-engine/element/modifier/tabbable.ts b/Build/Sources/TypeScript/backend/form-engine/element/modifier/tabbable.ts index 9edcb226ec21..2816cccbadca 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/modifier/tabbable.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/modifier/tabbable.ts @@ -21,7 +21,7 @@ export class Tabbable { */ public static enable(textarea: HTMLTextAreaElement): void { if (textarea.classList.contains('t3js-enable-tab')) { - import('taboverride').then(({default: taboverride}): void => { + import('taboverride').then(({ default: taboverride }): void => { taboverride.set(textarea); }); } diff --git a/Build/Sources/TypeScript/backend/form-engine/element/select-check-box-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/select-check-box-element.ts index 7102912a59e8..01018991a508 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/select-check-box-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/select-check-box-element.ts @@ -25,17 +25,6 @@ class SelectCheckBoxElement { private table: HTMLTableElement = null; private checkedBoxes: NodeListOf<HTMLInputElement> = null; - /** - * Determines whether all available checkboxes are checked - * - * @param {NodeListOf<HTMLInputElement>} checkBoxes - * @return {boolean} - */ - private static allCheckBoxesAreChecked(checkBoxes: NodeListOf<HTMLInputElement>): boolean { - const checkboxArray = Array.from(checkBoxes); - return checkBoxes.length === checkboxArray.filter((checkBox: HTMLInputElement) => checkBox.checked).length; - } - /** * @param {string} checkBoxId */ @@ -50,6 +39,17 @@ class SelectCheckBoxElement { }); } + /** + * Determines whether all available checkboxes are checked + * + * @param {NodeListOf<HTMLInputElement>} checkBoxes + * @return {boolean} + */ + private static allCheckBoxesAreChecked(checkBoxes: NodeListOf<HTMLInputElement>): boolean { + const checkboxArray = Array.from(checkBoxes); + return checkBoxes.length === checkboxArray.filter((checkBox: HTMLInputElement) => checkBox.checked).length; + } + /** * Registers the events for clicking the "Toggle all" and the single item checkboxes */ diff --git a/Build/Sources/TypeScript/backend/form-engine/element/select-multiple-side-by-side-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/select-multiple-side-by-side-element.ts index 8d9735807152..3cfe43e7b9dd 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/select-multiple-side-by-side-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/select-multiple-side-by-side-element.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {AbstractSortableSelectItems} from './abstract-sortable-select-items'; +import { AbstractSortableSelectItems } from './abstract-sortable-select-items'; import DocumentService from '@typo3/core/document-service'; import FormEngine from '@typo3/backend/form-engine'; import SelectBoxFilter from './extra/select-box-filter'; diff --git a/Build/Sources/TypeScript/backend/form-engine/element/select-single-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/select-single-element.ts index 7cbd1447967b..e2273f199bea 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/select-single-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/select-single-element.ts @@ -32,7 +32,7 @@ class SelectSingleElement { } public initialize = (selector: string, options: SelectSingleElementOptions): void => { - let selectElement: HTMLSelectElement = document.querySelector(selector); + const selectElement: HTMLSelectElement = document.querySelector(selector); options = options || {}; new RegularEvent('change', (e: Event): void => { @@ -52,7 +52,7 @@ class SelectSingleElement { } const selectionIcon = selectIcons.querySelector('[data-select-index="' + target.selectedIndex + '"]'); - if (selectionIcon !== null) { + if (selectionIcon !== null) { selectionIcon.closest('.form-wizard-icon-list-item a').classList.add('active'); } } @@ -73,7 +73,7 @@ class SelectSingleElement { selectElement.dispatchEvent(new Event('change')); target.closest('.form-wizard-icon-list-item a').classList.add('active'); }).delegateTo(selectElement.closest('.form-control-wrap'), '.t3js-forms-select-single-icons .form-wizard-icon-list-item a:not(.active)'); - } + }; } export default new SelectSingleElement(); diff --git a/Build/Sources/TypeScript/backend/form-engine/element/select-tree-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/select-tree-element.ts index 30e83c5e2786..4f7ff17c712f 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/select-tree-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/select-tree-element.ts @@ -11,12 +11,12 @@ * The TYPO3 project - inspiring people to share! */ -import type {SelectTree} from './select-tree'; -import type {SelectTreeToolbar} from './select-tree-toolbar'; +import type { SelectTree } from './select-tree'; +import type { SelectTreeToolbar } from './select-tree-toolbar'; import './select-tree'; import './select-tree-toolbar'; import '@typo3/backend/element/icon-element'; -import {TreeNode} from '@typo3/backend/tree/tree-node'; +import { TreeNode } from '@typo3/backend/tree/tree-node'; import FormEngine from '@typo3/backend/form-engine'; import OnFieldChangeItem = TYPO3.CMS.Backend.OnFieldChangeItem; @@ -37,7 +37,7 @@ export class SelectTreeElement { this.tree.addEventListener('typo3:svg-tree:node-selected', this.selectNode); if (onFieldChangeItems instanceof Array) { - this.tree.addEventListener('typo3:svg-tree:node-selected', () => { FormEngine.processOnFieldChange(onFieldChangeItems) } ); + this.tree.addEventListener('typo3:svg-tree:node-selected', () => { FormEngine.processOnFieldChange(onFieldChangeItems); } ); } const settings = { @@ -69,9 +69,9 @@ export class SelectTreeElement { private listenForVisibleTree(): void { if (!this.tree.offsetParent) { // Search for the parents that are tab containers - let idOfTabContainer = this.tree.closest('.tab-pane').getAttribute('id'); + const idOfTabContainer = this.tree.closest('.tab-pane').getAttribute('id'); if (idOfTabContainer) { - let btn = document.querySelector('[aria-controls="' + idOfTabContainer + '"]'); + const btn = document.querySelector('[aria-controls="' + idOfTabContainer + '"]'); btn.addEventListener('shown.bs.tab', () => { this.tree.dispatchEvent(new Event('svg-tree:visible')); }); } } @@ -103,8 +103,8 @@ export class SelectTreeElement { // check all nodes again, to ensure correct display of indeterminate state this.calculateIndeterminate(this.tree.nodes); this.saveCheckboxes(); - this.tree.setup.input.dispatchEvent(new Event('change', {bubbles: true, cancelable: true})); - } + this.tree.setup.input.dispatchEvent(new Event('change', { bubbles: true, cancelable: true })); + }; /** * Resets the node.indeterminate for the whole tree. @@ -117,7 +117,7 @@ export class SelectTreeElement { return node; }); this.calculateIndeterminate(this.tree.nodes); - } + }; /** * Sets a comma-separated list of selected nodes identifiers to configured input @@ -127,7 +127,7 @@ export class SelectTreeElement { return; } this.recordField.value = this.tree.getSelectedNodes().map((node: TreeNode): string => node.identifier).join(','); - } + }; /** * Updates the indeterminate state for ancestors of the current node diff --git a/Build/Sources/TypeScript/backend/form-engine/element/select-tree-toolbar.ts b/Build/Sources/TypeScript/backend/form-engine/element/select-tree-toolbar.ts index beb108f6a5d6..672e6017a83e 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/select-tree-toolbar.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/select-tree-toolbar.ts @@ -11,11 +11,11 @@ * The TYPO3 project - inspiring people to share! */ -import type {SelectTree} from './select-tree'; -import {html, LitElement, TemplateResult} from 'lit'; -import {customElement} from 'lit/decorators'; -import {lll} from '@typo3/core/lit-helper'; -import {TreeNode} from '../../tree/tree-node'; +import type { SelectTree } from './select-tree'; +import { html, LitElement, TemplateResult } from 'lit'; +import { customElement } from 'lit/decorators'; +import { lll } from '@typo3/core/lit-helper'; +import { TreeNode } from '../../tree/tree-node'; @customElement('typo3-backend-form-selecttree-toolbar') export class SelectTreeToolbar extends LitElement { diff --git a/Build/Sources/TypeScript/backend/form-engine/element/select-tree.ts b/Build/Sources/TypeScript/backend/form-engine/element/select-tree.ts index 0be5e09ea5d6..f9ff277217b1 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/select-tree.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/select-tree.ts @@ -12,9 +12,9 @@ */ import * as d3selection from 'd3-selection'; -import {SvgTree, SvgTreeSettings, TreeNodeSelection} from '../../svg-tree'; -import {TreeNode} from '../../tree/tree-node'; -import {customElement} from 'lit/decorators'; +import { SvgTree, SvgTreeSettings, TreeNodeSelection } from '../../svg-tree'; +import { TreeNode } from '../../tree/tree-node'; +import { customElement } from 'lit/decorators'; interface SelectTreeSettings extends SvgTreeSettings { exclusiveNodesIdentifiers: ''; @@ -87,7 +87,7 @@ export class SelectTree extends SvgTree node.checked = !checked; - this.dispatchEvent(new CustomEvent('typo3:svg-tree:node-selected', {detail: {node: node, propagate: propagate}})); + this.dispatchEvent(new CustomEvent('typo3:svg-tree:node-selected', { detail: { node: node, propagate: propagate } })); this.updateVisibleNodes(); } @@ -138,7 +138,7 @@ export class SelectTree extends SvgTree const position = Math.floor(Math.max(this.scrollTop - (this.settings.nodeHeight * 2), 0) / this.settings.nodeHeight); const visibleNodes = this.data.nodes.slice(position, position + visibleRows); - let nodes = this.nodesContainer.selectAll('.node') + const nodes = this.nodesContainer.selectAll('.node') .data(visibleNodes, (node: TreeNode) => node.stateIdentifier); nodes .selectAll('.tree-check use') @@ -165,12 +165,13 @@ export class SelectTree extends SvgTree protected isNodeSelectable(node: TreeNode): boolean { return !this.settings.readOnlyMode && this.settings.unselectableElements.indexOf(node.identifier) === -1; } + /** * Add checkbox before the text element */ protected appendTextElement(nodes: TreeNodeSelection): TreeNodeSelection { this.renderCheckbox(nodes); - return super.appendTextElement(nodes) + return super.appendTextElement(nodes); } /** @@ -218,7 +219,7 @@ export class SelectTree extends SvgTree * create stateIdentifier if doesn't exist (for category tree) */ private prepareLoadedNodes(evt: CustomEvent): void { - let nodes = evt.detail.nodes as Array<TreeNode>; + const nodes = evt.detail.nodes as Array<TreeNode>; evt.detail.nodes = nodes.map((node: TreeNode) => { if (!node.stateIdentifier) { const parentId = (node.parents.length) ? node.parents[node.parents.length - 1] : node.identifier; @@ -272,6 +273,6 @@ export class SelectTree extends SvgTree identifier: 'indeterminate', icon: '<g width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><rect height="16" width="16" fill="transparent"></rect><path transform="scale(0.01)" d="M1344 800v64q0 14-9 23t-23 9h-832q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h832q14 0 23 9t9 23zm128 448v-832q0-66-47-113t-113-47h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113zm128-832v832q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q119 0 203.5 84.5t84.5 203.5z"></path></g>' } - } + }; } } diff --git a/Build/Sources/TypeScript/backend/form-engine/element/slug-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/slug-element.ts index bf3bfde07fcd..3c58f116344a 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/slug-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/slug-element.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import DocumentService from '@typo3/core/document-service'; import DebounceEvent from '@typo3/core/event/debounce-event'; @@ -23,7 +23,7 @@ interface FieldOptions { tableName: string; fieldName: string; config: { [key: string]: any }; - listenerFieldNames: { [key: string]: string }; + listenerFieldNames: Record<string, string>; language: number; originalValue: string; signature: string; @@ -71,7 +71,7 @@ class SlugElement { private inputField: HTMLInputElement = null; private hiddenField: HTMLInputElement = null; private request: AjaxRequest = null; - private readonly fieldsToListenOn: { [key: string]: string } = {}; + private readonly fieldsToListenOn: Record<string, string> = {}; constructor(selector: string, options: FieldOptions) { this.options = options; @@ -156,7 +156,7 @@ class SlugElement { * @param {ProposalModes} mode */ private sendSlugProposal(mode: ProposalModes): void { - const input: { [key: string]: string } = {}; + const input: Record<string, string> = {}; if (mode === ProposalModes.AUTO || mode === ProposalModes.RECREATE) { Object.entries(this.getAvailableFieldsForProposalGeneration()).forEach((entry: [fieldName: string, field: HTMLInputElement|HTMLSelectElement]) => { input[entry[0]] = entry[1].value; @@ -182,7 +182,7 @@ class SlugElement { fieldName: this.options.fieldName, command: this.options.command, signature: this.options.signature, - }).then(async (response: AjaxResponse): Promise<any> => { + }).then(async (response: AjaxResponse): Promise<void> => { const data: Response = await response.resolve(); const visualProposal = '/' + data.proposal.replace(/^\//, ''); const acceptedProposalField: HTMLElement = this.fullElement.querySelector('.t3js-form-proposal-accepted'); @@ -195,7 +195,7 @@ class SlugElement { const isChanged = this.hiddenField.value !== data.proposal; if (isChanged) { this.fullElement.querySelector('input[data-formengine-input-name]') - .dispatchEvent(new Event('change', {bubbles: true, cancelable: true})); + .dispatchEvent(new Event('change', { bubbles: true, cancelable: true })); } if (mode === ProposalModes.AUTO || mode === ProposalModes.RECREATE) { this.readOnlyField.value = data.proposal; @@ -212,7 +212,7 @@ class SlugElement { /** * Gets a list of all available fields that can be used for slug generation * - * @return { [key: string]: string } + * @return Record<string, string> */ private getAvailableFieldsForProposalGeneration(): { [key: string]: HTMLElement } { const availableFields: { [key: string]: HTMLElement } = {}; diff --git a/Build/Sources/TypeScript/backend/form-engine/element/suggest/result-container.ts b/Build/Sources/TypeScript/backend/form-engine/element/suggest/result-container.ts index 29f6498cf049..4530bbe1b27f 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/suggest/result-container.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/suggest/result-container.ts @@ -11,15 +11,15 @@ * The TYPO3 project - inspiring people to share! */ -import {customElement, property} from 'lit/decorators'; -import {css, html, LitElement, TemplateResult} from 'lit'; -import {lll} from '@typo3/core/lit-helper'; +import { customElement, property } from 'lit/decorators'; +import { css, html, LitElement, TemplateResult } from 'lit'; +import { lll } from '@typo3/core/lit-helper'; import './result-item'; -import {ResultItemInterface} from './result-item'; +import { ResultItemInterface } from './result-item'; @customElement('typo3-backend-formengine-suggest-result-container') -class ResultContainer extends LitElement { - @property({type: Object}) results: ResultItemInterface[]|null = null; +export class ResultContainer extends LitElement { + @property({ type: Object }) results: ResultItemInterface[]|null = null; public connectedCallback(): void { super.connectedCallback(); @@ -82,7 +82,7 @@ class ResultContainer extends LitElement { let focusableCandidate; if (e.key === 'ArrowDown') { - focusableCandidate = document.activeElement.nextElementSibling + focusableCandidate = document.activeElement.nextElementSibling; } else { focusableCandidate = document.activeElement.previousElementSibling; if (focusableCandidate === null) { diff --git a/Build/Sources/TypeScript/backend/form-engine/element/suggest/result-item.ts b/Build/Sources/TypeScript/backend/form-engine/element/suggest/result-item.ts index 49ff6202063c..7a6a553af20b 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/suggest/result-item.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/suggest/result-item.ts @@ -11,12 +11,12 @@ * The TYPO3 project - inspiring people to share! */ -import {customElement, property} from 'lit/decorators'; -import {html, LitElement, TemplateResult} from 'lit'; +import { customElement, property } from 'lit/decorators'; +import { html, LitElement, TemplateResult } from 'lit'; import '@typo3/backend/element/icon-element'; export interface ResultItemInterface { - icon: { [key: string]: string }; + icon: Record<string, string>; uid: number; table: string; label: string; @@ -25,11 +25,11 @@ export interface ResultItemInterface { @customElement('typo3-backend-formengine-suggest-result-item') export class ResultItem extends LitElement { - @property({type: Object}) icon: { [key: string]: string }; - @property({type: Number}) uid: number; - @property({type: String}) table: string; - @property({type: String}) label: string; - @property({type: String}) path: string; + @property({ type: Object }) icon: Record<string, string>; + @property({ type: Number }) uid: number; + @property({ type: String }) table: string; + @property({ type: String }) label: string; + @property({ type: String }) path: string; public connectedCallback(): void { super.connectedCallback(); diff --git a/Build/Sources/TypeScript/backend/form-engine/element/text-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/text-element.ts index a70408a14b7e..e8b90c3616d6 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/text-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/text-element.ts @@ -11,8 +11,8 @@ * The TYPO3 project - inspiring people to share! */ -import {Resizable} from './modifier/resizable'; -import {Tabbable} from './modifier/tabbable'; +import { Resizable } from './modifier/resizable'; +import { Tabbable } from './modifier/tabbable'; /** * Module: @typo3/backend/form-engine/element/text-element diff --git a/Build/Sources/TypeScript/backend/form-engine/element/text-table-element.ts b/Build/Sources/TypeScript/backend/form-engine/element/text-table-element.ts index b7c4e665427b..d6acd72961f6 100644 --- a/Build/Sources/TypeScript/backend/form-engine/element/text-table-element.ts +++ b/Build/Sources/TypeScript/backend/form-engine/element/text-table-element.ts @@ -11,8 +11,8 @@ * The TYPO3 project - inspiring people to share! */ -import {Resizable} from './modifier/resizable'; -import {Tabbable} from './modifier/tabbable'; +import { Resizable } from './modifier/resizable'; +import { Tabbable } from './modifier/tabbable'; import DocumentService from '@typo3/core/document-service'; class TextTableElement { diff --git a/Build/Sources/TypeScript/backend/form-engine/field-control/add-record.ts b/Build/Sources/TypeScript/backend/form-engine/field-control/add-record.ts index 142c9a5378e2..06f59c64d938 100644 --- a/Build/Sources/TypeScript/backend/form-engine/field-control/add-record.ts +++ b/Build/Sources/TypeScript/backend/form-engine/field-control/add-record.ts @@ -34,7 +34,7 @@ class AddRecord { e.preventDefault(); FormEngine.preventFollowLinkIfNotSaved(this.controlElement.getAttribute('href')); - } + }; } export default AddRecord; diff --git a/Build/Sources/TypeScript/backend/form-engine/field-control/edit-popup.ts b/Build/Sources/TypeScript/backend/form-engine/field-control/edit-popup.ts index e9476f08bd26..f0820fa77eb5 100644 --- a/Build/Sources/TypeScript/backend/form-engine/field-control/edit-popup.ts +++ b/Build/Sources/TypeScript/backend/form-engine/field-control/edit-popup.ts @@ -38,7 +38,7 @@ class EditPopup { private registerChangeHandler = (): void => { this.controlElement.classList.toggle('disabled', this.assignedFormField.options.selectedIndex === -1); - } + }; /** * @param {Event} e @@ -48,7 +48,7 @@ class EditPopup { const values: Array<string> = []; for (let i = 0; i < this.assignedFormField.selectedOptions.length; ++i) { - const option = this.assignedFormField.selectedOptions.item(i); + const option = this.assignedFormField.selectedOptions.item(i); values.push(option.value); } @@ -58,7 +58,7 @@ class EditPopup { ; const popupWindow = window.open(url, '', this.controlElement.dataset.windowParameters); popupWindow.focus(); - } + }; } export default EditPopup; diff --git a/Build/Sources/TypeScript/backend/form-engine/field-control/insert-clipboard.ts b/Build/Sources/TypeScript/backend/form-engine/field-control/insert-clipboard.ts index 21d1042ae53b..0578ac248961 100644 --- a/Build/Sources/TypeScript/backend/form-engine/field-control/insert-clipboard.ts +++ b/Build/Sources/TypeScript/backend/form-engine/field-control/insert-clipboard.ts @@ -41,10 +41,10 @@ class InsertClipboard { const assignedElement: string = this.controlElement.dataset.element; const clipboardItems: Array<ClipboardItem> = JSON.parse(this.controlElement.dataset.clipboardItems); - for (let item of clipboardItems) { + for (const item of clipboardItems) { FormEngine.setSelectOptionFromExternalSource(assignedElement, item.value, item.title, item.title); } - } + }; } export default InsertClipboard; diff --git a/Build/Sources/TypeScript/backend/form-engine/field-control/link-popup.ts b/Build/Sources/TypeScript/backend/form-engine/field-control/link-popup.ts index 6fc74e48b7d2..e2d0ea392ec2 100644 --- a/Build/Sources/TypeScript/backend/form-engine/field-control/link-popup.ts +++ b/Build/Sources/TypeScript/backend/form-engine/field-control/link-popup.ts @@ -44,7 +44,7 @@ class LinkPopup { content: url, size: Modal.sizes.large, }); - } + }; } export default LinkPopup; diff --git a/Build/Sources/TypeScript/backend/form-engine/field-control/list-module.ts b/Build/Sources/TypeScript/backend/form-engine/field-control/list-module.ts index ac3e1961f09a..ec9e412626d9 100644 --- a/Build/Sources/TypeScript/backend/form-engine/field-control/list-module.ts +++ b/Build/Sources/TypeScript/backend/form-engine/field-control/list-module.ts @@ -34,7 +34,7 @@ class ListModule { e.preventDefault(); FormEngine.preventFollowLinkIfNotSaved(this.controlElement.getAttribute('href')); - } + }; } export default ListModule; diff --git a/Build/Sources/TypeScript/backend/form-engine/field-control/password-generator.ts b/Build/Sources/TypeScript/backend/form-engine/field-control/password-generator.ts index 2c1d6137b5fa..6f87495e1703 100644 --- a/Build/Sources/TypeScript/backend/form-engine/field-control/password-generator.ts +++ b/Build/Sources/TypeScript/backend/form-engine/field-control/password-generator.ts @@ -15,7 +15,7 @@ import DocumentService from '@typo3/core/document-service'; import SecurityUtility from '@typo3/core/security-utility'; import FormEngineValidation from '@typo3/backend/form-engine-validation'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import Notification from '@typo3/backend/notification'; interface PasswordRules { @@ -61,7 +61,7 @@ class PasswordGenerator { if (clearableContainer) { clearableContainer.classList.remove('form-control-clearable'); const closeButton = <HTMLButtonElement>clearableContainer.querySelector('button.close'); - closeButton && clearableContainer.removeChild(closeButton) + closeButton && clearableContainer.removeChild(closeButton); } } } @@ -97,7 +97,7 @@ class PasswordGenerator { }) .catch(() => { Notification.error('Password could not be generated'); - }) + }); } } diff --git a/Build/Sources/TypeScript/backend/form-engine/field-control/reset-selection.ts b/Build/Sources/TypeScript/backend/form-engine/field-control/reset-selection.ts index 9aa2cdae6fb5..0d7d3e61ae97 100644 --- a/Build/Sources/TypeScript/backend/form-engine/field-control/reset-selection.ts +++ b/Build/Sources/TypeScript/backend/form-engine/field-control/reset-selection.ts @@ -39,10 +39,10 @@ class ResetSelection { const field = (<HTMLSelectElement>document.forms.namedItem('editform').querySelector('[name="' + itemName + '[]"]')); field.selectedIndex = -1; - for (let i of selectedIndices) { + for (const i of selectedIndices) { field.options[i].selected = true; } - } + }; } export default ResetSelection; diff --git a/Build/Sources/TypeScript/backend/form-engine/field-control/table-wizard.ts b/Build/Sources/TypeScript/backend/form-engine/field-control/table-wizard.ts index 95b8440ae63b..b73a6885f70b 100644 --- a/Build/Sources/TypeScript/backend/form-engine/field-control/table-wizard.ts +++ b/Build/Sources/TypeScript/backend/form-engine/field-control/table-wizard.ts @@ -34,7 +34,7 @@ class TableWizard { e.preventDefault(); FormEngine.preventFollowLinkIfNotSaved(this.controlElement.getAttribute('href')); - } + }; } export default TableWizard; diff --git a/Build/Sources/TypeScript/backend/form-engine/field-wizard/value-picker.ts b/Build/Sources/TypeScript/backend/form-engine/field-wizard/value-picker.ts index 63bc1faf2ede..ae08aadc89a7 100644 --- a/Build/Sources/TypeScript/backend/form-engine/field-wizard/value-picker.ts +++ b/Build/Sources/TypeScript/backend/form-engine/field-wizard/value-picker.ts @@ -49,7 +49,7 @@ export class ValuePicker extends HTMLElement { this.setValue(); this.valuePicker.selectedIndex = 0; this.valuePicker.blur(); - } + }; private setValue (): void { const selectedValue = this.valuePicker.options[this.valuePicker.selectedIndex].value; @@ -63,7 +63,7 @@ export class ValuePicker extends HTMLElement { } else { linkedField.value = selectedValue; } - linkedField.dispatchEvent(new Event('change', {bubbles: true, cancelable: true})); + linkedField.dispatchEvent(new Event('change', { bubbles: true, cancelable: true })); } } diff --git a/Build/Sources/TypeScript/backend/form-engine/field-wizard/value-slider.ts b/Build/Sources/TypeScript/backend/form-engine/field-wizard/value-slider.ts index c049c025b613..f15ea5d9354c 100644 --- a/Build/Sources/TypeScript/backend/form-engine/field-wizard/value-slider.ts +++ b/Build/Sources/TypeScript/backend/form-engine/field-wizard/value-slider.ts @@ -43,7 +43,7 @@ export class ValueSlider extends HTMLElement { const target = e.target as HTMLInputElement; this.updateValue(target); this.updateTooltipValue(target); - } + }; /** * Update value of slider element @@ -53,7 +53,7 @@ export class ValueSlider extends HTMLElement { private updateValue(element: HTMLInputElement): void { const foreignField = document.querySelector(this.getAttribute('linked-field')) as HTMLInputElement; foreignField.value = element.value; - foreignField.dispatchEvent(new Event('change', {bubbles: true, cancelable: true})); + foreignField.dispatchEvent(new Event('change', { bubbles: true, cancelable: true })); } /** diff --git a/Build/Sources/TypeScript/backend/form-engine/inline-relation/ajax-dispatcher.ts b/Build/Sources/TypeScript/backend/form-engine/inline-relation/ajax-dispatcher.ts index 2a7ea3c66642..76333c9a15c1 100644 --- a/Build/Sources/TypeScript/backend/form-engine/inline-relation/ajax-dispatcher.ts +++ b/Build/Sources/TypeScript/backend/form-engine/inline-relation/ajax-dispatcher.ts @@ -11,9 +11,9 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {JavaScriptItemProcessor} from '@typo3/core/java-script-item-processor'; +import { JavaScriptItemProcessor } from '@typo3/core/java-script-item-processor'; import Notification from '../../notification'; import Utility from '../../utility'; @@ -58,8 +58,8 @@ export class AjaxDispatcher { throw 'Undefined endpoint for route "' + routeName + '"'; } - public send(request: AjaxRequest, params: Array<string>): Promise<any> { - const sentRequest = request.post(this.createRequestBody(params)).then(async (response: AjaxResponse): Promise<any> => { + public send(request: AjaxRequest, params: Array<string>): Promise<AjaxDispatcherResponse> { + const sentRequest = request.post(this.createRequestBody(params)).then(async (response: AjaxResponse): Promise<AjaxDispatcherResponse> => { return this.processResponse(await response.resolve()); }); sentRequest.catch((reason: Error): void => { @@ -69,8 +69,8 @@ export class AjaxDispatcher { return sentRequest; } - private createRequestBody(input: Array<string>): { [key: string]: string } { - const body: { [key: string]: string } = {}; + private createRequestBody(input: Array<string>): Record<string, string> { + const body: Record<string, string> = {}; for (let i = 0; i < input.length; i++) { body['ajax[' + i + ']'] = input[i]; } diff --git a/Build/Sources/TypeScript/backend/form-engine/inline-relation/inline-response-interface.ts b/Build/Sources/TypeScript/backend/form-engine/inline-relation/inline-response-interface.ts index d6082b35c251..7044629dbe4e 100644 --- a/Build/Sources/TypeScript/backend/form-engine/inline-relation/inline-response-interface.ts +++ b/Build/Sources/TypeScript/backend/form-engine/inline-relation/inline-response-interface.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxDispatcherResponse} from '@typo3/backend/form-engine/inline-relation/ajax-dispatcher'; +import { AjaxDispatcherResponse } from '@typo3/backend/form-engine/inline-relation/ajax-dispatcher'; export interface InlineResponseInterface extends AjaxDispatcherResponse{ data: string; diff --git a/Build/Sources/TypeScript/backend/form-engine/request-update.ts b/Build/Sources/TypeScript/backend/form-engine/request-update.ts index 19701480833e..8ea26283a6d0 100644 --- a/Build/Sources/TypeScript/backend/form-engine/request-update.ts +++ b/Build/Sources/TypeScript/backend/form-engine/request-update.ts @@ -11,8 +11,8 @@ * The TYPO3 project - inspiring people to share! */ -import {LitElement} from 'lit'; -import {customElement, property} from 'lit/decorators'; +import { LitElement } from 'lit'; +import { customElement, property } from 'lit/decorators'; import FormEngine from '@typo3/backend/form-engine'; enum UpdateMode { @@ -27,21 +27,21 @@ const selectorConverter = { }; @customElement('typo3-formengine-updater') -class RequestUpdate extends LitElement { - @property({type: String, attribute: 'mode'}) mode: String = UpdateMode.ask; +export class RequestUpdate extends LitElement { + @property({ type: String, attribute: 'mode' }) mode: string = UpdateMode.ask; - @property({attribute: 'field', converter: selectorConverter}) fields: NodeList; + @property({ attribute: 'field', converter: selectorConverter }) fields: NodeList; public connectedCallback(): void { super.connectedCallback(); - for (let field of this.fields) { + for (const field of this.fields) { field.addEventListener('change', this.requestFormEngineUpdate); } } public disconnectedCallback(): void { super.disconnectedCallback(); - for (let field of this.fields) { + for (const field of this.fields) { field.removeEventListener('change', this.requestFormEngineUpdate); } } @@ -49,5 +49,5 @@ class RequestUpdate extends LitElement { private requestFormEngineUpdate = (): void => { const askForUpdate = this.mode === UpdateMode.ask; FormEngine.requestFormEngineUpdate(askForUpdate); - } + }; } diff --git a/Build/Sources/TypeScript/backend/global-event-handler.ts b/Build/Sources/TypeScript/backend/global-event-handler.ts index eb20b3de1b6d..f837fdf89aa9 100644 --- a/Build/Sources/TypeScript/backend/global-event-handler.ts +++ b/Build/Sources/TypeScript/backend/global-event-handler.ts @@ -54,7 +54,7 @@ class GlobalEventHandler { constructor() { documentService.ready().then((): void => this.registerEvents()); - }; + } private registerEvents(): void { new RegularEvent('change', this.handleChangeEvent.bind(this)) diff --git a/Build/Sources/TypeScript/backend/grid-editor.ts b/Build/Sources/TypeScript/backend/grid-editor.ts index 6ad2759eeb8c..628f94beb21a 100644 --- a/Build/Sources/TypeScript/backend/grid-editor.ts +++ b/Build/Sources/TypeScript/backend/grid-editor.ts @@ -11,10 +11,10 @@ * The TYPO3 project - inspiring people to share! */ -import {SeverityEnum} from './enum/severity'; +import { SeverityEnum } from './enum/severity'; import 'bootstrap'; import $ from 'jquery'; -import {default as Modal, ModalElement} from '@typo3/backend/modal'; +import { default as Modal, ModalElement } from '@typo3/backend/modal'; import SecurityUtility from '@typo3/core/security-utility'; import Icons from './icons'; @@ -26,17 +26,7 @@ interface GridEditorConfigurationInterface { columnLabel: string; } -/** - * CellInterface - */ -interface CellInterface { - spanned: number; - rowspan: number; - colspan: number; - column: number; - name: string; - colpos: string; -} +type Cell = { spanned: number, rowspan: number, colspan: number, name: string, colpos: string, column: number } /** * Module: @typo3/backend/grid-editor @@ -52,7 +42,7 @@ export class GridEditor { protected nameLabel: string = 'name'; protected columnLabel: string = 'column label'; protected targetElement: JQuery; - protected defaultCell: object = {spanned: 0, rowspan: 1, colspan: 1, name: '', colpos: '', column: undefined}; + protected defaultCell: Cell = { spanned: 0, rowspan: 1, colspan: 1, name: '', colpos: '', column: undefined }; protected selectorEditor: string = '.t3js-grideditor'; protected selectorAddColumn: string = '.t3js-grideditor-addcolumn'; protected selectorRemoveColumn: string = '.t3js-grideditor-removecolumn'; @@ -69,17 +59,6 @@ export class GridEditor { protected selectorPreviewArea: string = '.t3js-tsconfig-preview-area'; protected selectorCodeMirror: string = '.t3js-grideditor-preview-config .CodeMirror'; - /** - * Remove all markup - * - * @param {String} input - * @returns {string} - */ - public static stripMarkup(input: string): string { - const securityUtility = new SecurityUtility(); - return securityUtility.stripHtml(input); - } - /** * * @param {GridEditorConfigurationInterface} config @@ -101,6 +80,17 @@ export class GridEditor { this.writeConfig(this.export2LayoutRecord()); } + /** + * Remove all markup + * + * @param {String} input + * @returns {string} + */ + public static stripMarkup(input: string): string { + const securityUtility = new SecurityUtility(); + return securityUtility.stripHtml(input); + } + /** * */ @@ -127,7 +117,7 @@ export class GridEditor { * @param {Event} e */ protected modalButtonClickHandler = (e: Event) => { - const button: any = e.target; + const button = e.target as HTMLButtonElement; const modal: ModalElement = e.currentTarget as ModalElement; if (button.name === 'cancel') { modal.hideModal(); @@ -146,7 +136,7 @@ export class GridEditor { this.writeConfig(this.export2LayoutRecord()); modal.hideModal(); } - } + }; /** * @@ -157,7 +147,7 @@ export class GridEditor { this.addColumn(); this.drawTable(); this.writeConfig(this.export2LayoutRecord()); - } + }; /** * @@ -168,7 +158,7 @@ export class GridEditor { this.removeColumn(); this.drawTable(); this.writeConfig(this.export2LayoutRecord()); - } + }; /** * @@ -179,7 +169,7 @@ export class GridEditor { this.addRowTop(); this.drawTable(); this.writeConfig(this.export2LayoutRecord()); - } + }; /** * @@ -190,7 +180,7 @@ export class GridEditor { this.addRowBottom(); this.drawTable(); this.writeConfig(this.export2LayoutRecord()); - } + }; /** * @@ -201,7 +191,7 @@ export class GridEditor { this.removeRowTop(); this.drawTable(); this.writeConfig(this.export2LayoutRecord()); - } + }; /** * @@ -212,7 +202,7 @@ export class GridEditor { this.removeRowBottom(); this.drawTable(); this.writeConfig(this.export2LayoutRecord()); - } + }; /** * @@ -222,7 +212,7 @@ export class GridEditor { e.preventDefault(); const $element = $(e.currentTarget); this.showOptions($element.data('col'), $element.data('row')); - } + }; /** * @@ -234,7 +224,7 @@ export class GridEditor { this.addColspan($element.data('col'), $element.data('row')); this.drawTable(); this.writeConfig(this.export2LayoutRecord()); - } + }; /** * @@ -246,7 +236,7 @@ export class GridEditor { this.removeColspan($element.data('col'), $element.data('row')); this.drawTable(); this.writeConfig(this.export2LayoutRecord()); - } + }; /** * @@ -258,7 +248,7 @@ export class GridEditor { this.addRowspan($element.data('col'), $element.data('row')); this.drawTable(); this.writeConfig(this.export2LayoutRecord()); - } + }; /** * @@ -270,13 +260,13 @@ export class GridEditor { this.removeRowspan($element.data('col'), $element.data('row')); this.drawTable(); this.writeConfig(this.export2LayoutRecord()); - } + }; /** * Create a new cell from defaultCell * @returns {Object} */ - protected getNewCell(): any { + protected getNewCell(): Cell { return $.extend({}, this.defaultCell); } @@ -285,7 +275,7 @@ export class GridEditor { * * @param data */ - protected writeConfig(data: any): void { + protected writeConfig(data: string): void { this.field.val(data); const configLines = data.split('\n'); let config = ''; @@ -295,12 +285,12 @@ export class GridEditor { } } - let content = 'mod.web_layout.BackendLayouts {\n' + + const content = 'mod.web_layout.BackendLayouts {\n' + ' exampleKey {\n' + ' title = Example\n' + ' icon = EXT:example_extension/Resources/Public/Images/BackendLayouts/default.gif\n' + ' config {\n' + - config.replace(new RegExp('\t', 'g'), ' ') + + config.replace(new RegExp('\\t', 'g'), ' ') + ' }\n' + ' }\n' + '}\n'; @@ -312,7 +302,7 @@ export class GridEditor { // Update CodeMirror content if instantiated const codemirror: any = document.querySelector(this.selectorCodeMirror); if (codemirror) { - codemirror.CodeMirror.setValue(content) + codemirror.CodeMirror.setValue(content); } } @@ -704,7 +694,7 @@ export class GridEditor { * @param {number} col * @param {number} row */ - protected getCell(col: number, row: number): any { + protected getCell(col: number, row: number): Cell|false|null { if (col > this.colCount - 1) { return false; } @@ -732,6 +722,10 @@ export class GridEditor { } const cell = this.getCell(col, row); + if (!cell) { + return false; + } + let checkCell; if (cell.rowspan > 1) { for (let rowIndex = row; rowIndex < row + cell.rowspan; rowIndex++) { @@ -764,6 +758,10 @@ export class GridEditor { } const cell = this.getCell(col, row); + if (!cell) { + return false; + } + let checkCell; if (cell.colspan > 1) { // we have to check all cells on the right side for the complete colspan @@ -941,7 +939,7 @@ export class GridEditor { // In case the editor is already visible, we don't have to add the observer return; } - new IntersectionObserver((entries: IntersectionObserverEntry[], observer: IntersectionObserver) => { + new IntersectionObserver((entries: IntersectionObserverEntry[]) => { entries.forEach(entry => { const codemirror: any = document.querySelector(this.selectorCodeMirror); if (entry.intersectionRatio > 0 && codemirror) { diff --git a/Build/Sources/TypeScript/backend/hashing/md5.ts b/Build/Sources/TypeScript/backend/hashing/md5.ts index b5c067634239..3ad9ae7bd94b 100644 --- a/Build/Sources/TypeScript/backend/hashing/md5.ts +++ b/Build/Sources/TypeScript/backend/hashing/md5.ts @@ -15,7 +15,6 @@ class Md5 { public static hash(value: string): string { - let x; let k; let AA; let BB; @@ -43,7 +42,7 @@ class Md5 { const S44 = 21; value = Md5.utf8Encode(value); - x = Md5.convertToWordArray(value); + const x = Md5.convertToWordArray(value); a = 0x67452301; b = 0xEFCDAB89; @@ -125,7 +124,7 @@ class Md5 { d = Md5.addUnsigned(d, DD); } - let temp = Md5.wordToHex(a) + Md5.wordToHex(b) + Md5.wordToHex(c) + Md5.wordToHex(d); + const temp = Md5.wordToHex(a) + Md5.wordToHex(b) + Md5.wordToHex(c) + Md5.wordToHex(d); return temp.toLowerCase(); } @@ -135,11 +134,11 @@ class Md5 { } private static addUnsigned(lX: number, lY: number): number { - let lX8 = (lX & 0x80000000); - let lY8 = (lY & 0x80000000); - let lX4 = (lX & 0x40000000); - let lY4 = (lY & 0x40000000); - let lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF); + const lX8 = (lX & 0x80000000); + const lY8 = (lY & 0x80000000); + const lX4 = (lX & 0x40000000); + const lY4 = (lY & 0x40000000); + const lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF); if (lX4 & lY4) { return (lResult ^ 0x80000000 ^ lX8 ^ lY8); } @@ -192,11 +191,11 @@ class Md5 { private static convertToWordArray(string: string): Array<number> { let lWordCount; - let lMessageLength = string.length; - let lNumberOfWords_temp1 = lMessageLength + 8; - let lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64; - let lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16; - let lWordArray = Array(lNumberOfWords - 1); + const lMessageLength = string.length; + const lNumberOfWords_temp1 = lMessageLength + 8; + const lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64; + const lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16; + const lWordArray = Array(lNumberOfWords - 1); let lBytePosition = 0; let lByteCount = 0; while (lByteCount < lMessageLength) { @@ -231,7 +230,7 @@ class Md5 { let utftext = ''; for (let n = 0; n < string.length; n++) { - let c = string.charCodeAt(n); + const c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); diff --git a/Build/Sources/TypeScript/backend/icons.ts b/Build/Sources/TypeScript/backend/icons.ts index 51bbd294e874..89a3b3e8e60e 100644 --- a/Build/Sources/TypeScript/backend/icons.ts +++ b/Build/Sources/TypeScript/backend/icons.ts @@ -11,10 +11,10 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import ClientStorage from './storage/client'; -import {Sizes, States, MarkupIdentifiers} from './enum/icon-types'; +import { Sizes, States, MarkupIdentifiers } from './enum/icon-types'; interface PromiseCache { [key: string]: Promise<string>; @@ -25,9 +25,9 @@ interface PromiseCache { * Uses the icon API of the core to fetch icons via AJAX. */ class Icons { - public readonly sizes: any = Sizes; - public readonly states: any = States; - public readonly markupIdentifiers: any = MarkupIdentifiers; + public readonly sizes: typeof Sizes = Sizes; + public readonly states: typeof States = States; + public readonly markupIdentifiers: typeof MarkupIdentifiers = MarkupIdentifiers; private readonly promiseCache: PromiseCache = {}; /** @@ -64,7 +64,7 @@ class Icons { const describedIcon = [identifier, size, overlayIdentifier, state, markupIdentifier]; const cacheIdentifier = describedIcon.join('_'); - return this.getIconRegistryCache().then((registryCacheIdentifier: string): any => { + return this.getIconRegistryCache().then((registryCacheIdentifier: string): Promise<string> => { if (!ClientStorage.isset('icon_registry_cache_identifier') || ClientStorage.get('icon_registry_cache_identifier') !== registryCacheIdentifier ) { @@ -72,7 +72,7 @@ class Icons { ClientStorage.set('icon_registry_cache_identifier', registryCacheIdentifier); } - return this.fetchFromLocal(cacheIdentifier).then(null, (): any => { + return this.fetchFromLocal(cacheIdentifier).then(null, (): Promise<string> => { return this.fetchFromRemote(describedIcon, cacheIdentifier); }); }); @@ -86,7 +86,7 @@ class Icons { promiseCacheIdentifier, (new AjaxRequest(TYPO3.settings.ajaxUrls.icons_cache)).get() .then(async (response: AjaxResponse): Promise<string> => { - return await response.resolve() + return await response.resolve(); }) ); } diff --git a/Build/Sources/TypeScript/backend/image-manipulation.ts b/Build/Sources/TypeScript/backend/image-manipulation.ts index 189f52d6bae2..42387f384973 100644 --- a/Build/Sources/TypeScript/backend/image-manipulation.ts +++ b/Build/Sources/TypeScript/backend/image-manipulation.ts @@ -12,15 +12,15 @@ */ import $ from 'jquery'; -import {html} from 'lit'; -import {unsafeHTML} from 'lit/directives/unsafe-html'; +import { html } from 'lit'; +import { unsafeHTML } from 'lit/directives/unsafe-html'; import 'jquery-ui/draggable'; import 'jquery-ui/resizable'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import FormEngineValidation from '@typo3/backend/form-engine-validation'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Cropper from 'cropperjs'; -import {default as Modal, ModalElement} from './modal'; +import { default as Modal, ModalElement } from './modal'; import '@typo3/backend/element/spinner-element'; interface Area { @@ -308,7 +308,7 @@ class ImageManipulation { this.setAspectRatio(ratio); // set data explicitly or setAspectRatio upscales the crop this.setCropArea(temp.cropArea); - this.currentCropVariant = $.extend(true, {}, temp, {selectedRatio: ratioId}); + this.currentCropVariant = $.extend(true, {}, temp, { selectedRatio: ratioId }); this.update(this.currentCropVariant); }); @@ -350,7 +350,7 @@ class ImageManipulation { } const resetCropVariant: CropVariant = JSON.parse(resetCropVariantString); const absoluteCropArea: Area = this.convertRelativeToAbsoluteCropArea(resetCropVariant.cropArea, imageData); - this.currentCropVariant = $.extend(true, {}, resetCropVariant, {cropArea: absoluteCropArea}); + this.currentCropVariant = $.extend(true, {}, resetCropVariant, { cropArea: absoluteCropArea }); this.update(this.currentCropVariant); }); @@ -400,7 +400,7 @@ class ImageManipulation { this.data[cropVariantId].cropArea, imageData, ); - const variant: CropVariant = $.extend(true, {}, this.data[cropVariantId], {cropArea}); + const variant: CropVariant = $.extend(true, {}, this.data[cropVariantId], { cropArea }); this.updatePreviewThumbnail(variant, $(elem)); }); @@ -433,7 +433,7 @@ class ImageManipulation { // set data explicitly or setAspectRatio up-scales the crop $(this.currentModal).find(`[data-bs-option='${this.currentCropVariant.selectedRatio}']`).addClass('active'); } - } + }; /** * @method cropMoveHandler @@ -445,14 +445,14 @@ class ImageManipulation { return; } - let minCroppedWidth = 15; - let minCroppedHeight = 15; + const minCroppedWidth = 15; + const minCroppedHeight = 15; let width = e.detail.width; let height = e.detail.height; if (width < minCroppedWidth || height < minCroppedHeight) { - width = Math.max(minCroppedHeight, height) - height = Math.max(minCroppedWidth, width) + width = Math.max(minCroppedHeight, height); + height = Math.max(minCroppedWidth, width); this.cropper.setData({ width: width, @@ -472,7 +472,7 @@ class ImageManipulation { const naturalWidth: number = Math.round(this.currentCropVariant.cropArea.width * this.imageOriginalSizeFactor); const naturalHeight: number = Math.round(this.currentCropVariant.cropArea.height * this.imageOriginalSizeFactor); this.cropInfo.text(`${naturalWidth}×${naturalHeight} px`); - } + }; /** * @method cropStartHandler @@ -484,7 +484,7 @@ class ImageManipulation { this.focusArea.draggable('option', 'disabled', true); this.focusArea.resizable('option', 'disabled', true); } - } + }; /** * @method cropEndHandler @@ -496,7 +496,7 @@ class ImageManipulation { this.focusArea.draggable('option', 'disabled', false); this.focusArea.resizable('option', 'disabled', false); } - } + }; /** * @method update @@ -575,9 +575,9 @@ class ImageManipulation { this.scaleAndMoveFocusArea(this.currentCropVariant.focusArea); }, drag: (): void => { - const {left, top}: Offset = container.offset(); - const {left: fLeft, top: fTop}: Offset = this.focusArea.offset(); - const {focusArea, coverAreas}: {focusArea?: Area, coverAreas?: Area[]} = this.currentCropVariant; + const { left, top }: Offset = container.offset(); + const { left: fLeft, top: fTop }: Offset = this.focusArea.offset(); + const { focusArea, coverAreas }: {focusArea?: Area, coverAreas?: Area[]} = this.currentCropVariant; focusArea.x = (fLeft - left) / container.width(); focusArea.y = (fTop - top) / container.height(); @@ -590,9 +590,9 @@ class ImageManipulation { }, revert: (): boolean => { const revertDelay = 250; - const {left, top}: Offset = container.offset(); - const {left: fLeft, top: fTop}: Offset = this.focusArea.offset(); - const {focusArea, coverAreas}: {focusArea?: Area, coverAreas?: Area[]} = this.currentCropVariant; + const { left, top }: Offset = container.offset(); + const { left: fLeft, top: fTop }: Offset = this.focusArea.offset(); + const { focusArea, coverAreas }: {focusArea?: Area, coverAreas?: Area[]} = this.currentCropVariant; if (this.checkFocusAndCoverAreasCollision(focusArea, coverAreas)) { this.focusArea.removeClass('has-nodrop'); @@ -607,9 +607,9 @@ class ImageManipulation { }, revertDuration: 200, stop: (): void => { - const {left, top}: Offset = container.offset(); - const {left: fLeft, top: fTop}: Offset = this.focusArea.offset(); - const {focusArea}: {focusArea?: Area} = this.currentCropVariant; + const { left, top }: Offset = container.offset(); + const { left: fLeft, top: fTop }: Offset = this.focusArea.offset(); + const { focusArea }: {focusArea?: Area} = this.currentCropVariant; focusArea.x = (fLeft - left) / container.width(); focusArea.y = (fTop - top) / container.height(); @@ -621,9 +621,9 @@ class ImageManipulation { containment: container, handles: 'all', resize: (): void => { - const {left, top}: Offset = container.offset(); - const {left: fLeft, top: fTop}: Offset = this.focusArea.offset(); - const {focusArea, coverAreas}: {focusArea?: Area, coverAreas?: Area[]} = this.currentCropVariant; + const { left, top }: Offset = container.offset(); + const { left: fLeft, top: fTop }: Offset = this.focusArea.offset(); + const { focusArea, coverAreas }: {focusArea?: Area, coverAreas?: Area[]} = this.currentCropVariant; focusArea.height = this.focusArea.height() / container.height(); focusArea.width = this.focusArea.width() / container.width(); @@ -640,9 +640,9 @@ class ImageManipulation { }, stop: (event: any, ui: any): void => { const revertDelay = 250; - const {left, top}: Offset = container.offset(); - const {left: fLeft, top: fTop}: Offset = this.focusArea.offset(); - const {focusArea, coverAreas}: {focusArea?: Area, coverAreas?: Area[]} = this.currentCropVariant; + const { left, top }: Offset = container.offset(); + const { left: fLeft, top: fTop }: Offset = this.focusArea.offset(); + const { focusArea, coverAreas }: {focusArea?: Area, coverAreas?: Area[]} = this.currentCropVariant; if (this.checkFocusAndCoverAreasCollision(focusArea, coverAreas)) { ui.element.animate($.extend(ui.originalPosition, ui.originalSize), revertDelay, (): void => { @@ -689,7 +689,6 @@ class ImageManipulation { * @private */ private updatePreviewThumbnail(cropVariant: CropVariant, cropVariantTrigger: JQuery): void { - let styles: any; const cropperPreviewThumbnailCrop: JQuery = cropVariantTrigger.find('.t3js-cropper-preview-thumbnail-crop-area'); const cropperPreviewThumbnailImage: JQuery = @@ -717,7 +716,7 @@ class ImageManipulation { } // destruct the preview container's CSS properties - styles = cropperPreviewThumbnailCrop.css([ + const styles = cropperPreviewThumbnailCrop.css([ 'width', 'height', 'left', 'top', ]); @@ -759,7 +758,7 @@ class ImageManipulation { private updateCropVariantData(currentCropVariant: CropVariant): void { const imageData: Cropper.ImageData = this.cropper.getImageData(); const absoluteCropArea: Area = this.convertAbsoluteToRelativeCropArea(currentCropVariant.cropArea, imageData); - this.data[currentCropVariant.id] = $.extend(true, {}, currentCropVariant, {cropArea: absoluteCropArea}); + this.data[currentCropVariant.id] = $.extend(true, {}, currentCropVariant, { cropArea: absoluteCropArea }); } /** @@ -826,7 +825,7 @@ class ImageManipulation { * @return {Area} */ private convertAbsoluteToRelativeCropArea(cropArea: Area, imageData: Cropper.ImageData): Area { - const {height, width, x, y}: Area = cropArea; + const { height, width, x, y }: Area = cropArea; return { height: height / imageData.naturalHeight, width: width / imageData.naturalWidth, @@ -843,7 +842,7 @@ class ImageManipulation { * @return {{height: number, width: number, x: number, y: number}} */ private convertRelativeToAbsoluteCropArea(cropArea: Area, imageData: Cropper.ImageData): Area { - const {height, width, x, y}: Area = cropArea; + const { height, width, x, y }: Area = cropArea; return { height: height * imageData.naturalHeight, width: width * imageData.naturalWidth, @@ -858,7 +857,8 @@ class ImageManipulation { * @param {Object} data - The internal crop variants state */ private setPreviewImages(data: {[key: string]: CropVariant}): void { - // @ts-ignore .image is not declared + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore const image: HTMLImageElement = this.cropper.image; const imageData: Cropper.ImageData = this.cropper.getImageData(); diff --git a/Build/Sources/TypeScript/backend/info-window.ts b/Build/Sources/TypeScript/backend/info-window.ts index 244db21bd038..21736604b234 100644 --- a/Build/Sources/TypeScript/backend/info-window.ts +++ b/Build/Sources/TypeScript/backend/info-window.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {SeverityEnum} from './enum/severity'; +import { SeverityEnum } from './enum/severity'; import Modal from './modal'; /** diff --git a/Build/Sources/TypeScript/backend/input/clearable.ts b/Build/Sources/TypeScript/backend/input/clearable.ts index 2564c7f94a13..42bd3b174bf7 100644 --- a/Build/Sources/TypeScript/backend/input/clearable.ts +++ b/Build/Sources/TypeScript/backend/input/clearable.ts @@ -12,6 +12,14 @@ */ class Clearable { + constructor() { + if (typeof HTMLInputElement.prototype.clearable === 'function') { + return; + } + + this.registerClearable(); + } + private static createCloseButton(): HTMLButtonElement { // The inlined markup represents the current generated markup from the // icon api for the icon actions-close that can be found in the official @@ -49,14 +57,6 @@ class Clearable { return closeButton; } - constructor() { - if (typeof HTMLInputElement.prototype.clearable === 'function') { - return; - } - - this.registerClearable(); - } - private registerClearable(): void { HTMLInputElement.prototype.clearable = function(options: Options = {}): void { if (this.isClearable) { @@ -87,7 +87,7 @@ class Clearable { options.onClear(this); } - this.dispatchEvent(new Event('change', {bubbles: true, cancelable: true})); + this.dispatchEvent(new Event('change', { bubbles: true, cancelable: true })); toggleClearButtonVisibility(); }); wrap.appendChild(clearButton); diff --git a/Build/Sources/TypeScript/backend/layout-module/drag-drop.ts b/Build/Sources/TypeScript/backend/layout-module/drag-drop.ts index 798b5d2bf4a4..b29c0d5870e7 100644 --- a/Build/Sources/TypeScript/backend/layout-module/drag-drop.ts +++ b/Build/Sources/TypeScript/backend/layout-module/drag-drop.ts @@ -43,6 +43,12 @@ class DragDrop { private static readonly dropPossibleHoverClass: string = 't3-page-ce-dropzone-possible'; private static readonly addContentIdentifier: string = '.t3js-page-new-ce'; + constructor() { + DocumentService.ready().then((): void => { + DragDrop.initialize(); + }); + } + /** * initializes Drag+Drop for all content elements on the page */ @@ -89,7 +95,7 @@ class DragDrop { const dropzoneRect = dropElement.getBoundingClientRect(); return (event.pageX >= dropzoneRect.left && event.pageX <= dropzoneRect.left + dropzoneRect.width) // is cursor in boundaries of x-axis - && (event.pageY >= dropzoneRect.top && event.pageY <= dropzoneRect.top + dropzoneRect.height) // is cursor in boundaries of y-axis; + && (event.pageY >= dropzoneRect.top && event.pageY <= dropzoneRect.top + dropzoneRect.height); // is cursor in boundaries of y-axis; } }).on('dragenter', (e: DropEvent): void => { e.target.classList.add(DragDrop.dropPossibleHoverClass); @@ -191,7 +197,7 @@ class DragDrop { const contentElementUid: number = parseInt(draggedElement.dataset.uid, 10); if (typeof (contentElementUid) === 'number' && contentElementUid > 0) { - let parameters: Parameters = {}; + const parameters: Parameters = {}; // add the information about a possible column position change const targetFound = (dropContainer.closest(DragDrop.contentIdentifier) as HTMLElement).dataset.uid; // the item was moved to the top of the colPos, so the page ID is used here @@ -259,7 +265,7 @@ class DragDrop { * @param {boolean} isCopyAction * @private */ - private static ajaxAction(dropContainer: HTMLElement, draggedElement: HTMLElement, parameters: Parameters, isCopyAction: boolean): Promise<any> { + private static ajaxAction(dropContainer: HTMLElement, draggedElement: HTMLElement, parameters: Parameters, isCopyAction: boolean): Promise<void> { const table: string = Object.keys(parameters.cmd).shift(); const uid: number = parseInt(Object.keys(parameters.cmd[table]).shift(), 10); const eventData = { component: 'dragdrop', action: isCopyAction ? 'copy' : 'move', table, uid }; @@ -293,12 +299,6 @@ class DragDrop { } return false; } - - constructor() { - DocumentService.ready().then((): void => { - DragDrop.initialize(); - }); - } } export default new DragDrop(); diff --git a/Build/Sources/TypeScript/backend/layout-module/paste.ts b/Build/Sources/TypeScript/backend/layout-module/paste.ts index 6263d57bb6a9..3f59fcc62258 100644 --- a/Build/Sources/TypeScript/backend/layout-module/paste.ts +++ b/Build/Sources/TypeScript/backend/layout-module/paste.ts @@ -21,10 +21,10 @@ import DocumentService from '@typo3/core/document-service'; import $ from 'jquery'; import ResponseInterface from '../ajax-data-handler/response-interface'; import DataHandler from '../ajax-data-handler'; -import {default as Modal, ModalElement, Button} from '@typo3/backend/modal'; +import { default as Modal, ModalElement, Button } from '@typo3/backend/modal'; import Severity from '../severity'; import '@typo3/backend/element/icon-element'; -import {SeverityEnum} from '../enum/severity'; +import { SeverityEnum } from '../enum/severity'; class Paste { private readonly itemOnClipboardUid: number = 0; @@ -34,19 +34,6 @@ class Paste { private pasteAfterLinkTemplate: string = ''; private pasteIntoLinkTemplate: string = ''; - /** - * @param {JQuery} $element - * @return number - */ - private static determineColumn($element: JQuery): number { - const $columnContainer = $element.closest('[data-colpos]'); - if ($columnContainer.length && $columnContainer.data('colpos') !== 'undefined') { - return $columnContainer.data('colpos'); - } - - return 0; - } - /** * initializes paste icons for all content elements on the page */ @@ -64,6 +51,19 @@ class Paste { }); } + /** + * @param {JQuery} $element + * @return number + */ + private static determineColumn($element: JQuery): number { + const $columnContainer = $element.closest('[data-colpos]'); + if ($columnContainer.length && $columnContainer.data('colpos') !== 'undefined') { + return $columnContainer.data('colpos'); + } + + return 0; + } + private initializeEvents(): void { $(document).on('click', '.t3js-paste', (evt: Event): void => { @@ -97,7 +97,7 @@ class Paste { private activatePasteIcons(): void { if (this.pasteAfterLinkTemplate && this.pasteIntoLinkTemplate) { document.querySelectorAll('.t3js-page-new-ce').forEach((el: HTMLElement): void => { - let template = el.parentElement.dataset.page ? this.pasteIntoLinkTemplate : this.pasteAfterLinkTemplate; + const template = el.parentElement.dataset.page ? this.pasteIntoLinkTemplate : this.pasteAfterLinkTemplate; el.append(document.createRange().createContextualFragment(template)); }); } diff --git a/Build/Sources/TypeScript/backend/link-browser.ts b/Build/Sources/TypeScript/backend/link-browser.ts index 83ce7bb288d3..ee7beb4a8181 100644 --- a/Build/Sources/TypeScript/backend/link-browser.ts +++ b/Build/Sources/TypeScript/backend/link-browser.ts @@ -30,7 +30,7 @@ class LinkBrowser { constructor() { DocumentService.ready().then((): void => { this.urlParameters = JSON.parse(document.body.dataset.urlParameters || '{}'); - this.parameters = JSON.parse(document.body.dataset.parameters || '{}'); + this.parameters = JSON.parse(document.body.dataset.parameters || '{}'); this.linkAttributeFields = JSON.parse(document.body.dataset.linkAttributeFields || '{}'); new RegularEvent('change', this.loadTarget) @@ -60,15 +60,6 @@ class LinkBrowser { } } - /** - * Stores the final link - * - * This method MUST be overridden in the actual implementation of the link browser. - * The function is responsible for encoding the link (and possible link attributes) and - * returning it to the caller (e.g. FormEngine, RTE, etc) - * - * @param {String} link The select element or anything else which identifies the link (e.g. "page:<pageUid>" or "file:<uid>") - */ public finalizeFunction(link: string): void { throw 'The link browser requires the finalizeFunction to be set. Seems like you discovered a major bug.'; } diff --git a/Build/Sources/TypeScript/backend/live-search/element/backend-search.ts b/Build/Sources/TypeScript/backend/live-search/element/backend-search.ts index 363b8cf6864e..833a8ba66653 100644 --- a/Build/Sources/TypeScript/backend/live-search/element/backend-search.ts +++ b/Build/Sources/TypeScript/backend/live-search/element/backend-search.ts @@ -11,8 +11,8 @@ * The TYPO3 project - inspiring people to share! */ -import {customElement} from 'lit/decorators'; -import {html, LitElement} from 'lit'; +import { customElement } from 'lit/decorators'; +import { LitElement } from 'lit'; /** * Module: @typo3/backend/live-search/element/backend-search diff --git a/Build/Sources/TypeScript/backend/live-search/element/provider/default-result-item.ts b/Build/Sources/TypeScript/backend/live-search/element/provider/default-result-item.ts index 792bafef0b72..c98f22762a8c 100644 --- a/Build/Sources/TypeScript/backend/live-search/element/provider/default-result-item.ts +++ b/Build/Sources/TypeScript/backend/live-search/element/provider/default-result-item.ts @@ -11,16 +11,16 @@ * The TYPO3 project - inspiring people to share! */ -import {customElement, property} from 'lit/decorators'; -import {html, LitElement, TemplateResult} from 'lit'; +import { customElement, property } from 'lit/decorators'; +import { html, LitElement, TemplateResult } from 'lit'; import '@typo3/backend/element/icon-element'; @customElement('typo3-backend-live-search-result-item-default') export class PageProviderResultItem extends LitElement { - @property({type: Object, attribute: false}) icon: { [key: string]: string }; - @property({type: String, attribute: false}) itemTitle: string; - @property({type: String, attribute: false}) typeLabel: string; - @property({type: Object, attribute: false}) extraData: { [key: string]: any }; + @property({ type: Object, attribute: false }) icon: Record<string, string>; + @property({ type: String, attribute: false }) itemTitle: string; + @property({ type: String, attribute: false }) typeLabel: string; + @property({ type: Object, attribute: false }) extraData: { [key: string]: any }; public createRenderRoot(): HTMLElement | ShadowRoot { // Avoid shadow DOM for Bootstrap CSS to be applied diff --git a/Build/Sources/TypeScript/backend/live-search/element/provider/page-provider-result-item.ts b/Build/Sources/TypeScript/backend/live-search/element/provider/page-provider-result-item.ts index 9c3be8648692..e31494d547e2 100644 --- a/Build/Sources/TypeScript/backend/live-search/element/provider/page-provider-result-item.ts +++ b/Build/Sources/TypeScript/backend/live-search/element/provider/page-provider-result-item.ts @@ -11,16 +11,16 @@ * The TYPO3 project - inspiring people to share! */ -import {customElement, property} from 'lit/decorators'; -import {html, LitElement, TemplateResult} from 'lit'; +import { customElement, property } from 'lit/decorators'; +import { html, LitElement, TemplateResult } from 'lit'; import '@typo3/backend/element/icon-element'; @customElement('typo3-backend-live-search-result-item-page-provider') export default class PageProviderResultItem extends LitElement { - @property({type: Object, attribute: false}) icon: { [key: string]: string }; - @property({type: String, attribute: false}) itemTitle: string; - @property({type: String, attribute: false}) typeLabel: string; - @property({type: Object, attribute: false}) extraData: { [key: string]: any }; + @property({ type: Object, attribute: false }) icon: Record<string, string>; + @property({ type: String, attribute: false }) itemTitle: string; + @property({ type: String, attribute: false }) typeLabel: string; + @property({ type: Object, attribute: false }) extraData: { [key: string]: any }; public createRenderRoot(): HTMLElement | ShadowRoot { // Avoid shadow DOM for Bootstrap CSS to be applied diff --git a/Build/Sources/TypeScript/backend/live-search/element/result/item/action/action-container.ts b/Build/Sources/TypeScript/backend/live-search/element/result/item/action/action-container.ts index 814ece8b8866..3a4e44ef2faf 100644 --- a/Build/Sources/TypeScript/backend/live-search/element/result/item/action/action-container.ts +++ b/Build/Sources/TypeScript/backend/live-search/element/result/item/action/action-container.ts @@ -11,17 +11,17 @@ * The TYPO3 project - inspiring people to share! */ -import {customElement, property} from 'lit/decorators'; -import {css, html, LitElement, TemplateResult} from 'lit'; +import { customElement, property } from 'lit/decorators'; +import { css, html, LitElement, TemplateResult } from 'lit'; import './action'; -import {ResultItemActionInterface, ResultItemInterface} from '../item'; -import {Action} from './action'; +import { ResultItemActionInterface, ResultItemInterface } from '../item'; +import { Action } from './action'; export const componentName = 'typo3-backend-live-search-result-item-action-container'; @customElement(componentName) export class ActionContainer extends LitElement { - @property({type: Object, attribute: false}) resultItem: ResultItemInterface|null = null; + @property({ type: Object, attribute: false }) resultItem: ResultItemInterface|null = null; public createRenderRoot(): HTMLElement | ShadowRoot { // Avoid shadow DOM for Bootstrap CSS to be applied @@ -95,7 +95,7 @@ export class ActionList extends LitElement { let focusableCandidate; if (e.key === 'ArrowDown') { - focusableCandidate = document.activeElement.nextElementSibling + focusableCandidate = document.activeElement.nextElementSibling; } else if (e.key === 'ArrowUp') { focusableCandidate = document.activeElement.previousElementSibling; } else if (e.key === 'ArrowLeft') { diff --git a/Build/Sources/TypeScript/backend/live-search/element/result/item/action/action.ts b/Build/Sources/TypeScript/backend/live-search/element/result/item/action/action.ts index aaff438349bc..673db7089c08 100644 --- a/Build/Sources/TypeScript/backend/live-search/element/result/item/action/action.ts +++ b/Build/Sources/TypeScript/backend/live-search/element/result/item/action/action.ts @@ -11,16 +11,16 @@ * The TYPO3 project - inspiring people to share! */ -import {customElement, property} from 'lit/decorators'; -import {ifDefined} from 'lit/directives/if-defined'; -import {html, LitElement, TemplateResult} from 'lit'; +import { customElement, property } from 'lit/decorators'; +import { ifDefined } from 'lit/directives/if-defined'; +import { html, LitElement, TemplateResult } from 'lit'; import '@typo3/backend/element/icon-element'; -import {ResultItemActionInterface, ResultItemInterface} from '../item'; +import { ResultItemActionInterface, ResultItemInterface } from '../item'; @customElement('typo3-backend-live-search-result-item-action') export class Action extends LitElement { - @property({type: Object, attribute: false}) resultItem: ResultItemInterface; - @property({type: Object, attribute: false}) resultItemAction: ResultItemActionInterface; + @property({ type: Object, attribute: false }) resultItem: ResultItemInterface; + @property({ type: Object, attribute: false }) resultItemAction: ResultItemActionInterface; public connectedCallback(): void { super.connectedCallback(); diff --git a/Build/Sources/TypeScript/backend/live-search/element/result/item/item-container.ts b/Build/Sources/TypeScript/backend/live-search/element/result/item/item-container.ts index ee8e7f4ececf..b64e774716a3 100644 --- a/Build/Sources/TypeScript/backend/live-search/element/result/item/item-container.ts +++ b/Build/Sources/TypeScript/backend/live-search/element/result/item/item-container.ts @@ -13,12 +13,12 @@ import '@typo3/backend/element/spinner-element'; import LiveSearchConfigurator from '@typo3/backend/live-search/live-search-configurator'; -import {css, html, LitElement, TemplateResult} from 'lit'; -import {customElement, property} from 'lit/decorators'; -import {until} from 'lit/directives/until'; +import { css, html, LitElement, TemplateResult } from 'lit'; +import { customElement, property } from 'lit/decorators'; +import { until } from 'lit/directives/until'; import '../../provider/default-result-item'; import './item'; -import {Item, ResultItemActionInterface, ResultItemInterface} from './item'; +import { Item, ResultItemActionInterface, ResultItemInterface } from './item'; type GroupedResultItems = { [key: string ]: ResultItemInterface[] }; @@ -26,7 +26,7 @@ export const componentName = 'typo3-backend-live-search-result-item-container'; @customElement(componentName) export class ItemContainer extends LitElement { - @property({type: Object, attribute: false}) results: ResultItemInterface[]|null = null; + @property({ type: Object, attribute: false }) results: ResultItemInterface[]|null = null; public connectedCallback(): void { super.connectedCallback(); @@ -60,8 +60,8 @@ export class ItemContainer extends LitElement { private renderGroupedResults(groupedResults: GroupedResultItems): TemplateResult { const items = []; - for (let [type, results] of Object.entries(groupedResults)) { - let countElements = results.length; + for (const [type, results] of Object.entries(groupedResults)) { + const countElements = results.length; items.push(html`<h6 class="livesearch-result-item-group-label">${type} (${countElements})</h6>`); items.push(...results.map((result: ResultItemInterface) => html`${until( this.renderResultItem(result), @@ -69,7 +69,7 @@ export class ItemContainer extends LitElement { )}`)); } - return html`${items}` + return html`${items}`; } private async renderResultItem(resultItem: ResultItemInterface): Promise<TemplateResult> { diff --git a/Build/Sources/TypeScript/backend/live-search/element/result/item/item.ts b/Build/Sources/TypeScript/backend/live-search/element/result/item/item.ts index ba0155e3a88e..51e2c2f2ad08 100644 --- a/Build/Sources/TypeScript/backend/live-search/element/result/item/item.ts +++ b/Build/Sources/TypeScript/backend/live-search/element/result/item/item.ts @@ -11,14 +11,14 @@ * The TYPO3 project - inspiring people to share! */ -import {customElement, property} from 'lit/decorators'; -import {html, LitElement, TemplateResult} from 'lit'; +import { customElement, property } from 'lit/decorators'; +import { html, LitElement, TemplateResult } from 'lit'; import '@typo3/backend/element/icon-element'; export interface ResultItemInterface { provider: string; actions: ResultItemActionInterface[]; - icon: { [key: string]: string }; + icon: Record<string, string>; itemTitle: string; typeLabel: string; extraData: { [key: string]: any } @@ -26,14 +26,14 @@ export interface ResultItemInterface { export interface ResultItemActionInterface { identifier: string; - icon: { [key: string]: string }; + icon: Record<string, string>; label: string; url: string; } @customElement('typo3-backend-live-search-result-item') export class Item extends LitElement { - @property({type: Object, attribute: false}) resultItem: ResultItemInterface; + @property({ type: Object, attribute: false }) resultItem: ResultItemInterface; private parentContainer: HTMLElement; private resultItemContainer: HTMLElement; diff --git a/Build/Sources/TypeScript/backend/live-search/element/result/result-container.ts b/Build/Sources/TypeScript/backend/live-search/element/result/result-container.ts index a7300937d029..5903c63eec73 100644 --- a/Build/Sources/TypeScript/backend/live-search/element/result/result-container.ts +++ b/Build/Sources/TypeScript/backend/live-search/element/result/result-container.ts @@ -12,21 +12,21 @@ */ import LiveSearchConfigurator from '@typo3/backend/live-search/live-search-configurator'; -import {customElement, property, query} from 'lit/decorators'; -import {html, LitElement, nothing, TemplateResult} from 'lit'; -import {lll} from '@typo3/core/lit-helper'; +import { customElement, property, query } from 'lit/decorators'; +import { html, LitElement, nothing, TemplateResult } from 'lit'; +import { lll } from '@typo3/core/lit-helper'; import './item/item-container'; import './result-detail-container'; -import {ResultItemInterface} from './item/item'; -import {ItemContainer} from './item/item-container'; -import {ResultDetailContainer} from './result-detail-container'; +import { ResultItemInterface } from './item/item'; +import { ItemContainer } from './item/item-container'; +import { ResultDetailContainer } from './result-detail-container'; export const componentName = 'typo3-backend-live-search-result-container'; @customElement(componentName) export class ResultContainer extends LitElement { - @property({type: Object}) results: ResultItemInterface[]|null = null; - @property({type: Boolean, attribute: false}) loading: boolean = false; + @property({ type: Object }) results: ResultItemInterface[]|null = null; + @property({ type: Boolean, attribute: false }) loading: boolean = false; @query('typo3-backend-live-search-result-item-container') itemContainer: ItemContainer; @query('typo3-backend-live-search-result-item-detail-container') resultDetailContainer: ResultDetailContainer; diff --git a/Build/Sources/TypeScript/backend/live-search/element/result/result-detail-container.ts b/Build/Sources/TypeScript/backend/live-search/element/result/result-detail-container.ts index d936e1815eab..9bd2cc719202 100644 --- a/Build/Sources/TypeScript/backend/live-search/element/result/result-detail-container.ts +++ b/Build/Sources/TypeScript/backend/live-search/element/result/result-detail-container.ts @@ -11,16 +11,16 @@ * The TYPO3 project - inspiring people to share! */ -import {customElement, property} from 'lit/decorators'; -import {html, LitElement, nothing, TemplateResult} from 'lit'; +import { customElement, property } from 'lit/decorators'; +import { html, LitElement, nothing, TemplateResult } from 'lit'; import './item/action/action-container'; -import {ResultItemInterface} from './item/item'; +import { ResultItemInterface } from './item/item'; export const componentName = 'typo3-backend-live-search-result-item-detail-container'; @customElement(componentName) export class ResultDetailContainer extends LitElement { - @property({type: Object, attribute: false}) resultItem: ResultItemInterface|null = null; + @property({ type: Object, attribute: false }) resultItem: ResultItemInterface|null = null; public createRenderRoot(): HTMLElement | ShadowRoot { // Avoid shadow DOM for Bootstrap CSS to be applied diff --git a/Build/Sources/TypeScript/backend/live-search/element/search-option-item.ts b/Build/Sources/TypeScript/backend/live-search/element/search-option-item.ts index 701fb86835b8..099c3bc5da5e 100644 --- a/Build/Sources/TypeScript/backend/live-search/element/search-option-item.ts +++ b/Build/Sources/TypeScript/backend/live-search/element/search-option-item.ts @@ -11,17 +11,17 @@ * The TYPO3 project - inspiring people to share! */ -import {customElement, property} from 'lit/decorators'; -import {html, LitElement, TemplateResult} from 'lit'; +import { customElement, property } from 'lit/decorators'; +import { html, LitElement, TemplateResult } from 'lit'; import BrowserSession from '@typo3/backend/storage/browser-session'; -import {ifDefined} from 'lit/directives/if-defined'; +import { ifDefined } from 'lit/directives/if-defined'; @customElement('typo3-backend-live-search-option-item') export class SearchOptionItem extends LitElement { - @property({type: Boolean}) active: boolean = false; - @property({type: String}) optionId: string; - @property({type: String}) optionName: string; - @property({type: String}) optionLabel: string; + @property({ type: Boolean }) active: boolean = false; + @property({ type: String }) optionId: string; + @property({ type: String }) optionName: string; + @property({ type: String }) optionLabel: string; private parentContainer: HTMLElement; diff --git a/Build/Sources/TypeScript/backend/live-search/element/show-all.ts b/Build/Sources/TypeScript/backend/live-search/element/show-all.ts index cd36ec1d4e87..d49f25280f0c 100644 --- a/Build/Sources/TypeScript/backend/live-search/element/show-all.ts +++ b/Build/Sources/TypeScript/backend/live-search/element/show-all.ts @@ -11,9 +11,9 @@ * The TYPO3 project - inspiring people to share! */ -import {customElement} from 'lit/decorators'; -import {html, LitElement, TemplateResult} from 'lit'; -import {lll} from '@typo3/core/lit-helper'; +import { customElement } from 'lit/decorators'; +import { html, LitElement, TemplateResult } from 'lit'; +import { lll } from '@typo3/core/lit-helper'; import Modal from '@typo3/backend/modal'; @customElement('typo3-backend-live-search-show-all') diff --git a/Build/Sources/TypeScript/backend/live-search/live-search-shortcut.ts b/Build/Sources/TypeScript/backend/live-search/live-search-shortcut.ts index ff1d9b467cfd..53942d67ffe8 100644 --- a/Build/Sources/TypeScript/backend/live-search/live-search-shortcut.ts +++ b/Build/Sources/TypeScript/backend/live-search/live-search-shortcut.ts @@ -1,9 +1,9 @@ -import {BroadcastMessage} from '@typo3/backend/broadcast-message'; +import { BroadcastMessage } from '@typo3/backend/broadcast-message'; import BroadcastService from '@typo3/backend/broadcast-service'; import RegularEvent from '@typo3/core/event/regular-event'; import Modal from '../modal'; -enum MODIFIER_KEYS { +enum ModifierKeys { META = 'Meta', CTRL = 'Control' } @@ -11,14 +11,14 @@ enum MODIFIER_KEYS { class LiveSearchShortcut { public constructor() { // navigator.platform is deprecated, but https://developer.mozilla.org/en-US/docs/Web/API/User-Agent_Client_Hints_API is experimental for now - const expectedModifierKey = navigator.platform.toLowerCase().startsWith('mac') ? MODIFIER_KEYS.META : MODIFIER_KEYS.CTRL; + const expectedModifierKey = navigator.platform.toLowerCase().startsWith('mac') ? ModifierKeys.META : ModifierKeys.CTRL; new RegularEvent('keydown', (e: KeyboardEvent): void => { if (e.repeat) { return; } - const modifierKeyIsDown = expectedModifierKey === MODIFIER_KEYS.META && e.metaKey || expectedModifierKey === MODIFIER_KEYS.CTRL && e.ctrlKey; + const modifierKeyIsDown = expectedModifierKey === ModifierKeys.META && e.metaKey || expectedModifierKey === ModifierKeys.CTRL && e.ctrlKey; if (modifierKeyIsDown && ['k', 'K'].includes(e.key)) { if (Modal.currentModal) { // A modal window is already active, keep default behavior of browser @@ -32,7 +32,7 @@ class LiveSearchShortcut { 'live-search', 'trigger-open', {} - )) + )); } }).bindTo(document); } diff --git a/Build/Sources/TypeScript/backend/live-search/result-types/default-result-type.ts b/Build/Sources/TypeScript/backend/live-search/result-types/default-result-type.ts index 75df36fee8b5..9219a73bf69b 100644 --- a/Build/Sources/TypeScript/backend/live-search/result-types/default-result-type.ts +++ b/Build/Sources/TypeScript/backend/live-search/result-types/default-result-type.ts @@ -1,15 +1,15 @@ import LiveSearchConfigurator from '@typo3/backend/live-search/live-search-configurator'; -import {ResultItemInterface} from '@typo3/backend/live-search/element/result/item/item'; +import { ResultItemInterface } from '@typo3/backend/live-search/element/result/item/item'; import '@typo3/backend/live-search/element/provider/page-provider-result-item'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import Notification from '@typo3/backend/notification'; export function registerType(type: string) { LiveSearchConfigurator.addInvokeHandler(type, 'switch_backend_user', (resultItem: ResultItemInterface): void => { (new AjaxRequest(TYPO3.settings.ajaxUrls.switch_user)).post({ targetUser: resultItem.extraData.uid, - }).then(async (response: AjaxResponse): Promise<any> => { + }).then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && data.url) { top.window.location.href = data.url; diff --git a/Build/Sources/TypeScript/backend/live-search/result-types/page-result-type.ts b/Build/Sources/TypeScript/backend/live-search/result-types/page-result-type.ts index ff6f6db6416e..f3a90ee7d08b 100644 --- a/Build/Sources/TypeScript/backend/live-search/result-types/page-result-type.ts +++ b/Build/Sources/TypeScript/backend/live-search/result-types/page-result-type.ts @@ -1,6 +1,6 @@ import LiveSearchConfigurator from '@typo3/backend/live-search/live-search-configurator'; -import {html, TemplateResult} from 'lit'; -import {ResultItemActionInterface, ResultItemInterface} from '@typo3/backend/live-search/element/result/item/item'; +import { html, TemplateResult } from 'lit'; +import { ResultItemActionInterface, ResultItemInterface } from '@typo3/backend/live-search/element/result/item/item'; import windowManager from '@typo3/backend/window-manager'; export function registerRenderer(type: string) { diff --git a/Build/Sources/TypeScript/backend/localization.ts b/Build/Sources/TypeScript/backend/localization.ts index 51e014ca47b3..c197eb89b132 100644 --- a/Build/Sources/TypeScript/backend/localization.ts +++ b/Build/Sources/TypeScript/backend/localization.ts @@ -13,8 +13,8 @@ import DocumentService from '@typo3/core/document-service'; import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {SeverityEnum} from './enum/severity'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { SeverityEnum } from './enum/severity'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Icons from './icons'; import Wizard from './wizard'; @@ -166,7 +166,7 @@ class Localization { Wizard.unlockNextStep(); }); - const $languageButtons = $('<div />', {class: 'row'}); + const $languageButtons = $('<div />', { class: 'row' }); for (const languageObject of result) { const id: string = 'language' + languageObject.uid; @@ -178,12 +178,12 @@ class Localization { style: 'display: none;', class: 'btn-check' }); - const $label: JQuery = $('<label />', {class: 'btn btn-default d-block t3js-language-option option', 'for': id}) + const $label: JQuery = $('<label />', { class: 'btn btn-default d-block t3js-language-option option', 'for': id }) .text(' ' + languageObject.title) .prepend(languageObject.flagIcon); $languageButtons.append( - $('<div />', {class: 'col-sm-4'}) + $('<div />', { class: 'col-sm-4' }) .append($input) .append($label), ); @@ -218,16 +218,16 @@ class Localization { } const column = columns[colPos]; - const $row = $('<div />', {class: 'row'}); + const $row = $('<div />', { class: 'row' }); result.records[colPos].forEach((record: SummaryColPosRecord): void => { const label = ' (' + record.uid + ') ' + record.title; this.records.push(record.uid); $row.append( - $('<div />', {'class': 'col-sm-6'}).append( - $('<div />', {'class': 'input-group'}).append( - $('<span />', {'class': 'input-group-addon'}).append( + $('<div />', { 'class': 'col-sm-6' }).append( + $('<div />', { 'class': 'input-group' }).append( + $('<span />', { 'class': 'input-group-addon' }).append( $('<input />', { type: 'checkbox', 'class': 't3js-localization-toggle-record', diff --git a/Build/Sources/TypeScript/backend/login-refresh.ts b/Build/Sources/TypeScript/backend/login-refresh.ts index b51691f762e9..743414950b8b 100644 --- a/Build/Sources/TypeScript/backend/login-refresh.ts +++ b/Build/Sources/TypeScript/backend/login-refresh.ts @@ -12,7 +12,7 @@ */ import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Notification from '@typo3/backend/notification'; @@ -69,7 +69,7 @@ class LoginRefresh { return; } // set interval to 60 seconds - let interval: number = this.intervalTime * 1000; + const interval: number = this.intervalTime * 1000; this.intervalId = setInterval(this.checkActiveSession, interval); } @@ -200,14 +200,14 @@ class LoginRefresh { this.$timeoutModal.find('.modal-header h4').text(TYPO3.lang['mess.login_about_to_expire_title']); this.$timeoutModal.find('.modal-body').append( $('<p />').text(TYPO3.lang['mess.login_about_to_expire']), - $('<div />', {class: 'progress'}).append( + $('<div />', { class: 'progress' }).append( $('<div />', { class: 'progress-bar progress-bar-warning progress-bar-striped progress-bar-animated', role: 'progressbar', 'aria-valuemin': '0', 'aria-valuemax': '100', }).append( - $('<span />', {class: 'visually-hidden'}), + $('<span />', { class: 'visually-hidden' }), ), ), ); @@ -247,7 +247,7 @@ class LoginRefresh { this.$loginForm = this.generateModal(MarkupIdentifiers.loginFormModal); this.$loginForm.addClass('modal-notice'); - let refresh_login_title = String(TYPO3.lang['mess.refresh_login_title']).replace('%s', TYPO3.configuration.username); + const refresh_login_title = String(TYPO3.lang['mess.refresh_login_title']).replace('%s', TYPO3.configuration.username); this.$loginForm.find('.modal-header h4').text(refresh_login_title); this.$loginForm.find('.modal-body').append( $('<p />').text(TYPO3.lang['mess.login_expired']), @@ -257,10 +257,10 @@ class LoginRefresh { action: TYPO3.settings.ajaxUrls.login, }).append( $('<div />').append( - $('<input />', {type: 'text', name: 'username', class: 'd-none', value: TYPO3.configuration.username}), - $('<input />', {type: 'hidden', name: 'userident', id: 't3-loginrefresh-userident'}) + $('<input />', { type: 'text', name: 'username', class: 'd-none', value: TYPO3.configuration.username }), + $('<input />', { type: 'hidden', name: 'userident', id: 't3-loginrefresh-userident' }) ), - $('<div />', {class: 'form-group'}).append( + $('<div />', { class: 'form-group' }).append( $('<input />', { type: 'password', name: 'p_field', @@ -279,7 +279,7 @@ class LoginRefresh { href: this.logoutUrl, class: 'btn btn-default', }).text(TYPO3.lang['mess.refresh_exit_button']), - $('<button />', {type: 'submit', class: 'btn btn-primary', 'data-action': 'refreshSession', form: 'beLoginRefresh'}) + $('<button />', { type: 'submit', class: 'btn btn-primary', 'data-action': 'refreshSession', form: 'beLoginRefresh' }) .text(TYPO3.lang['mess.refresh_login_button']) .on('click', () => { this.$loginForm.find('form').trigger('submit'); @@ -300,13 +300,13 @@ class LoginRefresh { id: identifier, class: 't3js-modal ' + identifier + ' modal modal-type-default modal-severity-notice modal-style-light modal-size-small fade', }).append( - $('<div />', {class: 'modal-dialog'}).append( - $('<div />', {class: 'modal-content'}).append( - $('<div />', {class: 'modal-header'}).append( - $('<h4 />', {class: 'modal-title'}), + $('<div />', { class: 'modal-dialog' }).append( + $('<div />', { class: 'modal-content' }).append( + $('<div />', { class: 'modal-header' }).append( + $('<h4 />', { class: 'modal-title' }), ), - $('<div />', {class: 'modal-body'}), - $('<div />', {class: 'modal-footer'}), + $('<div />', { class: 'modal-body' }), + $('<div />', { class: 'modal-footer' }), ), ), ); @@ -345,7 +345,7 @@ class LoginRefresh { const percentText = (current) + '%'; $progressBar.css('width', percentText); $srText.text(percentText); - }, 300); + }, 300); } /** @@ -376,7 +376,7 @@ class LoginRefresh { const postData: any = { login_status: 'login', }; - for (let field of $form.serializeArray()) { + for (const field of $form.serializeArray()) { postData[field.name] = field.value; } new AjaxRequest($form.attr('action')).post(postData).then(async (response: AjaxResponse): Promise<void> => { @@ -389,7 +389,7 @@ class LoginRefresh { $passwordField.focus(); } }); - } + }; /** * Registers the (shown|hidden).bs.modal events. @@ -444,7 +444,7 @@ class LoginRefresh { } } }); - } + }; private applyOptions(options: LoginRefreshOptions): void { if (options.intervalTime !== undefined) { diff --git a/Build/Sources/TypeScript/backend/login.ts b/Build/Sources/TypeScript/backend/login.ts index be447ed4782a..13374cdd8649 100644 --- a/Build/Sources/TypeScript/backend/login.ts +++ b/Build/Sources/TypeScript/backend/login.ts @@ -14,7 +14,7 @@ import 'bootstrap'; import '@typo3/backend/input/clearable'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import RegularEvent from '@typo3/core/event/regular-event'; interface PreflightResponse { diff --git a/Build/Sources/TypeScript/backend/modal.ts b/Build/Sources/TypeScript/backend/modal.ts index b04c6d67e449..4449a50d4ea7 100644 --- a/Build/Sources/TypeScript/backend/modal.ts +++ b/Build/Sources/TypeScript/backend/modal.ts @@ -11,19 +11,19 @@ * The TYPO3 project - inspiring people to share! */ -import {Modal as BootstrapModal} from 'bootstrap'; -import {html, nothing, LitElement, TemplateResult} from 'lit'; -import {customElement, property, state} from 'lit/decorators'; -import {unsafeHTML} from 'lit/directives/unsafe-html'; -import {classMap, ClassInfo} from 'lit/directives/class-map'; -import {styleMap, StyleInfo} from 'lit/directives/style-map'; -import {ifDefined} from 'lit/directives/if-defined'; -import {classesArrayToClassInfo} from '@typo3/core/lit-helper'; +import { Modal as BootstrapModal } from 'bootstrap'; +import { html, nothing, LitElement, TemplateResult, PropertyValues } from 'lit'; +import { customElement, property, state } from 'lit/decorators'; +import { unsafeHTML } from 'lit/directives/unsafe-html'; +import { classMap, ClassInfo } from 'lit/directives/class-map'; +import { styleMap, StyleInfo } from 'lit/directives/style-map'; +import { ifDefined } from 'lit/directives/if-defined'; +import { classesArrayToClassInfo } from '@typo3/core/lit-helper'; import RegularEvent from '@typo3/core/event/regular-event'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractAction} from './action-button/abstract-action'; -import {ModalResponseEvent} from '@typo3/backend/modal-interface'; -import {SeverityEnum} from './enum/severity'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractAction } from './action-button/abstract-action'; +import { ModalResponseEvent } from '@typo3/backend/modal-interface'; +import { SeverityEnum } from './enum/severity'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Severity from './severity'; import '@typo3/backend/element/icon-element'; @@ -60,11 +60,6 @@ export enum Types { type ModalCallbackFunction = (modal: ModalElement) => void; -export enum PostActionModalBehavior { - KEEP_OPEN, - CLOSE -} - export interface Button { text: string; active?: boolean; @@ -94,16 +89,16 @@ type PartialConfiguration = Partial<Omit<Configuration, 'buttons'> & { buttons: @customElement('typo3-backend-modal') export class ModalElement extends LitElement { - @property({type: String, reflect: true}) modalTitle: string = ''; - @property({type: String, reflect: true}) content: string = ''; - @property({type: String, reflect: true}) type: Types = Types.default; - @property({type: String, reflect: true}) severity: SeverityEnum = SeverityEnum.notice; - @property({type: String, reflect: true}) variant: Styles = Styles.default; - @property({type: String, reflect: true}) size: Sizes = Sizes.default; - @property({type: Number, reflect: true}) zindex: Number = 5000; - @property({type: Boolean}) staticBackdrop: boolean = false; - @property({type: Array}) additionalCssClasses: Array<string> = []; - @property({type: Array, attribute: false}) buttons: Array<Button> = []; + @property({ type: String, reflect: true }) modalTitle: string = ''; + @property({ type: String, reflect: true }) content: string = ''; + @property({ type: String, reflect: true }) type: Types = Types.default; + @property({ type: String, reflect: true }) severity: SeverityEnum = SeverityEnum.notice; + @property({ type: String, reflect: true }) variant: Styles = Styles.default; + @property({ type: String, reflect: true }) size: Sizes = Sizes.default; + @property({ type: Number, reflect: true }) zindex: number = 5000; + @property({ type: Boolean }) staticBackdrop: boolean = false; + @property({ type: Array }) additionalCssClasses: Array<string> = []; + @property({ type: Array, attribute: false }) buttons: Array<Button> = []; @state() templateResultContent: TemplateResult | JQuery | Element | DocumentFragment = null; @state() activeButton: Button = null; @@ -111,6 +106,7 @@ export class ModalElement extends LitElement { public bootstrapModal: BootstrapModal = null; public callback: ModalCallbackFunction = null; public ajaxCallback: ModalCallbackFunction = null; + public userData: { [key: string]: any } = {}; public setContent(content: TemplateResult | JQuery | Element | DocumentFragment): void { @@ -136,9 +132,9 @@ export class ModalElement extends LitElement { } } - protected updated(changedProperties: Map<string, any>) { + protected updated(changedProperties: PropertyValues) { if (changedProperties.has('templateResultContent')) { - this.dispatchEvent(new CustomEvent('modal-updated', {bubbles: true})); + this.dispatchEvent(new CustomEvent('modal-updated', { bubbles: true })); } } @@ -191,23 +187,11 @@ export class ModalElement extends LitElement { const buttonElement = event.currentTarget as HTMLButtonElement; if (button.action) { this.activeButton = button; - button.action.execute(buttonElement).then((postActionBehavior: PostActionModalBehavior = PostActionModalBehavior.CLOSE): void => { - this.activeButton = null; - - // Safety-net if 3rd party code doesn't provide a valid PostActionModalBehavior - const isValidEnumValue = Object.values(PostActionModalBehavior).includes(postActionBehavior as unknown as string); - if (!isValidEnumValue) { - console.warn(`postActionBehavior ${postActionBehavior} provided but expected any of ${Object.values(PostActionModalBehavior).join(',')}. Falling back to PostActionModalBehavior.CLOSE`); - postActionBehavior = PostActionModalBehavior.CLOSE; - } - if (postActionBehavior === PostActionModalBehavior.CLOSE) { - this.bootstrapModal.hide(); - } - }); + button.action.execute(buttonElement).then((): void => this.bootstrapModal.hide()); } else if (button.trigger) { button.trigger(event, this); } - buttonElement.dispatchEvent(new CustomEvent('button.clicked', {bubbles: true})); + buttonElement.dispatchEvent(new CustomEvent('button.clicked', { bubbles: true })); } private renderAjaxBody(): TemplateResult { @@ -280,7 +264,7 @@ export class ModalElement extends LitElement { } private trigger(event: string): void { - this.dispatchEvent(new CustomEvent(event, {bubbles: true, composed: true})); + this.dispatchEvent(new CustomEvent(event, { bubbles: true, composed: true })); } } @@ -290,9 +274,9 @@ export class ModalElement extends LitElement { */ class Modal { // @todo: drop? available as named exports - public readonly sizes: any = Sizes; - public readonly styles: any = Styles; - public readonly types: any = Types; + public readonly sizes: typeof Sizes = Sizes; + public readonly styles: typeof Styles = Styles; + public readonly types: typeof Types = Types; // @todo: currentModal could be a getter method for the last element in this.instances public currentModal: ModalElement = null; @@ -312,6 +296,10 @@ class Modal { staticBackdrop: false }; + constructor() { + this.initializeMarkupTrigger(document); + } + private static createModalResponseEventFromElement(element: HTMLElement, result: boolean): ModalResponseEvent | null { if (!element.dataset.eventName) { return null; @@ -323,10 +311,6 @@ class Modal { }); } - constructor() { - this.initializeMarkupTrigger(document); - } - /** * Close the current open modal */ @@ -384,9 +368,9 @@ class Modal { modal.addEventListener('button.clicked', (e: Event): void => { const button = e.target as HTMLButtonElement; if (button.getAttribute('name') === 'cancel') { - button.dispatchEvent(new CustomEvent('confirm.button.cancel', {bubbles: true})); + button.dispatchEvent(new CustomEvent('confirm.button.cancel', { bubbles: true })); } else if (button.getAttribute('name') === 'ok') { - button.dispatchEvent(new CustomEvent('confirm.button.ok', {bubbles: true})); + button.dispatchEvent(new CustomEvent('confirm.button.ok', { bubbles: true })); } }); @@ -493,20 +477,20 @@ class Modal { * * @param {HTMLDocument} theDocument */ - private initializeMarkupTrigger(theDocument: HTMLDocument): void { + private initializeMarkupTrigger(theDocument: Document): void { const modalTrigger = (evt: Event, triggerElement: HTMLElement): void => { evt.preventDefault(); const content = triggerElement.dataset.bsContent || TYPO3.lang['message.confirmation'] || 'Are you sure?'; let severity = SeverityEnum.info; - if (<any>triggerElement.dataset.severity in SeverityEnum) { - const severityKey: keyof typeof SeverityEnum = <any>triggerElement.dataset.severity; + if (triggerElement.dataset.severity in SeverityEnum) { + const severityKey = triggerElement.dataset.severity as keyof typeof SeverityEnum; severity = SeverityEnum[severityKey]; } let url = triggerElement.dataset.url || null; if (url !== null) { const separator = url.includes('?') ? '&' : '?'; const params = new URLSearchParams(triggerElement.dataset).toString(); - url = url + separator + params + url = url + separator + params; } this.advanced({ type: url !== null ? Types.ajax : Types.default, @@ -536,7 +520,7 @@ class Modal { if (event !== null) { triggerElement.dispatchEvent(event); } - let targetLocation = triggerElement.dataset.uri || triggerElement.dataset.href || triggerElement.getAttribute('href'); + const targetLocation = triggerElement.dataset.uri || triggerElement.dataset.href || triggerElement.getAttribute('href'); if (targetLocation && targetLocation !== '#') { triggerElement.ownerDocument.location.href = targetLocation; } @@ -613,7 +597,7 @@ class Modal { } }); - currentModal.addEventListener('typo3-modal-hidden', (e: Event): void => { + currentModal.addEventListener('typo3-modal-hidden', (): void => { currentModal.remove(); // Keep class modal-open on body tag as long as open modals exist if (this.instances.length > 0) { diff --git a/Build/Sources/TypeScript/backend/module-menu.ts b/Build/Sources/TypeScript/backend/module-menu.ts index 3501b8ee65ac..87080f62dd66 100644 --- a/Build/Sources/TypeScript/backend/module-menu.ts +++ b/Build/Sources/TypeScript/backend/module-menu.ts @@ -45,6 +45,12 @@ interface ModuleMenuItem { class ModuleMenu { private loadedModule: string = null; + constructor() { + // @todo: DocumentService.ready() doesn't work here as it apparently is too fast or whatever. + // It keeps breaking acceptance tests. Bonkers. + $((): void => this.initialize()); + } + private static getModuleMenuItemFromElement(element: HTMLElement): ModuleMenuItem { const item: ModuleMenuItem = { identifier: element.dataset.modulemenuIdentifier, @@ -52,7 +58,7 @@ class ModuleMenu { collapsible: element.dataset.modulemenuCollapsible === 'true', expanded: element.attributes.getNamedItem('aria-expanded')?.value === 'true', element: element, - } + }; return item; } @@ -218,7 +224,7 @@ class ModuleMenu { } private static getPreviousItem(item: HTMLElement): HTMLElement { - let previousParent = item.parentElement.previousElementSibling; // previous <li> + const previousParent = item.parentElement.previousElementSibling; // previous <li> if (previousParent === null) { return ModuleMenu.getLastItem(item); } @@ -226,7 +232,7 @@ class ModuleMenu { } private static getNextItem(item: HTMLElement): HTMLElement { - let nextParent = item.parentElement.nextElementSibling; // next <li> + const nextParent = item.parentElement.nextElementSibling; // next <li> if (nextParent === null) { return ModuleMenu.getFirstItem(item); } @@ -253,12 +259,6 @@ class ModuleMenu { return item.nextElementSibling.firstElementChild.firstElementChild as HTMLElement; } - constructor() { - // @todo: DocumentService.ready() doesn't work here as it apparently is too fast or whatever. - // It keeps breaking acceptance tests. Bonkers. - $((): void => this.initialize()); - } - /** * Refresh the HTML by fetching the menu again */ @@ -309,7 +309,7 @@ class ModuleMenu { return; } - let deferred = $.Deferred(); + const deferred = $.Deferred(); deferred.resolve(); deferred.then((): void => { @@ -318,7 +318,7 @@ class ModuleMenu { // Only initialize top bar events when top bar exists. // E.g. install tool has no top bar if (document.querySelector('.t3js-scaffold-toolbar')) { - this.initializeTopBarEvents() + this.initializeTopBarEvents(); } }); }); diff --git a/Build/Sources/TypeScript/backend/module/iframe.ts b/Build/Sources/TypeScript/backend/module/iframe.ts index 3dec8c9d8669..134b72574ef2 100644 --- a/Build/Sources/TypeScript/backend/module/iframe.ts +++ b/Build/Sources/TypeScript/backend/module/iframe.ts @@ -11,10 +11,10 @@ * The TYPO3 project - inspiring people to share! */ -import {html, css, LitElement, TemplateResult, nothing} from 'lit'; -import {customElement, property, query} from 'lit/decorators'; -import {ModuleState} from '../module'; -import {lll} from '@typo3/core/lit-helper'; +import { html, LitElement, TemplateResult, nothing } from 'lit'; +import { customElement, property, query } from 'lit/decorators'; +import { ModuleState } from '../module'; +import { lll } from '@typo3/core/lit-helper'; /** * Module: @typo3/backend/module/iframe @@ -24,7 +24,7 @@ export const componentName = 'typo3-iframe-module'; @customElement(componentName) export class IframeModuleElement extends LitElement { - @property({type: String}) endpoint: string = ''; + @property({ type: String }) endpoint: string = ''; @query('iframe', true) iframe: HTMLIFrameElement; @@ -71,7 +71,7 @@ export class IframeModuleElement extends LitElement { private registerUnloadHandler(iframe: HTMLIFrameElement): void { try { - iframe.contentWindow.addEventListener('unload', (e: Event) => this._unload(e, iframe), { once: true}); + iframe.contentWindow.addEventListener('unload', (e: Event) => this._unload(e, iframe), { once: true }); } catch (e) { console.error('Failed to access contentWindow of module iframe – using a foreign origin?'); throw e; @@ -91,7 +91,7 @@ export class IframeModuleElement extends LitElement { } } - private _loaded({target}: Event) { + private _loaded({ target }: Event) { const iframe = <HTMLIFrameElement> target; // The event handler for the "unload" event needs to be attached diff --git a/Build/Sources/TypeScript/backend/module/router.ts b/Build/Sources/TypeScript/backend/module/router.ts index b24443b43e65..ee0d1628212a 100644 --- a/Build/Sources/TypeScript/backend/module/router.ts +++ b/Build/Sources/TypeScript/backend/module/router.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import { html, css, LitElement, TemplateResult } from 'lit'; +import { html, css, HasChanged, LitElement, TemplateResult } from 'lit'; import { customElement, property, query } from 'lit/decorators'; import { ModuleState, ModuleUtility } from '@typo3/backend/module'; @@ -24,26 +24,13 @@ interface DecoratedModuleState { // Trigger a render cycle, even if property has been reset to // the current value (this is to trigger a module refresh). -const alwaysUpdate = (newVal: string, oldVal: string) => true; +const alwaysUpdate: HasChanged = () => true; /** * Module: @typo3/backend/module/router */ @customElement('typo3-backend-module-router') export class ModuleRouter extends LitElement { - - @property({ type: String, hasChanged: alwaysUpdate }) module: string = ''; - - @property({ type: String, hasChanged: alwaysUpdate }) endpoint: string = ''; - - @property({ type: String, attribute: 'state-tracker' }) stateTrackerUrl: string; - - @property({ type: String, attribute: 'sitename' }) sitename: string; - - @property({ type: Boolean, attribute: 'sitename-first' }) sitenameFirst: boolean; - - @query('slot', true) slotElement: HTMLSlotElement; - public static styles = css` :host { width: 100%; @@ -58,6 +45,13 @@ export class ModuleRouter extends LitElement { } `; + @property({ type: String, hasChanged: alwaysUpdate }) module: string = ''; + @property({ type: String, hasChanged: alwaysUpdate }) endpoint: string = ''; + @property({ type: String, attribute: 'state-tracker' }) stateTrackerUrl: string; + @property({ type: String, attribute: 'sitename' }) sitename: string; + @property({ type: Boolean, attribute: 'sitename-first' }) sitenameFirst: boolean; + @query('slot', true) slotElement: HTMLSlotElement; + constructor() { super(); @@ -92,7 +86,7 @@ export class ModuleRouter extends LitElement { // The "name" attribute of <slot> gets of out sync // due to browser history backwards or forward navigation. // Synchronize to the state as advertised by the iframe event. - this.slotElement.setAttribute('name', state.slotName) + this.slotElement.setAttribute('name', state.slotName); } // Mark active and sync endpoint attribute for modules. @@ -167,7 +161,7 @@ export class ModuleRouter extends LitElement { // @todo: Check if .componentName exists element = document.createElement(module.componentName); } catch (e) { - console.error({ msg: `Error importing ${moduleName} as backend module`, err: e }) + console.error({ msg: `Error importing ${moduleName} as backend module`, err: e }); throw e; } diff --git a/Build/Sources/TypeScript/backend/multi-record-selection.ts b/Build/Sources/TypeScript/backend/multi-record-selection.ts index 903482698f76..32e8ef613c85 100644 --- a/Build/Sources/TypeScript/backend/multi-record-selection.ts +++ b/Build/Sources/TypeScript/backend/multi-record-selection.ts @@ -14,7 +14,7 @@ import Notification from '@typo3/backend/notification'; import DocumentService from '@typo3/core/document-service'; import RegularEvent from '@typo3/core/event/regular-event'; -import {ActionConfiguration, ActionEventDetails} from '@typo3/backend/multi-record-selection-action'; +import { ActionConfiguration, ActionEventDetails } from '@typo3/backend/multi-record-selection-action'; export enum MultiRecordSelectionSelectors { actionsSelector = '.t3js-multi-record-selection-actions', @@ -49,12 +49,26 @@ class MultiRecordSelection { static disabledClass: string = 'disabled'; private lastChecked: HTMLInputElement = null; + constructor() { + DocumentService.ready().then((): void => { + MultiRecordSelection.restoreTemporaryState(); + this.registerActions(); + this.registerActionsEventHandlers(); + this.registerCheckboxActions(); + this.registerCheckboxKeyboardActions(); + this.registerCheckboxTableRowSelectionAction(); + this.registerToggleCheckboxActions(); + this.registerDispatchCheckboxStateChangedEvent(); + this.registerCheckboxStateChangedEventHandler(); + }); + } + private static getCheckboxes(state: CheckboxState = CheckboxState.any, identifier: string = ''): NodeListOf<HTMLInputElement> { return document.querySelectorAll(MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.checkboxSelector + state, identifier)); } private static getCombinedSelector(selector: string, identifier: string): string { - return identifier !== '' ? ['[data-multi-record-selection-identifier="' + identifier + '"]', selector].join (' ') : selector; + return identifier !== '' ? ['[data-multi-record-selection-identifier="' + identifier + '"]', selector].join (' ') : selector; } private static getIdentifier(element: HTMLElement): string { @@ -88,7 +102,7 @@ class MultiRecordSelection { // perform this for every checked checkbox. Therefore we store the identifiers, // which were already evaluated and do not call the evaluation for them again. let actionsToggled: boolean = false; - let identifiers: Array<string> = []; + const identifiers: Array<string> = []; checked.forEach((checkbox: HTMLInputElement) => { (checkbox.closest(MultiRecordSelectionSelectors.elementSelector) as HTMLElement)?.classList.add(MultiRecordSelection.activeClass); const identifier: string = MultiRecordSelection.getIdentifier(checkbox); @@ -150,7 +164,7 @@ class MultiRecordSelection { action.classList.add(this.disabledClass); // Get all currently checked elements const checked: NodeListOf<HTMLInputElement> = MultiRecordSelection.getCheckboxes(CheckboxState.checked, identifier); - for (let i=0; i < checked.length; i++) { + for (let i = 0; i < checked.length; i++) { // Evaluate each checked element if it contains the specified idField if ((checked[i].closest(MultiRecordSelectionSelectors.elementSelector) as HTMLElement)?.dataset[configuration.idField]) { // If a checked element contains the idField, remove the "disabled" @@ -175,12 +189,12 @@ class MultiRecordSelection { const panelElements: HTMLCollection = container.closest('.multi-record-selection-panel')?.children; if (visible) { if (panelElements) { - for (let i=0; i < panelElements.length; i++) { panelElements[i].classList.add('hidden') } + for (let i = 0; i < panelElements.length; i++) { panelElements[i].classList.add('hidden'); } } container.classList.remove('hidden'); } else { if (panelElements) { - for (let i=0; i < panelElements.length; i++) { panelElements[i].classList.remove('hidden') } + for (let i = 0; i < panelElements.length; i++) { panelElements[i].classList.remove('hidden'); } } container.classList.add('hidden'); } @@ -201,20 +215,6 @@ class MultiRecordSelection { }); } - constructor() { - DocumentService.ready().then((): void => { - MultiRecordSelection.restoreTemporaryState(); - this.registerActions(); - this.registerActionsEventHandlers(); - this.registerCheckboxActions(); - this.registerCheckboxKeyboardActions(); - this.registerCheckboxTableRowSelectionAction(); - this.registerToggleCheckboxActions(); - this.registerDispatchCheckboxStateChangedEvent(); - this.registerCheckboxStateChangedEventHandler(); - }); - } - private registerActions(): void { new RegularEvent('click', (e: Event, target: HTMLButtonElement): void => { if (!target.dataset.multiRecordSelectionAction) { @@ -335,7 +335,7 @@ class MultiRecordSelection { // should be performed as well. We also prevent the keyboard actions from unsetting // any state, e.g. the "manually changed flag", as this might have been set by any // component triggered by the above checkbox state change operation. - this.handleCheckboxKeyboardActions(e, checkbox, false) + this.handleCheckboxKeyboardActions(e, checkbox, false); }).delegateTo(document, MultiRecordSelectionSelectors.elementSelector); // In case row selection is enabled and a keyboard "shortcut" is used, prevent text selection on the rows @@ -377,7 +377,7 @@ class MultiRecordSelection { ].join(' ')); if (checkAll !== null) { - checkAll.classList.toggle('disabled', !MultiRecordSelection.getCheckboxes(CheckboxState.unchecked, identifier).length) + checkAll.classList.toggle('disabled', !MultiRecordSelection.getCheckboxes(CheckboxState.unchecked, identifier).length); } const checkNone: HTMLButtonElement = document.querySelector([ @@ -451,7 +451,7 @@ class MultiRecordSelection { if (checkbox !== target) { MultiRecordSelection.changeCheckboxState(checkbox, !checkbox.checked); } - }) + }); } // To prevent possible side effects we simply clean up and unset the attribute here again diff --git a/Build/Sources/TypeScript/backend/multi-step-wizard.ts b/Build/Sources/TypeScript/backend/multi-step-wizard.ts index bb0acf68a1e1..344ec0610f49 100644 --- a/Build/Sources/TypeScript/backend/multi-step-wizard.ts +++ b/Build/Sources/TypeScript/backend/multi-step-wizard.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {SeverityEnum} from './enum/severity'; +import { SeverityEnum } from './enum/severity'; import $ from 'jquery'; import Modal from './modal'; import Severity from './severity'; @@ -112,7 +112,7 @@ class MultiStepWizard { } return Icons.getIcon('spinner-circle', Icons.sizes.default, null, null).then((markup: string) => { - let $processingSlide = $('<div />', {class: 'text-center'}).append(markup); + const $processingSlide = $('<div />', { class: 'text-center' }).append(markup); this.addSlide( 'final-processing-slide', top.TYPO3.lang['wizard.processing.title'], $processingSlide[0].outerHTML, @@ -127,10 +127,10 @@ class MultiStepWizard { * Create wizard with modal, buttons, progress bar and carousel */ public show(): void { - let $slides = this.generateSlides(); - let firstSlide = this.setup.slides[0]; + const $slides = this.generateSlides(); + const firstSlide = this.setup.slides[0]; - const modal = Modal.advanced({ + Modal.advanced({ title: firstSlide.title, content: $slides, severity: firstSlide.severity, @@ -153,7 +153,7 @@ class MultiStepWizard { name: 'next', }], additionalCssClasses: ['modal-multi-step-wizard'], - callback: (): void => { + callback: (): void => { this.addButtonContainer(); this.addProgressBar(); this.initializeEvents(); @@ -190,7 +190,7 @@ class MultiStepWizard { * @returns {JQuery} */ public lockNextStep(): JQuery { - let $button = this.setup.$carousel.closest('.modal').find('button[name="next"]'); + const $button = this.setup.$carousel.closest('.modal').find('button[name="next"]'); $button.prop('disabled', true); return $button; } @@ -201,7 +201,7 @@ class MultiStepWizard { * @returns {JQuery} */ public unlockNextStep(): JQuery { - let $button = this.setup.$carousel.closest('.modal').find('button[name="next"]'); + const $button = this.setup.$carousel.closest('.modal').find('button[name="next"]'); $button.prop('disabled', false); return $button; } @@ -212,7 +212,7 @@ class MultiStepWizard { * @returns {JQuery} */ public lockPrevStep(): JQuery { - let $button = this.setup.$carousel.closest('.modal').find('button[name="prev"]'); + const $button = this.setup.$carousel.closest('.modal').find('button[name="prev"]'); $button.prop('disabled', true); return $button; } @@ -223,7 +223,7 @@ class MultiStepWizard { * @returns {JQuery} */ public unlockPrevStep(): JQuery { - let $button = this.setup.$carousel.closest('.modal').find('button[name="prev"]'); + const $button = this.setup.$carousel.closest('.modal').find('button[name="prev"]'); $button.prop('disabled', false); return $button; } @@ -235,7 +235,7 @@ class MultiStepWizard { * @returns {JQuery} */ public triggerStepButton(direction: string): JQuery { - let $button = this.setup.$carousel.closest('.modal').find('button[name="' + direction + '"]'); + const $button = this.setup.$carousel.closest('.modal').find('button[name="' + direction + '"]'); if ($button.length > 0 && $button.prop('disabled') !== true) { $button.trigger('click'); } @@ -248,7 +248,7 @@ class MultiStepWizard { * @returns {JQuery} */ public blurCancelStep(): JQuery { - let $button = this.setup.$carousel.closest('.modal').find('button[name="cancel"]'); + const $button = this.setup.$carousel.closest('.modal').find('button[name="cancel"]'); $button.trigger('blur'); return $button; } @@ -259,7 +259,7 @@ class MultiStepWizard { * @private */ private initializeEvents(): void { - let $modal = this.setup.$carousel.closest('.modal'); + const $modal = this.setup.$carousel.closest('.modal'); this.initializeSlideNextEvent($modal); this.initializeSlidePrevEvent($modal); @@ -273,8 +273,8 @@ class MultiStepWizard { }) // Event is fired when the carousel has completed its slide transition .on('slid.bs.carousel', (evt: JQueryEventObject): void => { - let currentIndex = this.setup.$carousel.data('currentIndex'); - let slide = this.setup.slides[currentIndex]; + const currentIndex = this.setup.$carousel.data('currentIndex'); + const slide = this.setup.slides[currentIndex]; this.runSlideCallback(slide, $(evt.relatedTarget)); @@ -284,7 +284,7 @@ class MultiStepWizard { }); // Custom event, closes the wizard - let cmp = this.getComponent(); + const cmp = this.getComponent(); cmp.on('wizard-dismiss', this.dismiss); Modal.currentModal.addEventListener('typo3-modal-hidden', (): void => { @@ -296,16 +296,16 @@ class MultiStepWizard { } private initializeSlideNextEvent($modal: JQuery) { - let $modalFooter = $modal.find('.modal-footer'); - let $nextButton = $modalFooter.find('button[name="next"]'); + const $modalFooter = $modal.find('.modal-footer'); + const $nextButton = $modalFooter.find('button[name="next"]'); $nextButton.off().on('click', (): void => { this.setup.$carousel.carousel('next'); }); } private initializeSlidePrevEvent($modal: JQuery) { - let $modalFooter = $modal.find('.modal-footer'); - let $prevButton = $modalFooter.find('button[name="prev"]'); + const $modalFooter = $modal.find('.modal-footer'); + const $prevButton = $modalFooter.find('button[name="prev"]'); $prevButton.off().on('click', (): void => { this.setup.$carousel.carousel('prev'); }); @@ -453,13 +453,11 @@ class MultiStepWizard { * @private */ private addProgressBar(): void { - let realSlideCount = this.setup.$carousel.find('.carousel-item').length; - let slideCount = Math.max(1, realSlideCount); - let initialStep; - let $modal = this.setup.$carousel.closest('.modal'); - let $modalFooter = $modal.find('.modal-footer'); - - initialStep = Math.round(100 / slideCount); + const realSlideCount = this.setup.$carousel.find('.carousel-item').length; + const slideCount = Math.max(1, realSlideCount); + const initialStep = Math.round(100 / slideCount); + const $modal = this.setup.$carousel.closest('.modal'); + const $modalFooter = $modal.find('.modal-footer'); this.setup.$carousel .data('initialStep', initialStep) @@ -470,7 +468,7 @@ class MultiStepWizard { // Append progress bar to modal footer if (slideCount > 1) { - $modalFooter.prepend($('<div />', {class: 'progress'})); + $modalFooter.prepend($('<div />', { class: 'progress' })); for (let i = 0; i < this.setup.slides.length; ++i) { let classes; if (i === 0) { @@ -502,8 +500,8 @@ class MultiStepWizard { * @private */ private addButtonContainer(): void { - let $modal = this.setup.$carousel.closest('.modal'); - let $modalFooterButtons = $modal.find('.modal-footer .btn'); + const $modal = this.setup.$carousel.closest('.modal'); + const $modalFooterButtons = $modal.find('.modal-footer .btn'); $modalFooterButtons.wrapAll('<div class="modal-btn-group" />'); } @@ -524,7 +522,7 @@ class MultiStepWizard { + '<div class="carousel-inner" role="listbox">'; for (let i = 0; i < this.setup.slides.length; ++i) { - let currentSlide: Slide = this.setup.slides[i]; + const currentSlide: Slide = this.setup.slides[i]; let slideContent = currentSlide.content; if (typeof slideContent === 'object') { diff --git a/Build/Sources/TypeScript/backend/new-content-element-wizard-button.ts b/Build/Sources/TypeScript/backend/new-content-element-wizard-button.ts index 6f64ac13de4f..6e32689831a7 100644 --- a/Build/Sources/TypeScript/backend/new-content-element-wizard-button.ts +++ b/Build/Sources/TypeScript/backend/new-content-element-wizard-button.ts @@ -40,7 +40,7 @@ export class NewContentElementWizardButton extends LitElement { e.preventDefault(); this.renderWizard(); } - }) + }); } public connectedCallback(): void { diff --git a/Build/Sources/TypeScript/backend/new-content-element-wizard.ts b/Build/Sources/TypeScript/backend/new-content-element-wizard.ts index 9ec8d9ff74e4..c47b60a1b9fd 100644 --- a/Build/Sources/TypeScript/backend/new-content-element-wizard.ts +++ b/Build/Sources/TypeScript/backend/new-content-element-wizard.ts @@ -25,6 +25,17 @@ import RegularEvent from '@typo3/core/event/regular-event'; class Item { public visible: boolean = true; + public constructor( + public readonly identifier: string, + public readonly label: string, + public readonly description: string, + public readonly icon: string, + public readonly url: string, + public readonly requestType: string, + public readonly defaultValues: Array<any>, + public readonly saveAndClose: boolean + ) { } + public static fromData(data: DataItemInterface) { return new Item( data.identifier, @@ -37,16 +48,6 @@ class Item { data.saveAndClose ?? false, ); } - public constructor( - public readonly identifier: string, - public readonly label: string, - public readonly description: string, - public readonly icon: string, - public readonly url: string, - public readonly requestType: string, - public readonly defaultValues: Array<any>, - public readonly saveAndClose: boolean - ) { } public reset(): void { @@ -57,6 +58,12 @@ class Item { class Category { public disabled: boolean = false; + public constructor( + public readonly identifier: string, + public readonly label: string, + public readonly items: Item[], + ) { } + public static fromData(data: DataCategoryInterface) { return new Category( data.identifier, @@ -64,11 +71,6 @@ class Category { data.items.map((item: DataItemInterface) => Item.fromData(item)) ); } - public constructor( - public readonly identifier: string, - public readonly label: string, - public readonly items: Item[], - ) { } public reset(): void { @@ -82,14 +84,15 @@ class Category { } class Categories { + public constructor( + public readonly items: Category[], + ) { } + public static fromData(data: DataCategoriesInterface) { return new Categories( Object.values(data).map((item: DataCategoryInterface) => Category.fromData(item)) ); } - public constructor( - public readonly items: Category[], - ) { } public reset(): void { @@ -339,7 +342,7 @@ export class NewContentElementWizard extends LitElement { protected firstUpdated(): void { // Load shared css file - let link = document.createElement('link'); + const link = document.createElement('link'); link.setAttribute('rel', 'stylesheet'); link.setAttribute('href', TYPO3.settings.cssUrls.backend); this.shadowRoot.appendChild(link); @@ -431,7 +434,7 @@ export class NewContentElementWizard extends LitElement { return html` <button class="navigation-toggle btn btn-light" - @click="${() => { this.toggleMenu = !this.toggleMenu }}" + @click="${() => { this.toggleMenu = !this.toggleMenu; }}" > ${this.selectedCategory.label} <typo3-backend-icon identifier="actions-chevron-${(this.toggleMenu === true) ? 'up' : 'down'}" size="small"></typo3-backend-icon> @@ -514,7 +517,7 @@ export class NewContentElementWizard extends LitElement { (new AjaxRequest(item.url)).post({ defVals: item.defaultValues, saveAndClose: item.saveAndClose ? '1' : '0' - }).then(async (response: AjaxResponse): Promise<any> => { + }).then(async (response: AjaxResponse): Promise<void> => { const result = document.createRange().createContextualFragment(await response.resolve()); // Handle buttons with data-target diff --git a/Build/Sources/TypeScript/backend/new-multiple-pages.ts b/Build/Sources/TypeScript/backend/new-multiple-pages.ts index 32a40f9a8ecd..afb71f4ba00e 100644 --- a/Build/Sources/TypeScript/backend/new-multiple-pages.ts +++ b/Build/Sources/TypeScript/backend/new-multiple-pages.ts @@ -73,7 +73,7 @@ class NewMultiplePages { } private actOnTypeSelectChange(this: HTMLSelectElement): void { - for (let option of this.options) { + for (const option of this.options) { option.removeAttribute('selected'); } const optionElement: HTMLOptionElement = this.options[this.selectedIndex]; diff --git a/Build/Sources/TypeScript/backend/notification.ts b/Build/Sources/TypeScript/backend/notification.ts index afc4484cbef7..8258851a4832 100644 --- a/Build/Sources/TypeScript/backend/notification.ts +++ b/Build/Sources/TypeScript/backend/notification.ts @@ -11,12 +11,12 @@ * The TYPO3 project - inspiring people to share! */ -import {LitElement, html} from 'lit'; -import {customElement, property, state} from 'lit/decorators'; -import {classMap} from 'lit/directives/class-map'; -import {ifDefined} from 'lit/directives/if-defined'; -import {AbstractAction} from './action-button/abstract-action'; -import {SeverityEnum} from './enum/severity'; +import { LitElement, html } from 'lit'; +import { customElement, property, state } from 'lit/decorators'; +import { classMap } from 'lit/directives/class-map'; +import { ifDefined } from 'lit/directives/if-defined'; +import { AbstractAction } from './action-button/abstract-action'; +import { SeverityEnum } from './enum/severity'; import Severity from './severity'; import '@typo3/backend/element/icon-element'; @@ -135,9 +135,9 @@ class NotificationMessage extends LitElement { @property() notificationId: string; @property() title: string; @property() message: string; - @property({type: Number}) severity: SeverityEnum = SeverityEnum.info; + @property({ type: Number }) severity: SeverityEnum = SeverityEnum.info; @property() duration: number = 0; - @property({type: Array, attribute: false}) actions: Array<Action> = []; + @property({ type: Array, attribute: false }) actions: Array<Action> = []; @state() visible: boolean = false; @state() executingAction: number = -1; @@ -207,7 +207,7 @@ class NotificationMessage extends LitElement { id="${ifDefined(this.notificationId || undefined)}" class="${'alert alert-' + className + ' alert-dismissible fade' + (this.visible ? ' in' : '')}" role="alert"> - <button type="button" class="close" @click="${async (e: Event) => this.close()}"> + <button type="button" class="close" @click="${async () => this.close()}"> <span aria-hidden="true"><typo3-backend-icon identifier="actions-close" size="small"></typo3-backend-icon></span> <span class="visually-hidden">Close</span> </button> @@ -227,12 +227,12 @@ class NotificationMessage extends LitElement { ${this.actions.map((action, index) => html` <a href="#" title="${action.label}" - @click="${async (e: any) => { - e.preventDefault() + @click="${async (event: PointerEvent) => { + event.preventDefault(); this.executingAction = index; await this.updateComplete; if ('action' in action) { - await action.action.execute(e.currentTarget); + await action.action.execute(event.currentTarget as HTMLAnchorElement); } this.close(); }}" @@ -250,7 +250,7 @@ class NotificationMessage extends LitElement { } } -let notificationObject: any; +let notificationObject: typeof Notification; try { // fetch from parent diff --git a/Build/Sources/TypeScript/backend/online-media.ts b/Build/Sources/TypeScript/backend/online-media.ts index 26c70c956fd5..100a0689ba49 100644 --- a/Build/Sources/TypeScript/backend/online-media.ts +++ b/Build/Sources/TypeScript/backend/online-media.ts @@ -13,9 +13,9 @@ import DocumentService from '@typo3/core/document-service'; import $ from 'jquery'; -import {MessageUtility} from '@typo3/backend/utility/message-utility'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {KeyTypesEnum} from './enum/key-types'; +import { MessageUtility } from '@typo3/backend/utility/message-utility'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { KeyTypesEnum } from './enum/key-types'; import NProgress from 'nprogress'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import SecurityUtility from '@typo3/core/security-utility'; @@ -82,7 +82,7 @@ class OnlineMedia { name: 'ok', active: true, }], - ) + ); modal.addEventListener('confirm.button.ok', (): void => { modal.hideModal(); }); diff --git a/Build/Sources/TypeScript/backend/page-actions.ts b/Build/Sources/TypeScript/backend/page-actions.ts index 7e06794e239a..61b6f8ba3524 100644 --- a/Build/Sources/TypeScript/backend/page-actions.ts +++ b/Build/Sources/TypeScript/backend/page-actions.ts @@ -49,7 +49,7 @@ class PageActions { me.hidden = true; me.insertAdjacentElement('afterend', spinner); - for (let hiddenElement of hiddenElements) { + for (const hiddenElement of hiddenElements) { hiddenElement.style.display = 'block'; const scrollHeight = hiddenElement.scrollHeight; hiddenElement.style.display = ''; diff --git a/Build/Sources/TypeScript/backend/page-link-handler.ts b/Build/Sources/TypeScript/backend/page-link-handler.ts index 4c294872bd69..6991ab911d48 100644 --- a/Build/Sources/TypeScript/backend/page-link-handler.ts +++ b/Build/Sources/TypeScript/backend/page-link-handler.ts @@ -27,13 +27,13 @@ class PageLinkHandler { }).delegateTo(document, 'a.t3js-pageLink'); // Link to current page - new RegularEvent('click', (evt: MouseEvent, targetEl: HTMLElement): void => { + new RegularEvent('click', (evt: MouseEvent): void => { evt.preventDefault(); LinkBrowser.finalizeFunction(document.body.dataset.currentLink); }).delegateTo(document, 'input.t3js-linkCurrent'); // Input field - new RegularEvent('click', (evt: MouseEvent, targetEl: HTMLElement): void => { + new RegularEvent('click', (evt: MouseEvent): void => { evt.preventDefault(); this.linkPageByTextfield(); }).delegateTo(document, 'input.t3js-pageLink'); @@ -51,7 +51,7 @@ class PageLinkHandler { value = 't3://page?uid=' + valueAsNumber; } LinkBrowser.finalizeFunction(value); - } + }; } export default new PageLinkHandler(); diff --git a/Build/Sources/TypeScript/backend/page-tree/page-tree-element.ts b/Build/Sources/TypeScript/backend/page-tree/page-tree-element.ts index 9c6246923300..00f4f4bb9d79 100644 --- a/Build/Sources/TypeScript/backend/page-tree/page-tree-element.ts +++ b/Build/Sources/TypeScript/backend/page-tree/page-tree-element.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {html, LitElement, TemplateResult, PropertyValues, nothing} from 'lit'; +import { html, LitElement, TemplateResult, PropertyValues, nothing } from 'lit'; import { customElement, property, query } from 'lit/decorators'; import { until } from 'lit/directives/until'; import { lll } from '@typo3/core/lit-helper'; @@ -24,7 +24,7 @@ import { ModuleUtility } from '@typo3/backend/module'; import ContextMenu from '../context-menu'; import * as d3selection from 'd3-selection'; import { KeyTypesEnum as KeyTypes } from '@typo3/backend/enum/key-types'; -import { TreeNodeSelection, TreeWrapperSelection, Toolbar, SvgTreeWrapper } from '../svg-tree'; +import { TreeNodeSelection, TreeWrapperSelection, Toolbar } from '../svg-tree'; import { DragDrop, DragDropHandler, DraggablePositionEnum, DragDropTargetPosition } from '../tree/drag-drop'; import Modal from '../modal'; import Severity from '../severity'; @@ -118,7 +118,7 @@ export class EditablePageTree extends PageTree { * Initializes a drag&drop when called on the page tree. Should be moved somewhere else at some point */ public initializeDragForNode() { - return this.dragDrop.connectDragHandler(new PageTreeNodeDragHandler(this, this.dragDrop)) + return this.dragDrop.connectDragHandler(new PageTreeNodeDragHandler(this, this.dragDrop)); } public removeEditedText() { @@ -193,7 +193,7 @@ export class EditablePageTree extends PageTree { } this.disableFocusedNodes(); - node.focused = true + node.focused = true; this.updateVisibleNodes(); this.removeEditedText(); @@ -313,7 +313,7 @@ export class PageTreeNavigationComponent extends LitElement { this.tree.addEventListener('typo3:svg-tree:node-selected', this.loadContent); this.tree.addEventListener('typo3:svg-tree:node-context', this.showContextMenu); this.tree.addEventListener('typo3:svg-tree:nodes-prepared', this.selectActiveNode); - } + }; return html` <div> @@ -338,15 +338,15 @@ export class PageTreeNavigationComponent extends LitElement { private refresh = (): void => { this.tree.refreshOrFilterTree(); - } + }; private setMountPoint = (e: CustomEvent): void => { this.setTemporaryMountPoint(e.detail.pageId as number); - } + }; private selectFirstNode = (): void => { this.tree.selectFirstNode(); - } + }; private unsetTemporaryMountPoint() { this.mountPointPath = null; @@ -395,7 +395,7 @@ export class PageTreeNavigationComponent extends LitElement { if (node) { Persistent.set('BackendComponents.States.Pagetree.stateHash.' + node.stateIdentifier, (node.expanded ? '1' : '0')); } - } + }; private loadContent = (evt: CustomEvent): void => { const node = evt.detail.node as TreeNode; @@ -414,7 +414,7 @@ export class PageTreeNavigationComponent extends LitElement { let contentUrl = ModuleUtility.getFromName(moduleMenu.getCurrentModule()).link; contentUrl += contentUrl.includes('?') ? '&' : '?'; top.TYPO3.Backend.ContentContainer.setUrl(contentUrl + 'id=' + node.identifier); - } + }; private showContextMenu = (evt: CustomEvent): void => { const node = evt.detail.node as TreeNode; @@ -429,7 +429,7 @@ export class PageTreeNavigationComponent extends LitElement { '', this.tree.getElementFromNode(node) ); - } + }; /** * Event listener called for each loaded node, @@ -437,14 +437,14 @@ export class PageTreeNavigationComponent extends LitElement { */ private selectActiveNode = (evt: CustomEvent): void => { const selectedNodeIdentifier = ModuleStateStorage.current('web').selection; - let nodes = evt.detail.nodes as Array<TreeNode>; + const nodes = evt.detail.nodes as Array<TreeNode>; evt.detail.nodes = nodes.map((node: TreeNode) => { if (node.stateIdentifier === selectedNodeIdentifier) { node.checked = true; } return node; }); - } + }; } @customElement('typo3-backend-navigation-component-pagetree-toolbar') @@ -581,7 +581,7 @@ class PageTreeDragDrop extends DragDrop { target: target, // hovered node position: position, // before, in, after command: command // element is copied or moved - } + }; } /** @@ -598,7 +598,7 @@ class PageTreeDragDrop extends DragDrop { // Calculate the Y-axis pixel WITHIN the bg node container to find out if the mouse is on the top // of the node or on the bottom const coordinates = d3selection.pointer(event, elementNodeBg.node()); - let y = coordinates[1]; + const y = coordinates[1]; if (y < 3) { this.updatePositioningLine(this.tree.hoveredNode); @@ -676,10 +676,10 @@ class PageTreeDragDrop extends DragDrop { return false; } if (!this.tree.isOverSvg) { - return false + return false; } if (!this.tree.hoveredNode) { - return false + return false; } if (draggingNode.isOver) { return false; @@ -712,14 +712,14 @@ class ToolbarDragHandler implements DragDropHandler { this.dragDrop = dragDrop; } - public onDragStart(event: MouseEvent, draggingNode: TreeNode | null): boolean { + public onDragStart(event: MouseEvent): boolean { this.dragStarted = false; this.startPageX = event.pageX; this.startPageY = event.pageY; return true; } - public onDragOver(event: MouseEvent, draggingNode: TreeNode | null): boolean { + public onDragOver(event: MouseEvent): boolean { if (this.dragDrop.isDragNodeDistanceMore(event, this)) { this.dragStarted = true; } else { @@ -767,7 +767,7 @@ class ToolbarDragHandler implements DragDropHandler { const newNode = {} as TreeNode; this.tree.disableFocusedNodes(); - newNode.focused = true + newNode.focused = true; this.tree.updateVisibleNodes(); newNode.command = 'new'; @@ -860,7 +860,7 @@ class ToolbarDragHandler implements DragDropHandler { } private removeNode(newNode: TreeNode) { - let index = this.tree.nodes.indexOf(newNode); + const index = this.tree.nodes.indexOf(newNode); // if newNode is only one child if (this.tree.nodes[index - 1].depth != newNode.depth && (!this.tree.nodes[index + 1] || this.tree.nodes[index + 1].depth != newNode.depth)) { @@ -871,7 +871,7 @@ class ToolbarDragHandler implements DragDropHandler { this.tree.prepareDataForVisibleNodes(); this.tree.updateVisibleNodes(); this.tree.removeEditedText(); - }; + } } /** @@ -935,7 +935,7 @@ class PageTreeNodeDragHandler implements DragDropHandler { this.startPageY = event.pageY; this.dragStarted = false; return true; - }; + } public onDragOver(event: MouseEvent, draggingNode: TreeNode | null): boolean { if (this.dragDrop.isDragNodeDistanceMore(event, this)) { @@ -1024,7 +1024,7 @@ class PageTreeNodeDragHandler implements DragDropHandler { btnClass: 'btn-warning', name: 'move' } - ]) + ]); modal.addEventListener('button.clicked', (e: JQueryEventObject) => { const target = e.target as HTMLInputElement; if (target.name === 'move') { diff --git a/Build/Sources/TypeScript/backend/page-tree/page-tree.ts b/Build/Sources/TypeScript/backend/page-tree/page-tree.ts index e9600e10aba9..be1ada6c30b6 100644 --- a/Build/Sources/TypeScript/backend/page-tree/page-tree.ts +++ b/Build/Sources/TypeScript/backend/page-tree/page-tree.ts @@ -12,9 +12,9 @@ */ import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {SvgTree, TreeNodeSelection} from '../svg-tree'; -import {TreeNode} from '../tree/tree-node'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { SvgTree, TreeNodeSelection } from '../svg-tree'; +import { TreeNode } from '../tree/tree-node'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; /** * A Tree based on SVG for pages, which has a AJAX-based loading of the tree @@ -56,7 +56,7 @@ export class PageTree extends SvgTree nodes = super.nodesUpdate(nodes); // append the stop element - let nodeStop = nodes + const nodeStop = nodes .append('svg') .attr('class', 'node-stop') .attr('y', (super.settings.icon.size / 2 * -1)) @@ -65,7 +65,7 @@ export class PageTree extends SvgTree .attr('width', super.settings.icon.size) .attr('visibility', (node: TreeNode) => node.stopPageTree && node.depth !== 0 ? 'visible' : 'hidden') .on('click', (evt: MouseEvent, node: TreeNode) => { - document.dispatchEvent(new CustomEvent('typo3:pagetree:mountPoint', {detail: {pageId: parseInt(node.identifier, 10)}})); + document.dispatchEvent(new CustomEvent('typo3:pagetree:mountPoint', { detail: { pageId: parseInt(node.identifier, 10) } })); }); nodeStop.append('rect') .attr('height', super.settings.icon.size) @@ -96,10 +96,10 @@ export class PageTree extends SvgTree this.nodesAddPlaceholder(); (new AjaxRequest(this.settings.dataUrl + '&pid=' + parentNode.identifier + '&mount=' + parentNode.mountPoint + '&pidDepth=' + parentNode.depth)) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then((response: AjaxResponse) => response.resolve()) .then((json: any) => { - let nodes = Array.isArray(json) ? json : []; + const nodes = Array.isArray(json) ? json : []; // first element is a parent nodes.shift(); const index = this.nodes.indexOf(parentNode) + 1; @@ -117,7 +117,7 @@ export class PageTree extends SvgTree this.focusNode(parentNode); }) .catch((error: any) => { - this.errorNotification(error, false) + this.errorNotification(error, false); this.nodesRemovePlaceholder(); throw error; }); diff --git a/Build/Sources/TypeScript/backend/pagetsconfig/pagetsconfig-includes.ts b/Build/Sources/TypeScript/backend/pagetsconfig/pagetsconfig-includes.ts index dc9727a346c1..63d54180e595 100644 --- a/Build/Sources/TypeScript/backend/pagetsconfig/pagetsconfig-includes.ts +++ b/Build/Sources/TypeScript/backend/pagetsconfig/pagetsconfig-includes.ts @@ -12,13 +12,13 @@ */ import DocumentService from '@typo3/core/document-service'; -import {default as Modal} from '@typo3/backend/modal'; -import {topLevelModuleImport} from '@typo3/backend/utility/top-level-module-import'; -import {html, TemplateResult} from 'lit'; -import {until} from 'lit/directives/until'; +import { default as Modal } from '@typo3/backend/modal'; +import { topLevelModuleImport } from '@typo3/backend/utility/top-level-module-import'; +import { html, TemplateResult } from 'lit'; +import { until } from 'lit/directives/until'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import type {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import type {JavaScriptItemPayload} from '@typo3/core/java-script-item-processor'; +import type { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import type { JavaScriptItemPayload } from '@typo3/core/java-script-item-processor'; /** * @todo: This could be code-de-duplicated with ext:tstemplate template-analyzer.ts, @@ -45,7 +45,7 @@ class PageTsConfigIncludes { this.fetchModalContent(url), html`<div class="modal-loading"><typo3-backend-spinner size="default"></typo3-backend-spinner></div>` )}`; - const modal = Modal.advanced({type, title, size, content}); + Modal.advanced({ type, title, size, content }); }); }); } diff --git a/Build/Sources/TypeScript/backend/popover.ts b/Build/Sources/TypeScript/backend/popover.ts index 49826cd598db..ea202788cd81 100644 --- a/Build/Sources/TypeScript/backend/popover.ts +++ b/Build/Sources/TypeScript/backend/popover.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {Popover as BootstrapPopover} from 'bootstrap'; +import { Popover as BootstrapPopover } from 'bootstrap'; /** * Module: @typo3/backend/popover @@ -73,13 +73,13 @@ class Popover { delete options.content; const popover = BootstrapPopover.getInstance(element); - // @ts-ignore popover.setContent({ '.popover-header': title, '.popover-body': content }); for (const [optionName, optionValue] of Object.entries(options)) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: using internal _config attribute popover._config[optionName] = optionValue; } diff --git a/Build/Sources/TypeScript/backend/record-download-button.ts b/Build/Sources/TypeScript/backend/record-download-button.ts index e5a5264789db..57f6607da567 100644 --- a/Build/Sources/TypeScript/backend/record-download-button.ts +++ b/Build/Sources/TypeScript/backend/record-download-button.ts @@ -11,12 +11,12 @@ * The TYPO3 project - inspiring people to share! */ -import {html, css, TemplateResult, LitElement} from 'lit'; -import {customElement, property} from 'lit/decorators'; -import {SeverityEnum} from '@typo3/backend/enum/severity'; +import { html, css, TemplateResult, LitElement } from 'lit'; +import { customElement, property } from 'lit/decorators'; +import { SeverityEnum } from '@typo3/backend/enum/severity'; import Severity from '@typo3/backend/severity'; import Modal from '@typo3/backend/modal'; -import {lll} from '@typo3/core/lit-helper'; +import { lll } from '@typo3/core/lit-helper'; enum Selectors { formatSelector = '.t3js-record-download-format-selector', @@ -32,12 +32,12 @@ enum Selectors { * </typo3-recordlist-record-download-button> */ @customElement('typo3-recordlist-record-download-button') -class RecordDownloadButton extends LitElement { +export class RecordDownloadButton extends LitElement { static styles = [css`:host { cursor: pointer; appearance: button; }`]; - @property({type: String}) url: string; - @property({type: String}) title: string; - @property({type: String}) ok: string; - @property({type: String}) close: string; + @property({ type: String }) url: string; + @property({ type: String }) title: string; + @property({ type: String }) ok: string; + @property({ type: String }) close: string; public constructor() { super(); @@ -50,7 +50,7 @@ class RecordDownloadButton extends LitElement { e.preventDefault(); this.showDownloadConfigurationModal(); } - }) + }); } public connectedCallback(): void { diff --git a/Build/Sources/TypeScript/backend/record-link-handler.ts b/Build/Sources/TypeScript/backend/record-link-handler.ts index b13392fc6a65..5042d122c5bd 100644 --- a/Build/Sources/TypeScript/backend/record-link-handler.ts +++ b/Build/Sources/TypeScript/backend/record-link-handler.ts @@ -25,7 +25,7 @@ class RecordLinkHandler { const data = targetEl.closest('span').dataset; LinkBrowser.finalizeFunction(document.body.dataset.identifier + data.uid); }).delegateTo(document, '[data-close]'); - new RegularEvent('click', (evt: MouseEvent, targetEl: HTMLElement): void => { + new RegularEvent('click', (evt: MouseEvent): void => { evt.preventDefault(); LinkBrowser.finalizeFunction(document.body.dataset.currentLink); }).delegateTo(document, 'input.t3js-linkCurrent'); diff --git a/Build/Sources/TypeScript/backend/recordlist.ts b/Build/Sources/TypeScript/backend/recordlist.ts index 015e15af7cea..b8ba2f9eebed 100644 --- a/Build/Sources/TypeScript/backend/recordlist.ts +++ b/Build/Sources/TypeScript/backend/recordlist.ts @@ -16,9 +16,9 @@ import Icons from '@typo3/backend/icons'; import PersistentStorage from '@typo3/backend/storage/persistent'; import RegularEvent from '@typo3/core/event/regular-event'; import DocumentService from '@typo3/core/document-service'; -import {ActionConfiguration, ActionEventDetails} from '@typo3/backend/multi-record-selection-action'; -import {default as Modal, ModalElement} from '@typo3/backend/modal'; -import {SeverityEnum} from '@typo3/backend/enum/severity'; +import { ActionConfiguration, ActionEventDetails } from '@typo3/backend/multi-record-selection-action'; +import { default as Modal, ModalElement } from '@typo3/backend/modal'; +import { SeverityEnum } from '@typo3/backend/enum/severity'; import Severity from '@typo3/backend/severity'; import { MultiRecordSelectionSelectors } from '@typo3/backend/multi-record-selection'; @@ -66,6 +66,26 @@ class Recordlist { }, }; + constructor() { + new RegularEvent('click', this.toggleClick).delegateTo(document, this.identifier.toggle); + $(document).on('click', this.identifier.icons.editMultiple, this.onEditMultiple); + $(document).on('click', this.identifier.localize, this.disableButton); + DocumentService.ready().then((): void => { + this.registerPaginationEvents(); + }); + new RegularEvent('typo3:datahandler:process', this.handleDataHandlerResult.bind(this)).bindTo(document); + + // multi record selection events + new RegularEvent('multiRecordSelection:action:edit', this.onEditMultiple).bindTo(document); + new RegularEvent('multiRecordSelection:action:delete', this.deleteMultiple).bindTo(document); + new RegularEvent('multiRecordSelection:action:copyMarked', (event: CustomEvent): void => { + Recordlist.submitClipboardFormWithCommand('copyMarked', event.target as HTMLButtonElement); + }).bindTo(document); + new RegularEvent('multiRecordSelection:action:removeMarked', (event: CustomEvent): void => { + Recordlist.submitClipboardFormWithCommand('removeMarked', event.target as HTMLButtonElement); + }).bindTo(document); + } + private static submitClipboardFormWithCommand(cmd: string, target: HTMLButtonElement) { const clipboardForm = <HTMLFormElement>target.closest('form'); if (!clipboardForm) { @@ -86,26 +106,6 @@ class Recordlist { return encodeURIComponent(returnUrl); } - constructor() { - new RegularEvent('click', this.toggleClick).delegateTo(document, this.identifier.toggle); - $(document).on('click', this.identifier.icons.editMultiple, this.onEditMultiple); - $(document).on('click', this.identifier.localize, this.disableButton); - DocumentService.ready().then((): void => { - this.registerPaginationEvents(); - }); - new RegularEvent('typo3:datahandler:process', this.handleDataHandlerResult.bind(this)).bindTo(document); - - // multi record selection events - new RegularEvent('multiRecordSelection:action:edit', this.onEditMultiple).bindTo(document); - new RegularEvent('multiRecordSelection:action:delete', this.deleteMultiple).bindTo(document); - new RegularEvent('multiRecordSelection:action:copyMarked', (event: CustomEvent): void => { - Recordlist.submitClipboardFormWithCommand('copyMarked', event.target as HTMLButtonElement) - }).bindTo(document); - new RegularEvent('multiRecordSelection:action:removeMarked', (event: CustomEvent): void => { - Recordlist.submitClipboardFormWithCommand('removeMarked', event.target as HTMLButtonElement) - }).bindTo(document); - } - public toggleClick = (e: MouseEvent, targetEl: HTMLElement): void => { e.preventDefault(); @@ -127,14 +127,14 @@ class Recordlist { storedModuleDataList = PersistentStorage.get('moduleData.web_list.collapsedTables'); } - const collapseConfig: any = {}; + const collapseConfig: Record<string, number> = {}; collapseConfig[table] = isExpanded ? 1 : 0; $.extend(storedModuleDataList, collapseConfig); PersistentStorage.set('moduleData.web_list.collapsedTables', storedModuleDataList).then((): void => { $target.data('state', isExpanded ? 'collapsed' : 'expanded'); }); - } + }; /** * Handles editing multiple records. @@ -144,7 +144,7 @@ class Recordlist { let tableName: string = ''; let returnUrl: string = ''; let columnsOnly: string = ''; - let entityIdentifiers: Array<string> = []; + const entityIdentifiers: Array<string> = []; if (event.type === 'multiRecordSelection:action:edit') { // In case the request is triggerd by the multi record selection event, handling @@ -184,7 +184,7 @@ class Recordlist { // If there are selected records, only those are added to the list selection.forEach((entity: HTMLInputElement): void => { entityIdentifiers.push((entity.closest(this.identifier.entity + '[data-uid][data-table="' + tableName + '"]') as HTMLElement).dataset.uid); - }) + }); } else { // Get all records for the current table and add their uid to the list const entities: NodeListOf<HTMLElement> = tableContainer.querySelectorAll(this.identifier.entity + '[data-uid][data-table="' + tableName + '"]'); @@ -211,13 +211,13 @@ class Recordlist { } window.location.href = editUrl; - } + }; private disableButton = (event: JQueryEventObject): void => { const $me = $(event.currentTarget); $me.prop('disable', true).addClass('disabled'); - } + }; private handleDataHandlerResult(e: CustomEvent): void { const payload = e.detail.payload; @@ -234,7 +234,7 @@ class Recordlist { if (payload.action === 'delete') { this.deleteRow(payload); } - }; + } private deleteRow = (payload: DataHandlerEventPayload): void => { const $tableElement = $(`table[data-table="${payload.table}"]`); @@ -260,7 +260,7 @@ class Recordlist { if (payload.table === 'pages') { top.document.dispatchEvent(new CustomEvent('typo3:pagetree:refresh')); } - } + }; private deleteMultiple (event: CustomEvent): void { event.preventDefault(); @@ -282,7 +282,7 @@ class Recordlist { btnClass: 'btn-' + Severity.getCssClass(SeverityEnum.warning), trigger: (e: Event, modal: ModalElement) => { modal.hideModal(); - Recordlist.submitClipboardFormWithCommand('delete', event.target as HTMLButtonElement) + Recordlist.submitClipboardFormWithCommand('delete', event.target as HTMLButtonElement); } } ] @@ -305,7 +305,7 @@ class Recordlist { } }); }); - } + }; } export default new Recordlist(); diff --git a/Build/Sources/TypeScript/backend/severity.ts b/Build/Sources/TypeScript/backend/severity.ts index eac3296f498c..897c9307de8b 100644 --- a/Build/Sources/TypeScript/backend/severity.ts +++ b/Build/Sources/TypeScript/backend/severity.ts @@ -11,13 +11,13 @@ * The TYPO3 project - inspiring people to share! */ -import {SeverityEnum} from './enum/severity'; +import { SeverityEnum } from './enum/severity'; /** * Module: @typo3/backend/severity * Severity for the TYPO3 backend */ -class Severity { +export default class Severity { public static notice: number = SeverityEnum.notice; public static info: number = SeverityEnum.info; public static ok: number = SeverityEnum.ok; @@ -55,7 +55,7 @@ class Severity { } } -let severityObject: any; +let severityObject: Severity; try { // fetch from opening window if (window.opener && window.opener.TYPO3 && window.opener.TYPO3.Severity) { @@ -86,5 +86,3 @@ if (!severityObject) { TYPO3.Severity = severityObject; } } - -export default severityObject; diff --git a/Build/Sources/TypeScript/backend/storage/abstract-client-storage.ts b/Build/Sources/TypeScript/backend/storage/abstract-client-storage.ts index d18e245d6b15..d8cf563289d7 100644 --- a/Build/Sources/TypeScript/backend/storage/abstract-client-storage.ts +++ b/Build/Sources/TypeScript/backend/storage/abstract-client-storage.ts @@ -26,7 +26,7 @@ export default abstract class AbstractClientStorage { return this.storage.getItem(this.keyPrefix + key); } - public getByPrefix(prefix: string): { [key: string]: string } { + public getByPrefix(prefix: string): Record<string, string> { if (this.storage === null) { return {}; } diff --git a/Build/Sources/TypeScript/backend/storage/module-state-storage.ts b/Build/Sources/TypeScript/backend/storage/module-state-storage.ts index 7c6ecfd988a8..79c4103520f3 100644 --- a/Build/Sources/TypeScript/backend/storage/module-state-storage.ts +++ b/Build/Sources/TypeScript/backend/storage/module-state-storage.ts @@ -48,7 +48,7 @@ export class ModuleStateStorage { throw new SyntaxError('mount must be of type string'); } const state = ModuleStateStorage.assignProperties( - {mount, identifier, selected} as StateChange, + { mount, identifier, selected } as StateChange, ModuleStateStorage.fetch(module) ); ModuleStateStorage.commit(module, state); @@ -61,7 +61,7 @@ export class ModuleStateStorage { identifier, selected, ModuleStateStorage.current(module).mount - ) + ); } public static current(module: string): CurrentState { @@ -89,7 +89,7 @@ export class ModuleStateStorage { private static assignProperties(change: StateChange, state: CurrentState|null): CurrentState { - let target = Object.assign(ModuleStateStorage.createCurrentState(), state) as CurrentState; + const target = Object.assign(ModuleStateStorage.createCurrentState(), state) as CurrentState; if (change.mount) { target.mount = change.mount; } @@ -104,7 +104,7 @@ export class ModuleStateStorage { private static createCurrentState(): CurrentState { - return {mount: null, identifier: '', selection: null} as CurrentState; + return { mount: null, identifier: '', selection: null } as CurrentState; } } diff --git a/Build/Sources/TypeScript/backend/storage/persistent.ts b/Build/Sources/TypeScript/backend/storage/persistent.ts index 76603bbadeda..2f4303de8f36 100644 --- a/Build/Sources/TypeScript/backend/storage/persistent.ts +++ b/Build/Sources/TypeScript/backend/storage/persistent.ts @@ -12,9 +12,9 @@ */ import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; -type UC = { [key: string]: string | number | boolean | null | UC }; +export type UC = { [key: string]: string | number | boolean | null | UC }; /** * Module: @typo3/backend/storage/persistent @@ -116,7 +116,7 @@ class Persistent { * * @param {UC} data */ - public load(data: UC): any { + public load(data: UC): void { this.data = data; } diff --git a/Build/Sources/TypeScript/backend/svg-tree.ts b/Build/Sources/TypeScript/backend/svg-tree.ts index 691615a063e4..51cd8745b546 100644 --- a/Build/Sources/TypeScript/backend/svg-tree.ts +++ b/Build/Sources/TypeScript/backend/svg-tree.ts @@ -11,17 +11,17 @@ * The TYPO3 project - inspiring people to share! */ -import {html, LitElement, TemplateResult} from 'lit'; -import {customElement, property, state} from 'lit/decorators'; -import {TreeNode} from './tree/tree-node'; +import { html, LitElement, TemplateResult } from 'lit'; +import { customElement, property, state } from 'lit/decorators'; +import { TreeNode } from './tree/tree-node'; import * as d3selection from 'd3-selection'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Notification from './notification'; -import {KeyTypesEnum as KeyTypes} from './enum/key-types'; +import { KeyTypesEnum as KeyTypes } from './enum/key-types'; import Icons from './icons'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {MarkupIdentifiers} from './enum/icon-types'; -import {lll} from '@typo3/core/lit-helper'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { MarkupIdentifiers } from './enum/icon-types'; +import { lll } from '@typo3/core/lit-helper'; import DebounceEvent from '@typo3/core/event/debounce-event'; import '@typo3/backend/element/icon-element'; @@ -53,7 +53,7 @@ export interface SvgTreeWrapper extends HTMLElement { } export class SvgTree extends LitElement { - @property({type: Object}) setup?: {[keys: string]: any} = null; + @property({ type: Object }) setup?: {[keys: string]: any} = null; @state() settings: SvgTreeSettings = { showIcons: false, marginTop: 15, @@ -165,9 +165,9 @@ export class SvgTree extends LitElement { public loadCommonIcons(): void { this.fetchIcon('actions-chevron-right', false); // used as toggle icon - this.fetchIcon('overlay-backenduser', false); // used as locked indicator (a different user is editing) - this.fetchIcon('actions-caret-right', false); // used as enter icon for stopped trees - this.fetchIcon('actions-link', false); // used as link indicator + this.fetchIcon('overlay-backenduser', false); // used as locked indicator (a different user is editing) + this.fetchIcon('actions-caret-right', false); // used as enter icon for stopped trees + this.fetchIcon('actions-link', false); // used as link indicator } /** @@ -218,7 +218,7 @@ export class SvgTree extends LitElement { public loadData() { this.nodesAddPlaceholder(); (new AjaxRequest(this.settings.dataUrl)) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then((response: AjaxResponse) => response.resolve()) .then((json) => { const nodes = Array.isArray(json) ? json : []; @@ -266,7 +266,7 @@ export class SvgTree extends LitElement { if (node.depth > 0) { let currentDepth = node.depth; for (let i = index; i >= 0; i--) { - let currentNode = nodes[i]; + const currentNode = nodes[i]; if (currentNode.depth < currentDepth) { node.parents.push(i); node.parentsStateIdentifier.push(nodes[i].stateIdentifier); @@ -289,7 +289,7 @@ export class SvgTree extends LitElement { if (nodesOnRootLevel.length === 1) { nodes[0].expanded = true; } - const evt = new CustomEvent('typo3:svg-tree:nodes-prepared', {detail: {nodes: nodes}, bubbles: false}); + const evt = new CustomEvent('typo3:svg-tree:nodes-prepared', { detail: { nodes: nodes }, bubbles: false }); this.dispatchEvent(evt); this.nodes = evt.detail.nodes; } @@ -330,7 +330,7 @@ export class SvgTree extends LitElement { public hideChildren(node: TreeNode): void { node.expanded = false; this.setExpandedState(node); - this.dispatchEvent(new CustomEvent('typo3:svg-tree:expand-toggle', {detail: {node: node}})); + this.dispatchEvent(new CustomEvent('typo3:svg-tree:expand-toggle', { detail: { node: node } })); } /** @@ -341,7 +341,7 @@ export class SvgTree extends LitElement { public showChildren(node: TreeNode): void { node.expanded = true; this.setExpandedState(node); - this.dispatchEvent(new CustomEvent('typo3:svg-tree:expand-toggle', {detail: {node: node}})); + this.dispatchEvent(new CustomEvent('typo3:svg-tree:expand-toggle', { detail: { node: node } })); } /** @@ -391,7 +391,7 @@ export class SvgTree extends LitElement { }); this.data.nodes = this.nodes.filter((node: TreeNode): boolean => { - return node.hidden !== true && !node.parents.some((index: number) => Boolean(blacklist[index])) + return node.hidden !== true && !node.parents.some((index: number) => Boolean(blacklist[index])); }); this.data.links = []; @@ -435,9 +435,9 @@ export class SvgTree extends LitElement { icon: null } as SvgTreeDataIcon; Icons.getIcon(iconName, Icons.sizes.small, null, null, MarkupIdentifiers.inline).then((icon: string) => { - let result = icon.match(/<svg[\s\S]*<\/svg>/i); + const result = icon.match(/<svg[\s\S]*<\/svg>/i); if (result) { - let iconEl = document.createRange().createContextualFragment(result[0]); + const iconEl = document.createRange().createContextualFragment(result[0]); this.icons[iconName].icon = iconEl.firstElementChild as SVGElement; } if (update) { @@ -552,7 +552,7 @@ export class SvgTree extends LitElement { // 2. offset the element by 0.5px - done in SvgTree::getNodeBackgroundTransform nodeHeight = nodeHeight - 1; - let node = nodesBg.enter() + const node = nodesBg.enter() .append('rect') .merge(nodesBg as d3selection.Selection<SVGRectElement, TreeNode, any, any>); return node @@ -569,7 +569,7 @@ export class SvgTree extends LitElement { }) .on('contextmenu', (evt: MouseEvent, node: TreeNode) => { evt.preventDefault(); - this.dispatchEvent(new CustomEvent('typo3:svg-tree:node-context', {detail: {node: node}})); + this.dispatchEvent(new CustomEvent('typo3:svg-tree:node-context', { detail: { node: node } })); }); } @@ -597,12 +597,13 @@ export class SvgTree extends LitElement { if (!this.isNodeSelectable(node)) { return; } + // Disable already selected nodes this.disableSelectedNodes(); this.disableFocusedNodes(); node.checked = true; node.focused = true; - this.dispatchEvent(new CustomEvent('typo3:svg-tree:node-selected', {detail: {node: node, propagate: propagate}})); + this.dispatchEvent(new CustomEvent('typo3:svg-tree:node-selected', { detail: { node: node, propagate: propagate } })); this.updateVisibleNodes(); } @@ -613,10 +614,10 @@ export class SvgTree extends LitElement { this.nodesAddPlaceholder(); if (this.searchTerm && this.settings.filterUrl) { (new AjaxRequest(this.settings.filterUrl + '&q=' + this.searchTerm)) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then((response: AjaxResponse) => response.resolve()) .then((json) => { - let nodes = Array.isArray(json) ? json : []; + const nodes = Array.isArray(json) ? json : []; if (nodes.length > 0) { if (this.unfilteredNodes === '') { this.unfilteredNodes = JSON.stringify(this.nodes); @@ -626,7 +627,7 @@ export class SvgTree extends LitElement { this.nodesRemovePlaceholder(); }) .catch((error: any) => { - this.errorNotification(error, false) + this.errorNotification(error, false); this.nodesRemovePlaceholder(); throw error; }); @@ -640,7 +641,7 @@ export class SvgTree extends LitElement { { this.searchTerm = ''; if (this.unfilteredNodes.length > 0) { - let currentlySelected = this.getSelectedNodes()[0]; + const currentlySelected = this.getSelectedNodes()[0]; if (typeof currentlySelected === 'undefined') { this.refreshTree(); return; @@ -673,7 +674,7 @@ export class SvgTree extends LitElement { error.forEach((message: any) => { Notification.error( message.title, message.message - )}); + );}); } else { let title = this.networkErrorTitle; if (error && error.target && (error.target.status || error.target.statusText)) { @@ -755,7 +756,7 @@ export class SvgTree extends LitElement { } protected firstUpdated(): void { - this.svg = d3selection.select(this.querySelector('svg')) + this.svg = d3selection.select(this.querySelector('svg')); this.container = d3selection.select(this.querySelector('.nodes-wrapper')) .attr('transform', 'translate(' + (this.settings.indentWidth / 2) + ',' + (this.settings.nodeHeight / 2) + ')') as any; this.nodesBgContainer = d3selection.select(this.querySelector('.nodes-bg')) as any; @@ -783,7 +784,7 @@ export class SvgTree extends LitElement { if (this.getClientRects().length > 0) { this.updateView(); } - } + }; protected disableSelectedNodes(): void { // Disable already selected nodes @@ -808,7 +809,7 @@ export class SvgTree extends LitElement { .on('mouseover', (evt: MouseEvent, node: TreeNode) => this.onMouseOverNode(node)) .on('mouseout', (evt: MouseEvent, node: TreeNode) => this.onMouseOutOfNode(node)) .attr('data-state-id', this.getNodeStateIdentifier) - .attr('transform', (node: TreeNode) => this.getNodeActionTransform(node, this.settings.indentWidth, this.settings.nodeHeight)) + .attr('transform', (node: TreeNode) => this.getNodeActionTransform(node, this.settings.indentWidth, this.settings.nodeHeight)); } return nodesActions.enter(); } @@ -846,11 +847,6 @@ export class SvgTree extends LitElement { .attr('xlink:href', '#icon-' + iconIdentifier); } - /** - * Check whether node can be selected. - * In some cases (e.g. selecting a parent) it should not be possible to select - * element (as it's own parent). - */ protected isNodeSelectable(node: TreeNode): boolean { return true; } @@ -887,7 +883,7 @@ export class SvgTree extends LitElement { .on('mouseout', (evt: MouseEvent, node: TreeNode) => this.onMouseOutOfNode(node)) .on('contextmenu', (evt: MouseEvent, node: TreeNode) => { evt.preventDefault(); - this.dispatchEvent(new CustomEvent('typo3:svg-tree:node-context', {detail: {node: node}})); + this.dispatchEvent(new CustomEvent('typo3:svg-tree:node-context', { detail: { node: node } })); }); nodes @@ -1036,7 +1032,7 @@ export class SvgTree extends LitElement { */ protected getNodeBackgroundTransform(node: TreeNode, indentWidth: number, nodeHeight: number): string { - let positionX = (indentWidth / 2 * -1); + const positionX = (indentWidth / 2 * -1); let positionY = (node.y || 0) - (nodeHeight / 2); // IMPORTANT @@ -1069,7 +1065,7 @@ export class SvgTree extends LitElement { * Event handler for clicking on a node's icon */ protected clickOnIcon(node: TreeNode): void { - this.dispatchEvent(new CustomEvent('typo3:svg-tree:node-context', {detail: {node: node}})); + this.dispatchEvent(new CustomEvent('typo3:svg-tree:node-context', { detail: { node: node } })); } /** @@ -1121,7 +1117,7 @@ export class SvgTree extends LitElement { const nodeEnter = this.nodesUpdate(nodes); // append the toggle element - let nodeToggle = nodeEnter + const nodeToggle = nodeEnter .append('svg') .attr('class', 'node-toggle') .attr('y', (this.settings.icon.size / 2 * -1)) @@ -1150,7 +1146,7 @@ export class SvgTree extends LitElement { .attr('y', '-10') .on('click', (evt: MouseEvent, node: TreeNode) => { evt.preventDefault(); - this.clickOnIcon(node) + this.clickOnIcon(node); }); // improve usability by making the click area a 20px square @@ -1213,12 +1209,12 @@ export class SvgTree extends LitElement { node.isOver = true; this.hoveredNode = node; - let elementNodeBg = this.svg.select('.nodes-bg .node-bg[data-state-id="' + node.stateIdentifier + '"]'); + const elementNodeBg = this.svg.select('.nodes-bg .node-bg[data-state-id="' + node.stateIdentifier + '"]'); if (elementNodeBg.size()) { elementNodeBg.classed('node-over', true); } - let elementNodeAction = this.nodesActionsContainer.select('.node-action[data-state-id="' + node.stateIdentifier + '"]'); + const elementNodeAction = this.nodesActionsContainer.select('.node-action[data-state-id="' + node.stateIdentifier + '"]'); if (elementNodeAction.size()) { elementNodeAction.classed('node-action-over', true); // @todo: needs to be adapted for active nodes @@ -1233,12 +1229,12 @@ export class SvgTree extends LitElement { node.isOver = false; this.hoveredNode = null; - let elementNodeBg = this.svg.select('.nodes-bg .node-bg[data-state-id="' + node.stateIdentifier + '"]'); + const elementNodeBg = this.svg.select('.nodes-bg .node-bg[data-state-id="' + node.stateIdentifier + '"]'); if (elementNodeBg.size()) { elementNodeBg.classed('node-over node-alert', false); } - let elementNodeAction = this.nodesActionsContainer.select('.node-action[data-state-id="' + node.stateIdentifier + '"]'); + const elementNodeAction = this.nodesActionsContainer.select('.node-action[data-state-id="' + node.stateIdentifier + '"]'); if (elementNodeAction.size()) { elementNodeAction.classed('node-action-over', false); } @@ -1257,7 +1253,7 @@ export class SvgTree extends LitElement { */ private handleKeyboardInteraction(evt: KeyboardEvent) { const evtTarget = evt.target as SVGElement; - let currentNode = d3selection.select(evtTarget).datum() as TreeNode; + const currentNode = d3selection.select(evtTarget).datum() as TreeNode; const charCodes = [ KeyTypes.ENTER, KeyTypes.SPACE, @@ -1278,13 +1274,13 @@ export class SvgTree extends LitElement { case KeyTypes.END: // scroll to end, select last node this.scrollTop = this.lastElementChild.getBoundingClientRect().height + this.settings.nodeHeight - this.viewportHeight; - parentDomNode.scrollIntoView({behavior: 'smooth', block: 'end'}); + parentDomNode.scrollIntoView({ behavior: 'smooth', block: 'end' }); this.focusNode(this.getNodeFromElement(parentDomNode.lastElementChild as SVGElement)); this.updateVisibleNodes(); break; case KeyTypes.HOME: // scroll to top, select first node - this.scrollTo({'top': this.nodes[0].y, 'behavior': 'smooth'}); + this.scrollTo({ 'top': this.nodes[0].y, 'behavior': 'smooth' }); this.prepareDataForVisibleNodes(); this.focusNode(this.getNodeFromElement(parentDomNode.firstElementChild as SVGElement)); this.updateVisibleNodes(); @@ -1299,7 +1295,7 @@ export class SvgTree extends LitElement { } } else if (currentNode.parents.length > 0) { // go to parent node - let parentNode = this.nodes[currentNode.parents[0]]; + const parentNode = this.nodes[currentNode.parents[0]]; this.scrollNodeIntoVisibleArea(parentNode, 'up'); this.focusNode(parentNode); this.updateVisibleNodes(); @@ -1342,7 +1338,7 @@ export class SvgTree extends LitElement { case KeyTypes.ENTER: case KeyTypes.SPACE: this.selectNode(currentNode, true); - this.focusNode(currentNode) + this.focusNode(currentNode); break; default: } @@ -1361,7 +1357,7 @@ export class SvgTree extends LitElement { } else { return; } - this.scrollTo({'top': scrollTop, 'behavior': 'smooth'}); + this.scrollTo({ 'top': scrollTop, 'behavior': 'smooth' }); this.updateVisibleNodes(); } @@ -1388,10 +1384,10 @@ export class SvgTree extends LitElement { .attr('class', 'link') .attr('id', this.getGroupIdentifier) .attr('role', (link: SvgTreeDataLink): null|string => { - return link.target.siblingsPosition === 1 && link.source.owns.length > 0 ? 'group' : null + return link.target.siblingsPosition === 1 && link.source.owns.length > 0 ? 'group' : null; }) .attr('aria-owns', (link: SvgTreeDataLink): null|string => { - return link.target.siblingsPosition === 1 && link.source.owns.length > 0 ? link.source.owns.join(' ') : null + return link.target.siblingsPosition === 1 && link.source.owns.length > 0 ? link.source.owns.join(' ') : null; }) // create + update .merge(links as d3selection.Selection<any, any, any, any>) @@ -1415,7 +1411,7 @@ export class SvgTree extends LitElement { */ @customElement('typo3-backend-tree-toolbar') export class Toolbar extends LitElement { - @property({type: SvgTree}) tree: SvgTree = null; + @property({ type: SvgTree }) tree: SvgTree = null; protected settings = { searchInput: '.search-input', filterTimeout: 450 diff --git a/Build/Sources/TypeScript/backend/switch-user.ts b/Build/Sources/TypeScript/backend/switch-user.ts index c56987612727..bec88b90bac2 100644 --- a/Build/Sources/TypeScript/backend/switch-user.ts +++ b/Build/Sources/TypeScript/backend/switch-user.ts @@ -11,10 +11,10 @@ * The TYPO3 project - inspiring people to share! */ -import {html, TemplateResult, LitElement} from 'lit'; -import {customElement, property} from 'lit/decorators'; +import { html, TemplateResult, LitElement } from 'lit'; +import { customElement, property } from 'lit/decorators'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import Notification from '@typo3/backend/notification'; enum Modes { @@ -31,9 +31,9 @@ enum Modes { * </typo3-switch-user> */ @customElement('typo3-backend-switch-user') -class SwitchUser extends LitElement { - @property({type: String}) targetUser: string; - @property({type: Modes}) mode: Modes = Modes.switch; +export class SwitchUser extends LitElement { + @property({ type: String }) targetUser: string; + @property({ type: Modes }) mode: Modes = Modes.switch; public constructor() { super(); @@ -60,7 +60,7 @@ class SwitchUser extends LitElement { (new AjaxRequest(TYPO3.settings.ajaxUrls.switch_user)).post({ targetUser: this.targetUser, - }).then(async (response: AjaxResponse): Promise<any> => { + }).then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && data.url) { top.window.location.href = data.url; @@ -71,7 +71,7 @@ class SwitchUser extends LitElement { } private handleExitSwitchUser(): void { - (new AjaxRequest(TYPO3.settings.ajaxUrls.switch_user_exit)).post({}).then(async (response: AjaxResponse): Promise<any> => { + (new AjaxRequest(TYPO3.settings.ajaxUrls.switch_user_exit)).post({}).then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && data.url) { top.window.location.href = data.url; diff --git a/Build/Sources/TypeScript/backend/tabs.ts b/Build/Sources/TypeScript/backend/tabs.ts index c9cce1d3dab5..cc19b3dec3bd 100644 --- a/Build/Sources/TypeScript/backend/tabs.ts +++ b/Build/Sources/TypeScript/backend/tabs.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {Tab} from 'bootstrap'; +import { Tab } from 'bootstrap'; import BrowserSession from './storage/browser-session'; import Client from './storage/client'; import DocumentService from '@typo3/core/document-service'; @@ -21,26 +21,6 @@ import DocumentService from '@typo3/core/document-service'; * @exports @typo3/backend/tabs */ class Tabs { - /** - * Receive active tab from storage - * - * @param {string} id - * @returns {string} - */ - private static receiveActiveTab(id: string): string { - return BrowserSession.get(id) || ''; - } - - /** - * Set active tab to storage - * - * @param {string} id - * @param {string} target - */ - private static storeActiveTab(id: string, target: string): void { - BrowserSession.set(id, target); - } - constructor() { DocumentService.ready().then((): void => { const tabContainers = document.querySelectorAll('.t3js-tabs'); @@ -64,6 +44,26 @@ class Tabs { // Remove legacy values from localStorage Client.unsetByPrefix('tabs-'); } + + /** + * Receive active tab from storage + * + * @param {string} id + * @returns {string} + */ + private static receiveActiveTab(id: string): string { + return BrowserSession.get(id) || ''; + } + + /** + * Set active tab to storage + * + * @param {string} id + * @param {string} target + */ + private static storeActiveTab(id: string, target: string): void { + BrowserSession.set(id, target); + } } export default new Tabs(); diff --git a/Build/Sources/TypeScript/backend/tests/backend-exception-test.ts b/Build/Sources/TypeScript/backend/tests/backend-exception-test.ts index 8c0fcbc65629..41a1ce582923 100644 --- a/Build/Sources/TypeScript/backend/tests/backend-exception-test.ts +++ b/Build/Sources/TypeScript/backend/tests/backend-exception-test.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {BackendException} from '@typo3/backend/backend-exception'; +import { BackendException } from '@typo3/backend/backend-exception'; describe('@typo3/backend/backend-exception', () => { it('sets exception message', () => { diff --git a/Build/Sources/TypeScript/backend/tests/element/immediate-action-element-test.ts b/Build/Sources/TypeScript/backend/tests/element/immediate-action-element-test.ts index cfece04cf968..50d59662de9e 100644 --- a/Build/Sources/TypeScript/backend/tests/element/immediate-action-element-test.ts +++ b/Build/Sources/TypeScript/backend/tests/element/immediate-action-element-test.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {ImmediateActionElement} from '@typo3/backend/element/immediate-action-element'; +import { ImmediateActionElement } from '@typo3/backend/element/immediate-action-element'; import moduleMenuApp from '@typo3/backend/module-menu'; import viewportObject from '@typo3/backend/viewport'; @@ -42,7 +42,7 @@ describe('TYPO3/CMS/Backend/Element/ImmediateActionElement:', () => { expect(observer.refresh).not.toHaveBeenCalled(); root.appendChild(element); await import('@typo3/backend/viewport'); - await new Promise((resolve) => setTimeout(resolve, 100)) + await new Promise((resolve) => setTimeout(resolve, 100)); expect(observer.refresh).toHaveBeenCalled(); (viewportObject as any).Topbar = backup; }); @@ -61,7 +61,7 @@ describe('TYPO3/CMS/Backend/Element/ImmediateActionElement:', () => { expect(observer.refresh).not.toHaveBeenCalled(); root.appendChild(element); await import('@typo3/backend/viewport'); - await new Promise((resolve) => setTimeout(resolve, 100)) + await new Promise((resolve) => setTimeout(resolve, 100)); expect(observer.refresh).toHaveBeenCalled(); (viewportObject as any).Topbar = backup; }); @@ -79,7 +79,7 @@ describe('TYPO3/CMS/Backend/Element/ImmediateActionElement:', () => { expect(observer.refreshMenu).not.toHaveBeenCalled(); root.appendChild(element); await import('@typo3/backend/module-menu'); - await new Promise((resolve) => setTimeout(resolve, 100)) + await new Promise((resolve) => setTimeout(resolve, 100)); expect(observer.refreshMenu).toHaveBeenCalled(); (viewportObject as any).App = backup; }); @@ -95,7 +95,7 @@ describe('TYPO3/CMS/Backend/Element/ImmediateActionElement:', () => { (moduleMenuApp as any).App = observer; root.innerHTML = '<typo3-immediate-action action="TYPO3.ModuleMenu.App.refreshMenu"></typo3-immediate-action>'; await import('@typo3/backend/module-menu'); - await new Promise((resolve) => setTimeout(resolve, 100)) + await new Promise((resolve) => setTimeout(resolve, 100)); expect(observer.refreshMenu).toHaveBeenCalled(); (moduleMenuApp as any).App = backup; }); diff --git a/Build/Sources/TypeScript/backend/tests/form-engine-validation-test.ts b/Build/Sources/TypeScript/backend/tests/form-engine-validation-test.ts index 5e0491ea304e..09fe02e18d64 100644 --- a/Build/Sources/TypeScript/backend/tests/form-engine-validation-test.ts +++ b/Build/Sources/TypeScript/backend/tests/form-engine-validation-test.ts @@ -1,4 +1,3 @@ -import $ from 'jquery'; import FormEngineValidation from '@typo3/backend/form-engine-validation'; declare function using(values: Function|Array<Object>|Object, func: Function): void; @@ -313,7 +312,7 @@ describe('TYPO3/CMS/Backend/FormEngineValidationTest:', () => { const baseTime = new Date(2013, 9, 23); jasmine.clock().mockDate(baseTime); expect(FormEngineValidation.getYear(baseTime)).toBe(2013); - }) + }); }); /** @@ -329,6 +328,6 @@ describe('TYPO3/CMS/Backend/FormEngineValidationTest:', () => { const baseTime = new Date(2013, 9, 23, 13, 13, 13); jasmine.clock().mockDate(baseTime); expect(FormEngineValidation.getDate(baseTime)).toBe(1382479200); - }) + }); }); }); diff --git a/Build/Sources/TypeScript/backend/tests/grid-editor-test.ts b/Build/Sources/TypeScript/backend/tests/grid-editor-test.ts index b11b6b9db685..131e6580c86e 100644 --- a/Build/Sources/TypeScript/backend/tests/grid-editor-test.ts +++ b/Build/Sources/TypeScript/backend/tests/grid-editor-test.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {GridEditor} from '@typo3/backend/grid-editor'; +import { GridEditor } from '@typo3/backend/grid-editor'; describe('TYPO3/CMS/Backend/GridEditorTest:', () => { diff --git a/Build/Sources/TypeScript/backend/tests/icons-test.ts b/Build/Sources/TypeScript/backend/tests/icons-test.ts index 942fbfe1ae92..8e7f76642a0c 100644 --- a/Build/Sources/TypeScript/backend/tests/icons-test.ts +++ b/Build/Sources/TypeScript/backend/tests/icons-test.ts @@ -1,4 +1,3 @@ -import $ from 'jquery'; import Icons from '@typo3/backend/icons'; describe('TYPO3/CMS/Backend/IconsTest:', () => { @@ -62,4 +61,4 @@ describe('TYPO3/CMS/Backend/IconsTest:', () => { }); }); }); -; + diff --git a/Build/Sources/TypeScript/backend/tests/notification-test.ts b/Build/Sources/TypeScript/backend/tests/notification-test.ts index 247ae95aab13..ce97b828c5a7 100644 --- a/Build/Sources/TypeScript/backend/tests/notification-test.ts +++ b/Build/Sources/TypeScript/backend/tests/notification-test.ts @@ -14,7 +14,7 @@ import DeferredAction from '@typo3/backend/action-button/deferred-action'; import ImmediateAction from '@typo3/backend/action-button/immediate-action'; import Notification from '@typo3/backend/notification'; -import type {LitElement} from 'lit'; +import type { LitElement } from 'lit'; import Icons from '@typo3/backend/icons'; describe('TYPO3/CMS/Backend/Notification:', () => { @@ -26,7 +26,7 @@ describe('TYPO3/CMS/Backend/Notification:', () => { spyOn(Icons, 'getIcon').and.callFake((): Promise<string> => { return Promise.resolve('X'); - }) + }); }); describe('can render notifications with dismiss after 1000ms', () => { @@ -72,7 +72,7 @@ describe('TYPO3/CMS/Backend/Notification:', () => { ]; } - for (let dataSet of notificationProvider()) { + for (const dataSet of notificationProvider()) { it('can render a notification of type ' + dataSet.class, async () => { dataSet.method(dataSet.title, dataSet.message, 1); @@ -97,13 +97,13 @@ describe('TYPO3/CMS/Backend/Notification:', () => { [ { label: 'My action', - action: new ImmediateAction((promise: Promise<any>): Promise<any> => { + action: new ImmediateAction((promise: Promise<void>): Promise<void> => { return promise; }), }, { label: 'My other action', - action: new DeferredAction((promise: Promise<any>): Promise<any> => { + action: new DeferredAction((promise: Promise<void>): Promise<void> => { return promise; }), }, diff --git a/Build/Sources/TypeScript/backend/tests/popover-test.ts b/Build/Sources/TypeScript/backend/tests/popover-test.ts index c34ad1ed00f8..f7d303ac579d 100644 --- a/Build/Sources/TypeScript/backend/tests/popover-test.ts +++ b/Build/Sources/TypeScript/backend/tests/popover-test.ts @@ -1,4 +1,4 @@ -import {Popover as BootstrapPopover} from 'bootstrap'; +import { Popover as BootstrapPopover } from 'bootstrap'; import Popover from '@typo3/backend/popover'; describe('TYPO3/CMS/Backend/PopoverTest:', () => { @@ -34,7 +34,7 @@ describe('TYPO3/CMS/Backend/PopoverTest:', () => { }); const element4 = document.createElement('div'); - element4.classList.add('t3js-popover') + element4.classList.add('t3js-popover'); document.body.append(element4); it('works with custom selector', () => { Popover.initialize('.t3js-popover'); diff --git a/Build/Sources/TypeScript/backend/toolbar.ts b/Build/Sources/TypeScript/backend/toolbar.ts index d2533945353b..a50f6c18f2bb 100644 --- a/Build/Sources/TypeScript/backend/toolbar.ts +++ b/Build/Sources/TypeScript/backend/toolbar.ts @@ -34,7 +34,7 @@ class Toolbar { new RegularEvent('click', (): void => { const scaffold = document.querySelector('.scaffold'); scaffold.classList.remove('scaffold-modulemenu-expanded', 'scaffold-toolbar-expanded'); - }).bindTo(document.querySelector('.t3js-topbar-button-search')) + }).bindTo(document.querySelector('.t3js-topbar-button-search')); } } diff --git a/Build/Sources/TypeScript/backend/toolbar/clear-cache-menu.ts b/Build/Sources/TypeScript/backend/toolbar/clear-cache-menu.ts index 6e0af22c4593..18fabbbe0ac7 100644 --- a/Build/Sources/TypeScript/backend/toolbar/clear-cache-menu.ts +++ b/Build/Sources/TypeScript/backend/toolbar/clear-cache-menu.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Icons from '../icons'; import Notification from '../notification'; @@ -42,12 +42,12 @@ class ClearCacheMenu { const toolbarItemContainer = document.querySelector(Identifiers.containerSelector); new RegularEvent('click', (e: Event, menuItem: HTMLAnchorElement): void => { - e.preventDefault() + e.preventDefault(); if (menuItem.href) { this.clearCache(menuItem.href); } }).delegateTo(toolbarItemContainer, Identifiers.menuItemSelector); - } + }; /** * Calls TYPO3 to clear a cache, then changes the topbar icon @@ -68,7 +68,7 @@ class ClearCacheMenu { }); (new AjaxRequest(ajaxUrl)).post({}).then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { Notification.success(data.title, data.message); diff --git a/Build/Sources/TypeScript/backend/toolbar/live-search.ts b/Build/Sources/TypeScript/backend/toolbar/live-search.ts index 511f3f5a638c..6add4599e150 100644 --- a/Build/Sources/TypeScript/backend/toolbar/live-search.ts +++ b/Build/Sources/TypeScript/backend/toolbar/live-search.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {lll} from '@typo3/core/lit-helper'; +import { lll } from '@typo3/core/lit-helper'; import Modal from '../modal'; import '@typo3/backend/element/icon-element'; import '@typo3/backend/input/clearable'; @@ -21,11 +21,11 @@ import '../live-search/live-search-shortcut'; import DocumentService from '@typo3/core/document-service'; import RegularEvent from '@typo3/core/event/regular-event'; import DebounceEvent from '@typo3/core/event/debounce-event'; -import {SeverityEnum} from '@typo3/backend/enum/severity'; +import { SeverityEnum } from '@typo3/backend/enum/severity'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import BrowserSession from '@typo3/backend/storage/browser-session'; -import {ResultContainer, componentName as resultContainerComponentName} from '@typo3/backend/live-search/element/result/result-container'; -import {ResultItemInterface} from '@typo3/backend/live-search/element/result/item/item'; +import { ResultContainer, componentName as resultContainerComponentName } from '@typo3/backend/live-search/element/result/result-container'; +import { ResultItemInterface } from '@typo3/backend/live-search/element/result/item/item'; enum Identifiers { toolbarItem = '.t3js-topbar-button-search', @@ -72,12 +72,12 @@ class LiveSearch { .map((item: [string, string]): SearchOption => { const trimmedKey = item[0].replace('livesearch-option-', ''); const [key, value] = trimmedKey.split('-', 2); - return {key, value} + return { key, value }; }); const searchOptions = this.composeSearchOptions(persistedSearchOptions); for (const [optionKey, optionValues] of Object.entries(searchOptions)) { - for (let optionValue of optionValues) { + for (const optionValue of optionValues) { url.searchParams.append(`${optionKey}[]`, optionValue); } } @@ -91,7 +91,7 @@ class LiveSearch { }); modal.addEventListener('typo3-modal-shown', () => { - const liveSearchContainer = modal.querySelector('typo3-backend-live-search') + const liveSearchContainer = modal.querySelector('typo3-backend-live-search'); const searchField = liveSearchContainer.querySelector('input[type="search"]') as HTMLInputElement; const searchForm = searchField.closest('form'); @@ -104,7 +104,7 @@ class LiveSearch { BrowserSession.set('livesearch-term', query); }); const optionCounterElement = searchForm.querySelector('[data-active-options-counter]') as HTMLElement; - let count = parseInt(optionCounterElement.dataset.activeOptionsCounter, 10); + const count = parseInt(optionCounterElement.dataset.activeOptionsCounter, 10); optionCounterElement.querySelector('output').textContent = count.toString(10); optionCounterElement.classList.toggle('hidden', count === 0); }).bindTo(searchForm); @@ -171,7 +171,7 @@ class LiveSearch { } this.updateSearchResults(resultSet); - } + }; private handleKeyDown(e: KeyboardEvent): void { if (e.key !== 'ArrowDown') { diff --git a/Build/Sources/TypeScript/backend/toolbar/shortcut-menu.ts b/Build/Sources/TypeScript/backend/toolbar/shortcut-menu.ts index 8c780e54cad1..23b744c0d499 100644 --- a/Build/Sources/TypeScript/backend/toolbar/shortcut-menu.ts +++ b/Build/Sources/TypeScript/backend/toolbar/shortcut-menu.ts @@ -11,16 +11,16 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Icons from '../icons'; import Modal from '../modal'; import Notification from '../notification'; import Viewport from '../viewport'; import SecurityUtility from '@typo3/core/security-utility'; -import {ModuleStateStorage} from '@typo3/backend/storage/module-state-storage'; +import { ModuleStateStorage } from '@typo3/backend/storage/module-state-storage'; import '@typo3/backend/element/spinner-element'; -import {Sizes} from '../enum/icon-types'; +import { Sizes } from '../enum/icon-types'; import RegularEvent from '@typo3/core/event/regular-event'; enum Identifiers { @@ -139,10 +139,10 @@ class ShortcutMenu { ModuleStateStorage.updateWithCurrentMount('web', pageId, true); } const router = document.querySelector('typo3-backend-module-router'); - router.setAttribute('endpoint', target.href) + router.setAttribute('endpoint', target.href); router.setAttribute('module', target.dataset.module); }).delegateTo(containerSelector, Identifiers.shortcutJumpSelector); - } + }; /** * Removes an existing short by sending an AJAX call @@ -150,7 +150,7 @@ class ShortcutMenu { * @param shortcutId number */ private deleteShortcut(shortcutId: number): void { - const modal = Modal.confirm(TYPO3.lang['bookmark.delete'], TYPO3.lang['bookmark.confirmDelete']) + const modal = Modal.confirm(TYPO3.lang['bookmark.delete'], TYPO3.lang['bookmark.confirmDelete']); modal.addEventListener('confirm.button.ok', (): void => { (new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_remove)).post({ shortcutId, @@ -175,7 +175,7 @@ class ShortcutMenu { (new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_editform)).withQueryArguments({ shortcutId, shortcutGroup, - }).get({cache: 'no-cache'}).then(async (response: AjaxResponse): Promise<any> => { + }).get({ cache: 'no-cache' }).then(async (response: AjaxResponse): Promise<void> => { document.querySelector(Identifiers.containerSelector + ' ' + Identifiers.toolbarMenuSelector).innerHTML = await response.resolve(); }); } @@ -201,7 +201,7 @@ class ShortcutMenu { spinner.setAttribute('size', Sizes.small); toolbarItemIcon.replaceWith(spinner); - (new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_list)).get({cache: 'no-cache'}).then(async (response: AjaxResponse): Promise<any> => { + (new AjaxRequest(TYPO3.settings.ajaxUrls.shortcut_list)).get({ cache: 'no-cache' }).then(async (response: AjaxResponse): Promise<void> => { document.querySelector(Identifiers.containerSelector + ' ' + Identifiers.toolbarMenuSelector).innerHTML = await response.resolve(); }).finally((): void => { document.querySelector(Identifiers.containerSelector + ' typo3-backend-spinner').replaceWith(existingIcon); diff --git a/Build/Sources/TypeScript/backend/toolbar/system-information-menu.ts b/Build/Sources/TypeScript/backend/toolbar/system-information-menu.ts index bccd9fafb47b..5825cbc18ad4 100644 --- a/Build/Sources/TypeScript/backend/toolbar/system-information-menu.ts +++ b/Build/Sources/TypeScript/backend/toolbar/system-information-menu.ts @@ -12,7 +12,7 @@ */ import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import RegularEvent from '@typo3/core/event/regular-event'; import Icons from '../icons'; import PersistentStorage from '../storage/persistent'; @@ -50,8 +50,14 @@ interface SystemInformationMessageData { class SystemInformationMenu { private timer: number = null; + constructor() { + new RegularEvent('click', this.handleMessageLinkClick) + .delegateTo(document, SystemInformationSelector.messageLink); + Viewport.Topbar.Toolbar.registerEvent(this.updateMenu); + } + private static getData(): SystemInformationData { - const element = document.querySelector(SystemInformationSelector.data) as HTMLElement + const element = document.querySelector(SystemInformationSelector.data) as HTMLElement; const data: DOMStringMap = element.dataset; return { count: data.systeminformationDataCount ? parseInt(data.systeminformationDataCount, 10) : 0, @@ -86,12 +92,6 @@ class SystemInformationMenu { element.classList.toggle('hidden', !(data.count > 0)); } - constructor() { - new RegularEvent('click', this.handleMessageLinkClick) - .delegateTo(document, SystemInformationSelector.messageLink); - Viewport.Topbar.Toolbar.registerEvent(this.updateMenu); - } - private updateMenu = (): void => { const toolbarItemIcon = document.querySelector(SystemInformationSelector.icon); const currentIcon = toolbarItemIcon.cloneNode(true); @@ -105,7 +105,7 @@ class SystemInformationMenu { toolbarItemIcon.replaceWith(document.createRange().createContextualFragment(spinner)); }); - (new AjaxRequest(TYPO3.settings.ajaxUrls.systeminformation_render)).get().then(async (response: AjaxResponse): Promise<any> => { + (new AjaxRequest(TYPO3.settings.ajaxUrls.systeminformation_render)).get().then(async (response: AjaxResponse): Promise<void> => { document.querySelector(SystemInformationSelector.menu).innerHTML = await response.resolve(); SystemInformationMenu.updateBadge(); }).finally((): void => { @@ -113,7 +113,7 @@ class SystemInformationMenu { // reload error data every five minutes this.timer = setTimeout(this.updateMenu, 1000 * 300); }); - } + }; /** * Updates the UC and opens the linked module diff --git a/Build/Sources/TypeScript/backend/tooltip.ts b/Build/Sources/TypeScript/backend/tooltip.ts index 0935b22b8a04..91ba0167e067 100644 --- a/Build/Sources/TypeScript/backend/tooltip.ts +++ b/Build/Sources/TypeScript/backend/tooltip.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {Tooltip as BootstrapTooltip} from 'bootstrap'; +import { Tooltip as BootstrapTooltip } from 'bootstrap'; import DocumentService from '@typo3/core/document-service'; /** @@ -22,12 +22,6 @@ import DocumentService from '@typo3/core/document-service'; * @deprecated bootstrap tooltip has been deprecated since TYPO3 v12 and will be removed with v13. */ class Tooltip { - private static applyAttributes(attributes: { [key: string]: string }, node: HTMLElement): void { - for (const [attribute, value] of Object.entries(attributes)) { - node.setAttribute(attribute, value); - } - } - constructor() { DocumentService.ready().then((): void => { console.warn('Tooltip has been deprecated since TYPO3 v12 and will be removed with v13. Rely on browser title instead.'); @@ -35,6 +29,12 @@ class Tooltip { }); } + private static applyAttributes(attributes: Record<string, string>, node: HTMLElement): void { + for (const [attribute, value] of Object.entries(attributes)) { + node.setAttribute(attribute, value); + } + } + public initialize(selector: string, options: Partial<BootstrapTooltip.Options> = {}): void { if (Object.entries(options).length === 0) { options = { @@ -44,7 +44,7 @@ class Tooltip { show: 500, hide: 100 } - } + }; } const elements = document.querySelectorAll(selector); for (const element of elements) { diff --git a/Build/Sources/TypeScript/backend/tree/drag-drop.ts b/Build/Sources/TypeScript/backend/tree/drag-drop.ts index e6afaa7ecb86..545086908c97 100644 --- a/Build/Sources/TypeScript/backend/tree/drag-drop.ts +++ b/Build/Sources/TypeScript/backend/tree/drag-drop.ts @@ -11,11 +11,11 @@ * The TYPO3 project - inspiring people to share! */ -import {html, TemplateResult} from 'lit'; -import {renderNodes} from '@typo3/core/lit-helper'; +import { html, TemplateResult } from 'lit'; +import { renderNodes } from '@typo3/core/lit-helper'; import * as d3drag from 'd3-drag'; -import {SvgTree} from '../svg-tree'; -import {TreeNode} from '@typo3/backend/tree/tree-node'; +import { SvgTree } from '../svg-tree'; +import { TreeNode } from '@typo3/backend/tree/tree-node'; /** * Contains basic types for allowing dragging + dropping in trees @@ -69,6 +69,10 @@ export class DragDrop { private timeout: any = {}; private minimalDistance: number = 10; + constructor(svgTree: SvgTree) { + this.tree = svgTree; + } + public static setDragStart(): void { document.querySelectorAll('iframe').forEach((htmlElement: HTMLIFrameElement) => htmlElement.style.pointerEvents = 'none' ); } @@ -77,10 +81,6 @@ export class DragDrop { document.querySelectorAll('iframe').forEach((htmlElement: HTMLIFrameElement) => htmlElement.style.pointerEvents = '' ); } - constructor(svgTree: SvgTree) { - this.tree = svgTree; - } - /** * Creates a new drag instance and initializes the clickDistance setting to * prevent clicks from being wrongly detected as drag attempts. @@ -92,7 +92,7 @@ export class DragDrop { .clickDistance(5) .on('start', function(evt: d3drag.D3DragEvent<any, any, any>) { dragHandler.onDragStart(evt.sourceEvent, evt.subject) && DragDrop.setDragStart(); }) .on('drag', function(evt: d3drag.D3DragEvent<any, any, any>) { dragHandler.onDragOver(evt.sourceEvent, evt.subject); }) - .on('end', function(evt: d3drag.D3DragEvent<any, any, any>) { DragDrop.setDragEnd(); dragHandler.onDrop(evt.sourceEvent, evt.subject); }) + .on('end', function(evt: d3drag.D3DragEvent<any, any, any>) { DragDrop.setDragEnd(); dragHandler.onDrop(evt.sourceEvent, evt.subject); }); } /** @@ -100,7 +100,7 @@ export class DragDrop { */ public createDraggable(icon: string, name: string) { - let svg = this.tree.svg.node() as SVGElement; + const svg = this.tree.svg.node() as SVGElement; const draggable = renderNodes(DraggableTemplate.get(icon, name)); svg.after(...draggable); this.tree.svg.node().querySelector('.nodes-wrapper')?.classList.add('nodes-wrapper--dragging'); @@ -122,7 +122,7 @@ export class DragDrop { */ public getDraggable(): HTMLElement|null { - let draggable = this.tree.svg.node().parentNode.querySelector('.node-dd') as HTMLElement; + const draggable = this.tree.svg.node().parentNode.querySelector('.node-dd') as HTMLElement; return draggable || null; } @@ -194,7 +194,7 @@ export class DragDrop { */ public createPositioningLine(): void { - let nodeBgBorder = this.tree.nodesBgContainer.selectAll('.node-bg__border'); + const nodeBgBorder = this.tree.nodesBgContainer.selectAll('.node-bg__border'); if (nodeBgBorder.empty()) { this.tree.nodesBgContainer .append('rect') diff --git a/Build/Sources/TypeScript/backend/tree/file-storage-browser.ts b/Build/Sources/TypeScript/backend/tree/file-storage-browser.ts index d584a45f3082..465505d08a37 100644 --- a/Build/Sources/TypeScript/backend/tree/file-storage-browser.ts +++ b/Build/Sources/TypeScript/backend/tree/file-storage-browser.ts @@ -11,17 +11,17 @@ * The TYPO3 project - inspiring people to share! */ -import {html, LitElement, TemplateResult} from 'lit'; -import {customElement, query} from 'lit/decorators'; +import { html, LitElement, TemplateResult } from 'lit'; +import { customElement, query } from 'lit/decorators'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {TreeNode} from './tree-node'; -import {Toolbar, TreeNodeSelection} from '../svg-tree'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { TreeNode } from './tree-node'; +import { Toolbar, TreeNodeSelection } from '../svg-tree'; import ElementBrowser from '@typo3/backend/element-browser'; import LinkBrowser from '@typo3/backend/link-browser'; import '@typo3/backend/element/icon-element'; import Persistent from '@typo3/backend/storage/persistent'; -import {FileStorageTree} from './file-storage-tree'; +import { FileStorageTree } from './file-storage-tree'; const componentName: string = 'typo3-backend-component-filestorage-browser'; @@ -79,7 +79,7 @@ class FileStorageBrowserTree extends FileStorageTree { export class FileStorageBrowser extends LitElement { @query('.svg-tree-wrapper') tree: FileStorageBrowserTree; - private activeFolder: String = ''; + private activeFolder: string = ''; private actions: Array<string> = []; public connectedCallback(): void { @@ -103,7 +103,7 @@ export class FileStorageBrowser extends LitElement { protected triggerRender = (): void => { this.tree.dispatchEvent(new Event('svg-tree:visible')); - } + }; protected render(): TemplateResult { if (this.hasAttribute('tree-actions') && this.getAttribute('tree-actions').length) { @@ -124,7 +124,7 @@ export class FileStorageBrowser extends LitElement { // set up toolbar now with updated properties const toolbar = this.querySelector('typo3-backend-tree-toolbar') as Toolbar; toolbar.tree = this.tree; - } + }; return html` <div class="svg-tree"> @@ -143,21 +143,21 @@ export class FileStorageBrowser extends LitElement { private selectActiveNode = (evt: CustomEvent): void => { // Activate the current node - let nodes = evt.detail.nodes as Array<TreeNode>; + const nodes = evt.detail.nodes as Array<TreeNode>; evt.detail.nodes = nodes.map((node: TreeNode) => { if (decodeURIComponent(node.identifier) === this.activeFolder) { node.checked = true; } return node; }); - } + }; private toggleExpandState = (evt: CustomEvent): void => { const node = evt.detail.node as TreeNode; if (node) { Persistent.set('BackendComponents.States.FileStorageTree.stateHash.' + node.stateIdentifier, (node.expanded ? '1' : '0')); } - } + }; /** * If a page is clicked, the content area needs to be updated @@ -167,12 +167,12 @@ export class FileStorageBrowser extends LitElement { if (!node.checked) { return; } - let contentsUrl = document.location.href + '&contentOnly=1&expandFolder=' + node.identifier; + const contentsUrl = document.location.href + '&contentOnly=1&expandFolder=' + node.identifier; (new AjaxRequest(contentsUrl)).get() .then((response: AjaxResponse) => response.resolve()) .then((response) => { const contentContainer = document.querySelector('.element-browser-main-content .element-browser-body') as HTMLElement; contentContainer.innerHTML = response; }); - } + }; } diff --git a/Build/Sources/TypeScript/backend/tree/file-storage-tree-container.ts b/Build/Sources/TypeScript/backend/tree/file-storage-tree-container.ts index 70e530425aeb..9460d7b5c0c9 100644 --- a/Build/Sources/TypeScript/backend/tree/file-storage-tree-container.ts +++ b/Build/Sources/TypeScript/backend/tree/file-storage-tree-container.ts @@ -75,7 +75,7 @@ class EditableFileStorageTree extends FileStorageTree { } e.preventDefault(); - } + }; private handleDrop = (event: DragEvent): void => { const target = event.target as Element; @@ -102,7 +102,7 @@ class EditableFileStorageTree extends FileStorageTree { this.actionHandler.initiateDropAction(fileOperationCollection); } event.preventDefault(); - } + }; /** * Initializes a drag&drop when called on the tree. @@ -172,30 +172,30 @@ export class FileStorageTreeNavigationComponent extends LitElement { private refresh = (): void => { this.tree.refreshOrFilterTree(); - } + }; private selectFirstNode = (): void => { const node = this.tree.nodes[0]; if (node) { this.tree.selectNode(node, true); } - } + }; // event listener updating current tree state, this can be removed in TYPO3 v12 private treeUpdateRequested = (evt: CustomEvent): void => { const identifier = encodeURIComponent(evt.detail.payload.identifier); - let nodeToSelect = this.tree.nodes.filter((node: TreeNode) => { return node.identifier === identifier })[0]; + const nodeToSelect = this.tree.nodes.filter((node: TreeNode) => { return node.identifier === identifier; })[0]; if (nodeToSelect && this.tree.getSelectedNodes().filter((selectedNode: TreeNode) => { return selectedNode.identifier === nodeToSelect.identifier; }).length === 0) { this.tree.selectNode(nodeToSelect, false); } - } + }; private toggleExpandState = (evt: CustomEvent): void => { const node = evt.detail.node as TreeNode; if (node) { Persistent.set('BackendComponents.States.FileStorageTree.stateHash.' + node.stateIdentifier, (node.expanded ? '1' : '0')); } - } + }; private loadContent = (evt: CustomEvent): void => { const node = evt.detail.node as TreeNode; @@ -215,7 +215,7 @@ export class FileStorageTreeNavigationComponent extends LitElement { let contentUrl = ModuleUtility.getFromName(moduleMenu.getCurrentModule()).link; contentUrl += contentUrl.includes('?') ? '&' : '?'; top.TYPO3.Backend.ContentContainer.setUrl(contentUrl + 'id=' + node.identifier); - } + }; private showContextMenu = (evt: CustomEvent): void => { const node = evt.detail.node as TreeNode; @@ -230,7 +230,7 @@ export class FileStorageTreeNavigationComponent extends LitElement { '', this.tree.getElementFromNode(node) ); - } + }; /** * Event listener called for each loaded node, @@ -238,14 +238,14 @@ export class FileStorageTreeNavigationComponent extends LitElement { */ private selectActiveNode = (evt: CustomEvent): void => { const selectedNodeIdentifier = ModuleStateStorage.current('file').selection; - let nodes = evt.detail.nodes as Array<TreeNode>; + const nodes = evt.detail.nodes as Array<TreeNode>; evt.detail.nodes = nodes.map((node: TreeNode) => { if (node.identifier === selectedNodeIdentifier) { node.checked = true; } return node; }); - } + }; } interface NodePositionOptions { @@ -300,7 +300,7 @@ class FileStorageTreeActions extends DragDrop { identifier: identifier, // dragged node id target: target, // hovered node position: position // before, in, after - } + }; } /** @@ -412,7 +412,7 @@ class FileStorageTreeNodeDragHandler implements DragDropHandler { this.startPageY = event.pageY; this.dragStarted = false; return true; - }; + } public onDragOver(event: MouseEvent, draggingNode: TreeNode | null): boolean { if (this.actionHandler.isDragNodeDistanceMore(event, this)) { @@ -445,7 +445,7 @@ class FileStorageTreeNodeDragHandler implements DragDropHandler { this.actionHandler.cleanupDrop(); if (this.actionHandler.isDropAllowed(this.tree.hoveredNode, draggingNode)) { - let options = this.actionHandler.getDropCommandDetails(this.tree.hoveredNode, draggingNode); + const options = this.actionHandler.getDropCommandDetails(this.tree.hoveredNode, draggingNode); if (options === null) { return false; } @@ -500,6 +500,11 @@ class FileResource extends Resource { } class FileOperationCollection { + protected constructor( + public readonly operations: FileOperation[], + public readonly target: ResourceInterface, + ) { + } public static fromDataTransfer(dataTransfer: DataTransfer, target: ResourceInterface): FileOperationCollection { return FileOperationCollection.fromArray(JSON.parse(dataTransfer.getData('application/json')), target); @@ -508,8 +513,8 @@ class FileOperationCollection { public static fromArray(items: ResourceInterface[], target: ResourceInterface): FileOperationCollection { const operations: FileOperation[] = []; - for (let item of items) { - operations.push(new FileOperation(item, DraggablePositionEnum.INSIDE)) + for (const item of items) { + operations.push(new FileOperation(item, DraggablePositionEnum.INSIDE)); } return new FileOperationCollection(operations, target); @@ -528,12 +533,6 @@ class FileOperationCollection { return new FileOperationCollection(operations, targetResource); } - protected constructor( - public readonly operations: FileOperation[], - public readonly target: ResourceInterface, - ) { - } - public getConflictingOperationsForTreeNode(node: TreeNode): FileOperation[] { return this.operations.filter((operation: FileOperation) => operation.hasConflictWithTreeNode(node)); } diff --git a/Build/Sources/TypeScript/backend/tree/file-storage-tree.ts b/Build/Sources/TypeScript/backend/tree/file-storage-tree.ts index b1742e08e8b5..2d2cd9548816 100644 --- a/Build/Sources/TypeScript/backend/tree/file-storage-tree.ts +++ b/Build/Sources/TypeScript/backend/tree/file-storage-tree.ts @@ -12,9 +12,9 @@ */ import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {SvgTree} from '../svg-tree'; -import {TreeNode} from '../tree/tree-node'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { SvgTree } from '../svg-tree'; +import { TreeNode } from '../tree/tree-node'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; /** * A tree for folders / storages @@ -61,10 +61,10 @@ export class FileStorageTree extends SvgTree { this.nodesAddPlaceholder(); (new AjaxRequest(this.settings.dataUrl + '&parent=' + parentNode.identifier + '¤tDepth=' + parentNode.depth)) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then((response: AjaxResponse) => response.resolve()) .then((json: any) => { - let nodes = Array.isArray(json) ? json : []; + const nodes = Array.isArray(json) ? json : []; const index = this.nodes.indexOf(parentNode) + 1; //adding fetched node after parent nodes.forEach((node: TreeNode, offset: number) => { diff --git a/Build/Sources/TypeScript/backend/tree/page-browser.ts b/Build/Sources/TypeScript/backend/tree/page-browser.ts index 3fba57a7ea61..4f4f66d754af 100644 --- a/Build/Sources/TypeScript/backend/tree/page-browser.ts +++ b/Build/Sources/TypeScript/backend/tree/page-browser.ts @@ -11,15 +11,15 @@ * The TYPO3 project - inspiring people to share! */ -import {html, LitElement, nothing, TemplateResult} from 'lit'; -import {customElement, property, query} from 'lit/decorators'; -import {until} from 'lit/directives/until'; -import {lll} from '@typo3/core/lit-helper'; -import {PageTree} from '../page-tree/page-tree'; +import { html, LitElement, nothing, TemplateResult } from 'lit'; +import { customElement, property, query } from 'lit/decorators'; +import { until } from 'lit/directives/until'; +import { lll } from '@typo3/core/lit-helper'; +import { PageTree } from '../page-tree/page-tree'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {TreeNode} from './tree-node'; -import {TreeNodeSelection, Toolbar} from '../svg-tree'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { TreeNode } from './tree-node'; +import { TreeNodeSelection, Toolbar } from '../svg-tree'; import ElementBrowser from '@typo3/backend/element-browser'; import LinkBrowser from '@typo3/backend/link-browser'; import '@typo3/backend/element/icon-element'; @@ -61,7 +61,7 @@ class PageBrowserTree extends PageTree { const linkAction = this.nodesActionsContainer.selectAll('.node-action') .append('g') .attr('visibility', (node: TreeNode) => { - return this.isLinkable(node) ? 'visible' : 'hidden' + return this.isLinkable(node) ? 'visible' : 'hidden'; }) .on('click', (evt: MouseEvent, node: TreeNode) => { this.linkItem(node); @@ -116,7 +116,7 @@ class PageBrowserTree extends PageTree { */ @customElement(componentName) export class PageBrowser extends LitElement { - @property({type: String}) mountPointPath: string = null; + @property({ type: String }) mountPointPath: string = null; @query('.svg-tree-wrapper') tree: PageBrowserTree; private activePageId: number = 0; @@ -148,7 +148,7 @@ export class PageBrowser extends LitElement { protected triggerRender = (): void => { this.tree.dispatchEvent(new Event('svg-tree:visible')); - } + }; protected getConfiguration(): Promise<Configuration> { if (this.configuration !== null) { @@ -159,7 +159,7 @@ export class PageBrowser extends LitElement { const alternativeEntryPoints = this.hasAttribute('alternative-entry-points') ? JSON.parse(this.getAttribute('alternative-entry-points')) : []; let request = new AjaxRequest(configurationUrl); if (alternativeEntryPoints.length) { - request = request.withQueryArguments('alternativeEntryPoints=' + encodeURIComponent(alternativeEntryPoints)) + request = request.withQueryArguments('alternativeEntryPoints=' + encodeURIComponent(alternativeEntryPoints)); } return request.get() .then(async (response: AjaxResponse): Promise<Configuration> => { @@ -190,7 +190,7 @@ export class PageBrowser extends LitElement { // set up toolbar now with updated properties const toolbar = this.querySelector('typo3-backend-tree-toolbar') as Toolbar; toolbar.tree = this.tree; - } + }; return html` <div> @@ -215,21 +215,21 @@ export class PageBrowser extends LitElement { private selectActivePageInTree = (evt: CustomEvent): void => { // Activate the current node - let nodes = evt.detail.nodes as Array<TreeNode>; + const nodes = evt.detail.nodes as Array<TreeNode>; evt.detail.nodes = nodes.map((node: TreeNode) => { if (parseInt(node.identifier, 10) === this.activePageId) { node.checked = true; } return node; }); - } + }; private toggleExpandState = (evt: CustomEvent): void => { const node = evt.detail.node as TreeNode; if (node) { Persistent.set('BackendComponents.States.Pagetree.stateHash.' + node.stateIdentifier, (node.expanded ? '1' : '0')); } - } + }; /** * If a page is clicked, the content area needs to be updated */ @@ -238,19 +238,19 @@ export class PageBrowser extends LitElement { if (!node.checked) { return; } - let contentsUrl = document.location.href + '&contentOnly=1&expandPage=' + node.identifier; + const contentsUrl = document.location.href + '&contentOnly=1&expandPage=' + node.identifier; (new AjaxRequest(contentsUrl)).get() .then((response: AjaxResponse) => response.resolve()) .then((response) => { const contentContainer = document.querySelector('.element-browser-main-content .element-browser-body') as HTMLElement; contentContainer.innerHTML = response; }); - } + }; private setMountPoint = (e: CustomEvent): void => { this.setTemporaryMountPoint(e.detail.pageId as number); - } + }; private unsetTemporaryMountPoint() { this.mountPointPath = null; @@ -277,7 +277,7 @@ export class PageBrowser extends LitElement { private setTemporaryMountPoint(pid: number): void { (new AjaxRequest(this.configuration.setTemporaryMountPointUrl)) .post('pid=' + pid, { - headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-Requested-With': 'XMLHttpRequest'}, + headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-Requested-With': 'XMLHttpRequest' }, }) .then((response) => response.resolve()) .then((response) => { diff --git a/Build/Sources/TypeScript/backend/tree/tree-node.ts b/Build/Sources/TypeScript/backend/tree/tree-node.ts index 0fa336a4e875..166322f2e9a9 100644 --- a/Build/Sources/TypeScript/backend/tree/tree-node.ts +++ b/Build/Sources/TypeScript/backend/tree/tree-node.ts @@ -12,7 +12,7 @@ */ import * as d3selection from 'd3-selection'; -import {DraggablePositionEnum} from './drag-drop'; +import { DraggablePositionEnum } from './drag-drop'; /** * Represents a single node in the SVG tree that is rendered. diff --git a/Build/Sources/TypeScript/backend/url-link-handler.ts b/Build/Sources/TypeScript/backend/url-link-handler.ts index f0e69afcf1e2..8612f21cda40 100644 --- a/Build/Sources/TypeScript/backend/url-link-handler.ts +++ b/Build/Sources/TypeScript/backend/url-link-handler.ts @@ -24,7 +24,7 @@ class UrlLinkHandler { new RegularEvent('submit', (evt: MouseEvent, targetEl: HTMLElement): void => { evt.preventDefault(); const inputField = targetEl.querySelector('[name="lurl"]') as HTMLInputElement; - let value = inputField.value.trim(); + const value = inputField.value.trim(); if (value === '') { return; } diff --git a/Build/Sources/TypeScript/backend/user-pass-login.ts b/Build/Sources/TypeScript/backend/user-pass-login.ts index bfd1cc659613..034aa627347d 100644 --- a/Build/Sources/TypeScript/backend/user-pass-login.ts +++ b/Build/Sources/TypeScript/backend/user-pass-login.ts @@ -20,40 +20,8 @@ import Login from './login'; * @exports @typo3/backend/user-pass-login */ class UserPassLogin { - protected options: any; - /** - * Checks whether capslock is enabled (returns TRUE if enabled, false otherwise) - * thanks to http://24ways.org/2007/capturing-caps-lock - * - * @param {Event} e - * @returns {boolean} - */ - public static isCapslockEnabled(e: any): boolean { - const ev = e ? e : window.event; - if (!ev) { - return false; - } - // get key pressed - let pressedKeyAsciiCode = -1; - if (ev.which) { - pressedKeyAsciiCode = ev.which; - } else if (ev.keyCode) { - pressedKeyAsciiCode = ev.keyCode; - } - // get shift status - let shiftPressed = false; - if (ev.shiftKey) { - shiftPressed = ev.shiftKey; - } else if (ev.modifiers) { - /* tslint:disable:no-bitwise */ - shiftPressed = !!(ev.modifiers & 4); - } - return (pressedKeyAsciiCode >= 65 && pressedKeyAsciiCode <= 90 && !shiftPressed) - || (pressedKeyAsciiCode >= 97 && pressedKeyAsciiCode <= 122 && shiftPressed); - } - constructor() { this.options = { passwordField: '.t3js-login-password-field', @@ -93,6 +61,38 @@ class UserPassLogin { } } + + /** + * Checks whether capslock is enabled (returns TRUE if enabled, false otherwise) + * thanks to http://24ways.org/2007/capturing-caps-lock + * + * @param {Event} e + * @returns {boolean} + */ + public static isCapslockEnabled(e: any): boolean { + const ev = e ? e : window.event; + if (!ev) { + return false; + } + // get key pressed + let pressedKeyAsciiCode = -1; + if (ev.which) { + pressedKeyAsciiCode = ev.which; + } else if (ev.keyCode) { + pressedKeyAsciiCode = ev.keyCode; + } + // get shift status + let shiftPressed = false; + if (ev.shiftKey) { + shiftPressed = ev.shiftKey; + } else if (ev.modifiers) { + /* tslint:disable:no-bitwise */ + shiftPressed = !!(ev.modifiers & 4); + } + return (pressedKeyAsciiCode >= 65 && pressedKeyAsciiCode <= 90 && !shiftPressed) + || (pressedKeyAsciiCode >= 97 && pressedKeyAsciiCode <= 122 && shiftPressed); + } + /** * Reset user password field to prevent it from being submitted */ @@ -102,7 +102,7 @@ class UserPassLogin { $(Login.options.useridentField).val($passwordField.val()); $passwordField.val(''); } - } + }; public showCapsLockWarning = (event: Event): void => { $(event.target) @@ -110,13 +110,13 @@ class UserPassLogin { .parent() .find('.t3js-login-alert-capslock') .toggleClass('hidden', !UserPassLogin.isCapslockEnabled(event)); - } + }; public toggleCopyright = (event: KeyboardEvent): void => { if (event.key === ' ') { (<HTMLLinkElement>(event.target)).click(); } - } + }; } export default new UserPassLogin(); diff --git a/Build/Sources/TypeScript/backend/utility.ts b/Build/Sources/TypeScript/backend/utility.ts index 0098d4d7eb0d..c4dfd2ada2a1 100644 --- a/Build/Sources/TypeScript/backend/utility.ts +++ b/Build/Sources/TypeScript/backend/utility.ts @@ -215,7 +215,7 @@ class Utility { */ private static isValidUrl(url: null|string): boolean { try { - new URL(url) + new URL(url); return true; } catch (e) { return false; diff --git a/Build/Sources/TypeScript/backend/utility/collapse-state-search.ts b/Build/Sources/TypeScript/backend/utility/collapse-state-search.ts index d88812ff4b03..f504d5f086fd 100644 --- a/Build/Sources/TypeScript/backend/utility/collapse-state-search.ts +++ b/Build/Sources/TypeScript/backend/utility/collapse-state-search.ts @@ -47,7 +47,7 @@ class CollapseStateSearch { this.numberOfSearchMatchesContainer = document.querySelectorAll('.t3js-collapse-states-search-numberOfSearchMatches'); this.searchField = document.querySelector(this.searchValueSelector); this.searchForm = this.searchField.closest('form'); - this.searchSessionKey = this.searchField.dataset.persistCollapseSearchKey + this.searchSessionKey = this.searchField.dataset.persistCollapseSearchKey; this.searchValue = Client.get(this.searchSessionKey) ?? ''; this.registerEvents(); @@ -117,13 +117,13 @@ class CollapseStateSearch { } const parentElements = this.parents(match, '.collapse'); - for (let parentEl of parentElements) { + for (const parentEl of parentElements) { matchingCollapsibleIds.add(parentEl.id); } }); const allNodes = Array.from(treeContainer.querySelectorAll('.collapse')) as HTMLElement[]; - for (let node of allNodes) { + for (const node of allNodes) { const isExpanded: boolean = node.classList.contains('show'); const id: string = node.id; if (matchingCollapsibleIds.has(id)) { diff --git a/Build/Sources/TypeScript/backend/utility/message-utility.ts b/Build/Sources/TypeScript/backend/utility/message-utility.ts index c3e2103b0e42..a63f122c30c9 100644 --- a/Build/Sources/TypeScript/backend/utility/message-utility.ts +++ b/Build/Sources/TypeScript/backend/utility/message-utility.ts @@ -12,8 +12,8 @@ */ interface Message { - actionName: string; [key: string]: any; + actionName: string; } export class MessageUtility { diff --git a/Build/Sources/TypeScript/backend/viewport/content-container.ts b/Build/Sources/TypeScript/backend/viewport/content-container.ts index 561293a1f520..0bb16d9147e9 100644 --- a/Build/Sources/TypeScript/backend/viewport/content-container.ts +++ b/Build/Sources/TypeScript/backend/viewport/content-container.ts @@ -11,8 +11,8 @@ * The TYPO3 project - inspiring people to share! */ -import {ScaffoldIdentifierEnum} from '../enum/viewport/scaffold-identifier'; -import {AbstractContainer} from './abstract-container'; +import { ScaffoldIdentifierEnum } from '../enum/viewport/scaffold-identifier'; +import { AbstractContainer } from './abstract-container'; import $ from 'jquery'; import ClientRequest from '../event/client-request'; import InteractionRequest from '../event/interaction-request'; diff --git a/Build/Sources/TypeScript/backend/viewport/loader.ts b/Build/Sources/TypeScript/backend/viewport/loader.ts index 3a381ab6be14..0645ff081fdc 100644 --- a/Build/Sources/TypeScript/backend/viewport/loader.ts +++ b/Build/Sources/TypeScript/backend/viewport/loader.ts @@ -11,12 +11,12 @@ * The TYPO3 project - inspiring people to share! */ -import {ScaffoldIdentifierEnum} from '../enum/viewport/scaffold-identifier'; +import { ScaffoldIdentifierEnum } from '../enum/viewport/scaffold-identifier'; import NProgress from 'nprogress'; class Loader { public static start(): void { - NProgress.configure({parent: ScaffoldIdentifierEnum.contentModule, showSpinner: false}); + NProgress.configure({ parent: ScaffoldIdentifierEnum.contentModule, showSpinner: false }); NProgress.start(); } diff --git a/Build/Sources/TypeScript/backend/viewport/navigation-container.ts b/Build/Sources/TypeScript/backend/viewport/navigation-container.ts index 7e5f84815243..b2829a678360 100644 --- a/Build/Sources/TypeScript/backend/viewport/navigation-container.ts +++ b/Build/Sources/TypeScript/backend/viewport/navigation-container.ts @@ -11,14 +11,19 @@ * The TYPO3 project - inspiring people to share! */ -import {ScaffoldIdentifierEnum} from '../enum/viewport/scaffold-identifier'; -import {AbstractContainer} from './abstract-container'; +import { ScaffoldIdentifierEnum } from '../enum/viewport/scaffold-identifier'; +import { AbstractContainer } from './abstract-container'; import TriggerRequest from '../event/trigger-request'; import InteractionRequest from '../event/interaction-request'; class NavigationContainer extends AbstractContainer { private activeComponentId: string = ''; + public constructor(consumerScope: any) + { + super(consumerScope); + } + private get parent(): HTMLElement { return document.querySelector(ScaffoldIdentifierEnum.scaffold); @@ -34,11 +39,6 @@ class NavigationContainer extends AbstractContainer { return document.querySelector(ScaffoldIdentifierEnum.contentNavigationSwitcher); } - public constructor(consumerScope: any) - { - super(consumerScope); - } - /** * Renders registered (non-iframe) navigation component e.g. a page tree * @@ -52,7 +52,7 @@ class NavigationContainer extends AbstractContainer { return; } if (this.activeComponentId !== '') { - let activeComponentElement = container.querySelector('#navigationComponent-' + this.activeComponentId.replace(/[/@]/g, '_')) as HTMLElement; + const activeComponentElement = container.querySelector('#navigationComponent-' + this.activeComponentId.replace(/[/@]/g, '_')) as HTMLElement; if (activeComponentElement) { activeComponentElement.style.display = 'none'; } @@ -84,9 +84,7 @@ class NavigationContainer extends AbstractContainer { ); // manual static initialize method, unused but kept for backwards-compatibility until TYPO3 v12 - // @ts-ignore const navigationComponent = Object.values(__esModule)[0] as any; - // @ts-ignore navigationComponent.initialize('#' + navigationComponentElement); } this.show(navigationComponentId); diff --git a/Build/Sources/TypeScript/backend/viewport/resizable-navigation.ts b/Build/Sources/TypeScript/backend/viewport/resizable-navigation.ts index 9e84d61b1f7d..093ad2745cc8 100644 --- a/Build/Sources/TypeScript/backend/viewport/resizable-navigation.ts +++ b/Build/Sources/TypeScript/backend/viewport/resizable-navigation.ts @@ -11,9 +11,9 @@ * The TYPO3 project - inspiring people to share! */ -import {html, LitElement, TemplateResult} from 'lit'; -import {customElement, property, state} from 'lit/decorators'; -import {lll} from '@typo3/core/lit-helper'; +import { html, LitElement, TemplateResult } from 'lit'; +import { customElement, property, state } from 'lit/decorators'; +import { lll } from '@typo3/core/lit-helper'; import Persistent from '../storage/persistent'; import '@typo3/backend/element/icon-element'; @@ -24,13 +24,13 @@ const selectorConverter = { }; @customElement('typo3-backend-navigation-switcher') -class ResizableNavigation extends LitElement { - @property({type: Number, attribute: 'minimum-width'}) minimumWidth: number = 250; - @property({type: Number, attribute: 'initial-width'}) initialWidth: number; - @property({type: String, attribute: 'persistence-identifier'}) persistenceIdentifier: string; +export class ResizableNavigation extends LitElement { + @property({ type: Number, attribute: 'minimum-width' }) minimumWidth: number = 250; + @property({ type: Number, attribute: 'initial-width' }) initialWidth: number; + @property({ type: String, attribute: 'persistence-identifier' }) persistenceIdentifier: string; - @property({attribute: 'parent', converter: selectorConverter}) parentContainer: HTMLElement; - @property({attribute: 'navigation', converter: selectorConverter}) navigationContainer: HTMLElement; + @property({ attribute: 'parent', converter: selectorConverter }) parentContainer: HTMLElement; + @property({ attribute: 'navigation', converter: selectorConverter }) navigationContainer: HTMLElement; @state() resizing: boolean = false; @@ -38,7 +38,7 @@ class ResizableNavigation extends LitElement { super.connectedCallback(); const initialWidth = this.initialWidth || parseInt(Persistent.get(this.persistenceIdentifier), 10); this.setNavigationWidth(initialWidth); - window.addEventListener('resize', this.fallbackNavigationSizeIfNeeded, {passive: true}); + window.addEventListener('resize', this.fallbackNavigationSizeIfNeeded, { passive: true }); } public disconnectedCallback(): void { @@ -56,8 +56,8 @@ class ResizableNavigation extends LitElement { await new Promise((r) => setTimeout(r, 0)); // needed to avoid any issues related to browsers, as lit-decorators (eventOptions) do not work yet // properly https://lit-element.polymer-project.org/guide/events - @touchstart would throw warnings in browser console without passive=true - this.querySelector('.scaffold-content-navigation-switcher-btn').addEventListener('touchstart', this.toggleNavigation, {passive: true}); - this.querySelector('.scaffold-content-navigation-drag').addEventListener('touchstart', this.startResizeNavigation, {passive: true}); + this.querySelector('.scaffold-content-navigation-switcher-btn').addEventListener('touchstart', this.toggleNavigation, { passive: true }); + this.querySelector('.scaffold-content-navigation-drag').addEventListener('touchstart', this.startResizeNavigation, { passive: true }); } protected render(): TemplateResult { @@ -80,30 +80,30 @@ class ResizableNavigation extends LitElement { } event.stopPropagation(); this.parentContainer.classList.toggle('scaffold-content-navigation-expanded'); - } + }; private fallbackNavigationSizeIfNeeded = (event: UIEvent) => { - let window = <Window>event.currentTarget; + const window = <Window>event.currentTarget; if (this.getNavigationWidth() === 0) { return; } if (window.outerWidth < this.getNavigationWidth() + this.getNavigationPosition().left + this.minimumWidth) { this.autoNavigationWidth(); } - } + }; private handleMouseMove = (event: MouseEvent) => { this.resizeNavigation(<number>event.clientX); - } + }; private handleTouchMove = (event: TouchEvent) => { this.resizeNavigation(<number>event.changedTouches[0].clientX); - } + }; private resizeNavigation = (position: number) => { - let width = Math.round(position) - Math.round(this.getNavigationPosition().left); + const width = Math.round(position) - Math.round(this.getNavigationPosition().left); this.setNavigationWidth(width); - } + }; private startResizeNavigation = (event: MouseEvent | TouchEvent) => { if (event instanceof MouseEvent && event.button === 2) { @@ -115,7 +115,7 @@ class ResizableNavigation extends LitElement { document.addEventListener('mouseup', this.stopResizeNavigation, false); document.addEventListener('touchmove', this.handleTouchMove, false); document.addEventListener('touchend', this.stopResizeNavigation, false); - } + }; private stopResizeNavigation = () => { this.resizing = false; @@ -125,7 +125,7 @@ class ResizableNavigation extends LitElement { document.removeEventListener('touchend', this.stopResizeNavigation, false); Persistent.set(this.persistenceIdentifier, <string><unknown>this.getNavigationWidth()); document.dispatchEvent(new CustomEvent('typo3:navigation:resized')); - } + }; private getNavigationPosition(): DOMRect { return this.navigationContainer.getBoundingClientRect(); diff --git a/Build/Sources/TypeScript/backend/viewport/toolbar.ts b/Build/Sources/TypeScript/backend/viewport/toolbar.ts index 07083e735bd5..e01655a2c724 100644 --- a/Build/Sources/TypeScript/backend/viewport/toolbar.ts +++ b/Build/Sources/TypeScript/backend/viewport/toolbar.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {ScaffoldIdentifierEnum} from '../enum/viewport/scaffold-identifier'; +import { ScaffoldIdentifierEnum } from '../enum/viewport/scaffold-identifier'; import $ from 'jquery'; class Toolbar { diff --git a/Build/Sources/TypeScript/backend/viewport/topbar.ts b/Build/Sources/TypeScript/backend/viewport/topbar.ts index 736aea9c0899..86295f682426 100644 --- a/Build/Sources/TypeScript/backend/viewport/topbar.ts +++ b/Build/Sources/TypeScript/backend/viewport/topbar.ts @@ -11,8 +11,8 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {ScaffoldIdentifierEnum} from '../enum/viewport/scaffold-identifier'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { ScaffoldIdentifierEnum } from '../enum/viewport/scaffold-identifier'; import Toolbar from './toolbar'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; diff --git a/Build/Sources/TypeScript/backend/window-manager.ts b/Build/Sources/TypeScript/backend/window-manager.ts index 08b706fd5a0f..01884e3906fc 100644 --- a/Build/Sources/TypeScript/backend/window-manager.ts +++ b/Build/Sources/TypeScript/backend/window-manager.ts @@ -20,9 +20,14 @@ class WindowManager { private windows: {[key: string]: Window} = {}; // alias for `localOpen` - public open = (...args: any[]): Window => this._localOpen.apply(this, args); + public open(...params: any[]): Window { + return this._localOpen.apply(null, params); + } + // @todo Not implemented, yet - public globalOpen = (...args: any[]): Window => this._localOpen.apply(this, args); + public globalOpen(...params: any[]): Window { + return this._localOpen.apply(null, params); + } public localOpen = (uri: string, switchFocus?: boolean, windowName: string = 'newTYPO3frontendWindow', windowFeatures: string = ''): Window | null => this._localOpen(uri, switchFocus, windowName, windowFeatures); diff --git a/Build/Sources/TypeScript/backend/wizard.ts b/Build/Sources/TypeScript/backend/wizard.ts index 52fffde31dee..81d0673ffc23 100644 --- a/Build/Sources/TypeScript/backend/wizard.ts +++ b/Build/Sources/TypeScript/backend/wizard.ts @@ -11,9 +11,9 @@ * The TYPO3 project - inspiring people to share! */ -import {SeverityEnum} from './enum/severity'; +import { SeverityEnum } from './enum/severity'; import $ from 'jquery'; -import {default as Modal, ModalElement} from './modal'; +import { default as Modal, ModalElement } from './modal'; import Severity from './severity'; import Icons from './icons'; @@ -86,7 +86,7 @@ class Wizard { } return Icons.getIcon('spinner-circle-dark', Icons.sizes.large, null, null).then((markup: string) => { - let $processingSlide = $('<div />', {class: 'text-center'}).append(markup); + const $processingSlide = $('<div />', { class: 'text-center' }).append(markup); this.addSlide( 'final-processing-slide', top.TYPO3.lang['wizard.processing.title'], $processingSlide[0].outerHTML, @@ -97,8 +97,8 @@ class Wizard { } public show(): void { - let $slides = this.generateSlides(); - let firstSlide = this.setup.slides[0]; + const $slides = this.generateSlides(); + const firstSlide = this.setup.slides[0]; const modal = Modal.advanced({ title: firstSlide.title, @@ -147,13 +147,13 @@ class Wizard { } public lockNextStep(): JQuery { - let $button = this.setup.$carousel.closest('.modal').find('button[name="next"]'); + const $button = this.setup.$carousel.closest('.modal').find('button[name="next"]'); $button.prop('disabled', true); return $button; } public unlockNextStep(): JQuery { - let $button = this.setup.$carousel.closest('.modal').find('button[name="next"]'); + const $button = this.setup.$carousel.closest('.modal').find('button[name="next"]'); $button.prop('disabled', false); return $button; } @@ -163,18 +163,18 @@ class Wizard { } private initializeEvents(modal: ModalElement): void { - let $modal = this.setup.$carousel.closest('.modal'); - let $modalTitle = $modal.find('.modal-title'); - let $modalFooter = $modal.find('.modal-footer'); - let $nextButton = $modalFooter.find('button[name="next"]'); + const $modal = this.setup.$carousel.closest('.modal'); + const $modalTitle = $modal.find('.modal-title'); + const $modalFooter = $modal.find('.modal-footer'); + const $nextButton = $modalFooter.find('button[name="next"]'); $nextButton.on('click', (): void => { this.setup.$carousel.carousel('next'); }); this.setup.$carousel.on('slide.bs.carousel', (): void => { - let nextSlideNumber = this.setup.$carousel.data('currentSlide') + 1; - let currentIndex = this.setup.$carousel.data('currentIndex') + 1; + const nextSlideNumber = this.setup.$carousel.data('currentSlide') + 1; + const currentIndex = this.setup.$carousel.data('currentIndex') + 1; $modalTitle.text(this.setup.slides[currentIndex].title); @@ -201,8 +201,8 @@ class Wizard { .removeClass('modal-severity-' + Severity.getCssClass(this.setup.slides[currentIndex - 1].severity)) .addClass('modal-severity-' + Severity.getCssClass(this.setup.slides[currentIndex].severity)); }).on('slid.bs.carousel', (evt: JQueryEventObject): void => { - let currentIndex = this.setup.$carousel.data('currentIndex'); - let slide = this.setup.slides[currentIndex]; + const currentIndex = this.setup.$carousel.data('currentIndex'); + const slide = this.setup.slides[currentIndex]; this.runSlideCallback(slide, $(evt.relatedTarget)); @@ -214,7 +214,7 @@ class Wizard { /** * Custom event, closes the wizard */ - let cmp = this.getComponent(); + const cmp = this.getComponent(); cmp.on('wizard-dismiss', this.dismiss); modal.addEventListener('typo3-modal-hidden', (): void => { @@ -232,13 +232,11 @@ class Wizard { } private addProgressBar(): void { - let realSlideCount = this.setup.$carousel.find('.carousel-item').length; - let slideCount = Math.max(1, realSlideCount); - let initialStep; - let $modal = this.setup.$carousel.closest('.modal'); - let $modalFooter = $modal.find('.modal-footer'); - - initialStep = Math.round(100 / slideCount); + const realSlideCount = this.setup.$carousel.find('.carousel-item').length; + const slideCount = Math.max(1, realSlideCount); + const initialStep = Math.round(100 / slideCount); + const $modal = this.setup.$carousel.closest('.modal'); + const $modalFooter = $modal.find('.modal-footer'); this.setup.$carousel .data('initialStep', initialStep) @@ -250,7 +248,7 @@ class Wizard { // Append progress bar to modal footer if (slideCount > 1) { $modalFooter.prepend( - $('<div />', {class: 'progress'}).append( + $('<div />', { class: 'progress' }).append( $('<div />', { role: 'progressbar', class: 'progress-bar', @@ -276,7 +274,7 @@ class Wizard { let slides = '<div class="carousel slide" data-bs-ride="false">' + '<div class="carousel-inner" role="listbox">'; - for (let currentSlide of Object.values(this.setup.slides)) { + for (const currentSlide of Object.values(this.setup.slides)) { let slideContent = currentSlide.content; if (typeof slideContent === 'object') { diff --git a/Build/Sources/TypeScript/belog/backend-log.ts b/Build/Sources/TypeScript/belog/backend-log.ts index 1f103c304ff0..60102c20f395 100644 --- a/Build/Sources/TypeScript/belog/backend-log.ts +++ b/Build/Sources/TypeScript/belog/backend-log.ts @@ -15,7 +15,7 @@ import Modal from '@typo3/backend/modal'; import DocumentService from '@typo3/core/document-service'; import DateTimePicker from '@typo3/backend/date-time-picker'; import '@typo3/backend/input/clearable'; -import {MessageUtility} from '@typo3/backend/utility/message-utility'; +import { MessageUtility } from '@typo3/backend/utility/message-utility'; /** * Module: @typo3/belog/backend-log @@ -25,7 +25,7 @@ import {MessageUtility} from '@typo3/backend/utility/message-utility'; class BackendLog { private clearableElements: NodeListOf<HTMLInputElement> = null; private dateTimePickerElements: NodeListOf<HTMLInputElement> = null; - private elementBrowserElements: NodeListOf<HTMLAnchorElement> = null + private elementBrowserElements: NodeListOf<HTMLAnchorElement> = null; constructor() { DocumentService.ready().then((): void => { diff --git a/Build/Sources/TypeScript/beuser/permissions.ts b/Build/Sources/TypeScript/beuser/permissions.ts index 598383b0f956..9a321c464f8b 100644 --- a/Build/Sources/TypeScript/beuser/permissions.ts +++ b/Build/Sources/TypeScript/beuser/permissions.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import RegularEvent from '@typo3/core/event/regular-event'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; @@ -27,12 +27,17 @@ class Permissions { private ajaxUrl: string = TYPO3.settings.ajaxUrls.user_access_permissions; + constructor() { + this.initializeCheckboxGroups(); + this.initializeEvents(); + } + /** * Changes the value of the permissions in the form */ private static setPermissionCheckboxes(checknames: string, permissionValue: number): void { const permissionCheckboxes: NodeListOf<HTMLInputElement> = document.querySelectorAll(`input[type="checkbox"][name^="${checknames}"]`); - for (let permissionCheckbox of permissionCheckboxes) { + for (const permissionCheckbox of permissionCheckboxes) { const value = parseInt(permissionCheckbox.value, 10); permissionCheckbox.checked = (permissionValue & value) === value; } @@ -44,23 +49,18 @@ class Permissions { private static updatePermissionValue(checknames: string, varname: string): void { let permissionValue = 0; const checkedPermissionCheckboxes: NodeListOf<HTMLInputElement> = document.querySelectorAll(`input[type="checkbox"][name^="${checknames}"]:checked`); - for (let permissionCheckbox of checkedPermissionCheckboxes) { + for (const permissionCheckbox of checkedPermissionCheckboxes) { permissionValue |= parseInt(permissionCheckbox.value, 10); } document.forms.namedItem('editform')[varname].value = permissionValue | (checknames === 'check[perms_user]' ? 1 : 0); } - constructor() { - this.initializeCheckboxGroups(); - this.initializeEvents(); - } - /** * Changes permissions by sending an AJAX request to the server */ private setPermissions(element: HTMLElement): void { - let page = element.dataset.page; - let who = element.dataset.who; + const page = element.dataset.page; + const who = element.dataset.who; (new AjaxRequest(this.ajaxUrl)).post({ page: page, @@ -80,7 +80,7 @@ class Permissions { * changes the flag to lock the editing on a page by sending an AJAX request */ private toggleEditLock(element: HTMLElement): void { - let page = element.dataset.page; + const page = element.dataset.page; (new AjaxRequest(this.ajaxUrl)).post({ action: 'toggle_edit_lock', page: page, @@ -95,7 +95,7 @@ class Permissions { * Owner-related: Set the new owner of a page by executing an ajax call */ private changeOwner(element: HTMLElement): void { - let page = element.dataset.page; + const page = element.dataset.page; const container: HTMLElement = document.getElementById('o_' + page); (new AjaxRequest(this.ajaxUrl)).post({ @@ -114,7 +114,7 @@ class Permissions { * the owner of a page by executing an ajax call */ private showChangeOwnerSelector(element: HTMLElement): void { - let page = element.dataset.page; + const page = element.dataset.page; (new AjaxRequest(this.ajaxUrl)).post({ action: 'show_change_owner_selector', @@ -177,7 +177,7 @@ class Permissions { * Group-related: Set the new group by executing an ajax call */ private changeGroup(element: HTMLElement): void { - let page = element.dataset.page; + const page = element.dataset.page; const container: HTMLElement = document.getElementById('g_' + page); (new AjaxRequest(this.ajaxUrl)).post({ @@ -195,7 +195,7 @@ class Permissions { * Group-related: Load the selector by executing an ajax call */ private showChangeGroupSelector(element: HTMLElement): void { - let page = element.dataset.page; + const page = element.dataset.page; (new AjaxRequest(this.ajaxUrl)).post({ action: 'show_change_group_selector', page: page, diff --git a/Build/Sources/TypeScript/core/ajax/ajax-request.ts b/Build/Sources/TypeScript/core/ajax/ajax-request.ts index 6151ab39fff6..4af27441f667 100644 --- a/Build/Sources/TypeScript/core/ajax/ajax-request.ts +++ b/Build/Sources/TypeScript/core/ajax/ajax-request.ts @@ -11,8 +11,8 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxResponse} from './ajax-response'; -import {GenericKeyValue, InputTransformer} from './input-transformer'; +import { AjaxResponse } from './ajax-response'; +import { GenericKeyValue, InputTransformer } from './input-transformer'; /** * @example send data as `Content-Type: multipart/form-data` (default) @@ -66,7 +66,7 @@ class AjaxRequest { method: 'GET', }; - const response = await this.send({...localDefaultOptions, ...init}); + const response = await this.send({ ...localDefaultOptions, ...init }); return new AjaxResponse(response); } @@ -84,7 +84,7 @@ class AjaxRequest { method: 'POST', }; - const response = await this.send({...localDefaultOptions, ...init}); + const response = await this.send({ ...localDefaultOptions, ...init }); return new AjaxResponse(response); } @@ -102,7 +102,7 @@ class AjaxRequest { method: 'PUT', }; - const response = await this.send({...localDefaultOptions, ...init}); + const response = await this.send({ ...localDefaultOptions, ...init }); return new AjaxResponse(response); } @@ -125,7 +125,7 @@ class AjaxRequest { localDefaultOptions.body = InputTransformer.byHeader(data, init?.headers); } - const response = await this.send({...localDefaultOptions, ...init}); + const response = await this.send({ ...localDefaultOptions, ...init }); return new AjaxResponse(response); } @@ -182,7 +182,7 @@ class AjaxRequest { * @return {RequestInit} */ private getMergedOptions(init: RequestInit): RequestInit { - return {...AjaxRequest.defaultOptions, ...init, signal: this.abortController.signal}; + return { ...AjaxRequest.defaultOptions, ...init, signal: this.abortController.signal }; } } diff --git a/Build/Sources/TypeScript/core/ajax/input-transformer.ts b/Build/Sources/TypeScript/core/ajax/input-transformer.ts index 46dc636551d2..d38f655947bf 100644 --- a/Build/Sources/TypeScript/core/ajax/input-transformer.ts +++ b/Build/Sources/TypeScript/core/ajax/input-transformer.ts @@ -21,7 +21,7 @@ export class InputTransformer { * @param headers */ public static byHeader(data: GenericKeyValue, headers: GenericKeyValue = {}): FormData | string { - if (headers.hasOwnProperty('Content-Type') && headers['Content-Type'].includes('application/json')) { + if ('Content-Type' in headers && headers['Content-Type'].includes('application/json')) { return JSON.stringify(data); } @@ -73,9 +73,9 @@ export class InputTransformer { const objPrefix = prefix.length ? prefix + '[' : ''; const objSuffix = prefix.length ? ']' : ''; if (typeof obj[currentValue] === 'object') { - Object.assign(accumulator, InputTransformer.flattenObject(obj[currentValue], objPrefix + currentValue + objSuffix)) + Object.assign(accumulator, InputTransformer.flattenObject(obj[currentValue], objPrefix + currentValue + objSuffix)); } else { - accumulator[objPrefix + currentValue + objSuffix] = obj[currentValue] + accumulator[objPrefix + currentValue + objSuffix] = obj[currentValue]; } return accumulator; }, {}); diff --git a/Build/Sources/TypeScript/core/ajax/simple-response-interface.ts b/Build/Sources/TypeScript/core/ajax/simple-response-interface.ts index 1cbade2fcb0e..cdc95f5d31d5 100644 --- a/Build/Sources/TypeScript/core/ajax/simple-response-interface.ts +++ b/Build/Sources/TypeScript/core/ajax/simple-response-interface.ts @@ -1,5 +1,5 @@ export default interface SimpleResponseInterface { status: number; headers: Map<string, string>; - body: string | any; + body: string | unknown; } diff --git a/Build/Sources/TypeScript/core/authentication/mfa-provider/totp.ts b/Build/Sources/TypeScript/core/authentication/mfa-provider/totp.ts index ad506b1b6020..bfee938f8753 100644 --- a/Build/Sources/TypeScript/core/authentication/mfa-provider/totp.ts +++ b/Build/Sources/TypeScript/core/authentication/mfa-provider/totp.ts @@ -11,20 +11,16 @@ * The TYPO3 project - inspiring people to share! */ -import {html, TemplateResult, LitElement} from 'lit'; -import {customElement, property} from 'lit/decorators'; +import { html, TemplateResult, LitElement } from 'lit'; +import { customElement, property } from 'lit/decorators'; import Modal from '@typo3/backend/modal'; -enum Selectors { - modalBody = '.t3js-modal-body' -} - @customElement('typo3-mfa-totp-url-info-button') -class MfaTotpUrlButton extends LitElement { - @property({type: String}) url: string; - @property({type: String}) title: string; - @property({type: String}) description: string; - @property({type: String}) ok: string; +export class MfaTotpUrlButton extends LitElement { + @property({ type: String }) url: string; + @property({ type: String }) title: string; + @property({ type: String }) description: string; + @property({ type: String }) ok: string; public constructor() { super(); diff --git a/Build/Sources/TypeScript/core/event/debounce-event.ts b/Build/Sources/TypeScript/core/event/debounce-event.ts index d682e3431bdb..40b91b032e0a 100644 --- a/Build/Sources/TypeScript/core/event/debounce-event.ts +++ b/Build/Sources/TypeScript/core/event/debounce-event.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {Listener} from './event-interface'; +import { Listener } from './event-interface'; import RegularEvent from './regular-event'; /** @@ -27,7 +27,7 @@ class DebounceEvent extends RegularEvent { private debounce(callback: Listener, wait: number, immediate: boolean): Listener { let timeout: number = null; - return function (this: Node, ...args: any[]): void { + return function (this: Node, ...args: unknown[]): void { const callNow = immediate && !timeout; // Reset timeout handler to make sure the callback is executed once diff --git a/Build/Sources/TypeScript/core/event/regular-event.ts b/Build/Sources/TypeScript/core/event/regular-event.ts index 37abf21036ea..86372f141e80 100644 --- a/Build/Sources/TypeScript/core/event/regular-event.ts +++ b/Build/Sources/TypeScript/core/event/regular-event.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {EventInterface, Listener} from './event-interface'; +import { EventInterface, Listener } from './event-interface'; class RegularEvent implements EventInterface { protected eventName: string; diff --git a/Build/Sources/TypeScript/core/event/request-animation-frame-event.ts b/Build/Sources/TypeScript/core/event/request-animation-frame-event.ts index 14b61623ab2f..7368cb607e0e 100644 --- a/Build/Sources/TypeScript/core/event/request-animation-frame-event.ts +++ b/Build/Sources/TypeScript/core/event/request-animation-frame-event.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {Listener} from './event-interface'; +import { Listener } from './event-interface'; import RegularEvent from './regular-event'; /** @@ -26,17 +26,14 @@ class RequestAnimationFrameEvent extends RegularEvent { private req(callback: Listener): Listener { let timeout: number = null; - return () => { - const context: any = this; - const args = arguments; - + return (...args: unknown[]) => { if (timeout) { window.cancelAnimationFrame(timeout); } - timeout = window.requestAnimationFrame(function () { + timeout = window.requestAnimationFrame(() => { // Run our scroll functions - callback.apply(context, args); + callback.apply(this, args); }); }; } diff --git a/Build/Sources/TypeScript/core/event/throttle-event.ts b/Build/Sources/TypeScript/core/event/throttle-event.ts index 3ebcc4a5b13b..75be03ed5ab3 100644 --- a/Build/Sources/TypeScript/core/event/throttle-event.ts +++ b/Build/Sources/TypeScript/core/event/throttle-event.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {Listener} from './event-interface'; +import { Listener } from './event-interface'; import RegularEvent from './regular-event'; /** @@ -26,7 +26,7 @@ class ThrottleEvent extends RegularEvent { private throttle(callback: Listener, limit: number): Listener { let wait: boolean = false; - return function (this: Node, ...args: any[]): void { + return function (this: Node, ...args: unknown[]): void { if (wait) { return; } diff --git a/Build/Sources/TypeScript/core/java-script-item-handler.ts b/Build/Sources/TypeScript/core/java-script-item-handler.ts index 060204750ae8..4e0b5fbf6244 100644 --- a/Build/Sources/TypeScript/core/java-script-item-handler.ts +++ b/Build/Sources/TypeScript/core/java-script-item-handler.ts @@ -19,12 +19,12 @@ if (document.currentScript) { const scriptElement = document.currentScript; // extracts JSON payload from `/* [JSON] */` content - const textContent = scriptElement.textContent.replace(/^\s*\/\*\s*|\s*\*\/\s*/g, '') + const textContent = scriptElement.textContent.replace(/^\s*\/\*\s*|\s*\*\/\s*/g, ''); const items = JSON.parse(textContent); const moduleImporter = (moduleName: string) => import(moduleName).catch(() => (window as any).importShim(moduleName)); - moduleImporter('@typo3/core/java-script-item-processor.js').then(({JavaScriptItemProcessor}) => { + moduleImporter('@typo3/core/java-script-item-processor.js').then(({ JavaScriptItemProcessor }) => { const processor = new JavaScriptItemProcessor(); processor.processItems(items); }); diff --git a/Build/Sources/TypeScript/core/java-script-item-processor.ts b/Build/Sources/TypeScript/core/java-script-item-processor.ts index d6ad12340a73..4f470f1b80b7 100644 --- a/Build/Sources/TypeScript/core/java-script-item-processor.ts +++ b/Build/Sources/TypeScript/core/java-script-item-processor.ts @@ -27,7 +27,7 @@ export interface JavaScriptInstruction { type: string; assignments?: object; method?: string; - args: Array<any>; + args: unknown[]; } export interface JavaScriptItemPayload { @@ -50,13 +50,13 @@ let useShim = false; const moduleImporter = (moduleName: string): Promise<any> => { if (useShim) { - return (window as any).importShim(moduleName) + return (window as any).importShim(moduleName); } else { return import(moduleName).catch(() => { // Consider that importmaps are not supported and use shim from now on useShim = true; - return moduleImporter(moduleName) - }) + return moduleImporter(moduleName); + }); } }; @@ -94,7 +94,7 @@ export function loadModule(payload: JavaScriptItemPayload): Promise<any> { }); } - throw new Error('Unknown JavaScript module type') + throw new Error('Unknown JavaScript module type'); } export function resolveSubjectRef(__esModule: any, payload: JavaScriptItemPayload): any { @@ -128,7 +128,7 @@ export function executeJavaScriptModuleInstruction(json: JavaScriptItemPayload): return (__esModule: any): any => { const subjectRef = resolveSubjectRef(__esModule, json); if ('method' in item && item.method) { - return subjectRef[item.method].apply(subjectRef, item.args); + return subjectRef[item.method](...item.args); } else { return subjectRef(...item.args); } @@ -139,12 +139,12 @@ export function executeJavaScriptModuleInstruction(json: JavaScriptItemPayload): // which will be reset when invoking `new` const args = [null].concat(item.args); const subjectRef = resolveSubjectRef(__esModule, json); - return new (subjectRef.bind.apply(subjectRef, args)); - } + return new (subjectRef.bind(...args)); + }; } else { - return (__esModule: any) => { + return (): void => { return; - } + }; } }); @@ -163,7 +163,7 @@ function mergeRecursive(target: { [key: string]: any }, source: { [key: string]: throw new Error('Property ' + property + ' is not allowed'); } if (!isObjectInstance(source[property]) || typeof target[property] === 'undefined') { - Object.assign(target, {[property]:source[property]}); + Object.assign(target, { [property]:source[property] }); } else { mergeRecursive(target[property], source[property]); } diff --git a/Build/Sources/TypeScript/core/lit-helper.ts b/Build/Sources/TypeScript/core/lit-helper.ts index 332a6a8f92b8..2e50c8b5de46 100644 --- a/Build/Sources/TypeScript/core/lit-helper.ts +++ b/Build/Sources/TypeScript/core/lit-helper.ts @@ -11,8 +11,8 @@ * The TYPO3 project - inspiring people to share! */ -import {render, TemplateResult} from 'lit'; -import {ClassInfo} from 'lit/directives/class-map'; +import { render, TemplateResult } from 'lit'; +import { ClassInfo } from 'lit/directives/class-map'; /** * @internal @@ -30,7 +30,7 @@ export const renderHTML = (result: TemplateResult): string => { const anvil = document.createElement('div'); render(result, anvil); return anvil.innerHTML; -} +}; /** * @internal @@ -52,4 +52,4 @@ export const classesArrayToClassInfo = (classes: Array<string>): ClassInfo => { }, {} as Writeable<ClassInfo> ); -} +}; diff --git a/Build/Sources/TypeScript/core/security-utility.ts b/Build/Sources/TypeScript/core/security-utility.ts index 4a43e2970562..b10c82869776 100644 --- a/Build/Sources/TypeScript/core/security-utility.ts +++ b/Build/Sources/TypeScript/core/security-utility.ts @@ -47,7 +47,7 @@ class SecurityUtility { * @return {string} */ public encodeHtml(value: string, doubleEncode: boolean = true): string { - let anvil: HTMLSpanElement = this.createAnvil(); + const anvil: HTMLSpanElement = this.createAnvil(); if (!doubleEncode) { // decode HTML entities step-by-step // but NEVER(!) as a whole, since that would allow XSS diff --git a/Build/Sources/TypeScript/core/tests/ajax/ajax-request-test.ts b/Build/Sources/TypeScript/core/tests/ajax/ajax-request-test.ts index 228fe3f9ad1e..a680375c2e77 100644 --- a/Build/Sources/TypeScript/core/tests/ajax/ajax-request-test.ts +++ b/Build/Sources/TypeScript/core/tests/ajax/ajax-request-test.ts @@ -12,33 +12,33 @@ */ import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; describe('@typo3/core/ajax/ajax-request', (): void => { - let promiseHelper: any; + let promiseHelper: { resolve: Function, reject: Function }; beforeEach((): void => { const fetchPromise: Promise<Response> = new Promise(((resolve: Function, reject: Function): void => { promiseHelper = { resolve: resolve, reject: reject, - } + }; })); spyOn(window, 'fetch').and.returnValue(fetchPromise); }); it('sends GET request', (): void => { (new AjaxRequest('https://example.com')).get(); - expect(window.fetch).toHaveBeenCalledWith('https://example.com/', jasmine.objectContaining({method: 'GET'})); + expect(window.fetch).toHaveBeenCalledWith('https://example.com/', jasmine.objectContaining({ method: 'GET' })); }); - for (let requestMethod of ['POST', 'PUT', 'DELETE']) { + for (const requestMethod of ['POST', 'PUT', 'DELETE']) { describe(`send a ${requestMethod} request`, (): void => { function* requestDataProvider(): any { yield [ 'object as payload', requestMethod, - {foo: 'bar', bar: 'baz', nested: {works: 'yes'}}, + { foo: 'bar', bar: 'baz', nested: { works: 'yes' } }, (): FormData => { const expected = new FormData(); expected.set('foo', 'bar'); @@ -51,30 +51,30 @@ describe('@typo3/core/ajax/ajax-request', (): void => { yield [ 'JSON object as payload', requestMethod, - {foo: 'bar', bar: 'baz', nested: {works: 'yes'}}, + { foo: 'bar', bar: 'baz', nested: { works: 'yes' } }, (): string => { - return JSON.stringify({foo: 'bar', bar: 'baz', nested: {works: 'yes'}}) + return JSON.stringify({ foo: 'bar', bar: 'baz', nested: { works: 'yes' } }); }, - {'Content-Type': 'application/json'} + { 'Content-Type': 'application/json' } ]; yield [ 'JSON string as payload', requestMethod, - JSON.stringify({foo: 'bar', bar: 'baz', nested: {works: 'yes'}}), + JSON.stringify({ foo: 'bar', bar: 'baz', nested: { works: 'yes' } }), (): string => { - return JSON.stringify({foo: 'bar', bar: 'baz', nested: {works: 'yes'}}) + return JSON.stringify({ foo: 'bar', bar: 'baz', nested: { works: 'yes' } }); }, - {'Content-Type': 'application/json'} + { 'Content-Type': 'application/json' } ]; } - for (let providedData of requestDataProvider()) { - let [name, requestMethod, payload, expectedFn, headers] = providedData; + for (const providedData of requestDataProvider()) { + const [name, requestMethod, payload, expectedFn, headers] = providedData; const requestFn: string = requestMethod.toLowerCase(); it(`with ${name}`, (done: DoneFn): void => { const request: any = (new AjaxRequest('https://example.com')); - request[requestFn](payload, {headers: headers}); - expect(window.fetch).toHaveBeenCalledWith('https://example.com/', jasmine.objectContaining({method: requestMethod, body: expectedFn()})); + request[requestFn](payload, { headers: headers }); + expect(window.fetch).toHaveBeenCalledWith('https://example.com/', jasmine.objectContaining({ method: requestMethod, body: expectedFn() })); done(); }); } @@ -94,8 +94,8 @@ describe('@typo3/core/ajax/ajax-request', (): void => { ]; yield [ 'JSON', - JSON.stringify({foo: 'bar', baz: 'bencer'}), - {'Content-Type': 'application/json'}, + JSON.stringify({ foo: 'bar', baz: 'bencer' }), + { 'Content-Type': 'application/json' }, (data: any, responseBody: any): void => { expect(typeof data === 'object').toBeTruthy(); expect(JSON.stringify(data)).toEqual(responseBody); @@ -103,8 +103,8 @@ describe('@typo3/core/ajax/ajax-request', (): void => { ]; yield [ 'JSON with utf-8', - JSON.stringify({foo: 'bar', baz: 'bencer'}), - {'Content-Type': 'application/json; charset=utf-8'}, + JSON.stringify({ foo: 'bar', baz: 'bencer' }), + { 'Content-Type': 'application/json; charset=utf-8' }, (data: any, responseBody: any): void => { expect(typeof data === 'object').toBeTruthy(); expect(JSON.stringify(data)).toEqual(responseBody); @@ -112,18 +112,18 @@ describe('@typo3/core/ajax/ajax-request', (): void => { ]; } - for (let providedData of responseDataProvider()) { - let [name, responseText, headers, onfulfill] = providedData; + for (const providedData of responseDataProvider()) { + const [name, responseText, headers, onfulfill] = providedData; it('receives a ' + name + ' response', (done: DoneFn): void => { - const response = new Response(responseText, {headers: headers}); + const response = new Response(responseText, { headers: headers }); promiseHelper.resolve(response); - (new AjaxRequest('https://example.com')).get().then(async (response: AjaxResponse): Promise<any> => { + (new AjaxRequest('https://example.com')).get().then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); - expect(window.fetch).toHaveBeenCalledWith('https://example.com/', jasmine.objectContaining({method: 'GET'})); + expect(window.fetch).toHaveBeenCalledWith('https://example.com/', jasmine.objectContaining({ method: 'GET' })); onfulfill(data, responseText); done(); - }) + }); }); } }); @@ -139,7 +139,7 @@ describe('@typo3/core/ajax/ajax-request', (): void => { yield [ 'absolute url with domain, with query parameter', 'https://example.com', - {foo: 'bar', bar: {baz: 'bencer'}}, + { foo: 'bar', bar: { baz: 'bencer' } }, 'https://example.com/?foo=bar&bar[baz]=bencer', ]; yield [ @@ -151,7 +151,7 @@ describe('@typo3/core/ajax/ajax-request', (): void => { yield [ 'absolute url without domain, with query parameter', '/foo/bar', - {foo: 'bar', bar: {baz: 'bencer'}}, + { foo: 'bar', bar: { baz: 'bencer' } }, window.location.origin + '/foo/bar?foo=bar&bar[baz]=bencer', ]; yield [ @@ -163,7 +163,7 @@ describe('@typo3/core/ajax/ajax-request', (): void => { yield [ 'relative url without domain, with query parameter', 'foo/bar', - {foo: 'bar', bar: {baz: 'bencer'}}, + { foo: 'bar', bar: { baz: 'bencer' } }, window.location.origin + '/foo/bar?foo=bar&bar[baz]=bencer', ]; yield [ @@ -174,11 +174,11 @@ describe('@typo3/core/ajax/ajax-request', (): void => { ]; } - for (let providedData of urlInputDataProvider()) { - let [name, input, queryParameter, expected] = providedData; + for (const providedData of urlInputDataProvider()) { + const [name, input, queryParameter, expected] = providedData; it('with ' + name, (): void => { (new AjaxRequest(input)).withQueryArguments(queryParameter).get(); - expect(window.fetch).toHaveBeenCalledWith(expected, jasmine.objectContaining({method: 'GET'})); + expect(window.fetch).toHaveBeenCalledWith(expected, jasmine.objectContaining({ method: 'GET' })); }); } }); @@ -187,12 +187,12 @@ describe('@typo3/core/ajax/ajax-request', (): void => { function* queryArgumentsDataProvider(): any { yield [ 'single level of arguments', - {foo: 'bar', bar: 'baz'}, + { foo: 'bar', bar: 'baz' }, 'https://example.com/?foo=bar&bar=baz', ]; yield [ 'nested arguments', - {foo: 'bar', bar: {baz: 'bencer'}}, + { foo: 'bar', bar: { baz: 'bencer' } }, 'https://example.com/?foo=bar&bar[baz]=bencer', ]; yield [ @@ -207,7 +207,7 @@ describe('@typo3/core/ajax/ajax-request', (): void => { ]; yield [ 'object with array', - {foo: ['bar', 'baz']}, + { foo: ['bar', 'baz'] }, 'https://example.com/?foo[0]=bar&foo[1]=baz', ]; yield [ @@ -243,11 +243,11 @@ describe('@typo3/core/ajax/ajax-request', (): void => { ]; } - for (let providedData of queryArgumentsDataProvider()) { - let [name, input, expected] = providedData; + for (const providedData of queryArgumentsDataProvider()) { + const [name, input, expected] = providedData; it('with ' + name, (): void => { (new AjaxRequest('https://example.com/')).withQueryArguments(input).get(); - expect(window.fetch).toHaveBeenCalledWith(expected, jasmine.objectContaining({method: 'GET'})); + expect(window.fetch).toHaveBeenCalledWith(expected, jasmine.objectContaining({ method: 'GET' })); }); } }); diff --git a/Build/Sources/TypeScript/core/tests/ajax/input-transformer-test.ts b/Build/Sources/TypeScript/core/tests/ajax/input-transformer-test.ts index 90f4cca37d85..95e19d282b3f 100644 --- a/Build/Sources/TypeScript/core/tests/ajax/input-transformer-test.ts +++ b/Build/Sources/TypeScript/core/tests/ajax/input-transformer-test.ts @@ -11,11 +11,11 @@ * The TYPO3 project - inspiring people to share! */ -import {GenericKeyValue, InputTransformer} from '@typo3/core/ajax/input-transformer'; +import { GenericKeyValue, InputTransformer } from '@typo3/core/ajax/input-transformer'; describe('@typo3/core/ajax/input-transformer', (): void => { it('converts object to FormData', (): void => { - const input: GenericKeyValue = {foo: 'bar', bar: 'baz', nested: {works: 'yes'}}; + const input: GenericKeyValue = { foo: 'bar', bar: 'baz', nested: { works: 'yes' } }; const expected = new FormData(); expected.set('foo', 'bar'); expected.set('bar', 'baz'); @@ -25,7 +25,7 @@ describe('@typo3/core/ajax/input-transformer', (): void => { }); it('undefined values are removed in FormData', (): void => { - const input: GenericKeyValue = {foo: 'bar', bar: 'baz', removeme: undefined}; + const input: GenericKeyValue = { foo: 'bar', bar: 'baz', removeme: undefined }; const expected = new FormData(); expected.set('foo', 'bar'); expected.set('bar', 'baz'); @@ -34,7 +34,7 @@ describe('@typo3/core/ajax/input-transformer', (): void => { }); it('converts object to SearchParams', (): void => { - const input: GenericKeyValue = {foo: 'bar', bar: 'baz', nested: {works: 'yes'}}; + const input: GenericKeyValue = { foo: 'bar', bar: 'baz', nested: { works: 'yes' } }; const expected = 'foo=bar&bar=baz&nested[works]=yes'; expect(InputTransformer.toSearchParams(input)).toEqual(expected); @@ -55,7 +55,7 @@ describe('@typo3/core/ajax/input-transformer', (): void => { }); it('undefined values are removed in SearchParams', (): void => { - const input: GenericKeyValue = {foo: 'bar', bar: 'baz', removeme: undefined}; + const input: GenericKeyValue = { foo: 'bar', bar: 'baz', removeme: undefined }; const expected = 'foo=bar&bar=baz'; expect(InputTransformer.toSearchParams(input)).toEqual(expected); }); diff --git a/Build/Sources/TypeScript/core/tests/security-utility-test.ts b/Build/Sources/TypeScript/core/tests/security-utility-test.ts index 112f67ab2924..94631b66b1e2 100644 --- a/Build/Sources/TypeScript/core/tests/security-utility-test.ts +++ b/Build/Sources/TypeScript/core/tests/security-utility-test.ts @@ -20,7 +20,7 @@ describe('@typo3/core/security-utility', (): void => { yield 20; yield 39; } - for (let validLength of validLengthDataProvider()) { + for (const validLength of validLengthDataProvider()) { const randomHexValue = (new SecurityUtility()).getRandomHexValue(validLength); expect(randomHexValue.length).toBe(validLength); } @@ -32,7 +32,7 @@ describe('@typo3/core/security-utility', (): void => { yield -90; yield 10.3; // length is "ceiled", 10.3 => 11, 10 != 11 } - for (let invalidLength of invalidLengthDataProvider()) { + for (const invalidLength of invalidLengthDataProvider()) { expect(() => (new SecurityUtility()).getRandomHexValue(invalidLength)).toThrowError(SyntaxError); } }); diff --git a/Build/Sources/TypeScript/dashboard/chart-initializer.ts b/Build/Sources/TypeScript/dashboard/chart-initializer.ts index 1dfa0dc06554..0e67627051a0 100644 --- a/Build/Sources/TypeScript/dashboard/chart-initializer.ts +++ b/Build/Sources/TypeScript/dashboard/chart-initializer.ts @@ -12,7 +12,7 @@ */ // @todo: offload import and registration of components into separated widgets in TYPO3 v13 -import {Chart, +import { Chart, ArcElement, LineElement, BarElement, @@ -36,7 +36,7 @@ import {Chart, Legend, Title, Tooltip, - SubTitle} from '@typo3/dashboard/contrib/chartjs'; + SubTitle } from '@typo3/dashboard/contrib/chartjs'; import RegularEvent from '@typo3/core/event/regular-event'; class ChartInitializer { @@ -84,7 +84,7 @@ class ChartInitializer { return; } - let _canvas: any = this.querySelector('canvas'); + const _canvas: any = this.querySelector('canvas'); let context; if (_canvas !== null) { @@ -95,8 +95,8 @@ class ChartInitializer { return; } - new Chart(context, config.graphConfig) - }).delegateTo(document, this.selector) + new Chart(context, config.graphConfig); + }).delegateTo(document, this.selector); } } diff --git a/Build/Sources/TypeScript/dashboard/dashboard-delete.ts b/Build/Sources/TypeScript/dashboard/dashboard-delete.ts index 4ae90b1389ed..5c644da5f048 100644 --- a/Build/Sources/TypeScript/dashboard/dashboard-delete.ts +++ b/Build/Sources/TypeScript/dashboard/dashboard-delete.ts @@ -12,7 +12,7 @@ */ import Modal from '@typo3/backend/modal'; -import {SeverityEnum} from '@typo3/backend/enum/severity'; +import { SeverityEnum } from '@typo3/backend/enum/severity'; import RegularEvent from '@typo3/core/event/regular-event'; class DashboardDelete { diff --git a/Build/Sources/TypeScript/dashboard/dashboard-modal.ts b/Build/Sources/TypeScript/dashboard/dashboard-modal.ts index 8bfae65bc2bb..9fe729602799 100644 --- a/Build/Sources/TypeScript/dashboard/dashboard-modal.ts +++ b/Build/Sources/TypeScript/dashboard/dashboard-modal.ts @@ -11,8 +11,8 @@ * The TYPO3 project - inspiring people to share! */ -import {default as Modal, ModalElement} from '@typo3/backend/modal'; -import {SeverityEnum} from '@typo3/backend/enum/severity'; +import { default as Modal, ModalElement } from '@typo3/backend/modal'; +import { SeverityEnum } from '@typo3/backend/enum/severity'; import RegularEvent from '@typo3/core/event/regular-event'; class DashboardModal { diff --git a/Build/Sources/TypeScript/dashboard/grid.ts b/Build/Sources/TypeScript/dashboard/grid.ts index 4710208c9a0f..4e0107e2c3ca 100644 --- a/Build/Sources/TypeScript/dashboard/grid.ts +++ b/Build/Sources/TypeScript/dashboard/grid.ts @@ -12,7 +12,7 @@ */ import Muuri from 'muuri'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import RegularEvent from '@typo3/core/event/regular-event'; @@ -74,7 +74,7 @@ class Grid { } public saveItems(dashboard: any): void { - let widgets = dashboard.getItems().map(function (item: any) { + const widgets = dashboard.getItems().map(function (item: any) { return [ item.getElement().getAttribute('data-widget-key'), item.getElement().getAttribute('data-widget-hash') @@ -83,7 +83,7 @@ class Grid { (new AjaxRequest(TYPO3.settings.ajaxUrls.dashboard_save_widget_positions)).post({ widgets: widgets - }).then(async (response: AjaxResponse): Promise<any> => { + }).then(async (response: AjaxResponse): Promise<void> => { await response.resolve(); }); } diff --git a/Build/Sources/TypeScript/dashboard/widget-content-collector.ts b/Build/Sources/TypeScript/dashboard/widget-content-collector.ts index b7be636d3145..71c60fe2a325 100644 --- a/Build/Sources/TypeScript/dashboard/widget-content-collector.ts +++ b/Build/Sources/TypeScript/dashboard/widget-content-collector.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import RegularEvent from '@typo3/core/event/regular-event'; @@ -27,11 +27,10 @@ class WidgetContentCollector { this.registerEvents(); const items = document.querySelectorAll(this.selector); items.forEach((triggerElement: HTMLElement): void => { - let event: Event; const eventInitDict: EventInit = { bubbles: true, }; - event = new Event('widgetRefresh', eventInitDict); + const event = new Event('widgetRefresh', eventInitDict); triggerElement.dispatchEvent(event); }); } @@ -57,7 +56,7 @@ class WidgetContentCollector { widget: element.dataset.widgetKey, }) .get() - .then(async (response: AjaxResponse): Promise<any> => { + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (widgetContentElement !== null) { widgetContentElement.innerHTML = data.content; @@ -72,7 +71,7 @@ class WidgetContentCollector { bubbles: true, }; if (Object.keys(data.eventdata).length > 0) { - event = new CustomEvent('widgetContentRendered', {...eventInitDict, detail: data.eventdata}); + event = new CustomEvent('widgetContentRendered', { ...eventInitDict, detail: data.eventdata }); } else { event = new Event('widgetContentRendered', eventInitDict); } diff --git a/Build/Sources/TypeScript/dashboard/widget-refresh.ts b/Build/Sources/TypeScript/dashboard/widget-refresh.ts index 69c6ca9bae68..c35004525ebe 100644 --- a/Build/Sources/TypeScript/dashboard/widget-refresh.ts +++ b/Build/Sources/TypeScript/dashboard/widget-refresh.ts @@ -11,15 +11,15 @@ * The TYPO3 project - inspiring people to share! */ -import {html, LitElement, TemplateResult} from 'lit'; -import {customElement} from 'lit/decorators'; +import { html, LitElement, TemplateResult } from 'lit'; +import { customElement } from 'lit/decorators'; enum Selectors { dashboardItem = '.dashboard-item' } @customElement('typo3-dashboard-widget-refresh') -class WidgetRefresh extends LitElement { +export class WidgetRefresh extends LitElement { public connectedCallback(): void { super.connectedCallback(); @@ -40,7 +40,7 @@ class WidgetRefresh extends LitElement { private onRefresh(e: Event): void { e.preventDefault(); this.closest(Selectors.dashboardItem).dispatchEvent( - new Event('widgetRefresh', {bubbles: true}) + new Event('widgetRefresh', { bubbles: true }) ); this.querySelector('button').blur(); } diff --git a/Build/Sources/TypeScript/dashboard/widget-remover.ts b/Build/Sources/TypeScript/dashboard/widget-remover.ts index 75e43fe2018e..01e1f69854c6 100644 --- a/Build/Sources/TypeScript/dashboard/widget-remover.ts +++ b/Build/Sources/TypeScript/dashboard/widget-remover.ts @@ -12,7 +12,7 @@ */ import Modal from '@typo3/backend/modal'; -import {SeverityEnum} from '@typo3/backend/enum/severity'; +import { SeverityEnum } from '@typo3/backend/enum/severity'; import RegularEvent from '@typo3/core/event/regular-event'; class WidgetRemover { diff --git a/Build/Sources/TypeScript/dashboard/widget-selector.ts b/Build/Sources/TypeScript/dashboard/widget-selector.ts index de8304a15780..4c0da87311d1 100644 --- a/Build/Sources/TypeScript/dashboard/widget-selector.ts +++ b/Build/Sources/TypeScript/dashboard/widget-selector.ts @@ -11,8 +11,8 @@ * The TYPO3 project - inspiring people to share! */ -import {default as Modal, ModalElement} from '@typo3/backend/modal'; -import {SeverityEnum} from '@typo3/backend/enum/severity'; +import { default as Modal, ModalElement } from '@typo3/backend/modal'; +import { SeverityEnum } from '@typo3/backend/enum/severity'; import RegularEvent from '@typo3/core/event/regular-event'; class WidgetSelector { @@ -47,7 +47,7 @@ class WidgetSelector { // Display button only if all initialized document.querySelectorAll(this.selector).forEach((item) => { item.classList.remove('hide'); - }) + }); } } diff --git a/Build/Sources/TypeScript/extensionmanager/distribution-image.ts b/Build/Sources/TypeScript/extensionmanager/distribution-image.ts index f9e9ae993ba1..82c4b7e1c801 100644 --- a/Build/Sources/TypeScript/extensionmanager/distribution-image.ts +++ b/Build/Sources/TypeScript/extensionmanager/distribution-image.ts @@ -35,17 +35,17 @@ export class DistributionImage extends HTMLElement { return; } - this.attachShadow({mode: 'open'}); + this.attachShadow({ mode: 'open' }); this.imageElement = document.createElement('img'); const alt: string = this.getAttribute('alt') || ''; if (alt.length) { - this.imageElement.setAttribute('alt', alt) + this.imageElement.setAttribute('alt', alt); } const title: string = this.getAttribute('title') || ''; if (title.length) { - this.imageElement.setAttribute('title', title) + this.imageElement.setAttribute('title', title); } if (this.welcomeImage.length) { @@ -82,7 +82,7 @@ export class DistributionImage extends HTMLElement { } else if (this.fallback.length) { this.imageElement.setAttribute('src', this.fallback); } - } + }; } window.customElements.define('typo3-extensionmanager-distribution-image', DistributionImage); diff --git a/Build/Sources/TypeScript/extensionmanager/main.ts b/Build/Sources/TypeScript/extensionmanager/main.ts index 6c4674208680..5f57f9653eca 100644 --- a/Build/Sources/TypeScript/extensionmanager/main.ts +++ b/Build/Sources/TypeScript/extensionmanager/main.ts @@ -15,7 +15,7 @@ import DocumentService from '@typo3/core/document-service'; import $ from 'jquery'; import BrowserSession from '@typo3/backend/storage/browser-session'; import NProgress from 'nprogress'; -import {default as Modal, ModalElement} from '@typo3/backend/modal'; +import { default as Modal, ModalElement } from '@typo3/backend/modal'; import Severity from '@typo3/backend/severity'; import SecurityUtility from '@typo3/core/security-utility'; import ExtensionManagerRepository from './repository'; @@ -24,7 +24,7 @@ import ExtensionManagerUploadForm from './upload-form'; import Tablesort from 'tablesort'; import 'tablesort.dotsep'; import '@typo3/backend/input/clearable'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import DebounceEvent from '@typo3/core/event/debounce-event'; import RegularEvent from '@typo3/core/event/regular-event'; @@ -37,7 +37,7 @@ enum ExtensionManagerIdentifier { } interface UpdateInformation { - updateComments: { [key: string]: string }, + updateComments: Record<string, string>, url: string } @@ -56,7 +56,6 @@ class ExtensionManager { private readonly searchFilterSessionKey: string = 'tx-extensionmanager-local-filter'; constructor() { - const me = this; DocumentService.ready().then((): void => { this.Update = new ExtensionManagerUpdate(); this.UploadForm = new ExtensionManagerUploadForm(); @@ -66,7 +65,7 @@ class ExtensionManager { if (extensionList !== null) { new Tablesort(extensionList); - new RegularEvent('click', function (this: HTMLAnchorElement, e: Event): void { + new RegularEvent('click', (e: Event, target: HTMLAnchorElement): void => { e.preventDefault(); Modal.confirm( @@ -85,7 +84,7 @@ class ExtensionManager { text: TYPO3.lang['button.remove'], btnClass: 'btn-danger', trigger: (): void => { - me.removeExtensionFromDisk(this); + this.removeExtensionFromDisk(target); Modal.dismiss(); }, }, @@ -171,8 +170,8 @@ class ExtensionManager { let i = 0; const data: UpdateInformation = await response.resolve(); const $form = $('<form>'); - for (let [version, comment] of Object.entries(data.updateComments)) { - const $input = $('<input>').attr({type: 'radio', name: 'version'}).val(version); + for (const [version, comment] of Object.entries(data.updateComments)) { + const $input = $('<input>').attr({ type: 'radio', name: 'version' }).val(version); if (i === 0) { $input.attr('checked', 'checked'); } @@ -229,7 +228,7 @@ class ExtensionManager { } } -let extensionManagerObject = new ExtensionManager(); +const extensionManagerObject = new ExtensionManager(); if (typeof TYPO3.ExtensionManager === 'undefined') { TYPO3.ExtensionManager = extensionManagerObject; diff --git a/Build/Sources/TypeScript/extensionmanager/repository.ts b/Build/Sources/TypeScript/extensionmanager/repository.ts index 74d780b237bb..ceb1f89cc5a1 100644 --- a/Build/Sources/TypeScript/extensionmanager/repository.ts +++ b/Build/Sources/TypeScript/extensionmanager/repository.ts @@ -18,7 +18,7 @@ import Notification from '@typo3/backend/notification'; import Severity from '@typo3/backend/severity'; import Tablesort from 'tablesort'; import '@typo3/backend/input/clearable'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import RegularEvent from '@typo3/core/event/regular-event'; @@ -40,7 +40,7 @@ class Repository { public downloadPath: string = ''; public initDom(): void { - NProgress.configure({parent: '.module-loading-indicator', showSpinner: false}); + NProgress.configure({ parent: '.module-loading-indicator', showSpinner: false }); const terVersionTable = document.getElementById('terVersionTable'); const terSearchTable = document.getElementById('terSearchTable'); @@ -57,16 +57,14 @@ class Repository { } private bindDownload(): void { - const me = this; - new RegularEvent('click', function (this: HTMLInputElement, e: Event): void { + new RegularEvent('click', (e: Event, target: HTMLInputElement): void => { e.preventDefault(); - const form = this.closest('form'); + const form = target.closest('form'); const url = form.dataset.href; - me.downloadPath = (form.querySelector('input.downloadPath:checked') as HTMLInputElement).value; + this.downloadPath = (form.querySelector('input.downloadPath:checked') as HTMLInputElement).value; NProgress.start(); - new AjaxRequest(url).get().then(me.getDependencies); - + new AjaxRequest(url).get().then(this.getDependencies); }).delegateTo(document, '.downloadFromTer form.download button[type=submit]'); } @@ -101,7 +99,7 @@ class Repository { + '&downloadPath=' + this.downloadPath); } } - } + }; private getResolveDependenciesAndInstallResult(url: string): void { NProgress.start(); @@ -144,9 +142,9 @@ class Repository { + data.installationTypeLanguageKey].replace(/\{0\}/g, data.extension); successMessage += '\n' + TYPO3.lang['extensionList.dependenciesResolveDownloadSuccess.header'] + ': '; - for (let [index, value] of Object.entries(data.result)) { + for (const [index, value] of Object.entries(data.result)) { successMessage += '\n\n' + TYPO3.lang['extensionList.dependenciesResolveDownloadSuccess.item'] + ' ' + index + ': '; - for (let extkey of value) { + for (const extkey of value) { successMessage += '\n* ' + extkey; } } @@ -159,7 +157,7 @@ class Repository { top.TYPO3.ModuleMenu.App.refreshMenu(); } }).finally((): void => { - NProgress.done() + NProgress.done(); }); } diff --git a/Build/Sources/TypeScript/extensionmanager/update.ts b/Build/Sources/TypeScript/extensionmanager/update.ts index 69bc1eb078b4..03de753ea8fd 100644 --- a/Build/Sources/TypeScript/extensionmanager/update.ts +++ b/Build/Sources/TypeScript/extensionmanager/update.ts @@ -14,7 +14,7 @@ import $ from 'jquery'; import NProgress from 'nprogress'; import Notification from '@typo3/backend/notification'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import RegularEvent from '@typo3/core/event/regular-event'; diff --git a/Build/Sources/TypeScript/extensionmanager/upload-form.ts b/Build/Sources/TypeScript/extensionmanager/upload-form.ts index 529b487c0e25..f4fd0af838dc 100644 --- a/Build/Sources/TypeScript/extensionmanager/upload-form.ts +++ b/Build/Sources/TypeScript/extensionmanager/upload-form.ts @@ -12,7 +12,7 @@ */ import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; class UploadForm { diff --git a/Build/Sources/TypeScript/filelist/browse-files.ts b/Build/Sources/TypeScript/filelist/browse-files.ts index 2da6ccdd60d5..ab5e735fec0e 100644 --- a/Build/Sources/TypeScript/filelist/browse-files.ts +++ b/Build/Sources/TypeScript/filelist/browse-files.ts @@ -15,7 +15,7 @@ import { MessageUtility } from '@typo3/backend/utility/message-utility'; import ElementBrowser from '@typo3/backend/element-browser'; import NProgress from 'nprogress'; import RegularEvent from '@typo3/core/event/regular-event'; -import Icons = TYPO3.Icons; +import Icons from '@typo3/backend/icons'; import { ActionEventDetails } from '@typo3/backend/multi-record-selection-action'; import { FileListActionEvent, FileListActionSelector, FileListActionUtility } from '@typo3/filelist/file-list-actions'; import InfoWindow from '@typo3/backend/info-window'; @@ -24,23 +24,6 @@ import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import { ResourceInterface } from '@typo3/backend/resource/resource'; class BrowseFiles { - public static insertElement(fileName: string, fileUid: number, close?: boolean): boolean { - return ElementBrowser.insertElement( - 'sys_file', - String(fileUid), - fileName, - String(fileUid), - close, - ); - } - - private static handleNext(items: ResourceInterface[]): void { - if (items.length > 0) { - const item = items.pop(); - BrowseFiles.insertElement(item.name, Number(item.uid)); - } - } - constructor() { new RegularEvent(FileListActionEvent.primary, (event: CustomEvent): void => { @@ -70,12 +53,29 @@ class BrowseFiles { } + public static insertElement(fileName: string, fileUid: number, close?: boolean): boolean { + return ElementBrowser.insertElement( + 'sys_file', + String(fileUid), + fileName, + String(fileUid), + close, + ); + } + + private static handleNext(items: ResourceInterface[]): void { + if (items.length > 0) { + const item = items.pop(); + BrowseFiles.insertElement(item.name, Number(item.uid)); + } + } + private loadContent(resource: ResourceInterface): void { if (resource.type !== 'folder') { return; } - let contentsUrl = document.location.href + '&contentOnly=1&expandFolder=' + resource.identifier; + const contentsUrl = document.location.href + '&contentOnly=1&expandFolder=' + resource.identifier; (new AjaxRequest(contentsUrl)).get() .then((response: AjaxResponse) => response.resolve()) .then((response) => { @@ -130,7 +130,7 @@ class BrowseFiles { } } }).bindTo(window); - } + }; } export default new BrowseFiles(); diff --git a/Build/Sources/TypeScript/filelist/browse-folders.ts b/Build/Sources/TypeScript/filelist/browse-folders.ts index 62053a647922..de4d23dd680a 100644 --- a/Build/Sources/TypeScript/filelist/browse-folders.ts +++ b/Build/Sources/TypeScript/filelist/browse-folders.ts @@ -24,16 +24,6 @@ import { ResourceInterface } from '@typo3/backend/resource/resource'; * @exports @typo3/backend/browse-folders */ class BrowseFolders { - public static insertElement(identifier: string, close?: boolean): boolean { - return ElementBrowser.insertElement( - '', - identifier, - identifier, - identifier, - close, - ); - } - constructor() { new RegularEvent(FileListActionEvent.primary, (event: CustomEvent): void => { @@ -60,6 +50,16 @@ class BrowseFolders { } + public static insertElement(identifier: string, close?: boolean): boolean { + return ElementBrowser.insertElement( + '', + identifier, + identifier, + identifier, + close, + ); + } + private importSelection = (event: CustomEvent): void => { event.preventDefault(); const items: NodeListOf<HTMLInputElement> = (event.detail as ActionEventDetails).checkboxes; @@ -83,7 +83,7 @@ class BrowseFolders { BrowseFolders.insertElement(resource.identifier); }); ElementBrowser.focusOpenerAndClose(); - } + }; } export default new BrowseFolders(); diff --git a/Build/Sources/TypeScript/filelist/context-menu-actions.ts b/Build/Sources/TypeScript/filelist/context-menu-actions.ts index 544eadb16bff..6edc72a5a563 100644 --- a/Build/Sources/TypeScript/filelist/context-menu-actions.ts +++ b/Build/Sources/TypeScript/filelist/context-menu-actions.ts @@ -121,7 +121,7 @@ class ContextMenuActions { Notification.info(lll('file_download.prepare'), '', 2); const actionUrl: string = dataset.actionUrl; (new AjaxRequest(actionUrl)).post({ items: [uid] }) - .then(async (response): Promise<any> => { + .then(async (response): Promise<void> => { let fileName = response.response.headers.get('Content-Disposition'); if (!fileName) { const data = await response.resolve(); diff --git a/Build/Sources/TypeScript/filelist/create-folder.ts b/Build/Sources/TypeScript/filelist/create-folder.ts index 50645a7cb159..edc33cd77740 100644 --- a/Build/Sources/TypeScript/filelist/create-folder.ts +++ b/Build/Sources/TypeScript/filelist/create-folder.ts @@ -14,8 +14,8 @@ import DocumentService from '@typo3/core/document-service'; import RegularEvent from '@typo3/core/event/regular-event'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {default as Modal} from '@typo3/backend/modal'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { default as Modal } from '@typo3/backend/modal'; /** * Module: @typo3/filelist/create-folder diff --git a/Build/Sources/TypeScript/filelist/file-delete.ts b/Build/Sources/TypeScript/filelist/file-delete.ts index 6a9d4a08125d..32a2803003e0 100644 --- a/Build/Sources/TypeScript/filelist/file-delete.ts +++ b/Build/Sources/TypeScript/filelist/file-delete.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {SeverityEnum} from '@typo3/backend/enum/severity'; +import { SeverityEnum } from '@typo3/backend/enum/severity'; import RegularEvent from '@typo3/core/event/regular-event'; import DocumentService from '@typo3/core/document-service'; import Modal from '@typo3/backend/modal'; diff --git a/Build/Sources/TypeScript/filelist/file-list-actions.ts b/Build/Sources/TypeScript/filelist/file-list-actions.ts index 6631f015212b..488f64fa1440 100644 --- a/Build/Sources/TypeScript/filelist/file-list-actions.ts +++ b/Build/Sources/TypeScript/filelist/file-list-actions.ts @@ -120,7 +120,7 @@ class FileListActions { action: action, resource: FileListActionUtility.getResourceForElement(element), url: target.dataset.filelistActionUrl ?? null, - } + }; return detail; } } diff --git a/Build/Sources/TypeScript/filelist/file-list-dragdrop.ts b/Build/Sources/TypeScript/filelist/file-list-dragdrop.ts index e8226023bd45..17de20129af1 100644 --- a/Build/Sources/TypeScript/filelist/file-list-dragdrop.ts +++ b/Build/Sources/TypeScript/filelist/file-list-dragdrop.ts @@ -89,7 +89,7 @@ class FileListDragDrop { const preview = this.rootDocument.getElementById(this.dragPreviewId); const calculatedPosition = this.determinePreviewPosition(event); - let currentPosition = preview.getBoundingClientRect(); + const currentPosition = preview.getBoundingClientRect(); if (calculatedPosition.left === currentPosition.left && calculatedPosition.top === currentPosition.top) { return; } diff --git a/Build/Sources/TypeScript/filelist/file-list-transfer-handler.ts b/Build/Sources/TypeScript/filelist/file-list-transfer-handler.ts index ab3f72666a44..9ecf22b5e9f0 100644 --- a/Build/Sources/TypeScript/filelist/file-list-transfer-handler.ts +++ b/Build/Sources/TypeScript/filelist/file-list-transfer-handler.ts @@ -103,10 +103,10 @@ class FileListTransferHandler { const operation: FileListTransferOperation = { data: resource.identifier, target: target.identifier, - } + }; payload.push(operation); }); - const params = { data: { [type]: payload } } as any; + const params = { data: { [type]: payload } }; (new AjaxRequest(top.TYPO3.settings.ajaxUrls.file_process)) .post(params) diff --git a/Build/Sources/TypeScript/filelist/file-list.ts b/Build/Sources/TypeScript/filelist/file-list.ts index 7f8a252fc6db..0b0820ec7bc8 100644 --- a/Build/Sources/TypeScript/filelist/file-list.ts +++ b/Build/Sources/TypeScript/filelist/file-list.ts @@ -31,7 +31,7 @@ import Severity from '@typo3/backend/severity'; import { MultiRecordSelectionSelectors } from '@typo3/backend/multi-record-selection'; import ContextMenu from '@typo3/backend/context-menu'; -type QueryParameters = { [key: string]: string }; +type QueryParameters = Record<string, string>; interface EditFileMetadataConfiguration extends ActionConfiguration { table: string; @@ -64,75 +64,6 @@ export const fileListOpenElementBrowser = 'typo3:filelist:openElementBrowser'; * @exports @typo3/filelist/filelist */ export default class Filelist { - public static submitClipboardFormWithCommand(cmd: string, target: HTMLButtonElement): void { - const fileListForm: HTMLFormElement = target.closest(Selectors.fileListFormSelector); - if (!fileListForm) { - return; - } - const commandField: HTMLInputElement = fileListForm.querySelector(Selectors.commandSelector); - if (!commandField) { - return; - } - commandField.value = cmd; - // In case we just change elements on the clipboard, we try to fetch a possible pointer from the query - // parameters, so after the form submit, we get to the same view as before. This is not done for delete - // commands, since this may lead to empty sites, in case all elements from the current site are deleted. - if (cmd === 'copyMarked' || cmd === 'removeMarked') { - const pointerField: HTMLInputElement = fileListForm.querySelector(Selectors.pointerFieldSelector); - const pointerValue: string = Filelist.parseQueryParameters(document.location).pointer; - if (pointerField && pointerValue) { - pointerField.value = pointerValue; - } - } - fileListForm.submit(); - } - - protected static openInfoPopup(type: string, identifier: string): void { - InfoWindow.showItem(type, identifier); - } - - private static processTriggers(): void { - const mainElement: HTMLElement = document.querySelector('.filelist-main'); - if (mainElement === null) { - return - } - // update ModuleStateStorage to the current folder identifier - const id = encodeURIComponent(mainElement.dataset.filelistCurrentIdentifier); - ModuleStateStorage.update('file', id, true, undefined); - // emit event for currently shown folder so the folder tree gets updated - Filelist.emitTreeUpdateRequest( - mainElement.dataset.filelistCurrentIdentifier - ); - } - - private static emitTreeUpdateRequest(identifier: string): void { - const message = new BroadcastMessage( - 'filelist', - 'treeUpdateRequested', - { type: 'folder', identifier: identifier } - ); - broadcastService.post(message); - } - - private static parseQueryParameters(location: Location): QueryParameters { - let queryParameters: QueryParameters = {}; - if (location && Object.prototype.hasOwnProperty.call(location, 'search')) { - let parameters = location.search.substr(1).split('&'); - for (let i = 0; i < parameters.length; i++) { - const parameter = parameters[i].split('='); - queryParameters[decodeURIComponent(parameter[0])] = decodeURIComponent(parameter[1]); - } - } - return queryParameters; - } - - private static getReturnUrl(returnUrl: string): string { - if (returnUrl === '') { - returnUrl = top.list_frame.document.location.pathname + top.list_frame.document.location.search; - } - return encodeURIComponent(returnUrl); - } - constructor() { Filelist.processTriggers(); @@ -163,14 +94,14 @@ export default class Filelist { + '&returnUrl=' + Filelist.getReturnUrl(''); } if (detail.resource.type === 'folder') { - let parameters = Filelist.parseQueryParameters(document.location) - parameters.id = detail.resource.identifier + const parameters = Filelist.parseQueryParameters(document.location); + parameters.id = detail.resource.identifier; let parameterString = ''; Object.keys(parameters).forEach(key => { if (parameters[key] === '') { return; } parameterString = parameterString + '&' + key + '=' + parameters[key]; }); - window.location.href = window.location.pathname + '?' + parameterString.substring(1) + window.location.href = window.location.pathname + '?' + parameterString.substring(1); } }).bindTo(document); @@ -215,10 +146,10 @@ export default class Filelist { new RegularEvent('multiRecordSelection:action:delete', this.deleteMultiple).bindTo(document); new RegularEvent('multiRecordSelection:action:download', this.downloadFilesAndFolders).bindTo(document); new RegularEvent('multiRecordSelection:action:copyMarked', (event: CustomEvent): void => { - Filelist.submitClipboardFormWithCommand('copyMarked', event.target as HTMLButtonElement) + Filelist.submitClipboardFormWithCommand('copyMarked', event.target as HTMLButtonElement); }).bindTo(document); new RegularEvent('multiRecordSelection:action:removeMarked', (event: CustomEvent): void => { - Filelist.submitClipboardFormWithCommand('removeMarked', event.target as HTMLButtonElement) + Filelist.submitClipboardFormWithCommand('removeMarked', event.target as HTMLButtonElement); }).bindTo(document); // Respond to browser related clearable event @@ -231,6 +162,75 @@ export default class Filelist { }).delegateTo(document, Selectors.searchFieldSelector); } + public static submitClipboardFormWithCommand(cmd: string, target: HTMLButtonElement): void { + const fileListForm: HTMLFormElement = target.closest(Selectors.fileListFormSelector); + if (!fileListForm) { + return; + } + const commandField: HTMLInputElement = fileListForm.querySelector(Selectors.commandSelector); + if (!commandField) { + return; + } + commandField.value = cmd; + // In case we just change elements on the clipboard, we try to fetch a possible pointer from the query + // parameters, so after the form submit, we get to the same view as before. This is not done for delete + // commands, since this may lead to empty sites, in case all elements from the current site are deleted. + if (cmd === 'copyMarked' || cmd === 'removeMarked') { + const pointerField: HTMLInputElement = fileListForm.querySelector(Selectors.pointerFieldSelector); + const pointerValue: string = Filelist.parseQueryParameters(document.location).pointer; + if (pointerField && pointerValue) { + pointerField.value = pointerValue; + } + } + fileListForm.submit(); + } + + protected static openInfoPopup(type: string, identifier: string): void { + InfoWindow.showItem(type, identifier); + } + + private static processTriggers(): void { + const mainElement: HTMLElement = document.querySelector('.filelist-main'); + if (mainElement === null) { + return; + } + // update ModuleStateStorage to the current folder identifier + const id = encodeURIComponent(mainElement.dataset.filelistCurrentIdentifier); + ModuleStateStorage.update('file', id, true, undefined); + // emit event for currently shown folder so the folder tree gets updated + Filelist.emitTreeUpdateRequest( + mainElement.dataset.filelistCurrentIdentifier + ); + } + + private static emitTreeUpdateRequest(identifier: string): void { + const message = new BroadcastMessage( + 'filelist', + 'treeUpdateRequested', + { type: 'folder', identifier: identifier } + ); + broadcastService.post(message); + } + + private static parseQueryParameters(location: Location): QueryParameters { + const queryParameters: QueryParameters = {}; + if (location && Object.prototype.hasOwnProperty.call(location, 'search')) { + const parameters = location.search.substr(1).split('&'); + for (let i = 0; i < parameters.length; i++) { + const parameter = parameters[i].split('='); + queryParameters[decodeURIComponent(parameter[0])] = decodeURIComponent(parameter[1]); + } + } + return queryParameters; + } + + private static getReturnUrl(returnUrl: string): string { + if (returnUrl === '') { + returnUrl = top.list_frame.document.location.pathname + top.list_frame.document.location.search; + } + return encodeURIComponent(returnUrl); + } + private deleteMultiple(e: CustomEvent): void { e.preventDefault(); const eventDetails: ActionEventDetails = e.detail as ActionEventDetails; @@ -250,7 +250,7 @@ export default class Filelist { text: configuration.ok || TYPO3.lang['button.ok'] || 'OK', btnClass: 'btn-' + Severity.getCssClass(SeverityEnum.warning), trigger: (modalEvent: Event, modal: ModalElement) => { - Filelist.submitClipboardFormWithCommand('delete', e.target as HTMLButtonElement) + Filelist.submitClipboardFormWithCommand('delete', e.target as HTMLButtonElement); modal.hideModal(); } } @@ -302,7 +302,7 @@ export default class Filelist { } else { Notification.warning(lll('file_download.invalidSelection')); } - } + }; private triggerDownload(items: Array<string>, downloadUrl: string, button: HTMLElement | null): void { // Add notification about the download being prepared @@ -322,7 +322,7 @@ export default class Filelist { .configure({ parent: '#typo3-filelist', showSpinner: false }) .start(); (new AjaxRequest(downloadUrl)).post({ items: items }) - .then(async (response: AjaxResponse): Promise<any> => { + .then(async (response: AjaxResponse): Promise<void> => { let fileName = response.response.headers.get('Content-Disposition'); if (!fileName) { const data = await response.resolve(); diff --git a/Build/Sources/TypeScript/filelist/rename-file.ts b/Build/Sources/TypeScript/filelist/rename-file.ts index b48b85a1887c..4cad795a4498 100644 --- a/Build/Sources/TypeScript/filelist/rename-file.ts +++ b/Build/Sources/TypeScript/filelist/rename-file.ts @@ -11,8 +11,8 @@ * The TYPO3 project - inspiring people to share! */ -import {SeverityEnum} from '@typo3/backend/enum/severity'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { SeverityEnum } from '@typo3/backend/enum/severity'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Modal from '@typo3/backend/modal'; import DocumentService from '@typo3/core/document-service'; @@ -33,7 +33,7 @@ class RenameFile { public initialize(): void { const submitButton = document.querySelector('.t3js-submit-file-rename'); if (submitButton !== null) { - submitButton.addEventListener('click', this.checkForDuplicate) + submitButton.addEventListener('click', this.checkForDuplicate); } } @@ -45,7 +45,7 @@ class RenameFile { const destinationField = form.querySelector('input[name="data[rename][0][destination]"]') as HTMLInputElement; const conflictModeField = form.querySelector('input[name="data[rename][0][conflictMode]"]') as HTMLInputElement; - const data: any = { + const data: Record<string, string> = { fileName: fileNameField.value }; // destination is not set if we deal with a folder @@ -53,7 +53,7 @@ class RenameFile { data.fileTarget = destinationField.value; } - new AjaxRequest(TYPO3.settings.ajaxUrls.file_exists).withQueryArguments(data).get({cache: 'no-cache'}).then(async (response: AjaxResponse): Promise<void> => { + new AjaxRequest(TYPO3.settings.ajaxUrls.file_exists).withQueryArguments(data).get({ cache: 'no-cache' }).then(async (response: AjaxResponse): Promise<void> => { const result = await response.resolve(); const fileExists: boolean = typeof result.uid !== 'undefined'; diff --git a/Build/Sources/TypeScript/form/backend/helper.ts b/Build/Sources/TypeScript/form/backend/helper.ts index 540d5b833f60..877fda9d1a11 100644 --- a/Build/Sources/TypeScript/form/backend/helper.ts +++ b/Build/Sources/TypeScript/form/backend/helper.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import {loadModule, JavaScriptItemPayload} from '@typo3/core/java-script-item-processor'; +import { loadModule, JavaScriptItemPayload } from '@typo3/core/java-script-item-processor'; interface ModuleRequirements { app: JavaScriptItemPayload; @@ -29,9 +29,11 @@ interface FormManagerLike { run(): FormEditorLike; } +// eslint-disable-next-line @typescript-eslint/no-empty-interface interface MediatorLike { } +// eslint-disable-next-line @typescript-eslint/no-empty-interface interface ViewModelLike { } diff --git a/Build/Sources/TypeScript/install/ajax/ajax-queue.ts b/Build/Sources/TypeScript/install/ajax/ajax-queue.ts index 2f7de0c935e4..824ec1e89cb6 100644 --- a/Build/Sources/TypeScript/install/ajax/ajax-queue.ts +++ b/Build/Sources/TypeScript/install/ajax/ajax-queue.ts @@ -12,13 +12,15 @@ */ import AjaxRequest from '@typo3/core/ajax/ajax-request'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; + interface Payload { url: string; method?: string; data?: { [key: string]: any}, - onfulfilled: Function; - onrejected: Function; + onfulfilled: (value: AjaxResponse) => Promise<void>; + onrejected: (reason: string) => void; finally?: Function; } @@ -54,7 +56,7 @@ class AjaxQueue { private async sendRequest(payload: Payload): Promise<void> { const request = new AjaxRequest(payload.url); - let response: any; + let response: Promise<AjaxResponse>; if (typeof payload.method !== 'undefined' && payload.method.toUpperCase() === 'POST') { response = request.post(payload.data); } else { diff --git a/Build/Sources/TypeScript/install/install.ts b/Build/Sources/TypeScript/install/install.ts index 5ebda80ee168..577147a32c53 100644 --- a/Build/Sources/TypeScript/install/install.ts +++ b/Build/Sources/TypeScript/install/install.ts @@ -12,7 +12,6 @@ */ import DocumentService from '@typo3/core/document-service'; -import $ from 'jquery'; import Router from './router'; class Install { diff --git a/Build/Sources/TypeScript/install/installer.ts b/Build/Sources/TypeScript/install/installer.ts index ad68cadb8bcf..7698ac101de8 100644 --- a/Build/Sources/TypeScript/install/installer.ts +++ b/Build/Sources/TypeScript/install/installer.ts @@ -14,12 +14,13 @@ import DocumentService from '@typo3/core/document-service'; import $ from 'jquery'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import PasswordStrength from './module/password-strength'; import InfoBox from './renderable/info-box'; import ProgressBar from './renderable/progress-bar'; import Severity from './renderable/severity'; import '@typo3/backend/element/icon-element'; +import MessageInterface from '@typo3/install/message-interface'; /** * Walk through the installation process of TYPO3 @@ -85,7 +86,7 @@ class Installer { // Database connect db driver selection $(document).on('change', '#t3js-connect-database-driver', (e: JQueryEventObject): void => { - let driver: string = $(e.currentTarget).val(); + const driver: string = $(e.currentTarget).val(); $('.t3-install-driver-data').hide(); $('.t3-install-driver-data input').attr('disabled', 'disabled'); $('#' + driver + ' input').attr('disabled', null); @@ -108,7 +109,7 @@ class Installer { } private setProgress(done: number): void { - let $progressBar: JQuery = $(this.selectorProgressBar); + const $progressBar: JQuery = $(this.selectorProgressBar); let percent: number = 0; if (done !== 0) { percent = (done / 5) * 100; @@ -122,8 +123,8 @@ class Installer { private getMainLayout(): void { (new AjaxRequest(this.getUrl('mainLayout'))) - .get({cache: 'no-cache'}) - .then(async (response: AjaxResponse): Promise<any> => { + .get({ cache: 'no-cache' }) + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); $(this.selectorBody).empty().append(data.html); this.checkInstallerAvailable(); @@ -132,8 +133,8 @@ class Installer { private checkInstallerAvailable(): void { (new AjaxRequest(this.getUrl('checkInstallerAvailable'))) - .get({cache: 'no-cache'}) - .then(async (response: AjaxResponse): Promise<any> => { + .get({ cache: 'no-cache' }) + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); data.success ? this.checkEnvironmentAndFolders() @@ -142,10 +143,10 @@ class Installer { } private showInstallerNotAvailable(): void { - let $outputContainer: JQuery = $(this.selectorMainContent); + const $outputContainer: JQuery = $(this.selectorMainContent); (new AjaxRequest(this.getUrl('showInstallerNotAvailable'))) - .get({cache: 'no-cache'}) - .then(async (response: AjaxResponse): Promise<any> => { + .get({ cache: 'no-cache' }) + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { $outputContainer.empty().append(data.html); @@ -156,8 +157,8 @@ class Installer { private checkEnvironmentAndFolders(): void { this.setProgress(1); (new AjaxRequest(this.getUrl('checkEnvironmentAndFolders'))) - .get({cache: 'no-cache'}) - .then(async (response: AjaxResponse): Promise<any> => { + .get({ cache: 'no-cache' }) + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.checkTrustedHostsPattern(); @@ -168,33 +169,33 @@ class Installer { } private showEnvironmentAndFolders(): void { - let $outputContainer: JQuery = $(this.selectorMainContent); + const $outputContainer: JQuery = $(this.selectorMainContent); (new AjaxRequest(this.getUrl('showEnvironmentAndFolders'))) - .get({cache: 'no-cache'}) - .then(async (response: AjaxResponse): Promise<any> => { + .get({ cache: 'no-cache' }) + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { $outputContainer.empty().html(data.html); - let $detailContainer: JQuery = $('.t3js-installer-environment-details'); + const $detailContainer: JQuery = $('.t3js-installer-environment-details'); let hasMessage: boolean = false; if (Array.isArray(data.environmentStatusErrors)) { data.environmentStatusErrors.forEach((element: any): void => { hasMessage = true; - let message: any = InfoBox.render(element.severity, element.title, element.message); + const message = InfoBox.render(element.severity, element.title, element.message); $detailContainer.append(message); }); } if (Array.isArray(data.environmentStatusWarnings)) { data.environmentStatusWarnings.forEach((element: any): void => { hasMessage = true; - let message: any = InfoBox.render(element.severity, element.title, element.message); + const message = InfoBox.render(element.severity, element.title, element.message); $detailContainer.append(message); }); } if (Array.isArray(data.structureErrors)) { data.structureErrors.forEach((element: any): void => { hasMessage = true; - let message: any = InfoBox.render(element.severity, element.title, element.message); + const message = InfoBox.render(element.severity, element.title, element.message); $detailContainer.append(message); }); } @@ -210,8 +211,8 @@ class Installer { private executeEnvironmentAndFolders(): void { (new AjaxRequest(this.getUrl('executeEnvironmentAndFolders'))) - .get({cache: 'no-cache'}) - .then(async (response: AjaxResponse): Promise<any> => { + .get({ cache: 'no-cache' }) + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.checkTrustedHostsPattern(); @@ -223,8 +224,8 @@ class Installer { private checkTrustedHostsPattern(): void { (new AjaxRequest(this.getUrl('checkTrustedHostsPattern'))) - .get({cache: 'no-cache'}) - .then(async (response: AjaxResponse): Promise<any> => { + .get({ cache: 'no-cache' }) + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.executeSilentConfigurationUpdate(); @@ -236,7 +237,7 @@ class Installer { private executeAdjustTrustedHostsPattern(): void { (new AjaxRequest(this.getUrl('executeAdjustTrustedHostsPattern'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then((): void => { this.executeSilentConfigurationUpdate(); }); @@ -244,8 +245,8 @@ class Installer { private executeSilentConfigurationUpdate(): void { (new AjaxRequest(this.getUrl('executeSilentConfigurationUpdate'))) - .get({cache: 'no-cache'}) - .then(async (response: AjaxResponse): Promise<any> => { + .get({ cache: 'no-cache' }) + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.executeSilentTemplateFileUpdate(); @@ -257,8 +258,8 @@ class Installer { private executeSilentTemplateFileUpdate(): void { (new AjaxRequest(this.getUrl('executeSilentTemplateFileUpdate'))) - .get({cache: 'no-cache'}) - .then(async (response: AjaxResponse): Promise<any> => { + .get({ cache: 'no-cache' }) + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.checkDatabaseConnect(); @@ -271,8 +272,8 @@ class Installer { private checkDatabaseConnect(): void { this.setProgress(2); (new AjaxRequest(this.getUrl('checkDatabaseConnect'))) - .get({cache: 'no-cache'}) - .then(async (response: AjaxResponse): Promise<any> => { + .get({ cache: 'no-cache' }) + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.checkDatabaseSelect(); @@ -283,10 +284,10 @@ class Installer { } private showDatabaseConnect(): void { - let $outputContainer: JQuery = $(this.selectorMainContent); + const $outputContainer: JQuery = $(this.selectorMainContent); (new AjaxRequest(this.getUrl('showDatabaseConnect'))) - .get({cache: 'no-cache'}) - .then(async (response: AjaxResponse): Promise<any> => { + .get({ cache: 'no-cache' }) + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { $outputContainer.empty().html(data.html); @@ -296,25 +297,25 @@ class Installer { } private executeDatabaseConnect(): void { - let $outputContainer: JQuery = $(this.selectorDatabaseConnectOutput); - let postData: any = { + const $outputContainer: JQuery = $(this.selectorDatabaseConnectOutput); + const postData: Record<string, string> = { 'install[action]': 'executeDatabaseConnect', 'install[token]': $(this.selectorModuleContent).data('installer-database-connect-execute-token'), }; - for (let element of $(this.selectorBody + ' form').serializeArray()) { + for (const element of $(this.selectorBody + ' form').serializeArray()) { postData[element.name] = element.value; } (new AjaxRequest(this.getUrl())) .post(postData) - .then(async (response: AjaxResponse): Promise<any> => { + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.checkDatabaseSelect(); } else { if (Array.isArray(data.status)) { $outputContainer.empty(); - data.status.forEach((element: any): void => { - let message: any = InfoBox.render(element.severity, element.title, element.message); + data.status.forEach((element: MessageInterface): void => { + const message = InfoBox.render(element.severity, element.title, element.message); $outputContainer.append(message); }); } @@ -325,8 +326,8 @@ class Installer { private checkDatabaseSelect(): void { this.setProgress(3); (new AjaxRequest(this.getUrl('checkDatabaseSelect'))) - .get({cache: 'no-cache'}) - .then(async (response: AjaxResponse): Promise<any> => { + .get({ cache: 'no-cache' }) + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.checkDatabaseData(); @@ -337,10 +338,10 @@ class Installer { } private showDatabaseSelect(): void { - let $outputContainer: JQuery = $(this.selectorMainContent); + const $outputContainer: JQuery = $(this.selectorMainContent); (new AjaxRequest(this.getUrl('showDatabaseSelect'))) - .get({cache: 'no-cache'}) - .then(async (response: AjaxResponse): Promise<any> => { + .get({ cache: 'no-cache' }) + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { $outputContainer.empty().html(data.html); @@ -349,24 +350,24 @@ class Installer { } private executeDatabaseSelect(): void { - let $outputContainer: JQuery = $(this.selectorDatabaseSelectOutput); - let postData: { [id: string]: string } = { + const $outputContainer: JQuery = $(this.selectorDatabaseSelectOutput); + const postData: { [id: string]: string } = { 'install[action]': 'executeDatabaseSelect', 'install[token]': $(this.selectorModuleContent).data('installer-database-select-execute-token'), }; - for (let element of $(this.selectorBody + ' form').serializeArray()) { + for (const element of $(this.selectorBody + ' form').serializeArray()) { postData[element.name] = element.value; } (new AjaxRequest(this.getUrl())) .post(postData) - .then(async (response: AjaxResponse): Promise<any> => { + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.checkDatabaseRequirements(); } else { if (Array.isArray(data.status)) { - data.status.forEach((element: any): void => { - let message: any = InfoBox.render(element.severity, element.title, element.message); + data.status.forEach((element: MessageInterface): void => { + const message = InfoBox.render(element.severity, element.title, element.message); $outputContainer.empty().append(message); }); } @@ -375,25 +376,25 @@ class Installer { } private checkDatabaseRequirements(): void { - let $outputContainer: JQuery = $(this.selectorDatabaseSelectOutput); - let postData: any = { + const $outputContainer: JQuery = $(this.selectorDatabaseSelectOutput); + const postData: Record<string, string> = { 'install[action]': 'checkDatabaseRequirements', 'install[token]': $(this.selectorModuleContent).data('installer-database-check-requirements-execute-token'), }; - for (let element of $(this.selectorBody + ' form').serializeArray()) { + for (const element of $(this.selectorBody + ' form').serializeArray()) { postData[element.name] = element.value; } (new AjaxRequest(this.getUrl())) .post(postData) - .then(async (response: AjaxResponse): Promise<any> => { + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.checkDatabaseData(); } else { if (Array.isArray(data.status)) { $outputContainer.empty(); - data.status.forEach((element: any): void => { - let message: any = InfoBox.render(element.severity, element.title, element.message); + data.status.forEach((element: MessageInterface): void => { + const message = InfoBox.render(element.severity, element.title, element.message); $outputContainer.append(message); }); } @@ -404,8 +405,8 @@ class Installer { private checkDatabaseData(): void { this.setProgress(4); (new AjaxRequest(this.getUrl('checkDatabaseData'))) - .get({cache: 'no-cache'}) - .then(async (response: AjaxResponse): Promise<any> => { + .get({ cache: 'no-cache' }) + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.showDefaultConfiguration(); @@ -416,10 +417,10 @@ class Installer { } private showDatabaseData(): void { - let $outputContainer: JQuery = $(this.selectorMainContent); + const $outputContainer: JQuery = $(this.selectorMainContent); (new AjaxRequest(this.getUrl('showDatabaseData'))) - .get({cache: 'no-cache'}) - .then(async (response: AjaxResponse): Promise<any> => { + .get({ cache: 'no-cache' }) + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { $outputContainer.empty().html(data.html); @@ -428,28 +429,28 @@ class Installer { } private executeDatabaseData(): void { - let $outputContainer: JQuery = $(this.selectorDatabaseDataOutput); - let postData: any = { + const $outputContainer: JQuery = $(this.selectorDatabaseDataOutput); + const postData: Record<string, string> = { 'install[action]': 'executeDatabaseData', 'install[token]': $(this.selectorModuleContent).data('installer-database-data-execute-token'), }; - for (let element of $(this.selectorBody + ' form').serializeArray()) { + for (const element of $(this.selectorBody + ' form').serializeArray()) { postData[element.name] = element.value; } - let message: any = ProgressBar.render(Severity.loading, 'Loading...', ''); - $outputContainer.empty().html(message); + const message: JQuery = ProgressBar.render(Severity.loading, 'Loading...', ''); + $outputContainer.empty().append(message); (new AjaxRequest(this.getUrl())) .post(postData) - .then(async (response: AjaxResponse): Promise<any> => { + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.showDefaultConfiguration(); } else { if (Array.isArray(data.status)) { $outputContainer.empty(); - data.status.forEach((element: any): void => { - let m: any = InfoBox.render(element.severity, element.title, element.message); - $outputContainer.append(m); + data.status.forEach((element: MessageInterface): void => { + const message = InfoBox.render(element.severity, element.title, element.message); + $outputContainer.append(message); }); } } @@ -457,11 +458,11 @@ class Installer { } private showDefaultConfiguration(): void { - let $outputContainer: JQuery = $(this.selectorMainContent); + const $outputContainer: JQuery = $(this.selectorMainContent); this.setProgress(5); (new AjaxRequest(this.getUrl('showDefaultConfiguration'))) - .get({cache: 'no-cache'}) - .then(async (response: AjaxResponse): Promise<any> => { + .get({ cache: 'no-cache' }) + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { $outputContainer.empty().html(data.html); @@ -470,16 +471,16 @@ class Installer { } private executeDefaultConfiguration(): void { - let postData: any = { + const postData: Record<string, string> = { 'install[action]': 'executeDefaultConfiguration', 'install[token]': $(this.selectorModuleContent).data('installer-default-configuration-execute-token'), }; - for (let element of $(this.selectorBody + ' form').serializeArray()) { + for (const element of $(this.selectorBody + ' form').serializeArray()) { postData[element.name] = element.value; } (new AjaxRequest(this.getUrl())) .post(postData) - .then(async (response: AjaxResponse): Promise<any> => { + .then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); top.location.href = data.redirect; }); diff --git a/Build/Sources/TypeScript/install/module/abstract-interactable-module.ts b/Build/Sources/TypeScript/install/module/abstract-interactable-module.ts index 62eaccf1877a..d9601c7046c9 100644 --- a/Build/Sources/TypeScript/install/module/abstract-interactable-module.ts +++ b/Build/Sources/TypeScript/install/module/abstract-interactable-module.ts @@ -37,7 +37,7 @@ export abstract class AbstractInteractableModule { protected setModalButtonsState(interactable: boolean): void { this.getModalFooter().find('button').each((_: number, elem: Element): void => { - this.setModalButtonState($(elem), interactable) + this.setModalButtonState($(elem), interactable); }); } diff --git a/Build/Sources/TypeScript/install/module/environment/environment-check.ts b/Build/Sources/TypeScript/install/module/environment/environment-check.ts index 5b6685cf19f8..89c89fb0f0d0 100644 --- a/Build/Sources/TypeScript/install/module/environment/environment-check.ts +++ b/Build/Sources/TypeScript/install/module/environment/environment-check.ts @@ -13,8 +13,8 @@ import 'bootstrap'; import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Modal from '@typo3/backend/modal'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; @@ -67,25 +67,25 @@ class EnvironmentCheck extends AbstractInteractableModule { modalContent.find(this.selectorOutputContainer).empty().append(message); (new AjaxRequest(Router.getUrl('environmentCheckGetStatus'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data: EnvironmentCheckResponse = await response.resolve(); modalContent.empty().append(data.html); Modal.setButtons(data.buttons); let warningCount = 0; let errorCount = 0; if (data.success === true && typeof (data.status) === 'object') { - for (let messages of Object.values(data.status)) { - for (let status of messages) { + for (const messages of Object.values(data.status)) { + for (const status of messages) { if (status.severity === 1) { warningCount++; } if (status.severity === 2) { errorCount++; } - const aMessage = InfoBox.render(status.severity, status.title, status.message); - modalContent.find(this.selectorOutputContainer).append(aMessage); + const message = InfoBox.render(status.severity, status.title, status.message); + modalContent.find(this.selectorOutputContainer).append(message); } } if (errorCount > 0) { diff --git a/Build/Sources/TypeScript/install/module/environment/folder-structure.ts b/Build/Sources/TypeScript/install/module/environment/folder-structure.ts index 2948a56acb51..09346f3f860d 100644 --- a/Build/Sources/TypeScript/install/module/environment/folder-structure.ts +++ b/Build/Sources/TypeScript/install/module/environment/folder-structure.ts @@ -13,8 +13,8 @@ import 'bootstrap'; import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Modal from '@typo3/backend/modal'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; @@ -60,9 +60,9 @@ class FolderStructure extends AbstractInteractableModule { ProgressBar.render(Severity.loading, 'Loading...', ''), ); (new AjaxRequest(Router.getUrl('folderStructureGetStatus'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); modalContent.empty().append(data.html); Modal.setButtons(data.buttons); @@ -74,8 +74,8 @@ class FolderStructure extends AbstractInteractableModule { data.errorStatus.forEach(((aElement: any): void => { errorCount++; $errorBadge.text(errorCount).show(); - const aMessage = InfoBox.render(aElement.severity, aElement.title, aElement.message); - modalContent.find(this.selectorErrorList).append(aMessage); + const message = InfoBox.render(aElement.severity, aElement.title, aElement.message); + modalContent.find(this.selectorErrorList).append(message); })); } else { modalContent.find(this.selectorErrorContainer).hide(); @@ -86,8 +86,8 @@ class FolderStructure extends AbstractInteractableModule { modalContent.find(this.selectorOkContainer).show(); modalContent.find(this.selectorOkList).empty(); data.okStatus.forEach(((aElement: any): void => { - const aMessage = InfoBox.render(aElement.severity, aElement.title, aElement.message); - modalContent.find(this.selectorOkList).append(aMessage); + const message = InfoBox.render(aElement.severity, aElement.title, aElement.message); + modalContent.find(this.selectorOkList).append(message); })); } else { modalContent.find(this.selectorOkContainer).hide(); @@ -113,12 +113,12 @@ class FolderStructure extends AbstractInteractableModule { const modalContent: JQuery = this.getModalBody(); const $outputContainer: JQuery = this.findInModal(this.selectorOutputContainer); - const message: any = ProgressBar.render(Severity.loading, 'Loading...', ''); - $outputContainer.empty().html(message); + const message = ProgressBar.render(Severity.loading, 'Loading...', ''); + $outputContainer.empty().append(message); (new AjaxRequest(Router.getUrl('folderStructureFix'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); FolderStructure.removeLoadingMessage($outputContainer); if (data.success === true && Array.isArray(data.fixedStatus)) { diff --git a/Build/Sources/TypeScript/install/module/environment/image-processing.ts b/Build/Sources/TypeScript/install/module/environment/image-processing.ts index e5bc62ad5515..d065ab219fd7 100644 --- a/Build/Sources/TypeScript/install/module/environment/image-processing.ts +++ b/Build/Sources/TypeScript/install/module/environment/image-processing.ts @@ -13,14 +13,15 @@ import 'bootstrap'; import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Modal from '@typo3/backend/modal'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import InfoBox from '../../renderable/info-box'; import Severity from '../../renderable/severity'; import Router from '../../router'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/image-processing @@ -46,9 +47,9 @@ class ImageProcessing extends AbstractInteractableModule { private getData(): void { const modalContent = this.getModalBody(); (new AjaxRequest(Router.getUrl('imageProcessingGetData'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { modalContent.empty().append(data.html); @@ -70,23 +71,23 @@ class ImageProcessing extends AbstractInteractableModule { this.setModalButtonsState(false); const $twinImageTemplate = this.findInModal(this.selectorTwinImageTemplate); - const promises: Array<Promise<any>> = []; - modalContent.find(this.selectorTestContainer).each((index: number, container: any): void => { + const promises: Array<Promise<void>> = []; + modalContent.find(this.selectorTestContainer).each((index: number, container: Element): void => { const $container: JQuery = $(container); const testType: string = $container.data('test'); - const message: any = InfoBox.render(Severity.loading, 'Loading...', ''); - $container.empty().html(message); + const message = InfoBox.render(Severity.loading, 'Loading...', ''); + $container.empty().append(message); const request = (new AjaxRequest(Router.getUrl(testType))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { $container.empty(); if (Array.isArray(data.status)) { - data.status.forEach((element: any): void => { - const aMessage = InfoBox.render(element.severity, element.title, element.message); - $container.append(aMessage); + data.status.forEach((element: MessageInterface): void => { + const message = InfoBox.render(element.severity, element.title, element.message); + $container.append(message); }); } const $aTwin = $twinImageTemplate.clone(); diff --git a/Build/Sources/TypeScript/install/module/environment/mail-test.ts b/Build/Sources/TypeScript/install/module/environment/mail-test.ts index 9189f8067711..194679187e37 100644 --- a/Build/Sources/TypeScript/install/module/environment/mail-test.ts +++ b/Build/Sources/TypeScript/install/module/environment/mail-test.ts @@ -12,8 +12,8 @@ */ import 'bootstrap'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Modal from '@typo3/backend/modal'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; @@ -21,6 +21,7 @@ import InfoBox from '../../renderable/info-box'; import ProgressBar from '../../renderable/progress-bar'; import Severity from '../../renderable/severity'; import Router from '../../router'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/create-admin @@ -45,9 +46,9 @@ class MailTest extends AbstractInteractableModule { private getData(): void { const modalContent = this.getModalBody(); (new AjaxRequest(Router.getUrl('mailTestGetData'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { modalContent.empty().append(data.html); @@ -67,8 +68,8 @@ class MailTest extends AbstractInteractableModule { const executeToken: string = this.getModuleContent().data('mail-test-token'); const $outputContainer: JQuery = this.findInModal(this.selectorOutputContainer); - const message: any = ProgressBar.render(Severity.loading, 'Loading...', ''); - $outputContainer.empty().html(message); + const message: JQuery = ProgressBar.render(Severity.loading, 'Loading...', ''); + $outputContainer.empty().append(message); (new AjaxRequest(Router.getUrl())).post({ install: { action: 'mailTest', @@ -76,13 +77,13 @@ class MailTest extends AbstractInteractableModule { email: this.findInModal('.t3js-mailTest-email').val(), }, }).then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); $outputContainer.empty(); if (Array.isArray(data.status)) { - data.status.forEach((element: any): void => { - const aMessage: any = InfoBox.render(element.severity, element.title, element.message); - $outputContainer.html(aMessage); + data.status.forEach((element: MessageInterface): void => { + const message = InfoBox.render(element.severity, element.title, element.message); + $outputContainer.empty().append(message); }); } else { Notification.error('Something went wrong', 'The request was not processed successfully. Please check the browser\'s console and TYPO3\'s log.'); diff --git a/Build/Sources/TypeScript/install/module/environment/php-info.ts b/Build/Sources/TypeScript/install/module/environment/php-info.ts index 44d4496824a5..473a1502b03f 100644 --- a/Build/Sources/TypeScript/install/module/environment/php-info.ts +++ b/Build/Sources/TypeScript/install/module/environment/php-info.ts @@ -14,14 +14,14 @@ import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Router from '../../router'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; /** * Module: @typo3/install/module/php-info */ class PhpInfo extends AbstractInteractableModule { - public initialize(currentModal: any): void { + public initialize(currentModal: JQuery): void { this.currentModal = currentModal; this.getData(); } @@ -29,9 +29,9 @@ class PhpInfo extends AbstractInteractableModule { private getData(): void { const modalContent = this.getModalBody(); (new AjaxRequest(Router.getUrl('phpInfoGetData'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { modalContent.empty().append(data.html); diff --git a/Build/Sources/TypeScript/install/module/environment/system-information.ts b/Build/Sources/TypeScript/install/module/environment/system-information.ts index 2801fdb1b756..5e9524a88539 100644 --- a/Build/Sources/TypeScript/install/module/environment/system-information.ts +++ b/Build/Sources/TypeScript/install/module/environment/system-information.ts @@ -14,14 +14,14 @@ import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Router from '../../router'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; /** * Module: @typo3/install/module/system-information */ class SystemInformation extends AbstractInteractableModule { - public initialize(currentModal: any): void { + public initialize(currentModal: JQuery): void { this.currentModal = currentModal; this.getData(); } @@ -29,9 +29,9 @@ class SystemInformation extends AbstractInteractableModule { private getData(): void { const modalContent = this.getModalBody(); (new AjaxRequest(Router.getUrl('systemInformationGetData'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { modalContent.empty().append(data.html); diff --git a/Build/Sources/TypeScript/install/module/maintenance/cache.ts b/Build/Sources/TypeScript/install/module/maintenance/cache.ts index 056c8dff3b2e..8851cdc50151 100644 --- a/Build/Sources/TypeScript/install/module/maintenance/cache.ts +++ b/Build/Sources/TypeScript/install/module/maintenance/cache.ts @@ -14,8 +14,9 @@ import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Router from '../../router'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInlineModule} from '../abstract-inline-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInlineModule } from '../abstract-inline-module'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/cache @@ -25,13 +26,13 @@ class Cache extends AbstractInlineModule { this.setButtonState($trigger, false); (new AjaxRequest(Router.getUrl('cacheClearAll', 'maintenance'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && Array.isArray(data.status)) { if (data.status.length > 0) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { Notification.success(element.title, element.message); }); } diff --git a/Build/Sources/TypeScript/install/module/maintenance/clear-tables.ts b/Build/Sources/TypeScript/install/module/maintenance/clear-tables.ts index 72a9fd2c196f..ef0422820139 100644 --- a/Build/Sources/TypeScript/install/module/maintenance/clear-tables.ts +++ b/Build/Sources/TypeScript/install/module/maintenance/clear-tables.ts @@ -12,12 +12,13 @@ */ import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Modal from '@typo3/backend/modal'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Router from '../../router'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/clear-tables @@ -32,7 +33,7 @@ class ClearTables extends AbstractInteractableModule { private selectorStatRows: string = '.t3js-clearTables-stat-rows'; private selectorStatName: string = '.t3js-clearTables-stat-name'; - public initialize(currentModal: any): void { + public initialize(currentModal: JQuery): void { this.currentModal = currentModal; this.getStats(); @@ -54,9 +55,9 @@ class ClearTables extends AbstractInteractableModule { const modalContent: JQuery = this.getModalBody(); (new AjaxRequest(Router.getUrl('clearTablesStats'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { modalContent.empty().append(data.html); @@ -95,10 +96,10 @@ class ClearTables extends AbstractInteractableModule { }, }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && Array.isArray(data.status)) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { Notification.success(element.title, element.message); }); } else { diff --git a/Build/Sources/TypeScript/install/module/maintenance/clear-typo3temp-files.ts b/Build/Sources/TypeScript/install/module/maintenance/clear-typo3temp-files.ts index 718a2b03d410..65d4d32b9a87 100644 --- a/Build/Sources/TypeScript/install/module/maintenance/clear-typo3temp-files.ts +++ b/Build/Sources/TypeScript/install/module/maintenance/clear-typo3temp-files.ts @@ -12,12 +12,13 @@ */ import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Modal from '@typo3/backend/modal'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Router from '../../router'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/clear-typo3temp-files @@ -53,9 +54,9 @@ class ClearTypo3tempFiles extends AbstractInteractableModule { const modalContent = this.getModalBody(); (new AjaxRequest(Router.getUrl('clearTypo3tempFilesStats'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { modalContent.empty().append(data.html); @@ -95,10 +96,10 @@ class ClearTypo3tempFiles extends AbstractInteractableModule { }, }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && Array.isArray(data.status)) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { Notification.success(element.title, element.message); }); this.getStats(); diff --git a/Build/Sources/TypeScript/install/module/maintenance/create-admin.ts b/Build/Sources/TypeScript/install/module/maintenance/create-admin.ts index 4258dfbfdeb6..5dec8a22d138 100644 --- a/Build/Sources/TypeScript/install/module/maintenance/create-admin.ts +++ b/Build/Sources/TypeScript/install/module/maintenance/create-admin.ts @@ -16,8 +16,9 @@ import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Router from '../../router'; import PasswordStrength from '../password-strength'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/create-admin @@ -42,9 +43,9 @@ class CreateAdmin extends AbstractInteractableModule { private getData(): void { const modalContent = this.getModalBody(); (new AjaxRequest(Router.getUrl('createAdminGetData'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { modalContent.empty().append(data.html); @@ -78,10 +79,10 @@ class CreateAdmin extends AbstractInteractableModule { }; this.getModuleContent().find(':input').prop('disabled', true); - (new AjaxRequest(Router.getUrl())).post(payload).then(async (response: AjaxResponse): Promise<any> => { + (new AjaxRequest(Router.getUrl())).post(payload).then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && Array.isArray(data.status)) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { Notification.showMessage(element.title, element.message, element.severity); }); if (data.userCreated) { diff --git a/Build/Sources/TypeScript/install/module/maintenance/database-analyzer.ts b/Build/Sources/TypeScript/install/module/maintenance/database-analyzer.ts index 81736f315ed4..c6b16258059b 100644 --- a/Build/Sources/TypeScript/install/module/maintenance/database-analyzer.ts +++ b/Build/Sources/TypeScript/install/module/maintenance/database-analyzer.ts @@ -12,8 +12,8 @@ */ import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Modal from '@typo3/backend/modal'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; @@ -21,6 +21,7 @@ import InfoBox from '../../renderable/info-box'; import ProgressBar from '../../renderable/progress-bar'; import Severity from '../../renderable/severity'; import Router from '../../router'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/database-analyzer @@ -55,9 +56,9 @@ class DatabaseAnalyzer extends AbstractInteractableModule { private getData(): void { const modalContent = this.getModalBody(); (new AjaxRequest(Router.getUrl('databaseAnalyzer'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { modalContent.empty().append(data.html); @@ -89,14 +90,14 @@ class DatabaseAnalyzer extends AbstractInteractableModule { }); (new AjaxRequest(Router.getUrl('databaseAnalyzerAnalyze'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { if (Array.isArray(data.status)) { outputContainer.find('.alert-loading').remove(); - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { const message = InfoBox.render(element.severity, element.title, element.message); outputContainer.append(message); }); @@ -158,8 +159,8 @@ class DatabaseAnalyzer extends AbstractInteractableModule { const executeToken = this.getModuleContent().data('database-analyzer-execute-token'); const outputContainer = modalContent.find(this.selectorOutputContainer); - const selectedHashes: Array<any> = []; - outputContainer.find('.t3js-databaseAnalyzer-suggestion-line input:checked').each((index: number, element: any): void => { + const selectedHashes: string[] = []; + outputContainer.find('.t3js-databaseAnalyzer-suggestion-line input:checked').each((index: number, element: Element): void => { selectedHashes.push($(element).data('hash')); }); outputContainer.empty().append(ProgressBar.render(Severity.loading, 'Executing database updates...', '')); @@ -173,10 +174,10 @@ class DatabaseAnalyzer extends AbstractInteractableModule { }, }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (Array.isArray(data.status)) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { Notification.showMessage(element.title, element.message, element.severity); }); } diff --git a/Build/Sources/TypeScript/install/module/maintenance/dump-autoload.ts b/Build/Sources/TypeScript/install/module/maintenance/dump-autoload.ts index 9aa18fcf7e4c..078d7580fd89 100644 --- a/Build/Sources/TypeScript/install/module/maintenance/dump-autoload.ts +++ b/Build/Sources/TypeScript/install/module/maintenance/dump-autoload.ts @@ -14,8 +14,9 @@ import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Router from '../../router'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInlineModule} from '../abstract-inline-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInlineModule } from '../abstract-inline-module'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/dump-autoload @@ -25,13 +26,13 @@ class DumpAutoload extends AbstractInlineModule { this.setButtonState($trigger, false); (new AjaxRequest(Router.getUrl('dumpAutoload'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && Array.isArray(data.status)) { if (data.status.length > 0) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { Notification.success(element.message); }); } diff --git a/Build/Sources/TypeScript/install/module/maintenance/language-packs.ts b/Build/Sources/TypeScript/install/module/maintenance/language-packs.ts index 063c7b665b75..bd836e5c1843 100644 --- a/Build/Sources/TypeScript/install/module/maintenance/language-packs.ts +++ b/Build/Sources/TypeScript/install/module/maintenance/language-packs.ts @@ -13,8 +13,8 @@ import 'bootstrap'; import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import SecurityUtility from '@typo3/core/security-utility'; import FlashMessage from '../../renderable/flash-message'; @@ -22,6 +22,7 @@ import InfoBox from '../../renderable/info-box'; import ProgressBar from '../../renderable/progress-bar'; import Severity from '../../renderable/severity'; import Router from '../../router'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/language-packs @@ -39,8 +40,8 @@ class LanguagePacks extends AbstractInteractableModule { private selectorLanguageUpdateIcon: string = '#t3js-languagePacks-languageUpdate-icon'; private selectorNotifications: string = '.t3js-languagePacks-notifications'; - private activeLanguages: Array<any> = []; - private activeExtensions: Array<any> = []; + private activeLanguages: string[] = []; + private activeExtensions: string[] = []; private packsUpdateDetails: { [id: string]: number } = { toHandle: 0, @@ -51,7 +52,7 @@ class LanguagePacks extends AbstractInteractableModule { skipped: 0, }; - private notifications: Array<any> = []; + private notifications: JQuery[] = []; private static pluralize(count: number, word: string = 'pack', suffix: string = 's', additionalCount: number = 0): string { return count !== 1 && additionalCount !== 1 ? word + suffix : word; @@ -90,9 +91,9 @@ class LanguagePacks extends AbstractInteractableModule { private getData(): void { const modalContent = this.getModalBody(); (new AjaxRequest(Router.getUrl('languagePacksGetData'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.activeLanguages = data.activeLanguages; @@ -131,17 +132,17 @@ class LanguagePacks extends AbstractInteractableModule { }, }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); $outputContainer.empty(); if (data.success === true && Array.isArray(data.status)) { - data.status.forEach((element: any): void => { - const m: any = InfoBox.render(element.severity, element.title, element.message); - this.addNotification(m); + data.status.forEach((element: MessageInterface): void => { + const message = InfoBox.render(element.severity, element.title, element.message); + this.addNotification(message); }); } else { - const m2: any = FlashMessage.render(Severity.error, 'Something went wrong', ''); - this.addNotification(m2); + const message = FlashMessage.render(Severity.error, 'Something went wrong', ''); + this.addNotification(message); } this.getData(); }, @@ -166,17 +167,17 @@ class LanguagePacks extends AbstractInteractableModule { }, }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); $outputContainer.empty(); if (data.success === true && Array.isArray(data.status)) { - data.status.forEach((element: any): void => { - const m: any = InfoBox.render(element.severity, element.title, element.message); - this.addNotification(m); + data.status.forEach((element: MessageInterface): void => { + const message = InfoBox.render(element.severity, element.title, element.message); + this.addNotification(message); }); } else { - const m2: any = FlashMessage.render(Severity.error, 'Something went wrong', ''); - this.addNotification(m2); + const message = FlashMessage.render(Severity.error, 'Something went wrong', ''); + this.addNotification(message); } this.getData(); }, @@ -207,7 +208,7 @@ class LanguagePacks extends AbstractInteractableModule { }; $outputContainer.empty().append( - $('<div>', {'class': 'progress'}).append( + $('<div>', { 'class': 'progress' }).append( $('<div>', { 'class': 'progress-bar progress-bar-info', 'role': 'progressbar', @@ -218,7 +219,7 @@ class LanguagePacks extends AbstractInteractableModule { }).append( $( '<span>', - {'class': 'text-nowrap'}).text('0 of ' + this.packsUpdateDetails.toHandle + ' language ' + + { 'class': 'text-nowrap' }).text('0 of ' + this.packsUpdateDetails.toHandle + ' language ' + LanguagePacks.pluralize(this.packsUpdateDetails.toHandle) + ' updated' ), ), @@ -239,7 +240,7 @@ class LanguagePacks extends AbstractInteractableModule { }, }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.packsUpdateDetails.handled++; @@ -269,7 +270,7 @@ class LanguagePacks extends AbstractInteractableModule { }); } - private packUpdateDone(updateIsoTimes: boolean, isos: Array<any>): void { + private packUpdateDone(updateIsoTimes: boolean, isos: string[]): void { const modalContent = this.getModalBody(); const $outputContainer = this.findInModal(this.selectorOutputContainer); if (this.packsUpdateDetails.handled === this.packsUpdateDetails.toHandle) { @@ -293,12 +294,12 @@ class LanguagePacks extends AbstractInteractableModule { }, }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.getData(); } else { - const m: any = FlashMessage.render(Severity.error, 'Something went wrong', ''); + const m = FlashMessage.render(Severity.error, 'Something went wrong', ''); this.addNotification(m); } }, @@ -337,7 +338,7 @@ class LanguagePacks extends AbstractInteractableModule { $tbody.append( $tr.append( $('<td>').text(' ' + language.name).prepend( - $('<div />', {class: 'btn-group'}).append( + $('<div />', { class: 'btn-group' }).append( $('<a>', { 'class': 'btn btn-default t3js-languagePacks-deactivateLanguage', 'data-iso': language.iso, @@ -354,9 +355,9 @@ class LanguagePacks extends AbstractInteractableModule { ); } else { $tbody.append( - $tr.addClass('t3-languagePacks-inactive t3js-languagePacks-inactive').css({'display': 'none'}).append( + $tr.addClass('t3-languagePacks-inactive t3js-languagePacks-inactive').css({ 'display': 'none' }).append( $('<td>').text(' ' + language.name).prepend( - $('<div />', {class: 'btn-group'}).append( + $('<div />', { class: 'btn-group' }).append( $('<a>', { 'class': 'btn btn-default t3js-languagePacks-activateLanguage', 'data-iso': language.iso, @@ -377,11 +378,11 @@ class LanguagePacks extends AbstractInteractableModule { }); $markupContainer.append( $('<h3>').text('Active languages'), - $('<table>', {'class': 'table table-striped table-bordered'}).append( + $('<table>', { 'class': 'table table-striped table-bordered' }).append( $('<thead>').append( $('<tr>').append( $('<th>').append( - $('<div />', {class: 'btn-group'}).append( + $('<div />', { class: 'btn-group' }).append( $('<button>', { 'class': 'btn btn-default t3js-languagePacks-addLanguage-toggle', 'type': 'button' @@ -389,7 +390,7 @@ class LanguagePacks extends AbstractInteractableModule { $('<span>').append(activateIcon), ' Add language', ), - $('<button>', {'class': 'btn btn-default disabled update-all t3js-languagePacks-update', 'type': 'button', 'disabled': 'disabled'}).append( + $('<button>', { 'class': 'btn btn-default disabled update-all t3js-languagePacks-update', 'type': 'button', 'disabled': 'disabled' }).append( $('<span>').append(updateIcon), ' Update all', ), @@ -502,7 +503,7 @@ class LanguagePacks extends AbstractInteractableModule { $markupContainer.append( $('<h3>').text('Translation status'), - $('<table>', {'class': 'table table-striped table-bordered'}).append( + $('<table>', { 'class': 'table table-striped table-bordered' }).append( $('<thead>').append($headerRow), $tbody, ), @@ -521,13 +522,13 @@ class LanguagePacks extends AbstractInteractableModule { return this.findInModal(this.selectorNotifications); } - private addNotification(notification: any): void { + private addNotification(notification: JQuery): void { this.notifications.push(notification); } private renderNotifications(): void { const $notificationBox: JQuery = this.getNotificationBox(); - for (let notification of this.notifications) { + for (const notification of this.notifications) { $notificationBox.append(notification); } this.notifications = []; diff --git a/Build/Sources/TypeScript/install/module/maintenance/reset-backend-user-uc.ts b/Build/Sources/TypeScript/install/module/maintenance/reset-backend-user-uc.ts index 43a72a405780..f5bafbad4008 100644 --- a/Build/Sources/TypeScript/install/module/maintenance/reset-backend-user-uc.ts +++ b/Build/Sources/TypeScript/install/module/maintenance/reset-backend-user-uc.ts @@ -12,10 +12,11 @@ */ import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInlineModule} from '../abstract-inline-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInlineModule } from '../abstract-inline-module'; import Notification from '@typo3/backend/notification'; import Router from '../../router'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/reset-backend-user-uc @@ -25,13 +26,13 @@ class ResetBackendUserUc extends AbstractInlineModule { this.setButtonState($trigger, false); (new AjaxRequest(Router.getUrl('resetBackendUserUc'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && Array.isArray(data.status)) { if (data.status.length > 0) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { Notification.success(element.title, element.message); }); } diff --git a/Build/Sources/TypeScript/install/module/settings/change-install-tool-password.ts b/Build/Sources/TypeScript/install/module/settings/change-install-tool-password.ts index 801ce9ad5d7e..527b81939612 100644 --- a/Build/Sources/TypeScript/install/module/settings/change-install-tool-password.ts +++ b/Build/Sources/TypeScript/install/module/settings/change-install-tool-password.ts @@ -16,8 +16,9 @@ import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Router from '../../router'; import PasswordStrength from '../password-strength'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/change-install-tool-password @@ -41,9 +42,9 @@ class ChangeInstallToolPassword extends AbstractInteractableModule { private getData(): void { const modalContent = this.getModalBody(); (new AjaxRequest(Router.getUrl('changeInstallToolPasswordGetData'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { modalContent.empty().append(data.html); @@ -70,10 +71,10 @@ class ChangeInstallToolPassword extends AbstractInteractableModule { password: this.findInModal('.t3js-changeInstallToolPassword-password').val(), passwordCheck: this.findInModal('.t3js-changeInstallToolPassword-password-check').val(), }, - }).then(async (response: AjaxResponse): Promise<any> => { + }).then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && Array.isArray(data.status)) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { Notification.showMessage(element.title, element.message, element.severity); }); } else { diff --git a/Build/Sources/TypeScript/install/module/settings/extension-configuration.ts b/Build/Sources/TypeScript/install/module/settings/extension-configuration.ts index e6609b1823f2..d952bff3cf10 100644 --- a/Build/Sources/TypeScript/install/module/settings/extension-configuration.ts +++ b/Build/Sources/TypeScript/install/module/settings/extension-configuration.ts @@ -13,14 +13,15 @@ import 'bootstrap'; import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import '../../renderable/clearable'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import ModuleMenu from '@typo3/backend/module-menu'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Router from '../../router'; -import {topLevelModuleImport} from '@typo3/backend/utility/top-level-module-import'; +import { topLevelModuleImport } from '@typo3/backend/utility/top-level-module-import'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/extension-configuration @@ -53,7 +54,7 @@ class ExtensionConfiguration extends AbstractInteractableModule { currentModal.on('keyup', this.selectorSearchInput, (e: JQueryEventObject): void => { const typedQuery = $(e.target).val(); const $searchInput = currentModal.find(this.selectorSearchInput); - currentModal.find('.search-item').each((index: number, element: any): void => { + currentModal.find('.search-item').each((index: number, element: Element): void => { const $item = $(element); if ($(':contains(' + typedQuery + ')', $item).length > 0 || $('input[value*="' + typedQuery + '"]', $item).length > 0) { $item.removeClass('hidden').addClass('searchhit'); @@ -77,9 +78,9 @@ class ExtensionConfiguration extends AbstractInteractableModule { private getContent(): void { const modalContent = this.getModalBody(); (new AjaxRequest(Router.getUrl('extensionConfigurationGetContent'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { modalContent.html(data.html); @@ -96,11 +97,11 @@ class ExtensionConfiguration extends AbstractInteractableModule { private initializeColorPicker(): void { const isInIframe = window.location !== window.parent.location; if (isInIframe) { - topLevelModuleImport('@typo3/backend/color-picker.js').then(({default: ColorPicker}: typeof import('@typo3/backend/color-picker')): void => { + topLevelModuleImport('@typo3/backend/color-picker.js').then(({ default: ColorPicker }: typeof import('@typo3/backend/color-picker')): void => { parent.document.querySelectorAll('.t3js-color-input').forEach((element: HTMLInputElement) => ColorPicker.initialize(element)); }); } else { - import('@typo3/backend/color-picker').then(({default: ColorPicker}): void => { + import('@typo3/backend/color-picker').then(({ default: ColorPicker }): void => { document.querySelectorAll('.t3js-color-input').forEach((element: HTMLInputElement) => ColorPicker.initialize(element)); }); } @@ -114,8 +115,8 @@ class ExtensionConfiguration extends AbstractInteractableModule { private write($form: JQuery): void { const modalContent = this.getModalBody(); const executeToken = this.getModuleContent().data('extension-configuration-write-token'); - const extensionConfiguration: any = {}; - for (let element of $form.serializeArray()) { + const extensionConfiguration: Record<string, string> = {}; + for (const element of $form.serializeArray()) { extensionConfiguration[element.name] = element.value; } @@ -129,10 +130,10 @@ class ExtensionConfiguration extends AbstractInteractableModule { }, }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && Array.isArray(data.status)) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { Notification.showMessage(element.title, element.message, element.severity); }); if ($('body').data('context') === 'backend') { @@ -152,7 +153,7 @@ class ExtensionConfiguration extends AbstractInteractableModule { * configuration properties */ private initializeWrap(): void { - this.findInModal('.t3js-emconf-offset').each((index: number, element: any): void => { + this.findInModal('.t3js-emconf-offset').each((index: number, element: Element): void => { const $me = $(element); const $parent = $me.parent(); const id = $me.attr('id'); @@ -164,9 +165,9 @@ class ExtensionConfiguration extends AbstractInteractableModule { .attr('data-offsetfield-y', '#' + id + '_offset_y') .wrap('<div class="hidden"></div>'); - const elementX = $('<div>', {'class': 'form-multigroup-item'}).append( - $('<div>', {'class': 'input-group'}).append( - $('<div>', {'class': 'input-group-addon'}).text('x'), + const elementX = $('<div>', { 'class': 'form-multigroup-item' }).append( + $('<div>', { 'class': 'input-group' }).append( + $('<div>', { 'class': 'input-group-addon' }).text('x'), $('<input>', { 'id': id + '_offset_x', 'class': 'form-control t3js-emconf-offsetfield', @@ -175,9 +176,9 @@ class ExtensionConfiguration extends AbstractInteractableModule { }), ), ); - const elementY = $('<div>', {'class': 'form-multigroup-item'}).append( - $('<div>', {'class': 'input-group'}).append( - $('<div>', {'class': 'input-group-addon'}).text('y'), + const elementY = $('<div>', { 'class': 'form-multigroup-item' }).append( + $('<div>', { 'class': 'input-group' }).append( + $('<div>', { 'class': 'input-group-addon' }).text('y'), $('<input>', { 'id': id + '_offset_y', 'class': 'form-control t3js-emconf-offsetfield', @@ -187,7 +188,7 @@ class ExtensionConfiguration extends AbstractInteractableModule { ), ); - const offsetGroup = $('<div>', {'class': 'form-multigroup-wrap'}).append(elementX, elementY); + const offsetGroup = $('<div>', { 'class': 'form-multigroup-wrap' }).append(elementX, elementY); $parent.append(offsetGroup); $parent.find('.t3js-emconf-offsetfield').on('keyup', (evt: JQueryEventObject): void => { const $target = $parent.find($(evt.currentTarget).data('target')); @@ -195,7 +196,7 @@ class ExtensionConfiguration extends AbstractInteractableModule { }); }); - this.findInModal('.t3js-emconf-wrap').each((index: number, element: any): void => { + this.findInModal('.t3js-emconf-wrap').each((index: number, element: Element): void => { const $me = $(element); const $parent = $me.parent(); const id = $me.attr('id'); @@ -206,8 +207,8 @@ class ExtensionConfiguration extends AbstractInteractableModule { .attr('data-wrapfield-end', '#' + id + '_wrap_end') .wrap('<div class="hidden"></div>'); - const wrapGroup = $('<div>', {'class': 'form-multigroup-wrap'}).append( - $('<div>', {'class': 'form-multigroup-item'}).append( + const wrapGroup = $('<div>', { 'class': 'form-multigroup-wrap' }).append( + $('<div>', { 'class': 'form-multigroup-item' }).append( $('<input>', { 'id': id + '_wrap_start', 'class': 'form-control t3js-emconf-wrapfield', @@ -215,7 +216,7 @@ class ExtensionConfiguration extends AbstractInteractableModule { 'value': valArr[0]?.trim(), }), ), - $('<div>', {'class': 'form-multigroup-item'}).append( + $('<div>', { 'class': 'form-multigroup-item' }).append( $('<input>', { 'id': id + '_wrap_end', 'class': 'form-control t3js-emconf-wrapfield', diff --git a/Build/Sources/TypeScript/install/module/settings/features.ts b/Build/Sources/TypeScript/install/module/settings/features.ts index 8ac4030face2..d16603d4c582 100644 --- a/Build/Sources/TypeScript/install/module/settings/features.ts +++ b/Build/Sources/TypeScript/install/module/settings/features.ts @@ -11,13 +11,13 @@ * The TYPO3 project - inspiring people to share! */ -import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Modal from '@typo3/backend/modal'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Router from '../../router'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/features @@ -25,7 +25,7 @@ import Router from '../../router'; class Features extends AbstractInteractableModule { private selectorSaveTrigger: string = '.t3js-features-save'; - public initialize(currentModal: any): void { + public initialize(currentModal: JQuery): void { this.currentModal = currentModal; this.getContent(); @@ -38,9 +38,9 @@ class Features extends AbstractInteractableModule { private getContent(): void { const modalContent = this.getModalBody(); (new AjaxRequest(Router.getUrl('featuresGetContent'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && data.html !== 'undefined' && data.html.length > 0) { modalContent.empty().append(data.html); @@ -60,8 +60,8 @@ class Features extends AbstractInteractableModule { const modalContent = this.getModalBody(); const executeToken = this.getModuleContent().data('features-save-token'); - const postData: any = {}; - for (let element of this.findInModal('form').serializeArray()) { + const postData: Record<string, string> = {}; + for (const element of this.findInModal('form').serializeArray()) { postData[element.name] = element.value; } postData['install[action]'] = 'featuresSave'; @@ -69,10 +69,10 @@ class Features extends AbstractInteractableModule { (new AjaxRequest(Router.getUrl())) .post(postData) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && Array.isArray(data.status)) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { Notification.showMessage(element.title, element.message, element.severity); }); this.getContent(); diff --git a/Build/Sources/TypeScript/install/module/settings/local-configuration.ts b/Build/Sources/TypeScript/install/module/settings/local-configuration.ts index 9cc70644cdcf..79fb7908e3d2 100644 --- a/Build/Sources/TypeScript/install/module/settings/local-configuration.ts +++ b/Build/Sources/TypeScript/install/module/settings/local-configuration.ts @@ -13,13 +13,14 @@ import 'bootstrap'; import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import '../../renderable/clearable'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Modal from '@typo3/backend/modal'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Router from '../../router'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/local-configuration @@ -49,8 +50,8 @@ class LocalConfiguration extends AbstractInteractableModule { }); // Make jquerys "contains" work case-insensitive - $.expr[':'].contains = $.expr.createPseudo((arg: any): Function => { - return (elem: any): boolean => { + $.expr[':'].contains = $.expr.createPseudo((arg: string): Function => { + return (elem: JQuery): boolean => { return $(elem).text().toUpperCase().includes(arg.toUpperCase()); }; }); @@ -82,8 +83,8 @@ class LocalConfiguration extends AbstractInteractableModule { }); } - private search(typedQuery: String): void { - this.currentModal.find(this.selectorItem).each((index: number, element: any): void => { + private search(typedQuery: string): void { + this.currentModal.find(this.selectorItem).each((index: number, element: Element): void => { const $item = $(element); if ($(':contains(' + typedQuery + ')', $item).length > 0 || $('input[value*="' + typedQuery + '"]', $item).length > 0) { $item.removeClass('hidden').addClass('searchhit'); @@ -97,9 +98,9 @@ class LocalConfiguration extends AbstractInteractableModule { private getContent(): void { const modalContent = this.getModalBody(); (new AjaxRequest(Router.getUrl('localConfigurationGetContent'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { modalContent.html(data.html); @@ -119,8 +120,8 @@ class LocalConfiguration extends AbstractInteractableModule { const modalContent: JQuery = this.getModalBody(); const executeToken: JQuery = this.getModuleContent().data('local-configuration-write-token'); - const configurationValues: any = {}; - this.findInModal('.t3js-localConfiguration-pathValue').each((i: number, element: any): void => { + const configurationValues: Record<string, string> = {}; + this.findInModal('.t3js-localConfiguration-pathValue').each((i: number, element: HTMLInputElement): void => { const $element: JQuery = $(element); if ($element.attr('type') === 'checkbox') { if (element.checked) { @@ -138,10 +139,10 @@ class LocalConfiguration extends AbstractInteractableModule { token: executeToken, configurationValues: configurationValues, }, - }).then(async (response: AjaxResponse): Promise<any> => { + }).then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && Array.isArray(data.status)) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { Notification.showMessage(element.title, element.message, element.severity); }); } else { diff --git a/Build/Sources/TypeScript/install/module/settings/presets.ts b/Build/Sources/TypeScript/install/module/settings/presets.ts index 2c112a634709..2f1aa20142d0 100644 --- a/Build/Sources/TypeScript/install/module/settings/presets.ts +++ b/Build/Sources/TypeScript/install/module/settings/presets.ts @@ -13,12 +13,13 @@ import 'bootstrap'; import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Modal from '@typo3/backend/modal'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Router from '../../router'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/presets @@ -53,9 +54,9 @@ class Presets extends AbstractInteractableModule { private getContent(): void { const modalContent = this.getModalBody(); (new AjaxRequest(Router.getUrl('presetsGetContent'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && data.html !== 'undefined' && data.html.length > 0) { modalContent.empty().append(data.html); @@ -86,7 +87,7 @@ class Presets extends AbstractInteractableModule { }, }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && data.html !== 'undefined' && data.html.length > 0) { modalContent.empty().append(data.html); @@ -105,17 +106,17 @@ class Presets extends AbstractInteractableModule { const modalContent: JQuery = this.getModalBody(); const executeToken: string = this.getModuleContent().data('presets-activate-token'); - const postData: any = {}; - for (let element of this.findInModal('form').serializeArray()) { + const postData: Record<string, string> = {}; + for (const element of this.findInModal('form').serializeArray()) { postData[element.name] = element.value; } postData['install[action]'] = 'presetsActivate'; postData['install[token]'] = executeToken; (new AjaxRequest(Router.getUrl())).post(postData).then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && Array.isArray(data.status)) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { Notification.showMessage(element.title, element.message, element.severity); }); } else { diff --git a/Build/Sources/TypeScript/install/module/settings/system-maintainer.ts b/Build/Sources/TypeScript/install/module/settings/system-maintainer.ts index 9c4d4c052a39..6d940e00ede8 100644 --- a/Build/Sources/TypeScript/install/module/settings/system-maintainer.ts +++ b/Build/Sources/TypeScript/install/module/settings/system-maintainer.ts @@ -13,13 +13,29 @@ import 'bootstrap'; import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; -import {topLevelModuleImport} from '@typo3/backend/utility/top-level-module-import'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; +import { topLevelModuleImport } from '@typo3/backend/utility/top-level-module-import'; import Modal from '@typo3/backend/modal'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Router from '../../router'; +import MessageInterface from '@typo3/install/message-interface'; + +type SystemMaintainerListResponse = { + success: boolean; + users: { + uid: number; + username: string; + disable: boolean; + isSystemMaintainer: boolean; + }[]; + html: string; + buttons: { + btnClass: string; + text: string + }[] +} /** * Module: @typo3/install/module/system-maintainer @@ -51,27 +67,27 @@ class SystemMaintainer extends AbstractInteractableModule { private getList(): void { const modalContent = this.getModalBody(); (new AjaxRequest(Router.getUrl('systemMaintainerGetList'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { - const data = await response.resolve(); + async (response: AjaxResponse): Promise<void> => { + const data: SystemMaintainerListResponse = await response.resolve(); if (data.success === true) { modalContent.html(data.html); Modal.setButtons(data.buttons); if (Array.isArray(data.users)) { - data.users.forEach((element: any): void => { + data.users.forEach((element): void => { let name = element.username; if (element.disable) { name = '[DISABLED] ' + name; } - const $option = $('<option>', {'value': element.uid}).text(name); + const $option = $('<option>', { 'value': element.uid }).text(name); if (element.isSystemMaintainer) { $option.attr('selected', 'selected'); } modalContent.find(this.selectorChosenField).append($option); }); } - const config: any = { + const config: { [key: string]: Record<string, string> } = { '.t3js-systemMaintainer-chosen-select': { width: '100%', placeholder_text_multiple: 'users', @@ -79,7 +95,7 @@ class SystemMaintainer extends AbstractInteractableModule { }; for (const selector in config) { - if (config.hasOwnProperty(selector)) { + if (selector in config) { modalContent.find(selector).chosen(config[selector]); } } @@ -105,11 +121,11 @@ class SystemMaintainer extends AbstractInteractableModule { token: executeToken, action: 'systemMaintainerWrite', }, - }).then(async (response: AjaxResponse): Promise<any> => { + }).then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { if (Array.isArray(data.status)) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { Notification.success(element.title, element.message); }); } diff --git a/Build/Sources/TypeScript/install/module/upgrade/core-update.ts b/Build/Sources/TypeScript/install/module/upgrade/core-update.ts index de6efc5d1584..20f24095b44d 100644 --- a/Build/Sources/TypeScript/install/module/upgrade/core-update.ts +++ b/Build/Sources/TypeScript/install/module/upgrade/core-update.ts @@ -12,8 +12,8 @@ */ import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Modal from '@typo3/backend/modal'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; @@ -77,7 +77,7 @@ class CoreUpdate extends AbstractInteractableModule { /** * Clone of a DOM object acts as button template */ - private buttonTemplate: any = null; + private buttonTemplate: JQuery = null; /** * Fetching the templates out of the DOM @@ -111,12 +111,12 @@ class CoreUpdate extends AbstractInteractableModule { }); } - private getData(): Promise<any> { + private getData(): Promise<void> { const modalContent = this.getModalBody(); return (new AjaxRequest(Router.getUrl('coreUpdateGetData'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { modalContent.empty().append(data.html); @@ -161,9 +161,9 @@ class CoreUpdate extends AbstractInteractableModule { this.addLoadingMessage(this.actionQueue[actionName].loadingMessage); (new AjaxRequest(Router.getUrl())) .withQueryArguments(data) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const result = await response.resolve(); const canContinue = this.handleResult(result, this.actionQueue[actionName].finishMessage); if (canContinue === true && (this.actionQueue[actionName].nextActionName !== undefined)) { @@ -218,7 +218,7 @@ class CoreUpdate extends AbstractInteractableModule { * @param messages */ private showStatusMessages(messages: MessageInterface[]): void { - for (let element of messages) { + for (const element of messages) { this.addMessage(element.severity, element.title ?? '', element.message ?? ''); } } @@ -229,20 +229,12 @@ class CoreUpdate extends AbstractInteractableModule { * @param button */ private showActionButton(button: any): void { - let title = false; - let action = false; - if (button.title) { - title = button.title; - } - if (button.action) { - action = button.action; - } const domButton = this.buttonTemplate; - if (action) { - domButton.attr('data-action', action); + if (button.action) { + domButton.attr('data-action', button.action); } - if (title) { - domButton.text(title); + if (button.title) { + domButton.text(button.title); } this.findInModal(this.updateButton).replaceWith(domButton); } diff --git a/Build/Sources/TypeScript/install/module/upgrade/extension-compat-tester.ts b/Build/Sources/TypeScript/install/module/upgrade/extension-compat-tester.ts index 5db256c370b3..4ab46bdc4fbd 100644 --- a/Build/Sources/TypeScript/install/module/upgrade/extension-compat-tester.ts +++ b/Build/Sources/TypeScript/install/module/upgrade/extension-compat-tester.ts @@ -13,8 +13,8 @@ import 'bootstrap'; import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Modal from '@typo3/backend/modal'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; @@ -22,6 +22,7 @@ import InfoBox from '../../renderable/info-box'; import ProgressBar from '../../renderable/progress-bar'; import Severity from '../../renderable/severity'; import Router from '../../router'; +import MessageInterface from '@typo3/install/message-interface'; interface BrokenExtension { name: string; @@ -60,9 +61,9 @@ class ExtensionCompatTester extends AbstractInteractableModule { } (new AjaxRequest(Router.getUrl('extensionCompatTesterLoadedExtensionList'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); modalContent.empty().append(data.html); Modal.setButtons(data.buttons); @@ -83,7 +84,7 @@ class ExtensionCompatTester extends AbstractInteractableModule { this.renderFailureMessages('ext_tables.php', (await error.response.json()).brokenExtensions, $innerOutputContainer); }).finally((): void => { this.unlockModal(); - }) + }); }, async (error: AjaxResponse): Promise<void> => { this.renderFailureMessages('ext_localconf.php', (await error.response.json()).brokenExtensions, $innerOutputContainer); $innerOutputContainer.append( @@ -107,10 +108,10 @@ class ExtensionCompatTester extends AbstractInteractableModule { } private renderFailureMessages(scope: string, brokenExtensions: Array<BrokenExtension>, $outputContainer: JQuery): void { - for (let extension of brokenExtensions) { + for (const extension of brokenExtensions) { let uninstallAction; if (!extension.isProtected) { - uninstallAction = $('<button />', {'class': 'btn btn-danger t3js-extensionCompatTester-uninstall'}) + uninstallAction = $('<button />', { 'class': 'btn btn-danger t3js-extensionCompatTester-uninstall' }) .attr('data-extension', extension.name) .text('Uninstall extension "' + extension.name + '"'); } @@ -167,11 +168,11 @@ class ExtensionCompatTester extends AbstractInteractableModule { }, }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success) { if (Array.isArray(data.status)) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { const aMessage = InfoBox.render(element.severity, element.title, element.message); modalContent.find(this.selectorOutputContainer).empty().append(aMessage); }); diff --git a/Build/Sources/TypeScript/install/module/upgrade/extension-scanner.ts b/Build/Sources/TypeScript/install/module/upgrade/extension-scanner.ts index ededa67ef318..98ea6585dd35 100644 --- a/Build/Sources/TypeScript/install/module/upgrade/extension-scanner.ts +++ b/Build/Sources/TypeScript/install/module/upgrade/extension-scanner.ts @@ -14,8 +14,8 @@ import 'bootstrap'; import $ from 'jquery'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Modal from '@typo3/backend/modal'; import Notification from '@typo3/backend/notification'; import AjaxQueue from '../../ajax/ajax-queue'; @@ -49,7 +49,7 @@ interface RestFile { } class ExtensionScanner extends AbstractInteractableModule { - private listOfAffectedRestFileHashes: Array<any> = []; + private listOfAffectedRestFileHashes: string[] = []; private selectorExtensionContainer: string = '.t3js-extensionScanner-extension'; private selectorNumberOfFiles: string = '.t3js-extensionScanner-number-of-files'; private selectorScanSingleTrigger: string = '.t3js-extensionScanner-scan-single'; @@ -86,7 +86,7 @@ class ExtensionScanner extends AbstractInteractableModule { private getData(): void { const modalContent = this.getModalBody(); (new AjaxRequest(Router.getUrl('extensionScannerGetData'))).get().then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { modalContent.empty().append(data.html); @@ -114,7 +114,7 @@ class ExtensionScanner extends AbstractInteractableModule { .find('span') .text('0%'); this.setProgressForAll(); - $extensions.each((index: number, element: any): void => { + $extensions.each((index: number, element: Element): void => { const $me: JQuery = $(element); const extension = $me.data('extension'); this.scanSingleExtension(extension); @@ -166,7 +166,7 @@ class ExtensionScanner extends AbstractInteractableModule { hashes: Array.from(new Set(this.listOfAffectedRestFileHashes)), }, }).then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { Notification.success('Marked not affected files', 'Marked ' + data.markedAsNotAffected + ' ReST files as not affected.'); @@ -204,7 +204,7 @@ class ExtensionScanner extends AbstractInteractableModule { extension: extension, }, }).then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && Array.isArray(data.files)) { const numberOfFiles = data.files.length; @@ -237,7 +237,7 @@ class ExtensionScanner extends AbstractInteractableModule { if (fileData.success && Array.isArray(fileData.matches)) { fileData.matches.forEach((match: Match): void => { hitFound = true; - const aMatch: any = modalContent.find(hitTemplate).find('.panel').clone(); + const aMatch: JQuery = modalContent.find(hitTemplate).find('.panel').clone(); aMatch.find('.t3js-extensionScanner-hit-file-panel-head').attr('href', '#collapse' + match.uniqueId); aMatch.find('.t3js-extensionScanner-hit-file-panel-body').attr('id', 'collapse' + match.uniqueId); aMatch.find('.t3js-extensionScanner-hit-filename').text(file); @@ -270,7 +270,7 @@ class ExtensionScanner extends AbstractInteractableModule { }); } const panelClass = - aMatch.find('.panel-breaking', '.t3js-extensionScanner-hit-file-rest-container').length > 0 + aMatch.find('.panel-breaking, .t3js-extensionScanner-hit-file-rest-container').length > 0 ? 'panel-danger' : 'panel-warning'; aMatch.addClass(panelClass); diff --git a/Build/Sources/TypeScript/install/module/upgrade/tca-ext-tables-check.ts b/Build/Sources/TypeScript/install/module/upgrade/tca-ext-tables-check.ts index 90cbdc4e7a90..1e20deeb857d 100644 --- a/Build/Sources/TypeScript/install/module/upgrade/tca-ext-tables-check.ts +++ b/Build/Sources/TypeScript/install/module/upgrade/tca-ext-tables-check.ts @@ -12,8 +12,8 @@ */ import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Modal from '@typo3/backend/modal'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; @@ -21,6 +21,7 @@ import InfoBox from '../../renderable/info-box'; import ProgressBar from '../../renderable/progress-bar'; import Severity from '../../renderable/severity'; import Router from '../../router'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/tca-ext-tables-check @@ -43,30 +44,30 @@ class TcaExtTablesCheck extends AbstractInteractableModule { const modalContent = this.getModalBody(); const $outputContainer = $(this.selectorOutputContainer); - const m: any = ProgressBar.render(Severity.loading, 'Loading...', ''); - $outputContainer.empty().html(m); + const m: JQuery = ProgressBar.render(Severity.loading, 'Loading...', ''); + $outputContainer.empty().append(m); (new AjaxRequest(Router.getUrl('tcaExtTablesCheck'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); modalContent.empty().append(data.html); Modal.setButtons(data.buttons); if (data.success === true && Array.isArray(data.status)) { if (data.status.length > 0) { - const aMessage: any = InfoBox.render( + const aMessage = InfoBox.render( Severity.warning, 'Following extensions change TCA in ext_tables.php', 'Check ext_tables.php files, look for ExtensionManagementUtility calls and $GLOBALS[\'TCA\'] modifications', ); modalContent.find(this.selectorOutputContainer).append(aMessage); - data.status.forEach((element: any): void => { - const m2: any = InfoBox.render(element.severity, element.title, element.message); + data.status.forEach((element: MessageInterface): void => { + const m2 = InfoBox.render(element.severity, element.title, element.message); $outputContainer.append(m2); modalContent.append(m2); }); } else { - const aMessage: any = InfoBox.render(Severity.ok, 'No TCA changes in ext_tables.php files. Good job!', ''); + const aMessage = InfoBox.render(Severity.ok, 'No TCA changes in ext_tables.php files. Good job!', ''); modalContent.find(this.selectorOutputContainer).append(aMessage); } } else { diff --git a/Build/Sources/TypeScript/install/module/upgrade/tca-migrations-check.ts b/Build/Sources/TypeScript/install/module/upgrade/tca-migrations-check.ts index afb4907e78ea..c040871c8246 100644 --- a/Build/Sources/TypeScript/install/module/upgrade/tca-migrations-check.ts +++ b/Build/Sources/TypeScript/install/module/upgrade/tca-migrations-check.ts @@ -12,8 +12,8 @@ */ import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Modal from '@typo3/backend/modal'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import FlashMessage from '../../renderable/flash-message'; @@ -21,6 +21,7 @@ import InfoBox from '../../renderable/info-box'; import ProgressBar from '../../renderable/progress-bar'; import Severity from '../../renderable/severity'; import Router from '../../router'; +import MessageInterface from '@typo3/install/message-interface'; /** * Module: @typo3/install/module/tca-migrations-check @@ -43,25 +44,25 @@ class TcaMigrationsCheck extends AbstractInteractableModule { const $outputContainer: JQuery = $(this.selectorOutputContainer); const modalContent: JQuery = this.getModalBody(); - const message: any = ProgressBar.render(Severity.loading, 'Loading...', ''); - $outputContainer.empty().html(message); + const message: JQuery = ProgressBar.render(Severity.loading, 'Loading...', ''); + $outputContainer.empty().append(message); (new AjaxRequest(Router.getUrl('tcaMigrationsCheck'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); modalContent.empty().append(data.html); Modal.setButtons(data.buttons); if (data.success === true && Array.isArray(data.status)) { if (data.status.length > 0) { - const m: any = InfoBox.render( + const m = InfoBox.render( Severity.warning, 'TCA migrations need to be applied', 'Check the following list and apply needed changes.', ); modalContent.find(this.selectorOutputContainer).empty(); modalContent.find(this.selectorOutputContainer).append(m); - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { const m2 = InfoBox.render(element.severity, element.title, element.message); modalContent.find(this.selectorOutputContainer).append(m2); }); diff --git a/Build/Sources/TypeScript/install/module/upgrade/upgrade-docs.ts b/Build/Sources/TypeScript/install/module/upgrade/upgrade-docs.ts index ba45617dcf34..09176a9947c9 100644 --- a/Build/Sources/TypeScript/install/module/upgrade/upgrade-docs.ts +++ b/Build/Sources/TypeScript/install/module/upgrade/upgrade-docs.ts @@ -13,12 +13,12 @@ import 'bootstrap'; import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import '../../renderable/clearable'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {topLevelModuleImport} from '@typo3/backend/utility/top-level-module-import'; +import { topLevelModuleImport } from '@typo3/backend/utility/top-level-module-import'; import Router from '../../router'; import DebounceEvent from '@typo3/core/event/debounce-event'; import '@typo3/backend/element/icon-element'; @@ -58,8 +58,8 @@ class UpgradeDocs extends AbstractInteractableModule { }); // Make jquerys "contains" work case-insensitive - $.expr[':'].contains = $.expr.createPseudo((arg: any): Function => { - return (elem: any): boolean => { + $.expr[':'].contains = $.expr.createPseudo((arg: string): Function => { + return (elem: JQuery): boolean => { return $(elem).text().toUpperCase().includes(arg.toUpperCase()); }; }); @@ -69,9 +69,9 @@ class UpgradeDocs extends AbstractInteractableModule { const modalContent = this.getModalBody(); (new AjaxRequest(Router.getUrl('upgradeDocsGetContent'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && data.html !== 'undefined' && data.html.length > 0) { modalContent.empty().append(data.html); @@ -88,18 +88,18 @@ class UpgradeDocs extends AbstractInteractableModule { } private loadChangelogs(): void { - const promises: Array<Promise<AjaxRequest>> = []; + const promises: Array<Promise<void>> = []; const modalContent = this.getModalBody(); - this.findInModal(this.selectorChangeLogsForVersionContainer).each((index: number, el: any): void => { + this.findInModal(this.selectorChangeLogsForVersionContainer).each((index: number, el: HTMLElement): void => { const request = (new AjaxRequest(Router.getUrl('upgradeDocsGetChangelogForVersion'))) .withQueryArguments({ install: { version: el.dataset.version, }, }) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { const $panelGroup = $(el); @@ -145,15 +145,15 @@ class UpgradeDocs extends AbstractInteractableModule { private initializeChosenSelector(): void { this.chosenField = this.getModalBody().find(this.selectorChosenField); - const config: any = { - '.chosen-select': {width: '100%', placeholder_text_multiple: 'tags'}, - '.chosen-select-deselect': {allow_single_deselect: true}, - '.chosen-select-no-single': {disable_search_threshold: 10}, - '.chosen-select-no-results': {no_results_text: 'Oops, nothing found!'}, - '.chosen-select-width': {width: '100%'}, + const config: { [key: string]: { [key: string]: string|number|boolean } } = { + '.chosen-select': { width: '100%', placeholder_text_multiple: 'tags' }, + '.chosen-select-deselect': { allow_single_deselect: true }, + '.chosen-select-no-single': { disable_search_threshold: 10 }, + '.chosen-select-no-results': { no_results_text: 'Oops, nothing found!' }, + '.chosen-select-width': { width: '100%' }, }; for (const selector in config) { - if (config.hasOwnProperty(selector)) { + if (selector in config) { this.findInModal(selector).chosen(config[selector]); } } @@ -173,10 +173,10 @@ class UpgradeDocs extends AbstractInteractableModule { */ private appendItemsToChosenSelector(): void { let tagString = ''; - $(this.findInModal(this.selectorUpgradeDoc)).each((index: number, element: any): void => { + $(this.findInModal(this.selectorUpgradeDoc)).each((index: number, element: Element): void => { tagString += $(element).data('item-tags') + ','; }); - let tagSet = new Set(tagString.slice(0, -1).split(',')); + const tagSet = new Set(tagString.slice(0, -1).split(',')); const uniqueTags = [...tagSet.values()].reduce((tagList: string[], tag: string): string[] => { const normalizedTag = tag.toLowerCase(); if (tagList.every(otherElement => otherElement.toLowerCase() !== normalizedTag)) { @@ -190,7 +190,7 @@ class UpgradeDocs extends AbstractInteractableModule { }); this.chosenField.prop('disabled', false); - for (let tag of uniqueTags) { + for (const tag of uniqueTags) { this.chosenField.append($('<option>').text(tag)); } this.chosenField.trigger('chosen:updated'); @@ -231,7 +231,7 @@ class UpgradeDocs extends AbstractInteractableModule { } // apply fulltext search const typedQuery = this.fulltextSearchField.val(); - modalContent.find('.filterhit').each((index: number, element: any): void => { + modalContent.find('.filterhit').each((index: number, element: Element): void => { const $item = $(element); if ($(':contains(' + typedQuery + ')', $item).length > 0 || $('input[value*="' + typedQuery + '"]', $item).length > 0) { $item.removeClass('hidden').addClass('searchhit'); @@ -244,13 +244,13 @@ class UpgradeDocs extends AbstractInteractableModule { // This is a workaround to improve the browser performance as the panels are not expanded at once window.setTimeout((): void => { $(item).collapse('show'); - }, 20) + }, 20); }); // Check for empty panels - modalContent.find('.panel-version').each((index: number, element: any): void => { - const $element: any = $(element); - if ($element.find('.searchhit', '.filterhit').length < 1) { + modalContent.find('.panel-version').each((index: number, element: Element): void => { + const $element: JQuery = $(element); + if ($element.find('.searchhit, .filterhit').length < 1) { $element.find(' > .panel-collapse').collapse('hide'); } }); @@ -264,7 +264,7 @@ class UpgradeDocs extends AbstractInteractableModule { $container.find('[data-item-state="notAffected"]').appendTo(this.findInModal('.panel-body-not-affected')); } - private markRead(element: any): void { + private markRead(element: Element): void { const modalContent = this.getModalBody(); const executeToken = this.getModuleContent().data('upgrade-docs-mark-read-token'); const $button = $(element).closest('button'); @@ -284,7 +284,7 @@ class UpgradeDocs extends AbstractInteractableModule { }); } - private unmarkRead(element: any): void { + private unmarkRead(element: Element): void { const modalContent = this.getModalBody(); const executeToken = this.getModuleContent().data('upgrade-docs-unmark-read-token'); const $button = $(element).closest('button'); diff --git a/Build/Sources/TypeScript/install/module/upgrade/upgrade-wizards.ts b/Build/Sources/TypeScript/install/module/upgrade/upgrade-wizards.ts index e58eedc533ab..38bcc44153a2 100644 --- a/Build/Sources/TypeScript/install/module/upgrade/upgrade-wizards.ts +++ b/Build/Sources/TypeScript/install/module/upgrade/upgrade-wizards.ts @@ -13,8 +13,8 @@ import 'bootstrap'; import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from '../abstract-interactable-module'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from '../abstract-interactable-module'; import Notification from '@typo3/backend/notification'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import SecurityUtility from '@typo3/core/security-utility'; @@ -23,6 +23,39 @@ import InfoBox from '../../renderable/info-box'; import ProgressBar from '../../renderable/progress-bar'; import Severity from '../../renderable/severity'; import Router from '../../router'; +import MessageInterface from '@typo3/install/message-interface'; + +type UpgradeWizardsBlockingDatabaseAddsResponse = { + success: boolean; + needsUpdate: boolean; + adds: { + tables?: { + table: string; + }[], + columns?: { + table: string; + field: string; + }[] + indexes?: { + table: string; + index: string; + }[] + } +}; + +type UpgradeWizard = { + class: string; + identifier: string; + title: string; + shouldRenderWizard: boolean; + explanation: string; +}; + +type UpgradeWizardDone = { + class: string; + identifier: string; + title: string; +}; /** * Module: @typo3/install/module/upgrade-wizards @@ -54,19 +87,19 @@ class UpgradeWizards extends AbstractInteractableModule { private selectorWizardsInputAbort: string = '.t3js-upgradeWizards-input-abort'; private securityUtility: SecurityUtility; + constructor() { + super(); + this.securityUtility = new SecurityUtility(); + } + private static removeLoadingMessage($container: JQuery): void { $container.find('.alert-loading').remove(); } - private static renderProgressBar(title: string): any { + private static renderProgressBar(title: string): JQuery { return ProgressBar.render(Severity.loading, title, ''); } - constructor() { - super(); - this.securityUtility = new SecurityUtility(); - } - public initialize(currentModal: JQuery): void { this.currentModal = currentModal; @@ -100,19 +133,19 @@ class UpgradeWizards extends AbstractInteractableModule { }); // Abort upgrade wizard - currentModal.on('click', this.selectorWizardsInputAbort, (e: JQueryEventObject): void => { + currentModal.on('click', this.selectorWizardsInputAbort, (): void => { this.findInModal(this.selectorOutputWizardsContainer).empty(); this.wizardsList(); }); } - private getData(): Promise<any> { + private getData(): Promise<void> { const modalContent = this.getModalBody(); const $outputContainer = this.findInModal(this.selectorOutputWizardsContainer); return (new AjaxRequest(Router.getUrl('upgradeWizardsGetData'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { modalContent.empty().append(data.html); @@ -130,11 +163,11 @@ class UpgradeWizards extends AbstractInteractableModule { private blockingUpgradesDatabaseCharsetTest(): void { const modalContent = this.getModalBody(); const $outputContainer = this.findInModal(this.selectorOutputWizardsContainer); - $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Checking database charset...')); + $outputContainer.empty().append(UpgradeWizards.renderProgressBar('Checking database charset...')); (new AjaxRequest(Router.getUrl('upgradeWizardsBlockingDatabaseCharsetTest'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); UpgradeWizards.removeLoadingMessage($outputContainer); if (data.success === true) { @@ -154,17 +187,17 @@ class UpgradeWizards extends AbstractInteractableModule { private blockingUpgradesDatabaseCharsetFix(): void { const $outputContainer = $(this.selectorOutputWizardsContainer); - $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Setting database charset to UTF-8...')); + $outputContainer.empty().append(UpgradeWizards.renderProgressBar('Setting database charset to UTF-8...')); (new AjaxRequest(Router.getUrl('upgradeWizardsBlockingDatabaseCharsetFix'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); UpgradeWizards.removeLoadingMessage($outputContainer); if (data.success === true) { if (Array.isArray(data.status) && data.status.length > 0) { - data.status.forEach((element: any): void => { - const message: any = InfoBox.render(element.severity, element.title, element.message); + data.status.forEach((element: MessageInterface): void => { + const message = InfoBox.render(element.severity, element.title, element.message); $outputContainer.append(message); }); } @@ -183,31 +216,31 @@ class UpgradeWizards extends AbstractInteractableModule { private blockingUpgradesDatabaseAdds(): void { const modalContent = this.getModalBody(); const $outputContainer = this.findInModal(this.selectorOutputWizardsContainer); - $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Check for missing mandatory database tables and fields...')); + $outputContainer.empty().append(UpgradeWizards.renderProgressBar('Check for missing mandatory database tables and fields...')); (new AjaxRequest(Router.getUrl('upgradeWizardsBlockingDatabaseAdds'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { - const data = await response.resolve(); + async (response: AjaxResponse): Promise<void> => { + const data: UpgradeWizardsBlockingDatabaseAddsResponse = await response.resolve(); UpgradeWizards.removeLoadingMessage($outputContainer); if (data.success === true) { if (data.needsUpdate === true) { const adds = modalContent.find(this.selectorWizardsBlockingAddsTemplate).clone(); if (typeof (data.adds.tables) === 'object') { - data.adds.tables.forEach((element: any): void => { + data.adds.tables.forEach((element): void => { const title = 'Table: ' + this.securityUtility.encodeHtml(element.table); adds.find(this.selectorWizardsBlockingAddsRows).append(title, '<br>'); }); } if (typeof (data.adds.columns) === 'object') { - data.adds.columns.forEach((element: any): void => { + data.adds.columns.forEach((element): void => { const title = 'Table: ' + this.securityUtility.encodeHtml(element.table) + ', Field: ' + this.securityUtility.encodeHtml(element.field); adds.find(this.selectorWizardsBlockingAddsRows).append(title, '<br>'); }); } if (typeof (data.adds.indexes) === 'object') { - data.adds.indexes.forEach((element: any): void => { + data.adds.indexes.forEach((element): void => { const title = 'Table: ' + this.securityUtility.encodeHtml(element.table) + ', Index: ' + this.securityUtility.encodeHtml(element.index); adds.find(this.selectorWizardsBlockingAddsRows).append(title, '<br>'); @@ -229,15 +262,15 @@ class UpgradeWizards extends AbstractInteractableModule { private blockingUpgradesDatabaseAddsExecute(): void { const $outputContainer = this.findInModal(this.selectorOutputWizardsContainer); - $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Adding database tables and fields...')); + $outputContainer.empty().append(UpgradeWizards.renderProgressBar('Adding database tables and fields...')); (new AjaxRequest(Router.getUrl('upgradeWizardsBlockingDatabaseExecute'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); UpgradeWizards.removeLoadingMessage($outputContainer); if (Array.isArray(data.status) && data.status.length > 0) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { const message = InfoBox.render(element.severity, element.title, element.message); $outputContainer.append(message); }); @@ -274,9 +307,9 @@ class UpgradeWizards extends AbstractInteractableModule { const $outputContainer = this.findInModal(this.selectorOutputWizardsContainer); $outputContainer.append(UpgradeWizards.renderProgressBar('Loading upgrade wizards...')); (new AjaxRequest(Router.getUrl('upgradeWizardsList'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); UpgradeWizards.removeLoadingMessage($outputContainer); const list = modalContent.find(this.selectorWizardsListTemplate).clone(); @@ -286,7 +319,7 @@ class UpgradeWizards extends AbstractInteractableModule { let numberOfWizards = 0; if (Array.isArray(data.wizards) && data.wizards.length > 0) { numberOfWizards = data.wizards.length; - data.wizards.forEach((element: any): void => { + data.wizards.forEach((element: UpgradeWizard): void => { if (element.shouldRenderWizard === true) { const aRow = modalContent.find(this.selectorWizardsListRowTemplate).clone(); numberOfWizardsTodo = numberOfWizardsTodo + 1; @@ -330,7 +363,7 @@ class UpgradeWizards extends AbstractInteractableModule { const executeToken = this.getModuleContent().data('upgrade-wizards-input-token'); const modalContent = this.getModalBody(); const $outputContainer = this.findInModal(this.selectorOutputWizardsContainer); - $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Loading "' + title + '"...')); + $outputContainer.empty().append(UpgradeWizards.renderProgressBar('Loading "' + title + '"...')); modalContent.animate( { @@ -348,14 +381,14 @@ class UpgradeWizards extends AbstractInteractableModule { }, }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); $outputContainer.empty(); const input = modalContent.find(this.selectorWizardsInputTemplate).clone(); input.removeClass('t3js-upgradeWizards-input'); if (data.success === true) { if (Array.isArray(data.status)) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { const message = FlashMessage.render(element.severity, element.title, element.message); $outputContainer.append(message); }); @@ -380,27 +413,27 @@ class UpgradeWizards extends AbstractInteractableModule { private wizardExecute(identifier: string, title: string): void { const executeToken = this.getModuleContent().data('upgrade-wizards-execute-token'); const modalContent = this.getModalBody(); - const postData: any = { + const postData: Record<string, string> = { 'install[action]': 'upgradeWizardsExecute', 'install[token]': executeToken, 'install[identifier]': identifier, }; - for (let element of this.findInModal(this.selectorOutputWizardsContainer + ' form').serializeArray()) { + for (const element of this.findInModal(this.selectorOutputWizardsContainer + ' form').serializeArray()) { postData[element.name] = element.value; } const $outputContainer = this.findInModal(this.selectorOutputWizardsContainer); // modalContent.find(this.selectorOutputWizardsContainer).empty(); - $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Executing "' + title + '"...')); + $outputContainer.empty().append(UpgradeWizards.renderProgressBar('Executing "' + title + '"...')); this.findInModal(this.selectorWizardsDoneRowMarkUndone).prop('disabled', true); (new AjaxRequest(Router.getUrl())) .post(postData) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); $outputContainer.empty(); if (data.success === true) { if (Array.isArray(data.status)) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { const message = InfoBox.render(element.severity, element.title, element.message); $outputContainer.append(message); }); @@ -421,16 +454,16 @@ class UpgradeWizards extends AbstractInteractableModule { private doneUpgrades(): void { const modalContent = this.getModalBody(); const $outputContainer = modalContent.find(this.selectorOutputDoneContainer); - $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Loading executed upgrade wizards...')); + $outputContainer.empty().append(UpgradeWizards.renderProgressBar('Loading executed upgrade wizards...')); (new AjaxRequest(Router.getUrl('upgradeWizardsDoneUpgrades'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); UpgradeWizards.removeLoadingMessage($outputContainer); if (data.success === true) { if (Array.isArray(data.status) && data.status.length > 0) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { const message = InfoBox.render(element.severity, element.title, element.message); $outputContainer.append(message); }); @@ -439,7 +472,7 @@ class UpgradeWizards extends AbstractInteractableModule { const $wizardsDoneContainer = body.find(this.selectorWizardsDoneRows); let hasBodyContent: boolean = false; if (Array.isArray(data.wizardsDone) && data.wizardsDone.length > 0) { - data.wizardsDone.forEach((element: any): void => { + data.wizardsDone.forEach((element: UpgradeWizardDone): void => { hasBodyContent = true; const aRow = modalContent.find(this.selectorWizardsDoneRowTemplate).clone(); aRow.find(this.selectorWizardsDoneRowMarkUndone).attr('data-identifier', element.identifier); @@ -448,7 +481,7 @@ class UpgradeWizards extends AbstractInteractableModule { }); } if (Array.isArray(data.rowUpdatersDone) && data.rowUpdatersDone.length > 0) { - data.rowUpdatersDone.forEach((element: any): void => { + data.rowUpdatersDone.forEach((element: UpgradeWizardDone): void => { hasBodyContent = true; const aRow = modalContent.find(this.selectorWizardsDoneRowTemplate).clone(); aRow.find(this.selectorWizardsDoneRowMarkUndone).attr('data-identifier', element.identifier); @@ -474,7 +507,7 @@ class UpgradeWizards extends AbstractInteractableModule { const executeToken = this.getModuleContent().data('upgrade-wizards-mark-undone-token'); const modalContent = this.getModalBody(); const $outputContainer = this.findInModal(this.selectorOutputDoneContainer); - $outputContainer.empty().html(UpgradeWizards.renderProgressBar('Marking upgrade wizard as undone...')); + $outputContainer.empty().append(UpgradeWizards.renderProgressBar('Marking upgrade wizard as undone...')); (new AjaxRequest(Router.getUrl())) .post({ install: { @@ -484,12 +517,12 @@ class UpgradeWizards extends AbstractInteractableModule { }, }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); $outputContainer.empty(); modalContent.find(this.selectorOutputDoneContainer).empty(); if (data.success === true && Array.isArray(data.status)) { - data.status.forEach((element: any): void => { + data.status.forEach((element: MessageInterface): void => { Notification.success(element.title, element.message); this.doneUpgrades(); this.blockingUpgradesDatabaseCharsetTest(); diff --git a/Build/Sources/TypeScript/install/renderable/clearable.ts b/Build/Sources/TypeScript/install/renderable/clearable.ts index 0e20724e0bd8..d5b69a839481 100644 --- a/Build/Sources/TypeScript/install/renderable/clearable.ts +++ b/Build/Sources/TypeScript/install/renderable/clearable.ts @@ -12,6 +12,14 @@ */ class Clearable { + constructor() { + if (typeof HTMLInputElement.prototype.clearable === 'function') { + return; + } + + this.registerClearable(); + } + private static createCloseButton(): HTMLButtonElement { // The inlined markup represents the current generated markup from the // icon api for the icon actions-close that can be found in the official @@ -49,14 +57,6 @@ class Clearable { return closeButton; } - constructor() { - if (typeof HTMLInputElement.prototype.clearable === 'function') { - return; - } - - this.registerClearable(); - } - private registerClearable(): void { HTMLInputElement.prototype.clearable = function(options: Options = {}): void { if (this.dataset.clearable) { @@ -87,7 +87,7 @@ class Clearable { options.onClear(this); } - this.dispatchEvent(new Event('change', {bubbles: true, cancelable: true})); + this.dispatchEvent(new Event('change', { bubbles: true, cancelable: true })); toggleClearButtonVisibility(); }); wrap.appendChild(clearButton); diff --git a/Build/Sources/TypeScript/install/renderable/flash-message.ts b/Build/Sources/TypeScript/install/renderable/flash-message.ts index a2e56e2124de..d4f206cb166b 100644 --- a/Build/Sources/TypeScript/install/renderable/flash-message.ts +++ b/Build/Sources/TypeScript/install/renderable/flash-message.ts @@ -21,7 +21,7 @@ class FlashMessage { private template: JQuery = $('<div class="t3js-message typo3-message alert"><h4></h4><p class="messageText"></p></div>'); public render(severity: number, title: string, message?: string): JQuery { - let flashMessage = this.template.clone(); + const flashMessage = this.template.clone(); flashMessage.addClass('alert-' + Severity.getCssClass(severity)); if (title) { flashMessage.find('h4').text(title); diff --git a/Build/Sources/TypeScript/install/renderable/info-box.ts b/Build/Sources/TypeScript/install/renderable/info-box.ts index 0fedfefaee0f..d57c87236106 100644 --- a/Build/Sources/TypeScript/install/renderable/info-box.ts +++ b/Build/Sources/TypeScript/install/renderable/info-box.ts @@ -26,7 +26,7 @@ class InfoBox { ); public render(severity: number, title: string, message?: string): JQuery { - let infoBox: JQuery = this.template.clone(); + const infoBox: JQuery = this.template.clone(); infoBox.addClass('callout-' + Severity.getCssClass(severity)); if (title) { infoBox.find('h4').text(title); diff --git a/Build/Sources/TypeScript/install/renderable/progress-bar.ts b/Build/Sources/TypeScript/install/renderable/progress-bar.ts index e9fc843de46f..ab43f1edb9ac 100644 --- a/Build/Sources/TypeScript/install/renderable/progress-bar.ts +++ b/Build/Sources/TypeScript/install/renderable/progress-bar.ts @@ -26,7 +26,7 @@ class ProgressBar { '</div>'); public render(severity: number, title: string, progress: string): JQuery { - let progressBar = this.template.clone(); + const progressBar = this.template.clone(); progressBar.addClass('progress-bar-' + Severity.getCssClass(severity)); if (progress) { progressBar.css('width', progress + '%'); diff --git a/Build/Sources/TypeScript/install/router.ts b/Build/Sources/TypeScript/install/router.ts index 0cf7b5969c2d..ef85be60d9d7 100644 --- a/Build/Sources/TypeScript/install/router.ts +++ b/Build/Sources/TypeScript/install/router.ts @@ -12,17 +12,18 @@ */ import $ from 'jquery'; -import {html} from 'lit'; +import { html } from 'lit'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {AbstractInteractableModule} from './module/abstract-interactable-module'; -import {AbstractInlineModule} from './module/abstract-inline-module'; -import {default as Modal, ModalElement} from '@typo3/backend/modal'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { AbstractInteractableModule } from './module/abstract-interactable-module'; +import { AbstractInlineModule } from './module/abstract-inline-module'; +import { default as Modal, ModalElement } from '@typo3/backend/modal'; import InfoBox from './renderable/info-box'; import ProgressBar from './renderable/progress-bar'; import Severity from './renderable/severity'; -import {topLevelModuleImport} from '@typo3/backend/utility/top-level-module-import'; +import { topLevelModuleImport } from '@typo3/backend/utility/top-level-module-import'; import '@typo3/backend/element/spinner-element'; +import MessageInterface from '@typo3/install/message-interface'; class Router { private rootSelector: string = '.t3js-body'; @@ -38,7 +39,7 @@ class Router { public setContent(content: string): void { - let container = this.rootContainer.querySelector(this.contentSelector) as HTMLElement + const container = this.rootContainer.querySelector(this.contentSelector) as HTMLElement; container.innerHTML = content; } @@ -72,13 +73,13 @@ class Router { const inlineState = $me.data('inline'); const isInline = typeof inlineState !== 'undefined' && parseInt(inlineState, 10) === 1; if (isInline) { - import(importModule).then(({default: aModule}: {default: AbstractInlineModule}): void => { + import(importModule).then(({ default: aModule }: {default: AbstractInlineModule}): void => { aModule.initialize($me); }); } else { const modalTitle = $me.closest('.card').find('.card-title').html(); const modalSize = $me.data('modalSize') || Modal.sizes.large; - const modal = Modal.advanced({ + Modal.advanced({ type: Modal.types.default, title: modalTitle, size: modalSize, @@ -86,11 +87,11 @@ class Router { additionalCssClasses: ['install-tool-modal'], staticBackdrop: true, callback: (currentModal: ModalElement): void => { - import(importModule).then(({default: aModule}: {default: AbstractInteractableModule}): void => { + import(importModule).then(({ default: aModule }: {default: AbstractInteractableModule}): void => { const isInIframe = window.location !== window.parent.location; // @todo: Rework AbstractInteractableModule to avoid JQuery usage and pass ModalElement if (isInIframe) { - topLevelModuleImport('jquery').then(({default: topLevelJQuery}: {default: JQueryStatic}): void => { + topLevelModuleImport('jquery').then(({ default: topLevelJQuery }: {default: JQueryStatic}): void => { aModule.initialize(topLevelJQuery(currentModal)); }); } else { @@ -140,9 +141,9 @@ class Router { public executeSilentConfigurationUpdate(): void { this.updateLoadingInfo('Checking session and executing silent configuration update'); (new AjaxRequest(this.getUrl('executeSilentConfigurationUpdate', 'layout'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.executeSilentTemplateFileUpdate(); @@ -151,7 +152,7 @@ class Router { } }, (error: AjaxResponse): void => { - this.handleAjaxError(error) + this.handleAjaxError(error); } ); } @@ -159,9 +160,9 @@ class Router { public executeSilentTemplateFileUpdate(): void { this.updateLoadingInfo('Checking session and executing silent template file update'); (new AjaxRequest(this.getUrl('executeSilentTemplateFileUpdate', 'layout'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.executeSilentExtensionConfigurationSynchronization(); @@ -170,7 +171,7 @@ class Router { } }, (error: AjaxResponse): void => { - this.handleAjaxError(error) + this.handleAjaxError(error); } ); } @@ -182,9 +183,9 @@ class Router { public executeSilentExtensionConfigurationSynchronization(): void { this.updateLoadingInfo('Executing silent extension configuration synchronization'); (new AjaxRequest(this.getUrl('executeSilentExtensionConfigurationSynchronization', 'layout'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.loadMainLayout(); @@ -193,7 +194,7 @@ class Router { } }, (error: AjaxResponse): void => { - this.handleAjaxError(error) + this.handleAjaxError(error); } ); } @@ -201,9 +202,9 @@ class Router { public loadMainLayout(): void { this.updateLoadingInfo('Loading main layout'); (new AjaxRequest(this.getUrl('mainLayout', 'layout', 'install[module]=' + this.controller))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && data.html !== 'undefined' && data.html.length > 0) { this.rootContainer.innerHTML = data.html; @@ -218,13 +219,12 @@ class Router { } }, (error: AjaxResponse): void => { - this.handleAjaxError(error) + this.handleAjaxError(error); } ); } - public async handleAjaxError(error: AjaxResponse, $outputContainer?: JQuery): Promise<any> { - let $message: any; + public async handleAjaxError(error: AjaxResponse, $outputContainer?: JQuery): Promise<void> { if (error.response.status === 403) { // Install tool session expired - depending on context render error message or login if (this.context === 'backend') { @@ -283,9 +283,9 @@ class Router { public checkEnableInstallToolFile(): void { (new AjaxRequest(this.getUrl('checkEnableInstallToolFile'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.checkLogin(); @@ -294,32 +294,32 @@ class Router { } }, (error: AjaxResponse): void => { - this.handleAjaxError(error) + this.handleAjaxError(error); } ); } public showEnableInstallTool(): void { (new AjaxRequest(this.getUrl('showEnableInstallToolFile'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.rootContainer.innerHTML = data.html; } }, (error: AjaxResponse): void => { - this.handleAjaxError(error) + this.handleAjaxError(error); } ); } public checkLogin(): void { (new AjaxRequest(this.getUrl('checkLogin'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.loadMainLayout(); @@ -328,31 +328,31 @@ class Router { } }, (error: AjaxResponse): void => { - this.handleAjaxError(error) + this.handleAjaxError(error); } ); } public showLogin(): void { (new AjaxRequest(this.getUrl('showLogin'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.rootContainer.innerHTML = data.html; } }, (error: AjaxResponse): void => { - this.handleAjaxError(error) + this.handleAjaxError(error); } ); } public login(): void { const $outputContainer: JQuery = $('.t3js-login-output'); - const message: any = ProgressBar.render(Severity.loading, 'Loading...', ''); - $outputContainer.empty().html(message); + const message: JQuery = ProgressBar.render(Severity.loading, 'Loading...', ''); + $outputContainer.empty().append(message); (new AjaxRequest(this.getUrl())) .post({ install: { @@ -362,44 +362,44 @@ class Router { }, }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.executeSilentConfigurationUpdate(); } else { - data.status.forEach((element: any): void => { - const m: any = InfoBox.render(element.severity, element.title, element.message); - $outputContainer.empty().html(m); + data.status.forEach((element: MessageInterface): void => { + const message = InfoBox.render(element.severity, element.title, element.message); + $outputContainer.empty().append(message); }); } }, (error: AjaxResponse): void => { - this.handleAjaxError(error) + this.handleAjaxError(error); } ); } public logout(): void { (new AjaxRequest(this.getUrl('logout'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true) { this.showEnableInstallTool(); } }, (error: AjaxResponse): void => { - this.handleAjaxError(error) + this.handleAjaxError(error); } ); } public loadCards(): void { (new AjaxRequest(this.getUrl('cards'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.success === true && data.html !== 'undefined' && data.html.length > 0) { this.setContent(data.html); @@ -408,7 +408,7 @@ class Router { } }, (error: AjaxResponse): void => { - this.handleAjaxError(error) + this.handleAjaxError(error); } ); } @@ -427,7 +427,7 @@ class Router { this.toggleMenu(true); }); document.querySelectorAll('[data-installroute-controller]').forEach((element: Element) => { - element.addEventListener('click', (event: MouseEvent) => { + element.addEventListener('click', () => { if (window.innerWidth < 768) { localStorage.setItem('typo3-install-modulesCollapsed', 'true'); } @@ -455,9 +455,9 @@ class Router { private preAccessCheck(): void { this.updateLoadingInfo('Execute pre access check'); (new AjaxRequest(this.getUrl('preAccessCheck', 'layout'))) - .get({cache: 'no-cache'}) + .get({ cache: 'no-cache' }) .then( - async (response: AjaxResponse): Promise<any> => { + async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (data.installToolLocked) { this.checkEnableInstallToolFile(); @@ -468,7 +468,7 @@ class Router { } }, (error: AjaxResponse): void => { - this.handleAjaxError(error) + this.handleAjaxError(error); } ); } diff --git a/Build/Sources/TypeScript/linkvalidator/linkvalidator.ts b/Build/Sources/TypeScript/linkvalidator/linkvalidator.ts index a53d43c89a74..d818fe4aca58 100644 --- a/Build/Sources/TypeScript/linkvalidator/linkvalidator.ts +++ b/Build/Sources/TypeScript/linkvalidator/linkvalidator.ts @@ -23,6 +23,13 @@ enum Selectors { * Module: @typo3/linkvalidator/linkvalidator */ class Linkvalidator { + constructor() { + this.initializeEvents(); + document.querySelectorAll(Selectors.settingsContainerSelector).forEach((container: HTMLElement): void => { + Linkvalidator.toggleActionButtons(container); + }); + } + private static toggleActionButtons(settingsContainer: HTMLElement): void { settingsContainer.querySelector(Selectors.actionButtonSelector)?.toggleAttribute( 'disabled', @@ -30,13 +37,6 @@ class Linkvalidator { ); } - constructor() { - this.initializeEvents(); - document.querySelectorAll(Selectors.settingsContainerSelector).forEach((container: HTMLElement): void => { - Linkvalidator.toggleActionButtons(container); - }) - } - private initializeEvents(): void { new RegularEvent('change', (e: Event, checkbox: HTMLInputElement): void => { Linkvalidator.toggleActionButtons(checkbox.closest(Selectors.settingsContainerSelector)); diff --git a/Build/Sources/TypeScript/lowlevel/query-generator.ts b/Build/Sources/TypeScript/lowlevel/query-generator.ts index 78705637eb3a..77a971d6de5b 100644 --- a/Build/Sources/TypeScript/lowlevel/query-generator.ts +++ b/Build/Sources/TypeScript/lowlevel/query-generator.ts @@ -53,7 +53,7 @@ class QueryGenerator { }); this.form.on('change', '[data-assign-store-control-title]', (evt: JQueryEventObject): void => { const $currentTarget = $(evt.currentTarget); - const $titleField = this.form.find('[name="storeControl\[title\]"]'); + const $titleField = this.form.find('[name="storeControl[title]"]'); if ($currentTarget.val() !== '0') { $titleField.val($currentTarget.find('option:selected').text()); } else { diff --git a/Build/Sources/TypeScript/lowlevel/reference-index.ts b/Build/Sources/TypeScript/lowlevel/reference-index.ts index cde274843bec..531dbe002404 100644 --- a/Build/Sources/TypeScript/lowlevel/reference-index.ts +++ b/Build/Sources/TypeScript/lowlevel/reference-index.ts @@ -30,7 +30,7 @@ class ReferenceIndex { private registerActionButtonEvents(): void { new RegularEvent('click', (e: Event, target: HTMLButtonElement): void => { - NProgress.configure({showSpinner: false}); + NProgress.configure({ showSpinner: false }); NProgress.start(); // Disable all action buttons to avoid duplicate execution Array.from(target.parentNode.querySelectorAll('button')).forEach((button: HTMLButtonElement) => { diff --git a/Build/Sources/TypeScript/opendocs/toolbar/opendocs-menu.ts b/Build/Sources/TypeScript/opendocs/toolbar/opendocs-menu.ts index b278d261878d..441ae538f349 100644 --- a/Build/Sources/TypeScript/opendocs/toolbar/opendocs-menu.ts +++ b/Build/Sources/TypeScript/opendocs/toolbar/opendocs-menu.ts @@ -12,11 +12,11 @@ */ import $ from 'jquery'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import Icons from '@typo3/backend/icons'; import Viewport from '@typo3/backend/viewport'; -import {ModuleStateStorage} from '@typo3/backend/storage/module-state-storage'; +import { ModuleStateStorage } from '@typo3/backend/storage/module-state-storage'; enum Selectors { containerSelector = '#typo3-cms-opendocs-backend-toolbaritems-opendocstoolbaritem', @@ -37,6 +37,14 @@ enum Selectors { class OpendocsMenu { private readonly hashDataAttributeName: string = 'opendocsidentifier'; + constructor() { + document.addEventListener('typo3:opendocs:updateRequested', () => this.updateMenu()); + Viewport.Topbar.Toolbar.registerEvent((): void => { + this.initializeEvents(); + this.updateMenu(); + }); + } + /** * Updates the number of open documents in the toolbar according to the * number of items in the menu bar. @@ -46,29 +54,18 @@ class OpendocsMenu { $(Selectors.counterSelector).text(num).toggle(num > 0); } - constructor() { - document.addEventListener( - 'typo3:opendocs:updateRequested', - (evt: CustomEvent) => this.updateMenu(), - ); - Viewport.Topbar.Toolbar.registerEvent((): void => { - this.initializeEvents(); - this.updateMenu(); - }); - } - /** * Displays the menu and does the AJAX call to the TYPO3 backend */ public updateMenu(): void { - let $toolbarItemIcon = $(Selectors.toolbarIconSelector, Selectors.containerSelector); - let $existingIcon = $toolbarItemIcon.clone(); + const $toolbarItemIcon = $(Selectors.toolbarIconSelector, Selectors.containerSelector); + const $existingIcon = $toolbarItemIcon.clone(); Icons.getIcon('spinner-circle-light', Icons.sizes.small).then((spinner: string): void => { $toolbarItemIcon.replaceWith(spinner); }); - (new AjaxRequest(TYPO3.settings.ajaxUrls.opendocs_menu)).get().then(async (response: AjaxResponse): Promise<any> => { + (new AjaxRequest(TYPO3.settings.ajaxUrls.opendocs_menu)).get().then(async (response: AjaxResponse): Promise<void> => { $(Selectors.containerSelector).find(Selectors.menuContainerSelector).html(await response.resolve()); OpendocsMenu.updateNumberOfDocs(); }).finally((): void => { @@ -91,7 +88,7 @@ class OpendocsMenu { ModuleStateStorage.updateWithCurrentMount('web', $entry.data('pid'), true); const router = document.querySelector('typo3-backend-module-router'); - router.setAttribute('endpoint', $entry.attr('href')) + router.setAttribute('endpoint', $entry.attr('href')); }); } @@ -103,7 +100,7 @@ class OpendocsMenu { if (md5sum) { payload.md5sum = md5sum; } - (new AjaxRequest(TYPO3.settings.ajaxUrls.opendocs_closedoc)).post(payload).then(async (response: AjaxResponse): Promise<any> => { + (new AjaxRequest(TYPO3.settings.ajaxUrls.opendocs_closedoc)).post(payload).then(async (response: AjaxResponse): Promise<void> => { $(Selectors.menuContainerSelector, Selectors.containerSelector).html(await response.resolve()); OpendocsMenu.updateNumberOfDocs(); // Re-open the menu after closing a document @@ -117,11 +114,10 @@ class OpendocsMenu { private toggleMenu = (): void => { $('.scaffold').removeClass('scaffold-toolbar-expanded'); $(Selectors.containerSelector).toggleClass('open'); - } + }; } -let opendocsMenuObject: OpendocsMenu; -opendocsMenuObject = new OpendocsMenu(); +const opendocsMenuObject = new OpendocsMenu(); if (typeof TYPO3 !== 'undefined') { TYPO3.OpendocsMenu = opendocsMenuObject; diff --git a/Build/Sources/TypeScript/recycler/recycler.ts b/Build/Sources/TypeScript/recycler/recycler.ts index 7503f018e4a2..827c2bbdba49 100644 --- a/Build/Sources/TypeScript/recycler/recycler.ts +++ b/Build/Sources/TypeScript/recycler/recycler.ts @@ -19,10 +19,10 @@ import '@typo3/backend/element/icon-element'; import DeferredAction from '@typo3/backend/action-button/deferred-action'; import Modal from '@typo3/backend/modal'; import Notification from '@typo3/backend/notification'; -import {SeverityEnum} from '@typo3/backend/enum/severity'; +import { SeverityEnum } from '@typo3/backend/enum/severity'; import RegularEvent from '@typo3/core/event/regular-event'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; enum RecyclerIdentifiers { searchForm = '#recycler-form', @@ -39,19 +39,34 @@ enum RecyclerIdentifiers { massDelete = 'button[data-multi-record-selection-action=massdelete]', } +interface RecyclerPagingConfig { + currentPage: number; + totalPages: number; + totalItems: number; + itemsPerPage: number; +} + +type RecordToDelete = string; + /** * Module: @typo3/recycler/recycler * RequireJS module for Recycler */ class Recycler { - public elements: any = {}; // filled in getElements() - public paging: any = { + public elements: { [key: string]: JQuery } = {}; // filled in getElements() + public paging: RecyclerPagingConfig = { currentPage: 1, totalPages: 1, totalItems: 0, - itemsPerPage: TYPO3.settings.Recycler.pagingSize, + itemsPerPage: parseInt(TYPO3.settings.Recycler.pagingSize, 10), }; - public markedRecordsForMassAction: Array<string> = []; + public markedRecordsForMassAction: RecordToDelete[] = []; + + constructor() { + DocumentService.ready().then((): void => { + this.initialize(); + }); + } /** * Reloads the page tree @@ -60,12 +75,6 @@ class Recycler { top.document.dispatchEvent(new CustomEvent('typo3:pagetree:refresh')); } - constructor() { - DocumentService.ready().then((): void => { - this.initialize(); - }); - } - /** * Gets required elements */ @@ -99,7 +108,7 @@ class Recycler { // changing the search field this.elements.$searchTextField.on('keyup', (e: JQueryEventObject): void => { - let $me = $(e.currentTarget); + const $me = $(e.currentTarget); if ($me.val() !== '') { this.elements.$searchSubmitBtn.removeClass('disabled'); @@ -108,7 +117,7 @@ class Recycler { this.loadDeletedElements(); } }); - this.elements.$searchTextField.get(0).clearable( + (this.elements.$searchTextField.get(0) as HTMLInputElement).clearable( { onClear: () => { this.elements.$searchSubmitBtn.addClass('disabled'); @@ -191,7 +200,7 @@ class Recycler { * Initialize the recycler module */ private initialize(): void { - NProgress.configure({parent: '.module-loading-indicator', showSpinner: false}); + NProgress.configure({ parent: '.module-loading-indicator', showSpinner: false }); this.getElements(); this.registerEvents(); @@ -226,15 +235,15 @@ class Recycler { if (this.markedRecordsForMassAction.length > 0) { this.elements.$massUndo.find('span.text').text( - this.createMessage(TYPO3.lang['button.undoselected'], [this.markedRecordsForMassAction.length]) + this.createMessage(TYPO3.lang['button.undoselected'], [this.markedRecordsForMassAction.length.toString(10)]) ); this.elements.$massDelete.find('span.text').text( - this.createMessage(TYPO3.lang['button.deleteselected'], [this.markedRecordsForMassAction.length]) + this.createMessage(TYPO3.lang['button.deleteselected'], [this.markedRecordsForMassAction.length.toString(10)]) ); } else { this.resetMassActionButtons(); } - } + }; /** * Resets the mass action state @@ -263,7 +272,7 @@ class Recycler { const tables: Array<JQuery> = []; this.elements.$tableSelector.children().remove(); - for (let value of data) { + for (const value of data) { const tableName = value[0]; const deletedRecords = value[1]; const tableDescription = value[2] ? value[2] : TYPO3.lang.label_allrecordtypes; @@ -349,7 +358,7 @@ class Recycler { }, ] }); - } + }; private undoRecord = (e: Event): void => { const $tr = $(e.target).parents('tr'); @@ -381,7 +390,7 @@ class Recycler { if (recoverPages) { $message = $('<div />').append( $('<p />').text(messageText), - $('<div />', {class: 'form-check'}).append( + $('<div />', { class: 'form-check' }).append( $('<input />', { type: 'checkbox', id: 'undo-recursive', @@ -423,16 +432,10 @@ class Recycler { }, ] }); - } + }; - /** - * @param {string} action - * @param {Object} records - * @param {boolean} isMassAction - * @param {boolean} recursive - */ - private callAjaxAction(action: string, records: Object, isMassAction: boolean, recursive: boolean = false): Promise<AjaxResponse>|null { - let data: any = { + private callAjaxAction(action: string, records: RecordToDelete[], isMassAction: boolean, recursive: boolean = false): Promise<AjaxResponse>|null { + const data: { records: RecordToDelete[], action: string, recursive?: number } = { records: records, action: '', }; @@ -478,14 +481,14 @@ class Recycler { /** * Replaces the placeholders with actual values */ - private createMessage(message: string, placeholders: Array<any>): string { + private createMessage(message: string, placeholders: string[]): string { if (typeof message === 'undefined') { return ''; } return message.replace( /\{([0-9]+)\}/g, - function(_: string, index: any): string { + function(_: string, index: number): string { return placeholders[index]; }, ); @@ -510,16 +513,16 @@ class Recycler { return; } - const $ul = $('<ul />', {class: 'pagination'}), + const $ul = $('<ul />', { class: 'pagination' }), liElements = [], - $controlFirstPage = $('<li />', {class: 'page-item'}).append( - $('<button />', {class: 'page-link', type: 'button', 'data-action': 'previous'}).append( - $('<typo3-backend-icon />', {'identifier': 'actions-arrow-left-alt', 'size': 'small'}), + $controlFirstPage = $('<li />', { class: 'page-item' }).append( + $('<button />', { class: 'page-link', type: 'button', 'data-action': 'previous' }).append( + $('<typo3-backend-icon />', { 'identifier': 'actions-arrow-left-alt', 'size': 'small' }), ), ), - $controlLastPage = $('<li />', {class: 'page-item'}).append( - $('<button />', {class: 'page-link', type: 'button', 'data-action': 'next'}).append( - $('<typo3-backend-icon />', {'identifier': 'actions-arrow-right-alt', 'size': 'small'}), + $controlLastPage = $('<li />', { class: 'page-item' }).append( + $('<button />', { class: 'page-link', type: 'button', 'data-action': 'next' }).append( + $('<typo3-backend-icon />', { 'identifier': 'actions-arrow-right-alt', 'size': 'small' }), ), ); @@ -532,9 +535,9 @@ class Recycler { } for (let i = 1; i <= this.paging.totalPages; i++) { - const $li = $('<li />', {class: 'page-item' + (this.paging.currentPage === i ? ' active' : '')}); + const $li = $('<li />', { class: 'page-item' + (this.paging.currentPage === i ? ' active' : '') }); $li.append( - $('<button />', {class: 'page-link', type: 'button', 'data-action': 'page'}).append( + $('<button />', { class: 'page-link', type: 'button', 'data-action': 'page' }).append( $('<span />').text(i), ), ); @@ -542,7 +545,7 @@ class Recycler { } $ul.append($controlFirstPage, liElements, $controlLastPage); - this.elements.$paginator.html($ul); + this.elements.$paginator.empty().append($ul); } } diff --git a/Build/Sources/TypeScript/redirects/event-handler.ts b/Build/Sources/TypeScript/redirects/event-handler.ts index d1608d12c20d..f61d9c5e3897 100644 --- a/Build/Sources/TypeScript/redirects/event-handler.ts +++ b/Build/Sources/TypeScript/redirects/event-handler.ts @@ -11,10 +11,22 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import NotificationService from '@typo3/backend/notification'; import DeferredAction from '@typo3/backend/action-button/deferred-action'; +import { AbstractAction } from '@typo3/backend/action-button/abstract-action'; + +type Correlation = { + correlationIdSlugUpdate: string; + correlationIdRedirectCreation: string; +} + +type SlugChangeDetail = { + correlations: Correlation; + autoUpdateSlugs: boolean; + autoCreateRedirects: boolean; +} /** * Module: @typo3/redirects/event-handler @@ -29,29 +41,33 @@ class EventHandler { } public dispatchCustomEvent(name: string, detail: any = null): void { - const event = new CustomEvent(name, {detail: detail}); + const event = new CustomEvent(name, { detail: detail }); document.dispatchEvent(event); } - public onSlugChanged(detail: any): void { - let actions: any = []; + public onSlugChanged(detail: SlugChangeDetail): void { + const actions: { label: string; action: AbstractAction }[] = []; const correlations = detail.correlations; if (detail.autoUpdateSlugs) { actions.push({ label: TYPO3.lang['notification.redirects.button.revert_update'], - action: new DeferredAction(() => this.revert([ - correlations.correlationIdSlugUpdate, - correlations.correlationIdRedirectCreation, - ])), + action: new DeferredAction(async () => { + await this.revert([ + correlations.correlationIdSlugUpdate, + correlations.correlationIdRedirectCreation, + ]); + }), }); } if (detail.autoCreateRedirects) { actions.push({ label: TYPO3.lang['notification.redirects.button.revert_redirect'], - action: new DeferredAction(() => this.revert([ - correlations.correlationIdRedirectCreation, - ])), + action: new DeferredAction(async () => { + await this.revert([ + correlations.correlationIdRedirectCreation, + ]); + }), }); } diff --git a/Build/Sources/TypeScript/redirects/form-engine-evaluation.ts b/Build/Sources/TypeScript/redirects/form-engine-evaluation.ts index ce58a9737346..148e010b1bfb 100644 --- a/Build/Sources/TypeScript/redirects/form-engine-evaluation.ts +++ b/Build/Sources/TypeScript/redirects/form-engine-evaluation.ts @@ -11,7 +11,7 @@ * The TYPO3 project - inspiring people to share! */ -import FormEngineValidation from '@typo3/backend/form-engine-validation' +import FormEngineValidation from '@typo3/backend/form-engine-validation'; /** * Module: @typo3/redirects/form-engine-evaluation diff --git a/Build/Sources/TypeScript/rte_ckeditor/ckeditor5.ts b/Build/Sources/TypeScript/rte_ckeditor/ckeditor5.ts index 7e949ce6d250..4b2cab69bdc5 100644 --- a/Build/Sources/TypeScript/rte_ckeditor/ckeditor5.ts +++ b/Build/Sources/TypeScript/rte_ckeditor/ckeditor5.ts @@ -1,8 +1,8 @@ -import {html, LitElement, TemplateResult} from 'lit'; -import {customElement, property, query} from 'lit/decorators'; -import {CKEditor5, Core} from '@typo3/ckeditor5-bundle'; -import type {PluginInterface} from '@ckeditor/ckeditor5-core/src/plugin'; -import type {EditorWithUI} from '@ckeditor/ckeditor5-core/src/editor/editorwithui'; +import { html, LitElement, TemplateResult } from 'lit'; +import { customElement, property, query } from 'lit/decorators'; +import { CKEditor5, Core } from '@typo3/ckeditor5-bundle'; +import type { PluginInterface } from '@ckeditor/ckeditor5-core/src/plugin'; +import type { EditorWithUI } from '@ckeditor/ckeditor5-core/src/editor/editorwithui'; interface CKEditor5Config { // in TYPO3 always `items` property is used, skipping `string[]` @@ -48,8 +48,8 @@ type Typo3Plugin = PluginInterface & {overrides?: Typo3Plugin[]}; */ @customElement('typo3-rte-ckeditor-ckeditor5') export class CKEditor5Element extends LitElement { - @property({type: Object}) options?: CKEditor5Config = {}; - @property({type: Object, attribute: 'form-engine'}) formEngine?: FormEngineConfig = {}; + @property({ type: Object }) options?: CKEditor5Config = {}; + @property({ type: Object, attribute: 'form-engine' }) formEngine?: FormEngineConfig = {}; @query('textarea') target: HTMLElement; @@ -101,7 +101,7 @@ export class CKEditor5Element extends LitElement { const toolbar = this.options.toolbar; - let config = { + const config = { // link.defaultProtocol: 'https://' // @todo use complete `config` later - currently step-by-step only toolbar, diff --git a/Build/Sources/TypeScript/rte_ckeditor/plugin/typo3-link.ts b/Build/Sources/TypeScript/rte_ckeditor/plugin/typo3-link.ts index b5d74f3bd070..98b0f15d79a6 100644 --- a/Build/Sources/TypeScript/rte_ckeditor/plugin/typo3-link.ts +++ b/Build/Sources/TypeScript/rte_ckeditor/plugin/typo3-link.ts @@ -258,6 +258,7 @@ export class Typo3LinkActionsView extends LinkActionsView { const textView = new Typo3TextView(this.locale); const t = this.t; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore textView.bind('text').to(this, 'href', href => { return href || t('This link has no URL'); @@ -451,19 +452,19 @@ export class Typo3LinkUI extends Core.Plugin { this.listenTo(this.balloon, 'change:visibleView', update); } - private areActionsInPanel(): Boolean { + private areActionsInPanel(): boolean { return this.balloon.hasView(this.actionsView); } - private areActionsVisible(): Boolean { + private areActionsVisible(): boolean { return this.balloon.visibleView === this.actionsView; } - private isUIInPanel(): Boolean { + private isUIInPanel(): boolean { return this.areActionsInPanel(); } - private isUIVisible(): Boolean { + private isUIVisible(): boolean { return this.areActionsVisible(); } diff --git a/Build/Sources/TypeScript/rte_ckeditor/plugin/whitespace.ts b/Build/Sources/TypeScript/rte_ckeditor/plugin/whitespace.ts index 91d8154a6f30..8488a481d3ce 100644 --- a/Build/Sources/TypeScript/rte_ckeditor/plugin/whitespace.ts +++ b/Build/Sources/TypeScript/rte_ckeditor/plugin/whitespace.ts @@ -57,7 +57,7 @@ export default class Whitespace extends Core.Plugin { const viewWriter = conversionApi.writer; const chunks = data.item.data .split(/([\u00AD,\u00A0])/) - .filter((value: String) => value !== ''); + .filter((value: string) => value !== ''); let currentPosition = data.range.start; chunks.forEach((chunk) => { diff --git a/Build/Sources/TypeScript/rte_ckeditor/rte-link-browser.ts b/Build/Sources/TypeScript/rte_ckeditor/rte-link-browser.ts index 673517535aa2..1cb2907d1ab6 100644 --- a/Build/Sources/TypeScript/rte_ckeditor/rte-link-browser.ts +++ b/Build/Sources/TypeScript/rte_ckeditor/rte-link-browser.ts @@ -27,10 +27,7 @@ class RteLinkBrowser { protected selectionStartPosition: Position = null; protected selectionEndPosition: Position = null; - /** - * @param {String} editorId Id of CKEditor - */ - public initialize(editorId: string): void { + public initialize(): void { this.editor = Modal.currentModal.userData.editor; this.selectionStartPosition = Modal.currentModal.userData.selectionStartPosition; this.selectionEndPosition = Modal.currentModal.userData.selectionEndPosition; @@ -52,7 +49,7 @@ class RteLinkBrowser { * @param {String} link The select element or anything else which identifies the link (e.g. "page:<pageUid>" or "file:<uid>") */ public finalizeFunction(link: string): void { - const attributes: { [key: string]: string } = LinkBrowser.getLinkAttributeValues(); + const attributes: Record<string, string> = LinkBrowser.getLinkAttributeValues(); const queryParams = attributes.params ? attributes.params : ''; delete attributes.params; @@ -73,7 +70,7 @@ class RteLinkBrowser { } private convertAttributes(attributes: Record<string, string>, text?: string): Typo3LinkDict { - const linkAttr: any = { attrs: {} }; + const linkAttr: { attrs: { [key: string]: string}, linkText?: string} = { attrs: {} }; for (const [attribute, value] of Object.entries(attributes)) { if (LINK_ALLOWED_ATTRIBUTES.includes(attribute)) { linkAttr.attrs[addLinkPrefix(attribute)] = value; @@ -88,13 +85,13 @@ class RteLinkBrowser { private sanitizeLink(link: string, queryParams: string): string { // @todo taken from previous code - enhance generation // Make sure, parameters and anchor are in correct order - const linkMatch = link.match(/^([a-z0-9]+:\/\/[^:\/?#]+(?:\/?[^?#]*)?)(\??[^#]*)(#?.*)$/) + const linkMatch = link.match(/^([a-z0-9]+:\/\/[^:/?#]+(?:\/?[^?#]*)?)(\??[^#]*)(#?.*)$/); if (linkMatch && linkMatch.length > 0) { link = linkMatch[1] + linkMatch[2]; const paramsPrefix = linkMatch[2].length > 0 ? '&' : '?'; if (queryParams.length > 0) { if (queryParams[0] === '&') { - queryParams = queryParams.substr(1) + queryParams = queryParams.substr(1); } // If params is set, append it if (queryParams.length > 0) { @@ -108,6 +105,6 @@ class RteLinkBrowser { } // @todo check whether this is still required - if, document why/where -let rteLinkBrowser = new RteLinkBrowser(); +const rteLinkBrowser = new RteLinkBrowser(); export default rteLinkBrowser; LinkBrowser.finalizeFunction = (link: string): void => { rteLinkBrowser.finalizeFunction(link); }; diff --git a/Build/Sources/TypeScript/scheduler/scheduler.ts b/Build/Sources/TypeScript/scheduler/scheduler.ts index 15851b8b90c1..4ba88b9d8285 100644 --- a/Build/Sources/TypeScript/scheduler/scheduler.ts +++ b/Build/Sources/TypeScript/scheduler/scheduler.ts @@ -18,7 +18,7 @@ import RegularEvent from '@typo3/core/event/regular-event'; import Modal from '@typo3/backend/modal'; import Icons from '@typo3/backend/icons'; import { MessageUtility } from '@typo3/backend/utility/message-utility'; -import {ActionEventDetails} from '@typo3/backend/multi-record-selection-action'; +import { ActionEventDetails } from '@typo3/backend/multi-record-selection-action'; import PersistentStorage from '@typo3/backend/storage/persistent'; import DateTimePicker from '@typo3/backend/date-time-picker'; import { MultiRecordSelectionSelectors } from '@typo3/backend/multi-record-selection'; @@ -32,6 +32,19 @@ interface TableNumberMapping { * @exports @typo3/scheduler/scheduler */ class Scheduler { + constructor() { + this.initializeEvents(); + this.initializeDefaultStates(); + + DocumentSaveActions.getInstance().addPreSubmitCallback((): void => { + let taskClass = $('#task_class').val(); + taskClass = taskClass.toLowerCase().replace(/\\/g, '-'); + + $('.extraFields').appendTo($('#extraFieldsHidden')); + $('.extra_fields_' + taskClass).appendTo($('#extraFieldsSection')); + }); + } + private static updateElementBrowserTriggers(): void { const triggers = document.querySelectorAll('.t3js-element-browser'); @@ -46,7 +59,7 @@ class Scheduler { if (element === null || typeof element.dataset.defaultNumberOfDays === 'undefined') { return null; } - return JSON.parse(element.dataset.defaultNumberOfDays) as TableNumberMapping + return JSON.parse(element.dataset.defaultNumberOfDays) as TableNumberMapping; } /** @@ -59,26 +72,13 @@ class Scheduler { storedModuleData = PersistentStorage.get('moduleData.scheduler_manage'); } - const collapseConfig: any = {}; + const collapseConfig: Record<string, number> = {}; collapseConfig[table] = isCollapsed ? 1 : 0; $.extend(storedModuleData, collapseConfig); PersistentStorage.set('moduleData.scheduler_manage', storedModuleData); } - constructor() { - this.initializeEvents(); - this.initializeDefaultStates(); - - DocumentSaveActions.getInstance().addPreSubmitCallback((): void => { - let taskClass = $('#task_class').val(); - taskClass = taskClass.toLowerCase().replace(/\\/g, '-'); - - $('.extraFields').appendTo($('#extraFieldsHidden')); - $('.extra_fields_' + taskClass).appendTo($('#extraFieldsSection')); - }); - } - /** * This method reacts on changes to the task class * It switches on or off the relevant extra fields @@ -104,8 +104,8 @@ class Scheduler { * This method reacts on field changes of all table field for table garbage collection task */ public actOnChangeSchedulerTableGarbageCollectionAllTables(theCheckbox: JQuery): void { - let $numberOfDays = $('#task_tableGarbageCollection_numberOfDays'); - let $taskTableGarbageCollectionTable = $('#task_tableGarbageCollection_table'); + const $numberOfDays = $('#task_tableGarbageCollection_numberOfDays'); + const $taskTableGarbageCollectionTable = $('#task_tableGarbageCollection_table'); if (theCheckbox.prop('checked')) { $taskTableGarbageCollectionTable.prop('disabled', true); $numberOfDays.prop('disabled', true); @@ -113,7 +113,7 @@ class Scheduler { // Get number of days for selected table let numberOfDays = parseInt($numberOfDays.val(), 10); if (numberOfDays < 1) { - let selectedTable = $taskTableGarbageCollectionTable.val(); + const selectedTable = $taskTableGarbageCollectionTable.val(); const defaultNumberOfDays = Scheduler.resolveDefaultNumberOfDays(); if (defaultNumberOfDays !== null) { numberOfDays = defaultNumberOfDays[selectedTable]; @@ -132,7 +132,7 @@ class Scheduler { * of the selected table */ public actOnChangeSchedulerTableGarbageCollectionTable(theSelector: JQuery): void { - let $numberOfDays = $('#task_tableGarbageCollection_numberOfDays'); + const $numberOfDays = $('#task_tableGarbageCollection_numberOfDays'); const defaultNumberOfDays = Scheduler.resolveDefaultNumberOfDays(); if (defaultNumberOfDays !== null && defaultNumberOfDays[theSelector.val()] > 0) { $numberOfDays.prop('disabled', false); @@ -210,11 +210,11 @@ class Scheduler { * Initialize default states */ public initializeDefaultStates(): void { - let $taskType = $('#task_type'); + const $taskType = $('#task_type'); if ($taskType.length) { this.toggleFieldsByTaskType($taskType.val()); } - let $taskClass = $('#task_class'); + const $taskClass = $('#task_class'); if ($taskClass.length) { this.actOnChangedTaskClass($taskClass); Scheduler.updateElementBrowserTriggers(); diff --git a/Build/Sources/TypeScript/setup/setup-module.ts b/Build/Sources/TypeScript/setup/setup-module.ts index 83579e3a4393..83e49272bf18 100644 --- a/Build/Sources/TypeScript/setup/setup-module.ts +++ b/Build/Sources/TypeScript/setup/setup-module.ts @@ -11,8 +11,8 @@ * The TYPO3 project - inspiring people to share! */ -import {ModalResponseEvent} from '@typo3/backend/modal-interface'; -import {MessageUtility} from '@typo3/backend/utility/message-utility'; +import { ModalResponseEvent } from '@typo3/backend/modal-interface'; +import { MessageUtility } from '@typo3/backend/utility/message-utility'; import RegularEvent from '@typo3/core/event/regular-event'; import Modal from '@typo3/backend/modal'; @@ -23,18 +23,6 @@ import Modal from '@typo3/backend/modal'; class SetupModule { private avatarWindowRef: Window; - private static handleConfirmationResponse(evt: ModalResponseEvent): void { - if (evt.detail.result && evt.detail.payload === 'resetConfiguration') { - const input: HTMLInputElement = document.querySelector('#setValuesToDefault'); - input.value = '1'; - input.form.submit(); - } - } - - private static hideElement(element: HTMLElement): void { - element.style.display = 'none'; - } - constructor() { new RegularEvent('setup:confirmation:response', SetupModule.handleConfirmationResponse) .delegateTo(document, '[data-event-name="setup:confirmation:response"]'); @@ -42,7 +30,7 @@ class SetupModule { const clickEvent = new CustomEvent( element.dataset.eventName, { bubbles: true, - detail: {payload: element.dataset.eventPayload} + detail: { payload: element.dataset.eventPayload } }); element.dispatchEvent(clickEvent); }).delegateTo(document, '[data-event="click"][data-event-name]'); @@ -60,6 +48,18 @@ class SetupModule { } } + private static handleConfirmationResponse(evt: ModalResponseEvent): void { + if (evt.detail.result && evt.detail.payload === 'resetConfiguration') { + const input: HTMLInputElement = document.querySelector('#setValuesToDefault'); + input.value = '1'; + input.form.submit(); + } + } + + private static hideElement(element: HTMLElement): void { + element.style.display = 'none'; + } + private initializeMessageListener(): void { window.addEventListener('message', (evt: MessageEvent): void => { if (!MessageUtility.verifyOrigin(evt.origin)) { diff --git a/Build/Sources/TypeScript/t3editor/element/code-mirror-element.ts b/Build/Sources/TypeScript/t3editor/element/code-mirror-element.ts index f5a64d6d0eb2..8a1f78ed57f2 100644 --- a/Build/Sources/TypeScript/t3editor/element/code-mirror-element.ts +++ b/Build/Sources/TypeScript/t3editor/element/code-mirror-element.ts @@ -11,8 +11,8 @@ * The TYPO3 project - inspiring people to share! */ -import {LitElement, html, css} from 'lit'; -import {customElement, property, state} from 'lit/decorators'; +import { LitElement, html, css } from 'lit'; +import { customElement, property, state } from 'lit/decorators'; import { EditorView, ViewUpdate, @@ -23,21 +23,12 @@ import { KeyBinding, placeholder } from '@codemirror/view'; -import {Extension, EditorState} from '@codemirror/state'; -import {syntaxHighlighting, defaultHighlightStyle} from '@codemirror/language'; -import {defaultKeymap, indentWithTab} from '@codemirror/commands'; -import {oneDark} from '@codemirror/theme-one-dark'; -import {executeJavaScriptModuleInstruction, loadModule, resolveSubjectRef, JavaScriptItemPayload} from '@typo3/core/java-script-item-processor'; -import '@typo3/backend/element/spinner-element' - -interface MarkTextPosition { - line: number; - ch: number; -} -interface MarkText { - to: MarkTextPosition; - from: MarkTextPosition; -} +import { Extension, EditorState } from '@codemirror/state'; +import { syntaxHighlighting, defaultHighlightStyle } from '@codemirror/language'; +import { defaultKeymap, indentWithTab } from '@codemirror/commands'; +import { oneDark } from '@codemirror/theme-one-dark'; +import { executeJavaScriptModuleInstruction, loadModule, resolveSubjectRef, JavaScriptItemPayload } from '@typo3/core/java-script-item-processor'; +import '@typo3/backend/element/spinner-element'; /** * Module: @typo3/t3editor/element/code-mirror-element @@ -45,22 +36,6 @@ interface MarkText { */ @customElement('typo3-t3editor-codemirror') export class CodeMirrorElement extends LitElement { - @property({type: Object}) mode: JavaScriptItemPayload = null; - @property({type: Array}) addons: JavaScriptItemPayload[] = []; - @property({type: Array}) keymaps: JavaScriptItemPayload[] = []; - - @property({type: Number}) lineDigits: number = 0; - @property({type: Boolean, reflect: true}) autoheight: boolean = false; - @property({type: Boolean}) nolazyload: boolean = false; - @property({type: Boolean}) readonly: boolean = false; - @property({type: Boolean, reflect: true}) fullscreen: boolean = false; - - @property({type: String}) label: string; - @property({type: String}) placeholder: string; - @property({type: String}) panel: string = 'bottom'; - - @state() editorView: EditorView = null; - static styles = css` :host { display: flex; @@ -126,6 +101,22 @@ export class CodeMirrorElement extends LitElement { } `; + @property({ type: Object }) mode: JavaScriptItemPayload = null; + @property({ type: Array }) addons: JavaScriptItemPayload[] = []; + @property({ type: Array }) keymaps: JavaScriptItemPayload[] = []; + + @property({ type: Number }) lineDigits: number = 0; + @property({ type: Boolean, reflect: true }) autoheight: boolean = false; + @property({ type: Boolean }) nolazyload: boolean = false; + @property({ type: Boolean }) readonly: boolean = false; + @property({ type: Boolean, reflect: true }) fullscreen: boolean = false; + + @property({ type: String }) label: string; + @property({ type: String }) placeholder: string; + @property({ type: String }) panel: string = 'bottom'; + + @state() editorView: EditorView = null; + render() { return html` <div id="codemirror-parent" @keydown=${(e: KeyboardEvent) => this.onKeydown(e)}></div> @@ -142,7 +133,7 @@ export class CodeMirrorElement extends LitElement { const observerOptions = { root: document.body }; - let observer = new IntersectionObserver((entries: IntersectionObserverEntry[]): void => { + const observer = new IntersectionObserver((entries: IntersectionObserverEntry[]): void => { entries.forEach((entry: IntersectionObserverEntry): void => { if (entry.intersectionRatio > 0) { observer.unobserve(entry.target); @@ -171,7 +162,7 @@ export class CodeMirrorElement extends LitElement { const updateListener = EditorView.updateListener.of((v: ViewUpdate) => { if (v.docChanged) { textarea.value = v.state.doc.toString(); - textarea.dispatchEvent(new CustomEvent('change', {bubbles: true})); + textarea.dispatchEvent(new CustomEvent('change', { bubbles: true })); } }); @@ -188,7 +179,7 @@ export class CodeMirrorElement extends LitElement { highlightSpecialChars(), drawSelection(), EditorState.allowMultipleSelections.of(true), - syntaxHighlighting(defaultHighlightStyle, {fallback: true}), + syntaxHighlighting(defaultHighlightStyle, { fallback: true }), ]; if (this.readonly) { @@ -225,6 +216,6 @@ export class CodeMirrorElement extends LitElement { }), parent: this.renderRoot.querySelector('#codemirror-parent'), root: this.renderRoot as ShadowRoot - }) + }); } } diff --git a/Build/Sources/TypeScript/t3editor/language/typoscript.ts b/Build/Sources/TypeScript/t3editor/language/typoscript.ts index 4316c3b23c28..388b538f3a00 100644 --- a/Build/Sources/TypeScript/t3editor/language/typoscript.ts +++ b/Build/Sources/TypeScript/t3editor/language/typoscript.ts @@ -1,9 +1,9 @@ -import {StreamLanguage, LanguageSupport} from '@codemirror/language'; -import {CompletionContext, CompletionResult} from '@codemirror/autocomplete'; -import {typoScriptStreamParser} from '@typo3/t3editor/stream-parser/typoscript'; +import { StreamLanguage, LanguageSupport } from '@codemirror/language'; +import { CompletionContext, CompletionResult } from '@codemirror/autocomplete'; +import { typoScriptStreamParser } from '@typo3/t3editor/stream-parser/typoscript'; import TsCodeCompletion from '@typo3/t3editor/autocomplete/ts-code-completion'; -import {syntaxTree} from '@codemirror/language'; -import type {SyntaxNodeRef} from '@lezer/common'; +import { syntaxTree } from '@codemirror/language'; +import type { SyntaxNodeRef } from '@lezer/common'; interface Token { type: string; @@ -36,14 +36,12 @@ export function typoscript() { const completion = language.data.of({ autocomplete: complete - }) + }); return new LanguageSupport(language, [completion]); } export function complete (context: CompletionContext): Promise<CompletionResult | null> | CompletionResult | null { - let word = context.matchBefore(/\w*/) - if (!context.explicit) { return null; } @@ -82,18 +80,18 @@ export function complete (context: CompletionContext): Promise<CompletionResult return { from: completionStart, options: completions.map((result: string) => { - return { label: result, type: 'keyword' } + return { label: result, type: 'keyword' }; }) }; return null; -}; +} function parseCodeMirror5CompatibleCompletionState(context: CompletionContext): CodeMirror5CompatibleCompletionState { const lineCount = context.state.sliceDoc().split(context.state.lineBreak).length; const currentLineNumber = context.state.sliceDoc(0, context.pos).split(context.state.lineBreak).length; - const currentLine = context.state.sliceDoc().split(context.state.lineBreak)[currentLineNumber-1]; - const lastChar = context.state.sliceDoc(context.pos-1, context.pos); + const currentLine = context.state.sliceDoc().split(context.state.lineBreak)[currentLineNumber - 1]; + const lastChar = context.state.sliceDoc(context.pos - 1, context.pos); const completingAfterDot = lastChar === '.'; const lineTokens = extractCodemirror5StyleLineTokens(lineCount, context); @@ -103,7 +101,7 @@ function parseCodeMirror5CompatibleCompletionState(context: CompletionContext): currentLine, lineCount, completingAfterDot - } + }; } function extractCodemirror5StyleLineTokens(lineCount: number, context: CompletionContext): Token[][] { @@ -124,7 +122,7 @@ function extractCodemirror5StyleLineTokens(lineCount: number, context: Completio if (lastToken < start) { context.state.sliceDoc(lastToken, start).split(context.state.lineBreak).forEach((part: string) => { if (part) { - lineTokens[Math.min(lineNumber - 1, lineCount - 1)].push({ type: null, string: part, start: lastToken, end: lastToken + part.length}); + lineTokens[Math.min(lineNumber - 1, lineCount - 1)].push({ type: null, string: part, start: lastToken, end: lastToken + part.length }); lineNumber++; lastToken += part.length; } @@ -132,11 +130,11 @@ function extractCodemirror5StyleLineTokens(lineCount: number, context: Completio } const string = context.state.sliceDoc(node.from, node.to); lineNumber = context.state.sliceDoc(0, node.from).split(context.state.lineBreak).length; - lineTokens[lineNumber-1].push({ type, string, start, end }); + lineTokens[lineNumber - 1].push({ type, string, start, end }); lastToken = end; }); if (lastToken < context.state.doc.length) { - lineTokens[lineNumber-1].push({ type: null, string: context.state.sliceDoc(lastToken), start: lastToken, end: context.state.doc.length}); + lineTokens[lineNumber - 1].push({ type: null, string: context.state.sliceDoc(lastToken), start: lastToken, end: context.state.doc.length }); } return lineTokens; @@ -161,7 +159,7 @@ function getCompletions(token: string, keywords: string[]) { if (str.lastIndexOf(token, 0) === 0 && !found.has(str)) { found.add(str); } - } + }; for (let i = 0, e = keywords.length; i < e; ++i) { maybeAdd(keywords[i]); diff --git a/Build/Sources/TypeScript/tstemplate/constant-editor.ts b/Build/Sources/TypeScript/tstemplate/constant-editor.ts index 86b4c2748dd2..baf7ce52200c 100644 --- a/Build/Sources/TypeScript/tstemplate/constant-editor.ts +++ b/Build/Sources/TypeScript/tstemplate/constant-editor.ts @@ -29,7 +29,7 @@ class ConstantEditor { DocumentService.ready().then((document: Document): void => { const colorInputElements: NodeListOf<HTMLInputElement> = document.querySelectorAll(Selectors.colorInputSelector); if (colorInputElements.length) { - import('@typo3/backend/color-picker').then(({default: ColorPicker}): void => { + import('@typo3/backend/color-picker').then(({ default: ColorPicker }): void => { colorInputElements.forEach((element: HTMLInputElement): void => { ColorPicker.initialize(element); }); @@ -58,7 +58,7 @@ class ConstantEditor { if (toggleState === 'edit') { defaultDiv.style.display = 'none'; userDiv.style.removeProperty('display'); - userDiv.querySelectorAll('input').forEach((element: HTMLInputElement): void => {element.style.background = '#fdf8bd'}); + userDiv.querySelectorAll('input').forEach((element: HTMLInputElement): void => {element.style.background = '#fdf8bd';}); checkBox.removeAttribute('disabled'); } else if (toggleState === 'undo') { userDiv.style.display = 'none'; diff --git a/Build/Sources/TypeScript/tstemplate/template-analyzer.ts b/Build/Sources/TypeScript/tstemplate/template-analyzer.ts index 41985d3d9c1d..7a97ec41c71a 100644 --- a/Build/Sources/TypeScript/tstemplate/template-analyzer.ts +++ b/Build/Sources/TypeScript/tstemplate/template-analyzer.ts @@ -12,13 +12,13 @@ */ import DocumentService from '@typo3/core/document-service'; -import {default as Modal} from '@typo3/backend/modal'; -import {topLevelModuleImport} from '@typo3/backend/utility/top-level-module-import'; -import {html, TemplateResult} from 'lit'; -import {until} from 'lit/directives/until'; +import { default as Modal } from '@typo3/backend/modal'; +import { topLevelModuleImport } from '@typo3/backend/utility/top-level-module-import'; +import { html, TemplateResult } from 'lit'; +import { until } from 'lit/directives/until'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import type {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import type {JavaScriptItemPayload} from '@typo3/core/java-script-item-processor'; +import type { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import type { JavaScriptItemPayload } from '@typo3/core/java-script-item-processor'; /** * @todo: This could be code-de-duplicated with ext:backend pagetsconfig/pagetconfig-includes.ts, @@ -45,7 +45,7 @@ class TemplateAnalyzer { this.fetchModalContent(url), html`<div class="modal-loading"><typo3-backend-spinner size="default"></typo3-backend-spinner></div>` )}`; - const modal = Modal.advanced({type, title, size, content}); + Modal.advanced({ type, title, size, content }); }); }); } diff --git a/Build/Sources/TypeScript/viewpage/main.ts b/Build/Sources/TypeScript/viewpage/main.ts index f5509b592a0c..e259e6a70384 100644 --- a/Build/Sources/TypeScript/viewpage/main.ts +++ b/Build/Sources/TypeScript/viewpage/main.ts @@ -13,10 +13,10 @@ import interact from 'interactjs'; import DocumentService from '@typo3/core/document-service'; -import PersistentStorage from '@typo3/backend/storage/persistent'; +import PersistentStorage, { UC } from '@typo3/backend/storage/persistent'; import RegularEvent from '@typo3/core/event/regular-event'; import DebounceEvent from '@typo3/core/event/debounce-event'; -import {ResizeEvent} from '@interactjs/actions/resize/plugin'; +import { ResizeEvent } from '@interactjs/actions/resize/plugin'; enum Selectors { resizableContainerIdentifier = '.t3js-viewpage-resizeable', @@ -52,7 +52,7 @@ class ViewPage { private currentLabelElement: HTMLElement; private resizableContainer: HTMLElement; - private queueDelayTimer: any; + private queueDelayTimer: number; constructor() { DocumentService.ready().then((): void => { @@ -88,7 +88,7 @@ class ViewPage { return this.currentLabelElement.textContent; } - private persistChanges(storageIdentifier: string, data: any): void { + private persistChanges(storageIdentifier: string, data: string | UC): void { PersistentStorage.set(storageIdentifier, data); } @@ -115,7 +115,7 @@ class ViewPage { } private persistCurrentPreset(): void { - let data = { + const data = { width: this.getCurrentWidth(), height: this.getCurrentHeight(), label: this.getCurrentLabel(), @@ -124,7 +124,7 @@ class ViewPage { } private persistCustomPreset(): void { - let data = { + const data = { width: this.getCurrentWidth(), height: this.getCurrentHeight(), }; @@ -196,7 +196,7 @@ class ViewPage { Object.assign(event.target.style, { width: `${event.rect.width}px`, height: `${event.rect.height}px`, - }) + }); this.inputCustomWidth.valueAsNumber = event.rect.width; this.inputCustomHeight.valueAsNumber = event.rect.height; diff --git a/Build/Sources/TypeScript/workspaces/backend.ts b/Build/Sources/TypeScript/workspaces/backend.ts index 88c2379b96f2..57598bff2089 100644 --- a/Build/Sources/TypeScript/workspaces/backend.ts +++ b/Build/Sources/TypeScript/workspaces/backend.ts @@ -11,14 +11,14 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import DocumentService from '@typo3/core/document-service'; import $ from 'jquery'; import '@typo3/backend/element/icon-element'; -import {SeverityEnum} from '@typo3/backend/enum/severity'; +import { SeverityEnum } from '@typo3/backend/enum/severity'; import '@typo3/backend/input/clearable'; import Workspaces from './workspaces'; -import {default as Modal, ModalElement} from '@typo3/backend/modal'; +import { default as Modal, ModalElement } from '@typo3/backend/modal'; import Persistent from '@typo3/backend/storage/persistent'; import Utility from '@typo3/backend/utility'; import Wizard from '@typo3/backend/wizard'; @@ -44,6 +44,22 @@ enum Identifiers { pagination = '#workspace-pagination', } +type Diff = { field: string, label: string, content: string, html: string }; +type Comment = { + user_comment: string; + previous_stage_title: string; + stage_title: string; + tstamp: number; + user_username: string; + user_avatar: string +} +type History = { + differences: string | Diff[]; + datetime: string; + user: string; + user_avatar: string; +} + /** * Backend workspace module. Loaded only in Backend context, not in * workspace preview. Contains all JavaScript of the main BE module. @@ -61,15 +77,35 @@ class Backend extends Workspaces { start: 0, filterTxt: '', }; - private paging: { [key: string]: number } = { + private paging: Record<string, number> = { currentPage: 1, totalPages: 1, totalItems: 0, }; private latestPath: string = ''; - private markedRecordsForMassAction: Array<any> = []; + private markedRecordsForMassAction: string[] = []; private indentationPadding: number = 26; + constructor() { + super(); + + DocumentService.ready().then((): void => { + this.getElements(); + this.registerEvents(); + this.notifyWorkspaceSwitchAction(); + + // Set the depth from the main element + this.settings.depth = this.elements.$depthSelector.val(); + this.settings.language = this.elements.$languageSelector.val(); + this.settings.stage = this.elements.$stagesSelector.val(); + + // Fetch workspace info (listing) if workspace is accessible + if (this.elements.$container.length) { + this.getWorkspaceInfos(); + } + }); + } + /** * Reloads the page tree */ @@ -83,14 +119,14 @@ class Backend extends Workspaces { * @param {Object} diff * @return {$} */ - private static generateDiffView(diff: Array<any>): JQuery { - const $diff = $('<div />', {class: 'diff'}); + private static generateDiffView(diff: Diff[]): JQuery { + const $diff = $('<div />', { class: 'diff' }); - for (let currentDiff of diff) { + for (const currentDiff of diff) { $diff.append( - $('<div />', {class: 'diff-item'}).append( - $('<div />', {class: 'diff-item-title'}).text(currentDiff.label), - $('<div />', {class: 'diff-item-result diff-item-result-inline'}).html(currentDiff.content), + $('<div />', { class: 'diff-item' }).append( + $('<div />', { class: 'diff-item-title' }).text(currentDiff.label), + $('<div />', { class: 'diff-item-result diff-item-result-inline' }).html(currentDiff.content), ), ); } @@ -103,31 +139,31 @@ class Backend extends Workspaces { * @param {Object} comments * @return {$} */ - private static generateCommentView(comments: Array<any>): JQuery { + private static generateCommentView(comments: Comment[]): JQuery { const $comments = $('<div />'); - for (let comment of comments) { - const $panel = $('<div />', {class: 'panel panel-default'}); + for (const comment of comments) { + const $panel = $('<div />', { class: 'panel panel-default' }); if (comment.user_comment.length > 0) { $panel.append( - $('<div />', {class: 'panel-body'}).html(comment.user_comment), + $('<div />', { class: 'panel-body' }).html(comment.user_comment), ); } $panel.append( - $('<div />', {class: 'panel-footer'}).append( - $('<span />', {class: 'badge badge-success me-2'}).text(comment.previous_stage_title + ' > ' + comment.stage_title), - $('<span />', {class: 'badge badge-info'}).text(comment.tstamp), + $('<div />', { class: 'panel-footer' }).append( + $('<span />', { class: 'badge badge-success me-2' }).text(comment.previous_stage_title + ' > ' + comment.stage_title), + $('<span />', { class: 'badge badge-info' }).text(comment.tstamp), ), ); $comments.append( - $('<div />', {class: 'media'}).append( - $('<div />', {class: 'media-left text-center'}).text(comment.user_username).prepend( + $('<div />', { class: 'media' }).append( + $('<div />', { class: 'media-left text-center' }).text(comment.user_username).prepend( $('<div />').html(comment.user_avatar), ), - $('<div />', {class: 'media-body'}).append($panel), + $('<div />', { class: 'media-body' }).append($panel), ), ); } @@ -141,11 +177,11 @@ class Backend extends Workspaces { * @param {Object} data * @return {JQuery} */ - private static generateHistoryView(data: Array<any>): JQuery { + private static generateHistoryView(data: History[]): JQuery { const $history = $('<div />'); - for (let currentData of data) { - const $panel = $('<div />', {class: 'panel panel-default'}); + for (const currentData of data) { + const $panel = $('<div />', { class: 'panel panel-default' }); let $diff; if (typeof currentData.differences === 'object') { @@ -153,13 +189,13 @@ class Backend extends Workspaces { // Somehow here are no differences. What a pity, skip that record continue; } - $diff = $('<div />', {class: 'diff'}); + $diff = $('<div />', { class: 'diff' }); for (let j = 0; j < currentData.differences.length; ++j) { $diff.append( - $('<div />', {class: 'diff-item'}).append( - $('<div />', {class: 'diff-item-title'}).text(currentData.differences[j].label), - $('<div />', {class: 'diff-item-result diff-item-result-inline'}).html(currentData.differences[j].html), + $('<div />', { class: 'diff-item' }).append( + $('<div />', { class: 'diff-item-title' }).text(currentData.differences[j].label), + $('<div />', { class: 'diff-item-result diff-item-result-inline' }).html(currentData.differences[j].html), ), ); } @@ -169,21 +205,21 @@ class Backend extends Workspaces { ); } else { $panel.append( - $('<div />', {class: 'panel-body'}).text(currentData.differences), + $('<div />', { class: 'panel-body' }).text(currentData.differences), ); } $panel.append( - $('<div />', {class: 'panel-footer'}).append( - $('<span />', {class: 'badge badge-info'}).text(currentData.datetime), + $('<div />', { class: 'panel-footer' }).append( + $('<span />', { class: 'badge badge-info' }).text(currentData.datetime), ), ); $history.append( - $('<div />', {class: 'media'}).append( - $('<div />', {class: 'media-left text-center'}).text(currentData.user).prepend( + $('<div />', { class: 'media' }).append( + $('<div />', { class: 'media-left text-center' }).text(currentData.user).prepend( $('<div />').html(currentData.user_avatar), ), - $('<div />', {class: 'media-body'}).append($panel), + $('<div />', { class: 'media-body' }).append($panel), ), ); } @@ -207,7 +243,7 @@ class Backend extends Workspaces { if (parent !== null && parent.checked !== check) { parent.checked = check; parent.dataset.manuallyChanged = 'true'; - parent.dispatchEvent(new CustomEvent('multiRecordSelection:checkbox:state:changed', {bubbles: true, cancelable: false})); + parent.dispatchEvent(new CustomEvent('multiRecordSelection:checkbox:state:changed', { bubbles: true, cancelable: false })); } } @@ -230,32 +266,12 @@ class Backend extends Workspaces { if (checkbox.checked !== check) { checkbox.checked = check; checkbox.dataset.manuallyChanged = 'true'; - checkbox.dispatchEvent(new CustomEvent('multiRecordSelection:checkbox:state:changed', {bubbles: true, cancelable: false})); + checkbox.dispatchEvent(new CustomEvent('multiRecordSelection:checkbox:state:changed', { bubbles: true, cancelable: false })); } - }) + }); } } - constructor() { - super(); - - DocumentService.ready().then((): void => { - this.getElements(); - this.registerEvents(); - this.notifyWorkspaceSwitchAction(); - - // Set the depth from the main element - this.settings.depth = this.elements.$depthSelector.val(); - this.settings.language = this.elements.$languageSelector.val(); - this.settings.stage = this.elements.$stagesSelector.val(); - - // Fetch workspace info (listing) if workspace is accessible - if (this.elements.$container.length) { - this.getWorkspaceInfos(); - } - }); - } - private notifyWorkspaceSwitchAction(): void { const mainElement = document.querySelector('main[data-workspace-switch-action]') as HTMLElement; if (mainElement.dataset.workspaceSwitchAction) { @@ -349,7 +365,7 @@ class Backend extends Workspaces { .on('click', '[data-action="preview"]', this.openPreview.bind(this)) .on('click', '[data-action="open"]', (e: JQueryEventObject): void => { const row = <HTMLTableRowElement>e.currentTarget.closest('tr'); - let newUrl = TYPO3.settings.FormEngine.moduleUrl + const newUrl = TYPO3.settings.FormEngine.moduleUrl + '&returnUrl=' + encodeURIComponent(document.location.href) + '&id=' + TYPO3.settings.Workspaces.id + '&edit[' + row.dataset.table + '][' + row.dataset.uid + ']=edit'; @@ -512,7 +528,7 @@ class Backend extends Workspaces { } this.elements.$chooseMassAction.prop('disabled', this.markedRecordsForMassAction.length > 0); - } + }; /** * Sends a record to a stage @@ -604,9 +620,9 @@ class Backend extends Workspaces { for (let i = 0; i < result.data.length; ++i) { const item = result.data[i]; - const $actions = $('<div />', {class: 'btn-group'}); + const $actions = $('<div />', { class: 'btn-group' }); let $integrityIcon: JQuery; - let hasSubitems = item.Workspaces_CollectionChildren > 0 && item.Workspaces_CollectionCurrent !== ''; + const hasSubitems = item.Workspaces_CollectionChildren > 0 && item.Workspaces_CollectionCurrent !== ''; $actions.append( this.getAction( hasSubitems, @@ -659,17 +675,17 @@ class Backend extends Workspaces { this.elements.$tableBody.append( $('<tr />').append( $('<th />'), - $('<th />', {colspan: 6}).html( + $('<th />', { colspan: 6 }).html( '<span title="' + item.path_Workspace + '">' + item.path_Workspace_crop + '</span>' ), ), ); } - const $checkbox = $('<span />', {class: 'form-check form-toggle'}).append( - $('<input />', {type: 'checkbox', class: 'form-check-input t3js-multi-record-selection-check'}) + const $checkbox = $('<span />', { class: 'form-check form-toggle' }).append( + $('<input />', { type: 'checkbox', class: 'form-check-input t3js-multi-record-selection-check' }) ); - const rowConfiguration: { [key: string]: any } = { + const rowConfiguration: Record<string, string> = { 'data-uid': item.uid, 'data-pid': item.livepid, 'data-t3ver_oid': item.t3ver_oid, @@ -683,14 +699,14 @@ class Backend extends Workspaces { if (item.Workspaces_CollectionParent !== '') { // fetch parent and see if this one is expanded - let parentItem = result.data.find((element: any) => { + const parentItem = result.data.find((element: any) => { return element.Workspaces_CollectionCurrent === item.Workspaces_CollectionParent; }); rowConfiguration['data-collection'] = item.Workspaces_CollectionParent; - rowConfiguration.class = 'collapse' + (parentItem.expanded ? ' show' : ''); + rowConfiguration.class = 'collapse' + (parentItem.expanded ? ' show' : ''); } else if (item.Workspaces_CollectionCurrent !== '') { // Set CollectionCurrent attribute for parent records - rowConfiguration['data-collection-current'] = item.Workspaces_CollectionCurrent + rowConfiguration['data-collection-current'] = item.Workspaces_CollectionCurrent; } this.elements.$tableBody.append( @@ -708,7 +724,7 @@ class Backend extends Workspaces { + '<span class="workspace-state-' + item.state_Workspace + '" title="' + item.label_Workspace + '">' + item.label_Workspace_crop + '</span>' + '</a>', ), - $('<td />', {class: 't3js-title-live'}).html( + $('<td />', { class: 't3js-title-live' }).html( '<span class="icon icon-size-small">' + this.getIcon(item.icon_Live) + '</span>' + ' ' + '<span class"workspace-live-title title="' + item.label_Live + '">' + item.label_Live_crop + '</span>' @@ -716,7 +732,7 @@ class Backend extends Workspaces { $('<td />').text(item.label_Stage), $('<td />').empty().append($integrityIcon), $('<td />').html(this.getIcon(item.language.icon)), - $('<td />', {class: 'text-end nowrap'}).append($actions), + $('<td />', { class: 'text-end nowrap' }).append($actions), ), ); } @@ -742,16 +758,16 @@ class Backend extends Workspaces { return; } - const $ul = $('<ul />', {class: 'pagination'}); + const $ul = $('<ul />', { class: 'pagination' }); const liElements: Array<JQuery> = []; - const $controlFirstPage = $('<li />', {class: 'page-item'}).append( - $('<button />', {class: 'page-link', type: 'button', 'data-action': 'previous'}).append( - $('<typo3-backend-icon />', {'identifier': 'actions-arrow-left-alt', 'size': 'small'}), + const $controlFirstPage = $('<li />', { class: 'page-item' }).append( + $('<button />', { class: 'page-link', type: 'button', 'data-action': 'previous' }).append( + $('<typo3-backend-icon />', { 'identifier': 'actions-arrow-left-alt', 'size': 'small' }), ), ), - $controlLastPage = $('<li />', {class: 'page-item'}).append( - $('<button />', {class: 'page-link', type: 'button', 'data-action': 'next'}).append( - $('<typo3-backend-icon />', {'identifier': 'actions-arrow-right-alt', 'size': 'small'}), + $controlLastPage = $('<li />', { class: 'page-item' }).append( + $('<button />', { class: 'page-link', type: 'button', 'data-action': 'next' }).append( + $('<typo3-backend-icon />', { 'identifier': 'actions-arrow-right-alt', 'size': 'small' }), ), ); @@ -764,9 +780,9 @@ class Backend extends Workspaces { } for (let i = 1; i <= this.paging.totalPages; i++) { - const $li = $('<li />', {class: 'page-item' + (this.paging.currentPage === i ? ' active' : '')}); + const $li = $('<li />', { class: 'page-item' + (this.paging.currentPage === i ? ' active' : '') }); $li.append( - $('<button />', {class: 'page-link', type: 'button', 'data-action': 'page', 'data-page': i}).append( + $('<button />', { class: 'page-link', type: 'button', 'data-action': 'page', 'data-page': i }).append( $('<span />').text(i), ), ); @@ -797,8 +813,8 @@ class Backend extends Workspaces { ).then(async (response: AjaxResponse): Promise<void> => { const item = (await response.resolve())[0].result.data[0]; const $content = $('<div />'); - const $tabsNav = $('<ul />', {class: 'nav nav-tabs', role: 'tablist'}); - const $tabsContent = $('<div />', {class: 'tab-content'}); + const $tabsNav = $('<ul />', { class: 'nav nav-tabs', role: 'tablist' }); + const $tabsContent = $('<div />', { class: 'tab-content' }); const modalButtons = []; $content.append( @@ -812,7 +828,7 @@ class Backend extends Workspaces { if (item.diff.length > 0) { $tabsNav.append( - $('<li />', {role: 'presentation', class: 'nav-item'}).append( + $('<li />', { role: 'presentation', class: 'nav-item' }).append( $('<a />', { class: 'nav-link', href: '#workspace-changes', @@ -823,8 +839,8 @@ class Backend extends Workspaces { ), ); $tabsContent.append( - $('<div />', {role: 'tabpanel', class: 'tab-pane', id: 'workspace-changes'}).append( - $('<div />', {class: 'form-section'}).append( + $('<div />', { role: 'tabpanel', class: 'tab-pane', id: 'workspace-changes' }).append( + $('<div />', { class: 'form-section' }).append( Backend.generateDiffView(item.diff), ), ), @@ -833,7 +849,7 @@ class Backend extends Workspaces { if (item.comments.length > 0) { $tabsNav.append( - $('<li />', {role: 'presentation', class: 'nav-item'}).append( + $('<li />', { role: 'presentation', class: 'nav-item' }).append( $('<a />', { class: 'nav-link', href: '#workspace-comments', @@ -841,13 +857,13 @@ class Backend extends Workspaces { role: 'tab', 'data-bs-toggle': 'tab', }).html(TYPO3.lang['window.recordChanges.tabs.comments'] + ' ').append( - $('<span />', {class: 'badge'}).text(item.comments.length), + $('<span />', { class: 'badge' }).text(item.comments.length), ), ), ); $tabsContent.append( - $('<div />', {role: 'tabpanel', class: 'tab-pane', id: 'workspace-comments'}).append( - $('<div />', {class: 'form-section'}).append( + $('<div />', { role: 'tabpanel', class: 'tab-pane', id: 'workspace-comments' }).append( + $('<div />', { class: 'form-section' }).append( Backend.generateCommentView(item.comments), ), ), @@ -856,7 +872,7 @@ class Backend extends Workspaces { if (item.history.total > 0) { $tabsNav.append( - $('<li />', {role: 'presentation', class: 'nav-item'}).append( + $('<li />', { role: 'presentation', class: 'nav-item' }).append( $('<a />', { class: 'nav-link', href: '#workspace-history', @@ -868,8 +884,8 @@ class Backend extends Workspaces { ); $tabsContent.append( - $('<div />', {role: 'tabpanel', class: 'tab-pane', id: 'workspace-history'}).append( - $('<div />', {class: 'form-section'}).append( + $('<div />', { role: 'tabpanel', class: 'tab-pane', id: 'workspace-history' }).append( + $('<div />', { class: 'form-section' }).append( Backend.generateHistoryView(item.history.data), ), ), @@ -930,7 +946,7 @@ class Backend extends Workspaces { size: Modal.sizes.medium, }); }); - } + }; /** * Opens a record in a preview window @@ -992,7 +1008,7 @@ class Backend extends Workspaces { }); } }); - } + }; /** * Runs a mass action @@ -1033,7 +1049,7 @@ class Backend extends Workspaces { this.renderSelectionActionWizard(selectedAction, affectedRecords); }); } - } + }; /** * Adds a slide to the wizard concerning an integrity check warning. @@ -1045,7 +1061,7 @@ class Backend extends Workspaces { TYPO3.lang['integrity.hasIssuesDescription'] + '<br>' + TYPO3.lang['integrity.hasIssuesQuestion'], SeverityEnum.warning, ); - } + }; /** * Renders the wizard for selection actions @@ -1112,7 +1128,7 @@ class Backend extends Workspaces { this.renderMassActionWizard(selectedAction); }); } - } + }; /** * Renders the wizard for mass actions @@ -1222,7 +1238,7 @@ class Backend extends Workspaces { this.elements.$chooseStageAction.val(''); }); }); - } + }; /** * Renders the action button based on the user's permission. @@ -1239,7 +1255,7 @@ class Backend extends Workspaces { 'data-action': action, }).append(this.getIcon(iconIdentifier)); } - return $('<span />', {class: 'btn btn-default disabled'}).append(this.getIcon('empty-empty')); + return $('<span />', { class: 'btn btn-default disabled' }).append(this.getIcon('empty-empty')); } /** @@ -1251,14 +1267,14 @@ class Backend extends Workspaces { this.settings.id, ]), ).then(async (response: AjaxResponse): Promise<void> => { - const result: { [key: string]: string } = (await response.resolve())[0].result; + const result: Record<string, string> = (await response.resolve())[0].result; const $list = $('<dl />'); - for (let [language, url] of Object.entries(result)) { + for (const [language, url] of Object.entries(result)) { $list.append( $('<dt />').text(language), $('<dd />').append( - $('<a />', {href: url, target: '_blank'}).text(url), + $('<a />', { href: url, target: '_blank' }).text(url), ), ); } @@ -1277,7 +1293,7 @@ class Backend extends Workspaces { ['modal-inner-scroll'], ); }); - } + }; /** * Gets a specific icon. A specific "switch" is added due to the integrity diff --git a/Build/Sources/TypeScript/workspaces/preview.ts b/Build/Sources/TypeScript/workspaces/preview.ts index 785681f4bcbd..95b4f47aa49d 100644 --- a/Build/Sources/TypeScript/workspaces/preview.ts +++ b/Build/Sources/TypeScript/workspaces/preview.ts @@ -11,8 +11,8 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; -import {SeverityEnum} from '@typo3/backend/enum/severity'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; +import { SeverityEnum } from '@typo3/backend/enum/severity'; import DocumentService from '@typo3/core/document-service'; import $ from 'jquery'; import Modal from '@typo3/backend/modal'; @@ -42,6 +42,16 @@ class Preview extends Workspaces { private currentSlidePosition: number = 100; private elements: { [key: string]: JQuery } = {}; + constructor() { + super(); + + DocumentService.ready().then((): void => { + this.getElements(); + this.resizeViews(); + this.registerEvents(); + }); + } + /** * Calculate the available space based on the viewport height * @@ -54,16 +64,6 @@ class Preview extends Workspaces { return $viewportHeight - $topbarHeight; } - constructor() { - super(); - - DocumentService.ready().then((): void => { - this.getElements(); - this.resizeViews(); - this.registerEvents(); - }); - } - /** * Fetches and stores often required elements */ @@ -117,7 +117,7 @@ class Preview extends Workspaces { private updateSlidePosition = (e: Event): void => { this.currentSlidePosition = parseInt((e.target as HTMLInputElement).value, 10); this.resizeViews(); - } + }; /** * Resize the views based on the current viewport height and slider position @@ -173,7 +173,7 @@ class Preview extends Workspaces { }); } }); - } + }; /** * Renders the "send page to stage" window @@ -215,7 +215,7 @@ class Preview extends Workspaces { } }); }); - } + }; /** * Changes the preview mode @@ -246,7 +246,7 @@ class Preview extends Workspaces { this.elements.$liveView.height('50%'); } } - } + }; } export default new Preview(); diff --git a/Build/Sources/TypeScript/workspaces/toolbar/workspaces-menu.ts b/Build/Sources/TypeScript/workspaces/toolbar/workspaces-menu.ts index cc68ee597b85..b2b417c5b149 100644 --- a/Build/Sources/TypeScript/workspaces/toolbar/workspaces-menu.ts +++ b/Build/Sources/TypeScript/workspaces/toolbar/workspaces-menu.ts @@ -11,12 +11,12 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; import ModuleMenu from '@typo3/backend/module-menu'; import Viewport from '@typo3/backend/viewport'; import RegularEvent from '@typo3/core/event/regular-event'; -import {ModuleStateStorage} from '@typo3/backend/storage/module-state-storage'; +import { ModuleStateStorage } from '@typo3/backend/storage/module-state-storage'; enum Identifiers { topbarHeaderSelector = '.t3js-topbar-header', @@ -44,6 +44,19 @@ interface WorkspaceState { * and jump to the workspaces module */ class WorkspacesMenu { + constructor() { + Viewport.Topbar.Toolbar.registerEvent((): void => { + this.initializeEvents(); + WorkspacesMenu.updateBackendContext(); + }); + + new RegularEvent('typo3:datahandler:process', (e: CustomEvent): void => { + const payload = e.detail.payload; + if (payload.table === 'sys_workspace' && payload.action === 'delete' && payload.hasErrors === false) { + Viewport.Topbar.refresh(); + } + }).bindTo(document); + } /** * Refresh the page tree @@ -67,7 +80,7 @@ class WorkspacesMenu { id: workspaceId, title: selectedWorkspaceLink.innerText.trim(), inWorkspace: workspaceId !== 0 - } + }; } /** @@ -110,20 +123,6 @@ class WorkspacesMenu { WorkspacesMenu.updateTopBar(workspaceState); } - constructor() { - Viewport.Topbar.Toolbar.registerEvent((): void => { - this.initializeEvents(); - WorkspacesMenu.updateBackendContext(); - }); - - new RegularEvent('typo3:datahandler:process', (e: CustomEvent): void => { - const payload = e.detail.payload; - if (payload.table === 'sys_workspace' && payload.action === 'delete' && payload.hasErrors === false) { - Viewport.Topbar.refresh(); - } - }).bindTo(document); - } - /** * Changes the data in the module menu and the updates the backend context * This method is also used in the workspaces backend module. @@ -140,7 +139,7 @@ class WorkspacesMenu { toolbarItemContainer.querySelector(Identifiers.menuItemLinkSelector + '[data-workspaceid="' + id + '"]')?.classList.add('active'); // Initiate backend context update - WorkspacesMenu.updateBackendContext({id: id, title: title, inWorkspace: id !== 0}); + WorkspacesMenu.updateBackendContext({ id: id, title: title, inWorkspace: id !== 0 }); } private initializeEvents(): void { @@ -161,7 +160,7 @@ class WorkspacesMenu { (new AjaxRequest(TYPO3.settings.ajaxUrls.workspace_switch)).post({ workspaceId: workspaceId, pageId: ModuleStateStorage.current('web').identifier - }).then(async (response: AjaxResponse): Promise<any> => { + }).then(async (response: AjaxResponse): Promise<void> => { const data = await response.resolve(); if (!data.workspaceId) { data.workspaceId = 0; diff --git a/Build/Sources/TypeScript/workspaces/workspaces.ts b/Build/Sources/TypeScript/workspaces/workspaces.ts index f45f54a9fc84..3191cb15d511 100644 --- a/Build/Sources/TypeScript/workspaces/workspaces.ts +++ b/Build/Sources/TypeScript/workspaces/workspaces.ts @@ -11,12 +11,12 @@ * The TYPO3 project - inspiring people to share! */ -import {AjaxResponse} from '@typo3/core/ajax/ajax-response'; +import { AjaxResponse } from '@typo3/core/ajax/ajax-response'; import AjaxRequest from '@typo3/core/ajax/ajax-request'; -import {SeverityEnum} from '@typo3/backend/enum/severity'; +import { SeverityEnum } from '@typo3/backend/enum/severity'; import $ from 'jquery'; import NProgress from 'nprogress'; -import {default as Modal, ModalElement} from '@typo3/backend/modal'; +import { default as Modal, ModalElement } from '@typo3/backend/modal'; export default class Workspaces { private tid: number = 0; @@ -32,10 +32,10 @@ export default class Workspaces { if (typeof result.sendMailTo !== 'undefined' && result.sendMailTo.length > 0) { $form.append( - $('<label />', {class: 'control-label'}).text(TYPO3.lang['window.sendToNextStageWindow.itemsWillBeSentTo']), + $('<label />', { class: 'control-label' }).text(TYPO3.lang['window.sendToNextStageWindow.itemsWillBeSentTo']), ); $form.append( - $('<div />', {class: 'form-group'}).append( + $('<div />', { class: 'form-group' }).append( $('<button type="button" class="btn btn-default btn-xs t3js-workspace-recipients-selectall" />') .text(TYPO3.lang['window.sendToNextStageWindow.selectAll']), ' ', @@ -46,7 +46,7 @@ export default class Workspaces { for (const recipient of result.sendMailTo) { $form.append( - $('<div />', {class: 'form-check'}).append( + $('<div />', { class: 'form-check' }).append( $('<input />', { type: 'checkbox', name: 'recipients', @@ -65,7 +65,7 @@ export default class Workspaces { if (typeof result.additional !== 'undefined') { $form.append( - $('<div />', {class: 'form-group'}).append( + $('<div />', { class: 'form-group' }).append( $('<label />', { class: 'control-label', 'for': 'additional', @@ -75,13 +75,13 @@ export default class Workspaces { name: 'additional', id: 'additional', }).text(result.additional.value), - $('<span />', {class: 'help-block'}).text(TYPO3.lang['window.sendToNextStageWindow.additionalRecipients.hint']), + $('<span />', { class: 'help-block' }).text(TYPO3.lang['window.sendToNextStageWindow.additionalRecipients.hint']), ), ); } $form.append( - $('<div />', {class: 'form-group'}).append( + $('<div />', { class: 'form-group' }).append( $('<label />', { class: 'control-label', 'for': 'comments', diff --git a/Build/eslintrc.json b/Build/eslintrc.json deleted file mode 100644 index b93b754e5642..000000000000 --- a/Build/eslintrc.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "env": { - "browser": true, - "es6": true - }, - "parser": "@typescript-eslint/parser", - "parserOptions": { - "project": "tsconfig.json", - "sourceType": "module" - }, - "plugins": [ - "@typescript-eslint", - "lit", - "wc" - ], - "settings": { - "wc": { - "elementBaseClasses": ["LitElement"] - } - }, - "rules": { - "@typescript-eslint/indent": ["error", 2], - "@typescript-eslint/interface-name-prefix": "off", - "@typescript-eslint/member-ordering": ["error", { - "default": [ - "public-field", - "protected-field", - "private-field", - "public-static-method", - "protected-static-method", - "private-static-method", - "constructor", - "public-instance-method", - "protected-instance-method", - "private-instance-method" - ] - }], - "@typescript-eslint/naming-convention": ["error", - { - "selector": "class", - "format": ["PascalCase"] - } - ], - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-require-imports": "off", - "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/quotes": ["error", "single"], - "@typescript-eslint/type-annotation-spacing": "error", - "@typescript-eslint/typedef": ["error", { - "parameter": true, - "propertyDeclaration": true, - "memberVariableDeclaration": false - }], - "curly": "error", - "default-case": "error", - "dot-notation": "error", - "eol-last": "error", - "guard-for-in": "error", - "lit/attribute-value-entities": "error", - "lit/binding-positions": "error", - "lit/no-duplicate-template-bindings": "error", - "lit/no-native-attributes": "warn", - "lit/no-invalid-escape-sequences": "error", - "lit/no-invalid-html": "error", - "lit/no-legacy-imports": "error", - "lit/no-legacy-template-syntax": "error", - "lit/no-property-change-update": "error", - "lit/no-useless-template-literals": "error", - "lit/prefer-nothing": "error", - "no-bitwise": "off", - "no-caller": "error", - "no-debugger": "error", - "no-empty": "error", - "no-empty-function": ["error", { - "allow": ["constructors"] - }], - "no-eval": "error", - "no-fallthrough": "error", - "no-new-wrappers": "error", - "no-unused-labels": "error", - "no-unused-vars": "off", - "no-var": "error", - "quotes": "off", - "radix": "error", - "semi": "off", - "wc/no-constructor-attributes": "error", - "wc/no-constructor-params": "error", - "wc/no-invalid-element-name": "error", - "wc/no-self-class": "error", - "wc/no-typos": "error", - "wc/require-listener-teardown": "error" - } -} diff --git a/Build/types/JQuery/draguploader.d.ts b/Build/types/JQuery/draguploader.d.ts new file mode 100644 index 000000000000..a706b7b97724 --- /dev/null +++ b/Build/types/JQuery/draguploader.d.ts @@ -0,0 +1,4 @@ +interface JQuery { + // eslint-disable-next-line @typescript-eslint/ban-types + dragUploader(options?: {}): JQuery; +} diff --git a/Build/types/JQuery/general.d.ts b/Build/types/JQuery/general.d.ts new file mode 100644 index 000000000000..79ca232bdc2e --- /dev/null +++ b/Build/types/JQuery/general.d.ts @@ -0,0 +1,7 @@ +interface JQueryStatic { + escapeSelector(selector: string): string; +} + +interface JQueryTypedEvent<T extends Event> extends JQueryEventObject { + originalEvent: T; +} diff --git a/Build/types/JQuery/minicolors.d.ts b/Build/types/JQuery/minicolors.d.ts new file mode 100644 index 000000000000..00edde5526d2 --- /dev/null +++ b/Build/types/JQuery/minicolors.d.ts @@ -0,0 +1,4 @@ +interface JQuery { + // eslint-disable-next-line @typescript-eslint/ban-types + minicolors(options?: {}): JQuery; +} diff --git a/Build/types/JQuery/paging.d.ts b/Build/types/JQuery/paging.d.ts new file mode 100644 index 000000000000..ab742f852c6b --- /dev/null +++ b/Build/types/JQuery/paging.d.ts @@ -0,0 +1,3 @@ +interface JQuery { + disablePagingAction(): void; +} diff --git a/Build/types/TYPO3/index.d.ts b/Build/types/TYPO3/index.d.ts index 6e04f2d238a1..3796f25d73bc 100644 --- a/Build/types/TYPO3/index.d.ts +++ b/Build/types/TYPO3/index.d.ts @@ -69,15 +69,8 @@ declare module '@typo3/ckeditor5-bundle' { // type definition for global namespace object interface Window { TYPO3: any; - $: any; // only required in ImageManipulation.ts require: Function; list_frame: Window; - jump: Function; - - // required for Paste.ts - // TODO these should be passed as data attributes - pasteAfterLinkTemplate: string; - pasteIntoLinkTemplate: string; } /** @@ -103,19 +96,3 @@ declare module 'taboverride' { const _exported: Taboverride; export default _exported; } - -interface JQueryTypedEvent<T extends Event> extends JQueryEventObject { - originalEvent: T; -} - -/** - * Required to make jQuery plugins "available" in TypeScript - */ -interface JQuery { - dragUploader(options?: any): JQuery; - disablePagingAction(): void; -} - -interface JQueryStatic { - escapeSelector(selector: string): string; -} diff --git a/Build/types/tablesort.d.ts b/Build/types/tablesort.d.ts index aea8d5d9cad8..ad0d8907f7c9 100644 --- a/Build/types/tablesort.d.ts +++ b/Build/types/tablesort.d.ts @@ -1,8 +1,14 @@ +// eslint-disable-next-line @typescript-eslint/no-empty-interface interface Tablesort { } +type TablesortOptions = { + descending: boolean; + sortAttribute: string; +}; + declare const Tablesort: { - new(table: Element, options?: {[key: string]: any}): Tablesort; + new(table: Element, options?: {[key: string]: TablesortOptions}): Tablesort; extend(name: string, pattern: Function, sort: Function): void; } diff --git a/typo3/sysext/adminpanel/Resources/Public/JavaScript/admin-panel.js b/typo3/sysext/adminpanel/Resources/Public/JavaScript/admin-panel.js index 89f89724e76f..75443a66f19e 100644 --- a/typo3/sysext/adminpanel/Resources/Public/JavaScript/admin-panel.js +++ b/typo3/sysext/adminpanel/Resources/Public/JavaScript/admin-panel.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -"use strict";var TYPO3;!function(e){e.AdminPanelSelectors={adminPanelRole:"form[data-typo3-role=typo3-adminPanel]",moduleTriggerRole:"[data-typo3-role=typo3-adminPanel-module-trigger]",moduleParentClass:".typo3-adminPanel-module",contentTabRole:"[data-typo3-role=typo3-adminPanel-content-tab]",saveButtonRole:"[data-typo3-role=typo3-adminPanel-saveButton]",triggerRole:"[data-typo3-role=typo3-adminPanel-trigger]",popupTriggerRole:"[data-typo3-role=typo3-adminPanel-popup-trigger]",panelTriggerRole:"[data-typo3-role=typo3-adminPanel-panel-trigger]",panelParentClass:".typo3-adminPanel-panel",contentSettingsTriggerRole:"[data-typo3-role=typo3-adminPanel-content-settings]",contentSettingsParentClass:".typo3-adminPanel-content-settings",contentParentClass:".typo3-adminPanel-content",zoomTarget:"[data-typo3-zoom-target]",zoomClose:"[data-typo3-zoom-close]",currentContentRole:"[data-typo3-role=typo3-adminPanel-content]",contentPaneRole:"[data-typo3-role=typo3-adminPanel-content-pane]"},e.AdminPanelClasses={active:"active",activeModule:"typo3-adminPanel-module-active",activeContentSetting:"typo3-adminPanel-content-settings-active",backdrop:"typo3-adminPanel-backdrop",activeTab:"typo3-adminPanel-content-header-item-active",activePane:"typo3-adminPanel-content-panes-item-active",noScroll:"typo3-adminPanel-noscroll",zoomShow:"typo3-adminPanel-zoom-show"};e.AdminPanel=class{constructor(){this.adminPanel=document.querySelector(e.AdminPanelSelectors.adminPanelRole),this.modules=this.querySelectorAll(e.AdminPanelSelectors.moduleTriggerRole).map((t=>{const n=t.closest(e.AdminPanelSelectors.moduleParentClass);return new s(this,n,t)})),this.popups=this.querySelectorAll(e.AdminPanelSelectors.popupTriggerRole).map((e=>new t(this,e))),this.panels=this.querySelectorAll(e.AdminPanelSelectors.panelTriggerRole).map((t=>{const a=t.closest(e.AdminPanelSelectors.panelParentClass);return new n(a,t)})),this.contentSettings=this.querySelectorAll(e.AdminPanelSelectors.contentSettingsTriggerRole).map((t=>{const n=t.closest(e.AdminPanelSelectors.contentParentClass).querySelector(e.AdminPanelSelectors.contentSettingsParentClass);return new a(n,t)})),this.trigger=document.querySelector(e.AdminPanelSelectors.triggerRole),this.initializeEvents(),this.addBackdropListener()}disableModules(){this.modules.forEach((e=>e.disable()))}disablePopups(){this.popups.forEach((e=>e.disable()))}renderBackdrop(){const t=document.getElementById("TSFE_ADMIN_PANEL_FORM"),n=document.createElement("div");document.querySelector("body").classList.add(e.AdminPanelClasses.noScroll),n.classList.add(e.AdminPanelClasses.backdrop),t.appendChild(n),this.addBackdropListener()}removeBackdrop(){const t=document.querySelector("."+e.AdminPanelClasses.backdrop);document.querySelector("body").classList.remove(e.AdminPanelClasses.noScroll),null!==t&&t.remove()}querySelectorAll(e,t=null){return null===t?Array.from(document.querySelectorAll(e)):Array.from(t.querySelectorAll(e))}initializeEvents(){this.querySelectorAll(e.AdminPanelSelectors.contentTabRole).forEach((e=>e.addEventListener("click",this.switchTab.bind(this)))),this.querySelectorAll(e.AdminPanelSelectors.zoomTarget).forEach((e=>e.addEventListener("click",this.openZoom.bind(this)))),this.querySelectorAll(e.AdminPanelSelectors.zoomClose).forEach((e=>e.addEventListener("click",this.closeZoom.bind(this)))),this.querySelectorAll(e.AdminPanelSelectors.triggerRole).forEach((e=>e.addEventListener("click",this.toggleAdminPanelState.bind(this)))),this.querySelectorAll(e.AdminPanelSelectors.saveButtonRole).forEach((e=>e.addEventListener("click",this.sendAdminPanelForm.bind(this)))),this.querySelectorAll("[data-typo3-role=typo3-adminPanel-content-close]").forEach((e=>{e.addEventListener("click",(()=>{this.disableModules(),this.removeBackdrop()}))})),this.querySelectorAll(".typo3-adminPanel-table th, .typo3-adminPanel-table td").forEach((e=>{e.addEventListener("click",(()=>{e.focus();try{document.execCommand("copy")}catch(e){}}))}))}switchTab(t){t.preventDefault();const n=e.AdminPanelClasses.activeTab,a=e.AdminPanelClasses.activePane,s=t.currentTarget,l=s.closest(e.AdminPanelSelectors.currentContentRole),i=this.querySelectorAll(e.AdminPanelSelectors.contentTabRole,l),o=this.querySelectorAll(e.AdminPanelSelectors.contentPaneRole,l);i.forEach((e=>e.classList.remove(n))),s.classList.add(n),o.forEach((e=>e.classList.remove(a)));document.querySelector("[data-typo3-tab-id="+s.dataset.typo3TabTarget+"]").classList.add(a)}openZoom(t){t.preventDefault();const n=t.currentTarget.getAttribute("data-typo3-zoom-target");document.querySelector("[data-typo3-zoom-id="+n+"]").classList.add(e.AdminPanelClasses.zoomShow)}closeZoom(t){t.preventDefault();t.currentTarget.closest("[data-typo3-zoom-id]").classList.remove(e.AdminPanelClasses.zoomShow)}sendAdminPanelForm(e){e.preventDefault();const t=new FormData(this.adminPanel),n=new XMLHttpRequest;n.open("POST",this.adminPanel.dataset.typo3AjaxUrl),n.send(t),n.onload=()=>location.assign(this.getCleanReloadUrl())}toggleAdminPanelState(){const e=new XMLHttpRequest;e.open("GET",this.trigger.dataset.typo3AjaxUrl),e.send(),e.onload=()=>location.reload()}getCleanReloadUrl(){let e=[];location.search.substr(1).split("&").forEach((t=>{t&&!t.includes("ADMCMD_")&&e.push(t)}));const t=e?"?"+e.join("&"):"";return location.origin+location.pathname+t}addBackdropListener(){this.querySelectorAll("."+e.AdminPanelClasses.backdrop).forEach((t=>{t.addEventListener("click",(()=>{this.removeBackdrop(),this.querySelectorAll(e.AdminPanelSelectors.moduleTriggerRole).forEach((t=>{t.closest(e.AdminPanelSelectors.moduleParentClass).classList.remove(e.AdminPanelClasses.activeModule)}))}))}))}};class t{constructor(e,t){this.adminPanel=e,this.element=t,this.initializeEvents()}isActive(){return this.element.classList.contains(e.AdminPanelClasses.active)}enable(){this.element.classList.add(e.AdminPanelClasses.active)}disable(){this.element.classList.remove(e.AdminPanelClasses.active)}initializeEvents(){this.element.addEventListener("click",(()=>{this.isActive()?this.disable():(this.adminPanel.disablePopups(),this.enable())}))}}class n{constructor(e,t){this.element=e,this.trigger=t,this.initializeEvents()}isActive(){return this.element.classList.contains(e.AdminPanelClasses.active)}enable(){this.element.classList.add(e.AdminPanelClasses.active)}disable(){this.element.classList.remove(e.AdminPanelClasses.active)}initializeEvents(){this.trigger.addEventListener("click",(()=>{this.isActive()?this.disable():this.enable()}))}}class a{constructor(e,t){this.element=e,this.trigger=t,this.initializeEvents()}isActive(){return this.element.classList.contains(e.AdminPanelClasses.activeContentSetting)}enable(){this.element.classList.add(e.AdminPanelClasses.activeContentSetting)}disable(){this.element.classList.remove(e.AdminPanelClasses.activeContentSetting)}initializeEvents(){this.trigger.addEventListener("click",(()=>{this.isActive()?this.disable():this.enable()}))}}class s{constructor(e,t,n){this.adminPanel=e,this.element=t,this.trigger=n,this.initializeEvents()}isActive(){return this.element.classList.contains(e.AdminPanelClasses.activeModule)}enable(){this.element.classList.add(e.AdminPanelClasses.activeModule)}disable(){this.element.classList.remove(e.AdminPanelClasses.activeModule)}initializeEvents(){this.trigger.addEventListener("click",(()=>{this.adminPanel.removeBackdrop(),this.isActive()?this.disable():(this.adminPanel.disableModules(),this.adminPanel.renderBackdrop(),this.enable())}))}}}(TYPO3||(TYPO3={})),window.addEventListener("load",(()=>new TYPO3.AdminPanel),!1); \ No newline at end of file +"use strict";var TYPO3;!function(e){e.AdminPanelSelectors={adminPanelRole:"form[data-typo3-role=typo3-adminPanel]",moduleTriggerRole:"[data-typo3-role=typo3-adminPanel-module-trigger]",moduleParentClass:".typo3-adminPanel-module",contentTabRole:"[data-typo3-role=typo3-adminPanel-content-tab]",saveButtonRole:"[data-typo3-role=typo3-adminPanel-saveButton]",triggerRole:"[data-typo3-role=typo3-adminPanel-trigger]",popupTriggerRole:"[data-typo3-role=typo3-adminPanel-popup-trigger]",panelTriggerRole:"[data-typo3-role=typo3-adminPanel-panel-trigger]",panelParentClass:".typo3-adminPanel-panel",contentSettingsTriggerRole:"[data-typo3-role=typo3-adminPanel-content-settings]",contentSettingsParentClass:".typo3-adminPanel-content-settings",contentParentClass:".typo3-adminPanel-content",zoomTarget:"[data-typo3-zoom-target]",zoomClose:"[data-typo3-zoom-close]",currentContentRole:"[data-typo3-role=typo3-adminPanel-content]",contentPaneRole:"[data-typo3-role=typo3-adminPanel-content-pane]"},e.AdminPanelClasses={active:"active",activeModule:"typo3-adminPanel-module-active",activeContentSetting:"typo3-adminPanel-content-settings-active",backdrop:"typo3-adminPanel-backdrop",activeTab:"typo3-adminPanel-content-header-item-active",activePane:"typo3-adminPanel-content-panes-item-active",noScroll:"typo3-adminPanel-noscroll",zoomShow:"typo3-adminPanel-zoom-show"};e.AdminPanel=class{constructor(){this.adminPanel=document.querySelector(e.AdminPanelSelectors.adminPanelRole),this.modules=this.querySelectorAll(e.AdminPanelSelectors.moduleTriggerRole).map((t=>{const n=t.closest(e.AdminPanelSelectors.moduleParentClass);return new s(this,n,t)})),this.popups=this.querySelectorAll(e.AdminPanelSelectors.popupTriggerRole).map((e=>new t(this,e))),this.panels=this.querySelectorAll(e.AdminPanelSelectors.panelTriggerRole).map((t=>{const a=t.closest(e.AdminPanelSelectors.panelParentClass);return new n(a,t)})),this.contentSettings=this.querySelectorAll(e.AdminPanelSelectors.contentSettingsTriggerRole).map((t=>{const n=t.closest(e.AdminPanelSelectors.contentParentClass).querySelector(e.AdminPanelSelectors.contentSettingsParentClass);return new a(n,t)})),this.trigger=document.querySelector(e.AdminPanelSelectors.triggerRole),this.initializeEvents(),this.addBackdropListener()}disableModules(){this.modules.forEach((e=>e.disable()))}disablePopups(){this.popups.forEach((e=>e.disable()))}renderBackdrop(){const t=document.getElementById("TSFE_ADMIN_PANEL_FORM"),n=document.createElement("div");document.querySelector("body").classList.add(e.AdminPanelClasses.noScroll),n.classList.add(e.AdminPanelClasses.backdrop),t.appendChild(n),this.addBackdropListener()}removeBackdrop(){const t=document.querySelector("."+e.AdminPanelClasses.backdrop);document.querySelector("body").classList.remove(e.AdminPanelClasses.noScroll),null!==t&&t.remove()}querySelectorAll(e,t=null){return null===t?Array.from(document.querySelectorAll(e)):Array.from(t.querySelectorAll(e))}initializeEvents(){this.querySelectorAll(e.AdminPanelSelectors.contentTabRole).forEach((e=>e.addEventListener("click",this.switchTab.bind(this)))),this.querySelectorAll(e.AdminPanelSelectors.zoomTarget).forEach((e=>e.addEventListener("click",this.openZoom.bind(this)))),this.querySelectorAll(e.AdminPanelSelectors.zoomClose).forEach((e=>e.addEventListener("click",this.closeZoom.bind(this)))),this.querySelectorAll(e.AdminPanelSelectors.triggerRole).forEach((e=>e.addEventListener("click",this.toggleAdminPanelState.bind(this)))),this.querySelectorAll(e.AdminPanelSelectors.saveButtonRole).forEach((e=>e.addEventListener("click",this.sendAdminPanelForm.bind(this)))),this.querySelectorAll("[data-typo3-role=typo3-adminPanel-content-close]").forEach((e=>{e.addEventListener("click",(()=>{this.disableModules(),this.removeBackdrop()}))})),this.querySelectorAll(".typo3-adminPanel-table th, .typo3-adminPanel-table td").forEach((e=>{e.addEventListener("click",(()=>{e.focus();try{document.execCommand("copy")}catch(e){}}))}))}switchTab(t){t.preventDefault();const n=e.AdminPanelClasses.activeTab,a=e.AdminPanelClasses.activePane,s=t.currentTarget,l=s.closest(e.AdminPanelSelectors.currentContentRole),i=this.querySelectorAll(e.AdminPanelSelectors.contentTabRole,l),o=this.querySelectorAll(e.AdminPanelSelectors.contentPaneRole,l);i.forEach((e=>e.classList.remove(n))),s.classList.add(n),o.forEach((e=>e.classList.remove(a)));document.querySelector("[data-typo3-tab-id="+s.dataset.typo3TabTarget+"]").classList.add(a)}openZoom(t){t.preventDefault();const n=t.currentTarget.getAttribute("data-typo3-zoom-target");document.querySelector("[data-typo3-zoom-id="+n+"]").classList.add(e.AdminPanelClasses.zoomShow)}closeZoom(t){t.preventDefault();t.currentTarget.closest("[data-typo3-zoom-id]").classList.remove(e.AdminPanelClasses.zoomShow)}sendAdminPanelForm(e){e.preventDefault();const t=new FormData(this.adminPanel),n=new XMLHttpRequest;n.open("POST",this.adminPanel.dataset.typo3AjaxUrl),n.send(t),n.onload=()=>location.assign(this.getCleanReloadUrl())}toggleAdminPanelState(){const e=new XMLHttpRequest;e.open("GET",this.trigger.dataset.typo3AjaxUrl),e.send(),e.onload=()=>location.reload()}getCleanReloadUrl(){const e=[];location.search.substr(1).split("&").forEach((t=>{t&&!t.includes("ADMCMD_")&&e.push(t)}));const t=e?"?"+e.join("&"):"";return location.origin+location.pathname+t}addBackdropListener(){this.querySelectorAll("."+e.AdminPanelClasses.backdrop).forEach((t=>{t.addEventListener("click",(()=>{this.removeBackdrop(),this.querySelectorAll(e.AdminPanelSelectors.moduleTriggerRole).forEach((t=>{t.closest(e.AdminPanelSelectors.moduleParentClass).classList.remove(e.AdminPanelClasses.activeModule)}))}))}))}};class t{constructor(e,t){this.adminPanel=e,this.element=t,this.initializeEvents()}isActive(){return this.element.classList.contains(e.AdminPanelClasses.active)}enable(){this.element.classList.add(e.AdminPanelClasses.active)}disable(){this.element.classList.remove(e.AdminPanelClasses.active)}initializeEvents(){this.element.addEventListener("click",(()=>{this.isActive()?this.disable():(this.adminPanel.disablePopups(),this.enable())}))}}class n{constructor(e,t){this.element=e,this.trigger=t,this.initializeEvents()}isActive(){return this.element.classList.contains(e.AdminPanelClasses.active)}enable(){this.element.classList.add(e.AdminPanelClasses.active)}disable(){this.element.classList.remove(e.AdminPanelClasses.active)}initializeEvents(){this.trigger.addEventListener("click",(()=>{this.isActive()?this.disable():this.enable()}))}}class a{constructor(e,t){this.element=e,this.trigger=t,this.initializeEvents()}isActive(){return this.element.classList.contains(e.AdminPanelClasses.activeContentSetting)}enable(){this.element.classList.add(e.AdminPanelClasses.activeContentSetting)}disable(){this.element.classList.remove(e.AdminPanelClasses.activeContentSetting)}initializeEvents(){this.trigger.addEventListener("click",(()=>{this.isActive()?this.disable():this.enable()}))}}class s{constructor(e,t,n){this.adminPanel=e,this.element=t,this.trigger=n,this.initializeEvents()}isActive(){return this.element.classList.contains(e.AdminPanelClasses.activeModule)}enable(){this.element.classList.add(e.AdminPanelClasses.activeModule)}disable(){this.element.classList.remove(e.AdminPanelClasses.activeModule)}initializeEvents(){this.trigger.addEventListener("click",(()=>{this.adminPanel.removeBackdrop(),this.isActive()?this.disable():(this.adminPanel.disableModules(),this.adminPanel.renderBackdrop(),this.enable())}))}}}(TYPO3||(TYPO3={})),window.addEventListener("load",(()=>new TYPO3.AdminPanel),!1); \ No newline at end of file diff --git a/typo3/sysext/adminpanel/Resources/Public/JavaScript/modules/cache.js b/typo3/sysext/adminpanel/Resources/Public/JavaScript/modules/cache.js index c23730e2f944..3eb36d879345 100644 --- a/typo3/sysext/adminpanel/Resources/Public/JavaScript/modules/cache.js +++ b/typo3/sysext/adminpanel/Resources/Public/JavaScript/modules/cache.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -"use strict";var TYPO3;!function(t){t.Cache=class{constructor(){this.buttons=document.querySelectorAll('[data-typo3-role="clearCacheButton"]'),this.buttons.forEach((t=>{t.addEventListener("click",(()=>{let e=t.dataset.typo3AjaxUrl,o=new XMLHttpRequest;o.open("GET",e),o.send(),o.onload=()=>{location.reload()}}))}))}}}(TYPO3||(TYPO3={})),window.addEventListener("load",(()=>new TYPO3.Cache),!1); \ No newline at end of file +"use strict";var TYPO3;!function(t){t.Cache=class{constructor(){this.buttons=document.querySelectorAll('[data-typo3-role="clearCacheButton"]'),this.buttons.forEach((t=>{t.addEventListener("click",(()=>{const e=t.dataset.typo3AjaxUrl,o=new XMLHttpRequest;o.open("GET",e),o.send(),o.onload=()=>{location.reload()}}))}))}}}(TYPO3||(TYPO3={})),window.addEventListener("load",(()=>new TYPO3.Cache),!1); \ No newline at end of file diff --git a/typo3/sysext/adminpanel/Resources/Public/JavaScript/modules/preview.js b/typo3/sysext/adminpanel/Resources/Public/JavaScript/modules/preview.js index b35e6377964b..30f1cbb3d54f 100644 --- a/typo3/sysext/adminpanel/Resources/Public/JavaScript/modules/preview.js +++ b/typo3/sysext/adminpanel/Resources/Public/JavaScript/modules/preview.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -"use strict";var TYPO3;!function(e){e.Preview=class{constructor(){if(this.dateField=null,this.timeField=null,this.targetField=null,this.toggleField=null,this.toggleDisplay=()=>{let e=this.toggleField.checked,t=document.getElementById("typo3-adminPanel-preview_simulateDate");e?(t.classList.remove("typo3-adminPanel-group-disable"),this.dateField.disabled=!1,this.timeField.disabled=!1,this.updateDateField()):(t.classList.add("typo3-adminPanel-group-disable"),this.dateField.disabled=!0,this.timeField.disabled=!0,this.targetField.value="")},this.updateDateField=()=>{let e=this.dateField.value,t=this.timeField.value;if(!e&&t){let t=new Date;e=t.getFullYear()+"-"+(t.getMonth()+1)+"-"+t.getDate()}if(e&&!t&&(t="00:00"),e||t){const i=new Date(e+" "+t);this.targetField.value=(i.valueOf()/1e3).toString()}else this.targetField.value=""},this.dateField=document.getElementById("preview_simulateDate-date-hr"),this.timeField=document.getElementById("preview_simulateDate-time-hr"),this.targetField=document.getElementById(this.dateField.dataset.bsTarget),this.toggleField=document.getElementById("typo3-adminPanel-simulate-date-toggle"),this.targetField.value){const e=new Date(1e3*parseInt(this.targetField.value,10));this.dateField.valueAsDate=e,this.timeField.valueAsDate=e}this.toggleField.addEventListener("change",this.toggleDisplay),this.dateField.addEventListener("change",this.updateDateField),this.timeField.addEventListener("change",this.updateDateField)}}}(TYPO3||(TYPO3={})),window.addEventListener("load",(()=>new TYPO3.Preview),!1); \ No newline at end of file +"use strict";var TYPO3;!function(e){e.Preview=class{constructor(){if(this.dateField=null,this.timeField=null,this.targetField=null,this.toggleField=null,this.toggleDisplay=()=>{const e=this.toggleField.checked,t=document.getElementById("typo3-adminPanel-preview_simulateDate");e?(t.classList.remove("typo3-adminPanel-group-disable"),this.dateField.disabled=!1,this.timeField.disabled=!1,this.updateDateField()):(t.classList.add("typo3-adminPanel-group-disable"),this.dateField.disabled=!0,this.timeField.disabled=!0,this.targetField.value="")},this.updateDateField=()=>{let e=this.dateField.value,t=this.timeField.value;if(!e&&t){const t=new Date;e=t.getFullYear()+"-"+(t.getMonth()+1)+"-"+t.getDate()}if(e&&!t&&(t="00:00"),e||t){const i=new Date(e+" "+t);this.targetField.value=(i.valueOf()/1e3).toString()}else this.targetField.value=""},this.dateField=document.getElementById("preview_simulateDate-date-hr"),this.timeField=document.getElementById("preview_simulateDate-time-hr"),this.targetField=document.getElementById(this.dateField.dataset.bsTarget),this.toggleField=document.getElementById("typo3-adminPanel-simulate-date-toggle"),this.targetField.value){const e=new Date(1e3*parseInt(this.targetField.value,10));this.dateField.valueAsDate=e,this.timeField.valueAsDate=e}this.toggleField.addEventListener("change",this.toggleDisplay),this.dateField.addEventListener("change",this.updateDateField),this.timeField.addEventListener("change",this.updateDateField)}}}(TYPO3||(TYPO3={})),window.addEventListener("load",(()=>new TYPO3.Preview),!1); \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/action-dispatcher.js b/typo3/sysext/backend/Resources/Public/JavaScript/action-dispatcher.js index 8958c19f634f..d8144ba02146 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/action-dispatcher.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/action-dispatcher.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import InfoWindow from"@typo3/backend/info-window.js";import RegularEvent from"@typo3/core/event/regular-event.js";import shortcutMenu from"@typo3/backend/toolbar/shortcut-menu.js";import windowManager from"@typo3/backend/window-manager.js";import moduleMenuApp from"@typo3/backend/module-menu.js";import documentService from"@typo3/core/document-service.js";import Utility from"@typo3/backend/utility.js";class ActionDispatcher{constructor(){this.delegates={},this.createDelegates(),documentService.ready().then((()=>this.registerEvents()))}static resolveArguments(e){if(e.dataset.dispatchArgs){const t=e.dataset.dispatchArgs.replace(/"/g,'"'),n=JSON.parse(t);return n instanceof Array?Utility.trimItems(n):null}if(e.dataset.dispatchArgsList){const t=e.dataset.dispatchArgsList.split(",");return Utility.trimItems(t)}return null}static enrichItems(e,t,n){return e.map((e=>e instanceof Object&&e.$event?e.$target?n:e.$event?t:void 0:e))}createDelegates(){this.delegates={"TYPO3.InfoWindow.showItem":InfoWindow.showItem.bind(null),"TYPO3.ShortcutMenu.createShortcut":shortcutMenu.createShortcut.bind(shortcutMenu),"TYPO3.WindowManager.localOpen":windowManager.localOpen.bind(windowManager),"TYPO3.ModuleMenu.showModule":moduleMenuApp.App.showModule.bind(moduleMenuApp.App)}}registerEvents(){new RegularEvent("click",this.handleClickEvent.bind(this)).delegateTo(document,"[data-dispatch-action]")}handleClickEvent(e,t){e.preventDefault(),this.delegateTo(e,t)}delegateTo(e,t){if(t.hasAttribute("data-dispatch-disabled"))return;const n=t.dataset.dispatchAction;let r=ActionDispatcher.resolveArguments(t);r instanceof Array&&(r=r.map((n=>{switch(n){case"{$target}":return t;case"{$event}":return e;default:return n}}))),this.delegates[n]&&this.delegates[n].apply(null,r||[])}}export default new ActionDispatcher; \ No newline at end of file +import InfoWindow from"@typo3/backend/info-window.js";import RegularEvent from"@typo3/core/event/regular-event.js";import shortcutMenu from"@typo3/backend/toolbar/shortcut-menu.js";import windowManager from"@typo3/backend/window-manager.js";import moduleMenuApp from"@typo3/backend/module-menu.js";import documentService from"@typo3/core/document-service.js";import Utility from"@typo3/backend/utility.js";class ActionDispatcher{constructor(){this.delegates={},this.createDelegates(),documentService.ready().then((()=>this.registerEvents()))}static resolveArguments(e){if(e.dataset.dispatchArgs){const t=e.dataset.dispatchArgs.replace(/"/g,'"'),n=JSON.parse(t);return n instanceof Array?Utility.trimItems(n):null}if(e.dataset.dispatchArgsList){const t=e.dataset.dispatchArgsList.split(",");return Utility.trimItems(t)}return null}createDelegates(){this.delegates={"TYPO3.InfoWindow.showItem":InfoWindow.showItem.bind(null),"TYPO3.ShortcutMenu.createShortcut":shortcutMenu.createShortcut.bind(shortcutMenu),"TYPO3.WindowManager.localOpen":windowManager.localOpen.bind(windowManager),"TYPO3.ModuleMenu.showModule":moduleMenuApp.App.showModule.bind(moduleMenuApp.App)}}registerEvents(){new RegularEvent("click",this.handleClickEvent.bind(this)).delegateTo(document,"[data-dispatch-action]")}handleClickEvent(e,t){e.preventDefault(),this.delegateTo(e,t)}delegateTo(e,t){if(t.hasAttribute("data-dispatch-disabled"))return;const n=t.dataset.dispatchAction;let r=ActionDispatcher.resolveArguments(t);r instanceof Array&&(r=r.map((n=>{switch(n){case"{$target}":return t;case"{$event}":return e;default:return n}}))),this.delegates[n]&&this.delegates[n].apply(null,r||[])}}export default new ActionDispatcher; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/ajax-data-handler.js b/typo3/sysext/backend/Resources/Public/JavaScript/ajax-data-handler.js index d488c3eb55c7..52b1e9e91344 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/ajax-data-handler.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/ajax-data-handler.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{BroadcastMessage}from"@typo3/backend/broadcast-message.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import DocumentService from"@typo3/core/document-service.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import $ from"jquery";import BroadcastService from"@typo3/backend/broadcast-service.js";import Icons from"@typo3/backend/icons.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";var Identifiers;!function(e){e.hide=".t3js-record-hide",e.delete=".t3js-record-delete",e.icon=".t3js-icon"}(Identifiers||(Identifiers={}));class AjaxDataHandler{static refreshPageTree(){top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))}static call(e){return new AjaxRequest(TYPO3.settings.ajaxUrls.record_process).withQueryArguments(e).get().then((async e=>await e.resolve()))}constructor(){DocumentService.ready().then((()=>{this.initialize()}))}process(e,t){return AjaxDataHandler.call(e).then((e=>{if(e.hasErrors&&this.handleErrors(e),t){const a={...t,hasErrors:e.hasErrors},n=new BroadcastMessage("datahandler","process",a);BroadcastService.post(n);const s=new CustomEvent("typo3:datahandler:process",{detail:{payload:a}});document.dispatchEvent(s)}return e}))}initialize(){$(document).on("click",Identifiers.hide,(e=>{e.preventDefault();const t=$(e.currentTarget),a=t.find(Identifiers.icon),n=t.closest("tr[data-uid]"),s=t.data("params");this._showSpinnerIcon(a),this.process(s).then((e=>{e.hasErrors||this.toggleRow(n)}))})),$(document).on("click",Identifiers.delete,(e=>{e.preventDefault();const t=$(e.currentTarget),a=Modal.confirm(t.data("title"),t.data("message"),SeverityEnum.warning,[{text:t.data("button-close-text")||TYPO3.lang["button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:t.data("button-ok-text")||TYPO3.lang["button.delete"]||"Delete",btnClass:"btn-warning",name:"delete"}]);a.addEventListener("button.clicked",(e=>{"cancel"===e.target.getAttribute("name")?a.hideModal():"delete"===e.target.getAttribute("name")&&(a.hideModal(),this.deleteRecord(t))}))}))}toggleRow(e){const t=e.find(Identifiers.hide),a=t.closest("table[data-table]").data("table"),n=t.data("params");let s,r,o;"hidden"===t.data("state")?(r="visible",s=n.replace("=0","=1"),o="actions-edit-hide"):(r="hidden",s=n.replace("=1","=0"),o="actions-edit-unhide"),t.data("state",r).data("params",s);const i=t.find(Identifiers.icon);Icons.getIcon(o,Icons.sizes.small).then((e=>{i.replaceWith(e)}));const d=e.find(".col-icon "+Identifiers.icon);"hidden"===r?Icons.getIcon("miscellaneous-placeholder",Icons.sizes.small,"overlay-hidden").then((e=>{d.append($(e).find(".icon-overlay"))})):d.find(".icon-overlay").remove(),e.fadeTo("fast",.4,(()=>{e.fadeTo("fast",1)})),"pages"===a&&AjaxDataHandler.refreshPageTree()}deleteRecord(e){const t=e.data("params");let a=e.find(Identifiers.icon);this._showSpinnerIcon(a);const n=e.closest("table[data-table]"),s=n.data("table");let r=e.closest("tr[data-uid]");const o=r.data("uid"),i={component:"datahandler",action:"delete",table:s,uid:o};this.process(t,i).then((t=>{if(Icons.getIcon("actions-edit-delete",Icons.sizes.small).then((t=>{a=e.find(Identifiers.icon),a.replaceWith(t)})),!t.hasErrors){const t=e.closest(".panel"),a=t.find(".panel-heading"),i=n.find("[data-l10nparent="+o+"]").closest("tr[data-uid]");if(r=r.add(i),r.fadeTo("slow",.4,(()=>{r.slideUp("slow",(()=>{r.remove(),0===n.find("tbody tr").length&&t.slideUp("slow")}))})),"0"===e.data("l10parent")||""===e.data("l10parent")){const e=Number(a.find(".t3js-table-total-items").html());a.find(".t3js-table-total-items").text(e-1)}"pages"===s&&AjaxDataHandler.refreshPageTree()}}))}handleErrors(e){for(let t of e.messages)Notification.error(t.title,t.message)}_showSpinnerIcon(e){Icons.getIcon("spinner-circle-dark",Icons.sizes.small).then((t=>{e.replaceWith(t)}))}}export default new AjaxDataHandler; \ No newline at end of file +import{BroadcastMessage}from"@typo3/backend/broadcast-message.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import DocumentService from"@typo3/core/document-service.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import $ from"jquery";import BroadcastService from"@typo3/backend/broadcast-service.js";import Icons from"@typo3/backend/icons.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";var Identifiers;!function(e){e.hide=".t3js-record-hide",e.delete=".t3js-record-delete",e.icon=".t3js-icon"}(Identifiers||(Identifiers={}));class AjaxDataHandler{constructor(){DocumentService.ready().then((()=>{this.initialize()}))}static refreshPageTree(){top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))}static call(e){return new AjaxRequest(TYPO3.settings.ajaxUrls.record_process).withQueryArguments(e).get().then((async e=>await e.resolve()))}process(e,t){return AjaxDataHandler.call(e).then((e=>{if(e.hasErrors&&this.handleErrors(e),t){const a={...t,hasErrors:e.hasErrors},n=new BroadcastMessage("datahandler","process",a);BroadcastService.post(n);const s=new CustomEvent("typo3:datahandler:process",{detail:{payload:a}});document.dispatchEvent(s)}return e}))}initialize(){$(document).on("click",Identifiers.hide,(e=>{e.preventDefault();const t=$(e.currentTarget),a=t.find(Identifiers.icon),n=t.closest("tr[data-uid]"),s=t.data("params");this._showSpinnerIcon(a),this.process(s).then((e=>{e.hasErrors||this.toggleRow(n)}))})),$(document).on("click",Identifiers.delete,(e=>{e.preventDefault();const t=$(e.currentTarget),a=Modal.confirm(t.data("title"),t.data("message"),SeverityEnum.warning,[{text:t.data("button-close-text")||TYPO3.lang["button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:t.data("button-ok-text")||TYPO3.lang["button.delete"]||"Delete",btnClass:"btn-warning",name:"delete"}]);a.addEventListener("button.clicked",(e=>{"cancel"===e.target.getAttribute("name")?a.hideModal():"delete"===e.target.getAttribute("name")&&(a.hideModal(),this.deleteRecord(t))}))}))}toggleRow(e){const t=e.find(Identifiers.hide),a=t.closest("table[data-table]").data("table"),n=t.data("params");let s,o,r;"hidden"===t.data("state")?(o="visible",s=n.replace("=0","=1"),r="actions-edit-hide"):(o="hidden",s=n.replace("=1","=0"),r="actions-edit-unhide"),t.data("state",o).data("params",s);const i=t.find(Identifiers.icon);Icons.getIcon(r,Icons.sizes.small).then((e=>{i.replaceWith(e)}));const d=e.find(".col-icon "+Identifiers.icon);"hidden"===o?Icons.getIcon("miscellaneous-placeholder",Icons.sizes.small,"overlay-hidden").then((e=>{d.append($(e).find(".icon-overlay"))})):d.find(".icon-overlay").remove(),e.fadeTo("fast",.4,(()=>{e.fadeTo("fast",1)})),"pages"===a&&AjaxDataHandler.refreshPageTree()}deleteRecord(e){const t=e.data("params");let a=e.find(Identifiers.icon);this._showSpinnerIcon(a);const n=e.closest("table[data-table]"),s=n.data("table");let o=e.closest("tr[data-uid]");const r=o.data("uid"),i={component:"datahandler",action:"delete",table:s,uid:r};this.process(t,i).then((t=>{if(Icons.getIcon("actions-edit-delete",Icons.sizes.small).then((t=>{a=e.find(Identifiers.icon),a.replaceWith(t)})),!t.hasErrors){const t=e.closest(".panel"),a=t.find(".panel-heading"),i=n.find("[data-l10nparent="+r+"]").closest("tr[data-uid]");if(o=o.add(i),o.fadeTo("slow",.4,(()=>{o.slideUp("slow",(()=>{o.remove(),0===n.find("tbody tr").length&&t.slideUp("slow")}))})),"0"===e.data("l10parent")||""===e.data("l10parent")){const e=Number(a.find(".t3js-table-total-items").html());a.find(".t3js-table-total-items").text(e-1)}"pages"===s&&AjaxDataHandler.refreshPageTree()}}))}handleErrors(e){for(const t of e.messages)Notification.error(t.title,t.message)}_showSpinnerIcon(e){Icons.getIcon("spinner-circle-dark",Icons.sizes.small).then((t=>{e.replaceWith(t)}))}}export default new AjaxDataHandler; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/broadcast-message.js b/typo3/sysext/backend/Resources/Public/JavaScript/broadcast-message.js index bbbcf2803d19..f3789c45ec0b 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/broadcast-message.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/broadcast-message.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -export class BroadcastMessage{constructor(e,t,a){if(!e||!t)throw new Error("Properties componentName and eventName have to be defined");this.componentName=e,this.eventName=t,this.payload=a||{}}static fromData(e){let t=Object.assign({},e);return delete t.componentName,delete t.eventName,new BroadcastMessage(e.componentName,e.eventName,t)}createCustomEvent(e="typo3"){return new CustomEvent([e,this.componentName,this.eventName].join(":"),{detail:this.payload})}} \ No newline at end of file +export class BroadcastMessage{constructor(e,t,a){if(!e||!t)throw new Error("Properties componentName and eventName have to be defined");this.componentName=e,this.eventName=t,this.payload=a||{}}static fromData(e){const t=Object.assign({},e);return delete t.componentName,delete t.eventName,new BroadcastMessage(e.componentName,e.eventName,t)}createCustomEvent(e="typo3"){return new CustomEvent([e,this.componentName,this.eventName].join(":"),{detail:this.payload})}} \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/clear-cache.js b/typo3/sysext/backend/Resources/Public/JavaScript/clear-cache.js index 62baf111da40..4ecc6fc38f49 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/clear-cache.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/clear-cache.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import Notification from"@typo3/backend/notification.js";import Icons from"@typo3/backend/icons.js";import RegularEvent from"@typo3/core/event/regular-event.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";var Identifiers;!function(e){e.clearCache=".t3js-clear-page-cache",e.icon=".t3js-icon"}(Identifiers||(Identifiers={}));class ClearCache{static setDisabled(e,t){e.disabled=t,e.classList.toggle("disabled",t)}static sendClearCacheRequest(e){const t=new AjaxRequest(TYPO3.settings.ajaxUrls.web_list_clearpagecache).withQueryArguments({id:e}).get({cache:"no-cache"});return t.then((async e=>{const t=await e.resolve();!0===t.success?Notification.success(t.title,t.message,1):Notification.error(t.title,t.message,1)}),(()=>{Notification.error("Clearing page caches went wrong on the server side.")})),t}constructor(){this.registerClickHandler()}registerClickHandler(){const e=document.querySelector(`${Identifiers.clearCache}:not([disabled])`);null!==e&&new RegularEvent("click",(e=>{e.preventDefault();const t=e.currentTarget,a=parseInt(t.dataset.id,10);ClearCache.setDisabled(t,!0),Icons.getIcon("spinner-circle-dark",Icons.sizes.small,null,"disabled").then((e=>{t.querySelector(Identifiers.icon).outerHTML=e})),ClearCache.sendClearCacheRequest(a).finally((()=>{Icons.getIcon("actions-system-cache-clear",Icons.sizes.small).then((e=>{t.querySelector(Identifiers.icon).outerHTML=e})),ClearCache.setDisabled(t,!1)}))})).bindTo(e)}}export default new ClearCache; \ No newline at end of file +import Notification from"@typo3/backend/notification.js";import Icons from"@typo3/backend/icons.js";import RegularEvent from"@typo3/core/event/regular-event.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";var Identifiers;!function(e){e.clearCache=".t3js-clear-page-cache",e.icon=".t3js-icon"}(Identifiers||(Identifiers={}));class ClearCache{constructor(){this.registerClickHandler()}static setDisabled(e,t){e.disabled=t,e.classList.toggle("disabled",t)}static sendClearCacheRequest(e){const t=new AjaxRequest(TYPO3.settings.ajaxUrls.web_list_clearpagecache).withQueryArguments({id:e}).get({cache:"no-cache"});return t.then((async e=>{const t=await e.resolve();!0===t.success?Notification.success(t.title,t.message,1):Notification.error(t.title,t.message,1)}),(()=>{Notification.error("Clearing page caches went wrong on the server side.")})),t}registerClickHandler(){const e=document.querySelector(`${Identifiers.clearCache}:not([disabled])`);null!==e&&new RegularEvent("click",(e=>{e.preventDefault();const t=e.currentTarget,a=parseInt(t.dataset.id,10);ClearCache.setDisabled(t,!0),Icons.getIcon("spinner-circle-dark",Icons.sizes.small,null,"disabled").then((e=>{t.querySelector(Identifiers.icon).outerHTML=e})),ClearCache.sendClearCacheRequest(a).finally((()=>{Icons.getIcon("actions-system-cache-clear",Icons.sizes.small).then((e=>{t.querySelector(Identifiers.icon).outerHTML=e})),ClearCache.setDisabled(t,!1)}))})).bindTo(e)}}export default new ClearCache; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/column-selector-button.js b/typo3/sysext/backend/Resources/Public/JavaScript/column-selector-button.js index 4bfd1a729dac..71940ee127c1 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/column-selector-button.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/column-selector-button.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -var ColumnSelectorButton_1,Selectors,SelectorActions,__decorate=function(e,t,o,l){var n,r=arguments.length,c=r<3?t:null===l?l=Object.getOwnPropertyDescriptor(t,o):l;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)c=Reflect.decorate(e,t,o,l);else for(var s=e.length-1;s>=0;s--)(n=e[s])&&(c=(r<3?n(c):r>3?n(t,o,c):n(t,o))||c);return r>3&&c&&Object.defineProperty(t,o,c),c};import{html,css,LitElement}from"lit";import{customElement,property}from"lit/decorators.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import Severity from"@typo3/backend/severity.js";import{default as Modal}from"@typo3/backend/modal.js";import{lll}from"@typo3/core/lit-helper.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Notification from"@typo3/backend/notification.js";!function(e){e.columnsSelector=".t3js-column-selector",e.columnsContainerSelector=".t3js-column-selector-container",e.columnsFilterSelector='input[name="columns-filter"]',e.columnsSelectorActionsSelector=".t3js-column-selector-actions"}(Selectors||(Selectors={})),function(e){e.toggle="select-toggle",e.all="select-all",e.none="select-none"}(SelectorActions||(SelectorActions={}));let ColumnSelectorButton=ColumnSelectorButton_1=class extends LitElement{constructor(){super(),this.title="Show columns",this.ok=lll("button.ok")||"Update",this.close=lll("button.close")||"Close",this.error="Could not update columns",this.addEventListener("click",(e=>{e.preventDefault(),this.showColumnSelectorModal()})),this.addEventListener("keydown",(e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),this.showColumnSelectorModal())}))}static toggleSelectorActions(e,t,o,l=!1){t.classList.add("disabled");for(let o=0;o<e.length;o++)if(!e[o].disabled&&!e[o].checked&&(l||!ColumnSelectorButton_1.isColumnHidden(e[o]))){t.classList.remove("disabled");break}o.classList.add("disabled");for(let t=0;t<e.length;t++)if(!e[t].disabled&&e[t].checked&&(l||!ColumnSelectorButton_1.isColumnHidden(e[t]))){o.classList.remove("disabled");break}}static isColumnHidden(e){return e.closest(Selectors.columnsContainerSelector)?.classList.contains("hidden")}static filterColumns(e,t){t.forEach((t=>{const o=t.closest(Selectors.columnsContainerSelector);if(!t.disabled&&null!==o){const t=o.querySelector(".form-check-label-text")?.textContent;t&&t.length&&o.classList.toggle("hidden",""!==e.value&&!RegExp(e.value,"i").test(t.trim().replace(/\[\]/g,"").replace(/\s+/g," ")))}}))}connectedCallback(){this.hasAttribute("role")||this.setAttribute("role","button"),this.hasAttribute("tabindex")||this.setAttribute("tabindex","0")}render(){return html`<slot></slot>`}showColumnSelectorModal(){if(!this.url||!this.target)return;const e=Modal.advanced({content:this.url,title:this.title,severity:SeverityEnum.notice,size:Modal.sizes.medium,type:Modal.types.ajax,buttons:[{text:this.close,active:!0,btnClass:"btn-default",name:"cancel",trigger:(e,t)=>t.hideModal()},{text:this.ok,btnClass:"btn-"+Severity.getCssClass(SeverityEnum.info),name:"update",trigger:(e,t)=>this.processSelection(t)}],ajaxCallback:()=>this.handleModalContentLoaded(e)})}processSelection(e){const t=e.querySelector("form");null!==t?new AjaxRequest(TYPO3.settings.ajaxUrls.show_columns).post(new FormData(t)).then((async e=>{const t=await e.resolve();!0===t.success?(this.ownerDocument.location.href=this.target,this.ownerDocument.location.reload()):Notification.error(t.message||"No update was performed"),Modal.dismiss()})).catch((()=>{this.abortSelection()})):this.abortSelection()}handleModalContentLoaded(e){const t=e.querySelector("form");if(null===t)return;t.addEventListener("submit",(e=>{e.preventDefault()}));const o=e.querySelectorAll(Selectors.columnsSelector),l=e.querySelector(Selectors.columnsFilterSelector),n=e.querySelector(Selectors.columnsSelectorActionsSelector),r=n.querySelector('button[data-action="'+SelectorActions.all+'"]'),c=n.querySelector('button[data-action="'+SelectorActions.none+'"]');o.length&&null!==l&&null!==r&&null!==c&&(ColumnSelectorButton_1.toggleSelectorActions(o,r,c,!0),o.forEach((e=>{e.addEventListener("change",(()=>{ColumnSelectorButton_1.toggleSelectorActions(o,r,c)}))})),l.addEventListener("keydown",(e=>{const t=e.target;"Escape"===e.code&&(e.stopImmediatePropagation(),t.value="")})),l.addEventListener("keyup",(e=>{ColumnSelectorButton_1.filterColumns(e.target,o),ColumnSelectorButton_1.toggleSelectorActions(o,r,c)})),l.addEventListener("search",(e=>{ColumnSelectorButton_1.filterColumns(e.target,o),ColumnSelectorButton_1.toggleSelectorActions(o,r,c)})),n.querySelectorAll("button[data-action]").forEach((e=>{e.addEventListener("click",(e=>{e.preventDefault();const t=e.currentTarget;if(t.dataset.action){switch(t.dataset.action){case SelectorActions.toggle:o.forEach((e=>{e.disabled||ColumnSelectorButton_1.isColumnHidden(e)||(e.checked=!e.checked)}));break;case SelectorActions.all:o.forEach((e=>{e.disabled||ColumnSelectorButton_1.isColumnHidden(e)||(e.checked=!0)}));break;case SelectorActions.none:o.forEach((e=>{e.disabled||ColumnSelectorButton_1.isColumnHidden(e)||(e.checked=!1)}));break;default:Notification.warning("Unknown selector action")}ColumnSelectorButton_1.toggleSelectorActions(o,r,c)}}))})))}abortSelection(){Notification.error(this.error),Modal.dismiss()}};ColumnSelectorButton.styles=[css`:host { cursor: pointer; appearance: button; }`],__decorate([property({type:String})],ColumnSelectorButton.prototype,"url",void 0),__decorate([property({type:String})],ColumnSelectorButton.prototype,"target",void 0),__decorate([property({type:String})],ColumnSelectorButton.prototype,"title",void 0),__decorate([property({type:String})],ColumnSelectorButton.prototype,"ok",void 0),__decorate([property({type:String})],ColumnSelectorButton.prototype,"close",void 0),__decorate([property({type:String})],ColumnSelectorButton.prototype,"error",void 0),ColumnSelectorButton=ColumnSelectorButton_1=__decorate([customElement("typo3-backend-column-selector-button")],ColumnSelectorButton); \ No newline at end of file +var ColumnSelectorButton_1,Selectors,SelectorActions,__decorate=function(e,t,o,l){var n,r=arguments.length,c=r<3?t:null===l?l=Object.getOwnPropertyDescriptor(t,o):l;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)c=Reflect.decorate(e,t,o,l);else for(var s=e.length-1;s>=0;s--)(n=e[s])&&(c=(r<3?n(c):r>3?n(t,o,c):n(t,o))||c);return r>3&&c&&Object.defineProperty(t,o,c),c};import{html,css,LitElement}from"lit";import{customElement,property}from"lit/decorators.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import Severity from"@typo3/backend/severity.js";import{default as Modal}from"@typo3/backend/modal.js";import{lll}from"@typo3/core/lit-helper.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Notification from"@typo3/backend/notification.js";!function(e){e.columnsSelector=".t3js-column-selector",e.columnsContainerSelector=".t3js-column-selector-container",e.columnsFilterSelector='input[name="columns-filter"]',e.columnsSelectorActionsSelector=".t3js-column-selector-actions"}(Selectors||(Selectors={})),function(e){e.toggle="select-toggle",e.all="select-all",e.none="select-none"}(SelectorActions||(SelectorActions={}));let ColumnSelectorButton=ColumnSelectorButton_1=class extends LitElement{constructor(){super(),this.title="Show columns",this.ok=lll("button.ok")||"Update",this.close=lll("button.close")||"Close",this.error="Could not update columns",this.addEventListener("click",(e=>{e.preventDefault(),this.showColumnSelectorModal()})),this.addEventListener("keydown",(e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),this.showColumnSelectorModal())}))}static toggleSelectorActions(e,t,o,l=!1){t.classList.add("disabled");for(let o=0;o<e.length;o++)if(!e[o].disabled&&!e[o].checked&&(l||!ColumnSelectorButton_1.isColumnHidden(e[o]))){t.classList.remove("disabled");break}o.classList.add("disabled");for(let t=0;t<e.length;t++)if(!e[t].disabled&&e[t].checked&&(l||!ColumnSelectorButton_1.isColumnHidden(e[t]))){o.classList.remove("disabled");break}}static isColumnHidden(e){return e.closest(Selectors.columnsContainerSelector)?.classList.contains("hidden")}static filterColumns(e,t){t.forEach((t=>{const o=t.closest(Selectors.columnsContainerSelector);if(!t.disabled&&null!==o){const t=o.querySelector(".form-check-label-text")?.textContent;t&&t.length&&o.classList.toggle("hidden",""!==e.value&&!RegExp(e.value,"i").test(t.trim().replace(/\[\]/g,"").replace(/\s+/g," ")))}}))}connectedCallback(){this.hasAttribute("role")||this.setAttribute("role","button"),this.hasAttribute("tabindex")||this.setAttribute("tabindex","0")}render(){return html`<slot></slot>`}showColumnSelectorModal(){if(!this.url||!this.target)return;const e=Modal.advanced({content:this.url,title:this.title,severity:SeverityEnum.notice,size:Modal.sizes.medium,type:Modal.types.ajax,buttons:[{text:this.close,active:!0,btnClass:"btn-default",name:"cancel",trigger:(e,t)=>t.hideModal()},{text:this.ok,btnClass:"btn-"+Severity.getCssClass(SeverityEnum.info),name:"update",trigger:(e,t)=>this.processSelection(t)}],ajaxCallback:()=>this.handleModalContentLoaded(e)})}processSelection(e){const t=e.querySelector("form");null!==t?new AjaxRequest(TYPO3.settings.ajaxUrls.show_columns).post(new FormData(t)).then((async e=>{const t=await e.resolve();!0===t.success?(this.ownerDocument.location.href=this.target,this.ownerDocument.location.reload()):Notification.error(t.message||"No update was performed"),Modal.dismiss()})).catch((()=>{this.abortSelection()})):this.abortSelection()}handleModalContentLoaded(e){const t=e.querySelector("form");if(null===t)return;t.addEventListener("submit",(e=>{e.preventDefault()}));const o=e.querySelectorAll(Selectors.columnsSelector),l=e.querySelector(Selectors.columnsFilterSelector),n=e.querySelector(Selectors.columnsSelectorActionsSelector),r=n.querySelector('button[data-action="'+SelectorActions.all+'"]'),c=n.querySelector('button[data-action="'+SelectorActions.none+'"]');o.length&&null!==l&&null!==r&&null!==c&&(ColumnSelectorButton_1.toggleSelectorActions(o,r,c,!0),o.forEach((e=>{e.addEventListener("change",(()=>{ColumnSelectorButton_1.toggleSelectorActions(o,r,c)}))})),l.addEventListener("keydown",(e=>{const t=e.target;"Escape"===e.code&&(e.stopImmediatePropagation(),t.value="")})),l.addEventListener("keyup",(e=>{ColumnSelectorButton_1.filterColumns(e.target,o),ColumnSelectorButton_1.toggleSelectorActions(o,r,c)})),l.addEventListener("search",(e=>{ColumnSelectorButton_1.filterColumns(e.target,o),ColumnSelectorButton_1.toggleSelectorActions(o,r,c)})),n.querySelectorAll("button[data-action]").forEach((e=>{e.addEventListener("click",(e=>{e.preventDefault();const t=e.currentTarget;if(t.dataset.action){switch(t.dataset.action){case SelectorActions.toggle:o.forEach((e=>{e.disabled||ColumnSelectorButton_1.isColumnHidden(e)||(e.checked=!e.checked)}));break;case SelectorActions.all:o.forEach((e=>{e.disabled||ColumnSelectorButton_1.isColumnHidden(e)||(e.checked=!0)}));break;case SelectorActions.none:o.forEach((e=>{e.disabled||ColumnSelectorButton_1.isColumnHidden(e)||(e.checked=!1)}));break;default:Notification.warning("Unknown selector action")}ColumnSelectorButton_1.toggleSelectorActions(o,r,c)}}))})))}abortSelection(){Notification.error(this.error),Modal.dismiss()}};ColumnSelectorButton.styles=[css`:host { cursor: pointer; appearance: button; }`],__decorate([property({type:String})],ColumnSelectorButton.prototype,"url",void 0),__decorate([property({type:String})],ColumnSelectorButton.prototype,"target",void 0),__decorate([property({type:String})],ColumnSelectorButton.prototype,"title",void 0),__decorate([property({type:String})],ColumnSelectorButton.prototype,"ok",void 0),__decorate([property({type:String})],ColumnSelectorButton.prototype,"close",void 0),__decorate([property({type:String})],ColumnSelectorButton.prototype,"error",void 0),ColumnSelectorButton=ColumnSelectorButton_1=__decorate([customElement("typo3-backend-column-selector-button")],ColumnSelectorButton);export{ColumnSelectorButton}; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/context-menu-actions.js b/typo3/sysext/backend/Resources/Public/JavaScript/context-menu-actions.js index 70d4aa390e0b..d4445b40edde 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/context-menu-actions.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/context-menu-actions.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{SeverityEnum}from"@typo3/backend/enum/severity.js";import AjaxDataHandler from"@typo3/backend/ajax-data-handler.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoWindow from"@typo3/backend/info-window.js";import Modal from"@typo3/backend/modal.js";import ModuleMenu from"@typo3/backend/module-menu.js";import Notification from"@typo3/backend/notification.js";import Viewport from"@typo3/backend/viewport.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";import"@typo3/backend/new-content-element-wizard.js";class ContextMenuActions{static getReturnUrl(){return encodeURIComponent(top.list_frame.document.location.pathname+top.list_frame.document.location.search)}static editRecord(t,e,n){let o="",r=n.pagesLanguageUid;r&&(o="&overrideVals[pages][sys_language_uid]="+r),Viewport.ContentContainer.setUrl(top.TYPO3.settings.FormEngine.moduleUrl+"&edit["+t+"]["+e+"]=edit"+o+"&returnUrl="+ContextMenuActions.getReturnUrl())}static viewRecord(t,e,n){const o=n.previewUrl;if(o){window.open(o,"newTYPO3frontendWindow").focus()}}static openInfoPopUp(t,e){InfoWindow.showItem(t,e)}static mountAsTreeRoot(t,e){if("pages"===t){const t=new CustomEvent("typo3:pagetree:mountPoint",{detail:{pageId:e}});top.document.dispatchEvent(t)}}static newPageWizard(t,e,n){const o=n.pagesNewWizardUrl;Viewport.ContentContainer.setUrl(o+"&returnUrl="+ContextMenuActions.getReturnUrl())}static newContentWizard(t,e,n){let o=n.newWizardUrl;o&&(o+="&returnUrl="+ContextMenuActions.getReturnUrl(),Modal.advanced({title:n.title,type:Modal.types.ajax,size:Modal.sizes.large,content:o,severity:SeverityEnum.notice}))}static newRecord(t,e){Viewport.ContentContainer.setUrl(top.TYPO3.settings.FormEngine.moduleUrl+"&edit["+t+"]["+("pages"!==t?"-":"")+e+"]=new&returnUrl="+ContextMenuActions.getReturnUrl())}static openHistoryPopUp(t,e){Viewport.ContentContainer.setUrl(top.TYPO3.settings.RecordHistory.moduleUrl+"&element="+t+":"+e+"&returnUrl="+ContextMenuActions.getReturnUrl())}static openListModule(t,e,n){const o="pages"===t?e:n.pageUid;ModuleMenu.App.showModule("web_list","id="+o)}static pagesSort(t,e,n){const o=n.pagesSortUrl;o&&Viewport.ContentContainer.setUrl(o)}static pagesNewMultiple(t,e,n){const o=n.pagesNewMultipleUrl;o&&Viewport.ContentContainer.setUrl(o)}static disableRecord(t,e,n){const o=n.disableField||"hidden";Viewport.ContentContainer.setUrl(top.TYPO3.settings.RecordCommit.moduleUrl+"&data["+t+"]["+e+"]["+o+"]=1&redirect="+ContextMenuActions.getReturnUrl())}static enableRecord(t,e,n){const o=n.disableField||"hidden";Viewport.ContentContainer.setUrl(top.TYPO3.settings.RecordCommit.moduleUrl+"&data["+t+"]["+e+"]["+o+"]=0&redirect="+ContextMenuActions.getReturnUrl())}static showInMenus(t,e){Viewport.ContentContainer.setUrl(top.TYPO3.settings.RecordCommit.moduleUrl+"&data["+t+"]["+e+"][nav_hide]=0&redirect="+ContextMenuActions.getReturnUrl())}static hideInMenus(t,e){Viewport.ContentContainer.setUrl(top.TYPO3.settings.RecordCommit.moduleUrl+"&data["+t+"]["+e+"][nav_hide]=1&redirect="+ContextMenuActions.getReturnUrl())}static deleteRecord(t,e,n){const o=Modal.confirm(n.title,n.message,SeverityEnum.warning,[{text:n.buttonCloseText||TYPO3.lang["button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:n.buttonOkText||TYPO3.lang["button.delete"]||"Delete",btnClass:"btn-warning",name:"delete"}]);o.addEventListener("button.clicked",(n=>{if("delete"===n.target.getAttribute("name")){const n={component:"contextmenu",action:"delete",table:t,uid:e};AjaxDataHandler.process("cmd["+t+"]["+e+"][delete]=1",n).then((()=>{"pages"===t?(ModuleStateStorage.current("web").identifier===e.toString()&&top.document.dispatchEvent(new CustomEvent("typo3:pagetree:selectFirstNode")),ContextMenuActions.refreshPageTree()):"tt_content"===t&&Viewport.ContentContainer.refresh()}))}o.hideModal()}))}static copy(t,e){const n=TYPO3.settings.ajaxUrls.contextmenu_clipboard+"&CB[el]["+t+"%7C"+e+"]=1&CB[setCopyMode]=1";new AjaxRequest(n).get().finally((()=>{ContextMenuActions.triggerRefresh(Viewport.ContentContainer.get().location.href)}))}static clipboardRelease(t,e){const n=TYPO3.settings.ajaxUrls.contextmenu_clipboard+"&CB[el]["+t+"%7C"+e+"]=0";new AjaxRequest(n).get().finally((()=>{ContextMenuActions.triggerRefresh(Viewport.ContentContainer.get().location.href)}))}static cut(t,e){const n=TYPO3.settings.ajaxUrls.contextmenu_clipboard+"&CB[el]["+t+"%7C"+e+"]=1&CB[setCopyMode]=0";new AjaxRequest(n).get().finally((()=>{ContextMenuActions.triggerRefresh(Viewport.ContentContainer.get().location.href)}))}static triggerRefresh(t){t.includes("record%2Fedit")||Viewport.ContentContainer.refresh()}static clearCache(t,e){new AjaxRequest(TYPO3.settings.ajaxUrls.web_list_clearpagecache).withQueryArguments({id:e}).get({cache:"no-cache"}).then((async t=>{const e=await t.resolve();!0===e.success?Notification.success(e.title,e.message,1):Notification.error(e.title,e.message,1)}),(()=>{Notification.error("Clearing page caches went wrong on the server side.")}))}static pasteAfter(t,e,n){ContextMenuActions.pasteInto(t,-e,n)}static pasteInto(t,e,n){const o=()=>{const n="&CB[paste]="+t+"%7C"+e+"&CB[pad]=normal&redirect="+ContextMenuActions.getReturnUrl();Viewport.ContentContainer.setUrl(top.TYPO3.settings.RecordCommit.moduleUrl+n)};if(!n.title)return void o();const r=Modal.confirm(n.title,n.message,SeverityEnum.warning,[{text:n.buttonCloseText||TYPO3.lang["button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:n.buttonOkText||TYPO3.lang["button.ok"]||"OK",btnClass:"btn-warning",name:"ok"}]);r.addEventListener("button.clicked",(t=>{"ok"===t.target.getAttribute("name")&&o(),r.hideModal()}))}static refreshPageTree(){top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))}}export default ContextMenuActions; \ No newline at end of file +import{SeverityEnum}from"@typo3/backend/enum/severity.js";import AjaxDataHandler from"@typo3/backend/ajax-data-handler.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoWindow from"@typo3/backend/info-window.js";import Modal from"@typo3/backend/modal.js";import ModuleMenu from"@typo3/backend/module-menu.js";import Notification from"@typo3/backend/notification.js";import Viewport from"@typo3/backend/viewport.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";import"@typo3/backend/new-content-element-wizard.js";class ContextMenuActions{static getReturnUrl(){return encodeURIComponent(top.list_frame.document.location.pathname+top.list_frame.document.location.search)}static editRecord(t,e,n){const o=n.pagesLanguageUid;let r="";o&&(r="&overrideVals[pages][sys_language_uid]="+o),Viewport.ContentContainer.setUrl(top.TYPO3.settings.FormEngine.moduleUrl+"&edit["+t+"]["+e+"]=edit"+r+"&returnUrl="+ContextMenuActions.getReturnUrl())}static viewRecord(t,e,n){const o=n.previewUrl;if(o){window.open(o,"newTYPO3frontendWindow").focus()}}static openInfoPopUp(t,e){InfoWindow.showItem(t,e)}static mountAsTreeRoot(t,e){if("pages"===t){const t=new CustomEvent("typo3:pagetree:mountPoint",{detail:{pageId:e}});top.document.dispatchEvent(t)}}static newPageWizard(t,e,n){const o=n.pagesNewWizardUrl;Viewport.ContentContainer.setUrl(o+"&returnUrl="+ContextMenuActions.getReturnUrl())}static newContentWizard(t,e,n){let o=n.newWizardUrl;o&&(o+="&returnUrl="+ContextMenuActions.getReturnUrl(),Modal.advanced({title:n.title,type:Modal.types.ajax,size:Modal.sizes.large,content:o,severity:SeverityEnum.notice}))}static newRecord(t,e){Viewport.ContentContainer.setUrl(top.TYPO3.settings.FormEngine.moduleUrl+"&edit["+t+"]["+("pages"!==t?"-":"")+e+"]=new&returnUrl="+ContextMenuActions.getReturnUrl())}static openHistoryPopUp(t,e){Viewport.ContentContainer.setUrl(top.TYPO3.settings.RecordHistory.moduleUrl+"&element="+t+":"+e+"&returnUrl="+ContextMenuActions.getReturnUrl())}static openListModule(t,e,n){const o="pages"===t?e:n.pageUid;ModuleMenu.App.showModule("web_list","id="+o)}static pagesSort(t,e,n){const o=n.pagesSortUrl;o&&Viewport.ContentContainer.setUrl(o)}static pagesNewMultiple(t,e,n){const o=n.pagesNewMultipleUrl;o&&Viewport.ContentContainer.setUrl(o)}static disableRecord(t,e,n){const o=n.disableField||"hidden";Viewport.ContentContainer.setUrl(top.TYPO3.settings.RecordCommit.moduleUrl+"&data["+t+"]["+e+"]["+o+"]=1&redirect="+ContextMenuActions.getReturnUrl())}static enableRecord(t,e,n){const o=n.disableField||"hidden";Viewport.ContentContainer.setUrl(top.TYPO3.settings.RecordCommit.moduleUrl+"&data["+t+"]["+e+"]["+o+"]=0&redirect="+ContextMenuActions.getReturnUrl())}static showInMenus(t,e){Viewport.ContentContainer.setUrl(top.TYPO3.settings.RecordCommit.moduleUrl+"&data["+t+"]["+e+"][nav_hide]=0&redirect="+ContextMenuActions.getReturnUrl())}static hideInMenus(t,e){Viewport.ContentContainer.setUrl(top.TYPO3.settings.RecordCommit.moduleUrl+"&data["+t+"]["+e+"][nav_hide]=1&redirect="+ContextMenuActions.getReturnUrl())}static deleteRecord(t,e,n){const o=Modal.confirm(n.title,n.message,SeverityEnum.warning,[{text:n.buttonCloseText||TYPO3.lang["button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:n.buttonOkText||TYPO3.lang["button.delete"]||"Delete",btnClass:"btn-warning",name:"delete"}]);o.addEventListener("button.clicked",(n=>{if("delete"===n.target.getAttribute("name")){const n={component:"contextmenu",action:"delete",table:t,uid:e};AjaxDataHandler.process("cmd["+t+"]["+e+"][delete]=1",n).then((()=>{"pages"===t?(ModuleStateStorage.current("web").identifier===e.toString()&&top.document.dispatchEvent(new CustomEvent("typo3:pagetree:selectFirstNode")),ContextMenuActions.refreshPageTree()):"tt_content"===t&&Viewport.ContentContainer.refresh()}))}o.hideModal()}))}static copy(t,e){const n=TYPO3.settings.ajaxUrls.contextmenu_clipboard+"&CB[el]["+t+"%7C"+e+"]=1&CB[setCopyMode]=1";new AjaxRequest(n).get().finally((()=>{ContextMenuActions.triggerRefresh(Viewport.ContentContainer.get().location.href)}))}static clipboardRelease(t,e){const n=TYPO3.settings.ajaxUrls.contextmenu_clipboard+"&CB[el]["+t+"%7C"+e+"]=0";new AjaxRequest(n).get().finally((()=>{ContextMenuActions.triggerRefresh(Viewport.ContentContainer.get().location.href)}))}static cut(t,e){const n=TYPO3.settings.ajaxUrls.contextmenu_clipboard+"&CB[el]["+t+"%7C"+e+"]=1&CB[setCopyMode]=0";new AjaxRequest(n).get().finally((()=>{ContextMenuActions.triggerRefresh(Viewport.ContentContainer.get().location.href)}))}static triggerRefresh(t){t.includes("record%2Fedit")||Viewport.ContentContainer.refresh()}static clearCache(t,e){new AjaxRequest(TYPO3.settings.ajaxUrls.web_list_clearpagecache).withQueryArguments({id:e}).get({cache:"no-cache"}).then((async t=>{const e=await t.resolve();!0===e.success?Notification.success(e.title,e.message,1):Notification.error(e.title,e.message,1)}),(()=>{Notification.error("Clearing page caches went wrong on the server side.")}))}static pasteAfter(t,e,n){ContextMenuActions.pasteInto(t,-e,n)}static pasteInto(t,e,n){const o=()=>{const n="&CB[paste]="+t+"%7C"+e+"&CB[pad]=normal&redirect="+ContextMenuActions.getReturnUrl();Viewport.ContentContainer.setUrl(top.TYPO3.settings.RecordCommit.moduleUrl+n)};if(!n.title)return void o();const r=Modal.confirm(n.title,n.message,SeverityEnum.warning,[{text:n.buttonCloseText||TYPO3.lang["button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:n.buttonOkText||TYPO3.lang["button.ok"]||"OK",btnClass:"btn-warning",name:"ok"}]);r.addEventListener("button.clicked",(t=>{"ok"===t.target.getAttribute("name")&&o(),r.hideModal()}))}static refreshPageTree(){top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))}}export default ContextMenuActions; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/context-menu.js b/typo3/sysext/backend/Resources/Public/JavaScript/context-menu.js index cdb4016d85e6..1d04b2abc013 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/context-menu.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/context-menu.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import ContextMenuActions from"@typo3/backend/context-menu-actions.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import RegularEvent from"@typo3/core/event/regular-event.js";import ThrottleEvent from"@typo3/core/event/throttle-event.js";class ContextMenu{constructor(){this.mousePos={X:null,Y:null},this.record={uid:null,table:null},this.eventSources=[],this.storeMousePositionEvent=e=>{this.mousePos={X:e.pageX,Y:e.pageY}},document.addEventListener("click",(e=>{this.handleTriggerEvent(e)})),document.addEventListener("contextmenu",(e=>{this.handleTriggerEvent(e)})),new ThrottleEvent("mousemove",this.storeMousePositionEvent.bind(this),50).bindTo(document)}static drawActionItem(e){const t=e.additionalAttributes||{};let n="";for(const e of Object.entries(t)){const[t,o]=e;n+=" "+t+'="'+o+'"'}return'<li role="menuitem" class="context-menu-item" tabindex="-1" data-callback-action="'+e.callbackAction+'"'+n+'><span class="context-menu-item-icon">'+e.icon+'</span> <span class="context-menu-item-label">'+e.label+"</span></li>"}static within(e,t,n){const o=e.getBoundingClientRect(),s=window.pageXOffset||document.documentElement.scrollLeft,i=window.pageYOffset||document.documentElement.scrollTop,c=t>=o.left+s&&t<=o.left+s+o.width,a=n>=o.top+i&&n<=o.top+i+o.height;return c&&a}show(e,t,n,o,s,i=null){this.hideAll(),this.record={table:e,uid:t};const c=i.matches("a, button, [tabindex]")?i:i.closest("a, button, [tabindex]");this.eventSources.push(c);let a="";void 0!==e&&(a+="table="+encodeURIComponent(e)),void 0!==t&&(a+=(a.length>0?"&":"")+"uid="+("number"==typeof t?t:encodeURIComponent(t))),void 0!==n&&(a+=(a.length>0?"&":"")+"context="+encodeURIComponent(n)),this.fetch(a)}initializeContextMenuContainer(){if(0===$("#contentMenu0").length){const e='<div id="contentMenu0" class="context-menu" style="display: none;"></div><div id="contentMenu1" class="context-menu" data-parent="#contentMenu0" style="display: none;"></div>';$("body").append(e),document.querySelectorAll(".context-menu").forEach((e=>{new RegularEvent("mouseenter",(e=>{e.target;this.storeMousePositionEvent(e)})).bindTo(e),new DebounceEvent("mouseleave",(e=>{const t=e.target,n=document.querySelector('[data-parent="#'+t.id+'"]');if(!ContextMenu.within(t,this.mousePos.X,this.mousePos.Y)&&(null===n||null===n.offsetParent)){let e;this.hide("#"+t.id),void 0!==t.dataset.parent&&null!==(e=document.querySelector(t.dataset.parent))&&(ContextMenu.within(e,this.mousePos.X,this.mousePos.Y)||this.hide(t.dataset.parent))}}),500).bindTo(e)}))}}handleTriggerEvent(e){if(!(e.target instanceof Element))return;const t=e.target.closest("[data-contextmenu-trigger]");if(t instanceof HTMLElement)return void this.handleContextMenuEvent(e,t);const n=e.target.closest(".t3js-contextmenutrigger");if(n instanceof HTMLElement)return console.warn('Using the contextmenu trigger .t3js-contextmenutrigger is deprecated. Please use [data-contextmenu-trigger="click"] instead and prefix your config with "data-contextmenu-".'),void this.handleLegacyContextMenuEvent(e,n);e.target.closest(".context-menu")||this.hideAll()}handleContextMenuEvent(e,t){const n=t.dataset.contextmenuTrigger;"click"!==n&&n!==e.type||(e.preventDefault(),this.show(t.dataset.contextmenuTable??"",t.dataset.contextmenuUid??"",t.dataset.contextmenuContext??"","","",t))}handleLegacyContextMenuEvent(e,t){t.getAttribute("onclick")&&"click"===e.type||(e.preventDefault(),this.show(t.dataset.table??"",t.dataset.uid??"",t.dataset.context??"","","",t))}fetch(e){const t=TYPO3.settings.ajaxUrls.contextmenu;new AjaxRequest(t).withQueryArguments(e).get().then((async e=>{const t=await e.resolve();void 0!==e&&Object.keys(e).length>0&&this.populateData(t,0)}))}populateData(e,t){this.initializeContextMenuContainer();const n=$("#contentMenu"+t);if(n.length&&(0===t||$("#contentMenu"+(t-1)).is(":visible"))){const o=this.drawMenu(e,t);n.html('<ul class="context-menu-group" role="menu">'+o+"</ul>"),$("li.context-menu-item",n).on("click",(e=>{e.preventDefault();const n=e.currentTarget;if(n.classList.contains("context-menu-item-submenu"))return void this.openSubmenu(t,$(n),!1);const{callbackAction:o,callbackModule:s,...i}=n.dataset,c=new Proxy($(n),{get(e,t,n){console.warn(`\`this\` being bound to the selected context menu item is marked as deprecated. To access data attributes, use the 3rd argument passed to callback \`${o}\` in \`${s}\`.`);const i=e[t];return i instanceof Function?function(...t){return i.apply(this===n?e:this,t)}:i}});n.dataset.callbackModule?import(s+".js").then((({default:e})=>{e[o].bind(c)(this.record.table,this.record.uid,i)})):ContextMenuActions&&"function"==typeof ContextMenuActions[o]?ContextMenuActions[o].bind(c)(this.record.table,this.record.uid,i):console.log("action: "+o+" not found"),this.hideAll()})),$("li.context-menu-item",n).on("keydown",(e=>{const n=$(e.currentTarget);switch(e.key){case"Down":case"ArrowDown":this.setFocusToNextItem(n.get(0));break;case"Up":case"ArrowUp":this.setFocusToPreviousItem(n.get(0));break;case"Right":case"ArrowRight":if(!n.hasClass("context-menu-item-submenu"))return;this.openSubmenu(t,n,!0);break;case"Home":this.setFocusToFirstItem(n.get(0));break;case"End":this.setFocusToLastItem(n.get(0));break;case"Enter":case"Space":n.click();break;case"Esc":case"Escape":case"Left":case"ArrowLeft":this.hide("#"+n.parents(".context-menu").first().attr("id"));break;case"Tab":this.hideAll();break;default:return}e.preventDefault()})),n.css(this.getPosition(n,!1)).show(),$("li.context-menu-item[tabindex=-1]",n).first().focus()}}setFocusToPreviousItem(e){let t=this.getItemBackward(e.previousElementSibling);t||(t=this.getLastItem(e)),t.focus()}setFocusToNextItem(e){let t=this.getItemForward(e.nextElementSibling);t||(t=this.getFirstItem(e)),t.focus()}setFocusToFirstItem(e){let t=this.getFirstItem(e);t&&t.focus()}setFocusToLastItem(e){let t=this.getLastItem(e);t&&t.focus()}getItemBackward(e){for(;e&&(!e.classList.contains("context-menu-item")||"-1"!==e.getAttribute("tabindex"));)e=e.previousElementSibling;return e}getItemForward(e){for(;e&&(!e.classList.contains("context-menu-item")||"-1"!==e.getAttribute("tabindex"));)e=e.nextElementSibling;return e}getFirstItem(e){return this.getItemForward(e.parentElement.firstElementChild)}getLastItem(e){return this.getItemBackward(e.parentElement.lastElementChild)}openSubmenu(e,t,n){this.eventSources.push(t[0]);const o=$("#contentMenu"+(e+1)).html("");t.next().find(".context-menu-group").clone(!0).appendTo(o),o.css(this.getPosition(o,n)).show(),$(".context-menu-item[tabindex=-1]",o).first().focus()}getPosition(e,t){let n=0,o=0;if(this.eventSources.length&&(null===this.mousePos.X||t)){const e=this.eventSources[this.eventSources.length-1].getBoundingClientRect();n=this.eventSources.length>1?e.right:e.x,o=e.y}else n=this.mousePos.X-1,o=this.mousePos.Y-1;const s=$(window).width()-20,i=$(window).height(),c=e.width(),a=e.height(),r=n-$(document).scrollLeft(),u=o-$(document).scrollTop();return i-a<u&&(u>a?o-=a-10:o+=i-a-u),s-c<r&&(r>c?n-=c-10:s-c-r<$(document).scrollLeft()?n=$(document).scrollLeft():n+=s-c-r),{left:n+"px",top:o+"px"}}drawMenu(e,t){let n="";for(const o of Object.values(e))if("item"===o.type)n+=ContextMenu.drawActionItem(o);else if("divider"===o.type)n+='<li role="separator" class="context-menu-item context-menu-item-divider"></li>';else if("submenu"===o.type||o.childItems){n+='<li role="menuitem" aria-haspopup="true" class="context-menu-item context-menu-item-submenu" tabindex="-1"><span class="context-menu-item-icon">'+o.icon+'</span><span class="context-menu-item-label">'+o.label+'</span><span class="context-menu-item-indicator"><typo3-backend-icon identifier="actions-chevron-right" size="small"></typo3-backend-icon></span></li>';n+='<div class="context-menu contentMenu'+(t+1)+'" style="display:none;"><ul role="menu" class="context-menu-group">'+this.drawMenu(o.childItems,1)+"</ul></div>"}return n}hide(e){$(e).hide();const t=this.eventSources.pop();t&&$(t).focus()}hideAll(){this.hide("#contentMenu0"),this.hide("#contentMenu1")}}export default new ContextMenu; \ No newline at end of file +import $ from"jquery";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import ContextMenuActions from"@typo3/backend/context-menu-actions.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import RegularEvent from"@typo3/core/event/regular-event.js";import ThrottleEvent from"@typo3/core/event/throttle-event.js";class ContextMenu{constructor(){this.mousePos={X:null,Y:null},this.record={uid:null,table:null},this.eventSources=[],this.storeMousePositionEvent=e=>{this.mousePos={X:e.pageX,Y:e.pageY}},document.addEventListener("click",(e=>{this.handleTriggerEvent(e)})),document.addEventListener("contextmenu",(e=>{this.handleTriggerEvent(e)})),new ThrottleEvent("mousemove",this.storeMousePositionEvent.bind(this),50).bindTo(document)}static drawActionItem(e){const t=e.additionalAttributes||{};let n="";for(const e of Object.entries(t)){const[t,o]=e;n+=" "+t+'="'+o+'"'}return'<li role="menuitem" class="context-menu-item" tabindex="-1" data-callback-action="'+e.callbackAction+'"'+n+'><span class="context-menu-item-icon">'+e.icon+'</span> <span class="context-menu-item-label">'+e.label+"</span></li>"}static within(e,t,n){const o=e.getBoundingClientRect(),s=window.pageXOffset||document.documentElement.scrollLeft,i=window.pageYOffset||document.documentElement.scrollTop,c=t>=o.left+s&&t<=o.left+s+o.width,a=n>=o.top+i&&n<=o.top+i+o.height;return c&&a}show(e,t,n,o,s,i=null){this.hideAll(),this.record={table:e,uid:t};const c=i.matches("a, button, [tabindex]")?i:i.closest("a, button, [tabindex]");this.eventSources.push(c);let a="";void 0!==e&&(a+="table="+encodeURIComponent(e)),void 0!==t&&(a+=(a.length>0?"&":"")+"uid="+("number"==typeof t?t:encodeURIComponent(t))),void 0!==n&&(a+=(a.length>0?"&":"")+"context="+encodeURIComponent(n)),this.fetch(a)}initializeContextMenuContainer(){if(0===$("#contentMenu0").length){const e='<div id="contentMenu0" class="context-menu" style="display: none;"></div><div id="contentMenu1" class="context-menu" data-parent="#contentMenu0" style="display: none;"></div>';$("body").append(e),document.querySelectorAll(".context-menu").forEach((e=>{new RegularEvent("mouseenter",(e=>{this.storeMousePositionEvent(e)})).bindTo(e),new DebounceEvent("mouseleave",(e=>{const t=e.target,n=document.querySelector('[data-parent="#'+t.id+'"]');if(!ContextMenu.within(t,this.mousePos.X,this.mousePos.Y)&&(null===n||null===n.offsetParent)){let e;this.hide("#"+t.id),void 0!==t.dataset.parent&&null!==(e=document.querySelector(t.dataset.parent))&&(ContextMenu.within(e,this.mousePos.X,this.mousePos.Y)||this.hide(t.dataset.parent))}}),500).bindTo(e)}))}}handleTriggerEvent(e){if(!(e.target instanceof Element))return;const t=e.target.closest("[data-contextmenu-trigger]");if(t instanceof HTMLElement)return void this.handleContextMenuEvent(e,t);const n=e.target.closest(".t3js-contextmenutrigger");if(n instanceof HTMLElement)return console.warn('Using the contextmenu trigger .t3js-contextmenutrigger is deprecated. Please use [data-contextmenu-trigger="click"] instead and prefix your config with "data-contextmenu-".'),void this.handleLegacyContextMenuEvent(e,n);e.target.closest(".context-menu")||this.hideAll()}handleContextMenuEvent(e,t){const n=t.dataset.contextmenuTrigger;"click"!==n&&n!==e.type||(e.preventDefault(),this.show(t.dataset.contextmenuTable??"",t.dataset.contextmenuUid??"",t.dataset.contextmenuContext??"","","",t))}handleLegacyContextMenuEvent(e,t){t.getAttribute("onclick")&&"click"===e.type||(e.preventDefault(),this.show(t.dataset.table??"",t.dataset.uid??"",t.dataset.context??"","","",t))}fetch(e){const t=TYPO3.settings.ajaxUrls.contextmenu;new AjaxRequest(t).withQueryArguments(e).get().then((async e=>{const t=await e.resolve();void 0!==e&&Object.keys(e).length>0&&this.populateData(t,0)}))}populateData(e,t){this.initializeContextMenuContainer();const n=$("#contentMenu"+t);if(n.length&&(0===t||$("#contentMenu"+(t-1)).is(":visible"))){const o=this.drawMenu(e,t);n.html('<ul class="context-menu-group" role="menu">'+o+"</ul>"),$("li.context-menu-item",n).on("click",(e=>{e.preventDefault();const n=e.currentTarget;if(n.classList.contains("context-menu-item-submenu"))return void this.openSubmenu(t,$(n),!1);const{callbackAction:o,callbackModule:s,...i}=n.dataset,c=new Proxy($(n),{get(e,t,n){console.warn(`\`this\` being bound to the selected context menu item is marked as deprecated. To access data attributes, use the 3rd argument passed to callback \`${o}\` in \`${s}\`.`);const i=e[t];return i instanceof Function?function(...t){return i.apply(this===n?e:this,t)}:i}});n.dataset.callbackModule?import(s+".js").then((({default:e})=>{e[o].bind(c)(this.record.table,this.record.uid,i)})):ContextMenuActions&&"function"==typeof ContextMenuActions[o]?ContextMenuActions[o].bind(c)(this.record.table,this.record.uid,i):console.error("action: "+o+" not found"),this.hideAll()})),$("li.context-menu-item",n).on("keydown",(e=>{const n=$(e.currentTarget);switch(e.key){case"Down":case"ArrowDown":this.setFocusToNextItem(n.get(0));break;case"Up":case"ArrowUp":this.setFocusToPreviousItem(n.get(0));break;case"Right":case"ArrowRight":if(!n.hasClass("context-menu-item-submenu"))return;this.openSubmenu(t,n,!0);break;case"Home":this.setFocusToFirstItem(n.get(0));break;case"End":this.setFocusToLastItem(n.get(0));break;case"Enter":case"Space":n.click();break;case"Esc":case"Escape":case"Left":case"ArrowLeft":this.hide("#"+n.parents(".context-menu").first().attr("id"));break;case"Tab":this.hideAll();break;default:return}e.preventDefault()})),n.css(this.getPosition(n,!1)).show(),$("li.context-menu-item[tabindex=-1]",n).first().focus()}}setFocusToPreviousItem(e){let t=this.getItemBackward(e.previousElementSibling);t||(t=this.getLastItem(e)),t.focus()}setFocusToNextItem(e){let t=this.getItemForward(e.nextElementSibling);t||(t=this.getFirstItem(e)),t.focus()}setFocusToFirstItem(e){const t=this.getFirstItem(e);t&&t.focus()}setFocusToLastItem(e){const t=this.getLastItem(e);t&&t.focus()}getItemBackward(e){for(;e&&(!e.classList.contains("context-menu-item")||"-1"!==e.getAttribute("tabindex"));)e=e.previousElementSibling;return e}getItemForward(e){for(;e&&(!e.classList.contains("context-menu-item")||"-1"!==e.getAttribute("tabindex"));)e=e.nextElementSibling;return e}getFirstItem(e){return this.getItemForward(e.parentElement.firstElementChild)}getLastItem(e){return this.getItemBackward(e.parentElement.lastElementChild)}openSubmenu(e,t,n){this.eventSources.push(t[0]);const o=$("#contentMenu"+(e+1)).html("");t.next().find(".context-menu-group").clone(!0).appendTo(o),o.css(this.getPosition(o,n)).show(),$(".context-menu-item[tabindex=-1]",o).first().focus()}getPosition(e,t){let n=0,o=0;if(this.eventSources.length&&(null===this.mousePos.X||t)){const e=this.eventSources[this.eventSources.length-1].getBoundingClientRect();n=this.eventSources.length>1?e.right:e.x,o=e.y}else n=this.mousePos.X-1,o=this.mousePos.Y-1;const s=$(window).width()-20,i=$(window).height(),c=e.width(),a=e.height(),r=n-$(document).scrollLeft(),u=o-$(document).scrollTop();return i-a<u&&(u>a?o-=a-10:o+=i-a-u),s-c<r&&(r>c?n-=c-10:s-c-r<$(document).scrollLeft()?n=$(document).scrollLeft():n+=s-c-r),{left:n+"px",top:o+"px"}}drawMenu(e,t){let n="";for(const o of Object.values(e))if("item"===o.type)n+=ContextMenu.drawActionItem(o);else if("divider"===o.type)n+='<li role="separator" class="context-menu-item context-menu-item-divider"></li>';else if("submenu"===o.type||o.childItems){n+='<li role="menuitem" aria-haspopup="true" class="context-menu-item context-menu-item-submenu" tabindex="-1"><span class="context-menu-item-icon">'+o.icon+'</span><span class="context-menu-item-label">'+o.label+'</span><span class="context-menu-item-indicator"><typo3-backend-icon identifier="actions-chevron-right" size="small"></typo3-backend-icon></span></li>';n+='<div class="context-menu contentMenu'+(t+1)+'" style="display:none;"><ul role="menu" class="context-menu-group">'+this.drawMenu(o.childItems,1)+"</ul></div>"}return n}hide(e){$(e).hide();const t=this.eventSources.pop();t&&$(t).focus()}hideAll(){this.hide("#contentMenu0"),this.hide("#contentMenu1")}}export default new ContextMenu; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/copy-to-clipboard.js b/typo3/sysext/backend/Resources/Public/JavaScript/copy-to-clipboard.js index c80cf0ade655..4ce359e534aa 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/copy-to-clipboard.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/copy-to-clipboard.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -var __decorate=function(t,o,e,r){var i,c=arguments.length,l=c<3?o:null===r?r=Object.getOwnPropertyDescriptor(o,e):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)l=Reflect.decorate(t,o,e,r);else for(var a=t.length-1;a>=0;a--)(i=t[a])&&(l=(c<3?i(l):c>3?i(o,e,l):i(o,e))||l);return c>3&&l&&Object.defineProperty(o,e,l),l};import{html,css,LitElement}from"lit";import{customElement,property}from"lit/decorators.js";import Notification from"@typo3/backend/notification.js";import{lll}from"@typo3/core/lit-helper.js";let CopyToClipboard=class extends LitElement{constructor(){super(),this.addEventListener("click",(t=>{t.preventDefault(),this.copyToClipboard()})),this.addEventListener("keydown",(t=>{"Enter"!==t.key&&" "!==t.key||(t.preventDefault(),this.copyToClipboard())}))}connectedCallback(){this.hasAttribute("role")||this.setAttribute("role","button"),this.hasAttribute("tabindex")||this.setAttribute("tabindex","0")}render(){return html`<slot></slot>`}copyToClipboard(){if("string"!=typeof this.text||!this.text.length)return console.warn("No text for copy to clipboard given."),void Notification.error(lll("copyToClipboard.error"));if(navigator.clipboard)navigator.clipboard.writeText(this.text).then((()=>{Notification.success(lll("copyToClipboard.success"),"",1)})).catch((()=>{Notification.error(lll("copyToClipboard.error"))}));else{const t=document.createElement("textarea");t.value=this.text,document.body.appendChild(t),t.focus(),t.select();try{document.execCommand("copy")?Notification.success(lll("copyToClipboard.success"),"",1):Notification.error(lll("copyToClipboard.error"))}catch(t){Notification.error(lll("copyToClipboard.error"))}document.body.removeChild(t)}}};CopyToClipboard.styles=[css`:host { cursor: pointer; appearance: button; }`],__decorate([property({type:String})],CopyToClipboard.prototype,"text",void 0),CopyToClipboard=__decorate([customElement("typo3-copy-to-clipboard")],CopyToClipboard); \ No newline at end of file +var __decorate=function(t,o,e,r){var i,c=arguments.length,l=c<3?o:null===r?r=Object.getOwnPropertyDescriptor(o,e):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)l=Reflect.decorate(t,o,e,r);else for(var p=t.length-1;p>=0;p--)(i=t[p])&&(l=(c<3?i(l):c>3?i(o,e,l):i(o,e))||l);return c>3&&l&&Object.defineProperty(o,e,l),l};import{html,css,LitElement}from"lit";import{customElement,property}from"lit/decorators.js";import Notification from"@typo3/backend/notification.js";import{lll}from"@typo3/core/lit-helper.js";let CopyToClipboard=class extends LitElement{constructor(){super(),this.addEventListener("click",(t=>{t.preventDefault(),this.copyToClipboard()})),this.addEventListener("keydown",(t=>{"Enter"!==t.key&&" "!==t.key||(t.preventDefault(),this.copyToClipboard())}))}connectedCallback(){this.hasAttribute("role")||this.setAttribute("role","button"),this.hasAttribute("tabindex")||this.setAttribute("tabindex","0")}render(){return html`<slot></slot>`}copyToClipboard(){if("string"!=typeof this.text||!this.text.length)return console.warn("No text for copy to clipboard given."),void Notification.error(lll("copyToClipboard.error"));if(navigator.clipboard)navigator.clipboard.writeText(this.text).then((()=>{Notification.success(lll("copyToClipboard.success"),"",1)})).catch((()=>{Notification.error(lll("copyToClipboard.error"))}));else{const t=document.createElement("textarea");t.value=this.text,document.body.appendChild(t),t.focus(),t.select();try{document.execCommand("copy")?Notification.success(lll("copyToClipboard.success"),"",1):Notification.error(lll("copyToClipboard.error"))}catch(t){Notification.error(lll("copyToClipboard.error"))}document.body.removeChild(t)}}};CopyToClipboard.styles=[css`:host { cursor: pointer; appearance: button; }`],__decorate([property({type:String})],CopyToClipboard.prototype,"text",void 0),CopyToClipboard=__decorate([customElement("typo3-copy-to-clipboard")],CopyToClipboard);export{CopyToClipboard}; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/document-save-actions.js b/typo3/sysext/backend/Resources/Public/JavaScript/document-save-actions.js index 9cfbdf490dac..df4a43913d46 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/document-save-actions.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/document-save-actions.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import Icons from"@typo3/backend/icons.js";class DocumentSaveActions{constructor(){this.preSubmitCallbacks=[],DocumentService.ready().then((()=>{this.initializeSaveHandling()}))}static getInstance(){return null===DocumentSaveActions.instance&&(DocumentSaveActions.instance=new DocumentSaveActions),DocumentSaveActions.instance}addPreSubmitCallback(t){if("function"!=typeof t)throw"callback must be a function.";this.preSubmitCallbacks.push(t)}initializeSaveHandling(){let t=!1;const e=["button[form]",'button[name^="_save"]','a[data-name^="_save"]','button[name="CMD"][value^="save"]','a[data-name="CMD"][data-value^="save"]'].join(",");$(".t3js-module-docheader").on("click",e,(e=>{if(!t){t=!0;const n=$(e.currentTarget),a=n.attr("form")||n.attr("data-form")||null,r=a?$("#"+a):n.closest("form"),i=n.data("name")||e.currentTarget.getAttribute("name"),o=n.data("value")||e.currentTarget.getAttribute("value"),c=$("<input />").attr("type","hidden").attr("name",i).attr("value",o);for(let n of this.preSubmitCallbacks)if(n(e),e.isPropagationStopped())return t=!1,!1;r.append(c),r.on("submit",(()=>{if(r.find(".has-error").length>0)return t=!1,!1;let e;const a=n.closest(".t3js-splitbutton");return a.length>0?(a.find("button").prop("disabled",!0),e=a.children().first()):(n.prop("disabled",!0),e=n),Icons.getIcon("spinner-circle-dark",Icons.sizes.small).then((t=>{e.find(".t3js-icon").replaceWith(t)})).catch((t=>{})),!0})),"A"!==e.currentTarget.tagName&&!n.attr("form")||e.isDefaultPrevented()||(r.find('[name="doSave"]').val("1"),r.trigger("submit"),e.preventDefault())}return!0}))}}DocumentSaveActions.instance=null;export default DocumentSaveActions; \ No newline at end of file +import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import Icons from"@typo3/backend/icons.js";class DocumentSaveActions{constructor(){this.preSubmitCallbacks=[],DocumentService.ready().then((()=>{this.initializeSaveHandling()}))}static getInstance(){return null===DocumentSaveActions.instance&&(DocumentSaveActions.instance=new DocumentSaveActions),DocumentSaveActions.instance}addPreSubmitCallback(t){if("function"!=typeof t)throw"callback must be a function.";this.preSubmitCallbacks.push(t)}initializeSaveHandling(){let t=!1;const e=["button[form]",'button[name^="_save"]','a[data-name^="_save"]','button[name="CMD"][value^="save"]','a[data-name="CMD"][data-value^="save"]'].join(",");$(".t3js-module-docheader").on("click",e,(e=>{if(!t){t=!0;const n=$(e.currentTarget),a=n.attr("form")||n.attr("data-form")||null,r=a?$("#"+a):n.closest("form"),i=n.data("name")||e.currentTarget.getAttribute("name"),o=n.data("value")||e.currentTarget.getAttribute("value"),c=$("<input />").attr("type","hidden").attr("name",i).attr("value",o);for(const n of this.preSubmitCallbacks)if(n(e),e.isPropagationStopped())return t=!1,!1;r.append(c),r.on("submit",(()=>{if(r.find(".has-error").length>0)return t=!1,!1;let e;const a=n.closest(".t3js-splitbutton");return a.length>0?(a.find("button").prop("disabled",!0),e=a.children().first()):(n.prop("disabled",!0),e=n),Icons.getIcon("spinner-circle-dark",Icons.sizes.small).then((t=>{e.find(".t3js-icon").replaceWith(t)})).catch((()=>{})),!0})),"A"!==e.currentTarget.tagName&&!n.attr("form")||e.isDefaultPrevented()||(r.find('[name="doSave"]').val("1"),r.trigger("submit"),e.preventDefault())}return!0}))}}DocumentSaveActions.instance=null;export default DocumentSaveActions; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/drag-uploader.js b/typo3/sysext/backend/Resources/Public/JavaScript/drag-uploader.js index 8fda8e4ee662..40a274a77ae7 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/drag-uploader.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/drag-uploader.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import{DateTime}from"luxon";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import{MessageUtility}from"@typo3/backend/utility/message-utility.js";import NProgress from"nprogress";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{default as Modal,Sizes as ModalSizes}from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import ImmediateAction from"@typo3/backend/action-button/immediate-action.js";import Md5 from"@typo3/backend/hashing/md5.js";import"@typo3/backend/element/icon-element.js";var Action;!function(e){e.OVERRIDE="replace",e.RENAME="rename",e.SKIP="cancel",e.USE_EXISTING="useExisting"}(Action||(Action={}));class DragUploaderPlugin{constructor(e){this.askForOverride=[],this.percentagePerFile=1,this.dragStartedInDocument=!1,this.hideDropzone=e=>{e.stopPropagation(),e.preventDefault(),this.$dropzone.hide(),this.$dropzone.removeClass("drop-status-ok"),this.manuallyTriggered=!1},this.dragFileIntoDocument=e=>(this.dragStartedInDocument||(e.stopPropagation(),e.preventDefault(),$(e.currentTarget).addClass("drop-in-progress"),this.$element.get(0)?.offsetParent&&this.showDropzone()),!1),this.dragAborted=e=>(e.stopPropagation(),e.preventDefault(),$(e.currentTarget).removeClass("drop-in-progress"),this.dragStartedInDocument=!1,!1),this.ignoreDrop=e=>(e.stopPropagation(),e.preventDefault(),this.dragAborted(e),!1),this.handleDrop=e=>{this.ignoreDrop(e),this.hideDropzone(e),this.processFiles(e.originalEvent.dataTransfer.files)},this.fileInDropzone=()=>{this.$dropzone.addClass("drop-status-ok")},this.fileOutOfDropzone=()=>{this.$dropzone.removeClass("drop-status-ok"),this.manuallyTriggered||this.$dropzone.hide()},this.$body=$("body"),this.$element=$(e);const t=void 0!==this.$element.data("dropzoneTrigger");this.$trigger=$(this.$element.data("dropzoneTrigger")),this.defaultAction=this.$element.data("defaultAction")||Action.SKIP,this.$dropzone=$("<div />").addClass("dropzone").hide(),this.irreObjectUid=this.$element.data("fileIrreObject");const i=this.$element.data("dropzoneTarget");if(this.irreObjectUid&&0!==this.$element.nextAll(i).length?(this.dropZoneInsertBefore=!0,this.$dropzone.insertBefore(i)):(this.dropZoneInsertBefore=!1,this.$dropzone.insertAfter(i)),this.$dropzoneMask=$("<div />").addClass("dropzone-mask").appendTo(this.$dropzone),this.fileInput=document.createElement("input"),this.fileInput.setAttribute("type","file"),this.fileInput.setAttribute("multiple","multiple"),this.fileInput.setAttribute("name","files[]"),this.fileInput.classList.add("upload-file-picker"),this.$body.append(this.fileInput),this.$fileList=$(this.$element.data("progress-container")),this.fileListColumnCount=$("thead tr:first th",this.$fileList).length+1,this.filesExtensionsAllowed=this.$element.data("file-allowed"),this.fileDenyPattern=this.$element.data("file-deny-pattern")?new RegExp(this.$element.data("file-deny-pattern"),"i"):null,this.maxFileSize=parseInt(this.$element.data("max-file-size"),10),this.target=this.$element.data("target-folder"),this.reloadUrl=this.$element.data("reload-url"),this.browserCapabilities={fileReader:"undefined"!=typeof FileReader,DnD:"draggable"in document.createElement("span"),Progress:"upload"in new XMLHttpRequest},this.browserCapabilities.DnD){if(this.$body.on("dragstart",(e=>{this.dragStartedInDocument=!0})),this.$body.on("dragover",this.dragFileIntoDocument),this.$body.on("dragend",this.dragAborted),this.$body.on("drop",this.ignoreDrop),this.$dropzone.on("dragenter",this.fileInDropzone),this.$dropzoneMask.on("dragenter",this.fileInDropzone),this.$dropzoneMask.on("dragleave",this.fileOutOfDropzone),this.$dropzoneMask.on("drop",(e=>this.handleDrop(e))),this.$dropzone.prepend('<button type="button" class="dropzone-hint" aria-labelledby="dropzone-title"><div class="dropzone-hint-media"><div class="dropzone-hint-icon"></div></div><div class="dropzone-hint-body"><h3 id="dropzone-title" class="dropzone-hint-title">'+TYPO3.lang["file_upload.dropzonehint.title"]+'</h3><p class="dropzone-hint-message">'+TYPO3.lang["file_upload.dropzonehint.message"]+"</p></div></div>").on("click",(()=>{this.fileInput.click()})),$('<button type="button" />').addClass("dropzone-close").attr("aria-label",TYPO3.lang["file_upload.dropzone.close"]).on("click",this.hideDropzone).appendTo(this.$dropzone),0===this.$fileList.length){this.$fileList=$("<table />").attr("id","typo3-filelist").addClass("table table-striped table-hover upload-queue").html("<tbody></tbody>");let e=$("<div/>",{class:"table-fit"}).hide().append(this.$fileList);this.dropZoneInsertBefore?e.insertAfter(this.$dropzone):e.insertBefore(this.$dropzone),this.fileListColumnCount=8,this.manualTable=!0}this.fileInput.addEventListener("change",(e=>{this.hideDropzone(e),this.processFiles(Array.apply(null,this.fileInput.files))})),document.addEventListener("keydown",(e=>{"Escape"===e.code&&this.$dropzone.is(":visible")&&this.hideDropzone(e)})),this.bindUploadButton(!0===t?this.$trigger:this.$element)}else console.warn("Browser has no Drag and drop capabilities; cannot initialize DragUploader")}showDropzone(){this.$dropzone.show()}processFiles(e){this.queueLength=e.length,this.$fileList.parent().is(":visible")||(this.$fileList.parent().show(),this.$fileList.closest(".t3-filelist-table-container")?.removeClass("hidden"),this.$fileList.closest("form")?.find(".t3-filelist-info-container")?.hide()),NProgress.start(),this.percentagePerFile=1/e.length;const t=[];Array.from(e).forEach((e=>{const i=new AjaxRequest(TYPO3.settings.ajaxUrls.file_exists).withQueryArguments({fileName:e.name,fileTarget:this.target}).get({cache:"no-cache"}).then((async t=>{const i=await t.resolve();void 0!==i.uid?(this.askForOverride.push({original:i,uploaded:e,action:this.irreObjectUid?Action.USE_EXISTING:this.defaultAction}),NProgress.inc(this.percentagePerFile)):new FileQueueItem(this,e,Action.SKIP)}));t.push(i)})),Promise.all(t).then((()=>{this.drawOverrideModal(),NProgress.done()})),this.fileInput.value=""}bindUploadButton(e){e.on("click",(e=>{e.preventDefault(),this.fileInput.click(),this.showDropzone(),this.manuallyTriggered=!0}))}decrementQueueLength(e){if(this.queueLength>0&&(this.queueLength--,0===this.queueLength)){const t=e&&e.length?5e3:0;if(t)for(let t of e)Notification.showMessage(t.title,t.message,t.severity);this.reloadUrl&&setTimeout((()=>{Notification.info(TYPO3.lang["file_upload.reload.filelist"],TYPO3.lang["file_upload.reload.filelist.message"],10,[{label:TYPO3.lang["file_upload.reload.filelist.actions.dismiss"]},{label:TYPO3.lang["file_upload.reload.filelist.actions.reload"],action:new ImmediateAction((()=>{top.list_frame.document.location.href=this.reloadUrl}))}])}),t)}}drawOverrideModal(){const e=Object.keys(this.askForOverride).length;if(0===e)return;const t=$("<div/>").append($("<p/>").text(TYPO3.lang["file_upload.existingfiles.description"]),$("<table/>",{class:"table"}).append($("<thead/>").append($("<tr />").append($("<th/>"),$("<th/>").text(TYPO3.lang["file_upload.header.originalFile"]),$("<th/>").text(TYPO3.lang["file_upload.header.uploadedFile"]),$("<th/>").text(TYPO3.lang["file_upload.header.action"])))));for(let i=0;i<e;++i){const e=$("<tr />").append($("<td />").append(""!==this.askForOverride[i].original.thumbUrl?$("<img />",{src:this.askForOverride[i].original.thumbUrl,height:40}):$(this.askForOverride[i].original.icon)),$("<td />").html(this.askForOverride[i].original.name+" ("+DragUploader.fileSizeAsString(this.askForOverride[i].original.size)+")<br>"+DateTime.fromSeconds(this.askForOverride[i].original.mtime).toLocaleString(DateTime.DATETIME_MED)),$("<td />").html(this.askForOverride[i].uploaded.name+" ("+DragUploader.fileSizeAsString(this.askForOverride[i].uploaded.size)+")<br>"+DateTime.fromMillis(this.askForOverride[i].uploaded.lastModified).toLocaleString(DateTime.DATETIME_MED)),$("<td />").append($("<select />",{class:"form-select t3js-actions","data-override":i}).append(this.irreObjectUid?$("<option/>").val(Action.USE_EXISTING).text(TYPO3.lang["file_upload.actions.use_existing"]):"",$("<option />",{selected:this.defaultAction===Action.SKIP}).val(Action.SKIP).text(TYPO3.lang["file_upload.actions.skip"]),$("<option />",{selected:this.defaultAction===Action.RENAME}).val(Action.RENAME).text(TYPO3.lang["file_upload.actions.rename"]),$("<option />",{selected:this.defaultAction===Action.OVERRIDE}).val(Action.OVERRIDE).text(TYPO3.lang["file_upload.actions.override"]))));t.find("table").append("<tbody />").append(e)}const i=Modal.advanced({title:TYPO3.lang["file_upload.existingfiles.title"],content:t,severity:SeverityEnum.warning,buttons:[{text:$(this).data("button-close-text")||TYPO3.lang["file_upload.button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:$(this).data("button-ok-text")||TYPO3.lang["file_upload.button.continue"]||"Continue with selected actions",btnClass:"btn-warning",name:"continue"}],additionalCssClasses:["modal-inner-scroll"],size:ModalSizes.large,callback:e=>{$(e).find(".modal-footer").prepend($("<span/>").addClass("form-inline").append($("<label/>").text(TYPO3.lang["file_upload.actions.all.label"]),$("<select/>",{class:"form-select t3js-actions-all"}).append($("<option/>").val("").text(TYPO3.lang["file_upload.actions.all.empty"]),this.irreObjectUid?$("<option/>").val(Action.USE_EXISTING).text(TYPO3.lang["file_upload.actions.all.use_existing"]):"",$("<option/>",{selected:this.defaultAction===Action.SKIP}).val(Action.SKIP).text(TYPO3.lang["file_upload.actions.all.skip"]),$("<option/>",{selected:this.defaultAction===Action.RENAME}).val(Action.RENAME).text(TYPO3.lang["file_upload.actions.all.rename"]),$("<option/>",{selected:this.defaultAction===Action.OVERRIDE}).val(Action.OVERRIDE).text(TYPO3.lang["file_upload.actions.all.override"]))))}}),s=this,o=$(i);o.on("change",".t3js-actions-all",(function(){const e=$(this).val();if(""!==e)for(let t of i.querySelectorAll(".t3js-actions")){const i=parseInt(t.dataset.override,10);t.value=e,t.disabled=!0,s.askForOverride[i].action=t.value}else o.find(".t3js-actions").removeProp("disabled")})),o.on("change",".t3js-actions",(function(){const e=$(this),t=parseInt(e.data("override"),10);s.askForOverride[t].action=e.val()})),i.addEventListener("button.clicked",(function(e){const t=e.target;if("cancel"===t.name)s.askForOverride=[],Modal.dismiss();else if("continue"===t.name){for(let e of s.askForOverride)e.action===Action.USE_EXISTING?DragUploader.addFileToIrre(s.irreObjectUid,e.original):e.action!==Action.SKIP&&new FileQueueItem(s,e.uploaded,e.action);s.askForOverride=[],i.hideModal()}})),i.addEventListener("typo3-modal-hidden",(()=>{this.askForOverride=[]}))}}class FileQueueItem{constructor(e,t,i){if(this.dragUploader=e,this.file=t,this.override=i,this.$row=$("<tr />").addClass("upload-queue-item uploading"),this.dragUploader.manualTable||(this.$selector=$("<td />").addClass("col-selector").appendTo(this.$row)),this.$iconCol=$("<td />",{class:"col-icon"}).appendTo(this.$row),this.$fileName=$("<td />",{class:"col-title col-responsive"}).text(t.name).appendTo(this.$row),this.$progress=$("<td />").attr("colspan",this.dragUploader.fileListColumnCount-this.$row.find("td").length).appendTo(this.$row),this.$progressContainer=$("<div />").addClass("upload-queue-progress").appendTo(this.$progress),this.$progressBar=$("<div />").addClass("upload-queue-progress-bar").appendTo(this.$progressContainer),this.$progressPercentage=$("<span />").addClass("upload-queue-progress-percentage").appendTo(this.$progressContainer),this.$progressMessage=$("<span />").addClass("upload-queue-progress-message").appendTo(this.$progressContainer),0===$("tbody tr.upload-queue-item",this.dragUploader.$fileList).length?(this.$row.prependTo($("tbody",this.dragUploader.$fileList)),this.$row.addClass("last")):this.$row.insertBefore($("tbody tr.upload-queue-item:first",this.dragUploader.$fileList)),this.$selector&&this.$selector.html('<span class="form-check form-toggle"><input type="checkbox" class="form-check-input t3js-multi-record-selection-check" disabled/></span>'),this.$iconCol.html('<typo3-backend-icon identifier="mimetypes-other-other" />'),this.dragUploader.maxFileSize>0&&this.file.size>this.dragUploader.maxFileSize)this.updateMessage(TYPO3.lang["file_upload.maxFileSizeExceeded"].replace(/\{0\}/g,this.file.name).replace(/\{1\}/g,DragUploader.fileSizeAsString(this.dragUploader.maxFileSize))),this.$row.addClass("error");else if(this.dragUploader.fileDenyPattern&&this.file.name.match(this.dragUploader.fileDenyPattern))this.updateMessage(TYPO3.lang["file_upload.fileNotAllowed"].replace(/\{0\}/g,this.file.name)),this.$row.addClass("error");else if(this.checkAllowedExtensions()){this.updateMessage("- "+DragUploader.fileSizeAsString(this.file.size));const e=new FormData;e.append("data[upload][1][target]",this.dragUploader.target),e.append("data[upload][1][data]","1"),e.append("overwriteExistingFiles",this.override),e.append("redirect",""),e.append("upload_1",this.file);const t=new XMLHttpRequest;t.onreadystatechange=()=>{if(t.readyState===XMLHttpRequest.DONE)if(200===t.status)try{const e=JSON.parse(t.responseText);e.hasErrors?this.uploadError(t):this.uploadSuccess(e)}catch(e){this.uploadError(t)}else this.uploadError(t)},t.upload.addEventListener("progress",(e=>this.updateProgress(e))),t.open("POST",TYPO3.settings.ajaxUrls.file_process),t.send(e)}else this.updateMessage(TYPO3.lang["file_upload.fileExtensionExpected"].replace(/\{0\}/g,this.dragUploader.filesExtensionsAllowed)),this.$row.addClass("error")}updateMessage(e){this.$progressMessage.text(e)}removeProgress(){this.$progress&&this.$progress.remove()}uploadStart(){this.$progressPercentage.text("(0%)"),this.$progressBar.width("1%"),this.dragUploader.$trigger.trigger("uploadStart",[this])}uploadError(e){const t=TYPO3.lang["file_upload.uploadFailed"].replace(/\{0\}/g,this.file.name);this.updateMessage(t);try{const t=JSON.parse(e.responseText).messages;if(this.$progressPercentage.text(""),t&&t.length)for(let e of t)Notification.showMessage(e.title,e.message,e.severity,10)}catch(e){}this.$row.addClass("error"),this.dragUploader.decrementQueueLength(),this.dragUploader.$trigger.trigger("uploadError",[this,e])}updateProgress(e){const t=Math.round(e.loaded/e.total*100)+"%";this.$progressBar.outerWidth(t),this.$progressPercentage.text(t),this.dragUploader.$trigger.trigger("updateProgress",[this,t,e])}uploadSuccess(e){if(e.upload){this.dragUploader.decrementQueueLength(e.messages),this.$row.removeClass("uploading"),this.$row.prop("data-type","file"),this.$row.prop("data-file-uid",e.upload[0].uid),this.$fileName.text(e.upload[0].name),this.$progressPercentage.text(""),this.$progressMessage.text("100%"),this.$progressBar.outerWidth("100%");const t=String(e.upload[0].id);if(this.$selector){const e=this.$selector.find("input")?.get(0);e&&(e.removeAttribute("disabled"),e.setAttribute("name","CBC[_FILE|"+Md5.hash(t)+"]"),e.setAttribute("value",t))}e.upload[0].icon&&this.$iconCol.html('<a href="#" data-contextmenu-trigger="click" data-contextmenu-uid="'+t+'" data-contextmenu-table="sys_file">'+e.upload[0].icon+"</span></a>"),this.dragUploader.irreObjectUid?(DragUploader.addFileToIrre(this.dragUploader.irreObjectUid,e.upload[0]),setTimeout((()=>{this.$row.remove(),0===$("tr",this.dragUploader.$fileList).length&&(this.dragUploader.$fileList.hide(),this.dragUploader.$fileList.closest(".t3-filelist-table-container")?.addClass("hidden"),this.dragUploader.$trigger.trigger("uploadSuccess",[this,e]))}),3e3)):setTimeout((()=>{this.showFileInfo(e.upload[0]),this.dragUploader.$trigger.trigger("uploadSuccess",[this,e])}),3e3)}}showFileInfo(e){this.removeProgress(),document.querySelector("#filelist-searchterm")?.value&&$("<td />").text(e.path).appendTo(this.$row),$("<td />",{class:"col-control"}).text("").appendTo(this.$row),$("<td />").text(TYPO3.lang["type.file"]+" ("+e.extension.toUpperCase()+")").appendTo(this.$row),$("<td />").text(DragUploader.fileSizeAsString(e.size)).appendTo(this.$row);let t="";e.permissions.read&&(t+='<strong class="text-danger">'+TYPO3.lang["permissions.read"]+"</strong>"),e.permissions.write&&(t+='<strong class="text-danger">'+TYPO3.lang["permissions.write"]+"</strong>"),$("<td />").html(t).appendTo(this.$row),$("<td />").text("-").appendTo(this.$row);for(let e=this.$row.find("td").length;e<this.dragUploader.fileListColumnCount;e++)$("<td />").text("").appendTo(this.$row)}checkAllowedExtensions(){if(!this.dragUploader.filesExtensionsAllowed)return!0;const e=this.file.name.split(".").pop(),t=this.dragUploader.filesExtensionsAllowed.split(",");return-1!==$.inArray(e.toLowerCase(),t)}}class DragUploader{static fileSizeAsString(e){const t=e/1024;let i="";return i=t>1024?(t/1024).toFixed(1)+" MB":t.toFixed(1)+" KB",i}static addFileToIrre(e,t){const i={actionName:"typo3:foreignRelation:insert",objectGroup:e,table:"sys_file",uid:t.uid};MessageUtility.send(i)}static init(){const e=this.options;$.fn.extend({dragUploader:function(e){return this.each(((t,i)=>{const s=$(i);let o=s.data("DragUploaderPlugin");o||s.data("DragUploaderPlugin",o=new DragUploaderPlugin(i)),"string"==typeof e&&o[e]()}))}}),DocumentService.ready().then((()=>{$(".t3js-drag-uploader").dragUploader(e)}));new MutationObserver((()=>{$(".t3js-drag-uploader").dragUploader(e)})).observe(document,{childList:!0,subtree:!0})}}export const initialize=function(){if(DragUploader.init(),void 0!==TYPO3.settings&&void 0!==TYPO3.settings.RequireJS&&void 0!==TYPO3.settings.RequireJS.PostInitializationModules&&void 0!==TYPO3.settings.RequireJS.PostInitializationModules["TYPO3/CMS/Backend/DragUploader"])for(let e of TYPO3.settings.RequireJS.PostInitializationModules["TYPO3/CMS/Backend/DragUploader"])window.require([e])};initialize(); \ No newline at end of file +import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import{DateTime}from"luxon";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import{MessageUtility}from"@typo3/backend/utility/message-utility.js";import NProgress from"nprogress";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{default as Modal,Sizes as ModalSizes}from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import ImmediateAction from"@typo3/backend/action-button/immediate-action.js";import Md5 from"@typo3/backend/hashing/md5.js";import"@typo3/backend/element/icon-element.js";var Action;!function(e){e.OVERRIDE="replace",e.RENAME="rename",e.SKIP="cancel",e.USE_EXISTING="useExisting"}(Action||(Action={}));class DragUploaderPlugin{constructor(e){this.askForOverride=[],this.percentagePerFile=1,this.dragStartedInDocument=!1,this.hideDropzone=e=>{e.stopPropagation(),e.preventDefault(),this.$dropzone.hide(),this.$dropzone.removeClass("drop-status-ok"),this.manuallyTriggered=!1},this.dragFileIntoDocument=e=>(this.dragStartedInDocument||(e.stopPropagation(),e.preventDefault(),$(e.currentTarget).addClass("drop-in-progress"),this.$element.get(0)?.offsetParent&&this.showDropzone()),!1),this.dragAborted=e=>(e.stopPropagation(),e.preventDefault(),$(e.currentTarget).removeClass("drop-in-progress"),this.dragStartedInDocument=!1,!1),this.ignoreDrop=e=>(e.stopPropagation(),e.preventDefault(),this.dragAborted(e),!1),this.handleDrop=e=>{this.ignoreDrop(e),this.hideDropzone(e),this.processFiles(e.originalEvent.dataTransfer.files)},this.fileInDropzone=()=>{this.$dropzone.addClass("drop-status-ok")},this.fileOutOfDropzone=()=>{this.$dropzone.removeClass("drop-status-ok"),this.manuallyTriggered||this.$dropzone.hide()},this.$body=$("body"),this.$element=$(e);const t=void 0!==this.$element.data("dropzoneTrigger");this.$trigger=$(this.$element.data("dropzoneTrigger")),this.defaultAction=this.$element.data("defaultAction")||Action.SKIP,this.$dropzone=$("<div />").addClass("dropzone").hide(),this.irreObjectUid=this.$element.data("fileIrreObject");const i=this.$element.data("dropzoneTarget");if(this.irreObjectUid&&0!==this.$element.nextAll(i).length?(this.dropZoneInsertBefore=!0,this.$dropzone.insertBefore(i)):(this.dropZoneInsertBefore=!1,this.$dropzone.insertAfter(i)),this.$dropzoneMask=$("<div />").addClass("dropzone-mask").appendTo(this.$dropzone),this.fileInput=document.createElement("input"),this.fileInput.setAttribute("type","file"),this.fileInput.setAttribute("multiple","multiple"),this.fileInput.setAttribute("name","files[]"),this.fileInput.classList.add("upload-file-picker"),this.$body.append(this.fileInput),this.$fileList=$(this.$element.data("progress-container")),this.fileListColumnCount=$("thead tr:first th",this.$fileList).length+1,this.filesExtensionsAllowed=this.$element.data("file-allowed"),this.fileDenyPattern=this.$element.data("file-deny-pattern")?new RegExp(this.$element.data("file-deny-pattern"),"i"):null,this.maxFileSize=parseInt(this.$element.data("max-file-size"),10),this.target=this.$element.data("target-folder"),this.reloadUrl=this.$element.data("reload-url"),this.browserCapabilities={fileReader:"undefined"!=typeof FileReader,DnD:"draggable"in document.createElement("span"),Progress:"upload"in new XMLHttpRequest},this.browserCapabilities.DnD){if(this.$body.on("dragstart",(()=>{this.dragStartedInDocument=!0})),this.$body.on("dragover",this.dragFileIntoDocument),this.$body.on("dragend",this.dragAborted),this.$body.on("drop",this.ignoreDrop),this.$dropzone.on("dragenter",this.fileInDropzone),this.$dropzoneMask.on("dragenter",this.fileInDropzone),this.$dropzoneMask.on("dragleave",this.fileOutOfDropzone),this.$dropzoneMask.on("drop",(e=>this.handleDrop(e))),this.$dropzone.prepend('<button type="button" class="dropzone-hint" aria-labelledby="dropzone-title"><div class="dropzone-hint-media"><div class="dropzone-hint-icon"></div></div><div class="dropzone-hint-body"><h3 id="dropzone-title" class="dropzone-hint-title">'+TYPO3.lang["file_upload.dropzonehint.title"]+'</h3><p class="dropzone-hint-message">'+TYPO3.lang["file_upload.dropzonehint.message"]+"</p></div></div>").on("click",(()=>{this.fileInput.click()})),$('<button type="button" />').addClass("dropzone-close").attr("aria-label",TYPO3.lang["file_upload.dropzone.close"]).on("click",this.hideDropzone).appendTo(this.$dropzone),0===this.$fileList.length){this.$fileList=$("<table />").attr("id","typo3-filelist").addClass("table table-striped table-hover upload-queue").html("<tbody></tbody>");const e=$("<div/>",{class:"table-fit"}).hide().append(this.$fileList);this.dropZoneInsertBefore?e.insertAfter(this.$dropzone):e.insertBefore(this.$dropzone),this.fileListColumnCount=8,this.manualTable=!0}this.fileInput.addEventListener("change",(e=>{this.hideDropzone(e),this.processFiles(this.fileInput.files)})),document.addEventListener("keydown",(e=>{"Escape"===e.code&&this.$dropzone.is(":visible")&&this.hideDropzone(e)})),this.bindUploadButton(!0===t?this.$trigger:this.$element)}else console.warn("Browser has no Drag and drop capabilities; cannot initialize DragUploader")}showDropzone(){this.$dropzone.show()}processFiles(e){this.queueLength=e.length,this.$fileList.parent().is(":visible")||(this.$fileList.parent().show(),this.$fileList.closest(".t3-filelist-table-container")?.removeClass("hidden"),this.$fileList.closest("form")?.find(".t3-filelist-info-container")?.hide()),NProgress.start(),this.percentagePerFile=1/e.length;const t=[];Array.from(e).forEach((e=>{const i=new AjaxRequest(TYPO3.settings.ajaxUrls.file_exists).withQueryArguments({fileName:e.name,fileTarget:this.target}).get({cache:"no-cache"}).then((async t=>{const i=await t.resolve();void 0!==i.uid?(this.askForOverride.push({original:i,uploaded:e,action:this.irreObjectUid?Action.USE_EXISTING:this.defaultAction}),NProgress.inc(this.percentagePerFile)):new FileQueueItem(this,e,Action.SKIP)}));t.push(i)})),Promise.all(t).then((()=>{this.drawOverrideModal(),NProgress.done()})),this.fileInput.value=""}bindUploadButton(e){e.on("click",(e=>{e.preventDefault(),this.fileInput.click(),this.showDropzone(),this.manuallyTriggered=!0}))}decrementQueueLength(e){if(this.queueLength>0&&(this.queueLength--,0===this.queueLength)){const t=e&&e.length?5e3:0;if(t)for(const t of e)Notification.showMessage(t.title,t.message,t.severity);this.reloadUrl&&setTimeout((()=>{Notification.info(TYPO3.lang["file_upload.reload.filelist"],TYPO3.lang["file_upload.reload.filelist.message"],10,[{label:TYPO3.lang["file_upload.reload.filelist.actions.dismiss"]},{label:TYPO3.lang["file_upload.reload.filelist.actions.reload"],action:new ImmediateAction((()=>{top.list_frame.document.location.href=this.reloadUrl}))}])}),t)}}drawOverrideModal(){const e=Object.keys(this.askForOverride).length;if(0===e)return;const t=$("<div/>").append($("<p/>").text(TYPO3.lang["file_upload.existingfiles.description"]),$("<table/>",{class:"table"}).append($("<thead/>").append($("<tr />").append($("<th/>"),$("<th/>").text(TYPO3.lang["file_upload.header.originalFile"]),$("<th/>").text(TYPO3.lang["file_upload.header.uploadedFile"]),$("<th/>").text(TYPO3.lang["file_upload.header.action"])))));for(let i=0;i<e;++i){const e=$("<tr />").append($("<td />").append(""!==this.askForOverride[i].original.thumbUrl?$("<img />",{src:this.askForOverride[i].original.thumbUrl,height:40}):$(this.askForOverride[i].original.icon)),$("<td />").html(this.askForOverride[i].original.name+" ("+DragUploader.fileSizeAsString(this.askForOverride[i].original.size)+")<br>"+DateTime.fromSeconds(this.askForOverride[i].original.mtime).toLocaleString(DateTime.DATETIME_MED)),$("<td />").html(this.askForOverride[i].uploaded.name+" ("+DragUploader.fileSizeAsString(this.askForOverride[i].uploaded.size)+")<br>"+DateTime.fromMillis(this.askForOverride[i].uploaded.lastModified).toLocaleString(DateTime.DATETIME_MED)),$("<td />").append($("<select />",{class:"form-select t3js-actions","data-override":i}).append(this.irreObjectUid?$("<option/>").val(Action.USE_EXISTING).text(TYPO3.lang["file_upload.actions.use_existing"]):"",$("<option />",{selected:this.defaultAction===Action.SKIP}).val(Action.SKIP).text(TYPO3.lang["file_upload.actions.skip"]),$("<option />",{selected:this.defaultAction===Action.RENAME}).val(Action.RENAME).text(TYPO3.lang["file_upload.actions.rename"]),$("<option />",{selected:this.defaultAction===Action.OVERRIDE}).val(Action.OVERRIDE).text(TYPO3.lang["file_upload.actions.override"]))));t.find("table").append("<tbody />").append(e)}const i=Modal.advanced({title:TYPO3.lang["file_upload.existingfiles.title"],content:t,severity:SeverityEnum.warning,buttons:[{text:$(this).data("button-close-text")||TYPO3.lang["file_upload.button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:$(this).data("button-ok-text")||TYPO3.lang["file_upload.button.continue"]||"Continue with selected actions",btnClass:"btn-warning",name:"continue"}],additionalCssClasses:["modal-inner-scroll"],size:ModalSizes.large,callback:e=>{$(e).find(".modal-footer").prepend($("<span/>").addClass("form-inline").append($("<label/>").text(TYPO3.lang["file_upload.actions.all.label"]),$("<select/>",{class:"form-select t3js-actions-all"}).append($("<option/>").val("").text(TYPO3.lang["file_upload.actions.all.empty"]),this.irreObjectUid?$("<option/>").val(Action.USE_EXISTING).text(TYPO3.lang["file_upload.actions.all.use_existing"]):"",$("<option/>",{selected:this.defaultAction===Action.SKIP}).val(Action.SKIP).text(TYPO3.lang["file_upload.actions.all.skip"]),$("<option/>",{selected:this.defaultAction===Action.RENAME}).val(Action.RENAME).text(TYPO3.lang["file_upload.actions.all.rename"]),$("<option/>",{selected:this.defaultAction===Action.OVERRIDE}).val(Action.OVERRIDE).text(TYPO3.lang["file_upload.actions.all.override"]))))}}),s=$(i);s.on("change",".t3js-actions-all",(e=>{const t=$(e.currentTarget).val();if(""!==t)for(const e of i.querySelectorAll(".t3js-actions")){const i=parseInt(e.dataset.override,10);e.value=t,e.disabled=!0,this.askForOverride[i].action=e.value}else s.find(".t3js-actions").removeProp("disabled")})),s.on("change",".t3js-actions",(e=>{const t=$(e.currentTarget),i=parseInt(t.data("override"),10);this.askForOverride[i].action=t.val()})),i.addEventListener("button.clicked",(e=>{const t=e.target;if("cancel"===t.name)this.askForOverride=[],Modal.dismiss();else if("continue"===t.name){for(const e of this.askForOverride)e.action===Action.USE_EXISTING?DragUploader.addFileToIrre(this.irreObjectUid,e.original):e.action!==Action.SKIP&&new FileQueueItem(this,e.uploaded,e.action);this.askForOverride=[],i.hideModal()}})),i.addEventListener("typo3-modal-hidden",(()=>{this.askForOverride=[]}))}}class FileQueueItem{constructor(e,t,i){if(this.dragUploader=e,this.file=t,this.override=i,this.$row=$("<tr />").addClass("upload-queue-item uploading"),this.dragUploader.manualTable||(this.$selector=$("<td />").addClass("col-selector").appendTo(this.$row)),this.$iconCol=$("<td />",{class:"col-icon"}).appendTo(this.$row),this.$fileName=$("<td />",{class:"col-title col-responsive"}).text(t.name).appendTo(this.$row),this.$progress=$("<td />").attr("colspan",this.dragUploader.fileListColumnCount-this.$row.find("td").length).appendTo(this.$row),this.$progressContainer=$("<div />").addClass("upload-queue-progress").appendTo(this.$progress),this.$progressBar=$("<div />").addClass("upload-queue-progress-bar").appendTo(this.$progressContainer),this.$progressPercentage=$("<span />").addClass("upload-queue-progress-percentage").appendTo(this.$progressContainer),this.$progressMessage=$("<span />").addClass("upload-queue-progress-message").appendTo(this.$progressContainer),0===$("tbody tr.upload-queue-item",this.dragUploader.$fileList).length?(this.$row.prependTo($("tbody",this.dragUploader.$fileList)),this.$row.addClass("last")):this.$row.insertBefore($("tbody tr.upload-queue-item:first",this.dragUploader.$fileList)),this.$selector&&this.$selector.html('<span class="form-check form-toggle"><input type="checkbox" class="form-check-input t3js-multi-record-selection-check" disabled/></span>'),this.$iconCol.html('<typo3-backend-icon identifier="mimetypes-other-other" />'),this.dragUploader.maxFileSize>0&&this.file.size>this.dragUploader.maxFileSize)this.updateMessage(TYPO3.lang["file_upload.maxFileSizeExceeded"].replace(/\{0\}/g,this.file.name).replace(/\{1\}/g,DragUploader.fileSizeAsString(this.dragUploader.maxFileSize))),this.$row.addClass("error");else if(this.dragUploader.fileDenyPattern&&this.file.name.match(this.dragUploader.fileDenyPattern))this.updateMessage(TYPO3.lang["file_upload.fileNotAllowed"].replace(/\{0\}/g,this.file.name)),this.$row.addClass("error");else if(this.checkAllowedExtensions()){this.updateMessage("- "+DragUploader.fileSizeAsString(this.file.size));const e=new FormData;e.append("data[upload][1][target]",this.dragUploader.target),e.append("data[upload][1][data]","1"),e.append("overwriteExistingFiles",this.override),e.append("redirect",""),e.append("upload_1",this.file);const t=new XMLHttpRequest;t.onreadystatechange=()=>{if(t.readyState===XMLHttpRequest.DONE)if(200===t.status)try{const e=JSON.parse(t.responseText);e.hasErrors?this.uploadError(t):this.uploadSuccess(e)}catch(e){this.uploadError(t)}else this.uploadError(t)},t.upload.addEventListener("progress",(e=>this.updateProgress(e))),t.open("POST",TYPO3.settings.ajaxUrls.file_process),t.send(e)}else this.updateMessage(TYPO3.lang["file_upload.fileExtensionExpected"].replace(/\{0\}/g,this.dragUploader.filesExtensionsAllowed)),this.$row.addClass("error")}updateMessage(e){this.$progressMessage.text(e)}removeProgress(){this.$progress&&this.$progress.remove()}uploadStart(){this.$progressPercentage.text("(0%)"),this.$progressBar.width("1%"),this.dragUploader.$trigger.trigger("uploadStart",[this])}uploadError(e){const t=TYPO3.lang["file_upload.uploadFailed"].replace(/\{0\}/g,this.file.name);this.updateMessage(t);try{const t=JSON.parse(e.responseText).messages;if(this.$progressPercentage.text(""),t&&t.length)for(const e of t)Notification.showMessage(e.title,e.message,e.severity,10)}catch(e){}this.$row.addClass("error"),this.dragUploader.decrementQueueLength(),this.dragUploader.$trigger.trigger("uploadError",[this,e])}updateProgress(e){const t=Math.round(e.loaded/e.total*100)+"%";this.$progressBar.outerWidth(t),this.$progressPercentage.text(t),this.dragUploader.$trigger.trigger("updateProgress",[this,t,e])}uploadSuccess(e){if(e.upload){this.dragUploader.decrementQueueLength(e.messages),this.$row.removeClass("uploading"),this.$row.prop("data-type","file"),this.$row.prop("data-file-uid",e.upload[0].uid),this.$fileName.text(e.upload[0].name),this.$progressPercentage.text(""),this.$progressMessage.text("100%"),this.$progressBar.outerWidth("100%");const t=String(e.upload[0].id);if(this.$selector){const e=this.$selector.find("input")?.get(0);e&&(e.removeAttribute("disabled"),e.setAttribute("name","CBC[_FILE|"+Md5.hash(t)+"]"),e.setAttribute("value",t))}e.upload[0].icon&&this.$iconCol.html('<a href="#" data-contextmenu-trigger="click" data-contextmenu-uid="'+t+'" data-contextmenu-table="sys_file">'+e.upload[0].icon+"</span></a>"),this.dragUploader.irreObjectUid?(DragUploader.addFileToIrre(this.dragUploader.irreObjectUid,e.upload[0]),setTimeout((()=>{this.$row.remove(),0===$("tr",this.dragUploader.$fileList).length&&(this.dragUploader.$fileList.hide(),this.dragUploader.$fileList.closest(".t3-filelist-table-container")?.addClass("hidden"),this.dragUploader.$trigger.trigger("uploadSuccess",[this,e]))}),3e3)):setTimeout((()=>{this.showFileInfo(e.upload[0]),this.dragUploader.$trigger.trigger("uploadSuccess",[this,e])}),3e3)}}showFileInfo(e){this.removeProgress(),document.querySelector("#filelist-searchterm")?.value&&$("<td />").text(e.path).appendTo(this.$row),$("<td />",{class:"col-control"}).text("").appendTo(this.$row),$("<td />").text(TYPO3.lang["type.file"]+" ("+e.extension.toUpperCase()+")").appendTo(this.$row),$("<td />").text(DragUploader.fileSizeAsString(e.size)).appendTo(this.$row);let t="";e.permissions.read&&(t+='<strong class="text-danger">'+TYPO3.lang["permissions.read"]+"</strong>"),e.permissions.write&&(t+='<strong class="text-danger">'+TYPO3.lang["permissions.write"]+"</strong>"),$("<td />").html(t).appendTo(this.$row),$("<td />").text("-").appendTo(this.$row);for(let e=this.$row.find("td").length;e<this.dragUploader.fileListColumnCount;e++)$("<td />").text("").appendTo(this.$row)}checkAllowedExtensions(){if(!this.dragUploader.filesExtensionsAllowed)return!0;const e=this.file.name.split(".").pop(),t=this.dragUploader.filesExtensionsAllowed.split(",");return-1!==$.inArray(e.toLowerCase(),t)}}class DragUploader{static fileSizeAsString(e){const t=e/1024;let i="";return i=t>1024?(t/1024).toFixed(1)+" MB":t.toFixed(1)+" KB",i}static addFileToIrre(e,t){const i={actionName:"typo3:foreignRelation:insert",objectGroup:e,table:"sys_file",uid:t.uid};MessageUtility.send(i)}static init(){const e=this.options;$.fn.extend({dragUploader:function(e){return this.each(((t,i)=>{const s=$(i);let o=s.data("DragUploaderPlugin");o||s.data("DragUploaderPlugin",o=new DragUploaderPlugin(i)),"string"==typeof e&&o[e]()}))}}),DocumentService.ready().then((()=>{$(".t3js-drag-uploader").dragUploader(e)}));new MutationObserver((()=>{$(".t3js-drag-uploader").dragUploader(e)})).observe(document,{childList:!0,subtree:!0})}}export const initialize=function(){if(DragUploader.init(),void 0!==TYPO3.settings&&void 0!==TYPO3.settings.RequireJS&&void 0!==TYPO3.settings.RequireJS.PostInitializationModules&&void 0!==TYPO3.settings.RequireJS.PostInitializationModules["TYPO3/CMS/Backend/DragUploader"])for(const e of TYPO3.settings.RequireJS.PostInitializationModules["TYPO3/CMS/Backend/DragUploader"])window.require([e])};initialize(); \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/element/editable-page-title.js b/typo3/sysext/backend/Resources/Public/JavaScript/element/editable-page-title.js index 5adb8aaac1ae..e738bec20809 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/element/editable-page-title.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/element/editable-page-title.js @@ -14,7 +14,7 @@ var __decorate=function(t,e,i,o){var a,r=arguments.length,n=r<3?e:null===o?o=Obj <div class="wrapper"> <h1 @dblclick="${()=>{this.startEditing()}}">${this.pageTitle}</h1> ${this.composeEditButton()} - </div>`,t}isEditable(){return this.editable&&this.pageId>0}endEditing(){this.isEditable()&&(this._isEditing=!1)}updatePageTitle(t){t.preventDefault();const e=new FormData(t.target),i=Object.fromEntries(e).newPageTitle.toString();if(this.pageTitle===i)return void this.endEditing();this._isSubmitting=!0;let o,a={};o=this.localizedPageId>0?this.localizedPageId:this.pageId,a.data={pages:{[o]:{title:i}}},AjaxDataHandler.process(a).then((()=>{this.pageTitle=i,top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))})).finally((()=>{this.endEditing(),this._isSubmitting=!1}))}composeEditButton(){return html` + </div>`,t}isEditable(){return this.editable&&this.pageId>0}endEditing(){this.isEditable()&&(this._isEditing=!1)}updatePageTitle(t){t.preventDefault();const e=new FormData(t.target),i=Object.fromEntries(e).newPageTitle.toString();if(this.pageTitle===i)return void this.endEditing();this._isSubmitting=!0;let o=this.pageId;this.localizedPageId>0&&(o=this.localizedPageId);const a={data:{pages:{[o]:{title:i}}}};AjaxDataHandler.process(a).then((()=>{this.pageTitle=i,top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))})).finally((()=>{this.endEditing(),this._isSubmitting=!1}))}composeEditButton(){return html` <button data-action="edit" type="button" aria-label="${lll("editPageTitle")}" @click="${()=>{this.startEditing()}}"> <typo3-backend-icon identifier="actions-open" size="small"></typo3-backend-icon> </button>`}composeEditForm(){return html` @@ -129,4 +129,4 @@ var __decorate=function(t,e,i,o){var a,r=arguments.length,n=r<3?e:null===o?o=Obj button[data-action="close"] { right: 0; } - `,__decorate([property({type:String})],EditablePageTitle.prototype,"pageTitle",void 0),__decorate([property({type:Number})],EditablePageTitle.prototype,"pageId",void 0),__decorate([property({type:Number})],EditablePageTitle.prototype,"localizedPageId",void 0),__decorate([property({type:Boolean})],EditablePageTitle.prototype,"editable",void 0),__decorate([state()],EditablePageTitle.prototype,"_isEditing",void 0),__decorate([state()],EditablePageTitle.prototype,"_isSubmitting",void 0),EditablePageTitle=__decorate([customElement("typo3-backend-editable-page-title")],EditablePageTitle); \ No newline at end of file + `,__decorate([property({type:String})],EditablePageTitle.prototype,"pageTitle",void 0),__decorate([property({type:Number})],EditablePageTitle.prototype,"pageId",void 0),__decorate([property({type:Number})],EditablePageTitle.prototype,"localizedPageId",void 0),__decorate([property({type:Boolean})],EditablePageTitle.prototype,"editable",void 0),__decorate([state()],EditablePageTitle.prototype,"_isEditing",void 0),__decorate([state()],EditablePageTitle.prototype,"_isSubmitting",void 0),EditablePageTitle=__decorate([customElement("typo3-backend-editable-page-title")],EditablePageTitle);export{EditablePageTitle}; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/element/immediate-action-element.js b/typo3/sysext/backend/Resources/Public/JavaScript/element/immediate-action-element.js index e98dd4f17759..1fd5a6562058 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/element/immediate-action-element.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/element/immediate-action-element.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import Utility from"@typo3/backend/utility.js";import{EventDispatcher}from"@typo3/backend/event/event-dispatcher.js";export class ImmediateActionElement extends HTMLElement{constructor(){super(...arguments),this.args=[]}static async getDelegate(t){switch(t){case"TYPO3.ModuleMenu.App.refreshMenu":const{default:e}=await import("@typo3/backend/module-menu.js");return e.App.refreshMenu.bind(e.App);case"TYPO3.Backend.Topbar.refresh":const{default:a}=await import("@typo3/backend/viewport.js");return a.Topbar.refresh.bind(a.Topbar);case"TYPO3.WindowManager.localOpen":const{default:r}=await import("@typo3/backend/window-manager.js");return r.localOpen.bind(r);case"TYPO3.Backend.Storage.ModuleStateStorage.update":return(await import("@typo3/backend/storage/module-state-storage.js")).ModuleStateStorage.update;case"TYPO3.Backend.Storage.ModuleStateStorage.updateWithCurrentMount":return(await import("@typo3/backend/storage/module-state-storage.js")).ModuleStateStorage.updateWithCurrentMount;case"TYPO3.Backend.Event.EventDispatcher.dispatchCustomEvent":return EventDispatcher.dispatchCustomEvent;default:throw Error('Unknown action "'+t+'"')}}static get observedAttributes(){return["action","args","args-list"]}attributeChangedCallback(t,e,a){if("action"===t)this.action=a;else if("args"===t){const t=a.replace(/"/g,'"'),e=JSON.parse(t);this.args=e instanceof Array?Utility.trimItems(e):[]}else if("args-list"===t){const t=a.split(",");this.args=Utility.trimItems(t)}}connectedCallback(){if(!this.action)throw new Error("Missing mandatory action attribute");ImmediateActionElement.getDelegate(this.action).then((t=>t.apply(null,this.args)))}}window.customElements.define("typo3-immediate-action",ImmediateActionElement); \ No newline at end of file +import Utility from"@typo3/backend/utility.js";import{EventDispatcher}from"@typo3/backend/event/event-dispatcher.js";export class ImmediateActionElement extends HTMLElement{constructor(){super(...arguments),this.args=[]}static get observedAttributes(){return["action","args","args-list"]}static async getDelegate(t){switch(t){case"TYPO3.ModuleMenu.App.refreshMenu":const{default:e}=await import("@typo3/backend/module-menu.js");return e.App.refreshMenu.bind(e.App);case"TYPO3.Backend.Topbar.refresh":const{default:a}=await import("@typo3/backend/viewport.js");return a.Topbar.refresh.bind(a.Topbar);case"TYPO3.WindowManager.localOpen":const{default:r}=await import("@typo3/backend/window-manager.js");return r.localOpen.bind(r);case"TYPO3.Backend.Storage.ModuleStateStorage.update":return(await import("@typo3/backend/storage/module-state-storage.js")).ModuleStateStorage.update;case"TYPO3.Backend.Storage.ModuleStateStorage.updateWithCurrentMount":return(await import("@typo3/backend/storage/module-state-storage.js")).ModuleStateStorage.updateWithCurrentMount;case"TYPO3.Backend.Event.EventDispatcher.dispatchCustomEvent":return EventDispatcher.dispatchCustomEvent;default:throw Error('Unknown action "'+t+'"')}}attributeChangedCallback(t,e,a){if("action"===t)this.action=a;else if("args"===t){const t=a.replace(/"/g,'"'),e=JSON.parse(t);this.args=e instanceof Array?Utility.trimItems(e):[]}else if("args-list"===t){const t=a.split(",");this.args=Utility.trimItems(t)}}connectedCallback(){if(!this.action)throw new Error("Missing mandatory action attribute");ImmediateActionElement.getDelegate(this.action).then((t=>t(...this.args)))}}window.customElements.define("typo3-immediate-action",ImmediateActionElement); \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/element/table-wizard-element.js b/typo3/sysext/backend/Resources/Public/JavaScript/element/table-wizard-element.js index 034993e778a8..c66a80831e7c 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/element/table-wizard-element.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/element/table-wizard-element.js @@ -10,7 +10,7 @@ * * The TYPO3 project - inspiring people to share! */ -var __decorate=function(t,e,l,a){var o,i=arguments.length,n=i<3?e:null===a?a=Object.getOwnPropertyDescriptor(e,l):a;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)n=Reflect.decorate(t,e,l,a);else for(var s=t.length-1;s>=0;s--)(o=t[s])&&(n=(i<3?o(n):i>3?o(e,l,n):o(e,l))||n);return i>3&&n&&Object.defineProperty(e,l,n),n};import{html,LitElement,render}from"lit";import{customElement,property}from"lit/decorators.js";import{lll}from"@typo3/core/lit-helper.js";import"@typo3/backend/element/icon-element.js";import Severity from"@typo3/backend/severity.js";import Modal from"@typo3/backend/modal.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";let TableWizardElement=class extends LitElement{constructor(){super(...arguments),this.type="textarea",this.selectorData="",this.delimiter="|",this.enclosure="",this.appendRows=1,this.l10n={},this.table=[]}connectedCallback(){super.connectedCallback(),this.selectorData=this.getAttribute("selector"),this.delimiter=this.getAttribute("delimiter"),this.enclosure=this.getAttribute("enclosure")||"",this.readTableFromTextarea()}get firstRow(){return this.table[0]||[]}createRenderRoot(){return this}render(){return this.renderTemplate()}provideMinimalTable(){0!==this.table.length&&0!==this.firstRow.length||(this.table=[[""]])}readTableFromTextarea(){let t=document.querySelector(this.selectorData),e=[];t.value.split("\n").forEach((t=>{if(""!==t){this.enclosure&&(t=t.replace(new RegExp(this.enclosure,"g"),""));let l=t.split(this.delimiter);e.push(l)}})),this.table=e}writeTableSyntaxToTextarea(){let t=document.querySelector(this.selectorData),e="";this.table.forEach((t=>{let l=t.length;e+=t.reduce(((t,e,a)=>{let o=l-1===a?"":this.delimiter;return t+this.enclosure+e+this.enclosure+o}),"")+"\n"})),t.value=e,t.dispatchEvent(new CustomEvent("change",{bubbles:!0}))}modifyTable(t,e,l){const a=t.target;this.table[e][l]=a.value,this.writeTableSyntaxToTextarea(),this.requestUpdate()}toggleType(t){this.type="input"===this.type?"textarea":"input"}moveColumn(t,e,l){this.table=this.table.map((t=>{const a=t.splice(e,1);return t.splice(l,0,...a),t})),this.writeTableSyntaxToTextarea(),this.requestUpdate()}appendColumn(t,e){this.table=this.table.map((t=>(t.splice(e+1,0,""),t))),this.writeTableSyntaxToTextarea(),this.requestUpdate()}removeColumn(t,e){this.table=this.table.map((t=>(t.splice(e,1),t))),this.writeTableSyntaxToTextarea(),this.requestUpdate()}moveRow(t,e,l){const a=this.table.splice(e,1);this.table.splice(l,0,...a),this.writeTableSyntaxToTextarea(),this.requestUpdate()}appendRow(t,e){let l=this.firstRow.concat().fill(""),a=new Array(this.appendRows).fill(l);this.table.splice(e+1,0,...a),this.writeTableSyntaxToTextarea(),this.requestUpdate()}removeRow(t,e){this.table.splice(e,1),this.writeTableSyntaxToTextarea(),this.requestUpdate()}renderTemplate(){this.provideMinimalTable();const t=Object.keys(this.firstRow).map((t=>parseInt(t,10))),e=t[t.length-1],l=this.table.length-1;return html` +var __decorate=function(t,e,l,a){var o,n=arguments.length,i=n<3?e:null===a?a=Object.getOwnPropertyDescriptor(e,l):a;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)i=Reflect.decorate(t,e,l,a);else for(var s=t.length-1;s>=0;s--)(o=t[s])&&(i=(n<3?o(i):n>3?o(e,l,i):o(e,l))||i);return n>3&&i&&Object.defineProperty(e,l,i),i};import{html,LitElement,render}from"lit";import{customElement,property}from"lit/decorators.js";import{lll}from"@typo3/core/lit-helper.js";import"@typo3/backend/element/icon-element.js";import Severity from"@typo3/backend/severity.js";import Modal from"@typo3/backend/modal.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";let TableWizardElement=class extends LitElement{constructor(){super(...arguments),this.type="textarea",this.selectorData="",this.delimiter="|",this.enclosure="",this.appendRows=1,this.table=[]}get firstRow(){return this.table[0]||[]}connectedCallback(){super.connectedCallback(),this.selectorData=this.getAttribute("selector"),this.delimiter=this.getAttribute("delimiter"),this.enclosure=this.getAttribute("enclosure")||"",this.readTableFromTextarea()}createRenderRoot(){return this}render(){return this.renderTemplate()}provideMinimalTable(){0!==this.table.length&&0!==this.firstRow.length||(this.table=[[""]])}readTableFromTextarea(){const t=document.querySelector(this.selectorData),e=[];t.value.split("\n").forEach((t=>{if(""!==t){this.enclosure&&(t=t.replace(new RegExp(this.enclosure,"g"),""));const l=t.split(this.delimiter);e.push(l)}})),this.table=e}writeTableSyntaxToTextarea(){const t=document.querySelector(this.selectorData);let e="";this.table.forEach((t=>{const l=t.length;e+=t.reduce(((t,e,a)=>{const o=l-1===a?"":this.delimiter;return t+this.enclosure+e+this.enclosure+o}),"")+"\n"})),t.value=e,t.dispatchEvent(new CustomEvent("change",{bubbles:!0}))}modifyTable(t,e,l){const a=t.target;this.table[e][l]=a.value,this.writeTableSyntaxToTextarea(),this.requestUpdate()}toggleType(){this.type="input"===this.type?"textarea":"input"}moveColumn(t,e){this.table=this.table.map((l=>{const a=l.splice(t,1);return l.splice(e,0,...a),l})),this.writeTableSyntaxToTextarea(),this.requestUpdate()}appendColumn(t,e){this.table=this.table.map((t=>(t.splice(e+1,0,""),t))),this.writeTableSyntaxToTextarea(),this.requestUpdate()}removeColumn(t,e){this.table=this.table.map((t=>(t.splice(e,1),t))),this.writeTableSyntaxToTextarea(),this.requestUpdate()}moveRow(t,e,l){const a=this.table.splice(e,1);this.table.splice(l,0,...a),this.writeTableSyntaxToTextarea(),this.requestUpdate()}appendRow(t,e){const l=this.firstRow.concat().fill(""),a=new Array(this.appendRows).fill(l);this.table.splice(e+1,0,...a),this.writeTableSyntaxToTextarea(),this.requestUpdate()}removeRow(t,e){this.table.splice(e,1),this.writeTableSyntaxToTextarea(),this.requestUpdate()}renderTemplate(){this.provideMinimalTable();const t=Object.keys(this.firstRow).map((t=>parseInt(t,10))),e=t[t.length-1],l=this.table.length-1;return html` <style> :host, typo3-backend-table-wizard { display: inline-block; } </style> @@ -43,7 +43,7 @@ var __decorate=function(t,e,l,a){var o,i=arguments.length,n=i<3?e:null===a?a=Obj `}renderTypeButton(){return html` <span class="btn-group"> <button class="btn btn-default" type="button" title="${lll("table_smallFields")}" - @click="${t=>this.toggleType(t)}"> + @click="${()=>this.toggleType()}"> <typo3-backend-icon identifier="${"input"===this.type?"actions-chevron-expand":"actions-chevron-contract"}" size="small"></typo3-backend-icon> </button> <button class="btn btn-default" type="button" title="${lll("table_setCount")}" @@ -51,18 +51,18 @@ var __decorate=function(t,e,l,a){var o,i=arguments.length,n=i<3?e:null===a?a=Obj <typo3-backend-icon identifier="actions-plus" size="small"></typo3-backend-icon> </button> <button class="btn btn-default" type="button" title="${lll("table_showCode")}" - @click="${t=>this.showTableSyntax(t)}"> + @click="${()=>this.showTableSyntax()}"> <typo3-backend-icon identifier="actions-code" size="small"></typo3-backend-icon> </button> </span> `}renderColButtons(t,e){const l={title:lll(0===t?"table_end":"table_left"),class:0===t?"bar-right":"left",target:0===t?e:t-1},a={title:lll(t===e?"table_start":"table_right"),class:t===e?"bar-left":"right",target:t===e?0:t+1};return html` <span class="btn-group"> <button class="btn btn-default" type="button" title="${l.title}" - @click="${e=>this.moveColumn(e,t,l.target)}"> + @click="${()=>this.moveColumn(t,l.target)}"> <typo3-backend-icon identifier="actions-chevron-${l.class}" size="small"></typo3-backend-icon> </button> <button class="btn btn-default" type="button" title="${a.title}" - @click="${e=>this.moveColumn(e,t,a.target)}"> + @click="${()=>this.moveColumn(t,a.target)}"> <typo3-backend-icon identifier="actions-chevron-${a.class}" size="small"></typo3-backend-icon> </button> <button class="btn btn-default" type="button" title="${lll("table_removeColumn")}" @@ -93,7 +93,7 @@ var __decorate=function(t,e,l,a){var o,i=arguments.length,n=i<3?e:null===a?a=Obj <typo3-backend-icon identifier="actions-plus" size="small"></typo3-backend-icon> </button> </span> - `}showTableConfigurationModal(t){const e=this.firstRow.length,l=this.table.length,a=l||1,o=e||1,i=Modal.advanced({content:"",title:lll("table_setCountHeadline"),severity:SeverityEnum.notice,size:Modal.sizes.small,buttons:[{text:lll("button.close")||"Close",active:!0,btnClass:"btn-default",name:"cancel",trigger:()=>Modal.dismiss()},{text:lll("table_buttonApply")||"Apply",btnClass:"btn-"+Severity.getCssClass(SeverityEnum.info),name:"apply",trigger:()=>{const a=i.querySelector("#t3js-expand-rows"),o=i.querySelector("#t3js-expand-cols");if(null!==a&&null!==o)if(a.checkValidity()&&o.checkValidity()){const i=Number(a.value)-l,n=Number(o.value)-e;this.setColAndRowCount(t,n,i),Modal.dismiss()}else a.reportValidity(),o.reportValidity()}}],callback:t=>{render(html` + `}showTableConfigurationModal(t){const e=this.firstRow.length,l=this.table.length,a=l||1,o=e||1,n=Modal.advanced({content:"",title:lll("table_setCountHeadline"),severity:SeverityEnum.notice,size:Modal.sizes.small,buttons:[{text:lll("button.close")||"Close",active:!0,btnClass:"btn-default",name:"cancel",trigger:()=>Modal.dismiss()},{text:lll("table_buttonApply")||"Apply",btnClass:"btn-"+Severity.getCssClass(SeverityEnum.info),name:"apply",trigger:()=>{const a=n.querySelector("#t3js-expand-rows"),o=n.querySelector("#t3js-expand-cols");if(null!==a&&null!==o)if(a.checkValidity()&&o.checkValidity()){const n=Number(a.value)-l,i=Number(o.value)-e;this.setColAndRowCount(t,i,n),Modal.dismiss()}else a.reportValidity(),o.reportValidity()}}],callback:t=>{render(html` <div class="form-group "> <label>${lll("table_rowCount")}</label> <input id="t3js-expand-rows" class="form-control" type="number" min="1" required value="${a}"> @@ -102,4 +102,4 @@ var __decorate=function(t,e,l,a){var o,i=arguments.length,n=i<3?e:null===a?a=Obj <label>${lll("table_colCount")}</label> <input id="t3js-expand-cols" class="form-control" type="number" min="1" required value="${o}"> </div> - `,t.querySelector(".t3js-modal-body"))}})}showTableSyntax(t){const e=Modal.advanced({content:"",title:lll("table_showCode"),severity:SeverityEnum.notice,size:Modal.sizes.small,buttons:[{text:lll("button.close")||"Close",active:!0,btnClass:"btn-default",name:"cancel",trigger:()=>Modal.dismiss()},{text:lll("table_buttonApply")||"Apply",btnClass:"btn-"+Severity.getCssClass(SeverityEnum.info),name:"apply",trigger:()=>{document.querySelector(this.selectorData).value=e.querySelector("textarea").value,this.readTableFromTextarea(),this.requestUpdate(),Modal.dismiss()}}],callback:t=>{let e=document.querySelector(this.selectorData);render(html`<textarea style="width: 100%;">${e.value}</textarea>`,t.querySelector(".t3js-modal-body"))}})}setColAndRowCount(t,e,l){const a=this.table.length;if(l>0)for(let e=0;e<l;e++)this.appendRow(t,a);else for(let e=0;e<Math.abs(l);e++)this.removeRow(t,this.table.length-1);if(e>0)for(let l=0;l<e;l++)this.appendColumn(t,e);else for(let l=0;l<Math.abs(e);l++)this.removeColumn(t,this.firstRow.length-1)}};__decorate([property({type:String})],TableWizardElement.prototype,"type",void 0),__decorate([property({type:String})],TableWizardElement.prototype,"selectorData",void 0),__decorate([property({type:String})],TableWizardElement.prototype,"delimiter",void 0),__decorate([property({type:String})],TableWizardElement.prototype,"enclosure",void 0),__decorate([property({type:Number,attribute:"append-rows"})],TableWizardElement.prototype,"appendRows",void 0),__decorate([property({type:Object})],TableWizardElement.prototype,"l10n",void 0),TableWizardElement=__decorate([customElement("typo3-backend-table-wizard")],TableWizardElement);export{TableWizardElement}; \ No newline at end of file + `,t.querySelector(".t3js-modal-body"))}})}showTableSyntax(){const t=Modal.advanced({content:"",title:lll("table_showCode"),severity:SeverityEnum.notice,size:Modal.sizes.small,buttons:[{text:lll("button.close")||"Close",active:!0,btnClass:"btn-default",name:"cancel",trigger:()=>Modal.dismiss()},{text:lll("table_buttonApply")||"Apply",btnClass:"btn-"+Severity.getCssClass(SeverityEnum.info),name:"apply",trigger:()=>{document.querySelector(this.selectorData).value=t.querySelector("textarea").value,this.readTableFromTextarea(),this.requestUpdate(),Modal.dismiss()}}],callback:t=>{const e=document.querySelector(this.selectorData);render(html`<textarea style="width: 100%;">${e.value}</textarea>`,t.querySelector(".t3js-modal-body"))}})}setColAndRowCount(t,e,l){const a=this.table.length;if(l>0)for(let e=0;e<l;e++)this.appendRow(t,a);else for(let e=0;e<Math.abs(l);e++)this.removeRow(t,this.table.length-1);if(e>0)for(let l=0;l<e;l++)this.appendColumn(t,e);else for(let l=0;l<Math.abs(e);l++)this.removeColumn(t,this.firstRow.length-1)}};__decorate([property({type:String})],TableWizardElement.prototype,"type",void 0),__decorate([property({type:String})],TableWizardElement.prototype,"selectorData",void 0),__decorate([property({type:String})],TableWizardElement.prototype,"delimiter",void 0),__decorate([property({type:String})],TableWizardElement.prototype,"enclosure",void 0),__decorate([property({type:Number,attribute:"append-rows"})],TableWizardElement.prototype,"appendRows",void 0),TableWizardElement=__decorate([customElement("typo3-backend-table-wizard")],TableWizardElement);export{TableWizardElement}; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/file-link-handler.js b/typo3/sysext/backend/Resources/Public/JavaScript/file-link-handler.js index e7861bdcbe13..684f5f1828f9 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/file-link-handler.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/file-link-handler.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import LinkBrowser from"@typo3/backend/link-browser.js";import RegularEvent from"@typo3/core/event/regular-event.js";class FileLinkHandler{constructor(){new RegularEvent("click",((e,n)=>{e.preventDefault(),LinkBrowser.finalizeFunction(n.getAttribute("href"))})).delegateTo(document,"a.t3js-fileLink"),new RegularEvent("click",((e,n)=>{e.preventDefault(),LinkBrowser.finalizeFunction(document.body.dataset.currentLink)})).delegateTo(document,"input.t3js-linkCurrent")}}export default new FileLinkHandler; \ No newline at end of file +import LinkBrowser from"@typo3/backend/link-browser.js";import RegularEvent from"@typo3/core/event/regular-event.js";class FileLinkHandler{constructor(){new RegularEvent("click",((e,n)=>{e.preventDefault(),LinkBrowser.finalizeFunction(n.getAttribute("href"))})).delegateTo(document,"a.t3js-fileLink"),new RegularEvent("click",(e=>{e.preventDefault(),LinkBrowser.finalizeFunction(document.body.dataset.currentLink)})).delegateTo(document,"input.t3js-linkCurrent")}}export default new FileLinkHandler; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-review.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-review.js index 3cdd04ec88c4..26c7c29a1f9f 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-review.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-review.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import"bootstrap";import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import FormEngine from"@typo3/backend/form-engine.js";import"@typo3/backend/element/icon-element.js";import Popover from"@typo3/backend/popover.js";class FormEngineReview{constructor(){this.toggleButtonClass="t3js-toggle-review-panel",this.labelSelector=".t3js-formengine-label",this.checkForReviewableField=()=>{const e=this,t=FormEngineReview.findInvalidField(),o=document.querySelector("."+this.toggleButtonClass);if(null!==o)if(t.length>0){const i=$("<div />",{class:"list-group"});t.each((function(){const t=$(this),o=t.find("[data-formengine-validation-rules]"),n=document.createElement("a");n.classList.add("list-group-item"),n.href="#",n.textContent=t.find(e.labelSelector).text(),n.addEventListener("click",(t=>e.switchToField(t,o))),i.append(n)})),o.classList.remove("hidden"),Popover.setOptions(o,{html:!0,content:i[0]})}else o.classList.add("hidden"),Popover.hide(o)},this.switchToField=(e,t)=>{e.preventDefault();e.currentTarget;t.parents('[id][role="tabpanel"]').each((function(){$('[aria-controls="'+$(this).attr("id")+'"]').tab("show")})),t.focus()},this.initialize()}static findInvalidField(){return $(document).find(".tab-content ."+FormEngine.Validation.errorClass)}static attachButtonToModuleHeader(e){const t=document.querySelector(".t3js-module-docheader-bar-buttons").lastElementChild.querySelector('[role="toolbar"]'),o=document.createElement("typo3-backend-icon");o.setAttribute("identifier","actions-info"),o.setAttribute("size","small");const i=document.createElement("button");i.type="button",i.classList.add("btn","btn-danger","btn-sm","hidden",e.toggleButtonClass),i.title=TYPO3.lang["buttons.reviewFailedValidationFields"],i.appendChild(o),Popover.popover(i),t.prepend(i)}initialize(){const e=this,t=$(document);DocumentService.ready().then((()=>{FormEngineReview.attachButtonToModuleHeader(e)})),t.on("t3-formengine-postfieldvalidation",this.checkForReviewableField)}}export default new FormEngineReview; \ No newline at end of file +import"bootstrap";import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import FormEngine from"@typo3/backend/form-engine.js";import"@typo3/backend/element/icon-element.js";import Popover from"@typo3/backend/popover.js";class FormEngineReview{constructor(){this.toggleButtonClass="t3js-toggle-review-panel",this.labelSelector=".t3js-formengine-label",this.checkForReviewableField=()=>{const e=this,t=FormEngineReview.findInvalidField(),o=document.querySelector("."+this.toggleButtonClass);if(null!==o)if(t.length>0){const i=$("<div />",{class:"list-group"});t.each((function(){const t=$(this),o=t.find("[data-formengine-validation-rules]"),n=document.createElement("a");n.classList.add("list-group-item"),n.href="#",n.textContent=t.find(e.labelSelector).text(),n.addEventListener("click",(t=>e.switchToField(t,o))),i.append(n)})),o.classList.remove("hidden"),Popover.setOptions(o,{html:!0,content:i[0]})}else o.classList.add("hidden"),Popover.hide(o)},this.switchToField=(e,t)=>{e.preventDefault(),t.parents('[id][role="tabpanel"]').each((function(){$('[aria-controls="'+$(this).attr("id")+'"]').tab("show")})),t.focus()},this.initialize()}static findInvalidField(){return $(document).find(".tab-content ."+FormEngine.Validation.errorClass)}static attachButtonToModuleHeader(e){const t=document.querySelector(".t3js-module-docheader-bar-buttons").lastElementChild.querySelector('[role="toolbar"]'),o=document.createElement("typo3-backend-icon");o.setAttribute("identifier","actions-info"),o.setAttribute("size","small");const i=document.createElement("button");i.type="button",i.classList.add("btn","btn-danger","btn-sm","hidden",e.toggleButtonClass),i.title=TYPO3.lang["buttons.reviewFailedValidationFields"],i.appendChild(o),Popover.popover(i),t.prepend(i)}initialize(){const e=this,t=$(document);DocumentService.ready().then((()=>{FormEngineReview.attachButtonToModuleHeader(e)})),t.on("t3-formengine-postfieldvalidation",this.checkForReviewableField)}}export default new FormEngineReview; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-validation.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-validation.js index 6bb768834170..7a70b5bb9479 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-validation.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-validation.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import{DateTime}from"luxon";import Md5 from"@typo3/backend/hashing/md5.js";import DocumentSaveActions from"@typo3/backend/document-save-actions.js";import Modal from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";export default(function(){const FormEngineValidation={rulesSelector:"[data-formengine-validation-rules]",inputSelector:"[data-formengine-input-params]",markerSelector:".t3js-formengine-validation-marker",groupFieldHiddenElement:".t3js-formengine-field-group input[type=hidden]",relatedFieldSelector:"[data-relatedfieldname]",errorClass:"has-error",lastYear:0,lastDate:0,lastTime:0,passwordDummy:"********"},customEvaluations=new Map;return FormEngineValidation.initialize=function(){$(document).find("."+FormEngineValidation.errorClass).removeClass(FormEngineValidation.errorClass),FormEngineValidation.initializeInputFields().promise().done((function(){$(document).on("change",FormEngineValidation.rulesSelector,(e=>{FormEngineValidation.validateField(e.currentTarget),FormEngineValidation.markFieldAsChanged(e.currentTarget)})),FormEngineValidation.registerSubmitCallback()}));const e=new Date;FormEngineValidation.lastYear=FormEngineValidation.getYear(e),FormEngineValidation.lastDate=FormEngineValidation.getDate(e),FormEngineValidation.lastTime=0,FormEngineValidation.validate()},FormEngineValidation.initializeInputFields=function(){return $(document).find(FormEngineValidation.inputSelector).each((function(e,n){const t=$(n).data("formengine-input-params"),a=t.field,i=$('[name="'+a+'"]');void 0===i.data("main-field")&&(i.data("main-field",a),i.data("config",t),FormEngineValidation.initializeInputField(a))}))},FormEngineValidation.initializeInputField=function(e){const n=$('[name="'+e+'"]'),t=$('[data-formengine-input-name="'+e+'"]');let a=$('[name="'+n.data("main-field")+'"]');0===a.length&&(a=n);const i=a.data("config");if(void 0!==i){const e=FormEngineValidation.trimExplode(",",i.evalList);let a=n.val();for(let n=0;n<e.length;n++)a=FormEngineValidation.formatValue(e[n],a,i);a.length&&t.val(a)}t.data("main-field",e),t.data("config",i),t.on("change",(function(){FormEngineValidation.updateInputField(t.attr("data-formengine-input-name"))})),t.attr("data-formengine-input-initialized","true")},FormEngineValidation.registerCustomEvaluation=function(e,n){customEvaluations.has(e)||customEvaluations.set(e,n)},FormEngineValidation.formatValue=function(e,n,t){let a,i,o="";switch(e){case"date":if(n.toString().indexOf("-")>0){o=DateTime.fromISO(n.toString(),{zone:"utc"}).toFormat("dd-MM-yyyy")}else{if(a=1*n,!a)return"";i=new Date(1e3*a);o=i.getUTCDate().toString(10).padStart(2,"0")+"-"+(i.getUTCMonth()+1).toString(10).padStart(2,"0")+"-"+this.getYear(i)}break;case"datetime":if(n.toString().indexOf("-")<=0&&!("number"==typeof n?n:parseInt(n)))return"";o=FormEngineValidation.formatValue("time",n,t)+" "+FormEngineValidation.formatValue("date",n,t);break;case"time":case"timesec":let r;if(n.toString().indexOf("-")>0)r=DateTime.fromISO(n.toString(),{zone:"utc"});else{if(a="number"==typeof n?n:parseInt(n),!a&&"0"!==n.toString())return"";r=DateTime.fromSeconds(a,{zone:"utc"})}o="timesec"===e?r.toFormat("HH:mm:ss"):r.toFormat("HH:mm");break;case"password":o=n?FormEngineValidation.passwordDummy:"";break;default:o=n}return o},FormEngineValidation.updateInputField=function(e){const n=$('[name="'+e+'"]');let t=$('[name="'+n.data("main-field")+'"]');0===t.length&&(t=n);const a=$('[data-formengine-input-name="'+t.attr("name")+'"]'),i=t.data("config");if(void 0!==i){const e=FormEngineValidation.trimExplode(",",i.evalList);let n=a.val();for(let t=0;t<e.length;t++)n=FormEngineValidation.processValue(e[t],n,i);let o=n;for(let n=0;n<e.length;n++)o=FormEngineValidation.formatValue(e[n],o,i);t.val(n),t.get(0).dispatchEvent(new Event("change")),a.val(o)}},FormEngineValidation.validateField=function(e,n){const t=e instanceof $?e.get(0):e;if(n=n||t.value||"",void 0===t.dataset.formengineValidationRules)return n;const a=JSON.parse(t.dataset.formengineValidationRules);let i,o,r,l=!1,s=0,m=n;Array.isArray(n)||(n=n.trimStart());for(let e of a){if(l)break;switch(e.type){case"required":""===n&&(l=!0,t.closest(FormEngineValidation.markerSelector).classList.add(FormEngineValidation.errorClass));break;case"range":if(""!==n){if((e.minItems||e.maxItems)&&(i=$(document).find('[name="'+t.dataset.relatedfieldname+'"]'),s=i.length?FormEngineValidation.trimExplode(",",i.val()).length:t.value,void 0!==e.minItems&&(o=1*e.minItems,!isNaN(o)&&s<o&&(l=!0)),void 0!==e.maxItems&&(r=1*e.maxItems,!isNaN(r)&&s>r&&(l=!0))),void 0!==e.lower){const t=1*e.lower;!isNaN(t)&&n<t&&(l=!0)}if(void 0!==e.upper){const t=1*e.upper;!isNaN(t)&&n>t&&(l=!0)}}break;case"select":case"category":(e.minItems||e.maxItems)&&(i=$(document).find('[name="'+t.dataset.relatedfieldname+'"]'),s=i.length?FormEngineValidation.trimExplode(",",i.val()).length:t instanceof HTMLSelectElement?t.querySelectorAll("option:checked").length:t.querySelectorAll("input[value]:checked").length,void 0!==e.minItems&&(o=1*e.minItems,!isNaN(o)&&s<o&&(l=!0)),void 0!==e.maxItems&&(r=1*e.maxItems,!isNaN(r)&&s>r&&(l=!0)));break;case"group":case"inline":(e.minItems||e.maxItems)&&(s=FormEngineValidation.trimExplode(",",t.value).length,void 0!==e.minItems&&(o=1*e.minItems,!isNaN(o)&&s<o&&(l=!0)),void 0!==e.maxItems&&(r=1*e.maxItems,!isNaN(r)&&s>r&&(l=!0)));break;case"min":(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement)&&t.value.length>0&&t.value.length<t.minLength&&(l=!0)}}const d=!l,c=t.closest(FormEngineValidation.markerSelector);return null!==c&&c.classList.toggle(FormEngineValidation.errorClass,!d),FormEngineValidation.markParentTab($(t),d),$(document).trigger("t3-formengine-postfieldvalidation"),m},FormEngineValidation.processValue=function(e,n,t){let a="",i="",o="",r=0,l=n;switch(e){case"alpha":case"num":case"alphanum":case"alphanum_x":for(a="",r=0;r<n.length;r++){const t=n.substr(r,1);let i="_"===t||"-"===t,o=t>="a"&&t<="z"||t>="A"&&t<="Z",l=t>="0"&&t<="9";switch(e){case"alphanum":i=!1;break;case"alpha":l=!1,i=!1;break;case"num":o=!1,i=!1}(o||l||i)&&(a+=t)}a!==n&&(l=a);break;case"is_in":if(t.is_in){i=""+n,t.is_in=t.is_in.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&");const e=new RegExp("[^"+t.is_in+"]+","g");a=i.replace(e,"")}else a=i;l=a;break;case"nospace":l=(""+n).replace(/ /g,"");break;case"md5":""!==n&&(l=Md5.hash(n));break;case"upper":l=n.toUpperCase();break;case"lower":l=n.toLowerCase();break;case"integer":""!==n&&(l=FormEngineValidation.parseInt(n));break;case"decimal":""!==n&&(l=FormEngineValidation.parseDouble(n));break;case"trim":l=String(n).trim();break;case"datetime":""!==n&&(o=n.substr(0,1),l=FormEngineValidation.parseDateTime(n));break;case"date":""!==n&&(o=n.substr(0,1),l=FormEngineValidation.parseDate(n));break;case"time":case"timesec":""!==n&&(o=n.substr(0,1),l=FormEngineValidation.parseTime(n,e));break;case"year":""!==n&&(o=n.substr(0,1),l=FormEngineValidation.parseYear(n));break;case"null":case"password":break;default:customEvaluations.has(e)?l=customEvaluations.get(e).call(null,n):"object"==typeof TBE_EDITOR&&void 0!==TBE_EDITOR.customEvalFunctions&&"function"==typeof TBE_EDITOR.customEvalFunctions[e]&&(l=TBE_EDITOR.customEvalFunctions[e](n))}return l},FormEngineValidation.validate=function(e){(void 0===e||e instanceof Document)&&$(document).find(FormEngineValidation.markerSelector+", .t3js-tabmenu-item").removeClass(FormEngineValidation.errorClass).removeClass("has-validation-error");const n=e||document;for(let e of n.querySelectorAll(FormEngineValidation.rulesSelector)){const n=$(e);if(!n.closest(".t3js-flex-section-deleted, .t3js-inline-record-deleted, .t3js-file-reference-deleted").length){let e=!1;const t=n.val(),a=FormEngineValidation.validateField(n,t);if(Array.isArray(a)&&Array.isArray(t)){if(a.length!==t.length)e=!0;else for(let n=0;n<a.length;n++)if(a[n]!==t[n]){e=!0;break}}else a.length&&t!==a&&(e=!0);e&&n.val(a)}}},FormEngineValidation.markFieldAsChanged=function(e){if(e instanceof $&&(e=e.get(0)),!(e instanceof HTMLElement))return;const n=e.closest(".t3js-formengine-palette-field");null!==n&&n.classList.add("has-change")},FormEngineValidation.trimExplode=function(e,n){const t=[],a=n.split(e);for(let e=0;e<a.length;e++){const n=a[e].trim();n.length>0&&t.push(n)}return t},FormEngineValidation.parseInt=function(e){let n;return e?(n=parseInt(""+e,10),isNaN(n)?0:n):0},FormEngineValidation.parseDouble=function(e,n=2){let t=""+e;t=t.replace(/[^0-9,\.-]/g,"");const a="-"===t.substring(0,1);t=t.replace(/-/g,""),t=t.replace(/,/g,"."),-1===t.indexOf(".")&&(t+=".0");const i=t.split("."),o=i.pop();let r=Number(i.join("")+"."+o);return a&&(r*=-1),t=r.toFixed(n),t},FormEngineValidation.parseDateTime=function(e){const n=e.indexOf(" ");if(-1!==n){const t=FormEngineValidation.parseDate(e.substring(n+1));FormEngineValidation.lastTime=t+FormEngineValidation.parseTime(e.substring(0,n),"time")}else FormEngineValidation.lastTime=FormEngineValidation.parseDate(e);return FormEngineValidation.lastTime},FormEngineValidation.parseDate=function(e){return FormEngineValidation.lastDate=DateTime.fromFormat(e,"dd-MM-yyyy",{zone:"utc"}).toUnixInteger(),FormEngineValidation.lastDate},FormEngineValidation.parseTime=function(e,n){const t="timesec"===n?"HH:mm:ss":"HH:mm";return FormEngineValidation.lastTime=DateTime.fromFormat(e,t,{zone:"utc"}).set({year:1970,month:1,day:1}).toUnixInteger(),FormEngineValidation.lastTime<0&&(FormEngineValidation.lastTime+=86400),FormEngineValidation.lastTime},FormEngineValidation.parseYear=function(e){let n=parseInt(e,10);return isNaN(n)&&(n=FormEngineValidation.getYear(new Date)),FormEngineValidation.lastYear=n,FormEngineValidation.lastYear},FormEngineValidation.getYear=function(e){return null===e?null:e.getUTCFullYear()},FormEngineValidation.getDate=function(e){const n=new Date(FormEngineValidation.getYear(e),e.getUTCMonth(),e.getUTCDate());return FormEngineValidation.getTimestamp(n)},FormEngineValidation.pol=function(foreign,value){return eval(("-"==foreign?"-":"")+value)},FormEngineValidation.getTimestamp=function(e){return Date.parse(e instanceof Date?e.toISOString():e)/1e3},FormEngineValidation.getTime=function(e){return 60*e.getUTCHours()*60+60*e.getUTCMinutes()+FormEngineValidation.getSecs(e)},FormEngineValidation.getSecs=function(e){return e.getUTCSeconds()},FormEngineValidation.getTimeSecs=function(e){return 60*e.getHours()*60+60*e.getMinutes()+e.getSeconds()},FormEngineValidation.markParentTab=function(e,n){e.parents(".tab-pane").each((function(e,t){const a=$(t);n&&(n=0===a.find(".has-error").length);const i=a.attr("id");$(document).find('a[href="#'+i+'"]').closest(".t3js-tabmenu-item").toggleClass("has-validation-error",!n)}))},FormEngineValidation.registerSubmitCallback=function(){DocumentSaveActions.getInstance().addPreSubmitCallback((function(e){if($("."+FormEngineValidation.errorClass).length>0){const n=Modal.confirm(TYPO3.lang.alert||"Alert",TYPO3.lang["FormEngine.fieldsMissing"],Severity.error,[{text:TYPO3.lang["button.ok"]||"OK",active:!0,btnClass:"btn-default",name:"ok"}]);n.addEventListener("button.clicked",(()=>n.hideModal())),e.stopImmediatePropagation()}}))},FormEngineValidation}()); \ No newline at end of file +import $ from"jquery";import{DateTime}from"luxon";import Md5 from"@typo3/backend/hashing/md5.js";import DocumentSaveActions from"@typo3/backend/document-save-actions.js";import Modal from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";export default(function(){const FormEngineValidation={rulesSelector:"[data-formengine-validation-rules]",inputSelector:"[data-formengine-input-params]",markerSelector:".t3js-formengine-validation-marker",groupFieldHiddenElement:".t3js-formengine-field-group input[type=hidden]",relatedFieldSelector:"[data-relatedfieldname]",errorClass:"has-error",lastYear:0,lastDate:0,lastTime:0,passwordDummy:"********"},customEvaluations=new Map;return FormEngineValidation.initialize=function(){$(document).find("."+FormEngineValidation.errorClass).removeClass(FormEngineValidation.errorClass),FormEngineValidation.initializeInputFields().promise().done((function(){$(document).on("change",FormEngineValidation.rulesSelector,(e=>{FormEngineValidation.validateField(e.currentTarget),FormEngineValidation.markFieldAsChanged(e.currentTarget)})),FormEngineValidation.registerSubmitCallback()}));const e=new Date;FormEngineValidation.lastYear=FormEngineValidation.getYear(e),FormEngineValidation.lastDate=FormEngineValidation.getDate(e),FormEngineValidation.lastTime=0,FormEngineValidation.validate()},FormEngineValidation.initializeInputFields=function(){return $(document).find(FormEngineValidation.inputSelector).each((function(e,n){const t=$(n).data("formengine-input-params"),a=t.field,i=$('[name="'+a+'"]');void 0===i.data("main-field")&&(i.data("main-field",a),i.data("config",t),FormEngineValidation.initializeInputField(a))}))},FormEngineValidation.initializeInputField=function(e){const n=$('[name="'+e+'"]'),t=$('[data-formengine-input-name="'+e+'"]');let a=$('[name="'+n.data("main-field")+'"]');0===a.length&&(a=n);const i=a.data("config");if(void 0!==i){const e=FormEngineValidation.trimExplode(",",i.evalList);let a=n.val();for(let n=0;n<e.length;n++)a=FormEngineValidation.formatValue(e[n],a,i);a.length&&t.val(a)}t.data("main-field",e),t.data("config",i),t.on("change",(function(){FormEngineValidation.updateInputField(t.attr("data-formengine-input-name"))})),t.attr("data-formengine-input-initialized","true")},FormEngineValidation.registerCustomEvaluation=function(e,n){customEvaluations.has(e)||customEvaluations.set(e,n)},FormEngineValidation.formatValue=function(e,n,t){let a,i,o="";switch(e){case"date":if(n.toString().indexOf("-")>0){o=DateTime.fromISO(n.toString(),{zone:"utc"}).toFormat("dd-MM-yyyy")}else{if(a=parseInt(n.toString(),10),!a)return"";i=new Date(1e3*a);o=i.getUTCDate().toString(10).padStart(2,"0")+"-"+(i.getUTCMonth()+1).toString(10).padStart(2,"0")+"-"+this.getYear(i)}break;case"datetime":if(n.toString().indexOf("-")<=0&&!("number"==typeof n?n:parseInt(n)))return"";o=FormEngineValidation.formatValue("time",n,t)+" "+FormEngineValidation.formatValue("date",n,t);break;case"time":case"timesec":let r;if(n.toString().indexOf("-")>0)r=DateTime.fromISO(n.toString(),{zone:"utc"});else{if(a="number"==typeof n?n:parseInt(n),!a&&"0"!==n.toString())return"";r=DateTime.fromSeconds(a,{zone:"utc"})}o="timesec"===e?r.toFormat("HH:mm:ss"):r.toFormat("HH:mm");break;case"password":o=n?FormEngineValidation.passwordDummy:"";break;default:o=n.toString()}return o},FormEngineValidation.updateInputField=function(e){const n=$('[name="'+e+'"]');let t=$('[name="'+n.data("main-field")+'"]');0===t.length&&(t=n);const a=$('[data-formengine-input-name="'+t.attr("name")+'"]'),i=t.data("config");if(void 0!==i){const e=FormEngineValidation.trimExplode(",",i.evalList);let n=a.val();for(let t=0;t<e.length;t++)n=FormEngineValidation.processValue(e[t],n,i);let o=n;for(let n=0;n<e.length;n++)o=FormEngineValidation.formatValue(e[n],o,i);t.val(n),t.get(0).dispatchEvent(new Event("change")),a.val(o)}},FormEngineValidation.validateField=function(e,n){const t=e instanceof $?e.get(0):e;if(n=n||t.value||"",void 0===t.dataset.formengineValidationRules)return n;const a=JSON.parse(t.dataset.formengineValidationRules);let i=!1,o=0;const r=n;let l,s,m;Array.isArray(n)||(n=n.trimStart());for(const e of a){if(i)break;switch(e.type){case"required":""===n&&(i=!0,t.closest(FormEngineValidation.markerSelector).classList.add(FormEngineValidation.errorClass));break;case"range":if(""!==n){if((e.minItems||e.maxItems)&&(l=$(document).find('[name="'+t.dataset.relatedfieldname+'"]'),o=l.length?FormEngineValidation.trimExplode(",",l.val()).length:parseInt(t.value,10),void 0!==e.minItems&&(s=1*e.minItems,!isNaN(s)&&o<s&&(i=!0)),void 0!==e.maxItems&&(m=1*e.maxItems,!isNaN(m)&&o>m&&(i=!0))),void 0!==e.lower){const t=1*e.lower;!isNaN(t)&&parseInt(n,10)<t&&(i=!0)}if(void 0!==e.upper){const t=1*e.upper;!isNaN(t)&&parseInt(n,10)>t&&(i=!0)}}break;case"select":case"category":(e.minItems||e.maxItems)&&(l=$(document).find('[name="'+t.dataset.relatedfieldname+'"]'),o=l.length?FormEngineValidation.trimExplode(",",l.val()).length:t instanceof HTMLSelectElement?t.querySelectorAll("option:checked").length:t.querySelectorAll("input[value]:checked").length,void 0!==e.minItems&&(s=1*e.minItems,!isNaN(s)&&o<s&&(i=!0)),void 0!==e.maxItems&&(m=1*e.maxItems,!isNaN(m)&&o>m&&(i=!0)));break;case"group":case"inline":(e.minItems||e.maxItems)&&(o=FormEngineValidation.trimExplode(",",t.value).length,void 0!==e.minItems&&(s=1*e.minItems,!isNaN(s)&&o<s&&(i=!0)),void 0!==e.maxItems&&(m=1*e.maxItems,!isNaN(m)&&o>m&&(i=!0)));break;case"min":(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement)&&t.value.length>0&&t.value.length<t.minLength&&(i=!0)}}const d=!i,c=t.closest(FormEngineValidation.markerSelector);return null!==c&&c.classList.toggle(FormEngineValidation.errorClass,!d),FormEngineValidation.markParentTab($(t),d),$(document).trigger("t3-formengine-postfieldvalidation"),r},FormEngineValidation.processValue=function(e,n,t){let a="",i="",o=0,r=n;switch(e){case"alpha":case"num":case"alphanum":case"alphanum_x":for(a="",o=0;o<n.length;o++){const t=n.substr(o,1);let i="_"===t||"-"===t,r=t>="a"&&t<="z"||t>="A"&&t<="Z",l=t>="0"&&t<="9";switch(e){case"alphanum":i=!1;break;case"alpha":l=!1,i=!1;break;case"num":r=!1,i=!1}(r||l||i)&&(a+=t)}a!==n&&(r=a);break;case"is_in":if(t.is_in){i=""+n,t.is_in=t.is_in.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&");const e=new RegExp("[^"+t.is_in+"]+","g");a=i.replace(e,"")}else a=i;r=a;break;case"nospace":r=(""+n).replace(/ /g,"");break;case"md5":""!==n&&(r=Md5.hash(n));break;case"upper":r=n.toUpperCase();break;case"lower":r=n.toLowerCase();break;case"integer":""!==n&&(r=FormEngineValidation.parseInt(n));break;case"decimal":""!==n&&(r=FormEngineValidation.parseDouble(n));break;case"trim":r=String(n).trim();break;case"datetime":""!==n&&(r=FormEngineValidation.parseDateTime(n));break;case"date":""!==n&&(r=FormEngineValidation.parseDate(n));break;case"time":case"timesec":""!==n&&(r=FormEngineValidation.parseTime(n,e));break;case"year":""!==n&&(r=FormEngineValidation.parseYear(n));break;case"null":case"password":break;default:customEvaluations.has(e)?r=customEvaluations.get(e).call(null,n):"object"==typeof TBE_EDITOR&&void 0!==TBE_EDITOR.customEvalFunctions&&"function"==typeof TBE_EDITOR.customEvalFunctions[e]&&(r=TBE_EDITOR.customEvalFunctions[e](n))}return r},FormEngineValidation.validate=function(e){(void 0===e||e instanceof Document)&&$(document).find(FormEngineValidation.markerSelector+", .t3js-tabmenu-item").removeClass(FormEngineValidation.errorClass).removeClass("has-validation-error");const n=e||document;for(const e of n.querySelectorAll(FormEngineValidation.rulesSelector)){const n=$(e);if(!n.closest(".t3js-flex-section-deleted, .t3js-inline-record-deleted, .t3js-file-reference-deleted").length){let e=!1;const t=n.val(),a=FormEngineValidation.validateField(n,t);if(Array.isArray(a)&&Array.isArray(t)){if(a.length!==t.length)e=!0;else for(let n=0;n<a.length;n++)if(a[n]!==t[n]){e=!0;break}}else a.length&&t!==a&&(e=!0);e&&n.val(a)}}},FormEngineValidation.markFieldAsChanged=function(e){if(e instanceof $&&(e=e.get(0)),!(e instanceof HTMLElement))return;const n=e.closest(".t3js-formengine-palette-field");null!==n&&n.classList.add("has-change")},FormEngineValidation.trimExplode=function(e,n){const t=[],a=n.split(e);for(let e=0;e<a.length;e++){const n=a[e].trim();n.length>0&&t.push(n)}return t},FormEngineValidation.parseInt=function(e){if(!e)return 0;const n=parseInt(""+e,10);return isNaN(n)?0:n},FormEngineValidation.parseDouble=function(e,n=2){let t=""+e;t=t.replace(/[^0-9,.-]/g,"");const a="-"===t.substring(0,1);t=t.replace(/-/g,""),t=t.replace(/,/g,"."),-1===t.indexOf(".")&&(t+=".0");const i=t.split("."),o=i.pop();let r=Number(i.join("")+"."+o);return a&&(r*=-1),t=r.toFixed(n),t},FormEngineValidation.parseDateTime=function(e){const n=e.indexOf(" ");if(-1!==n){const t=FormEngineValidation.parseDate(e.substring(n+1));FormEngineValidation.lastTime=t+FormEngineValidation.parseTime(e.substring(0,n),"time")}else FormEngineValidation.lastTime=FormEngineValidation.parseDate(e);return FormEngineValidation.lastTime},FormEngineValidation.parseDate=function(e){return FormEngineValidation.lastDate=DateTime.fromFormat(e,"dd-MM-yyyy",{zone:"utc"}).toUnixInteger(),FormEngineValidation.lastDate},FormEngineValidation.parseTime=function(e,n){const t="timesec"===n?"HH:mm:ss":"HH:mm";return FormEngineValidation.lastTime=DateTime.fromFormat(e,t,{zone:"utc"}).set({year:1970,month:1,day:1}).toUnixInteger(),FormEngineValidation.lastTime<0&&(FormEngineValidation.lastTime+=86400),FormEngineValidation.lastTime},FormEngineValidation.parseYear=function(e){let n=parseInt(e,10);return isNaN(n)&&(n=FormEngineValidation.getYear(new Date)),FormEngineValidation.lastYear=n,FormEngineValidation.lastYear},FormEngineValidation.getYear=function(e){return null===e?null:e.getUTCFullYear()},FormEngineValidation.getDate=function(e){const n=new Date(FormEngineValidation.getYear(e),e.getUTCMonth(),e.getUTCDate());return FormEngineValidation.getTimestamp(n)},FormEngineValidation.pol=function(foreign,value){return eval(("-"==foreign?"-":"")+value)},FormEngineValidation.getTimestamp=function(e){return Date.parse(e instanceof Date?e.toISOString():e)/1e3},FormEngineValidation.getTime=function(e){return 60*e.getUTCHours()*60+60*e.getUTCMinutes()+FormEngineValidation.getSecs(e)},FormEngineValidation.getSecs=function(e){return e.getUTCSeconds()},FormEngineValidation.getTimeSecs=function(e){return 60*e.getHours()*60+60*e.getMinutes()+e.getSeconds()},FormEngineValidation.markParentTab=function(e,n){e.parents(".tab-pane").each((function(e,t){const a=$(t);n&&(n=0===a.find(".has-error").length);const i=a.attr("id");$(document).find('a[href="#'+i+'"]').closest(".t3js-tabmenu-item").toggleClass("has-validation-error",!n)}))},FormEngineValidation.registerSubmitCallback=function(){DocumentSaveActions.getInstance().addPreSubmitCallback((function(e){if($("."+FormEngineValidation.errorClass).length>0){const n=Modal.confirm(TYPO3.lang.alert||"Alert",TYPO3.lang["FormEngine.fieldsMissing"],Severity.error,[{text:TYPO3.lang["button.ok"]||"OK",active:!0,btnClass:"btn-default",name:"ok"}]);n.addEventListener("button.clicked",(()=>n.hideModal())),e.stopImmediatePropagation()}}))},FormEngineValidation}()); \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine.js index 42d37fbb36b0..1715fc43d1c2 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import FormEngineValidation from"@typo3/backend/form-engine-validation.js";import Icons from"@typo3/backend/icons.js";import{default as Modal}from"@typo3/backend/modal.js";import*as MessageUtility from"@typo3/backend/utility/message-utility.js";import Severity from"@typo3/backend/severity.js";import*as BackendExceptionModule from"@typo3/backend/backend-exception.js";import InteractionRequestMap from"@typo3/backend/event/interaction-request-map.js";import Utility from"@typo3/backend/utility.js";export default(function(){function e(e,t){t?n.interactionRequestMap.resolveFor(e):n.interactionRequestMap.rejectFor(e)}const t=new Map;t.set("typo3-backend-form-update-value",((e,t)=>{const n=document.querySelector('[name="'+CSS.escape(e.elementName)+'"]'),a=document.querySelector('[data-formengine-input-name="'+CSS.escape(e.elementName)+'"]');FormEngineValidation.updateInputField(e.elementName),null!==n&&(FormEngineValidation.markFieldAsChanged(n),FormEngineValidation.validateField(n)),null!==a&&a!==n&&FormEngineValidation.validateField(a)})),t.set("typo3-backend-form-reload",((e,t)=>{if(!e.confirmation)return void n.saveDocument();const a=Modal.advanced({title:TYPO3.lang["FormEngine.refreshRequiredTitle"],content:TYPO3.lang["FormEngine.refreshRequiredContent"],severity:Severity.warning,staticBackdrop:!0,buttons:[{text:TYPO3.lang["button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(Severity.warning),name:"ok",trigger:()=>{n.saveDocument()}}]});a.addEventListener("button.clicked",(()=>a.hideModal()))})),t.set("typo3-backend-form-update-bitmask",((e,t)=>{const n=t.target,a=document.editform[e.elementName],o=n.checked!==e.invert,i=Math.pow(2,e.position),r=Math.pow(2,e.total)-i-1;a.value=o?a.value|i:a.value&r,a.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))}));const n={consumeTypes:["typo3.setUrl","typo3.beforeSetUrl","typo3.refresh"],Validation:FormEngineValidation,interactionRequestMap:InteractionRequestMap,formName:TYPO3.settings.FormEngine.formName,openedPopupWindow:null,legacyFieldChangedCb:function(){!$.isFunction(TYPO3.settings.FormEngine.legacyFieldChangedCb)||TYPO3.settings.FormEngine.legacyFieldChangedCb()},browserUrl:"",openPopupWindow:function(e,t,a){return Modal.advanced({type:Modal.types.iframe,content:n.browserUrl+"&mode="+e+"&bparams="+t+(a?"&"+("db"===e?"expandPage":"expandFolder")+"="+a:""),size:Modal.sizes.large})},setSelectOptionFromExternalSource:function(e,t,a,o,i,r){i=String(i);let c,l,s,d=!1,u=!1;if(l=n.getFieldElement(e),c=l.get(0),s=l.get(0),null===s||"--div--"===t||s instanceof HTMLOptGroupElement)return;const m=n.getFieldElement(e,"_list",!0);if(m.length>0&&(l=m,c=l.get(0),d=l.prop("multiple")&&"1"!=l.prop("size"),u=!0),d||u){const u=n.getFieldElement(e,"_avail");if(!d){for(let e of c.querySelectorAll("option")){const t=u.find('option[value="'+$.escapeSelector($(e).attr("value"))+'"]');t.length&&(t.removeClass("hidden").prop("disabled",!1),n.enableOptGroup(t.get(0)))}l.empty()}if(i){let e=!1,a=new RegExp("(^|,)"+t+"($|,)");i.match(a)?(l.empty(),e=!0):1==l.find("option").length&&(a=new RegExp("(^|,)"+l.find("option").prop("value")+"($|,)"),i.match(a)&&(l.empty(),e=!0)),e&&void 0!==r&&r.closest("select").querySelectorAll("[disabled]").forEach((function(e){e.classList.remove("hidden"),e.disabled=!1,n.enableOptGroup(e)}))}let m=!0;const f=n.getFieldElement(e,"_mul",!0);if(0==f.length||0==f.val()){for(let e of c.querySelectorAll("option"))if(e.value==t){m=!1;break}if(m&&void 0!==r){r.classList.add("hidden"),r.disabled=!0;const e=r.parentElement;e instanceof HTMLOptGroupElement&&0===e.querySelectorAll("option:not([disabled]):not([hidden]):not(.hidden)").length&&(e.disabled=!0,e.classList.add("hidden"))}}if(m){const e=$("<option></option>");e.attr({value:t,title:o}).text(a),e.appendTo(l),n.updateHiddenFieldValueFromSelect(c,s),n.legacyFieldChangedCb(),FormEngineValidation.markFieldAsChanged(s),n.Validation.validateField(l),n.Validation.validateField(u)}}else{const e=/_(\d+)$/,a=t.toString().match(e);null!=a&&(t=a[1]),l.val(t),n.Validation.validateField(l)}},updateHiddenFieldValueFromSelect:function(e,t){const n=Array.from(e.options).map((e=>e.value));t.value=n.join(","),t.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))},getFormElement:function(e){const t=$('form[name="'+n.formName+'"]:first');if(!e)return t;{const a=n.getFieldElement(e),o=n.getFieldElement(e,"_list");if(a.length>0&&("select-one"===a.prop("type")||o.length>0&&o.prop("type").match(/select-(one|multiple)/)))return t;console.error("Form fields missing: form: "+n.formName+", field name: "+e),alert("Form field is invalid")}},getFieldElement:function(e,t,a){const o=$('form[name="'+n.formName+'"]:first');if(t){let n;switch(t){case"_list":n=$(':input[data-formengine-input-name="'+e+'"]:not([type=hidden])',o);break;case"_avail":n=$(':input[data-relatedfieldname="'+e+'"]',o);break;case"_mul":case"_hr":n=$(':input[type=hidden][data-formengine-input-name="'+e+'"]',o);break;default:n=null}if(n&&n.length>0||!0===a)return n}return $(':input[name="'+e+'"]',o)},initializeEvents:function(){top.TYPO3&&void 0!==top.TYPO3.Backend&&(top.TYPO3.Backend.consumerScope.attach(n),$(window).on("unload",(function(){top.TYPO3.Backend.consumerScope.detach(n)}))),$(document).on("click",".t3js-editform-close",(e=>{e.preventDefault(),n.preventExitIfNotSaved(n.preventExitIfNotSavedCallback)})).on("click",".t3js-editform-view",(e=>{e.preventDefault(),n.previewAction(e,n.previewActionCallback)})).on("click",".t3js-editform-new",(e=>{e.preventDefault(),n.newAction(e,n.newActionCallback)})).on("click",".t3js-editform-duplicate",(e=>{e.preventDefault(),n.duplicateAction(e,n.duplicateActionCallback)})).on("click",".t3js-editform-delete-record",(e=>{e.preventDefault(),n.deleteAction(e,n.deleteActionCallback)})).on("click",".t3js-editform-submitButton",(e=>{const t=$(e.currentTarget),n=t.data("name")||e.currentTarget.name,a=$("<input />").attr("type","hidden").attr("name",n).attr("value","1");t.parents("form").append(a)})).on("change",'.t3-form-field-eval-null-checkbox input[type="checkbox"]',(e=>{$(e.currentTarget).closest(".t3js-formengine-field-item").toggleClass("disabled")})).on("change",'.t3js-form-field-eval-null-placeholder-checkbox input[type="checkbox"]',(e=>{n.toggleCheckboxField($(e.currentTarget)),FormEngineValidation.markFieldAsChanged($(e.currentTarget))})).on("change",(function(e){$(".module-docheader-bar .btn").removeClass("disabled").prop("disabled",!1)})).on("click",".t3js-element-browser",(function(e){e.preventDefault(),e.stopPropagation();const t=$(e.currentTarget),a=t.data("mode"),o=t.data("params"),i=t.data("entryPoint");n.openPopupWindow(a,o,i)})).on("click",'[data-formengine-field-change-event="click"]',(e=>{const t=JSON.parse(e.currentTarget.dataset.formengineFieldChangeItems);n.processOnFieldChange(t,e)})).on("change",'[data-formengine-field-change-event="change"]',(e=>{const t=JSON.parse(e.currentTarget.dataset.formengineFieldChangeItems);n.processOnFieldChange(t,e)})),document.editform.addEventListener("submit",(function(){if(document.editform.closeDoc.value)return;const e=["button[form]",'button[name^="_save"]','a[data-name^="_save"]','button[name="CMD"][value^="save"]','a[data-name="CMD"][data-value^="save"]'].join(","),t=document.querySelector(e);null!==t&&(t.disabled=!0,Icons.getIcon("spinner-circle-dark",Icons.sizes.small).then((function(e){t.querySelector(".t3js-icon").outerHTML=e})))})),window.addEventListener("message",n.handlePostMessage)},consume:function(t){if(!t)throw new BackendExceptionModule.BackendException("No interaction request given",1496589980);const a=$.Deferred();if(t.concernsTypes(n.consumeTypes)){const o=t.outerMostRequest;n.interactionRequestMap.attachFor(o,a),o.isProcessed()?e(o,o.getProcessedData().response):n.hasChange()?n.preventExitIfNotSaved((function(t){o.setProcessedData({response:t}),e(o,t)})):n.interactionRequestMap.resolveFor(o)}return a},handlePostMessage:function(e){if(!MessageUtility.MessageUtility.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;if("typo3:elementBrowser:elementAdded"===e.data.actionName){if(void 0===e.data.fieldName)throw"fieldName not defined in message";if(void 0===e.data.value)throw"value not defined in message";const t=e.data.label||e.data.value,a=e.data.title||t,o=e.data.exclusiveValues||"";n.setSelectOptionFromExternalSource(e.data.fieldName,e.data.value,t,a,o)}},initializeRemainingCharacterViews:function(){const e=$("[maxlength]").not(".t3js-datetimepicker").not(".t3js-color-picker").not(".t3js-charcounter-initialized");e.on("focus",(e=>{const t=$(e.currentTarget),a=t.parents(".t3js-formengine-field-item:first"),o=n.getCharacterCounterProperties(t);let i=a.find(".t3js-charcounter-wrapper");i.length||(i=$("<div>"),i.addClass("t3js-charcounter-wrapper"),a.append(i)),i.append($("<div />",{class:"t3js-charcounter"}).append($("<span />",{class:o.labelClass}).text(TYPO3.lang["FormEngine.remainingCharacters"].replace("{0}",o.remainingCharacters))))})).on("blur",(e=>{$(e.currentTarget).parents(".t3js-formengine-field-item:first").find(".t3js-charcounter").remove()})).on("keyup",(e=>{const t=$(e.currentTarget),a=t.parents(".t3js-formengine-field-item:first"),o=n.getCharacterCounterProperties(t);a.find(".t3js-charcounter span").removeClass().addClass(o.labelClass).text(TYPO3.lang["FormEngine.remainingCharacters"].replace("{0}",o.remainingCharacters))})),e.addClass("t3js-charcounter-initialized")},getCharacterCounterProperties:function(e){const t=e.val(),n=e.attr("maxlength")-t.length-(t.match(/\n/g)||[]).length;let a="";return a=n<15?"badge-danger":n<30?"badge-warning":"badge-info",{remainingCharacters:n,labelClass:"badge "+a}},initializeMinimumCharactersLeftViews:function(){const e=(t,n)=>t&&(n(t)?t:e(t.parentNode,n)),t=(t,n)=>{const a=e(n.currentTarget,(e=>e.classList.contains("t3js-formengine-field-item"))),o=a.querySelector(".t3js-charcounter-min"),i=TYPO3.lang["FormEngine.minCharactersLeft"].replace("{0}",t);if(o)o.querySelector("span").innerHTML=i;else{const e=document.createElement("div");e.classList.add("t3js-charcounter-min");const t=document.createElement("span");t.classList.add("badge","badge-danger"),t.innerHTML=i,e.append(t);let n=a.querySelector(".t3js-charcounter-wrapper");n||(n=document.createElement("div"),n.classList.add("t3js-charcounter-wrapper"),a.append(n)),n.prepend(e)}},a=t=>{const n=e(t.currentTarget,(e=>e.classList.contains("t3js-formengine-field-item"))).querySelector(".t3js-charcounter-min");n&&n.remove()};document.querySelectorAll("[minlength]:not(.t3js-datetimepicker):not(.t3js-charcounter-min-initialized)").forEach((e=>{e.addEventListener("focus",(a=>{const o=n.getMinCharacterLeftCount(e);o>0&&t(o,a)})),e.addEventListener("blur",a),e.addEventListener("keyup",(o=>{const i=n.getMinCharacterLeftCount(e);i>0?t(i,o):a(o)}))}))},getMinCharacterLeftCount:function(e){const t=e.value,n=e.minLength,a=t.length;if(0===a)return 0;return n-a-(t.match(/\n/g)||[]).length},initializeNullNoPlaceholderCheckboxes:function(){document.querySelectorAll(".t3-form-field-eval-null-checkbox").forEach((e=>{const t=e.querySelector('input[type="checkbox"]'),n=e.closest(".t3js-formengine-field-item");t.checked||n.classList.add("disabled")}))},initializeNullWithPlaceholderCheckboxes:function(){document.querySelectorAll(".t3js-form-field-eval-null-placeholder-checkbox").forEach((e=>{n.toggleCheckboxField($(e).find('input[type="checkbox"]'),!1)}))},toggleCheckboxField:function(e,t=!0){const n=e.closest(".t3js-formengine-field-item");e.prop("checked")?(n.find(".t3js-formengine-placeholder-placeholder").hide(),n.find(".t3js-formengine-placeholder-formfield").show(),t&&n.find(".t3js-formengine-placeholder-formfield").find(":input").trigger("focus")):(n.find(".t3js-formengine-placeholder-placeholder").show(),n.find(".t3js-formengine-placeholder-formfield").hide())},reinitialize:function(){const e=Array.from(document.querySelectorAll(".t3js-clearable"));e.length>0&&import("@typo3/backend/input/clearable.js").then((function(){e.forEach((e=>e.clearable()))})),n.initializeNullNoPlaceholderCheckboxes(),n.initializeNullWithPlaceholderCheckboxes(),n.initializeLocalizationStateSelector(),n.initializeMinimumCharactersLeftViews(),n.initializeRemainingCharacterViews()},initializeLocalizationStateSelector:function(){document.querySelectorAll(".t3js-l10n-state-container").forEach((e=>{const t=e.closest(".t3js-formengine-field-item")?.querySelector("[data-formengine-input-name]");if(void 0===t)return;const n=e.querySelector('input[type="radio"]:checked').value;"parent"!==n&&"source"!==n||(t.disabled=!0)}))},hasChange:function(){const e=$('form[name="'+n.formName+'"] .has-change').length>0,t=$('[name^="data["].has-change').length>0;return e||t},preventExitIfNotSavedCallback:function(e){n.closeDocument()},preventFollowLinkIfNotSaved:function(e){return n.preventExitIfNotSaved((function(){window.location.href=e})),!1},preventExitIfNotSaved:function(e){if(e=e||n.preventExitIfNotSavedCallback,n.hasChange()){const t=TYPO3.lang["label.confirm.close_without_save.title"]||"Do you want to close without saving?",a=TYPO3.lang["label.confirm.close_without_save.content"]||"You currently have unsaved changes. Are you sure you want to discard these changes?",o=$("<input />").attr("type","hidden").attr("name","_saveandclosedok").attr("value","1"),i=[{text:TYPO3.lang["buttons.confirm.close_without_save.no"]||"No, I will continue editing",btnClass:"btn-default",name:"no"},{text:TYPO3.lang["buttons.confirm.close_without_save.yes"]||"Yes, discard my changes",btnClass:"btn-default",name:"yes"}];0===$(".has-error").length&&i.push({text:TYPO3.lang["buttons.confirm.save_and_close"]||"Save and close",btnClass:"btn-primary",name:"save",active:!0});const r=Modal.confirm(t,a,Severity.warning,i);r.addEventListener("button.clicked",(function(t){"no"===t.target.name?r.hideModal():"yes"===t.target.name?(r.hideModal(),e.call(null,!0)):"save"===t.target.name&&($("form[name="+n.formName+"]").append(o),r.hideModal(),n.saveDocument())}))}else e.call(null,!0)},preventSaveIfHasErrors:function(){if($(".has-error").length>0){const e=TYPO3.lang["label.alert.save_with_error.title"]||"You have errors in your form!",t=TYPO3.lang["label.alert.save_with_error.content"]||"Please check the form, there is at least one error in your form.",n=Modal.confirm(e,t,Severity.error,[{text:TYPO3.lang["buttons.alert.save_with_error.ok"]||"OK",btnClass:"btn-danger",name:"ok"}]);return n.addEventListener("button.clicked",(function(e){"ok"===e.target.name&&n.hideModal()})),!1}return!0},requestFormEngineUpdate:function(e){if(e){const e=Modal.advanced({title:TYPO3.lang["FormEngine.refreshRequiredTitle"],content:TYPO3.lang["FormEngine.refreshRequiredContent"],severity:Severity.warning,staticBackdrop:!0,buttons:[{text:TYPO3.lang["button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel",trigger:()=>{e.hideModal()}},{text:TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(Severity.warning),name:"ok",trigger:()=>{n.closeModalsRecursive(),n.saveDocument()}}]})}else n.saveDocument()},processOnFieldChange:function(e,n){e.forEach((e=>{const a=t.get(e.name);a instanceof Function&&a.call(null,e.data||null,n)}))},registerOnFieldChangeHandler:function(e,n){t.has(e)&&console.warn("Handler for onFieldChange name `"+e+"` has been overridden."),t.set(e,n)},closeModalsRecursive:function(){void 0!==Modal.currentModal&&null!==Modal.currentModal&&(Modal.currentModal.addEventListener("typo3-modal-hidden",(function(){n.closeModalsRecursive()})),Modal.currentModal.hideModal())},previewAction:function(e,t){t=t||n.previewActionCallback;const a=e.currentTarget.href,o=e.target.dataset.hasOwnProperty("isNew"),i=$("<input />").attr("type","hidden").attr("name","_savedokview").attr("value","1");n.hasChange()?n.showPreviewModal(a,o,i,t):($("form[name="+n.formName+"]").append(i),window.open("","newTYPO3frontendWindow"),document.editform.submit())},previewActionCallback:function(e,t,a){switch(Modal.dismiss(),e){case"discard":const e=window.open(t,"newTYPO3frontendWindow");e.focus(),Utility.urlsPointToSameServerSideResource(e.location.href,t)&&e.location.reload();break;case"save":$("form[name="+n.formName+"]").append($(a)),window.open("","newTYPO3frontendWindow"),n.saveDocument()}},showPreviewModal:function(e,t,n,a){const o=TYPO3.lang["label.confirm.view_record_changed.title"]||"Do you want to save before viewing?",i={text:TYPO3.lang["buttons.confirm.view_record_changed.cancel"]||"Cancel",btnClass:"btn-default",name:"cancel"},r={text:TYPO3.lang["buttons.confirm.view_record_changed.no-save"]||"View without changes",btnClass:"btn-default",name:"discard"},c={text:TYPO3.lang["buttons.confirm.view_record_changed.save"]||"Save changes and view",btnClass:"btn-primary",name:"save",active:!0};let l=[],s="";t?(l=[i,c],s=TYPO3.lang["label.confirm.view_record_changed.content.is-new-page"]||"You need to save your changes before viewing the page. Do you want to save and view them now?"):(l=[i,r,c],s=TYPO3.lang["label.confirm.view_record_changed.content"]||"You currently have unsaved changes. You can either discard these changes or save and view them.");const d=Modal.confirm(o,s,Severity.info,l);d.addEventListener("button.clicked",(function(t){a(t.target.name,e,n,d)}))},newAction:function(e,t){t=t||n.newActionCallback;const a=$("<input />").attr("type","hidden").attr("name","_savedoknew").attr("value","1"),o=e.target.dataset.hasOwnProperty("isNew");n.hasChange()?n.showNewModal(o,a,t):($("form[name="+n.formName+"]").append(a),document.editform.submit())},newActionCallback:function(e,t){const a=$("form[name="+n.formName+"]");switch(Modal.dismiss(),e){case"no":a.append(t),document.editform.submit();break;case"yes":a.append(t),n.saveDocument()}},showNewModal:function(e,t,n){const a=TYPO3.lang["label.confirm.new_record_changed.title"]||"Do you want to save before adding?",o=TYPO3.lang["label.confirm.new_record_changed.content"]||"You need to save your changes before creating a new record. Do you want to save and create now?";let i=[];const r={text:TYPO3.lang["buttons.confirm.new_record_changed.cancel"]||"Cancel",btnClass:"btn-default",name:"cancel"},c={text:TYPO3.lang["buttons.confirm.new_record_changed.no"]||"No, just add",btnClass:"btn-default",name:"no"},l={text:TYPO3.lang["buttons.confirm.new_record_changed.yes"]||"Yes, save and create now",btnClass:"btn-primary",name:"yes",active:!0};i=e?[r,l]:[r,c,l];Modal.confirm(a,o,Severity.info,i).addEventListener("button.clicked",(function(e){n(e.target.name,t)}))},duplicateAction:function(e,t){t=t||n.duplicateActionCallback;const a=$("<input />").attr("type","hidden").attr("name","_duplicatedoc").attr("value","1"),o=e.target.dataset.hasOwnProperty("isNew");n.hasChange()?n.showDuplicateModal(o,a,t):($("form[name="+n.formName+"]").append(a),document.editform.submit())},duplicateActionCallback:function(e,t){const a=$("form[name="+n.formName+"]");switch(Modal.dismiss(),e){case"no":a.append(t),document.editform.submit();break;case"yes":a.append(t),n.saveDocument()}},showDuplicateModal:function(e,t,n){const a=TYPO3.lang["label.confirm.duplicate_record_changed.title"]||"Do you want to save before duplicating this record?",o=TYPO3.lang["label.confirm.duplicate_record_changed.content"]||"You currently have unsaved changes. Do you want to save your changes before duplicating this record?";let i=[];const r={text:TYPO3.lang["buttons.confirm.duplicate_record_changed.cancel"]||"Cancel",btnClass:"btn-default",name:"cancel"},c={text:TYPO3.lang["buttons.confirm.duplicate_record_changed.no"]||"No, just duplicate the original",btnClass:"btn-default",name:"no"},l={text:TYPO3.lang["buttons.confirm.duplicate_record_changed.yes"]||"Yes, save and duplicate this record",btnClass:"btn-primary",name:"yes",active:!0};i=e?[r,l]:[r,c,l];Modal.confirm(a,o,Severity.info,i).addEventListener("button.clicked",(function(e){n(e.target.name,t)}))},deleteAction:function(e,t){t=t||n.deleteActionCallback;const a=$(e.target);n.showDeleteModal(a,t)},deleteActionCallback:function(e,t){Modal.dismiss(),"yes"===e&&n.invokeRecordDeletion(t)},showDeleteModal:function(e,t){const n=TYPO3.lang["label.confirm.delete_record.title"]||"Delete this record?";let a=(TYPO3.lang["label.confirm.delete_record.content"]||"Are you sure you want to delete the record '%s'?").replace("%s",e.data("record-info"));e.data("reference-count-message")&&(a+="\n"+e.data("reference-count-message")),e.data("translation-count-message")&&(a+="\n"+e.data("translation-count-message"));Modal.confirm(n,a,Severity.warning,[{text:TYPO3.lang["buttons.confirm.delete_record.no"]||"Cancel",btnClass:"btn-default",name:"no"},{text:TYPO3.lang["buttons.confirm.delete_record.yes"]||"Yes, delete this record",btnClass:"btn-warning",name:"yes",active:!0}]).addEventListener("button.clicked",(function(n){t(n.target.name,e)}))},enableOptGroup:function(e){const t=e.parentElement;t instanceof HTMLOptGroupElement&&t.querySelectorAll("option:not([hidden]):not([disabled]):not(.hidden)").length&&(t.hidden=!1,t.disabled=!1,t.classList.remove("hidden"))},closeDocument:function(){document.editform.closeDoc.value=1,n.dispatchSubmitEvent(),document.editform.submit()},saveDocument:function(){document.editform.doSave.value=1,n.dispatchSubmitEvent(),document.editform.submit()},dispatchSubmitEvent:function(){const e=document.createEvent("Event");e.initEvent("submit",!1,!0),document.editform.dispatchEvent(e)},initialize:function(e){n.browserUrl=e,DocumentService.ready().then((()=>{n.initializeEvents(),n.Validation.initialize(),n.reinitialize(),$("#t3js-ui-block").remove()}))},invokeRecordDeletion:function(e){window.location.href=e.attr("href")}};if(void 0!==TYPO3.settings.RequireJS&&void 0!==TYPO3.settings.RequireJS.PostInitializationModules["TYPO3/CMS/Backend/FormEngine"])for(let e of TYPO3.settings.RequireJS.PostInitializationModules["TYPO3/CMS/Backend/FormEngine"])window.require([e]);return TYPO3.FormEngine=n,n}()); \ No newline at end of file +import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import FormEngineValidation from"@typo3/backend/form-engine-validation.js";import Icons from"@typo3/backend/icons.js";import{default as Modal}from"@typo3/backend/modal.js";import*as MessageUtility from"@typo3/backend/utility/message-utility.js";import Severity from"@typo3/backend/severity.js";import*as BackendExceptionModule from"@typo3/backend/backend-exception.js";import InteractionRequestMap from"@typo3/backend/event/interaction-request-map.js";import Utility from"@typo3/backend/utility.js";export default(function(){function e(e,t){t?n.interactionRequestMap.resolveFor(e):n.interactionRequestMap.rejectFor(e)}const t=new Map;t.set("typo3-backend-form-update-value",(e=>{const t=document.querySelector('[name="'+CSS.escape(e.elementName)+'"]'),n=document.querySelector('[data-formengine-input-name="'+CSS.escape(e.elementName)+'"]');FormEngineValidation.updateInputField(e.elementName),null!==t&&(FormEngineValidation.markFieldAsChanged(t),FormEngineValidation.validateField(t)),null!==n&&n!==t&&FormEngineValidation.validateField(n)})),t.set("typo3-backend-form-reload",(e=>{if(!e.confirmation)return void n.saveDocument();const t=Modal.advanced({title:TYPO3.lang["FormEngine.refreshRequiredTitle"],content:TYPO3.lang["FormEngine.refreshRequiredContent"],severity:Severity.warning,staticBackdrop:!0,buttons:[{text:TYPO3.lang["button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(Severity.warning),name:"ok",trigger:()=>{n.saveDocument()}}]});t.addEventListener("button.clicked",(()=>t.hideModal()))})),t.set("typo3-backend-form-update-bitmask",((e,t)=>{const n=t.target,a=document.editform[e.elementName],o=n.checked!==e.invert,i=Math.pow(2,e.position),r=Math.pow(2,e.total)-i-1;a.value=o?a.value|i:a.value&r,a.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))}));const n={consumeTypes:["typo3.setUrl","typo3.beforeSetUrl","typo3.refresh"],Validation:FormEngineValidation,interactionRequestMap:InteractionRequestMap,formName:TYPO3.settings.FormEngine.formName,openedPopupWindow:null,legacyFieldChangedCb:function(){!$.isFunction(TYPO3.settings.FormEngine.legacyFieldChangedCb)||TYPO3.settings.FormEngine.legacyFieldChangedCb()},browserUrl:"",openPopupWindow:function(e,t,a){return Modal.advanced({type:Modal.types.iframe,content:n.browserUrl+"&mode="+e+"&bparams="+t+(a?"&"+("db"===e?"expandPage":"expandFolder")+"="+a:""),size:Modal.sizes.large})},setSelectOptionFromExternalSource:function(e,t,a,o,i,r){i=String(i);let c,l,s=!1,d=!1;l=n.getFieldElement(e),c=l.get(0);const u=l.get(0);if(null===u||"--div--"===t||u instanceof HTMLOptGroupElement)return;const m=n.getFieldElement(e,"_list",!0);if(m.length>0&&(l=m,c=l.get(0),s=l.prop("multiple")&&"1"!=l.prop("size"),d=!0),s||d){const d=n.getFieldElement(e,"_avail");if(!s){for(const e of c.querySelectorAll("option")){const t=d.find('option[value="'+$.escapeSelector($(e).attr("value"))+'"]');t.length&&(t.removeClass("hidden").prop("disabled",!1),n.enableOptGroup(t.get(0)))}l.empty()}if(i){let e=!1,a=new RegExp("(^|,)"+t+"($|,)");i.match(a)?(l.empty(),e=!0):1==l.find("option").length&&(a=new RegExp("(^|,)"+l.find("option").prop("value")+"($|,)"),i.match(a)&&(l.empty(),e=!0)),e&&void 0!==r&&r.closest("select").querySelectorAll("[disabled]").forEach((function(e){e.classList.remove("hidden"),e.disabled=!1,n.enableOptGroup(e)}))}let m=!0;const f=n.getFieldElement(e,"_mul",!0);if(0==f.length||0==f.val()){for(const e of c.querySelectorAll("option"))if(e.value==t){m=!1;break}if(m&&void 0!==r){r.classList.add("hidden"),r.disabled=!0;const e=r.parentElement;e instanceof HTMLOptGroupElement&&0===e.querySelectorAll("option:not([disabled]):not([hidden]):not(.hidden)").length&&(e.disabled=!0,e.classList.add("hidden"))}}if(m){const e=$("<option></option>");e.attr({value:t,title:o}).text(a),e.appendTo(l),n.updateHiddenFieldValueFromSelect(c,u),n.legacyFieldChangedCb(),FormEngineValidation.markFieldAsChanged(u),n.Validation.validateField(l),n.Validation.validateField(d)}}else{const e=/_(\d+)$/,a=t.toString().match(e);null!=a&&(t=a[1]),l.val(t),n.Validation.validateField(l)}},updateHiddenFieldValueFromSelect:function(e,t){const n=Array.from(e.options).map((e=>e.value));t.value=n.join(","),t.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))},getFormElement:function(e){const t=$('form[name="'+n.formName+'"]:first');if(!e)return t;{const a=n.getFieldElement(e),o=n.getFieldElement(e,"_list");if(a.length>0&&("select-one"===a.prop("type")||o.length>0&&o.prop("type").match(/select-(one|multiple)/)))return t;console.error("Form fields missing: form: "+n.formName+", field name: "+e),alert("Form field is invalid")}},getFieldElement:function(e,t,a){const o=$('form[name="'+n.formName+'"]:first');if(t){let n;switch(t){case"_list":n=$(':input[data-formengine-input-name="'+e+'"]:not([type=hidden])',o);break;case"_avail":n=$(':input[data-relatedfieldname="'+e+'"]',o);break;case"_mul":case"_hr":n=$(':input[type=hidden][data-formengine-input-name="'+e+'"]',o);break;default:n=null}if(n&&n.length>0||!0===a)return n}return $(':input[name="'+e+'"]',o)},initializeEvents:function(){top.TYPO3&&void 0!==top.TYPO3.Backend&&(top.TYPO3.Backend.consumerScope.attach(n),$(window).on("unload",(function(){top.TYPO3.Backend.consumerScope.detach(n)}))),$(document).on("click",".t3js-editform-close",(e=>{e.preventDefault(),n.preventExitIfNotSaved(n.preventExitIfNotSavedCallback)})).on("click",".t3js-editform-view",(e=>{e.preventDefault(),n.previewAction(e,n.previewActionCallback)})).on("click",".t3js-editform-new",(e=>{e.preventDefault(),n.newAction(e,n.newActionCallback)})).on("click",".t3js-editform-duplicate",(e=>{e.preventDefault(),n.duplicateAction(e,n.duplicateActionCallback)})).on("click",".t3js-editform-delete-record",(e=>{e.preventDefault(),n.deleteAction(e,n.deleteActionCallback)})).on("click",".t3js-editform-submitButton",(e=>{const t=$(e.currentTarget),n=t.data("name")||e.currentTarget.name,a=$("<input />").attr("type","hidden").attr("name",n).attr("value","1");t.parents("form").append(a)})).on("change",'.t3-form-field-eval-null-checkbox input[type="checkbox"]',(e=>{$(e.currentTarget).closest(".t3js-formengine-field-item").toggleClass("disabled")})).on("change",'.t3js-form-field-eval-null-placeholder-checkbox input[type="checkbox"]',(e=>{n.toggleCheckboxField($(e.currentTarget)),FormEngineValidation.markFieldAsChanged($(e.currentTarget))})).on("change",(()=>{$(".module-docheader-bar .btn").removeClass("disabled").prop("disabled",!1)})).on("click",".t3js-element-browser",(function(e){e.preventDefault(),e.stopPropagation();const t=$(e.currentTarget),a=t.data("mode"),o=t.data("params"),i=t.data("entryPoint");n.openPopupWindow(a,o,i)})).on("click",'[data-formengine-field-change-event="click"]',(e=>{const t=JSON.parse(e.currentTarget.dataset.formengineFieldChangeItems);n.processOnFieldChange(t,e)})).on("change",'[data-formengine-field-change-event="change"]',(e=>{const t=JSON.parse(e.currentTarget.dataset.formengineFieldChangeItems);n.processOnFieldChange(t,e)})),document.editform.addEventListener("submit",(function(){if(document.editform.closeDoc.value)return;const e=["button[form]",'button[name^="_save"]','a[data-name^="_save"]','button[name="CMD"][value^="save"]','a[data-name="CMD"][data-value^="save"]'].join(","),t=document.querySelector(e);null!==t&&(t.disabled=!0,Icons.getIcon("spinner-circle-dark",Icons.sizes.small).then((function(e){t.querySelector(".t3js-icon").outerHTML=e})))})),window.addEventListener("message",n.handlePostMessage)},consume:function(t){if(!t)throw new BackendExceptionModule.BackendException("No interaction request given",1496589980);const a=$.Deferred();if(t.concernsTypes(n.consumeTypes)){const o=t.outerMostRequest;n.interactionRequestMap.attachFor(o,a),o.isProcessed()?e(o,o.getProcessedData().response):n.hasChange()?n.preventExitIfNotSaved((function(t){o.setProcessedData({response:t}),e(o,t)})):n.interactionRequestMap.resolveFor(o)}return a},handlePostMessage:function(e){if(!MessageUtility.MessageUtility.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;if("typo3:elementBrowser:elementAdded"===e.data.actionName){if(void 0===e.data.fieldName)throw"fieldName not defined in message";if(void 0===e.data.value)throw"value not defined in message";const t=e.data.label||e.data.value,a=e.data.title||t,o=e.data.exclusiveValues||"";n.setSelectOptionFromExternalSource(e.data.fieldName,e.data.value,t,a,o)}},initializeRemainingCharacterViews:function(){const e=$("[maxlength]").not(".t3js-datetimepicker").not(".t3js-color-picker").not(".t3js-charcounter-initialized");e.on("focus",(e=>{const t=$(e.currentTarget),a=t.parents(".t3js-formengine-field-item:first"),o=n.getCharacterCounterProperties(t);let i=a.find(".t3js-charcounter-wrapper");i.length||(i=$("<div>"),i.addClass("t3js-charcounter-wrapper"),a.append(i)),i.append($("<div />",{class:"t3js-charcounter"}).append($("<span />",{class:o.labelClass}).text(TYPO3.lang["FormEngine.remainingCharacters"].replace("{0}",o.remainingCharacters))))})).on("blur",(e=>{$(e.currentTarget).parents(".t3js-formengine-field-item:first").find(".t3js-charcounter").remove()})).on("keyup",(e=>{const t=$(e.currentTarget),a=t.parents(".t3js-formengine-field-item:first"),o=n.getCharacterCounterProperties(t);a.find(".t3js-charcounter span").removeClass().addClass(o.labelClass).text(TYPO3.lang["FormEngine.remainingCharacters"].replace("{0}",o.remainingCharacters))})),e.addClass("t3js-charcounter-initialized")},getCharacterCounterProperties:function(e){const t=e.val(),n=parseInt(e.attr("maxlength"),10)-t.length-(t.match(/\n/g)||[]).length;let a="";return a=n<15?"badge-danger":n<30?"badge-warning":"badge-info",{remainingCharacters:n,labelClass:"badge "+a}},initializeMinimumCharactersLeftViews:function(){const e=(t,n)=>t&&(n(t)?t:e(t.parentNode,n)),t=(t,n)=>{const a=e(n.currentTarget,(e=>e.classList.contains("t3js-formengine-field-item"))),o=a.querySelector(".t3js-charcounter-min"),i=TYPO3.lang["FormEngine.minCharactersLeft"].replace("{0}",t);if(o)o.querySelector("span").innerHTML=i;else{const e=document.createElement("div");e.classList.add("t3js-charcounter-min");const t=document.createElement("span");t.classList.add("badge","badge-danger"),t.innerHTML=i,e.append(t);let n=a.querySelector(".t3js-charcounter-wrapper");n||(n=document.createElement("div"),n.classList.add("t3js-charcounter-wrapper"),a.append(n)),n.prepend(e)}},a=t=>{const n=e(t.currentTarget,(e=>e.classList.contains("t3js-formengine-field-item"))).querySelector(".t3js-charcounter-min");n&&n.remove()};document.querySelectorAll("[minlength]:not(.t3js-datetimepicker):not(.t3js-charcounter-min-initialized)").forEach((e=>{e.addEventListener("focus",(a=>{const o=n.getMinCharacterLeftCount(e);o>0&&t(o,a)})),e.addEventListener("blur",a),e.addEventListener("keyup",(o=>{const i=n.getMinCharacterLeftCount(e);i>0?t(i,o):a(o)}))}))},getMinCharacterLeftCount:function(e){const t=e.value,n=e.minLength,a=t.length;if(0===a)return 0;return n-a-(t.match(/\n/g)||[]).length},initializeNullNoPlaceholderCheckboxes:function(){document.querySelectorAll(".t3-form-field-eval-null-checkbox").forEach((e=>{const t=e.querySelector('input[type="checkbox"]'),n=e.closest(".t3js-formengine-field-item");t.checked||n.classList.add("disabled")}))},initializeNullWithPlaceholderCheckboxes:function(){document.querySelectorAll(".t3js-form-field-eval-null-placeholder-checkbox").forEach((e=>{n.toggleCheckboxField($(e).find('input[type="checkbox"]'),!1)}))},toggleCheckboxField:function(e,t=!0){const n=e.closest(".t3js-formengine-field-item");e.prop("checked")?(n.find(".t3js-formengine-placeholder-placeholder").hide(),n.find(".t3js-formengine-placeholder-formfield").show(),t&&n.find(".t3js-formengine-placeholder-formfield").find(":input").trigger("focus")):(n.find(".t3js-formengine-placeholder-placeholder").show(),n.find(".t3js-formengine-placeholder-formfield").hide())},reinitialize:function(){const e=Array.from(document.querySelectorAll(".t3js-clearable"));e.length>0&&import("@typo3/backend/input/clearable.js").then((function(){e.forEach((e=>e.clearable()))})),n.initializeNullNoPlaceholderCheckboxes(),n.initializeNullWithPlaceholderCheckboxes(),n.initializeLocalizationStateSelector(),n.initializeMinimumCharactersLeftViews(),n.initializeRemainingCharacterViews()},initializeLocalizationStateSelector:function(){document.querySelectorAll(".t3js-l10n-state-container").forEach((e=>{const t=e.closest(".t3js-formengine-field-item")?.querySelector("[data-formengine-input-name]");if(void 0===t)return;const n=e.querySelector('input[type="radio"]:checked').value;"parent"!==n&&"source"!==n||(t.disabled=!0)}))},hasChange:function(){const e=$('form[name="'+n.formName+'"] .has-change').length>0,t=$('[name^="data["].has-change').length>0;return e||t},preventExitIfNotSavedCallback:()=>{n.closeDocument()},preventFollowLinkIfNotSaved:function(e){return n.preventExitIfNotSaved((function(){window.location.href=e})),!1},preventExitIfNotSaved:function(e){if(e=e||n.preventExitIfNotSavedCallback,n.hasChange()){const t=TYPO3.lang["label.confirm.close_without_save.title"]||"Do you want to close without saving?",a=TYPO3.lang["label.confirm.close_without_save.content"]||"You currently have unsaved changes. Are you sure you want to discard these changes?",o=$("<input />").attr("type","hidden").attr("name","_saveandclosedok").attr("value","1"),i=[{text:TYPO3.lang["buttons.confirm.close_without_save.no"]||"No, I will continue editing",btnClass:"btn-default",name:"no"},{text:TYPO3.lang["buttons.confirm.close_without_save.yes"]||"Yes, discard my changes",btnClass:"btn-default",name:"yes"}];0===$(".has-error").length&&i.push({text:TYPO3.lang["buttons.confirm.save_and_close"]||"Save and close",btnClass:"btn-primary",name:"save",active:!0});const r=Modal.confirm(t,a,Severity.warning,i);r.addEventListener("button.clicked",(function(t){"no"===t.target.name?r.hideModal():"yes"===t.target.name?(r.hideModal(),e.call(null,!0)):"save"===t.target.name&&($("form[name="+n.formName+"]").append(o),r.hideModal(),n.saveDocument())}))}else e.call(null,!0)},preventSaveIfHasErrors:function(){if($(".has-error").length>0){const e=TYPO3.lang["label.alert.save_with_error.title"]||"You have errors in your form!",t=TYPO3.lang["label.alert.save_with_error.content"]||"Please check the form, there is at least one error in your form.",n=Modal.confirm(e,t,Severity.error,[{text:TYPO3.lang["buttons.alert.save_with_error.ok"]||"OK",btnClass:"btn-danger",name:"ok"}]);return n.addEventListener("button.clicked",(function(e){"ok"===e.target.name&&n.hideModal()})),!1}return!0},requestFormEngineUpdate:function(e){if(e){const e=Modal.advanced({title:TYPO3.lang["FormEngine.refreshRequiredTitle"],content:TYPO3.lang["FormEngine.refreshRequiredContent"],severity:Severity.warning,staticBackdrop:!0,buttons:[{text:TYPO3.lang["button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel",trigger:()=>{e.hideModal()}},{text:TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(Severity.warning),name:"ok",trigger:()=>{n.closeModalsRecursive(),n.saveDocument()}}]})}else n.saveDocument()},processOnFieldChange:function(e,n){e.forEach((e=>{const a=t.get(e.name);a instanceof Function&&a.call(null,e.data||null,n)}))},registerOnFieldChangeHandler:function(e,n){t.has(e)&&console.warn("Handler for onFieldChange name `"+e+"` has been overridden."),t.set(e,n)},closeModalsRecursive:function(){void 0!==Modal.currentModal&&null!==Modal.currentModal&&(Modal.currentModal.addEventListener("typo3-modal-hidden",(function(){n.closeModalsRecursive()})),Modal.currentModal.hideModal())},previewAction:function(e,t){t=t||n.previewActionCallback;const a=e.currentTarget.href,o="isNew"in e.target.dataset,i=$("<input />").attr("type","hidden").attr("name","_savedokview").attr("value","1");n.hasChange()?n.showPreviewModal(a,o,i,t):($("form[name="+n.formName+"]").append(i),window.open("","newTYPO3frontendWindow"),document.editform.submit())},previewActionCallback:function(e,t,a){switch(Modal.dismiss(),e){case"discard":const e=window.open(t,"newTYPO3frontendWindow");e.focus(),Utility.urlsPointToSameServerSideResource(e.location.href,t)&&e.location.reload();break;case"save":$("form[name="+n.formName+"]").append($(a)),window.open("","newTYPO3frontendWindow"),n.saveDocument()}},showPreviewModal:function(e,t,n,a){const o=TYPO3.lang["label.confirm.view_record_changed.title"]||"Do you want to save before viewing?",i={text:TYPO3.lang["buttons.confirm.view_record_changed.cancel"]||"Cancel",btnClass:"btn-default",name:"cancel"},r={text:TYPO3.lang["buttons.confirm.view_record_changed.no-save"]||"View without changes",btnClass:"btn-default",name:"discard"},c={text:TYPO3.lang["buttons.confirm.view_record_changed.save"]||"Save changes and view",btnClass:"btn-primary",name:"save",active:!0};let l=[],s="";t?(l=[i,c],s=TYPO3.lang["label.confirm.view_record_changed.content.is-new-page"]||"You need to save your changes before viewing the page. Do you want to save and view them now?"):(l=[i,r,c],s=TYPO3.lang["label.confirm.view_record_changed.content"]||"You currently have unsaved changes. You can either discard these changes or save and view them.");const d=Modal.confirm(o,s,Severity.info,l);d.addEventListener("button.clicked",(function(t){a(t.target.name,e,n,d)}))},newAction:function(e,t){t=t||n.newActionCallback;const a=$("<input />").attr("type","hidden").attr("name","_savedoknew").attr("value","1"),o="isNew"in e.target.dataset;n.hasChange()?n.showNewModal(o,a,t):($("form[name="+n.formName+"]").append(a),document.editform.submit())},newActionCallback:function(e,t){const a=$("form[name="+n.formName+"]");switch(Modal.dismiss(),e){case"no":a.append(t),document.editform.submit();break;case"yes":a.append(t),n.saveDocument()}},showNewModal:function(e,t,n){const a=TYPO3.lang["label.confirm.new_record_changed.title"]||"Do you want to save before adding?",o=TYPO3.lang["label.confirm.new_record_changed.content"]||"You need to save your changes before creating a new record. Do you want to save and create now?";let i=[];const r={text:TYPO3.lang["buttons.confirm.new_record_changed.cancel"]||"Cancel",btnClass:"btn-default",name:"cancel"},c={text:TYPO3.lang["buttons.confirm.new_record_changed.no"]||"No, just add",btnClass:"btn-default",name:"no"},l={text:TYPO3.lang["buttons.confirm.new_record_changed.yes"]||"Yes, save and create now",btnClass:"btn-primary",name:"yes",active:!0};i=e?[r,l]:[r,c,l];Modal.confirm(a,o,Severity.info,i).addEventListener("button.clicked",(function(e){n(e.target.name,t)}))},duplicateAction:function(e,t){t=t||n.duplicateActionCallback;const a=$("<input />").attr("type","hidden").attr("name","_duplicatedoc").attr("value","1"),o="isNew"in e.target.dataset;n.hasChange()?n.showDuplicateModal(o,a,t):($("form[name="+n.formName+"]").append(a),document.editform.submit())},duplicateActionCallback:function(e,t){const a=$("form[name="+n.formName+"]");switch(Modal.dismiss(),e){case"no":a.append(t),document.editform.submit();break;case"yes":a.append(t),n.saveDocument()}},showDuplicateModal:function(e,t,n){const a=TYPO3.lang["label.confirm.duplicate_record_changed.title"]||"Do you want to save before duplicating this record?",o=TYPO3.lang["label.confirm.duplicate_record_changed.content"]||"You currently have unsaved changes. Do you want to save your changes before duplicating this record?";let i=[];const r={text:TYPO3.lang["buttons.confirm.duplicate_record_changed.cancel"]||"Cancel",btnClass:"btn-default",name:"cancel"},c={text:TYPO3.lang["buttons.confirm.duplicate_record_changed.no"]||"No, just duplicate the original",btnClass:"btn-default",name:"no"},l={text:TYPO3.lang["buttons.confirm.duplicate_record_changed.yes"]||"Yes, save and duplicate this record",btnClass:"btn-primary",name:"yes",active:!0};i=e?[r,l]:[r,c,l];Modal.confirm(a,o,Severity.info,i).addEventListener("button.clicked",(function(e){n(e.target.name,t)}))},deleteAction:function(e,t){t=t||n.deleteActionCallback;const a=$(e.target);n.showDeleteModal(a,t)},deleteActionCallback:function(e,t){Modal.dismiss(),"yes"===e&&n.invokeRecordDeletion(t)},showDeleteModal:function(e,t){const n=TYPO3.lang["label.confirm.delete_record.title"]||"Delete this record?";let a=(TYPO3.lang["label.confirm.delete_record.content"]||"Are you sure you want to delete the record '%s'?").replace("%s",e.data("record-info"));e.data("reference-count-message")&&(a+="\n"+e.data("reference-count-message")),e.data("translation-count-message")&&(a+="\n"+e.data("translation-count-message"));Modal.confirm(n,a,Severity.warning,[{text:TYPO3.lang["buttons.confirm.delete_record.no"]||"Cancel",btnClass:"btn-default",name:"no"},{text:TYPO3.lang["buttons.confirm.delete_record.yes"]||"Yes, delete this record",btnClass:"btn-warning",name:"yes",active:!0}]).addEventListener("button.clicked",(function(n){t(n.target.name,e)}))},enableOptGroup:function(e){const t=e.parentElement;t instanceof HTMLOptGroupElement&&t.querySelectorAll("option:not([hidden]):not([disabled]):not(.hidden)").length&&(t.hidden=!1,t.disabled=!1,t.classList.remove("hidden"))},closeDocument:function(){document.editform.closeDoc.value=1,n.dispatchSubmitEvent(),document.editform.submit()},saveDocument:function(){document.editform.doSave.value=1,n.dispatchSubmitEvent(),document.editform.submit()},dispatchSubmitEvent:function(){const e=document.createEvent("Event");e.initEvent("submit",!1,!0),document.editform.dispatchEvent(e)},initialize:function(e){n.browserUrl=e,DocumentService.ready().then((()=>{n.initializeEvents(),n.Validation.initialize(),n.reinitialize(),$("#t3js-ui-block").remove()}))},invokeRecordDeletion:function(e){window.location.href=e.attr("href")}};if(void 0!==TYPO3.settings.RequireJS&&void 0!==TYPO3.settings.RequireJS.PostInitializationModules["TYPO3/CMS/Backend/FormEngine"])for(const e of TYPO3.settings.RequireJS.PostInitializationModules["TYPO3/CMS/Backend/FormEngine"])window.require([e]);return TYPO3.FormEngine=n,n}()); \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/files-control-container.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/files-control-container.js index d3579e1785a2..a95c0f246a73 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/files-control-container.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/files-control-container.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{MessageUtility}from"@typo3/backend/utility/message-utility.js";import{AjaxDispatcher}from"@typo3/backend/form-engine/inline-relation/ajax-dispatcher.js";import NProgress from"nprogress";import Sortable from"sortablejs";import FormEngine from"@typo3/backend/form-engine.js";import FormEngineValidation from"@typo3/backend/form-engine-validation.js";import Icons from"@typo3/backend/icons.js";import InfoWindow from"@typo3/backend/info-window.js";import Modal from"@typo3/backend/modal.js";import RegularEvent from"@typo3/core/event/regular-event.js";import Severity from"@typo3/backend/severity.js";import Utility from"@typo3/backend/utility.js";var Selectors,States,Separators,SortDirections;!function(e){e.toggleSelector='[data-bs-toggle="formengine-file"]',e.controlSectionSelector=".t3js-formengine-file-header-control",e.deleteRecordButtonSelector=".t3js-editform-delete-file-reference",e.enableDisableRecordButtonSelector=".t3js-toggle-visibility-button",e.infoWindowButton='[data-action="infowindow"]',e.synchronizeLocalizeRecordButtonSelector=".t3js-synchronizelocalize-button",e.controlContainer=".t3js-file-controls"}(Selectors||(Selectors={})),function(e){e.new="isNewFileReference",e.visible="panel-visible",e.collapsed="panel-collapsed",e.notLoaded="t3js-not-loaded"}(States||(States={})),function(e){e.structureSeparator="-"}(Separators||(Separators={})),function(e){e.DOWN="down",e.UP="up"}(SortDirections||(SortDirections={}));class FilesControlContainer extends HTMLElement{constructor(){super(...arguments),this.container=null,this.ajaxDispatcher=null,this.appearance=null,this.requestQueue={},this.progessQueue={},this.noTitleString=TYPO3.lang?TYPO3.lang["FormEngine.noRecordTitle"]:"[No title]",this.handlePostMessage=e=>{if(!MessageUtility.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;if("typo3:foreignRelation:insert"===e.data.actionName){if(void 0===e.data.objectGroup)throw"No object group defined for message";if(e.data.objectGroup!==this.container.dataset.objectGroup)return;this.importRecord([e.data.objectGroup,e.data.uid]).then((()=>{if(e.source){const t={actionName:"typo3:foreignRelation:inserted",objectGroup:e.data.objectId,table:e.data.table,uid:e.data.uid};MessageUtility.send(t,e.source)}}))}}}static getFileReferenceContainer(e){return document.querySelector('[data-object-id="'+e+'"]')}static getCollapseButton(e){return document.querySelector('[aria-controls="'+e+'_fields"]')}static toggleElement(e){const t=FilesControlContainer.getFileReferenceContainer(e);t.classList.contains(States.collapsed)?FilesControlContainer.expandElement(t,e):FilesControlContainer.collapseElement(t,e)}static collapseElement(e,t){const n=FilesControlContainer.getCollapseButton(t);e.classList.remove(States.visible),e.classList.add(States.collapsed),n.setAttribute("aria-expanded","false")}static expandElement(e,t){const n=FilesControlContainer.getCollapseButton(t);e.classList.remove(States.collapsed),e.classList.add(States.visible),n.setAttribute("aria-expanded","true")}static isNewRecord(e){return FilesControlContainer.getFileReferenceContainer(e).classList.contains(States.new)}static updateExpandedCollapsedStateLocally(e,t){const n=FilesControlContainer.getFileReferenceContainer(e),o=document.getElementsByName("uc[inlineView]["+n.dataset.topmostParentTable+"]["+n.dataset.topmostParentUid+"]"+n.dataset.fieldName);o.length&&(o[0].value=t?"1":"0")}connectedCallback(){const e=this.getAttribute("identifier")||"";this.container=this.querySelector("#"+e),null!==this.container&&(this.ajaxDispatcher=new AjaxDispatcher(this.container.dataset.objectGroup),this.registerEvents())}registerEvents(){if(this.registerInfoButton(),this.registerSort(),this.registerEnableDisableButton(),this.registerDeleteButton(),this.registerSynchronizeLocalize(),this.registerToggle(),new RegularEvent("message",this.handlePostMessage).bindTo(window),this.getAppearance().useSortable){const e=document.getElementById(this.container.getAttribute("id")+"_records");new Sortable(e,{group:e.getAttribute("id"),handle:".sortableHandle",onSort:()=>{this.updateSorting()}})}}registerToggle(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation(),e.loadRecordDetails(this.closest(Selectors.toggleSelector).parentElement.dataset.objectId)})).delegateTo(this.container,`${Selectors.toggleSelector} .form-irre-header-cell:not(${Selectors.controlSectionSelector}`)}registerSort(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation(),e.changeSortingByButton(this.closest("[data-object-id]").dataset.objectId,this.dataset.direction)})).delegateTo(this.container,Selectors.controlSectionSelector+' [data-action="sort"]')}createRecord(e,t,n=null,o=null){let i=this.container.dataset.objectGroup;null!==n&&(i+=Separators.structureSeparator+n),null!==n?(FilesControlContainer.getFileReferenceContainer(i).insertAdjacentHTML("afterend",t),this.memorizeAddRecord(e,n,o)):(document.getElementById(this.container.getAttribute("id")+"_records").insertAdjacentHTML("beforeend",t),this.memorizeAddRecord(e,null,o))}async importRecord(e,t){return this.ajaxDispatcher.send(this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint("file_reference_create")),e).then((async e=>{this.isBelowMax()&&this.createRecord(e.compilerInput.uid,e.data,void 0!==t?t:null,void 0!==e.compilerInput.childChildUid?e.compilerInput.childChildUid:null)}))}registerEnableDisableButton(){new RegularEvent("click",((e,t)=>{e.preventDefault(),e.stopImmediatePropagation();const n=t.closest("[data-object-id]").dataset.objectId,o=FilesControlContainer.getFileReferenceContainer(n),i="data"+o.dataset.fieldName+"["+t.dataset.hiddenField+"]",r=document.querySelector('[data-formengine-input-name="'+i+'"'),a=document.querySelector('[name="'+i+'"');null!==r&&null!==a&&(r.checked=!r.checked,a.value=r.checked?"1":"0",FormEngineValidation.markFieldAsChanged(r));const s="t3-form-field-container-inline-hidden";let l;o.classList.contains(s)?(l="actions-edit-hide",o.classList.remove(s)):(l="actions-edit-unhide",o.classList.add(s)),Icons.getIcon(l,Icons.sizes.small).then((e=>{t.replaceChild(document.createRange().createContextualFragment(e),t.querySelector(".t3js-icon"))}))})).delegateTo(this.container,Selectors.enableDisableRecordButtonSelector)}registerInfoButton(){new RegularEvent("click",(function(e){e.preventDefault(),e.stopImmediatePropagation(),InfoWindow.showItem(this.dataset.infoTable,this.dataset.infoUid)})).delegateTo(this.container,Selectors.infoWindowButton)}registerDeleteButton(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const n=TYPO3.lang["label.confirm.delete_record.title"]||"Delete this record?",o=TYPO3.lang["label.confirm.delete_record.content"]||"Are you sure you want to delete this record?";Modal.confirm(n,o,Severity.warning,[{text:TYPO3.lang["buttons.confirm.delete_record.no"]||"Cancel",active:!0,btnClass:"btn-default",name:"no",trigger:(e,t)=>t.hideModal()},{text:TYPO3.lang["buttons.confirm.delete_record.yes"]||"Yes, delete this record",btnClass:"btn-warning",name:"yes",trigger:(t,n)=>{e.deleteRecord(this.closest("[data-object-id]").dataset.objectId),n.hideModal()}}])})).delegateTo(this.container,Selectors.deleteRecordButtonSelector)}registerSynchronizeLocalize(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation(),e.ajaxDispatcher.send(e.ajaxDispatcher.newRequest(e.ajaxDispatcher.getEndpoint("file_reference_synchronizelocalize")),[e.container.dataset.objectGroup,this.dataset.type]).then((async t=>{document.getElementById(e.container.getAttribute("id")+"_records").insertAdjacentHTML("beforeend",t.data);const n=e.container.dataset.objectGroup+Separators.structureSeparator;for(let o of t.compilerInput.delete)e.deleteRecord(n+o,!0);for(let o of Object.values(t.compilerInput.localize)){if(void 0!==o.remove){const e=FilesControlContainer.getFileReferenceContainer(n+o.remove);e.parentElement.removeChild(e)}e.memorizeAddRecord(o.uid,null,o.selectedValue)}}))})).delegateTo(this.container,Selectors.synchronizeLocalizeRecordButtonSelector)}loadRecordDetails(e){const t=document.getElementById(e+"_fields"),n=FilesControlContainer.getFileReferenceContainer(e),o=void 0!==this.requestQueue[e];if(null!==t&&!n.classList.contains(States.notLoaded))this.collapseExpandRecord(e);else{const i=this.getProgress(e,n.dataset.objectIdHash);if(o)this.requestQueue[e].abort(),delete this.requestQueue[e],delete this.progessQueue[e],i.done();else{const o=this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint("file_reference_details"));this.ajaxDispatcher.send(o,[e]).then((async o=>{delete this.requestQueue[e],delete this.progessQueue[e],n.classList.remove(States.notLoaded),t.innerHTML=o.data,this.collapseExpandRecord(e),i.done(),FormEngine.reinitialize(),FormEngineValidation.initializeInputFields(),FormEngineValidation.validate(this.container)})),this.requestQueue[e]=o,i.start()}}}collapseExpandRecord(e){const t=FilesControlContainer.getFileReferenceContainer(e),n=!0===this.getAppearance().expandSingle,o=t.classList.contains(States.collapsed);let i=[];const r=[];n&&o&&(i=this.collapseAllRecords(t.dataset.objectUid)),FilesControlContainer.toggleElement(e),FilesControlContainer.isNewRecord(e)?FilesControlContainer.updateExpandedCollapsedStateLocally(e,o):o?r.push(t.dataset.objectUid):o||i.push(t.dataset.objectUid),this.ajaxDispatcher.send(this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint("file_reference_expandcollapse")),[e,r.join(","),i.join(",")])}memorizeAddRecord(e,t=null,n=null){const o=this.getFormFieldForElements();if(null===o)return;let i=Utility.trimExplode(",",o.value);if(t){const n=[];for(let o=0;o<i.length;o++)i[o].length&&n.push(i[o]),t===i[o]&&n.push(e);i=n}else i.push(e);o.value=i.join(","),o.classList.add("has-change"),document.dispatchEvent(new Event("change")),this.redrawSortingButtons(this.container.dataset.objectGroup,i),this.isBelowMax()||this.toggleContainerControls(!1),FormEngine.reinitialize(),FormEngineValidation.initializeInputFields(),FormEngineValidation.validate(this.container)}memorizeRemoveRecord(e){const t=this.getFormFieldForElements();if(null===t)return[];let n=Utility.trimExplode(",",t.value);const o=n.indexOf(e);return o>-1&&(delete n[o],t.value=n.join(","),t.classList.add("has-change"),document.dispatchEvent(new Event("change")),this.redrawSortingButtons(this.container.dataset.objectGroup,n)),n}changeSortingByButton(e,t){const n=FilesControlContainer.getFileReferenceContainer(e),o=n.dataset.objectUid,i=document.getElementById(this.container.getAttribute("id")+"_records"),r=Array.from(i.children).map((e=>e.dataset.objectUid));let a=r.indexOf(o),s=!1;if(t===SortDirections.UP&&a>0?(r[a]=r[a-1],r[a-1]=o,s=!0):t===SortDirections.DOWN&&a<r.length-1&&(r[a]=r[a+1],r[a+1]=o,s=!0),s){const e=this.container.dataset.objectGroup+Separators.structureSeparator,o=t===SortDirections.UP?1:0;n.parentElement.insertBefore(FilesControlContainer.getFileReferenceContainer(e+r[a-o]),FilesControlContainer.getFileReferenceContainer(e+r[a+1-o])),this.updateSorting()}}updateSorting(){const e=this.getFormFieldForElements();if(null===e)return;const t=document.getElementById(this.container.getAttribute("id")+"_records"),n=Array.from(t.querySelectorAll('[data-object-parent-group="'+this.container.dataset.objectGroup+'"][data-placeholder-record="0"]')).map((e=>e.dataset.objectUid));e.value=n.join(","),e.classList.add("has-change"),document.dispatchEvent(new Event("formengine:files:sorting-changed")),document.dispatchEvent(new Event("change")),this.redrawSortingButtons(this.container.dataset.objectGroup,n)}deleteRecord(e,t=!1){const n=FilesControlContainer.getFileReferenceContainer(e),o=n.dataset.objectUid;if(n.classList.add("t3js-file-reference-deleted"),!FilesControlContainer.isNewRecord(e)&&!t){const e=this.container.querySelector('[name="cmd'+n.dataset.fieldName+'[delete]"]');e.removeAttribute("disabled"),n.parentElement.insertAdjacentElement("afterbegin",e)}new RegularEvent("transitionend",(()=>{n.parentElement.removeChild(n),FormEngineValidation.validate(this.container)})).bindTo(n),this.memorizeRemoveRecord(o),n.classList.add("form-irre-object--deleted"),this.isBelowMax()&&this.toggleContainerControls(!0)}toggleContainerControls(e){const t=this.container.querySelector(Selectors.controlContainer);if(null===t)return;t.querySelectorAll("button, a").forEach((t=>{t.style.display=e?null:"none"}))}getProgress(e,t){const n="#"+t+"_header";let o;return void 0!==this.progessQueue[e]?o=this.progessQueue[e]:(o=NProgress,o.configure({parent:n,showSpinner:!1}),this.progessQueue[e]=o),o}collapseAllRecords(e){const t=this.getFormFieldForElements(),n=[];if(null!==t){const o=Utility.trimExplode(",",t.value);for(let t of o){if(t===e)continue;const o=this.container.dataset.objectGroup+Separators.structureSeparator+t,i=FilesControlContainer.getFileReferenceContainer(o);i.classList.contains(States.visible)&&(FilesControlContainer.collapseElement(i,o),FilesControlContainer.isNewRecord(o)?FilesControlContainer.updateExpandedCollapsedStateLocally(o,!1):n.push(t))}}return n}getFormFieldForElements(){const e=document.getElementsByName(this.container.dataset.formField);return e.length>0?e[0]:null}redrawSortingButtons(e,t=[]){if(0===t.length){const e=this.getFormFieldForElements();null!==e&&(t=Utility.trimExplode(",",e.value))}0!==t.length&&t.forEach(((n,o)=>{const i=FilesControlContainer.getFileReferenceContainer(e+Separators.structureSeparator+n).dataset.objectIdHash+"_header",r=document.getElementById(i),a=r.querySelector('[data-action="sort"][data-direction="'+SortDirections.UP+'"]');if(null!==a){let e="actions-move-up";0===o?(a.classList.add("disabled"),e="empty-empty"):a.classList.remove("disabled"),Icons.getIcon(e,Icons.sizes.small).then((e=>{a.replaceChild(document.createRange().createContextualFragment(e),a.querySelector(".t3js-icon"))}))}const s=r.querySelector('[data-action="sort"][data-direction="'+SortDirections.DOWN+'"]');if(null!==s){let e="actions-move-down";o===t.length-1?(s.classList.add("disabled"),e="empty-empty"):s.classList.remove("disabled"),Icons.getIcon(e,Icons.sizes.small).then((e=>{s.replaceChild(document.createRange().createContextualFragment(e),s.querySelector(".t3js-icon"))}))}}))}isBelowMax(){const e=this.getFormFieldForElements();if(null===e)return!0;if(void 0!==TYPO3.settings.FormEngineInline.config[this.container.dataset.objectGroup]){if(Utility.trimExplode(",",e.value).length>=TYPO3.settings.FormEngineInline.config[this.container.dataset.objectGroup].max)return!1}return!0}getAppearance(){if(null===this.appearance&&(this.appearance={},"string"==typeof this.container.dataset.appearance))try{this.appearance=JSON.parse(this.container.dataset.appearance)}catch(e){console.error(e)}return this.appearance}}window.customElements.define("typo3-formengine-container-files",FilesControlContainer); \ No newline at end of file +import{MessageUtility}from"@typo3/backend/utility/message-utility.js";import{AjaxDispatcher}from"@typo3/backend/form-engine/inline-relation/ajax-dispatcher.js";import NProgress from"nprogress";import Sortable from"sortablejs";import FormEngine from"@typo3/backend/form-engine.js";import FormEngineValidation from"@typo3/backend/form-engine-validation.js";import Icons from"@typo3/backend/icons.js";import InfoWindow from"@typo3/backend/info-window.js";import Modal from"@typo3/backend/modal.js";import RegularEvent from"@typo3/core/event/regular-event.js";import Severity from"@typo3/backend/severity.js";import Utility from"@typo3/backend/utility.js";var Selectors,States,Separators,SortDirections;!function(e){e.toggleSelector='[data-bs-toggle="formengine-file"]',e.controlSectionSelector=".t3js-formengine-file-header-control",e.deleteRecordButtonSelector=".t3js-editform-delete-file-reference",e.enableDisableRecordButtonSelector=".t3js-toggle-visibility-button",e.infoWindowButton='[data-action="infowindow"]',e.synchronizeLocalizeRecordButtonSelector=".t3js-synchronizelocalize-button",e.controlContainer=".t3js-file-controls"}(Selectors||(Selectors={})),function(e){e.new="isNewFileReference",e.visible="panel-visible",e.collapsed="panel-collapsed",e.notLoaded="t3js-not-loaded"}(States||(States={})),function(e){e.structureSeparator="-"}(Separators||(Separators={})),function(e){e.DOWN="down",e.UP="up"}(SortDirections||(SortDirections={}));class FilesControlContainer extends HTMLElement{constructor(){super(...arguments),this.container=null,this.ajaxDispatcher=null,this.appearance=null,this.requestQueue={},this.progessQueue={},this.noTitleString=TYPO3.lang?TYPO3.lang["FormEngine.noRecordTitle"]:"[No title]",this.handlePostMessage=e=>{if(!MessageUtility.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;if("typo3:foreignRelation:insert"===e.data.actionName){if(void 0===e.data.objectGroup)throw"No object group defined for message";if(e.data.objectGroup!==this.container.dataset.objectGroup)return;this.importRecord([e.data.objectGroup,e.data.uid]).then((()=>{if(e.source){const t={actionName:"typo3:foreignRelation:inserted",objectGroup:e.data.objectId,table:e.data.table,uid:e.data.uid};MessageUtility.send(t,e.source)}}))}}}static getFileReferenceContainer(e){return document.querySelector('[data-object-id="'+e+'"]')}static getCollapseButton(e){return document.querySelector('[aria-controls="'+e+'_fields"]')}static toggleElement(e){const t=FilesControlContainer.getFileReferenceContainer(e);t.classList.contains(States.collapsed)?FilesControlContainer.expandElement(t,e):FilesControlContainer.collapseElement(t,e)}static collapseElement(e,t){const n=FilesControlContainer.getCollapseButton(t);e.classList.remove(States.visible),e.classList.add(States.collapsed),n.setAttribute("aria-expanded","false")}static expandElement(e,t){const n=FilesControlContainer.getCollapseButton(t);e.classList.remove(States.collapsed),e.classList.add(States.visible),n.setAttribute("aria-expanded","true")}static isNewRecord(e){return FilesControlContainer.getFileReferenceContainer(e).classList.contains(States.new)}static updateExpandedCollapsedStateLocally(e,t){const n=FilesControlContainer.getFileReferenceContainer(e),o=document.getElementsByName("uc[inlineView]["+n.dataset.topmostParentTable+"]["+n.dataset.topmostParentUid+"]"+n.dataset.fieldName);o.length&&(o[0].value=t?"1":"0")}connectedCallback(){const e=this.getAttribute("identifier")||"";this.container=this.querySelector("#"+e),null!==this.container&&(this.ajaxDispatcher=new AjaxDispatcher(this.container.dataset.objectGroup),this.registerEvents())}registerEvents(){if(this.registerInfoButton(),this.registerSort(),this.registerEnableDisableButton(),this.registerDeleteButton(),this.registerSynchronizeLocalize(),this.registerToggle(),new RegularEvent("message",this.handlePostMessage).bindTo(window),this.getAppearance().useSortable){const e=document.getElementById(this.container.getAttribute("id")+"_records");new Sortable(e,{group:e.getAttribute("id"),handle:".sortableHandle",onSort:()=>{this.updateSorting()}})}}registerToggle(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation(),e.loadRecordDetails(this.closest(Selectors.toggleSelector).parentElement.dataset.objectId)})).delegateTo(this.container,`${Selectors.toggleSelector} .form-irre-header-cell:not(${Selectors.controlSectionSelector}`)}registerSort(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation(),e.changeSortingByButton(this.closest("[data-object-id]").dataset.objectId,this.dataset.direction)})).delegateTo(this.container,Selectors.controlSectionSelector+' [data-action="sort"]')}createRecord(e,t,n=null){let o=this.container.dataset.objectGroup;null!==n&&(o+=Separators.structureSeparator+n),null!==n?(FilesControlContainer.getFileReferenceContainer(o).insertAdjacentHTML("afterend",t),this.memorizeAddRecord(e,n)):(document.getElementById(this.container.getAttribute("id")+"_records").insertAdjacentHTML("beforeend",t),this.memorizeAddRecord(e,null))}async importRecord(e,t){return this.ajaxDispatcher.send(this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint("file_reference_create")),e).then((async e=>{this.isBelowMax()&&this.createRecord(e.compilerInput.uid,e.data,void 0!==t?t:null)}))}registerEnableDisableButton(){new RegularEvent("click",((e,t)=>{e.preventDefault(),e.stopImmediatePropagation();const n=t.closest("[data-object-id]").dataset.objectId,o=FilesControlContainer.getFileReferenceContainer(n),i="data"+o.dataset.fieldName+"["+t.dataset.hiddenField+"]",r=document.querySelector('[data-formengine-input-name="'+i+'"'),a=document.querySelector('[name="'+i+'"');null!==r&&null!==a&&(r.checked=!r.checked,a.value=r.checked?"1":"0",FormEngineValidation.markFieldAsChanged(r));const s="t3-form-field-container-inline-hidden";let l;o.classList.contains(s)?(l="actions-edit-hide",o.classList.remove(s)):(l="actions-edit-unhide",o.classList.add(s)),Icons.getIcon(l,Icons.sizes.small).then((e=>{t.replaceChild(document.createRange().createContextualFragment(e),t.querySelector(".t3js-icon"))}))})).delegateTo(this.container,Selectors.enableDisableRecordButtonSelector)}registerInfoButton(){new RegularEvent("click",(function(e){e.preventDefault(),e.stopImmediatePropagation(),InfoWindow.showItem(this.dataset.infoTable,this.dataset.infoUid)})).delegateTo(this.container,Selectors.infoWindowButton)}registerDeleteButton(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const n=TYPO3.lang["label.confirm.delete_record.title"]||"Delete this record?",o=TYPO3.lang["label.confirm.delete_record.content"]||"Are you sure you want to delete this record?";Modal.confirm(n,o,Severity.warning,[{text:TYPO3.lang["buttons.confirm.delete_record.no"]||"Cancel",active:!0,btnClass:"btn-default",name:"no",trigger:(e,t)=>t.hideModal()},{text:TYPO3.lang["buttons.confirm.delete_record.yes"]||"Yes, delete this record",btnClass:"btn-warning",name:"yes",trigger:(t,n)=>{e.deleteRecord(this.closest("[data-object-id]").dataset.objectId),n.hideModal()}}])})).delegateTo(this.container,Selectors.deleteRecordButtonSelector)}registerSynchronizeLocalize(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation(),e.ajaxDispatcher.send(e.ajaxDispatcher.newRequest(e.ajaxDispatcher.getEndpoint("file_reference_synchronizelocalize")),[e.container.dataset.objectGroup,this.dataset.type]).then((async t=>{document.getElementById(e.container.getAttribute("id")+"_records").insertAdjacentHTML("beforeend",t.data);const n=e.container.dataset.objectGroup+Separators.structureSeparator;for(const o of t.compilerInput.delete)e.deleteRecord(n+o,!0);for(const o of Object.values(t.compilerInput.localize)){if(void 0!==o.remove){const e=FilesControlContainer.getFileReferenceContainer(n+o.remove);e.parentElement.removeChild(e)}e.memorizeAddRecord(o.uid,null)}}))})).delegateTo(this.container,Selectors.synchronizeLocalizeRecordButtonSelector)}loadRecordDetails(e){const t=document.getElementById(e+"_fields"),n=FilesControlContainer.getFileReferenceContainer(e),o=void 0!==this.requestQueue[e];if(null!==t&&!n.classList.contains(States.notLoaded))this.collapseExpandRecord(e);else{const i=this.getProgress(e,n.dataset.objectIdHash);if(o)this.requestQueue[e].abort(),delete this.requestQueue[e],delete this.progessQueue[e],i.done();else{const o=this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint("file_reference_details"));this.ajaxDispatcher.send(o,[e]).then((async o=>{delete this.requestQueue[e],delete this.progessQueue[e],n.classList.remove(States.notLoaded),t.innerHTML=o.data,this.collapseExpandRecord(e),i.done(),FormEngine.reinitialize(),FormEngineValidation.initializeInputFields(),FormEngineValidation.validate(this.container)})),this.requestQueue[e]=o,i.start()}}}collapseExpandRecord(e){const t=FilesControlContainer.getFileReferenceContainer(e),n=!0===this.getAppearance().expandSingle,o=t.classList.contains(States.collapsed);let i=[];const r=[];n&&o&&(i=this.collapseAllRecords(t.dataset.objectUid)),FilesControlContainer.toggleElement(e),FilesControlContainer.isNewRecord(e)?FilesControlContainer.updateExpandedCollapsedStateLocally(e,o):o?r.push(t.dataset.objectUid):o||i.push(t.dataset.objectUid),this.ajaxDispatcher.send(this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint("file_reference_expandcollapse")),[e,r.join(","),i.join(",")])}memorizeAddRecord(e,t=null){const n=this.getFormFieldForElements();if(null===n)return;let o=Utility.trimExplode(",",n.value);if(t){const n=[];for(let i=0;i<o.length;i++)o[i].length&&n.push(o[i]),t===o[i]&&n.push(e);o=n}else o.push(e);n.value=o.join(","),n.classList.add("has-change"),document.dispatchEvent(new Event("change")),this.redrawSortingButtons(this.container.dataset.objectGroup,o),this.isBelowMax()||this.toggleContainerControls(!1),FormEngine.reinitialize(),FormEngineValidation.initializeInputFields(),FormEngineValidation.validate(this.container)}memorizeRemoveRecord(e){const t=this.getFormFieldForElements();if(null===t)return[];const n=Utility.trimExplode(",",t.value),o=n.indexOf(e);return o>-1&&(delete n[o],t.value=n.join(","),t.classList.add("has-change"),document.dispatchEvent(new Event("change")),this.redrawSortingButtons(this.container.dataset.objectGroup,n)),n}changeSortingByButton(e,t){const n=FilesControlContainer.getFileReferenceContainer(e),o=n.dataset.objectUid,i=document.getElementById(this.container.getAttribute("id")+"_records"),r=Array.from(i.children).map((e=>e.dataset.objectUid)),a=r.indexOf(o);let s=!1;if(t===SortDirections.UP&&a>0?(r[a]=r[a-1],r[a-1]=o,s=!0):t===SortDirections.DOWN&&a<r.length-1&&(r[a]=r[a+1],r[a+1]=o,s=!0),s){const e=this.container.dataset.objectGroup+Separators.structureSeparator,o=t===SortDirections.UP?1:0;n.parentElement.insertBefore(FilesControlContainer.getFileReferenceContainer(e+r[a-o]),FilesControlContainer.getFileReferenceContainer(e+r[a+1-o])),this.updateSorting()}}updateSorting(){const e=this.getFormFieldForElements();if(null===e)return;const t=document.getElementById(this.container.getAttribute("id")+"_records"),n=Array.from(t.querySelectorAll('[data-object-parent-group="'+this.container.dataset.objectGroup+'"][data-placeholder-record="0"]')).map((e=>e.dataset.objectUid));e.value=n.join(","),e.classList.add("has-change"),document.dispatchEvent(new Event("formengine:files:sorting-changed")),document.dispatchEvent(new Event("change")),this.redrawSortingButtons(this.container.dataset.objectGroup,n)}deleteRecord(e,t=!1){const n=FilesControlContainer.getFileReferenceContainer(e),o=n.dataset.objectUid;if(n.classList.add("t3js-file-reference-deleted"),!FilesControlContainer.isNewRecord(e)&&!t){const e=this.container.querySelector('[name="cmd'+n.dataset.fieldName+'[delete]"]');e.removeAttribute("disabled"),n.parentElement.insertAdjacentElement("afterbegin",e)}new RegularEvent("transitionend",(()=>{n.parentElement.removeChild(n),FormEngineValidation.validate(this.container)})).bindTo(n),this.memorizeRemoveRecord(o),n.classList.add("form-irre-object--deleted"),this.isBelowMax()&&this.toggleContainerControls(!0)}toggleContainerControls(e){const t=this.container.querySelector(Selectors.controlContainer);if(null===t)return;t.querySelectorAll("button, a").forEach((t=>{t.style.display=e?null:"none"}))}getProgress(e,t){const n="#"+t+"_header";let o;return void 0!==this.progessQueue[e]?o=this.progessQueue[e]:(o=NProgress,o.configure({parent:n,showSpinner:!1}),this.progessQueue[e]=o),o}collapseAllRecords(e){const t=this.getFormFieldForElements(),n=[];if(null!==t){const o=Utility.trimExplode(",",t.value);for(const t of o){if(t===e)continue;const o=this.container.dataset.objectGroup+Separators.structureSeparator+t,i=FilesControlContainer.getFileReferenceContainer(o);i.classList.contains(States.visible)&&(FilesControlContainer.collapseElement(i,o),FilesControlContainer.isNewRecord(o)?FilesControlContainer.updateExpandedCollapsedStateLocally(o,!1):n.push(t))}}return n}getFormFieldForElements(){const e=document.getElementsByName(this.container.dataset.formField);return e.length>0?e[0]:null}redrawSortingButtons(e,t=[]){if(0===t.length){const e=this.getFormFieldForElements();null!==e&&(t=Utility.trimExplode(",",e.value))}0!==t.length&&t.forEach(((n,o)=>{const i=FilesControlContainer.getFileReferenceContainer(e+Separators.structureSeparator+n).dataset.objectIdHash+"_header",r=document.getElementById(i),a=r.querySelector('[data-action="sort"][data-direction="'+SortDirections.UP+'"]');if(null!==a){let e="actions-move-up";0===o?(a.classList.add("disabled"),e="empty-empty"):a.classList.remove("disabled"),Icons.getIcon(e,Icons.sizes.small).then((e=>{a.replaceChild(document.createRange().createContextualFragment(e),a.querySelector(".t3js-icon"))}))}const s=r.querySelector('[data-action="sort"][data-direction="'+SortDirections.DOWN+'"]');if(null!==s){let e="actions-move-down";o===t.length-1?(s.classList.add("disabled"),e="empty-empty"):s.classList.remove("disabled"),Icons.getIcon(e,Icons.sizes.small).then((e=>{s.replaceChild(document.createRange().createContextualFragment(e),s.querySelector(".t3js-icon"))}))}}))}isBelowMax(){const e=this.getFormFieldForElements();if(null===e)return!0;if(void 0!==TYPO3.settings.FormEngineInline.config[this.container.dataset.objectGroup]){if(Utility.trimExplode(",",e.value).length>=TYPO3.settings.FormEngineInline.config[this.container.dataset.objectGroup].max)return!1}return!0}getAppearance(){if(null===this.appearance&&(this.appearance={},"string"==typeof this.container.dataset.appearance))try{this.appearance=JSON.parse(this.container.dataset.appearance)}catch(e){console.error(e)}return this.appearance}}window.customElements.define("typo3-formengine-container-files",FilesControlContainer); \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/flex-form-container-container.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/flex-form-container-container.js index 761f558b41bb..74cc85dec1ff 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/flex-form-container-container.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/flex-form-container-container.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{Collapse}from"bootstrap";import SecurityUtility from"@typo3/core/security-utility.js";import Modal from"@typo3/backend/modal.js";import RegularEvent from"@typo3/core/event/regular-event.js";import Severity from"@typo3/backend/severity.js";var Selectors;!function(e){e.toggleSelector='[data-bs-toggle="flexform-inline"]',e.actionFieldSelector=".t3js-flex-control-action",e.toggleFieldSelector=".t3js-flex-control-toggle",e.controlSectionSelector=".t3js-formengine-irre-control",e.sectionContentContainerSelector=".t3js-flex-section-content",e.deleteContainerButtonSelector=".t3js-delete",e.contentPreviewSelector=".content-preview"}(Selectors||(Selectors={}));class FlexFormContainerContainer{constructor(e,t){this.securityUtility=new SecurityUtility,this.parentContainer=e,this.container=t,this.containerContent=t.querySelector(Selectors.sectionContentContainerSelector),this.containerId=t.dataset.flexformContainerId,this.panelHeading=t.querySelector('[data-bs-target="#flexform-container-'+this.containerId+'"]'),this.panelButton=this.panelHeading.querySelector('[aria-controls="flexform-container-'+this.containerId+'"]'),this.toggleField=t.querySelector(Selectors.toggleFieldSelector),this.registerEvents(),this.generatePreview()}static getCollapseInstance(e){return Collapse.getInstance(e)??new Collapse(e,{toggle:!1})}getStatus(){return{id:this.containerId,collapsed:"false"===this.panelButton.getAttribute("aria-expanded")}}registerEvents(){this.parentContainer.isRestructuringAllowed()&&this.registerDelete(),this.registerToggle(),this.registerPanelToggle()}registerDelete(){new RegularEvent("click",(()=>{const e=TYPO3.lang["flexform.section.delete.title"]||"Delete this container?",t=TYPO3.lang["flexform.section.delete.message"]||"Are you sure you want to delete this container?",n=Modal.confirm(e,t,Severity.warning,[{text:TYPO3.lang["buttons.confirm.delete_record.no"]||"Cancel",active:!0,btnClass:"btn-default",name:"no"},{text:TYPO3.lang["buttons.confirm.delete_record.yes"]||"Yes, delete this container",btnClass:"btn-warning",name:"yes"}]);n.addEventListener("button.clicked",(e=>{if("yes"===e.target.name){const e=this.container.querySelector(Selectors.actionFieldSelector);e.value="DELETE",this.container.appendChild(e),this.container.classList.add("t3-flex-section--deleted"),this.container.classList.add("has-change"),new RegularEvent("transitionend",(()=>{this.container.classList.add("hidden");const e=new CustomEvent("formengine:flexform:container-deleted",{detail:{containerId:this.containerId}});this.parentContainer.getContainer().dispatchEvent(e)})).bindTo(this.container)}n.hideModal()}))})).bindTo(this.container.querySelector(Selectors.deleteContainerButtonSelector))}registerToggle(){new RegularEvent("click",(()=>{FlexFormContainerContainer.getCollapseInstance(this.containerContent).toggle(),this.generatePreview()})).delegateTo(this.container,`${Selectors.toggleSelector} .form-irre-header-cell:not(${Selectors.controlSectionSelector}`)}registerPanelToggle(){["hide.bs.collapse","show.bs.collapse"].forEach((e=>{new RegularEvent(e,(e=>{const t="hide.bs.collapse"===e.type;this.toggleField.value=t?"1":"0",this.panelButton.setAttribute("aria-expanded",t?"false":"true"),this.panelButton.parentElement.classList.toggle("collapsed",t)})).bindTo(this.containerContent)}))}generatePreview(){let e="";if(this.getStatus().collapsed){const t=this.containerContent.querySelectorAll('input[type="text"], textarea');for(let n of t){let t=this.securityUtility.stripHtml(n.value);t.length>50&&(t=t.substring(0,50)+"..."),e+=(e?" / ":"")+t}}this.panelHeading.querySelector(Selectors.contentPreviewSelector).textContent=e}}export default FlexFormContainerContainer; \ No newline at end of file +import{Collapse}from"bootstrap";import SecurityUtility from"@typo3/core/security-utility.js";import Modal from"@typo3/backend/modal.js";import RegularEvent from"@typo3/core/event/regular-event.js";import Severity from"@typo3/backend/severity.js";var Selectors;!function(e){e.toggleSelector='[data-bs-toggle="flexform-inline"]',e.actionFieldSelector=".t3js-flex-control-action",e.toggleFieldSelector=".t3js-flex-control-toggle",e.controlSectionSelector=".t3js-formengine-irre-control",e.sectionContentContainerSelector=".t3js-flex-section-content",e.deleteContainerButtonSelector=".t3js-delete",e.contentPreviewSelector=".content-preview"}(Selectors||(Selectors={}));class FlexFormContainerContainer{constructor(e,t){this.securityUtility=new SecurityUtility,this.parentContainer=e,this.container=t,this.containerContent=t.querySelector(Selectors.sectionContentContainerSelector),this.containerId=t.dataset.flexformContainerId,this.panelHeading=t.querySelector('[data-bs-target="#flexform-container-'+this.containerId+'"]'),this.panelButton=this.panelHeading.querySelector('[aria-controls="flexform-container-'+this.containerId+'"]'),this.toggleField=t.querySelector(Selectors.toggleFieldSelector),this.registerEvents(),this.generatePreview()}static getCollapseInstance(e){return Collapse.getInstance(e)??new Collapse(e,{toggle:!1})}getStatus(){return{id:this.containerId,collapsed:"false"===this.panelButton.getAttribute("aria-expanded")}}registerEvents(){this.parentContainer.isRestructuringAllowed()&&this.registerDelete(),this.registerToggle(),this.registerPanelToggle()}registerDelete(){new RegularEvent("click",(()=>{const e=TYPO3.lang["flexform.section.delete.title"]||"Delete this container?",t=TYPO3.lang["flexform.section.delete.message"]||"Are you sure you want to delete this container?",n=Modal.confirm(e,t,Severity.warning,[{text:TYPO3.lang["buttons.confirm.delete_record.no"]||"Cancel",active:!0,btnClass:"btn-default",name:"no"},{text:TYPO3.lang["buttons.confirm.delete_record.yes"]||"Yes, delete this container",btnClass:"btn-warning",name:"yes"}]);n.addEventListener("button.clicked",(e=>{if("yes"===e.target.name){const e=this.container.querySelector(Selectors.actionFieldSelector);e.value="DELETE",this.container.appendChild(e),this.container.classList.add("t3-flex-section--deleted"),this.container.classList.add("has-change"),new RegularEvent("transitionend",(()=>{this.container.classList.add("hidden");const e=new CustomEvent("formengine:flexform:container-deleted",{detail:{containerId:this.containerId}});this.parentContainer.getContainer().dispatchEvent(e)})).bindTo(this.container)}n.hideModal()}))})).bindTo(this.container.querySelector(Selectors.deleteContainerButtonSelector))}registerToggle(){new RegularEvent("click",(()=>{FlexFormContainerContainer.getCollapseInstance(this.containerContent).toggle(),this.generatePreview()})).delegateTo(this.container,`${Selectors.toggleSelector} .form-irre-header-cell:not(${Selectors.controlSectionSelector}`)}registerPanelToggle(){["hide.bs.collapse","show.bs.collapse"].forEach((e=>{new RegularEvent(e,(e=>{const t="hide.bs.collapse"===e.type;this.toggleField.value=t?"1":"0",this.panelButton.setAttribute("aria-expanded",t?"false":"true"),this.panelButton.parentElement.classList.toggle("collapsed",t)})).bindTo(this.containerContent)}))}generatePreview(){let e="";if(this.getStatus().collapsed){const t=this.containerContent.querySelectorAll('input[type="text"], textarea');for(const n of t){let t=this.securityUtility.stripHtml(n.value);t.length>50&&(t=t.substring(0,50)+"..."),e+=(e?" / ":"")+t}}this.panelHeading.querySelector(Selectors.contentPreviewSelector).textContent=e}}export default FlexFormContainerContainer; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/flex-form-section-container.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/flex-form-section-container.js index b760431caf32..6c9fe1a81528 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/flex-form-section-container.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/flex-form-section-container.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{Collapse}from"bootstrap";import Sortable from"sortablejs";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import DocumentService from"@typo3/core/document-service.js";import FlexFormContainerContainer from"@typo3/backend/form-engine/container/flex-form-container-container.js";import FormEngine from"@typo3/backend/form-engine.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{JavaScriptItemProcessor}from"@typo3/core/java-script-item-processor.js";var Selectors;!function(e){e.toggleAllSelector=".t3-form-flexsection-toggle",e.addContainerSelector=".t3js-flex-container-add",e.actionFieldSelector=".t3js-flex-control-action",e.sectionContainerSelector=".t3js-flex-section",e.sectionContentContainerSelector=".t3js-flex-section-content",e.sortContainerButtonSelector=".t3js-sortable-handle"}(Selectors||(Selectors={}));class FlexFormSectionContainer{constructor(e){this.allowRestructure=!1,this.flexformContainerContainers=[],this.updateSorting=e=>{this.container.querySelectorAll(Selectors.actionFieldSelector).forEach(((e,t)=>{e.value=t.toString()})),this.updateToggleAllState(),this.flexformContainerContainers.splice(e.newIndex,0,this.flexformContainerContainers.splice(e.oldIndex,1)[0]),document.dispatchEvent(new Event("formengine:flexform:sorting-changed"))},this.sectionContainerId=e,DocumentService.ready().then((t=>{this.container=t.getElementById(e),this.sectionContainer=this.container.querySelector(this.container.dataset.section),this.allowRestructure="1"===this.sectionContainer.dataset.t3FlexAllowRestructure,this.registerEvents(),this.registerContainers()}))}static getCollapseInstance(e){return Collapse.getInstance(e)??new Collapse(e,{toggle:!1})}getContainer(){return this.container}isRestructuringAllowed(){return this.allowRestructure}registerEvents(){this.allowRestructure&&(this.registerSortable(),this.registerContainerDeleted()),this.registerToggleAll(),this.registerCreateNewContainer(),this.registerPanelToggle()}registerContainers(){const e=this.container.querySelectorAll(Selectors.sectionContainerSelector);for(let t of e)this.flexformContainerContainers.push(new FlexFormContainerContainer(this,t));this.updateToggleAllState()}getToggleAllButton(){return this.container.querySelector(Selectors.toggleAllSelector)}registerSortable(){new Sortable(this.sectionContainer,{group:this.sectionContainer.id,handle:Selectors.sortContainerButtonSelector,onSort:this.updateSorting})}registerToggleAll(){new RegularEvent("click",(e=>{const t="true"===e.target.dataset.expandAll,n=this.container.querySelectorAll(Selectors.sectionContentContainerSelector);for(let e of n)t?FlexFormSectionContainer.getCollapseInstance(e).show():FlexFormSectionContainer.getCollapseInstance(e).hide()})).bindTo(this.getToggleAllButton())}registerCreateNewContainer(){new RegularEvent("click",((e,t)=>{e.preventDefault(),this.createNewContainer(t.dataset)})).delegateTo(this.container,Selectors.addContainerSelector)}createNewContainer(dataset){new AjaxRequest(TYPO3.settings.ajaxUrls.record_flex_container_add).post({vanillaUid:dataset.vanillauid,databaseRowUid:dataset.databaserowuid,command:dataset.command,tableName:dataset.tablename,fieldName:dataset.fieldname,recordTypeValue:dataset.recordtypevalue,dataStructureIdentifier:JSON.parse(dataset.datastructureidentifier),flexFormSheetName:dataset.flexformsheetname,flexFormFieldName:dataset.flexformfieldname,flexFormContainerName:dataset.flexformcontainername}).then((async response=>{const data=await response.resolve(),createdContainer=(new DOMParser).parseFromString(data.html,"text/html").body.firstElementChild;this.flexformContainerContainers.push(new FlexFormContainerContainer(this,createdContainer));const sectionContainer=document.querySelector(dataset.target);if(sectionContainer.insertAdjacentElement("beforeend",createdContainer),data.scriptItems instanceof Array&&data.scriptItems.length>0){const e=new JavaScriptItemProcessor;e.processItems(data.scriptItems)}if(data.scriptCall&&data.scriptCall.length>0)for(let value of data.scriptCall)eval(value);if(data.stylesheetFiles&&data.stylesheetFiles.length>0)for(let e of data.stylesheetFiles){let t=document.createElement("link");t.rel="stylesheet",t.type="text/css",t.href=e,document.head.appendChild(t)}this.updateToggleAllState(),FormEngine.reinitialize(),FormEngine.Validation.initializeInputFields(),FormEngine.Validation.validate(sectionContainer),this.container.classList.add("has-change")}))}registerContainerDeleted(){new RegularEvent("formengine:flexform:container-deleted",(e=>{const t=e.detail.containerId;this.flexformContainerContainers=this.flexformContainerContainers.filter((e=>e.getStatus().id!==t)),FormEngine.Validation.validate(this.container),this.updateToggleAllState()})).bindTo(this.container)}registerPanelToggle(){["hide.bs.collapse","show.bs.collapse"].forEach((e=>{new RegularEvent(e,(()=>{this.updateToggleAllState()})).delegateTo(this.container,Selectors.sectionContentContainerSelector)}))}updateToggleAllState(){if(this.flexformContainerContainers.length>0){const e=this.flexformContainerContainers.find(Boolean);this.getToggleAllButton().dataset.expandAll=!0===e.getStatus().collapsed?"true":"false"}}}export default FlexFormSectionContainer; \ No newline at end of file +import{Collapse}from"bootstrap";import Sortable from"sortablejs";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import DocumentService from"@typo3/core/document-service.js";import FlexFormContainerContainer from"@typo3/backend/form-engine/container/flex-form-container-container.js";import FormEngine from"@typo3/backend/form-engine.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{JavaScriptItemProcessor}from"@typo3/core/java-script-item-processor.js";var Selectors;!function(e){e.toggleAllSelector=".t3-form-flexsection-toggle",e.addContainerSelector=".t3js-flex-container-add",e.actionFieldSelector=".t3js-flex-control-action",e.sectionContainerSelector=".t3js-flex-section",e.sectionContentContainerSelector=".t3js-flex-section-content",e.sortContainerButtonSelector=".t3js-sortable-handle"}(Selectors||(Selectors={}));class FlexFormSectionContainer{constructor(e){this.allowRestructure=!1,this.flexformContainerContainers=[],this.updateSorting=e=>{this.container.querySelectorAll(Selectors.actionFieldSelector).forEach(((e,t)=>{e.value=t.toString()})),this.updateToggleAllState(),this.flexformContainerContainers.splice(e.newIndex,0,this.flexformContainerContainers.splice(e.oldIndex,1)[0]),document.dispatchEvent(new Event("formengine:flexform:sorting-changed"))},this.sectionContainerId=e,DocumentService.ready().then((t=>{this.container=t.getElementById(e),this.sectionContainer=this.container.querySelector(this.container.dataset.section),this.allowRestructure="1"===this.sectionContainer.dataset.t3FlexAllowRestructure,this.registerEvents(),this.registerContainers()}))}static getCollapseInstance(e){return Collapse.getInstance(e)??new Collapse(e,{toggle:!1})}getContainer(){return this.container}isRestructuringAllowed(){return this.allowRestructure}registerEvents(){this.allowRestructure&&(this.registerSortable(),this.registerContainerDeleted()),this.registerToggleAll(),this.registerCreateNewContainer(),this.registerPanelToggle()}registerContainers(){const e=this.container.querySelectorAll(Selectors.sectionContainerSelector);for(const t of e)this.flexformContainerContainers.push(new FlexFormContainerContainer(this,t));this.updateToggleAllState()}getToggleAllButton(){return this.container.querySelector(Selectors.toggleAllSelector)}registerSortable(){new Sortable(this.sectionContainer,{group:this.sectionContainer.id,handle:Selectors.sortContainerButtonSelector,onSort:this.updateSorting})}registerToggleAll(){new RegularEvent("click",(e=>{const t="true"===e.target.dataset.expandAll,n=this.container.querySelectorAll(Selectors.sectionContentContainerSelector);for(const e of n)t?FlexFormSectionContainer.getCollapseInstance(e).show():FlexFormSectionContainer.getCollapseInstance(e).hide()})).bindTo(this.getToggleAllButton())}registerCreateNewContainer(){new RegularEvent("click",((e,t)=>{e.preventDefault(),this.createNewContainer(t.dataset)})).delegateTo(this.container,Selectors.addContainerSelector)}createNewContainer(dataset){new AjaxRequest(TYPO3.settings.ajaxUrls.record_flex_container_add).post({vanillaUid:dataset.vanillauid,databaseRowUid:dataset.databaserowuid,command:dataset.command,tableName:dataset.tablename,fieldName:dataset.fieldname,recordTypeValue:dataset.recordtypevalue,dataStructureIdentifier:JSON.parse(dataset.datastructureidentifier),flexFormSheetName:dataset.flexformsheetname,flexFormFieldName:dataset.flexformfieldname,flexFormContainerName:dataset.flexformcontainername}).then((async response=>{const data=await response.resolve(),createdContainer=(new DOMParser).parseFromString(data.html,"text/html").body.firstElementChild;this.flexformContainerContainers.push(new FlexFormContainerContainer(this,createdContainer));const sectionContainer=document.querySelector(dataset.target);if(sectionContainer.insertAdjacentElement("beforeend",createdContainer),data.scriptItems instanceof Array&&data.scriptItems.length>0){const e=new JavaScriptItemProcessor;e.processItems(data.scriptItems)}if(data.scriptCall&&data.scriptCall.length>0)for(const value of data.scriptCall)eval(value);if(data.stylesheetFiles&&data.stylesheetFiles.length>0)for(const e of data.stylesheetFiles){const t=document.createElement("link");t.rel="stylesheet",t.type="text/css",t.href=e,document.head.appendChild(t)}this.updateToggleAllState(),FormEngine.reinitialize(),FormEngine.Validation.initializeInputFields(),FormEngine.Validation.validate(sectionContainer),this.container.classList.add("has-change")}))}registerContainerDeleted(){new RegularEvent("formengine:flexform:container-deleted",(e=>{const t=e.detail.containerId;this.flexformContainerContainers=this.flexformContainerContainers.filter((e=>e.getStatus().id!==t)),FormEngine.Validation.validate(this.container),this.updateToggleAllState()})).bindTo(this.container)}registerPanelToggle(){["hide.bs.collapse","show.bs.collapse"].forEach((e=>{new RegularEvent(e,(()=>{this.updateToggleAllState()})).delegateTo(this.container,Selectors.sectionContentContainerSelector)}))}updateToggleAllState(){if(this.flexformContainerContainers.length>0){const e=this.flexformContainerContainers.find(Boolean);this.getToggleAllButton().dataset.expandAll=!0===e.getStatus().collapsed?"true":"false"}}}export default FlexFormSectionContainer; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/inline-control-container.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/inline-control-container.js index 8f5caf37924a..97f091526bd6 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/inline-control-container.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/inline-control-container.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{MessageUtility}from"@typo3/backend/utility/message-utility.js";import{AjaxDispatcher}from"@typo3/backend/form-engine/inline-relation/ajax-dispatcher.js";import DocumentService from"@typo3/core/document-service.js";import NProgress from"nprogress";import Sortable from"sortablejs";import FormEngine from"@typo3/backend/form-engine.js";import FormEngineValidation from"@typo3/backend/form-engine-validation.js";import Icons from"@typo3/backend/icons.js";import InfoWindow from"@typo3/backend/info-window.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import RegularEvent from"@typo3/core/event/regular-event.js";import Severity from"@typo3/backend/severity.js";import Utility from"@typo3/backend/utility.js";var Selectors,States,Separators,SortDirections;!function(e){e.toggleSelector='[data-bs-toggle="formengine-inline"]',e.controlSectionSelector=".t3js-formengine-irre-control",e.createNewRecordButtonSelector=".t3js-create-new-button",e.createNewRecordBySelectorSelector=".t3js-create-new-selector",e.deleteRecordButtonSelector=".t3js-editform-delete-inline-record",e.enableDisableRecordButtonSelector=".t3js-toggle-visibility-button",e.infoWindowButton='[data-action="infowindow"]',e.synchronizeLocalizeRecordButtonSelector=".t3js-synchronizelocalize-button",e.uniqueValueSelectors="select.t3js-inline-unique",e.revertUniqueness=".t3js-revert-unique",e.controlContainer=".t3js-inline-controls",e.controlTopOuterContainer=".t3js-inline-controls-top-outer-container"}(Selectors||(Selectors={})),function(e){e.new="inlineIsNewRecord",e.visible="panel-visible",e.collapsed="panel-collapsed",e.notLoaded="t3js-not-loaded"}(States||(States={})),function(e){e.structureSeparator="-"}(Separators||(Separators={})),function(e){e.DOWN="down",e.UP="up"}(SortDirections||(SortDirections={}));class InlineControlContainer{constructor(e){this.container=null,this.ajaxDispatcher=null,this.appearance=null,this.requestQueue={},this.progessQueue={},this.noTitleString=TYPO3.lang?TYPO3.lang["FormEngine.noRecordTitle"]:"[No title]",this.handlePostMessage=e=>{if(!MessageUtility.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;if("typo3:foreignRelation:insert"===e.data.actionName){if(void 0===e.data.objectGroup)throw"No object group defined for message";if(e.data.objectGroup!==this.container.dataset.objectGroup)return;if(this.isUniqueElementUsed(parseInt(e.data.uid,10),e.data.table))return void Notification.error("There is already a relation to the selected element");this.importRecord([e.data.objectGroup,e.data.uid]).then((()=>{if(e.source){const t={actionName:"typo3:foreignRelation:inserted",objectGroup:e.data.objectId,table:e.data.table,uid:e.data.uid};MessageUtility.send(t,e.source)}}))}},DocumentService.ready().then((t=>{this.container=t.getElementById(e),this.ajaxDispatcher=new AjaxDispatcher(this.container.dataset.objectGroup),this.registerEvents()}))}static getInlineRecordContainer(e){return document.querySelector('[data-object-id="'+e+'"]')}static getCollapseButton(e){return document.querySelector('[aria-controls="'+e+'_fields"]')}static toggleElement(e){const t=InlineControlContainer.getInlineRecordContainer(e);t.classList.contains(States.collapsed)?InlineControlContainer.expandElement(t,e):InlineControlContainer.collapseElement(t,e)}static collapseElement(e,t){const n=InlineControlContainer.getCollapseButton(t);e.classList.remove(States.visible),e.classList.add(States.collapsed),n.setAttribute("aria-expanded","false")}static expandElement(e,t){const n=InlineControlContainer.getCollapseButton(t);e.classList.remove(States.collapsed),e.classList.add(States.visible),n.setAttribute("aria-expanded","true")}static isNewRecord(e){return InlineControlContainer.getInlineRecordContainer(e).classList.contains(States.new)}static updateExpandedCollapsedStateLocally(e,t){const n=InlineControlContainer.getInlineRecordContainer(e),o="uc[inlineView]["+n.dataset.topmostParentTable+"]["+n.dataset.topmostParentUid+"]"+n.dataset.fieldName,i=document.getElementsByName(o);i.length&&(i[0].value=t?"1":"0")}static getValuesFromHashMap(e){return Object.keys(e).map((t=>e[t]))}static selectOptionValueExists(e,t){return null!==e.querySelector('option[value="'+t+'"]')}static removeSelectOptionByValue(e,t){const n=e.querySelector('option[value="'+t+'"]');null!==n&&n.remove()}static reAddSelectOption(e,t,n){if(InlineControlContainer.selectOptionValueExists(e,t))return;const o=e.querySelectorAll("option");let i=-1;for(let e of Object.keys(n.possible)){if(e===t)break;for(let t=0;t<o.length;++t){if(o[t].value===e){i=t;break}}}-1===i?i=0:i<o.length&&i++;const r=document.createElement("option");r.text=n.possible[t],r.value=t,e.insertBefore(r,e.options[i])}registerEvents(){if(this.registerInfoButton(),this.registerSort(),this.registerCreateRecordButton(),this.registerEnableDisableButton(),this.registerDeleteButton(),this.registerSynchronizeLocalize(),this.registerRevertUniquenessAction(),this.registerToggle(),this.registerCreateRecordBySelector(),this.registerUniqueSelectFieldChanged(),new RegularEvent("message",this.handlePostMessage).bindTo(window),this.getAppearance().useSortable){const e=document.getElementById(this.container.getAttribute("id")+"_records");new Sortable(e,{group:e.getAttribute("id"),handle:".sortableHandle",onSort:()=>{this.updateSorting()}})}}registerToggle(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation(),e.loadRecordDetails(this.closest(Selectors.toggleSelector).parentElement.dataset.objectId)})).delegateTo(this.container,`${Selectors.toggleSelector} .form-irre-header-cell:not(${Selectors.controlSectionSelector}`)}registerSort(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation(),e.changeSortingByButton(this.closest("[data-object-id]").dataset.objectId,this.dataset.direction)})).delegateTo(this.container,Selectors.controlSectionSelector+' [data-action="sort"]')}registerCreateRecordButton(){const e=this;new RegularEvent("click",(function(t){if(t.preventDefault(),t.stopImmediatePropagation(),e.isBelowMax()){let t=e.container.dataset.objectGroup;void 0!==this.dataset.recordUid&&(t+=Separators.structureSeparator+this.dataset.recordUid),e.importRecord([t,e.container.querySelector(Selectors.createNewRecordBySelectorSelector)?.value],this.dataset.recordUid??null)}})).delegateTo(this.container,Selectors.createNewRecordButtonSelector)}registerCreateRecordBySelector(){const e=this;new RegularEvent("change",(function(t){t.preventDefault(),t.stopImmediatePropagation();const n=this.options[this.selectedIndex].getAttribute("value");e.importRecord([e.container.dataset.objectGroup,n])})).delegateTo(this.container,Selectors.createNewRecordBySelectorSelector)}createRecord(e,t,n=null,o=null){let i=this.container.dataset.objectGroup;null!==n&&(i+=Separators.structureSeparator+n),null!==n?(InlineControlContainer.getInlineRecordContainer(i).insertAdjacentHTML("afterend",t),this.memorizeAddRecord(e,n,o)):(document.getElementById(this.container.getAttribute("id")+"_records").insertAdjacentHTML("beforeend",t),this.memorizeAddRecord(e,null,o))}async importRecord(e,t){return this.ajaxDispatcher.send(this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint("record_inline_create")),e).then((async e=>{this.isBelowMax()&&this.createRecord(e.compilerInput.uid,e.data,void 0!==t?t:null,void 0!==e.compilerInput.childChildUid?e.compilerInput.childChildUid:null)}))}registerEnableDisableButton(){new RegularEvent("click",((e,t)=>{e.preventDefault(),e.stopImmediatePropagation();const n=t.closest("[data-object-id]").dataset.objectId,o=InlineControlContainer.getInlineRecordContainer(n),i="data"+o.dataset.fieldName+"["+t.dataset.hiddenField+"]",r=document.querySelector('[data-formengine-input-name="'+i+'"'),a=document.querySelector('[name="'+i+'"');null!==r&&null!==a&&(r.checked=!r.checked,a.value=r.checked?"1":"0",FormEngineValidation.markFieldAsChanged(r));const s="t3-form-field-container-inline-hidden";let l;o.classList.contains(s)?(l="actions-edit-hide",o.classList.remove(s)):(l="actions-edit-unhide",o.classList.add(s)),Icons.getIcon(l,Icons.sizes.small).then((e=>{t.replaceChild(document.createRange().createContextualFragment(e),t.querySelector(".t3js-icon"))}))})).delegateTo(this.container,Selectors.enableDisableRecordButtonSelector)}registerInfoButton(){new RegularEvent("click",(function(e){e.preventDefault(),e.stopImmediatePropagation(),InfoWindow.showItem(this.dataset.infoTable,this.dataset.infoUid)})).delegateTo(this.container,Selectors.infoWindowButton)}registerDeleteButton(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const n=TYPO3.lang["label.confirm.delete_record.title"]||"Delete this record?",o=(TYPO3.lang["label.confirm.delete_record.content"]||"Are you sure you want to delete the record '%s'?").replace("%s",this.dataset.recordInfo),i=Modal.confirm(n,o,Severity.warning,[{text:TYPO3.lang["buttons.confirm.delete_record.no"]||"Cancel",active:!0,btnClass:"btn-default",name:"no"},{text:TYPO3.lang["buttons.confirm.delete_record.yes"]||"Yes, delete this record",btnClass:"btn-warning",name:"yes"}]);i.addEventListener("button.clicked",(t=>{if("yes"===t.target.name){const t=this.closest("[data-object-id]").dataset.objectId;e.deleteRecord(t)}i.hideModal()}))})).delegateTo(this.container,Selectors.deleteRecordButtonSelector)}registerSynchronizeLocalize(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation(),e.ajaxDispatcher.send(e.ajaxDispatcher.newRequest(e.ajaxDispatcher.getEndpoint("record_inline_synchronizelocalize")),[e.container.dataset.objectGroup,this.dataset.type]).then((async t=>{document.getElementById(e.container.getAttribute("id")+"_records").insertAdjacentHTML("beforeend",t.data);const n=e.container.dataset.objectGroup+Separators.structureSeparator;for(let o of t.compilerInput.delete)e.deleteRecord(n+o,!0);for(let o of Object.values(t.compilerInput.localize)){if(void 0!==o.remove){const e=InlineControlContainer.getInlineRecordContainer(n+o.remove);e.parentElement.removeChild(e)}e.memorizeAddRecord(o.uid,null,o.selectedValue)}}))})).delegateTo(this.container,Selectors.synchronizeLocalizeRecordButtonSelector)}registerUniqueSelectFieldChanged(){const e=this;new RegularEvent("change",(function(t){t.preventDefault(),t.stopImmediatePropagation();const n=this.closest("[data-object-id]");if(null!==n){const t=n.dataset.objectId,o=n.dataset.objectUid;e.handleChangedField(this,t);const i=e.getFormFieldForElements();if(null===i)return;e.updateUnique(this,i,o)}})).delegateTo(this.container,Selectors.uniqueValueSelectors)}registerRevertUniquenessAction(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation(),e.revertUnique(this.dataset.uid)})).delegateTo(this.container,Selectors.revertUniqueness)}loadRecordDetails(e){const t=document.getElementById(e+"_fields"),n=InlineControlContainer.getInlineRecordContainer(e),o=void 0!==this.requestQueue[e];if(null!==t&&!n.classList.contains(States.notLoaded))this.collapseExpandRecord(e);else{const i=this.getProgress(e,n.dataset.objectIdHash);if(o)this.requestQueue[e].abort(),delete this.requestQueue[e],delete this.progessQueue[e],i.done();else{const o=this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint("record_inline_details"));this.ajaxDispatcher.send(o,[e]).then((async o=>{if(delete this.requestQueue[e],delete this.progessQueue[e],n.classList.remove(States.notLoaded),t.innerHTML=o.data,this.collapseExpandRecord(e),i.done(),FormEngine.reinitialize(),FormEngineValidation.initializeInputFields(),FormEngineValidation.validate(this.container),this.hasObjectGroupDefinedUniqueConstraints()){const t=InlineControlContainer.getInlineRecordContainer(e);this.removeUsed(t)}})),this.requestQueue[e]=o,i.start()}}}collapseExpandRecord(e){const t=InlineControlContainer.getInlineRecordContainer(e),n=!0===this.getAppearance().expandSingle,o=t.classList.contains(States.collapsed);let i=[];const r=[];n&&o&&(i=this.collapseAllRecords(t.dataset.objectUid)),InlineControlContainer.toggleElement(e),InlineControlContainer.isNewRecord(e)?InlineControlContainer.updateExpandedCollapsedStateLocally(e,o):o?r.push(t.dataset.objectUid):o||i.push(t.dataset.objectUid),this.ajaxDispatcher.send(this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint("record_inline_expandcollapse")),[e,r.join(","),i.join(",")])}memorizeAddRecord(e,t=null,n=null){const o=this.getFormFieldForElements();if(null===o)return;let i=Utility.trimExplode(",",o.value);if(t){const n=[];for(let o=0;o<i.length;o++)i[o].length&&n.push(i[o]),t===i[o]&&n.push(e);i=n}else i.push(e);o.value=i.join(","),o.classList.add("has-change"),document.dispatchEvent(new Event("change")),this.redrawSortingButtons(this.container.dataset.objectGroup,i),this.setUnique(e,n),this.isBelowMax()||this.toggleContainerControls(!1),FormEngine.reinitialize(),FormEngineValidation.initializeInputFields(),FormEngineValidation.validate(this.container)}memorizeRemoveRecord(e){const t=this.getFormFieldForElements();if(null===t)return[];let n=Utility.trimExplode(",",t.value);const o=n.indexOf(e);return o>-1&&(delete n[o],t.value=n.join(","),t.classList.add("has-change"),document.dispatchEvent(new Event("change")),this.redrawSortingButtons(this.container.dataset.objectGroup,n)),n}changeSortingByButton(e,t){const n=InlineControlContainer.getInlineRecordContainer(e),o=n.dataset.objectUid,i=document.getElementById(this.container.getAttribute("id")+"_records"),r=Array.from(i.children).map((e=>e.dataset.objectUid));let a=r.indexOf(o),s=!1;if(t===SortDirections.UP&&a>0?(r[a]=r[a-1],r[a-1]=o,s=!0):t===SortDirections.DOWN&&a<r.length-1&&(r[a]=r[a+1],r[a+1]=o,s=!0),s){const e=this.container.dataset.objectGroup+Separators.structureSeparator,o=t===SortDirections.UP?1:0;n.parentElement.insertBefore(InlineControlContainer.getInlineRecordContainer(e+r[a-o]),InlineControlContainer.getInlineRecordContainer(e+r[a+1-o])),this.updateSorting()}}updateSorting(){const e=this.getFormFieldForElements();if(null===e)return;const t=document.getElementById(this.container.getAttribute("id")+"_records"),n=Array.from(t.querySelectorAll('[data-object-parent-group="'+this.container.dataset.objectGroup+'"][data-placeholder-record="0"]')).map((e=>e.dataset.objectUid));e.value=n.join(","),e.classList.add("has-change"),document.dispatchEvent(new Event("inline:sorting-changed")),document.dispatchEvent(new Event("change")),this.redrawSortingButtons(this.container.dataset.objectGroup,n)}deleteRecord(e,t=!1){const n=InlineControlContainer.getInlineRecordContainer(e),o=n.dataset.objectUid;if(n.classList.add("t3js-inline-record-deleted"),!InlineControlContainer.isNewRecord(e)&&!t){const e=this.container.querySelector('[name="cmd'+n.dataset.fieldName+'[delete]"]');e.removeAttribute("disabled"),n.parentElement.insertAdjacentElement("afterbegin",e)}new RegularEvent("transitionend",(()=>{n.parentElement.removeChild(n),FormEngineValidation.validate(this.container)})).bindTo(n),this.revertUnique(o),this.memorizeRemoveRecord(o),n.classList.add("form-irre-object--deleted"),this.isBelowMax()&&this.toggleContainerControls(!0)}toggleContainerControls(e){const t=this.container.querySelectorAll(":scope > "+Selectors.controlContainer+", :scope > "+Selectors.controlTopOuterContainer+" "+Selectors.controlContainer);null!==t&&t.forEach((t=>{t.querySelectorAll("button, a").forEach((t=>{t.style.display=e?null:"none"}))}))}getProgress(e,t){const n="#"+t+"_header";let o;return void 0!==this.progessQueue[e]?o=this.progessQueue[e]:(o=NProgress,o.configure({parent:n,showSpinner:!1}),this.progessQueue[e]=o),o}collapseAllRecords(e){const t=this.getFormFieldForElements(),n=[];if(null!==t){const o=Utility.trimExplode(",",t.value);for(let t of o){if(t===e)continue;const o=this.container.dataset.objectGroup+Separators.structureSeparator+t,i=InlineControlContainer.getInlineRecordContainer(o);i.classList.contains(States.visible)&&(InlineControlContainer.collapseElement(i,o),InlineControlContainer.isNewRecord(o)?InlineControlContainer.updateExpandedCollapsedStateLocally(o,!1):n.push(t))}}return n}getFormFieldForElements(){const e=document.getElementsByName(this.container.dataset.formField);return e.length>0?e[0]:null}redrawSortingButtons(e,t=[]){if(0===t.length){const e=this.getFormFieldForElements();null!==e&&(t=Utility.trimExplode(",",e.value))}0!==t.length&&t.forEach(((n,o)=>{const i=InlineControlContainer.getInlineRecordContainer(e+Separators.structureSeparator+n).dataset.objectIdHash+"_header",r=document.getElementById(i),a=r.querySelector('[data-action="sort"][data-direction="'+SortDirections.UP+'"]');if(null!==a){let e="actions-move-up";0===o?(a.classList.add("disabled"),e="empty-empty"):a.classList.remove("disabled"),Icons.getIcon(e,Icons.sizes.small).then((e=>{a.replaceChild(document.createRange().createContextualFragment(e),a.querySelector(".t3js-icon"))}))}const s=r.querySelector('[data-action="sort"][data-direction="'+SortDirections.DOWN+'"]');if(null!==s){let e="actions-move-down";o===t.length-1?(s.classList.add("disabled"),e="empty-empty"):s.classList.remove("disabled"),Icons.getIcon(e,Icons.sizes.small).then((e=>{s.replaceChild(document.createRange().createContextualFragment(e),s.querySelector(".t3js-icon"))}))}}))}isBelowMax(){const e=this.getFormFieldForElements();if(null===e)return!0;if(void 0!==TYPO3.settings.FormEngineInline.config[this.container.dataset.objectGroup]){if(Utility.trimExplode(",",e.value).length>=TYPO3.settings.FormEngineInline.config[this.container.dataset.objectGroup].max)return!1;if(this.hasObjectGroupDefinedUniqueConstraints()){const e=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup];if(e.used.length>=e.max&&e.max>=0)return!1}}return!0}isUniqueElementUsed(e,t){if(!this.hasObjectGroupDefinedUniqueConstraints())return!1;const n=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup],o=InlineControlContainer.getValuesFromHashMap(n.used);if("select"===n.type&&-1!==o.indexOf(e))return!0;if("groupdb"===n.type)for(let n=o.length-1;n>=0;n--)if(o[n].table===t&&o[n].uid===e)return!0;return!1}removeUsed(e){if(!this.hasObjectGroupDefinedUniqueConstraints())return;const t=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup];if("select"!==t.type)return;let n=e.querySelector('[name="data['+t.table+"]["+e.dataset.objectUid+"]["+t.field+']"]');const o=InlineControlContainer.getValuesFromHashMap(t.used);if(null!==n){const e=n.options[n.selectedIndex].value;for(let t of o)t!==e&&InlineControlContainer.removeSelectOptionByValue(n,t)}}setUnique(e,t){if(!this.hasObjectGroupDefinedUniqueConstraints())return;const n=document.getElementById(this.container.dataset.objectGroup+"_selector"),o=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup];if("select"===o.type){if(!o.selector||-1!==o.max){const i=this.getFormFieldForElements(),r=this.container.dataset.objectGroup+Separators.structureSeparator+e;let a=InlineControlContainer.getInlineRecordContainer(r).querySelector('[name="data['+o.table+"]["+e+"]["+o.field+']"]');const s=InlineControlContainer.getValuesFromHashMap(o.used);if(null!==n){if(null!==a){for(let e of s)InlineControlContainer.removeSelectOptionByValue(a,e);o.selector||(t=a.options[0].value,a.options[0].selected=!0,this.updateUnique(a,i,e),this.handleChangedField(a,this.container.dataset.objectGroup+"["+e+"]"))}for(let e of s)InlineControlContainer.removeSelectOptionByValue(a,e);void 0!==o.used.length&&(o.used={}),o.used[e]={table:o.elTable,uid:t}}if(null!==i&&InlineControlContainer.selectOptionValueExists(n,t)){const n=Utility.trimExplode(",",i.value);for(let i of n)a=document.querySelector('[name="data['+o.table+"]["+i+"]["+o.field+']"]'),null!==a&&i!==e&&InlineControlContainer.removeSelectOptionByValue(a,t)}}}else"groupdb"===o.type&&(o.used[e]={table:o.elTable,uid:t});"select"===o.selector&&InlineControlContainer.selectOptionValueExists(n,t)&&(InlineControlContainer.removeSelectOptionByValue(n,t),o.used[e]={table:o.elTable,uid:t})}updateUnique(e,t,n){if(!this.hasObjectGroupDefinedUniqueConstraints())return;const o=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup],i=o.used[n];if("select"===o.selector){const t=document.getElementById(this.container.dataset.objectGroup+"_selector");InlineControlContainer.removeSelectOptionByValue(t,e.value),void 0!==i&&InlineControlContainer.reAddSelectOption(t,i,o)}if(o.selector&&-1===o.max)return;if(!o||null===t)return;const r=Utility.trimExplode(",",t.value);let a;for(let t of r)a=document.querySelector('[name="data['+o.table+"]["+t+"]["+o.field+']"]'),null!==a&&a!==e&&(InlineControlContainer.removeSelectOptionByValue(a,e.value),void 0!==i&&InlineControlContainer.reAddSelectOption(a,i,o));o.used[n]=e.value}revertUnique(e){if(!this.hasObjectGroupDefinedUniqueConstraints())return;const t=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup],n=this.container.dataset.objectGroup+Separators.structureSeparator+e,o=InlineControlContainer.getInlineRecordContainer(n);let i=o.querySelector('[name="data['+t.table+"]["+o.dataset.objectUid+"]["+t.field+']"]');if("select"===t.type){let n;if(null!==i)n=i.value;else{if(""===o.dataset.tableUniqueOriginalValue)return;n=o.dataset.tableUniqueOriginalValue}if("select"===t.selector&&!isNaN(parseInt(n,10))){const e=document.getElementById(this.container.dataset.objectGroup+"_selector");InlineControlContainer.reAddSelectOption(e,n,t)}if(t.selector&&-1===t.max)return;const r=this.getFormFieldForElements();if(null===r)return;const a=Utility.trimExplode(",",r.value);let s;for(let e=0;e<a.length;e++)s=document.querySelector('[name="data['+t.table+"]["+a[e]+"]["+t.field+']"]'),null!==s&&InlineControlContainer.reAddSelectOption(s,n,t);delete t.used[e]}else"groupdb"===t.type&&delete t.used[e]}hasObjectGroupDefinedUniqueConstraints(){return void 0!==TYPO3.settings.FormEngineInline.unique&&void 0!==TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup]}handleChangedField(e,t){let n;n=e instanceof HTMLSelectElement?e.options[e.selectedIndex].text:e.value,document.getElementById(t+"_label").textContent=n.length?n:this.noTitleString}getAppearance(){if(null===this.appearance&&(this.appearance={},"string"==typeof this.container.dataset.appearance))try{this.appearance=JSON.parse(this.container.dataset.appearance)}catch(e){console.error(e)}return this.appearance}}export default InlineControlContainer; \ No newline at end of file +import{MessageUtility}from"@typo3/backend/utility/message-utility.js";import{AjaxDispatcher}from"@typo3/backend/form-engine/inline-relation/ajax-dispatcher.js";import DocumentService from"@typo3/core/document-service.js";import NProgress from"nprogress";import Sortable from"sortablejs";import FormEngine from"@typo3/backend/form-engine.js";import FormEngineValidation from"@typo3/backend/form-engine-validation.js";import Icons from"@typo3/backend/icons.js";import InfoWindow from"@typo3/backend/info-window.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import RegularEvent from"@typo3/core/event/regular-event.js";import Severity from"@typo3/backend/severity.js";import Utility from"@typo3/backend/utility.js";var Selectors,States,Separators,SortDirections;!function(e){e.toggleSelector='[data-bs-toggle="formengine-inline"]',e.controlSectionSelector=".t3js-formengine-irre-control",e.createNewRecordButtonSelector=".t3js-create-new-button",e.createNewRecordBySelectorSelector=".t3js-create-new-selector",e.deleteRecordButtonSelector=".t3js-editform-delete-inline-record",e.enableDisableRecordButtonSelector=".t3js-toggle-visibility-button",e.infoWindowButton='[data-action="infowindow"]',e.synchronizeLocalizeRecordButtonSelector=".t3js-synchronizelocalize-button",e.uniqueValueSelectors="select.t3js-inline-unique",e.revertUniqueness=".t3js-revert-unique",e.controlContainer=".t3js-inline-controls",e.controlTopOuterContainer=".t3js-inline-controls-top-outer-container"}(Selectors||(Selectors={})),function(e){e.new="inlineIsNewRecord",e.visible="panel-visible",e.collapsed="panel-collapsed",e.notLoaded="t3js-not-loaded"}(States||(States={})),function(e){e.structureSeparator="-"}(Separators||(Separators={})),function(e){e.DOWN="down",e.UP="up"}(SortDirections||(SortDirections={}));class InlineControlContainer{constructor(e){this.container=null,this.ajaxDispatcher=null,this.appearance=null,this.requestQueue={},this.progessQueue={},this.noTitleString=TYPO3.lang?TYPO3.lang["FormEngine.noRecordTitle"]:"[No title]",this.handlePostMessage=e=>{if(!MessageUtility.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;if("typo3:foreignRelation:insert"===e.data.actionName){if(void 0===e.data.objectGroup)throw"No object group defined for message";if(e.data.objectGroup!==this.container.dataset.objectGroup)return;if(this.isUniqueElementUsed(parseInt(e.data.uid,10),e.data.table))return void Notification.error("There is already a relation to the selected element");this.importRecord([e.data.objectGroup,e.data.uid]).then((()=>{if(e.source){const t={actionName:"typo3:foreignRelation:inserted",objectGroup:e.data.objectId,table:e.data.table,uid:e.data.uid};MessageUtility.send(t,e.source)}}))}},DocumentService.ready().then((t=>{this.container=t.getElementById(e),this.ajaxDispatcher=new AjaxDispatcher(this.container.dataset.objectGroup),this.registerEvents()}))}static getInlineRecordContainer(e){return document.querySelector('[data-object-id="'+e+'"]')}static getCollapseButton(e){return document.querySelector('[aria-controls="'+e+'_fields"]')}static toggleElement(e){const t=InlineControlContainer.getInlineRecordContainer(e);t.classList.contains(States.collapsed)?InlineControlContainer.expandElement(t,e):InlineControlContainer.collapseElement(t,e)}static collapseElement(e,t){const n=InlineControlContainer.getCollapseButton(t);e.classList.remove(States.visible),e.classList.add(States.collapsed),n.setAttribute("aria-expanded","false")}static expandElement(e,t){const n=InlineControlContainer.getCollapseButton(t);e.classList.remove(States.collapsed),e.classList.add(States.visible),n.setAttribute("aria-expanded","true")}static isNewRecord(e){return InlineControlContainer.getInlineRecordContainer(e).classList.contains(States.new)}static updateExpandedCollapsedStateLocally(e,t){const n=InlineControlContainer.getInlineRecordContainer(e),o="uc[inlineView]["+n.dataset.topmostParentTable+"]["+n.dataset.topmostParentUid+"]"+n.dataset.fieldName,i=document.getElementsByName(o);i.length&&(i[0].value=t?"1":"0")}static getValuesFromHashMap(e){return Object.keys(e).map((t=>e[t]))}static selectOptionValueExists(e,t){return null!==e.querySelector('option[value="'+t+'"]')}static removeSelectOptionByValue(e,t){const n=e.querySelector('option[value="'+t+'"]');null!==n&&n.remove()}static reAddSelectOption(e,t,n){if(InlineControlContainer.selectOptionValueExists(e,t))return;const o=e.querySelectorAll("option");let i=-1;for(const e of Object.keys(n.possible)){if(e===t)break;for(let t=0;t<o.length;++t){if(o[t].value===e){i=t;break}}}-1===i?i=0:i<o.length&&i++;const r=document.createElement("option");r.text=n.possible[t],r.value=t,e.insertBefore(r,e.options[i])}registerEvents(){if(this.registerInfoButton(),this.registerSort(),this.registerCreateRecordButton(),this.registerEnableDisableButton(),this.registerDeleteButton(),this.registerSynchronizeLocalize(),this.registerRevertUniquenessAction(),this.registerToggle(),this.registerCreateRecordBySelector(),this.registerUniqueSelectFieldChanged(),new RegularEvent("message",this.handlePostMessage).bindTo(window),this.getAppearance().useSortable){const e=document.getElementById(this.container.getAttribute("id")+"_records");new Sortable(e,{group:e.getAttribute("id"),handle:".sortableHandle",onSort:()=>{this.updateSorting()}})}}registerToggle(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation(),e.loadRecordDetails(this.closest(Selectors.toggleSelector).parentElement.dataset.objectId)})).delegateTo(this.container,`${Selectors.toggleSelector} .form-irre-header-cell:not(${Selectors.controlSectionSelector}`)}registerSort(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation(),e.changeSortingByButton(this.closest("[data-object-id]").dataset.objectId,this.dataset.direction)})).delegateTo(this.container,Selectors.controlSectionSelector+' [data-action="sort"]')}registerCreateRecordButton(){const e=this;new RegularEvent("click",(function(t){if(t.preventDefault(),t.stopImmediatePropagation(),e.isBelowMax()){let t=e.container.dataset.objectGroup;void 0!==this.dataset.recordUid&&(t+=Separators.structureSeparator+this.dataset.recordUid),e.importRecord([t,e.container.querySelector(Selectors.createNewRecordBySelectorSelector)?.value],this.dataset.recordUid??null)}})).delegateTo(this.container,Selectors.createNewRecordButtonSelector)}registerCreateRecordBySelector(){const e=this;new RegularEvent("change",(function(t){t.preventDefault(),t.stopImmediatePropagation();const n=this.options[this.selectedIndex].getAttribute("value");e.importRecord([e.container.dataset.objectGroup,n])})).delegateTo(this.container,Selectors.createNewRecordBySelectorSelector)}createRecord(e,t,n=null,o=null){let i=this.container.dataset.objectGroup;null!==n&&(i+=Separators.structureSeparator+n),null!==n?(InlineControlContainer.getInlineRecordContainer(i).insertAdjacentHTML("afterend",t),this.memorizeAddRecord(e,n,o)):(document.getElementById(this.container.getAttribute("id")+"_records").insertAdjacentHTML("beforeend",t),this.memorizeAddRecord(e,null,o))}async importRecord(e,t){return this.ajaxDispatcher.send(this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint("record_inline_create")),e).then((async e=>{this.isBelowMax()&&this.createRecord(e.compilerInput.uid,e.data,void 0!==t?t:null,void 0!==e.compilerInput.childChildUid?e.compilerInput.childChildUid:null)}))}registerEnableDisableButton(){new RegularEvent("click",((e,t)=>{e.preventDefault(),e.stopImmediatePropagation();const n=t.closest("[data-object-id]").dataset.objectId,o=InlineControlContainer.getInlineRecordContainer(n),i="data"+o.dataset.fieldName+"["+t.dataset.hiddenField+"]",r=document.querySelector('[data-formengine-input-name="'+i+'"'),a=document.querySelector('[name="'+i+'"');null!==r&&null!==a&&(r.checked=!r.checked,a.value=r.checked?"1":"0",FormEngineValidation.markFieldAsChanged(r));const s="t3-form-field-container-inline-hidden";let l;o.classList.contains(s)?(l="actions-edit-hide",o.classList.remove(s)):(l="actions-edit-unhide",o.classList.add(s)),Icons.getIcon(l,Icons.sizes.small).then((e=>{t.replaceChild(document.createRange().createContextualFragment(e),t.querySelector(".t3js-icon"))}))})).delegateTo(this.container,Selectors.enableDisableRecordButtonSelector)}registerInfoButton(){new RegularEvent("click",(function(e){e.preventDefault(),e.stopImmediatePropagation(),InfoWindow.showItem(this.dataset.infoTable,this.dataset.infoUid)})).delegateTo(this.container,Selectors.infoWindowButton)}registerDeleteButton(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const n=TYPO3.lang["label.confirm.delete_record.title"]||"Delete this record?",o=(TYPO3.lang["label.confirm.delete_record.content"]||"Are you sure you want to delete the record '%s'?").replace("%s",this.dataset.recordInfo),i=Modal.confirm(n,o,Severity.warning,[{text:TYPO3.lang["buttons.confirm.delete_record.no"]||"Cancel",active:!0,btnClass:"btn-default",name:"no"},{text:TYPO3.lang["buttons.confirm.delete_record.yes"]||"Yes, delete this record",btnClass:"btn-warning",name:"yes"}]);i.addEventListener("button.clicked",(t=>{if("yes"===t.target.name){const t=this.closest("[data-object-id]").dataset.objectId;e.deleteRecord(t)}i.hideModal()}))})).delegateTo(this.container,Selectors.deleteRecordButtonSelector)}registerSynchronizeLocalize(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation(),e.ajaxDispatcher.send(e.ajaxDispatcher.newRequest(e.ajaxDispatcher.getEndpoint("record_inline_synchronizelocalize")),[e.container.dataset.objectGroup,this.dataset.type]).then((async t=>{document.getElementById(e.container.getAttribute("id")+"_records").insertAdjacentHTML("beforeend",t.data);const n=e.container.dataset.objectGroup+Separators.structureSeparator;for(const o of t.compilerInput.delete)e.deleteRecord(n+o,!0);for(const o of Object.values(t.compilerInput.localize)){if(void 0!==o.remove){const e=InlineControlContainer.getInlineRecordContainer(n+o.remove);e.parentElement.removeChild(e)}e.memorizeAddRecord(o.uid,null,o.selectedValue)}}))})).delegateTo(this.container,Selectors.synchronizeLocalizeRecordButtonSelector)}registerUniqueSelectFieldChanged(){const e=this;new RegularEvent("change",(function(t){t.preventDefault(),t.stopImmediatePropagation();const n=this.closest("[data-object-id]");if(null!==n){const t=n.dataset.objectId,o=n.dataset.objectUid;e.handleChangedField(this,t);const i=e.getFormFieldForElements();if(null===i)return;e.updateUnique(this,i,o)}})).delegateTo(this.container,Selectors.uniqueValueSelectors)}registerRevertUniquenessAction(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation(),e.revertUnique(this.dataset.uid)})).delegateTo(this.container,Selectors.revertUniqueness)}loadRecordDetails(e){const t=document.getElementById(e+"_fields"),n=InlineControlContainer.getInlineRecordContainer(e),o=void 0!==this.requestQueue[e];if(null!==t&&!n.classList.contains(States.notLoaded))this.collapseExpandRecord(e);else{const i=this.getProgress(e,n.dataset.objectIdHash);if(o)this.requestQueue[e].abort(),delete this.requestQueue[e],delete this.progessQueue[e],i.done();else{const o=this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint("record_inline_details"));this.ajaxDispatcher.send(o,[e]).then((async o=>{if(delete this.requestQueue[e],delete this.progessQueue[e],n.classList.remove(States.notLoaded),t.innerHTML=o.data,this.collapseExpandRecord(e),i.done(),FormEngine.reinitialize(),FormEngineValidation.initializeInputFields(),FormEngineValidation.validate(this.container),this.hasObjectGroupDefinedUniqueConstraints()){const t=InlineControlContainer.getInlineRecordContainer(e);this.removeUsed(t)}})),this.requestQueue[e]=o,i.start()}}}collapseExpandRecord(e){const t=InlineControlContainer.getInlineRecordContainer(e),n=!0===this.getAppearance().expandSingle,o=t.classList.contains(States.collapsed);let i=[];const r=[];n&&o&&(i=this.collapseAllRecords(t.dataset.objectUid)),InlineControlContainer.toggleElement(e),InlineControlContainer.isNewRecord(e)?InlineControlContainer.updateExpandedCollapsedStateLocally(e,o):o?r.push(t.dataset.objectUid):o||i.push(t.dataset.objectUid),this.ajaxDispatcher.send(this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint("record_inline_expandcollapse")),[e,r.join(","),i.join(",")])}memorizeAddRecord(e,t=null,n=null){const o=this.getFormFieldForElements();if(null===o)return;let i=Utility.trimExplode(",",o.value);if(t){const n=[];for(let o=0;o<i.length;o++)i[o].length&&n.push(i[o]),t===i[o]&&n.push(e);i=n}else i.push(e);o.value=i.join(","),o.classList.add("has-change"),document.dispatchEvent(new Event("change")),this.redrawSortingButtons(this.container.dataset.objectGroup,i),this.setUnique(e,n),this.isBelowMax()||this.toggleContainerControls(!1),FormEngine.reinitialize(),FormEngineValidation.initializeInputFields(),FormEngineValidation.validate(this.container)}memorizeRemoveRecord(e){const t=this.getFormFieldForElements();if(null===t)return[];const n=Utility.trimExplode(",",t.value),o=n.indexOf(e);return o>-1&&(delete n[o],t.value=n.join(","),t.classList.add("has-change"),document.dispatchEvent(new Event("change")),this.redrawSortingButtons(this.container.dataset.objectGroup,n)),n}changeSortingByButton(e,t){const n=InlineControlContainer.getInlineRecordContainer(e),o=n.dataset.objectUid,i=document.getElementById(this.container.getAttribute("id")+"_records"),r=Array.from(i.children).map((e=>e.dataset.objectUid)),a=r.indexOf(o);let s=!1;if(t===SortDirections.UP&&a>0?(r[a]=r[a-1],r[a-1]=o,s=!0):t===SortDirections.DOWN&&a<r.length-1&&(r[a]=r[a+1],r[a+1]=o,s=!0),s){const e=this.container.dataset.objectGroup+Separators.structureSeparator,o=t===SortDirections.UP?1:0;n.parentElement.insertBefore(InlineControlContainer.getInlineRecordContainer(e+r[a-o]),InlineControlContainer.getInlineRecordContainer(e+r[a+1-o])),this.updateSorting()}}updateSorting(){const e=this.getFormFieldForElements();if(null===e)return;const t=document.getElementById(this.container.getAttribute("id")+"_records"),n=Array.from(t.querySelectorAll('[data-object-parent-group="'+this.container.dataset.objectGroup+'"][data-placeholder-record="0"]')).map((e=>e.dataset.objectUid));e.value=n.join(","),e.classList.add("has-change"),document.dispatchEvent(new Event("inline:sorting-changed")),document.dispatchEvent(new Event("change")),this.redrawSortingButtons(this.container.dataset.objectGroup,n)}deleteRecord(e,t=!1){const n=InlineControlContainer.getInlineRecordContainer(e),o=n.dataset.objectUid;if(n.classList.add("t3js-inline-record-deleted"),!InlineControlContainer.isNewRecord(e)&&!t){const e=this.container.querySelector('[name="cmd'+n.dataset.fieldName+'[delete]"]');e.removeAttribute("disabled"),n.parentElement.insertAdjacentElement("afterbegin",e)}new RegularEvent("transitionend",(()=>{n.parentElement.removeChild(n),FormEngineValidation.validate(this.container)})).bindTo(n),this.revertUnique(o),this.memorizeRemoveRecord(o),n.classList.add("form-irre-object--deleted"),this.isBelowMax()&&this.toggleContainerControls(!0)}toggleContainerControls(e){const t=this.container.querySelectorAll(":scope > "+Selectors.controlContainer+", :scope > "+Selectors.controlTopOuterContainer+" "+Selectors.controlContainer);null!==t&&t.forEach((t=>{t.querySelectorAll("button, a").forEach((t=>{t.style.display=e?null:"none"}))}))}getProgress(e,t){const n="#"+t+"_header";let o;return void 0!==this.progessQueue[e]?o=this.progessQueue[e]:(o=NProgress,o.configure({parent:n,showSpinner:!1}),this.progessQueue[e]=o),o}collapseAllRecords(e){const t=this.getFormFieldForElements(),n=[];if(null!==t){const o=Utility.trimExplode(",",t.value);for(const t of o){if(t===e)continue;const o=this.container.dataset.objectGroup+Separators.structureSeparator+t,i=InlineControlContainer.getInlineRecordContainer(o);i.classList.contains(States.visible)&&(InlineControlContainer.collapseElement(i,o),InlineControlContainer.isNewRecord(o)?InlineControlContainer.updateExpandedCollapsedStateLocally(o,!1):n.push(t))}}return n}getFormFieldForElements(){const e=document.getElementsByName(this.container.dataset.formField);return e.length>0?e[0]:null}redrawSortingButtons(e,t=[]){if(0===t.length){const e=this.getFormFieldForElements();null!==e&&(t=Utility.trimExplode(",",e.value))}0!==t.length&&t.forEach(((n,o)=>{const i=InlineControlContainer.getInlineRecordContainer(e+Separators.structureSeparator+n).dataset.objectIdHash+"_header",r=document.getElementById(i),a=r.querySelector('[data-action="sort"][data-direction="'+SortDirections.UP+'"]');if(null!==a){let e="actions-move-up";0===o?(a.classList.add("disabled"),e="empty-empty"):a.classList.remove("disabled"),Icons.getIcon(e,Icons.sizes.small).then((e=>{a.replaceChild(document.createRange().createContextualFragment(e),a.querySelector(".t3js-icon"))}))}const s=r.querySelector('[data-action="sort"][data-direction="'+SortDirections.DOWN+'"]');if(null!==s){let e="actions-move-down";o===t.length-1?(s.classList.add("disabled"),e="empty-empty"):s.classList.remove("disabled"),Icons.getIcon(e,Icons.sizes.small).then((e=>{s.replaceChild(document.createRange().createContextualFragment(e),s.querySelector(".t3js-icon"))}))}}))}isBelowMax(){const e=this.getFormFieldForElements();if(null===e)return!0;if(void 0!==TYPO3.settings.FormEngineInline.config[this.container.dataset.objectGroup]){if(Utility.trimExplode(",",e.value).length>=TYPO3.settings.FormEngineInline.config[this.container.dataset.objectGroup].max)return!1;if(this.hasObjectGroupDefinedUniqueConstraints()){const e=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup];if(e.used.length>=e.max&&e.max>=0)return!1}}return!0}isUniqueElementUsed(e,t){if(!this.hasObjectGroupDefinedUniqueConstraints())return!1;const n=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup],o=InlineControlContainer.getValuesFromHashMap(n.used);if("select"===n.type&&-1!==o.indexOf(e))return!0;if("groupdb"===n.type)for(let n=o.length-1;n>=0;n--)if(o[n].table===t&&o[n].uid===e)return!0;return!1}removeUsed(e){if(!this.hasObjectGroupDefinedUniqueConstraints())return;const t=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup];if("select"!==t.type)return;const n=e.querySelector('[name="data['+t.table+"]["+e.dataset.objectUid+"]["+t.field+']"]'),o=InlineControlContainer.getValuesFromHashMap(t.used);if(null!==n){const e=n.options[n.selectedIndex].value;for(const t of o)t!==e&&InlineControlContainer.removeSelectOptionByValue(n,t)}}setUnique(e,t){if(!this.hasObjectGroupDefinedUniqueConstraints())return;const n=document.getElementById(this.container.dataset.objectGroup+"_selector"),o=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup];if("select"===o.type){if(!o.selector||-1!==o.max){const i=this.getFormFieldForElements(),r=this.container.dataset.objectGroup+Separators.structureSeparator+e;let a=InlineControlContainer.getInlineRecordContainer(r).querySelector('[name="data['+o.table+"]["+e+"]["+o.field+']"]');const s=InlineControlContainer.getValuesFromHashMap(o.used);if(null!==n){if(null!==a){for(const e of s)InlineControlContainer.removeSelectOptionByValue(a,e);o.selector||(t=a.options[0].value,a.options[0].selected=!0,this.updateUnique(a,i,e),this.handleChangedField(a,this.container.dataset.objectGroup+"["+e+"]"))}for(const e of s)InlineControlContainer.removeSelectOptionByValue(a,e);void 0!==o.used.length&&(o.used={}),o.used[e]={table:o.elTable,uid:t}}if(null!==i&&InlineControlContainer.selectOptionValueExists(n,t)){const n=Utility.trimExplode(",",i.value);for(const i of n)a=document.querySelector('[name="data['+o.table+"]["+i+"]["+o.field+']"]'),null!==a&&i!==e&&InlineControlContainer.removeSelectOptionByValue(a,t)}}}else"groupdb"===o.type&&(o.used[e]={table:o.elTable,uid:t});"select"===o.selector&&InlineControlContainer.selectOptionValueExists(n,t)&&(InlineControlContainer.removeSelectOptionByValue(n,t),o.used[e]={table:o.elTable,uid:t})}updateUnique(e,t,n){if(!this.hasObjectGroupDefinedUniqueConstraints())return;const o=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup],i=o.used[n];if("select"===o.selector){const t=document.getElementById(this.container.dataset.objectGroup+"_selector");InlineControlContainer.removeSelectOptionByValue(t,e.value),void 0!==i&&InlineControlContainer.reAddSelectOption(t,i,o)}if(o.selector&&-1===o.max)return;if(!o||null===t)return;const r=Utility.trimExplode(",",t.value);let a;for(const t of r)a=document.querySelector('[name="data['+o.table+"]["+t+"]["+o.field+']"]'),null!==a&&a!==e&&(InlineControlContainer.removeSelectOptionByValue(a,e.value),void 0!==i&&InlineControlContainer.reAddSelectOption(a,i,o));o.used[n]=e.value}revertUnique(e){if(!this.hasObjectGroupDefinedUniqueConstraints())return;const t=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup],n=this.container.dataset.objectGroup+Separators.structureSeparator+e,o=InlineControlContainer.getInlineRecordContainer(n),i=o.querySelector('[name="data['+t.table+"]["+o.dataset.objectUid+"]["+t.field+']"]');if("select"===t.type){let n;if(null!==i)n=i.value;else{if(""===o.dataset.tableUniqueOriginalValue)return;n=o.dataset.tableUniqueOriginalValue}if("select"===t.selector&&!isNaN(parseInt(n,10))){const e=document.getElementById(this.container.dataset.objectGroup+"_selector");InlineControlContainer.reAddSelectOption(e,n,t)}if(t.selector&&-1===t.max)return;const r=this.getFormFieldForElements();if(null===r)return;const a=Utility.trimExplode(",",r.value);let s;for(let e=0;e<a.length;e++)s=document.querySelector('[name="data['+t.table+"]["+a[e]+"]["+t.field+']"]'),null!==s&&InlineControlContainer.reAddSelectOption(s,n,t);delete t.used[e]}else"groupdb"===t.type&&delete t.used[e]}hasObjectGroupDefinedUniqueConstraints(){return void 0!==TYPO3.settings.FormEngineInline.unique&&void 0!==TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup]}handleChangedField(e,t){let n;n=e instanceof HTMLSelectElement?e.options[e.selectedIndex].text:e.value,document.getElementById(t+"_label").textContent=n.length?n:this.noTitleString}getAppearance(){if(null===this.appearance&&(this.appearance={},"string"==typeof this.container.dataset.appearance))try{this.appearance=JSON.parse(this.container.dataset.appearance)}catch(e){console.error(e)}return this.appearance}}export default InlineControlContainer; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/site-language-container.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/site-language-container.js index d3bc343a30b4..0089821a7815 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/site-language-container.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/container/site-language-container.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{MessageUtility}from"@typo3/backend/utility/message-utility.js";import{AjaxDispatcher}from"@typo3/backend/form-engine/inline-relation/ajax-dispatcher.js";import NProgress from"nprogress";import FormEngine from"@typo3/backend/form-engine.js";import FormEngineValidation from"@typo3/backend/form-engine-validation.js";import{default as Modal}from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import RegularEvent from"@typo3/core/event/regular-event.js";import Severity from"@typo3/backend/severity.js";import Utility from"@typo3/backend/utility.js";var Selectors,States,Separators;!function(e){e.toggleSelector='[data-bs-toggle="formengine-inline"]',e.controlSectionSelector=".t3js-formengine-irre-control",e.createNewRecordButtonSelector=".t3js-create-new-button",e.createNewRecordBySelectorSelector=".t3js-create-new-selector",e.deleteRecordButtonSelector=".t3js-editform-delete-inline-record"}(Selectors||(Selectors={})),function(e){e.new="inlineIsNewRecord",e.visible="panel-visible",e.collapsed="panel-collapsed",e.notLoaded="t3js-not-loaded"}(States||(States={})),function(e){e.structureSeparator="-"}(Separators||(Separators={}));class SiteLanguageContainer extends HTMLElement{constructor(){super(...arguments),this.container=null,this.ajaxDispatcher=null,this.requestQueue={},this.progessQueue={},this.handlePostMessage=e=>{if(!MessageUtility.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;if("typo3:foreignRelation:insert"===e.data.actionName){if(void 0===e.data.objectGroup)throw"No object group defined for message";if(e.data.objectGroup!==this.container.dataset.objectGroup)return;if(this.isUniqueElementUsed(parseInt(e.data.uid,10)))return void Notification.error("There is already a relation to the selected element");this.importRecord([e.data.objectGroup,e.data.uid]).then((()=>{if(e.source){const t={actionName:"typo3:foreignRelation:inserted",objectGroup:e.data.objectId,table:e.data.table,uid:e.data.uid};MessageUtility.send(t,e.source)}}))}}}static getInlineRecordContainer(e){return document.querySelector('[data-object-id="'+e+'"]')}static getValuesFromHashMap(e){return Object.keys(e).map((t=>e[t]))}static selectOptionValueExists(e,t){return null!==e.querySelector('option[value="'+t+'"]')}static removeSelectOptionByValue(e,t){const n=e.querySelector('option[value="'+t+'"]');null!==n&&n.remove()}static reAddSelectOption(e,t,n){if(SiteLanguageContainer.selectOptionValueExists(e,t))return;const i=e.querySelectorAll("option");let o=-1;for(let e of Object.keys(n.possible)){if(e===t)break;for(let t=0;t<i.length;++t){if(i[t].value===e){o=t;break}}}-1===o?o=0:o<i.length&&o++;const a=document.createElement("option");a.text=n.possible[t],a.value=t,e.insertBefore(a,e.options[o])}static collapseExpandRecord(e){const t=SiteLanguageContainer.getInlineRecordContainer(e),n=document.querySelector('[aria-controls="'+e+'_fields"]');t.classList.contains(States.collapsed)?(t.classList.remove(States.collapsed),t.classList.add(States.visible),n.setAttribute("aria-expanded","true")):(t.classList.remove(States.visible),t.classList.add(States.collapsed),n.setAttribute("aria-expanded","false"))}connectedCallback(){const e=this.getAttribute("identifier")||"";this.container=this.querySelector("#"+e),null!==this.container&&(this.ajaxDispatcher=new AjaxDispatcher(this.container.dataset.objectGroup),this.registerEvents())}registerEvents(){this.registerCreateRecordButton(),this.registerCreateRecordBySelector(),this.registerRecordToggle(),this.registerDeleteButton(),new RegularEvent("message",this.handlePostMessage).bindTo(window)}registerCreateRecordButton(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();let n=e.container.dataset.objectGroup;void 0!==this.dataset.recordUid&&(n+=Separators.structureSeparator+this.dataset.recordUid),e.importRecord([n,e.container.querySelector(Selectors.createNewRecordBySelectorSelector)?.value],this.dataset.recordUid??null)})).delegateTo(this.container,Selectors.createNewRecordButtonSelector)}registerCreateRecordBySelector(){const e=this;new RegularEvent("change",(function(t){t.preventDefault(),t.stopImmediatePropagation();const n=this.options[this.selectedIndex].getAttribute("value");e.importRecord([e.container.dataset.objectGroup,n])})).delegateTo(this.container,Selectors.createNewRecordBySelectorSelector)}registerRecordToggle(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation(),e.loadRecordDetails(this.closest(Selectors.toggleSelector).parentElement.dataset.objectId)})).delegateTo(this.container,`${Selectors.toggleSelector} .form-irre-header-cell:not(${Selectors.controlSectionSelector}`)}registerDeleteButton(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const n=TYPO3.lang["label.confirm.delete_record.title"]||"Delete this record?",i=(TYPO3.lang["label.confirm.delete_record.content"]||"Are you sure you want to delete the record '%s'?").replace("%s",this.dataset.recordInfo);Modal.confirm(n,i,Severity.warning,[{text:TYPO3.lang["buttons.confirm.delete_record.no"]||"Cancel",active:!0,btnClass:"btn-default",name:"no",trigger:(e,t)=>t.hideModal()},{text:TYPO3.lang["buttons.confirm.delete_record.yes"]||"Yes, delete this record",btnClass:"btn-warning",name:"yes",trigger:(t,n)=>{e.deleteRecord(this.closest("[data-object-id]").dataset.objectId),n.hideModal()}}])})).delegateTo(this.container,Selectors.deleteRecordButtonSelector)}createRecord(e,t,n=null,i=null){let o=this.container.dataset.objectGroup;null!==n?(o+=Separators.structureSeparator+n,SiteLanguageContainer.getInlineRecordContainer(o).insertAdjacentHTML("afterend",t),this.memorizeAddRecord(e,n,i)):(document.getElementById(this.container.getAttribute("id")+"_records").insertAdjacentHTML("beforeend",t),this.memorizeAddRecord(e,null,i))}async importRecord(e,t){return this.ajaxDispatcher.send(this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint("site_configuration_inline_create")),e).then((async e=>{this.createRecord(e.compilerInput.uid,e.data,void 0!==t?t:null,void 0!==e.compilerInput.childChildUid?e.compilerInput.childChildUid:null)}))}loadRecordDetails(e){const t=document.getElementById(e+"_fields"),n=SiteLanguageContainer.getInlineRecordContainer(e),i=void 0!==this.requestQueue[e];if(null!==t&&!n.classList.contains(States.notLoaded))SiteLanguageContainer.collapseExpandRecord(e);else{const o=this.getProgress(e,n.dataset.objectIdHash);if(i)this.requestQueue[e].abort(),delete this.requestQueue[e],delete this.progessQueue[e],o.done();else{const i=this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint("site_configuration_inline_details"));this.ajaxDispatcher.send(i,[e]).then((async i=>{delete this.requestQueue[e],delete this.progessQueue[e],n.classList.remove(States.notLoaded),t.innerHTML=i.data,SiteLanguageContainer.collapseExpandRecord(e),o.done(),FormEngine.reinitialize(),FormEngineValidation.initializeInputFields(),FormEngineValidation.validate(this.container),this.removeUsed(SiteLanguageContainer.getInlineRecordContainer(e))})),this.requestQueue[e]=i,o.start()}}}memorizeAddRecord(e,t=null,n=null){const i=this.getFormFieldForElements();if(null===i)return;let o=Utility.trimExplode(",",i.value);if(t){const n=[];for(let i=0;i<o.length;i++)o[i].length&&n.push(o[i]),t===o[i]&&n.push(e);o=n}else o.push(e);i.value=o.join(","),i.classList.add("has-change"),document.dispatchEvent(new Event("change")),this.setUnique(e,n),FormEngine.reinitialize(),FormEngineValidation.initializeInputFields(),FormEngineValidation.validate(this.container)}memorizeRemoveRecord(e){const t=this.getFormFieldForElements();if(null===t)return[];let n=Utility.trimExplode(",",t.value);const i=n.indexOf(e);return i>-1&&(delete n[i],t.value=n.join(","),t.classList.add("has-change"),document.dispatchEvent(new Event("change"))),n}deleteRecord(e,t=!1){const n=SiteLanguageContainer.getInlineRecordContainer(e),i=n.dataset.objectUid;if(n.classList.add("t3js-inline-record-deleted"),!n.classList.contains(States.new)&&!t){const e=this.container.querySelector('[name="cmd'+n.dataset.fieldName+'[delete]"]');e.removeAttribute("disabled"),n.parentElement.insertAdjacentElement("afterbegin",e)}new RegularEvent("transitionend",(()=>{n.parentElement.removeChild(n),FormEngineValidation.validate(this.container)})).bindTo(n),this.revertUnique(i),this.memorizeRemoveRecord(i),n.classList.add("form-irre-object--deleted")}getProgress(e,t){const n="#"+t+"_header";let i;return void 0!==this.progessQueue[e]?i=this.progessQueue[e]:(i=NProgress,i.configure({parent:n,showSpinner:!1}),this.progessQueue[e]=i),i}getFormFieldForElements(){const e=document.getElementsByName(this.container.dataset.formField);return e.length>0?e[0]:null}isUniqueElementUsed(e){const t=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup];return-1!==SiteLanguageContainer.getValuesFromHashMap(t.used).indexOf(e)}removeUsed(e){const t=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup],n=SiteLanguageContainer.getValuesFromHashMap(t.used);let i=e.querySelector('[name="data['+t.table+"]["+e.dataset.objectUid+"]["+t.field+']"]');if(null!==i){const e=i.options[i.selectedIndex].value;for(let t of n)t!==e&&SiteLanguageContainer.removeSelectOptionByValue(i,t)}}setUnique(e,t){const n=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup],i=document.getElementById(this.container.dataset.objectGroup+"_selector");if(-1!==n.max){const o=this.getFormFieldForElements(),a=this.container.dataset.objectGroup+Separators.structureSeparator+e;let r=SiteLanguageContainer.getInlineRecordContainer(a).querySelector('[name="data['+n.table+"]["+e+"]["+n.field+']"]');const s=SiteLanguageContainer.getValuesFromHashMap(n.used);if(null!==i){if(null!==r)for(let e of s)SiteLanguageContainer.removeSelectOptionByValue(r,e);for(let e of s)SiteLanguageContainer.removeSelectOptionByValue(r,e);void 0!==n.used.length&&(n.used={}),n.used[e]={table:n.elTable,uid:t}}if(null!==o&&SiteLanguageContainer.selectOptionValueExists(i,t)){const i=Utility.trimExplode(",",o.value);for(let o of i)r=document.querySelector('[name="data['+n.table+"]["+o+"]["+n.field+']"]'),null!==r&&o!==e&&SiteLanguageContainer.removeSelectOptionByValue(r,t)}}SiteLanguageContainer.selectOptionValueExists(i,t)&&(SiteLanguageContainer.removeSelectOptionByValue(i,t),n.used[e]={table:n.elTable,uid:t})}revertUnique(e){const t=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup],n=this.container.dataset.objectGroup+Separators.structureSeparator+e,i=SiteLanguageContainer.getInlineRecordContainer(n);let o,a=i.querySelector('[name="data['+t.table+"]["+i.dataset.objectUid+"]["+t.field+']"]');if(null!==a)o=a.value;else{if(""===i.dataset.tableUniqueOriginalValue)return;o=i.dataset.tableUniqueOriginalValue.replace(t.table+"_","")}if(!isNaN(parseInt(o,10))&&0x8000000000000000!==parseInt(o,10)){const e=document.getElementById(this.container.dataset.objectGroup+"_selector");SiteLanguageContainer.reAddSelectOption(e,o,t)}if(-1===t.max)return;const r=this.getFormFieldForElements();if(null===r)return;const s=Utility.trimExplode(",",r.value);let l;for(let e=0;e<s.length;e++)l=document.querySelector('[name="data['+t.table+"]["+s[e]+"]["+t.field+']"]'),null!==l&&SiteLanguageContainer.reAddSelectOption(l,o,t);delete t.used[e]}}window.customElements.define("typo3-formengine-container-sitelanguage",SiteLanguageContainer); \ No newline at end of file +import{MessageUtility}from"@typo3/backend/utility/message-utility.js";import{AjaxDispatcher}from"@typo3/backend/form-engine/inline-relation/ajax-dispatcher.js";import NProgress from"nprogress";import FormEngine from"@typo3/backend/form-engine.js";import FormEngineValidation from"@typo3/backend/form-engine-validation.js";import{default as Modal}from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import RegularEvent from"@typo3/core/event/regular-event.js";import Severity from"@typo3/backend/severity.js";import Utility from"@typo3/backend/utility.js";var Selectors,States,Separators;!function(e){e.toggleSelector='[data-bs-toggle="formengine-inline"]',e.controlSectionSelector=".t3js-formengine-irre-control",e.createNewRecordButtonSelector=".t3js-create-new-button",e.createNewRecordBySelectorSelector=".t3js-create-new-selector",e.deleteRecordButtonSelector=".t3js-editform-delete-inline-record"}(Selectors||(Selectors={})),function(e){e.new="inlineIsNewRecord",e.visible="panel-visible",e.collapsed="panel-collapsed",e.notLoaded="t3js-not-loaded"}(States||(States={})),function(e){e.structureSeparator="-"}(Separators||(Separators={}));class SiteLanguageContainer extends HTMLElement{constructor(){super(...arguments),this.container=null,this.ajaxDispatcher=null,this.requestQueue={},this.progessQueue={},this.handlePostMessage=e=>{if(!MessageUtility.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;if("typo3:foreignRelation:insert"===e.data.actionName){if(void 0===e.data.objectGroup)throw"No object group defined for message";if(e.data.objectGroup!==this.container.dataset.objectGroup)return;if(this.isUniqueElementUsed(parseInt(e.data.uid,10)))return void Notification.error("There is already a relation to the selected element");this.importRecord([e.data.objectGroup,e.data.uid]).then((()=>{if(e.source){const t={actionName:"typo3:foreignRelation:inserted",objectGroup:e.data.objectId,table:e.data.table,uid:e.data.uid};MessageUtility.send(t,e.source)}}))}}}static getInlineRecordContainer(e){return document.querySelector('[data-object-id="'+e+'"]')}static getValuesFromHashMap(e){return Object.keys(e).map((t=>e[t]))}static selectOptionValueExists(e,t){return null!==e.querySelector('option[value="'+t+'"]')}static removeSelectOptionByValue(e,t){const n=e.querySelector('option[value="'+t+'"]');null!==n&&n.remove()}static reAddSelectOption(e,t,n){if(SiteLanguageContainer.selectOptionValueExists(e,t))return;const i=e.querySelectorAll("option");let o=-1;for(const e of Object.keys(n.possible)){if(e===t)break;for(let t=0;t<i.length;++t){if(i[t].value===e){o=t;break}}}-1===o?o=0:o<i.length&&o++;const a=document.createElement("option");a.text=n.possible[t],a.value=t,e.insertBefore(a,e.options[o])}static collapseExpandRecord(e){const t=SiteLanguageContainer.getInlineRecordContainer(e),n=document.querySelector('[aria-controls="'+e+'_fields"]');t.classList.contains(States.collapsed)?(t.classList.remove(States.collapsed),t.classList.add(States.visible),n.setAttribute("aria-expanded","true")):(t.classList.remove(States.visible),t.classList.add(States.collapsed),n.setAttribute("aria-expanded","false"))}connectedCallback(){const e=this.getAttribute("identifier")||"";this.container=this.querySelector("#"+e),null!==this.container&&(this.ajaxDispatcher=new AjaxDispatcher(this.container.dataset.objectGroup),this.registerEvents())}registerEvents(){this.registerCreateRecordButton(),this.registerCreateRecordBySelector(),this.registerRecordToggle(),this.registerDeleteButton(),new RegularEvent("message",this.handlePostMessage).bindTo(window)}registerCreateRecordButton(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();let n=e.container.dataset.objectGroup;void 0!==this.dataset.recordUid&&(n+=Separators.structureSeparator+this.dataset.recordUid),e.importRecord([n,e.container.querySelector(Selectors.createNewRecordBySelectorSelector)?.value],this.dataset.recordUid??null)})).delegateTo(this.container,Selectors.createNewRecordButtonSelector)}registerCreateRecordBySelector(){const e=this;new RegularEvent("change",(function(t){t.preventDefault(),t.stopImmediatePropagation();const n=this.options[this.selectedIndex].getAttribute("value");e.importRecord([e.container.dataset.objectGroup,n])})).delegateTo(this.container,Selectors.createNewRecordBySelectorSelector)}registerRecordToggle(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation(),e.loadRecordDetails(this.closest(Selectors.toggleSelector).parentElement.dataset.objectId)})).delegateTo(this.container,`${Selectors.toggleSelector} .form-irre-header-cell:not(${Selectors.controlSectionSelector}`)}registerDeleteButton(){const e=this;new RegularEvent("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const n=TYPO3.lang["label.confirm.delete_record.title"]||"Delete this record?",i=(TYPO3.lang["label.confirm.delete_record.content"]||"Are you sure you want to delete the record '%s'?").replace("%s",this.dataset.recordInfo);Modal.confirm(n,i,Severity.warning,[{text:TYPO3.lang["buttons.confirm.delete_record.no"]||"Cancel",active:!0,btnClass:"btn-default",name:"no",trigger:(e,t)=>t.hideModal()},{text:TYPO3.lang["buttons.confirm.delete_record.yes"]||"Yes, delete this record",btnClass:"btn-warning",name:"yes",trigger:(t,n)=>{e.deleteRecord(this.closest("[data-object-id]").dataset.objectId),n.hideModal()}}])})).delegateTo(this.container,Selectors.deleteRecordButtonSelector)}createRecord(e,t,n=null,i=null){let o=this.container.dataset.objectGroup;null!==n?(o+=Separators.structureSeparator+n,SiteLanguageContainer.getInlineRecordContainer(o).insertAdjacentHTML("afterend",t),this.memorizeAddRecord(e,n,i)):(document.getElementById(this.container.getAttribute("id")+"_records").insertAdjacentHTML("beforeend",t),this.memorizeAddRecord(e,null,i))}async importRecord(e,t){return this.ajaxDispatcher.send(this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint("site_configuration_inline_create")),e).then((async e=>{this.createRecord(e.compilerInput.uid,e.data,void 0!==t?t:null,void 0!==e.compilerInput.childChildUid?e.compilerInput.childChildUid:null)}))}loadRecordDetails(e){const t=document.getElementById(e+"_fields"),n=SiteLanguageContainer.getInlineRecordContainer(e),i=void 0!==this.requestQueue[e];if(null!==t&&!n.classList.contains(States.notLoaded))SiteLanguageContainer.collapseExpandRecord(e);else{const o=this.getProgress(e,n.dataset.objectIdHash);if(i)this.requestQueue[e].abort(),delete this.requestQueue[e],delete this.progessQueue[e],o.done();else{const i=this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint("site_configuration_inline_details"));this.ajaxDispatcher.send(i,[e]).then((async i=>{delete this.requestQueue[e],delete this.progessQueue[e],n.classList.remove(States.notLoaded),t.innerHTML=i.data,SiteLanguageContainer.collapseExpandRecord(e),o.done(),FormEngine.reinitialize(),FormEngineValidation.initializeInputFields(),FormEngineValidation.validate(this.container),this.removeUsed(SiteLanguageContainer.getInlineRecordContainer(e))})),this.requestQueue[e]=i,o.start()}}}memorizeAddRecord(e,t=null,n=null){const i=this.getFormFieldForElements();if(null===i)return;let o=Utility.trimExplode(",",i.value);if(t){const n=[];for(let i=0;i<o.length;i++)o[i].length&&n.push(o[i]),t===o[i]&&n.push(e);o=n}else o.push(e);i.value=o.join(","),i.classList.add("has-change"),document.dispatchEvent(new Event("change")),this.setUnique(e,n),FormEngine.reinitialize(),FormEngineValidation.initializeInputFields(),FormEngineValidation.validate(this.container)}memorizeRemoveRecord(e){const t=this.getFormFieldForElements();if(null===t)return[];const n=Utility.trimExplode(",",t.value),i=n.indexOf(e);return i>-1&&(delete n[i],t.value=n.join(","),t.classList.add("has-change"),document.dispatchEvent(new Event("change"))),n}deleteRecord(e,t=!1){const n=SiteLanguageContainer.getInlineRecordContainer(e),i=n.dataset.objectUid;if(n.classList.add("t3js-inline-record-deleted"),!n.classList.contains(States.new)&&!t){const e=this.container.querySelector('[name="cmd'+n.dataset.fieldName+'[delete]"]');e.removeAttribute("disabled"),n.parentElement.insertAdjacentElement("afterbegin",e)}new RegularEvent("transitionend",(()=>{n.parentElement.removeChild(n),FormEngineValidation.validate(this.container)})).bindTo(n),this.revertUnique(i),this.memorizeRemoveRecord(i),n.classList.add("form-irre-object--deleted")}getProgress(e,t){const n="#"+t+"_header";let i;return void 0!==this.progessQueue[e]?i=this.progessQueue[e]:(i=NProgress,i.configure({parent:n,showSpinner:!1}),this.progessQueue[e]=i),i}getFormFieldForElements(){const e=document.getElementsByName(this.container.dataset.formField);return e.length>0?e[0]:null}isUniqueElementUsed(e){const t=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup];return-1!==SiteLanguageContainer.getValuesFromHashMap(t.used).indexOf(e)}removeUsed(e){const t=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup],n=SiteLanguageContainer.getValuesFromHashMap(t.used),i=e.querySelector('[name="data['+t.table+"]["+e.dataset.objectUid+"]["+t.field+']"]');if(null!==i){const e=i.options[i.selectedIndex].value;for(const t of n)t!==e&&SiteLanguageContainer.removeSelectOptionByValue(i,t)}}setUnique(e,t){const n=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup],i=document.getElementById(this.container.dataset.objectGroup+"_selector");if(-1!==n.max){const o=this.getFormFieldForElements(),a=this.container.dataset.objectGroup+Separators.structureSeparator+e;let r=SiteLanguageContainer.getInlineRecordContainer(a).querySelector('[name="data['+n.table+"]["+e+"]["+n.field+']"]');const s=SiteLanguageContainer.getValuesFromHashMap(n.used);if(null!==i){if(null!==r)for(const e of s)SiteLanguageContainer.removeSelectOptionByValue(r,e);for(const e of s)SiteLanguageContainer.removeSelectOptionByValue(r,e);void 0!==n.used.length&&(n.used={}),n.used[e]={table:n.elTable,uid:t}}if(null!==o&&SiteLanguageContainer.selectOptionValueExists(i,t)){const i=Utility.trimExplode(",",o.value);for(const o of i)r=document.querySelector('[name="data['+n.table+"]["+o+"]["+n.field+']"]'),null!==r&&o!==e&&SiteLanguageContainer.removeSelectOptionByValue(r,t)}}SiteLanguageContainer.selectOptionValueExists(i,t)&&(SiteLanguageContainer.removeSelectOptionByValue(i,t),n.used[e]={table:n.elTable,uid:t})}revertUnique(e){const t=TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup],n=this.container.dataset.objectGroup+Separators.structureSeparator+e,i=SiteLanguageContainer.getInlineRecordContainer(n),o=i.querySelector('[name="data['+t.table+"]["+i.dataset.objectUid+"]["+t.field+']"]');let a;if(null!==o)a=o.value;else{if(""===i.dataset.tableUniqueOriginalValue)return;a=i.dataset.tableUniqueOriginalValue.replace(t.table+"_","")}if("9223372036854775807"!==a){const e=document.getElementById(this.container.dataset.objectGroup+"_selector");SiteLanguageContainer.reAddSelectOption(e,a,t)}if(-1===t.max)return;const r=this.getFormFieldForElements();if(null===r)return;const s=Utility.trimExplode(",",r.value);let l;for(let e=0;e<s.length;e++)l=document.querySelector('[name="data['+t.table+"]["+s[e]+"]["+t.field+']"]'),null!==l&&SiteLanguageContainer.reAddSelectOption(l,a,t);delete t.used[e]}}window.customElements.define("typo3-formengine-container-sitelanguage",SiteLanguageContainer); \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/abstract-sortable-select-items.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/abstract-sortable-select-items.js index 579cd7199b12..535d2cc1b7e6 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/abstract-sortable-select-items.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/abstract-sortable-select-items.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import FormEngine from"@typo3/backend/form-engine.js";import FormEngineValidation from"@typo3/backend/form-engine-validation.js";export class AbstractSortableSelectItems{constructor(){this.registerSortableEventHandler=e=>{const t=e.closest(".form-wizards-wrap").querySelector(".form-wizards-items-aside");null!==t&&t.addEventListener("click",(t=>{let o;if(null===(o=t.target.closest(".t3js-btn-option")))return void(t.target.matches(".t3js-btn-option")&&(o=t.target));t.preventDefault();const n=o.dataset.fieldname,r=FormEngine.getFieldElement(n).get(0),i=FormEngine.getFieldElement(n,"_avail").get(0);o.classList.contains("t3js-btn-moveoption-top")?AbstractSortableSelectItems.moveOptionToTop(e):o.classList.contains("t3js-btn-moveoption-up")?AbstractSortableSelectItems.moveOptionUp(e):o.classList.contains("t3js-btn-moveoption-down")?AbstractSortableSelectItems.moveOptionDown(e):o.classList.contains("t3js-btn-moveoption-bottom")?AbstractSortableSelectItems.moveOptionToBottom(e):o.classList.contains("t3js-btn-removeoption")&&AbstractSortableSelectItems.removeOption(e,i),FormEngine.updateHiddenFieldValueFromSelect(e,r),FormEngine.legacyFieldChangedCb(),FormEngineValidation.markFieldAsChanged(i),FormEngineValidation.validateField(i)}))}}static moveOptionToTop(e){Array.from(e.querySelectorAll(":checked")).reverse().forEach((t=>{e.insertBefore(t,e.firstElementChild)}))}static moveOptionToBottom(e){e.querySelectorAll(":checked").forEach((t=>{e.insertBefore(t,null)}))}static moveOptionUp(e){const t=Array.from(e.children),o=Array.from(e.querySelectorAll(":checked"));for(let n of o){if(0===t.indexOf(n)&&null===n.previousElementSibling)break;e.insertBefore(n,n.previousElementSibling)}}static moveOptionDown(e){const t=Array.from(e.children).reverse(),o=Array.from(e.querySelectorAll(":checked")).reverse();for(let n of o){if(0===t.indexOf(n)&&null===n.nextElementSibling)break;e.insertBefore(n,n.nextElementSibling.nextElementSibling)}}static removeOption(e,t){e.querySelectorAll(":checked").forEach((o=>{const n=t.querySelector('option[value="'+o.value+'"]');null!==n&&(n.classList.remove("hidden"),n.disabled=!1,FormEngine.enableOptGroup(n)),e.removeChild(o)}))}} \ No newline at end of file +import FormEngine from"@typo3/backend/form-engine.js";import FormEngineValidation from"@typo3/backend/form-engine-validation.js";export class AbstractSortableSelectItems{constructor(){this.registerSortableEventHandler=e=>{const t=e.closest(".form-wizards-wrap").querySelector(".form-wizards-items-aside");null!==t&&t.addEventListener("click",(t=>{let o;if(null===(o=t.target.closest(".t3js-btn-option")))return void(t.target.matches(".t3js-btn-option")&&(o=t.target));t.preventDefault();const n=o.dataset.fieldname,r=FormEngine.getFieldElement(n).get(0),i=FormEngine.getFieldElement(n,"_avail").get(0);o.classList.contains("t3js-btn-moveoption-top")?AbstractSortableSelectItems.moveOptionToTop(e):o.classList.contains("t3js-btn-moveoption-up")?AbstractSortableSelectItems.moveOptionUp(e):o.classList.contains("t3js-btn-moveoption-down")?AbstractSortableSelectItems.moveOptionDown(e):o.classList.contains("t3js-btn-moveoption-bottom")?AbstractSortableSelectItems.moveOptionToBottom(e):o.classList.contains("t3js-btn-removeoption")&&AbstractSortableSelectItems.removeOption(e,i),FormEngine.updateHiddenFieldValueFromSelect(e,r),FormEngine.legacyFieldChangedCb(),FormEngineValidation.markFieldAsChanged(i),FormEngineValidation.validateField(i)}))}}static moveOptionToTop(e){Array.from(e.querySelectorAll(":checked")).reverse().forEach((t=>{e.insertBefore(t,e.firstElementChild)}))}static moveOptionToBottom(e){e.querySelectorAll(":checked").forEach((t=>{e.insertBefore(t,null)}))}static moveOptionUp(e){const t=Array.from(e.children),o=Array.from(e.querySelectorAll(":checked"));for(const n of o){if(0===t.indexOf(n)&&null===n.previousElementSibling)break;e.insertBefore(n,n.previousElementSibling)}}static moveOptionDown(e){const t=Array.from(e.children).reverse(),o=Array.from(e.querySelectorAll(":checked")).reverse();for(const n of o){if(0===t.indexOf(n)&&null===n.nextElementSibling)break;e.insertBefore(n,n.nextElementSibling.nextElementSibling)}}static removeOption(e,t){e.querySelectorAll(":checked").forEach((o=>{const n=t.querySelector('option[value="'+o.value+'"]');null!==n&&(n.classList.remove("hidden"),n.disabled=!1,FormEngine.enableOptGroup(n)),e.removeChild(o)}))}} \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-single-element.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-single-element.js index 4930e5990ce5..2ec179900f8e 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-single-element.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-single-element.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import RegularEvent from"@typo3/core/event/regular-event.js";import DocumentService from"@typo3/core/document-service.js";import FormEngine from"@typo3/backend/form-engine.js";class SelectSingleElement{constructor(){this.initialize=(e,t)=>{let n=document.querySelector(e);t=t||{},new RegularEvent("change",(e=>{const t=e.target,n=t.parentElement.querySelector(".input-group-icon");null!==n&&(n.innerHTML=t.options[t.selectedIndex].dataset.icon);const i=t.closest(".t3js-formengine-field-item").querySelector(".t3js-forms-select-single-icons");if(null!==i){const e=i.querySelector(".form-wizard-icon-list-item a.active");null!==e&&e.classList.remove("active");const n=i.querySelector('[data-select-index="'+t.selectedIndex+'"]');null!==n&&n.closest(".form-wizard-icon-list-item a").classList.add("active")}})).bindTo(n),t.onChange instanceof Array&&new RegularEvent("change",(()=>FormEngine.processOnFieldChange(t.onChange))).bindTo(n),new RegularEvent("click",((e,t)=>{const i=t.closest(".t3js-forms-select-single-icons").querySelector(".form-wizard-icon-list-item a.active");null!==i&&i.classList.remove("active"),n.selectedIndex=parseInt(t.dataset.selectIndex,10),n.dispatchEvent(new Event("change")),t.closest(".form-wizard-icon-list-item a").classList.add("active")})).delegateTo(n.closest(".form-control-wrap"),".t3js-forms-select-single-icons .form-wizard-icon-list-item a:not(.active)")}}initializeOnReady(e,t){DocumentService.ready().then((()=>{this.initialize(e,t)}))}}export default new SelectSingleElement; \ No newline at end of file +import RegularEvent from"@typo3/core/event/regular-event.js";import DocumentService from"@typo3/core/document-service.js";import FormEngine from"@typo3/backend/form-engine.js";class SelectSingleElement{constructor(){this.initialize=(e,t)=>{const n=document.querySelector(e);t=t||{},new RegularEvent("change",(e=>{const t=e.target,n=t.parentElement.querySelector(".input-group-icon");null!==n&&(n.innerHTML=t.options[t.selectedIndex].dataset.icon);const i=t.closest(".t3js-formengine-field-item").querySelector(".t3js-forms-select-single-icons");if(null!==i){const e=i.querySelector(".form-wizard-icon-list-item a.active");null!==e&&e.classList.remove("active");const n=i.querySelector('[data-select-index="'+t.selectedIndex+'"]');null!==n&&n.closest(".form-wizard-icon-list-item a").classList.add("active")}})).bindTo(n),t.onChange instanceof Array&&new RegularEvent("change",(()=>FormEngine.processOnFieldChange(t.onChange))).bindTo(n),new RegularEvent("click",((e,t)=>{const i=t.closest(".t3js-forms-select-single-icons").querySelector(".form-wizard-icon-list-item a.active");null!==i&&i.classList.remove("active"),n.selectedIndex=parseInt(t.dataset.selectIndex,10),n.dispatchEvent(new Event("change")),t.closest(".form-wizard-icon-list-item a").classList.add("active")})).delegateTo(n.closest(".form-control-wrap"),".t3js-forms-select-single-icons .form-wizard-icon-list-item a:not(.active)")}}initializeOnReady(e,t){DocumentService.ready().then((()=>{this.initialize(e,t)}))}}export default new SelectSingleElement; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-tree-element.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-tree-element.js index c15e8ee2b696..7e15e20203ec 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-tree-element.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-tree-element.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import"@typo3/backend/form-engine/element/select-tree.js";import"@typo3/backend/form-engine/element/select-tree-toolbar.js";import"@typo3/backend/element/icon-element.js";import FormEngine from"@typo3/backend/form-engine.js";export class SelectTreeElement{constructor(e,t,r,i){if(this.recordField=null,this.tree=null,this.selectNode=e=>{const t=e.detail.node;this.updateAncestorsIndeterminateState(t),this.calculateIndeterminate(this.tree.nodes),this.saveCheckboxes(),this.tree.setup.input.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))},this.loadDataAfter=()=>{this.tree.nodes=this.tree.nodes.map((e=>(e.indeterminate=!1,e))),this.calculateIndeterminate(this.tree.nodes)},this.saveCheckboxes=()=>{void 0!==this.recordField&&(this.recordField.value=this.tree.getSelectedNodes().map((e=>e.identifier)).join(","))},r instanceof Function)throw new Error("Function `callback` is not supported anymore since TYPO3 v12.0");this.recordField=document.getElementById(t);const a=document.getElementById(e);this.tree=document.createElement("typo3-backend-form-selecttree"),this.tree.classList.add("svg-tree-wrapper"),this.tree.addEventListener("typo3:svg-tree:nodes-prepared",this.loadDataAfter),this.tree.addEventListener("typo3:svg-tree:node-selected",this.selectNode),i instanceof Array&&this.tree.addEventListener("typo3:svg-tree:node-selected",(()=>{FormEngine.processOnFieldChange(i)}));const s={id:e,dataUrl:this.generateRequestUrl(),readOnlyMode:1===parseInt(this.recordField.dataset.readOnly,10),input:this.recordField,exclusiveNodesIdentifiers:this.recordField.dataset.treeExclusiveKeys,validation:JSON.parse(this.recordField.dataset.formengineValidationRules)[0],expandUpToLevel:this.recordField.dataset.treeExpandUpToLevel,unselectableElements:[]};this.tree.addEventListener("svg-tree:initialized",(()=>{if(this.recordField.dataset.treeShowToolbar){const e=document.createElement("typo3-backend-form-selecttree-toolbar");e.tree=this.tree,this.tree.prepend(e)}})),this.tree.setup=s,a.append(this.tree),this.listenForVisibleTree()}listenForVisibleTree(){if(!this.tree.offsetParent){let e=this.tree.closest(".tab-pane").getAttribute("id");if(e){document.querySelector('[aria-controls="'+e+'"]').addEventListener("shown.bs.tab",(()=>{this.tree.dispatchEvent(new Event("svg-tree:visible"))}))}}}generateRequestUrl(){const e={tableName:this.recordField.dataset.tablename,fieldName:this.recordField.dataset.fieldname,uid:this.recordField.dataset.uid,defaultValues:this.recordField.dataset.defaultvalues,overrideValues:this.recordField.dataset.overridevalues,recordTypeValue:this.recordField.dataset.recordtypevalue,dataStructureIdentifier:this.recordField.dataset.datastructureidentifier,flexFormSheetName:this.recordField.dataset.flexformsheetname,flexFormFieldName:this.recordField.dataset.flexformfieldname,flexFormContainerName:this.recordField.dataset.flexformcontainername,flexFormContainerIdentifier:this.recordField.dataset.flexformcontaineridentifier,flexFormContainerFieldName:this.recordField.dataset.flexformcontainerfieldname,flexFormSectionContainerIsNew:this.recordField.dataset.flexformsectioncontainerisnew,command:this.recordField.dataset.command};return TYPO3.settings.ajaxUrls.record_tree_data+"&"+new URLSearchParams(e)}updateAncestorsIndeterminateState(e){let t=!1;e.parents.forEach((e=>{const r=this.tree.nodes[e];r.indeterminate=r.checked||r.indeterminate||t,t=r.checked||r.indeterminate||r.checked||r.indeterminate}))}calculateIndeterminate(e){e.forEach((t=>{(t.checked||t.indeterminate)&&t.parents&&t.parents.length>0&&t.parents.forEach((t=>{e[t].indeterminate=!0}))}))}} \ No newline at end of file +import"@typo3/backend/form-engine/element/select-tree.js";import"@typo3/backend/form-engine/element/select-tree-toolbar.js";import"@typo3/backend/element/icon-element.js";import FormEngine from"@typo3/backend/form-engine.js";export class SelectTreeElement{constructor(e,t,r,i){if(this.recordField=null,this.tree=null,this.selectNode=e=>{const t=e.detail.node;this.updateAncestorsIndeterminateState(t),this.calculateIndeterminate(this.tree.nodes),this.saveCheckboxes(),this.tree.setup.input.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))},this.loadDataAfter=()=>{this.tree.nodes=this.tree.nodes.map((e=>(e.indeterminate=!1,e))),this.calculateIndeterminate(this.tree.nodes)},this.saveCheckboxes=()=>{void 0!==this.recordField&&(this.recordField.value=this.tree.getSelectedNodes().map((e=>e.identifier)).join(","))},r instanceof Function)throw new Error("Function `callback` is not supported anymore since TYPO3 v12.0");this.recordField=document.getElementById(t);const a=document.getElementById(e);this.tree=document.createElement("typo3-backend-form-selecttree"),this.tree.classList.add("svg-tree-wrapper"),this.tree.addEventListener("typo3:svg-tree:nodes-prepared",this.loadDataAfter),this.tree.addEventListener("typo3:svg-tree:node-selected",this.selectNode),i instanceof Array&&this.tree.addEventListener("typo3:svg-tree:node-selected",(()=>{FormEngine.processOnFieldChange(i)}));const s={id:e,dataUrl:this.generateRequestUrl(),readOnlyMode:1===parseInt(this.recordField.dataset.readOnly,10),input:this.recordField,exclusiveNodesIdentifiers:this.recordField.dataset.treeExclusiveKeys,validation:JSON.parse(this.recordField.dataset.formengineValidationRules)[0],expandUpToLevel:this.recordField.dataset.treeExpandUpToLevel,unselectableElements:[]};this.tree.addEventListener("svg-tree:initialized",(()=>{if(this.recordField.dataset.treeShowToolbar){const e=document.createElement("typo3-backend-form-selecttree-toolbar");e.tree=this.tree,this.tree.prepend(e)}})),this.tree.setup=s,a.append(this.tree),this.listenForVisibleTree()}listenForVisibleTree(){if(!this.tree.offsetParent){const e=this.tree.closest(".tab-pane").getAttribute("id");if(e){document.querySelector('[aria-controls="'+e+'"]').addEventListener("shown.bs.tab",(()=>{this.tree.dispatchEvent(new Event("svg-tree:visible"))}))}}}generateRequestUrl(){const e={tableName:this.recordField.dataset.tablename,fieldName:this.recordField.dataset.fieldname,uid:this.recordField.dataset.uid,defaultValues:this.recordField.dataset.defaultvalues,overrideValues:this.recordField.dataset.overridevalues,recordTypeValue:this.recordField.dataset.recordtypevalue,dataStructureIdentifier:this.recordField.dataset.datastructureidentifier,flexFormSheetName:this.recordField.dataset.flexformsheetname,flexFormFieldName:this.recordField.dataset.flexformfieldname,flexFormContainerName:this.recordField.dataset.flexformcontainername,flexFormContainerIdentifier:this.recordField.dataset.flexformcontaineridentifier,flexFormContainerFieldName:this.recordField.dataset.flexformcontainerfieldname,flexFormSectionContainerIsNew:this.recordField.dataset.flexformsectioncontainerisnew,command:this.recordField.dataset.command};return TYPO3.settings.ajaxUrls.record_tree_data+"&"+new URLSearchParams(e)}updateAncestorsIndeterminateState(e){let t=!1;e.parents.forEach((e=>{const r=this.tree.nodes[e];r.indeterminate=r.checked||r.indeterminate||t,t=r.checked||r.indeterminate||r.checked||r.indeterminate}))}calculateIndeterminate(e){e.forEach((t=>{(t.checked||t.indeterminate)&&t.parents&&t.parents.length>0&&t.parents.forEach((t=>{e[t].indeterminate=!0}))}))}} \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-tree.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-tree.js index 51bb0289de01..3949fe5390b8 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-tree.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/select-tree.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -var __decorate=function(e,t,i,s){var n,d=arguments.length,r=d<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,i,s);else for(var o=e.length-1;o>=0;o--)(n=e[o])&&(r=(d<3?n(r):d>3?n(t,i,r):n(t,i))||r);return d>3&&r&&Object.defineProperty(t,i,r),r};import*as d3selection from"d3-selection";import{SvgTree}from"@typo3/backend/svg-tree.js";import{customElement}from"lit/decorators.js";let SelectTree=class extends SvgTree{constructor(){super(),this.textPosition=30,this.settings={unselectableElements:[],exclusiveNodesIdentifiers:"",validation:{},readOnlyMode:!1,showIcons:!0,marginTop:15,nodeHeight:26,icon:{size:16,containerSize:20},indentWidth:20,width:300,duration:400,dataUrl:"",defaultProperties:{},expandUpToLevel:null},this.exclusiveSelectedNode=null,this.addIcons(),this.addEventListener("typo3:svg-tree:nodes-prepared",this.prepareLoadedNodes)}expandAll(){this.nodes.forEach((e=>{this.showChildren(e)})),this.prepareDataForVisibleNodes(),this.updateVisibleNodes()}selectNode(e,t=!0){if(!this.isNodeSelectable(e))return;const i=e.checked;this.handleExclusiveNodeSelection(e),this.settings.validation&&this.settings.validation.maxItems&&!i&&this.getSelectedNodes().length>=this.settings.validation.maxItems||(e.checked=!i,this.dispatchEvent(new CustomEvent("typo3:svg-tree:node-selected",{detail:{node:e,propagate:t}})),this.updateVisibleNodes())}filter(e){this.searchTerm=e,this.nodes.length&&(this.nodes[0].expanded=!1);const t=new RegExp(e,"i");this.nodes.forEach((e=>{t.test(e.name)?(this.showParents(e),e.expanded=!0,e.hidden=!1):(e.hidden=!0,e.expanded=!1)})),this.prepareDataForVisibleNodes(),this.updateVisibleNodes()}showParents(e){if(0===e.parents.length)return;const t=this.nodes[e.parents[0]];t.hidden=!1,t.expanded=!0,this.showParents(t)}updateVisibleNodes(){super.updateVisibleNodes();const e=Math.ceil(this.viewportHeight/this.settings.nodeHeight+1),t=Math.floor(Math.max(this.scrollTop-2*this.settings.nodeHeight,0)/this.settings.nodeHeight),i=this.data.nodes.slice(t,t+e);this.nodesContainer.selectAll(".node").data(i,(e=>e.stateIdentifier)).selectAll(".tree-check use").attr("visibility",(function(e){const t=Boolean(e.checked),i=d3selection.select(this);return i.classed("icon-checked")&&t||i.classed("icon-indeterminate")&&e.indeterminate&&!t?"visible":!i.classed("icon-check")||e.indeterminate||t?"hidden":"visible"}))}isNodeSelectable(e){return!this.settings.readOnlyMode&&-1===this.settings.unselectableElements.indexOf(e.identifier)}appendTextElement(e){return this.renderCheckbox(e),super.appendTextElement(e)}renderCheckbox(e){const t=e.filter((e=>this.isNodeSelectable(e)||Boolean(e.checked))).append("g").attr("class","tree-check").on("click",((e,t)=>{this.selectNode(t),this.focusNode(t),this.updateVisibleNodes()}));t.append("use").attr("x",28).attr("y",-8).attr("visibility","hidden").attr("class","icon-check").attr("xlink:href","#icon-check"),t.append("use").attr("x",28).attr("y",-8).attr("visibility","hidden").attr("class","icon-checked").attr("xlink:href","#icon-checked"),t.append("use").attr("x",28).attr("y",-8).attr("visibility","hidden").attr("class","icon-indeterminate").attr("xlink:href","#icon-indeterminate")}prepareLoadedNodes(e){let t=e.detail.nodes;e.detail.nodes=t.map((e=>{if(!e.stateIdentifier){const t=e.parents.length?e.parents[e.parents.length-1]:e.identifier;e.stateIdentifier=t+"_"+e.identifier}return!1===e.selectable&&this.settings.unselectableElements.push(e.identifier),e}))}handleExclusiveNodeSelection(e){const t=this.settings.exclusiveNodesIdentifiers.split(",");this.settings.exclusiveNodesIdentifiers.length&&!1===e.checked&&(t.indexOf(""+e.identifier)>-1?(this.disableSelectedNodes(),this.exclusiveSelectedNode=e):-1===t.indexOf(""+e.identifier)&&this.exclusiveSelectedNode&&(this.exclusiveSelectedNode.checked=!1,this.exclusiveSelectedNode=null))}addIcons(){this.icons={check:{identifier:"check",icon:'<g width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><rect height="16" width="16" fill="transparent"></rect><path transform="scale(0.01)" d="M1312 256h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-832q0-66-47-113t-113-47zm288 160v832q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q119 0 203.5 84.5t84.5 203.5z"></path></g>'},checked:{identifier:"checked",icon:'<g width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><rect height="16" width="16" fill="transparent"></rect><path transform="scale(0.01)" d="M813 1299l614-614q19-19 19-45t-19-45l-102-102q-19-19-45-19t-45 19l-467 467-211-211q-19-19-45-19t-45 19l-102 102q-19 19-19 45t19 45l358 358q19 19 45 19t45-19zm851-883v960q0 119-84.5 203.5t-203.5 84.5h-960q-119 0-203.5-84.5t-84.5-203.5v-960q0-119 84.5-203.5t203.5-84.5h960q119 0 203.5 84.5t84.5 203.5z"></path></g>'},indeterminate:{identifier:"indeterminate",icon:'<g width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><rect height="16" width="16" fill="transparent"></rect><path transform="scale(0.01)" d="M1344 800v64q0 14-9 23t-23 9h-832q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h832q14 0 23 9t9 23zm128 448v-832q0-66-47-113t-113-47h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113zm128-832v832q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q119 0 203.5 84.5t84.5 203.5z"></path></g>'}}}};SelectTree=__decorate([customElement("typo3-backend-form-selecttree")],SelectTree);export{SelectTree}; \ No newline at end of file +var __decorate=function(e,t,i,s){var n,d=arguments.length,r=d<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,i,s);else for(var o=e.length-1;o>=0;o--)(n=e[o])&&(r=(d<3?n(r):d>3?n(t,i,r):n(t,i))||r);return d>3&&r&&Object.defineProperty(t,i,r),r};import*as d3selection from"d3-selection";import{SvgTree}from"@typo3/backend/svg-tree.js";import{customElement}from"lit/decorators.js";let SelectTree=class extends SvgTree{constructor(){super(),this.textPosition=30,this.settings={unselectableElements:[],exclusiveNodesIdentifiers:"",validation:{},readOnlyMode:!1,showIcons:!0,marginTop:15,nodeHeight:26,icon:{size:16,containerSize:20},indentWidth:20,width:300,duration:400,dataUrl:"",defaultProperties:{},expandUpToLevel:null},this.exclusiveSelectedNode=null,this.addIcons(),this.addEventListener("typo3:svg-tree:nodes-prepared",this.prepareLoadedNodes)}expandAll(){this.nodes.forEach((e=>{this.showChildren(e)})),this.prepareDataForVisibleNodes(),this.updateVisibleNodes()}selectNode(e,t=!0){if(!this.isNodeSelectable(e))return;const i=e.checked;this.handleExclusiveNodeSelection(e),this.settings.validation&&this.settings.validation.maxItems&&!i&&this.getSelectedNodes().length>=this.settings.validation.maxItems||(e.checked=!i,this.dispatchEvent(new CustomEvent("typo3:svg-tree:node-selected",{detail:{node:e,propagate:t}})),this.updateVisibleNodes())}filter(e){this.searchTerm=e,this.nodes.length&&(this.nodes[0].expanded=!1);const t=new RegExp(e,"i");this.nodes.forEach((e=>{t.test(e.name)?(this.showParents(e),e.expanded=!0,e.hidden=!1):(e.hidden=!0,e.expanded=!1)})),this.prepareDataForVisibleNodes(),this.updateVisibleNodes()}showParents(e){if(0===e.parents.length)return;const t=this.nodes[e.parents[0]];t.hidden=!1,t.expanded=!0,this.showParents(t)}updateVisibleNodes(){super.updateVisibleNodes();const e=Math.ceil(this.viewportHeight/this.settings.nodeHeight+1),t=Math.floor(Math.max(this.scrollTop-2*this.settings.nodeHeight,0)/this.settings.nodeHeight),i=this.data.nodes.slice(t,t+e);this.nodesContainer.selectAll(".node").data(i,(e=>e.stateIdentifier)).selectAll(".tree-check use").attr("visibility",(function(e){const t=Boolean(e.checked),i=d3selection.select(this);return i.classed("icon-checked")&&t||i.classed("icon-indeterminate")&&e.indeterminate&&!t?"visible":!i.classed("icon-check")||e.indeterminate||t?"hidden":"visible"}))}isNodeSelectable(e){return!this.settings.readOnlyMode&&-1===this.settings.unselectableElements.indexOf(e.identifier)}appendTextElement(e){return this.renderCheckbox(e),super.appendTextElement(e)}renderCheckbox(e){const t=e.filter((e=>this.isNodeSelectable(e)||Boolean(e.checked))).append("g").attr("class","tree-check").on("click",((e,t)=>{this.selectNode(t),this.focusNode(t),this.updateVisibleNodes()}));t.append("use").attr("x",28).attr("y",-8).attr("visibility","hidden").attr("class","icon-check").attr("xlink:href","#icon-check"),t.append("use").attr("x",28).attr("y",-8).attr("visibility","hidden").attr("class","icon-checked").attr("xlink:href","#icon-checked"),t.append("use").attr("x",28).attr("y",-8).attr("visibility","hidden").attr("class","icon-indeterminate").attr("xlink:href","#icon-indeterminate")}prepareLoadedNodes(e){const t=e.detail.nodes;e.detail.nodes=t.map((e=>{if(!e.stateIdentifier){const t=e.parents.length?e.parents[e.parents.length-1]:e.identifier;e.stateIdentifier=t+"_"+e.identifier}return!1===e.selectable&&this.settings.unselectableElements.push(e.identifier),e}))}handleExclusiveNodeSelection(e){const t=this.settings.exclusiveNodesIdentifiers.split(",");this.settings.exclusiveNodesIdentifiers.length&&!1===e.checked&&(t.indexOf(""+e.identifier)>-1?(this.disableSelectedNodes(),this.exclusiveSelectedNode=e):-1===t.indexOf(""+e.identifier)&&this.exclusiveSelectedNode&&(this.exclusiveSelectedNode.checked=!1,this.exclusiveSelectedNode=null))}addIcons(){this.icons={check:{identifier:"check",icon:'<g width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><rect height="16" width="16" fill="transparent"></rect><path transform="scale(0.01)" d="M1312 256h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-832q0-66-47-113t-113-47zm288 160v832q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q119 0 203.5 84.5t84.5 203.5z"></path></g>'},checked:{identifier:"checked",icon:'<g width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><rect height="16" width="16" fill="transparent"></rect><path transform="scale(0.01)" d="M813 1299l614-614q19-19 19-45t-19-45l-102-102q-19-19-45-19t-45 19l-467 467-211-211q-19-19-45-19t-45 19l-102 102q-19 19-19 45t19 45l358 358q19 19 45 19t45-19zm851-883v960q0 119-84.5 203.5t-203.5 84.5h-960q-119 0-203.5-84.5t-84.5-203.5v-960q0-119 84.5-203.5t203.5-84.5h960q119 0 203.5 84.5t84.5 203.5z"></path></g>'},indeterminate:{identifier:"indeterminate",icon:'<g width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><rect height="16" width="16" fill="transparent"></rect><path transform="scale(0.01)" d="M1344 800v64q0 14-9 23t-23 9h-832q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h832q14 0 23 9t9 23zm128 448v-832q0-66-47-113t-113-47h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113zm128-832v832q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q119 0 203.5 84.5t84.5 203.5z"></path></g>'}}}};SelectTree=__decorate([customElement("typo3-backend-form-selecttree")],SelectTree);export{SelectTree}; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/suggest/result-container.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/suggest/result-container.js index 2df586f0c560..fc3ab47f5966 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/suggest/result-container.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/element/suggest/result-container.js @@ -10,14 +10,14 @@ * * The TYPO3 project - inspiring people to share! */ -var __decorate=function(e,t,r,s){var n,l=arguments.length,o=l<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,r):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,r,s);else for(var i=e.length-1;i>=0;i--)(n=e[i])&&(o=(l<3?n(o):l>3?n(t,r,o):n(t,r))||o);return l>3&&o&&Object.defineProperty(t,r,o),o};import{customElement,property}from"lit/decorators.js";import{css,html,LitElement}from"lit";import{lll}from"@typo3/core/lit-helper.js";import"@typo3/backend/form-engine/element/suggest/result-item.js";let ResultContainer=class extends LitElement{constructor(){super(...arguments),this.results=null}connectedCallback(){super.connectedCallback(),this.addEventListener("keydown",this.handleKeyDown)}disconnectedCallback(){this.removeEventListener("keydown",this.handleKeyDown),super.disconnectedCallback()}createRenderRoot(){return this}render(){let e;return null!==this.results&&(e=0===this.results.length?html`<div class="alert alert-info">${lll("search.no_records_found")}</div>`:html`${this.results.map((e=>this.renderResultItem(e)))}`),html`<typo3-backend-formengine-suggest-result-list>${e}</typo3-backend-formengine-suggest-result-list>`}renderResultItem(e){return html`<typo3-backend-formengine-suggest-result-item +var __decorate=function(e,t,r,n){var s,l=arguments.length,o=l<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,r):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,r,n);else for(var i=e.length-1;i>=0;i--)(s=e[i])&&(o=(l<3?s(o):l>3?s(t,r,o):s(t,r))||o);return l>3&&o&&Object.defineProperty(t,r,o),o};import{customElement,property}from"lit/decorators.js";import{css,html,LitElement}from"lit";import{lll}from"@typo3/core/lit-helper.js";import"@typo3/backend/form-engine/element/suggest/result-item.js";let ResultContainer=class extends LitElement{constructor(){super(...arguments),this.results=null}connectedCallback(){super.connectedCallback(),this.addEventListener("keydown",this.handleKeyDown)}disconnectedCallback(){this.removeEventListener("keydown",this.handleKeyDown),super.disconnectedCallback()}createRenderRoot(){return this}render(){let e;return null!==this.results&&(e=0===this.results.length?html`<div class="alert alert-info">${lll("search.no_records_found")}</div>`:html`${this.results.map((e=>this.renderResultItem(e)))}`),html`<typo3-backend-formengine-suggest-result-list>${e}</typo3-backend-formengine-suggest-result-list>`}renderResultItem(e){return html`<typo3-backend-formengine-suggest-result-item tabindex="1" icon="${JSON.stringify(e.icon)}" uid="${e.uid}" table="${e.table}" label="${e.label}" path="${e.path}"> - </typo3-backend-formengine-suggest-result-item>`}handleKeyDown(e){if(e.preventDefault(),"Escape"===e.key)return this.closest(".t3-form-suggest-container").querySelector('input[type="search"]').focus(),void(this.hidden=!0);if(!["ArrowDown","ArrowUp"].includes(e.key))return;if("typo3-backend-formengine-suggest-result-item"!==document.activeElement.tagName.toLowerCase())return;let t;"ArrowDown"===e.key?t=document.activeElement.nextElementSibling:(t=document.activeElement.previousElementSibling,null===t&&(t=this.closest(".t3-form-suggest-container").querySelector('input[type="search"]'))),null!==t&&t.focus()}};__decorate([property({type:Object})],ResultContainer.prototype,"results",void 0),ResultContainer=__decorate([customElement("typo3-backend-formengine-suggest-result-container")],ResultContainer);let ResultList=class extends LitElement{render(){return html`<slot></slot>`}};ResultList.styles=css` + </typo3-backend-formengine-suggest-result-item>`}handleKeyDown(e){if(e.preventDefault(),"Escape"===e.key)return this.closest(".t3-form-suggest-container").querySelector('input[type="search"]').focus(),void(this.hidden=!0);if(!["ArrowDown","ArrowUp"].includes(e.key))return;if("typo3-backend-formengine-suggest-result-item"!==document.activeElement.tagName.toLowerCase())return;let t;"ArrowDown"===e.key?t=document.activeElement.nextElementSibling:(t=document.activeElement.previousElementSibling,null===t&&(t=this.closest(".t3-form-suggest-container").querySelector('input[type="search"]'))),null!==t&&t.focus()}};__decorate([property({type:Object})],ResultContainer.prototype,"results",void 0),ResultContainer=__decorate([customElement("typo3-backend-formengine-suggest-result-container")],ResultContainer);export{ResultContainer};let ResultList=class extends LitElement{render(){return html`<slot></slot>`}};ResultList.styles=css` :host { display: block; } diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/field-control/insert-clipboard.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/field-control/insert-clipboard.js index 634ec5d9c8d0..7f747cfa1faf 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/field-control/insert-clipboard.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/field-control/insert-clipboard.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DocumentService from"@typo3/core/document-service.js";import FormEngine from"@typo3/backend/form-engine.js";class InsertClipboard{constructor(e){this.controlElement=null,this.registerClickHandler=e=>{e.preventDefault();const t=this.controlElement.dataset.element,r=JSON.parse(this.controlElement.dataset.clipboardItems);for(let e of r)FormEngine.setSelectOptionFromExternalSource(t,e.value,e.title,e.title)},DocumentService.ready().then((()=>{this.controlElement=document.querySelector(e),this.controlElement.addEventListener("click",this.registerClickHandler)}))}}export default InsertClipboard; \ No newline at end of file +import DocumentService from"@typo3/core/document-service.js";import FormEngine from"@typo3/backend/form-engine.js";class InsertClipboard{constructor(e){this.controlElement=null,this.registerClickHandler=e=>{e.preventDefault();const t=this.controlElement.dataset.element,r=JSON.parse(this.controlElement.dataset.clipboardItems);for(const e of r)FormEngine.setSelectOptionFromExternalSource(t,e.value,e.title,e.title)},DocumentService.ready().then((()=>{this.controlElement=document.querySelector(e),this.controlElement.addEventListener("click",this.registerClickHandler)}))}}export default InsertClipboard; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/field-control/reset-selection.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/field-control/reset-selection.js index b5af2ad7f4f7..31e5e6e9bf7d 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/field-control/reset-selection.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/field-control/reset-selection.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DocumentService from"@typo3/core/document-service.js";class ResetSelection{constructor(e){this.controlElement=null,this.registerClickHandler=e=>{e.preventDefault();const t=this.controlElement.dataset.itemName,o=JSON.parse(this.controlElement.dataset.selectedIndices),n=document.forms.namedItem("editform").querySelector('[name="'+t+'[]"]');n.selectedIndex=-1;for(let e of o)n.options[e].selected=!0},DocumentService.ready().then((()=>{this.controlElement=document.querySelector(e),null!==this.controlElement&&this.controlElement.addEventListener("click",this.registerClickHandler)}))}}export default ResetSelection; \ No newline at end of file +import DocumentService from"@typo3/core/document-service.js";class ResetSelection{constructor(e){this.controlElement=null,this.registerClickHandler=e=>{e.preventDefault();const t=this.controlElement.dataset.itemName,o=JSON.parse(this.controlElement.dataset.selectedIndices),n=document.forms.namedItem("editform").querySelector('[name="'+t+'[]"]');n.selectedIndex=-1;for(const e of o)n.options[e].selected=!0},DocumentService.ready().then((()=>{this.controlElement=document.querySelector(e),null!==this.controlElement&&this.controlElement.addEventListener("click",this.registerClickHandler)}))}}export default ResetSelection; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/request-update.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/request-update.js index d1ae5c0b623e..f2c146772f13 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/request-update.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine/request-update.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -var UpdateMode,__decorate=function(e,t,o,r){var n,d=arguments.length,c=d<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,o):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)c=Reflect.decorate(e,t,o,r);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(c=(d<3?n(c):d>3?n(t,o,c):n(t,o))||c);return d>3&&c&&Object.defineProperty(t,o,c),c};import{LitElement}from"lit";import{customElement,property}from"lit/decorators.js";import FormEngine from"@typo3/backend/form-engine.js";!function(e){e.ask="ask",e.enforce="enforce"}(UpdateMode||(UpdateMode={}));const selectorConverter={fromAttribute:e=>document.querySelectorAll(e)};let RequestUpdate=class extends LitElement{constructor(){super(...arguments),this.mode=UpdateMode.ask,this.requestFormEngineUpdate=()=>{const e=this.mode===UpdateMode.ask;FormEngine.requestFormEngineUpdate(e)}}connectedCallback(){super.connectedCallback();for(let e of this.fields)e.addEventListener("change",this.requestFormEngineUpdate)}disconnectedCallback(){super.disconnectedCallback();for(let e of this.fields)e.removeEventListener("change",this.requestFormEngineUpdate)}};__decorate([property({type:String,attribute:"mode"})],RequestUpdate.prototype,"mode",void 0),__decorate([property({attribute:"field",converter:selectorConverter})],RequestUpdate.prototype,"fields",void 0),RequestUpdate=__decorate([customElement("typo3-formengine-updater")],RequestUpdate); \ No newline at end of file +var UpdateMode,__decorate=function(e,t,o,r){var n,d=arguments.length,c=d<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,o):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)c=Reflect.decorate(e,t,o,r);else for(var s=e.length-1;s>=0;s--)(n=e[s])&&(c=(d<3?n(c):d>3?n(t,o,c):n(t,o))||c);return d>3&&c&&Object.defineProperty(t,o,c),c};import{LitElement}from"lit";import{customElement,property}from"lit/decorators.js";import FormEngine from"@typo3/backend/form-engine.js";!function(e){e.ask="ask",e.enforce="enforce"}(UpdateMode||(UpdateMode={}));const selectorConverter={fromAttribute:e=>document.querySelectorAll(e)};let RequestUpdate=class extends LitElement{constructor(){super(...arguments),this.mode=UpdateMode.ask,this.requestFormEngineUpdate=()=>{const e=this.mode===UpdateMode.ask;FormEngine.requestFormEngineUpdate(e)}}connectedCallback(){super.connectedCallback();for(const e of this.fields)e.addEventListener("change",this.requestFormEngineUpdate)}disconnectedCallback(){super.disconnectedCallback();for(const e of this.fields)e.removeEventListener("change",this.requestFormEngineUpdate)}};__decorate([property({type:String,attribute:"mode"})],RequestUpdate.prototype,"mode",void 0),__decorate([property({attribute:"field",converter:selectorConverter})],RequestUpdate.prototype,"fields",void 0),RequestUpdate=__decorate([customElement("typo3-formengine-updater")],RequestUpdate);export{RequestUpdate}; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/grid-editor.js b/typo3/sysext/backend/Resources/Public/JavaScript/grid-editor.js index e2e397833e9d..e7d022c45664 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/grid-editor.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/grid-editor.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{SeverityEnum}from"@typo3/backend/enum/severity.js";import"bootstrap";import $ from"jquery";import{default as Modal}from"@typo3/backend/modal.js";import SecurityUtility from"@typo3/core/security-utility.js";import Icons from"@typo3/backend/icons.js";export class GridEditor{constructor(t=null){this.colCount=1,this.rowCount=1,this.readOnly=!1,this.nameLabel="name",this.columnLabel="column label",this.defaultCell={spanned:0,rowspan:1,colspan:1,name:"",colpos:"",column:void 0},this.selectorEditor=".t3js-grideditor",this.selectorAddColumn=".t3js-grideditor-addcolumn",this.selectorRemoveColumn=".t3js-grideditor-removecolumn",this.selectorAddRowTop=".t3js-grideditor-addrow-top",this.selectorRemoveRowTop=".t3js-grideditor-removerow-top",this.selectorAddRowBottom=".t3js-grideditor-addrow-bottom",this.selectorRemoveRowBottom=".t3js-grideditor-removerow-bottom",this.selectorLinkEditor=".t3js-grideditor-link-editor",this.selectorLinkExpandRight=".t3js-grideditor-link-expand-right",this.selectorLinkShrinkLeft=".t3js-grideditor-link-shrink-left",this.selectorLinkExpandDown=".t3js-grideditor-link-expand-down",this.selectorLinkShrinkUp=".t3js-grideditor-link-shrink-up",this.selectorConfigPreview=".t3js-grideditor-preview-config",this.selectorPreviewArea=".t3js-tsconfig-preview-area",this.selectorCodeMirror=".t3js-grideditor-preview-config .CodeMirror",this.modalButtonClickHandler=t=>{const e=t.target,o=t.currentTarget;"cancel"===e.name?o.hideModal():"ok"===e.name&&(this.setName(o.querySelector(".t3js-grideditor-field-name").value,o.userData.col,o.userData.row),this.setColumn(parseInt(o.querySelector(".t3js-grideditor-field-colpos").value,10),o.userData.col,o.userData.row),this.drawTable(),this.writeConfig(this.export2LayoutRecord()),o.hideModal())},this.addColumnHandler=t=>{t.preventDefault(),this.addColumn(),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.removeColumnHandler=t=>{t.preventDefault(),this.removeColumn(),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.addRowTopHandler=t=>{t.preventDefault(),this.addRowTop(),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.addRowBottomHandler=t=>{t.preventDefault(),this.addRowBottom(),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.removeRowTopHandler=t=>{t.preventDefault(),this.removeRowTop(),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.removeRowBottomHandler=t=>{t.preventDefault(),this.removeRowBottom(),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.linkEditorHandler=t=>{t.preventDefault();const e=$(t.currentTarget);this.showOptions(e.data("col"),e.data("row"))},this.linkExpandRightHandler=t=>{t.preventDefault();const e=$(t.currentTarget);this.addColspan(e.data("col"),e.data("row")),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.linkShrinkLeftHandler=t=>{t.preventDefault();const e=$(t.currentTarget);this.removeColspan(e.data("col"),e.data("row")),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.linkExpandDownHandler=t=>{t.preventDefault();const e=$(t.currentTarget);this.addRowspan(e.data("col"),e.data("row")),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.linkShrinkUpHandler=t=>{t.preventDefault();const e=$(t.currentTarget);this.removeRowspan(e.data("col"),e.data("row")),this.drawTable(),this.writeConfig(this.export2LayoutRecord())};const e=$(this.selectorEditor);this.colCount=e.data("colcount"),this.rowCount=e.data("rowcount"),this.readOnly=e.data("readonly"),this.field=$('input[name="'+e.data("field")+'"]'),this.data=e.data("data"),this.nameLabel=null!==t?t.nameLabel:"Name",this.columnLabel=null!==t?t.columnLabel:"Column",this.targetElement=$(this.selectorEditor),this.initializeEvents(),this.addVisibilityObserver(e.get(0)),this.drawTable(),this.writeConfig(this.export2LayoutRecord())}static stripMarkup(t){return(new SecurityUtility).stripHtml(t)}initializeEvents(){this.readOnly||($(document).on("click",this.selectorAddColumn,this.addColumnHandler),$(document).on("click",this.selectorRemoveColumn,this.removeColumnHandler),$(document).on("click",this.selectorAddRowTop,this.addRowTopHandler),$(document).on("click",this.selectorAddRowBottom,this.addRowBottomHandler),$(document).on("click",this.selectorRemoveRowTop,this.removeRowTopHandler),$(document).on("click",this.selectorRemoveRowBottom,this.removeRowBottomHandler),$(document).on("click",this.selectorLinkEditor,this.linkEditorHandler),$(document).on("click",this.selectorLinkExpandRight,this.linkExpandRightHandler),$(document).on("click",this.selectorLinkShrinkLeft,this.linkShrinkLeftHandler),$(document).on("click",this.selectorLinkExpandDown,this.linkExpandDownHandler),$(document).on("click",this.selectorLinkShrinkUp,this.linkShrinkUpHandler))}getNewCell(){return $.extend({},this.defaultCell)}writeConfig(t){this.field.val(t);const e=t.split("\n");let o="";for(const t of e)t&&(o+="\t\t\t"+t+"\n");let n="mod.web_layout.BackendLayouts {\n exampleKey {\n title = Example\n icon = EXT:example_extension/Resources/Public/Images/BackendLayouts/default.gif\n config {\n"+o.replace(new RegExp("\t","g")," ")+" }\n }\n}\n";$(this.selectorConfigPreview).find(this.selectorPreviewArea).empty().append(n);const i=document.querySelector(this.selectorCodeMirror);i&&i.CodeMirror.setValue(n)}addRowTop(){const t=[];for(let e=0;e<this.colCount;e++){const o=this.getNewCell();o.name=e+"x"+this.data.length,t[e]=o}this.data.unshift(t),this.rowCount++}addRowBottom(){const t=[];for(let e=0;e<this.colCount;e++){const o=this.getNewCell();o.name=e+"x"+this.data.length,t[e]=o}this.data.push(t),this.rowCount++}removeRowTop(){if(this.rowCount<=1)return!1;const t=[];for(let e=1;e<this.rowCount;e++)t.push(this.data[e]);for(let t=0;t<this.colCount;t++)1===this.data[0][t].spanned&&this.findUpperCellWidthRowspanAndDecreaseByOne(t,0);return this.data=t,this.rowCount--,!0}removeRowBottom(){if(this.rowCount<=1)return!1;const t=[];for(let e=0;e<this.rowCount-1;e++)t.push(this.data[e]);for(let t=0;t<this.colCount;t++)1===this.data[this.rowCount-1][t].spanned&&this.findUpperCellWidthRowspanAndDecreaseByOne(t,this.rowCount-1);return this.data=t,this.rowCount--,!0}findUpperCellWidthRowspanAndDecreaseByOne(t,e){const o=this.getCell(t,e-1);return!!o&&(1===o.spanned?this.findUpperCellWidthRowspanAndDecreaseByOne(t,e-1):o.rowspan>1&&this.removeRowspan(t,e-1),!0)}removeColumn(){if(this.colCount<=1)return!1;const t=[];for(let e=0;e<this.rowCount;e++){const o=[];for(let t=0;t<this.colCount-1;t++)o.push(this.data[e][t]);1===this.data[e][this.colCount-1].spanned&&this.findLeftCellWidthColspanAndDecreaseByOne(this.colCount-1,e),t.push(o)}return this.data=t,this.colCount--,!0}findLeftCellWidthColspanAndDecreaseByOne(t,e){const o=this.getCell(t-1,e);return!!o&&(1===o.spanned?this.findLeftCellWidthColspanAndDecreaseByOne(t-1,e):o.colspan>1&&this.removeColspan(t-1,e),!0)}addColumn(){for(let t=0;t<this.rowCount;t++){const e=this.getNewCell();e.name=this.colCount+"x"+t,this.data[t].push(e)}this.colCount++}drawTable(){const t=$('<div class="grideditor-editor-grid">');for(let e=0;e<this.rowCount;e++){if(0!==this.data[e].length)for(let o=0;o<this.colCount;o++){const n=this.data[e][o];if(1===n.spanned)continue;const i=$('<div class="grideditor-cell">');if(i.css("--grideditor-cell-col",o+1),i.css("--grideditor-cell-colspan",n.colspan),i.css("--grideditor-cell-row",e+1),i.css("--grideditor-cell-rowspan",n.rowspan),!this.readOnly){const t=$('<div class="grideditor-cell-actions">');i.append(t);const n=$('<a href="#" data-col="'+o+'" data-row="'+e+'">');Icons.getIcon("actions-open",Icons.sizes.small).then((e=>{t.append(n.clone().attr("class","t3js-grideditor-link-editor grideditor-action grideditor-action-edit").attr("title",TYPO3.lang.grid_editCell).append(e))})),this.cellCanSpanRight(o,e)&&Icons.getIcon("actions-caret-right",Icons.sizes.small).then((e=>{t.append(n.clone().attr("class","t3js-grideditor-link-expand-right grideditor-action grideditor-action-expand-right").attr("title",TYPO3.lang.grid_editCell).append(e))})),this.cellCanShrinkLeft(o,e)&&Icons.getIcon("actions-caret-left",Icons.sizes.small).then((e=>{t.append(n.clone().attr("class","t3js-grideditor-link-shrink-left grideditor-action grideditor-action-shrink-left").attr("title",TYPO3.lang.grid_editCell).append(e))})),this.cellCanSpanDown(o,e)&&Icons.getIcon("actions-caret-down",Icons.sizes.small).then((e=>{t.append(n.clone().attr("class","t3js-grideditor-link-expand-down grideditor-action grideditor-action-expand-down").attr("title",TYPO3.lang.grid_editCell).append(e))})),this.cellCanShrinkUp(o,e)&&Icons.getIcon("actions-caret-up",Icons.sizes.small).then((e=>{t.append(n.clone().attr("class","t3js-grideditor-link-shrink-up grideditor-action grideditor-action-shrink-up").attr("title",TYPO3.lang.grid_editCell).append(e))}))}i.append($('<div class="grideditor-cell-info">').html("<strong>"+TYPO3.lang.grid_name+":</strong> "+(n.name?GridEditor.stripMarkup(n.name):TYPO3.lang.grid_notSet)+"<br><strong>"+TYPO3.lang.grid_column+":</strong> "+(void 0===n.column||isNaN(n.column)?TYPO3.lang.grid_notSet:parseInt(n.column,10)))),t.append(i)}}$(this.targetElement).empty().append(t)}setName(t,e,o){const n=this.getCell(e,o);return!!n&&(n.name=GridEditor.stripMarkup(t),!0)}setColumn(t,e,o){const n=this.getCell(e,o);return!!n&&(n.column=parseInt(t.toString(),10),!0)}showOptions(t,e){const o=this.getCell(t,e);if(!o)return!1;let n;n=0===o.column?0:o.column?parseInt(o.column.toString(),10):"";const i=$("<div>"),r=$('<div class="form-group">'),s=$("<label>"),a=$("<input>");i.append([r.clone().append([s.clone().text(TYPO3.lang.grid_nameHelp),a.clone().attr("type","text").attr("class","t3js-grideditor-field-name form-control").attr("name","name").val(GridEditor.stripMarkup(o.name)||"")]),r.clone().append([s.clone().text(TYPO3.lang.grid_columnHelp),a.clone().attr("type","text").attr("class","t3js-grideditor-field-colpos form-control").attr("name","column").val(n)])]);const l=Modal.show(TYPO3.lang.grid_windowTitle,i,SeverityEnum.notice,[{active:!0,btnClass:"btn-default",name:"cancel",text:$(this).data("button-close-text")||TYPO3.lang["button.cancel"]||"Cancel"},{btnClass:"btn-primary",name:"ok",text:$(this).data("button-ok-text")||TYPO3.lang["button.ok"]||"OK"}]);return l.userData.col=t,l.userData.row=e,l.addEventListener("button.clicked",this.modalButtonClickHandler),!0}getCell(t,e){return!(t>this.colCount-1)&&(!(e>this.rowCount-1)&&(this.data.length>e-1&&this.data[e].length>t-1?this.data[e][t]:null))}cellCanSpanRight(t,e){if(t===this.colCount-1)return!1;const o=this.getCell(t,e);let n;if(o.rowspan>1){for(let i=e;i<e+o.rowspan;i++)if(n=this.getCell(t+o.colspan,i),!n||1===n.spanned||n.colspan>1||n.rowspan>1)return!1}else if(n=this.getCell(t+o.colspan,e),!n||1===o.spanned||1===n.spanned||n.colspan>1||n.rowspan>1)return!1;return!0}cellCanSpanDown(t,e){if(e===this.rowCount-1)return!1;const o=this.getCell(t,e);let n;if(o.colspan>1){for(let i=t;i<t+o.colspan;i++)if(n=this.getCell(i,e+o.rowspan),!n||1===n.spanned||n.colspan>1||n.rowspan>1)return!1}else if(n=this.getCell(t,e+o.rowspan),!n||1===o.spanned||1===n.spanned||n.colspan>1||n.rowspan>1)return!1;return!0}cellCanShrinkLeft(t,e){return this.data[e][t].colspan>1}cellCanShrinkUp(t,e){return this.data[e][t].rowspan>1}addColspan(t,e){const o=this.getCell(t,e);if(!o||!this.cellCanSpanRight(t,e))return!1;for(let n=e;n<e+o.rowspan;n++)this.data[n][t+o.colspan].spanned=1;return o.colspan+=1,!0}addRowspan(t,e){const o=this.getCell(t,e);if(!o||!this.cellCanSpanDown(t,e))return!1;for(let n=t;n<t+o.colspan;n++)this.data[e+o.rowspan][n].spanned=1;return o.rowspan+=1,!0}removeColspan(t,e){const o=this.getCell(t,e);if(!o||!this.cellCanShrinkLeft(t,e))return!1;o.colspan-=1;for(let n=e;n<e+o.rowspan;n++)this.data[n][t+o.colspan].spanned=0;return!0}removeRowspan(t,e){const o=this.getCell(t,e);if(!o||!this.cellCanShrinkUp(t,e))return!1;o.rowspan-=1;for(let n=t;n<t+o.colspan;n++)this.data[e+o.rowspan][n].spanned=0;return!0}export2LayoutRecord(){let t="backend_layout {\n\tcolCount = "+this.colCount+"\n\trowCount = "+this.rowCount+"\n\trows {\n";for(let e=0;e<this.rowCount;e++){t+="\t\t"+(e+1)+" {\n",t+="\t\t\tcolumns {\n";let o=0;for(let n=0;n<this.colCount;n++){const i=this.getCell(n,e);if(i&&!i.spanned){const r=GridEditor.stripMarkup(i.name)||"";o++,t+="\t\t\t\t"+o+" {\n",t+="\t\t\t\t\tname = "+(r||n+"x"+e)+"\n",i.colspan>1&&(t+="\t\t\t\t\tcolspan = "+i.colspan+"\n"),i.rowspan>1&&(t+="\t\t\t\t\trowspan = "+i.rowspan+"\n"),"number"==typeof i.column&&(t+="\t\t\t\t\tcolPos = "+i.column+"\n"),t+="\t\t\t\t}\n"}}t+="\t\t\t}\n",t+="\t\t}\n"}return t+="\t}\n}\n",t}addVisibilityObserver(t){null===t.offsetParent&&new IntersectionObserver(((t,e)=>{t.forEach((t=>{const e=document.querySelector(this.selectorCodeMirror);t.intersectionRatio>0&&e&&e.CodeMirror.refresh()}))})).observe(t)}} \ No newline at end of file +import{SeverityEnum}from"@typo3/backend/enum/severity.js";import"bootstrap";import $ from"jquery";import{default as Modal}from"@typo3/backend/modal.js";import SecurityUtility from"@typo3/core/security-utility.js";import Icons from"@typo3/backend/icons.js";export class GridEditor{constructor(t=null){this.colCount=1,this.rowCount=1,this.readOnly=!1,this.nameLabel="name",this.columnLabel="column label",this.defaultCell={spanned:0,rowspan:1,colspan:1,name:"",colpos:"",column:void 0},this.selectorEditor=".t3js-grideditor",this.selectorAddColumn=".t3js-grideditor-addcolumn",this.selectorRemoveColumn=".t3js-grideditor-removecolumn",this.selectorAddRowTop=".t3js-grideditor-addrow-top",this.selectorRemoveRowTop=".t3js-grideditor-removerow-top",this.selectorAddRowBottom=".t3js-grideditor-addrow-bottom",this.selectorRemoveRowBottom=".t3js-grideditor-removerow-bottom",this.selectorLinkEditor=".t3js-grideditor-link-editor",this.selectorLinkExpandRight=".t3js-grideditor-link-expand-right",this.selectorLinkShrinkLeft=".t3js-grideditor-link-shrink-left",this.selectorLinkExpandDown=".t3js-grideditor-link-expand-down",this.selectorLinkShrinkUp=".t3js-grideditor-link-shrink-up",this.selectorConfigPreview=".t3js-grideditor-preview-config",this.selectorPreviewArea=".t3js-tsconfig-preview-area",this.selectorCodeMirror=".t3js-grideditor-preview-config .CodeMirror",this.modalButtonClickHandler=t=>{const e=t.target,o=t.currentTarget;"cancel"===e.name?o.hideModal():"ok"===e.name&&(this.setName(o.querySelector(".t3js-grideditor-field-name").value,o.userData.col,o.userData.row),this.setColumn(parseInt(o.querySelector(".t3js-grideditor-field-colpos").value,10),o.userData.col,o.userData.row),this.drawTable(),this.writeConfig(this.export2LayoutRecord()),o.hideModal())},this.addColumnHandler=t=>{t.preventDefault(),this.addColumn(),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.removeColumnHandler=t=>{t.preventDefault(),this.removeColumn(),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.addRowTopHandler=t=>{t.preventDefault(),this.addRowTop(),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.addRowBottomHandler=t=>{t.preventDefault(),this.addRowBottom(),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.removeRowTopHandler=t=>{t.preventDefault(),this.removeRowTop(),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.removeRowBottomHandler=t=>{t.preventDefault(),this.removeRowBottom(),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.linkEditorHandler=t=>{t.preventDefault();const e=$(t.currentTarget);this.showOptions(e.data("col"),e.data("row"))},this.linkExpandRightHandler=t=>{t.preventDefault();const e=$(t.currentTarget);this.addColspan(e.data("col"),e.data("row")),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.linkShrinkLeftHandler=t=>{t.preventDefault();const e=$(t.currentTarget);this.removeColspan(e.data("col"),e.data("row")),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.linkExpandDownHandler=t=>{t.preventDefault();const e=$(t.currentTarget);this.addRowspan(e.data("col"),e.data("row")),this.drawTable(),this.writeConfig(this.export2LayoutRecord())},this.linkShrinkUpHandler=t=>{t.preventDefault();const e=$(t.currentTarget);this.removeRowspan(e.data("col"),e.data("row")),this.drawTable(),this.writeConfig(this.export2LayoutRecord())};const e=$(this.selectorEditor);this.colCount=e.data("colcount"),this.rowCount=e.data("rowcount"),this.readOnly=e.data("readonly"),this.field=$('input[name="'+e.data("field")+'"]'),this.data=e.data("data"),this.nameLabel=null!==t?t.nameLabel:"Name",this.columnLabel=null!==t?t.columnLabel:"Column",this.targetElement=$(this.selectorEditor),this.initializeEvents(),this.addVisibilityObserver(e.get(0)),this.drawTable(),this.writeConfig(this.export2LayoutRecord())}static stripMarkup(t){return(new SecurityUtility).stripHtml(t)}initializeEvents(){this.readOnly||($(document).on("click",this.selectorAddColumn,this.addColumnHandler),$(document).on("click",this.selectorRemoveColumn,this.removeColumnHandler),$(document).on("click",this.selectorAddRowTop,this.addRowTopHandler),$(document).on("click",this.selectorAddRowBottom,this.addRowBottomHandler),$(document).on("click",this.selectorRemoveRowTop,this.removeRowTopHandler),$(document).on("click",this.selectorRemoveRowBottom,this.removeRowBottomHandler),$(document).on("click",this.selectorLinkEditor,this.linkEditorHandler),$(document).on("click",this.selectorLinkExpandRight,this.linkExpandRightHandler),$(document).on("click",this.selectorLinkShrinkLeft,this.linkShrinkLeftHandler),$(document).on("click",this.selectorLinkExpandDown,this.linkExpandDownHandler),$(document).on("click",this.selectorLinkShrinkUp,this.linkShrinkUpHandler))}getNewCell(){return $.extend({},this.defaultCell)}writeConfig(t){this.field.val(t);const e=t.split("\n");let o="";for(const t of e)t&&(o+="\t\t\t"+t+"\n");const n="mod.web_layout.BackendLayouts {\n exampleKey {\n title = Example\n icon = EXT:example_extension/Resources/Public/Images/BackendLayouts/default.gif\n config {\n"+o.replace(new RegExp("\\t","g")," ")+" }\n }\n}\n";$(this.selectorConfigPreview).find(this.selectorPreviewArea).empty().append(n);const i=document.querySelector(this.selectorCodeMirror);i&&i.CodeMirror.setValue(n)}addRowTop(){const t=[];for(let e=0;e<this.colCount;e++){const o=this.getNewCell();o.name=e+"x"+this.data.length,t[e]=o}this.data.unshift(t),this.rowCount++}addRowBottom(){const t=[];for(let e=0;e<this.colCount;e++){const o=this.getNewCell();o.name=e+"x"+this.data.length,t[e]=o}this.data.push(t),this.rowCount++}removeRowTop(){if(this.rowCount<=1)return!1;const t=[];for(let e=1;e<this.rowCount;e++)t.push(this.data[e]);for(let t=0;t<this.colCount;t++)1===this.data[0][t].spanned&&this.findUpperCellWidthRowspanAndDecreaseByOne(t,0);return this.data=t,this.rowCount--,!0}removeRowBottom(){if(this.rowCount<=1)return!1;const t=[];for(let e=0;e<this.rowCount-1;e++)t.push(this.data[e]);for(let t=0;t<this.colCount;t++)1===this.data[this.rowCount-1][t].spanned&&this.findUpperCellWidthRowspanAndDecreaseByOne(t,this.rowCount-1);return this.data=t,this.rowCount--,!0}findUpperCellWidthRowspanAndDecreaseByOne(t,e){const o=this.getCell(t,e-1);return!!o&&(1===o.spanned?this.findUpperCellWidthRowspanAndDecreaseByOne(t,e-1):o.rowspan>1&&this.removeRowspan(t,e-1),!0)}removeColumn(){if(this.colCount<=1)return!1;const t=[];for(let e=0;e<this.rowCount;e++){const o=[];for(let t=0;t<this.colCount-1;t++)o.push(this.data[e][t]);1===this.data[e][this.colCount-1].spanned&&this.findLeftCellWidthColspanAndDecreaseByOne(this.colCount-1,e),t.push(o)}return this.data=t,this.colCount--,!0}findLeftCellWidthColspanAndDecreaseByOne(t,e){const o=this.getCell(t-1,e);return!!o&&(1===o.spanned?this.findLeftCellWidthColspanAndDecreaseByOne(t-1,e):o.colspan>1&&this.removeColspan(t-1,e),!0)}addColumn(){for(let t=0;t<this.rowCount;t++){const e=this.getNewCell();e.name=this.colCount+"x"+t,this.data[t].push(e)}this.colCount++}drawTable(){const t=$('<div class="grideditor-editor-grid">');for(let e=0;e<this.rowCount;e++){if(0!==this.data[e].length)for(let o=0;o<this.colCount;o++){const n=this.data[e][o];if(1===n.spanned)continue;const i=$('<div class="grideditor-cell">');if(i.css("--grideditor-cell-col",o+1),i.css("--grideditor-cell-colspan",n.colspan),i.css("--grideditor-cell-row",e+1),i.css("--grideditor-cell-rowspan",n.rowspan),!this.readOnly){const t=$('<div class="grideditor-cell-actions">');i.append(t);const n=$('<a href="#" data-col="'+o+'" data-row="'+e+'">');Icons.getIcon("actions-open",Icons.sizes.small).then((e=>{t.append(n.clone().attr("class","t3js-grideditor-link-editor grideditor-action grideditor-action-edit").attr("title",TYPO3.lang.grid_editCell).append(e))})),this.cellCanSpanRight(o,e)&&Icons.getIcon("actions-caret-right",Icons.sizes.small).then((e=>{t.append(n.clone().attr("class","t3js-grideditor-link-expand-right grideditor-action grideditor-action-expand-right").attr("title",TYPO3.lang.grid_editCell).append(e))})),this.cellCanShrinkLeft(o,e)&&Icons.getIcon("actions-caret-left",Icons.sizes.small).then((e=>{t.append(n.clone().attr("class","t3js-grideditor-link-shrink-left grideditor-action grideditor-action-shrink-left").attr("title",TYPO3.lang.grid_editCell).append(e))})),this.cellCanSpanDown(o,e)&&Icons.getIcon("actions-caret-down",Icons.sizes.small).then((e=>{t.append(n.clone().attr("class","t3js-grideditor-link-expand-down grideditor-action grideditor-action-expand-down").attr("title",TYPO3.lang.grid_editCell).append(e))})),this.cellCanShrinkUp(o,e)&&Icons.getIcon("actions-caret-up",Icons.sizes.small).then((e=>{t.append(n.clone().attr("class","t3js-grideditor-link-shrink-up grideditor-action grideditor-action-shrink-up").attr("title",TYPO3.lang.grid_editCell).append(e))}))}i.append($('<div class="grideditor-cell-info">').html("<strong>"+TYPO3.lang.grid_name+":</strong> "+(n.name?GridEditor.stripMarkup(n.name):TYPO3.lang.grid_notSet)+"<br><strong>"+TYPO3.lang.grid_column+":</strong> "+(void 0===n.column||isNaN(n.column)?TYPO3.lang.grid_notSet:parseInt(n.column,10)))),t.append(i)}}$(this.targetElement).empty().append(t)}setName(t,e,o){const n=this.getCell(e,o);return!!n&&(n.name=GridEditor.stripMarkup(t),!0)}setColumn(t,e,o){const n=this.getCell(e,o);return!!n&&(n.column=parseInt(t.toString(),10),!0)}showOptions(t,e){const o=this.getCell(t,e);if(!o)return!1;let n;n=0===o.column?0:o.column?parseInt(o.column.toString(),10):"";const i=$("<div>"),r=$('<div class="form-group">'),s=$("<label>"),a=$("<input>");i.append([r.clone().append([s.clone().text(TYPO3.lang.grid_nameHelp),a.clone().attr("type","text").attr("class","t3js-grideditor-field-name form-control").attr("name","name").val(GridEditor.stripMarkup(o.name)||"")]),r.clone().append([s.clone().text(TYPO3.lang.grid_columnHelp),a.clone().attr("type","text").attr("class","t3js-grideditor-field-colpos form-control").attr("name","column").val(n)])]);const l=Modal.show(TYPO3.lang.grid_windowTitle,i,SeverityEnum.notice,[{active:!0,btnClass:"btn-default",name:"cancel",text:$(this).data("button-close-text")||TYPO3.lang["button.cancel"]||"Cancel"},{btnClass:"btn-primary",name:"ok",text:$(this).data("button-ok-text")||TYPO3.lang["button.ok"]||"OK"}]);return l.userData.col=t,l.userData.row=e,l.addEventListener("button.clicked",this.modalButtonClickHandler),!0}getCell(t,e){return!(t>this.colCount-1)&&(!(e>this.rowCount-1)&&(this.data.length>e-1&&this.data[e].length>t-1?this.data[e][t]:null))}cellCanSpanRight(t,e){if(t===this.colCount-1)return!1;const o=this.getCell(t,e);if(!o)return!1;let n;if(o.rowspan>1){for(let i=e;i<e+o.rowspan;i++)if(n=this.getCell(t+o.colspan,i),!n||1===n.spanned||n.colspan>1||n.rowspan>1)return!1}else if(n=this.getCell(t+o.colspan,e),!n||1===o.spanned||1===n.spanned||n.colspan>1||n.rowspan>1)return!1;return!0}cellCanSpanDown(t,e){if(e===this.rowCount-1)return!1;const o=this.getCell(t,e);if(!o)return!1;let n;if(o.colspan>1){for(let i=t;i<t+o.colspan;i++)if(n=this.getCell(i,e+o.rowspan),!n||1===n.spanned||n.colspan>1||n.rowspan>1)return!1}else if(n=this.getCell(t,e+o.rowspan),!n||1===o.spanned||1===n.spanned||n.colspan>1||n.rowspan>1)return!1;return!0}cellCanShrinkLeft(t,e){return this.data[e][t].colspan>1}cellCanShrinkUp(t,e){return this.data[e][t].rowspan>1}addColspan(t,e){const o=this.getCell(t,e);if(!o||!this.cellCanSpanRight(t,e))return!1;for(let n=e;n<e+o.rowspan;n++)this.data[n][t+o.colspan].spanned=1;return o.colspan+=1,!0}addRowspan(t,e){const o=this.getCell(t,e);if(!o||!this.cellCanSpanDown(t,e))return!1;for(let n=t;n<t+o.colspan;n++)this.data[e+o.rowspan][n].spanned=1;return o.rowspan+=1,!0}removeColspan(t,e){const o=this.getCell(t,e);if(!o||!this.cellCanShrinkLeft(t,e))return!1;o.colspan-=1;for(let n=e;n<e+o.rowspan;n++)this.data[n][t+o.colspan].spanned=0;return!0}removeRowspan(t,e){const o=this.getCell(t,e);if(!o||!this.cellCanShrinkUp(t,e))return!1;o.rowspan-=1;for(let n=t;n<t+o.colspan;n++)this.data[e+o.rowspan][n].spanned=0;return!0}export2LayoutRecord(){let t="backend_layout {\n\tcolCount = "+this.colCount+"\n\trowCount = "+this.rowCount+"\n\trows {\n";for(let e=0;e<this.rowCount;e++){t+="\t\t"+(e+1)+" {\n",t+="\t\t\tcolumns {\n";let o=0;for(let n=0;n<this.colCount;n++){const i=this.getCell(n,e);if(i&&!i.spanned){const r=GridEditor.stripMarkup(i.name)||"";o++,t+="\t\t\t\t"+o+" {\n",t+="\t\t\t\t\tname = "+(r||n+"x"+e)+"\n",i.colspan>1&&(t+="\t\t\t\t\tcolspan = "+i.colspan+"\n"),i.rowspan>1&&(t+="\t\t\t\t\trowspan = "+i.rowspan+"\n"),"number"==typeof i.column&&(t+="\t\t\t\t\tcolPos = "+i.column+"\n"),t+="\t\t\t\t}\n"}}t+="\t\t\t}\n",t+="\t\t}\n"}return t+="\t}\n}\n",t}addVisibilityObserver(t){null===t.offsetParent&&new IntersectionObserver((t=>{t.forEach((t=>{const e=document.querySelector(this.selectorCodeMirror);t.intersectionRatio>0&&e&&e.CodeMirror.refresh()}))})).observe(t)}} \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/hashing/md5.js b/typo3/sysext/backend/Resources/Public/JavaScript/hashing/md5.js index 62b016ddb4af..3f84b1f90744 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/hashing/md5.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/hashing/md5.js @@ -11,4 +11,4 @@ * The TYPO3 project - inspiring people to share! */ /*! Based on http://www.webtoolkit.info/javascript_md5.html */ -class Md5{static hash(d){let M,t,e,r,n,a,o,i,s,H;for(d=Md5.utf8Encode(d),M=Md5.convertToWordArray(d),o=1732584193,i=4023233417,s=2562383102,H=271733878,t=0;t<M.length;t+=16)e=o,r=i,n=s,a=H,o=Md5.FF(o,i,s,H,M[t],7,3614090360),H=Md5.FF(H,o,i,s,M[t+1],12,3905402710),s=Md5.FF(s,H,o,i,M[t+2],17,606105819),i=Md5.FF(i,s,H,o,M[t+3],22,3250441966),o=Md5.FF(o,i,s,H,M[t+4],7,4118548399),H=Md5.FF(H,o,i,s,M[t+5],12,1200080426),s=Md5.FF(s,H,o,i,M[t+6],17,2821735955),i=Md5.FF(i,s,H,o,M[t+7],22,4249261313),o=Md5.FF(o,i,s,H,M[t+8],7,1770035416),H=Md5.FF(H,o,i,s,M[t+9],12,2336552879),s=Md5.FF(s,H,o,i,M[t+10],17,4294925233),i=Md5.FF(i,s,H,o,M[t+11],22,2304563134),o=Md5.FF(o,i,s,H,M[t+12],7,1804603682),H=Md5.FF(H,o,i,s,M[t+13],12,4254626195),s=Md5.FF(s,H,o,i,M[t+14],17,2792965006),i=Md5.FF(i,s,H,o,M[t+15],22,1236535329),o=Md5.GG(o,i,s,H,M[t+1],5,4129170786),H=Md5.GG(H,o,i,s,M[t+6],9,3225465664),s=Md5.GG(s,H,o,i,M[t+11],14,643717713),i=Md5.GG(i,s,H,o,M[t],20,3921069994),o=Md5.GG(o,i,s,H,M[t+5],5,3593408605),H=Md5.GG(H,o,i,s,M[t+10],9,38016083),s=Md5.GG(s,H,o,i,M[t+15],14,3634488961),i=Md5.GG(i,s,H,o,M[t+4],20,3889429448),o=Md5.GG(o,i,s,H,M[t+9],5,568446438),H=Md5.GG(H,o,i,s,M[t+14],9,3275163606),s=Md5.GG(s,H,o,i,M[t+3],14,4107603335),i=Md5.GG(i,s,H,o,M[t+8],20,1163531501),o=Md5.GG(o,i,s,H,M[t+13],5,2850285829),H=Md5.GG(H,o,i,s,M[t+2],9,4243563512),s=Md5.GG(s,H,o,i,M[t+7],14,1735328473),i=Md5.GG(i,s,H,o,M[t+12],20,2368359562),o=Md5.HH(o,i,s,H,M[t+5],4,4294588738),H=Md5.HH(H,o,i,s,M[t+8],11,2272392833),s=Md5.HH(s,H,o,i,M[t+11],16,1839030562),i=Md5.HH(i,s,H,o,M[t+14],23,4259657740),o=Md5.HH(o,i,s,H,M[t+1],4,2763975236),H=Md5.HH(H,o,i,s,M[t+4],11,1272893353),s=Md5.HH(s,H,o,i,M[t+7],16,4139469664),i=Md5.HH(i,s,H,o,M[t+10],23,3200236656),o=Md5.HH(o,i,s,H,M[t+13],4,681279174),H=Md5.HH(H,o,i,s,M[t],11,3936430074),s=Md5.HH(s,H,o,i,M[t+3],16,3572445317),i=Md5.HH(i,s,H,o,M[t+6],23,76029189),o=Md5.HH(o,i,s,H,M[t+9],4,3654602809),H=Md5.HH(H,o,i,s,M[t+12],11,3873151461),s=Md5.HH(s,H,o,i,M[t+15],16,530742520),i=Md5.HH(i,s,H,o,M[t+2],23,3299628645),o=Md5.II(o,i,s,H,M[t],6,4096336452),H=Md5.II(H,o,i,s,M[t+7],10,1126891415),s=Md5.II(s,H,o,i,M[t+14],15,2878612391),i=Md5.II(i,s,H,o,M[t+5],21,4237533241),o=Md5.II(o,i,s,H,M[t+12],6,1700485571),H=Md5.II(H,o,i,s,M[t+3],10,2399980690),s=Md5.II(s,H,o,i,M[t+10],15,4293915773),i=Md5.II(i,s,H,o,M[t+1],21,2240044497),o=Md5.II(o,i,s,H,M[t+8],6,1873313359),H=Md5.II(H,o,i,s,M[t+15],10,4264355552),s=Md5.II(s,H,o,i,M[t+6],15,2734768916),i=Md5.II(i,s,H,o,M[t+13],21,1309151649),o=Md5.II(o,i,s,H,M[t+4],6,4149444226),H=Md5.II(H,o,i,s,M[t+11],10,3174756917),s=Md5.II(s,H,o,i,M[t+2],15,718787259),i=Md5.II(i,s,H,o,M[t+9],21,3951481745),o=Md5.addUnsigned(o,e),i=Md5.addUnsigned(i,r),s=Md5.addUnsigned(s,n),H=Md5.addUnsigned(H,a);return(Md5.wordToHex(o)+Md5.wordToHex(i)+Md5.wordToHex(s)+Md5.wordToHex(H)).toLowerCase()}static rotateLeft(d,M){return d<<M|d>>>32-M}static addUnsigned(d,M){let t=2147483648&d,e=2147483648&M,r=1073741824&d,n=1073741824&M,a=(1073741823&d)+(1073741823&M);return r&n?2147483648^a^t^e:r|n?1073741824&a?3221225472^a^t^e:1073741824^a^t^e:a^t^e}static F(d,M,t){return d&M|~d&t}static G(d,M,t){return d&t|M&~t}static H(d,M,t){return d^M^t}static I(d,M,t){return M^(d|~t)}static FF(d,M,t,e,r,n,a){return d=Md5.addUnsigned(d,Md5.addUnsigned(Md5.addUnsigned(Md5.F(M,t,e),r),a)),Md5.addUnsigned(Md5.rotateLeft(d,n),M)}static GG(d,M,t,e,r,n,a){return d=Md5.addUnsigned(d,Md5.addUnsigned(Md5.addUnsigned(Md5.G(M,t,e),r),a)),Md5.addUnsigned(Md5.rotateLeft(d,n),M)}static HH(d,M,t,e,r,n,a){return d=Md5.addUnsigned(d,Md5.addUnsigned(Md5.addUnsigned(Md5.H(M,t,e),r),a)),Md5.addUnsigned(Md5.rotateLeft(d,n),M)}static II(d,M,t,e,r,n,a){return d=Md5.addUnsigned(d,Md5.addUnsigned(Md5.addUnsigned(Md5.I(M,t,e),r),a)),Md5.addUnsigned(Md5.rotateLeft(d,n),M)}static convertToWordArray(d){let M,t=d.length,e=t+8,r=16*((e-e%64)/64+1),n=Array(r-1),a=0,o=0;for(;o<t;)M=(o-o%4)/4,a=o%4*8,n[M]=n[M]|d.charCodeAt(o)<<a,o++;return M=(o-o%4)/4,a=o%4*8,n[M]=n[M]|128<<a,n[r-2]=t<<3,n[r-1]=t>>>29,n}static wordToHex(d){let M,t,e="",r="";for(t=0;t<=3;t++)M=d>>>8*t&255,r="0"+M.toString(16),e+=r.substr(r.length-2,2);return e}static utf8Encode(d){d=d.replace(/\r\n/g,"\n");let M="";for(let t=0;t<d.length;t++){let e=d.charCodeAt(t);e<128?M+=String.fromCharCode(e):e>127&&e<2048?(M+=String.fromCharCode(e>>6|192),M+=String.fromCharCode(63&e|128)):(M+=String.fromCharCode(e>>12|224),M+=String.fromCharCode(e>>6&63|128),M+=String.fromCharCode(63&e|128))}return M}}export default Md5; \ No newline at end of file +class Md5{static hash(d){let M,t,e,r,n,a,o,s,i;d=Md5.utf8Encode(d);const H=Md5.convertToWordArray(d);for(a=1732584193,o=4023233417,s=2562383102,i=271733878,M=0;M<H.length;M+=16)t=a,e=o,r=s,n=i,a=Md5.FF(a,o,s,i,H[M],7,3614090360),i=Md5.FF(i,a,o,s,H[M+1],12,3905402710),s=Md5.FF(s,i,a,o,H[M+2],17,606105819),o=Md5.FF(o,s,i,a,H[M+3],22,3250441966),a=Md5.FF(a,o,s,i,H[M+4],7,4118548399),i=Md5.FF(i,a,o,s,H[M+5],12,1200080426),s=Md5.FF(s,i,a,o,H[M+6],17,2821735955),o=Md5.FF(o,s,i,a,H[M+7],22,4249261313),a=Md5.FF(a,o,s,i,H[M+8],7,1770035416),i=Md5.FF(i,a,o,s,H[M+9],12,2336552879),s=Md5.FF(s,i,a,o,H[M+10],17,4294925233),o=Md5.FF(o,s,i,a,H[M+11],22,2304563134),a=Md5.FF(a,o,s,i,H[M+12],7,1804603682),i=Md5.FF(i,a,o,s,H[M+13],12,4254626195),s=Md5.FF(s,i,a,o,H[M+14],17,2792965006),o=Md5.FF(o,s,i,a,H[M+15],22,1236535329),a=Md5.GG(a,o,s,i,H[M+1],5,4129170786),i=Md5.GG(i,a,o,s,H[M+6],9,3225465664),s=Md5.GG(s,i,a,o,H[M+11],14,643717713),o=Md5.GG(o,s,i,a,H[M],20,3921069994),a=Md5.GG(a,o,s,i,H[M+5],5,3593408605),i=Md5.GG(i,a,o,s,H[M+10],9,38016083),s=Md5.GG(s,i,a,o,H[M+15],14,3634488961),o=Md5.GG(o,s,i,a,H[M+4],20,3889429448),a=Md5.GG(a,o,s,i,H[M+9],5,568446438),i=Md5.GG(i,a,o,s,H[M+14],9,3275163606),s=Md5.GG(s,i,a,o,H[M+3],14,4107603335),o=Md5.GG(o,s,i,a,H[M+8],20,1163531501),a=Md5.GG(a,o,s,i,H[M+13],5,2850285829),i=Md5.GG(i,a,o,s,H[M+2],9,4243563512),s=Md5.GG(s,i,a,o,H[M+7],14,1735328473),o=Md5.GG(o,s,i,a,H[M+12],20,2368359562),a=Md5.HH(a,o,s,i,H[M+5],4,4294588738),i=Md5.HH(i,a,o,s,H[M+8],11,2272392833),s=Md5.HH(s,i,a,o,H[M+11],16,1839030562),o=Md5.HH(o,s,i,a,H[M+14],23,4259657740),a=Md5.HH(a,o,s,i,H[M+1],4,2763975236),i=Md5.HH(i,a,o,s,H[M+4],11,1272893353),s=Md5.HH(s,i,a,o,H[M+7],16,4139469664),o=Md5.HH(o,s,i,a,H[M+10],23,3200236656),a=Md5.HH(a,o,s,i,H[M+13],4,681279174),i=Md5.HH(i,a,o,s,H[M],11,3936430074),s=Md5.HH(s,i,a,o,H[M+3],16,3572445317),o=Md5.HH(o,s,i,a,H[M+6],23,76029189),a=Md5.HH(a,o,s,i,H[M+9],4,3654602809),i=Md5.HH(i,a,o,s,H[M+12],11,3873151461),s=Md5.HH(s,i,a,o,H[M+15],16,530742520),o=Md5.HH(o,s,i,a,H[M+2],23,3299628645),a=Md5.II(a,o,s,i,H[M],6,4096336452),i=Md5.II(i,a,o,s,H[M+7],10,1126891415),s=Md5.II(s,i,a,o,H[M+14],15,2878612391),o=Md5.II(o,s,i,a,H[M+5],21,4237533241),a=Md5.II(a,o,s,i,H[M+12],6,1700485571),i=Md5.II(i,a,o,s,H[M+3],10,2399980690),s=Md5.II(s,i,a,o,H[M+10],15,4293915773),o=Md5.II(o,s,i,a,H[M+1],21,2240044497),a=Md5.II(a,o,s,i,H[M+8],6,1873313359),i=Md5.II(i,a,o,s,H[M+15],10,4264355552),s=Md5.II(s,i,a,o,H[M+6],15,2734768916),o=Md5.II(o,s,i,a,H[M+13],21,1309151649),a=Md5.II(a,o,s,i,H[M+4],6,4149444226),i=Md5.II(i,a,o,s,H[M+11],10,3174756917),s=Md5.II(s,i,a,o,H[M+2],15,718787259),o=Md5.II(o,s,i,a,H[M+9],21,3951481745),a=Md5.addUnsigned(a,t),o=Md5.addUnsigned(o,e),s=Md5.addUnsigned(s,r),i=Md5.addUnsigned(i,n);return(Md5.wordToHex(a)+Md5.wordToHex(o)+Md5.wordToHex(s)+Md5.wordToHex(i)).toLowerCase()}static rotateLeft(d,M){return d<<M|d>>>32-M}static addUnsigned(d,M){const t=2147483648&d,e=2147483648&M,r=1073741824&d,n=1073741824&M,a=(1073741823&d)+(1073741823&M);return r&n?2147483648^a^t^e:r|n?1073741824&a?3221225472^a^t^e:1073741824^a^t^e:a^t^e}static F(d,M,t){return d&M|~d&t}static G(d,M,t){return d&t|M&~t}static H(d,M,t){return d^M^t}static I(d,M,t){return M^(d|~t)}static FF(d,M,t,e,r,n,a){return d=Md5.addUnsigned(d,Md5.addUnsigned(Md5.addUnsigned(Md5.F(M,t,e),r),a)),Md5.addUnsigned(Md5.rotateLeft(d,n),M)}static GG(d,M,t,e,r,n,a){return d=Md5.addUnsigned(d,Md5.addUnsigned(Md5.addUnsigned(Md5.G(M,t,e),r),a)),Md5.addUnsigned(Md5.rotateLeft(d,n),M)}static HH(d,M,t,e,r,n,a){return d=Md5.addUnsigned(d,Md5.addUnsigned(Md5.addUnsigned(Md5.H(M,t,e),r),a)),Md5.addUnsigned(Md5.rotateLeft(d,n),M)}static II(d,M,t,e,r,n,a){return d=Md5.addUnsigned(d,Md5.addUnsigned(Md5.addUnsigned(Md5.I(M,t,e),r),a)),Md5.addUnsigned(Md5.rotateLeft(d,n),M)}static convertToWordArray(d){let M;const t=d.length,e=t+8,r=16*((e-e%64)/64+1),n=Array(r-1);let a=0,o=0;for(;o<t;)M=(o-o%4)/4,a=o%4*8,n[M]=n[M]|d.charCodeAt(o)<<a,o++;return M=(o-o%4)/4,a=o%4*8,n[M]=n[M]|128<<a,n[r-2]=t<<3,n[r-1]=t>>>29,n}static wordToHex(d){let M,t,e="",r="";for(t=0;t<=3;t++)M=d>>>8*t&255,r="0"+M.toString(16),e+=r.substr(r.length-2,2);return e}static utf8Encode(d){d=d.replace(/\r\n/g,"\n");let M="";for(let t=0;t<d.length;t++){const e=d.charCodeAt(t);e<128?M+=String.fromCharCode(e):e>127&&e<2048?(M+=String.fromCharCode(e>>6|192),M+=String.fromCharCode(63&e|128)):(M+=String.fromCharCode(e>>12|224),M+=String.fromCharCode(e>>6&63|128),M+=String.fromCharCode(63&e|128))}return M}}export default Md5; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/image-manipulation.js b/typo3/sysext/backend/Resources/Public/JavaScript/image-manipulation.js index 0be0ecf0a7b6..cae6fee6380f 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/image-manipulation.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/image-manipulation.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import{html}from"lit";import{unsafeHTML}from"lit/directives/unsafe-html.js";import"jquery-ui/draggable.js";import"jquery-ui/resizable.js";import FormEngineValidation from"@typo3/backend/form-engine-validation.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Cropper from"cropperjs";import{default as Modal}from"@typo3/backend/modal.js";import"@typo3/backend/element/spinner-element.js";class ImageManipulation{constructor(){this.initialized=!1,this.cropImageContainerSelector="#t3js-crop-image-container",this.cropImageSelector="#t3js-crop-image",this.coverAreaSelector=".t3js-cropper-cover-area",this.cropInfoSelector=".t3js-cropper-info-crop",this.focusAreaSelector="#t3js-cropper-focus-area",this.cropBox=$(),this.defaultFocusArea={height:1/3,width:1/3,x:0,y:0},this.defaultOpts={autoCrop:!0,autoCropArea:.7,dragMode:"crop",guides:!0,responsive:!0,viewMode:1,zoomable:!1,checkCrossOrigin:!1},this.cropBuiltHandler=()=>{this.initialized=!0;const t=this.cropper.getImageData(),e=this.currentModal.querySelector(this.cropImageSelector);$(this.currentModal).find(".cropper-canvas img").removeClass("cropper-hide"),this.imageOriginalSizeFactor=parseInt(e.dataset.originalWidth,10)/t.naturalWidth,this.cropVariantTriggers.each(((e,r)=>{const a=$(r).attr("data-crop-variant-id"),i=this.convertRelativeToAbsoluteCropArea(this.data[a].cropArea,t),o=$.extend(!0,{},this.data[a],{cropArea:i});this.updatePreviewThumbnail(o,$(r))})),this.currentCropVariant.cropArea=this.convertRelativeToAbsoluteCropArea(this.currentCropVariant.cropArea,t),this.cropBox=$(this.currentModal).find(".cropper-crop-box"),this.setCropArea(this.currentCropVariant.cropArea),this.currentCropVariant.coverAreas&&this.initCoverAreas(this.cropBox,this.currentCropVariant.coverAreas),this.currentCropVariant.focusArea&&(ImageManipulation.isEmptyArea(this.currentCropVariant.focusArea)&&(this.currentCropVariant.focusArea=$.extend(!0,{},this.defaultFocusArea)),this.initFocusArea(this.cropBox),this.scaleAndMoveFocusArea(this.currentCropVariant.focusArea)),this.currentCropVariant.selectedRatio&&$(this.currentModal).find(`[data-bs-option='${this.currentCropVariant.selectedRatio}']`).addClass("active")},this.cropMoveHandler=t=>{if(!this.initialized)return;let e=t.detail.width,r=t.detail.height;(e<15||r<15)&&(e=Math.max(15,r),r=Math.max(15,e),this.cropper.setData({width:e,height:r})),this.currentCropVariant.cropArea=$.extend(!0,this.currentCropVariant.cropArea,{width:Math.floor(e),height:Math.floor(r),x:Math.floor(t.detail.x),y:Math.floor(t.detail.y)}),this.updatePreviewThumbnail(this.currentCropVariant,this.activeCropVariantTrigger),this.updateCropVariantData(this.currentCropVariant);const a=Math.round(this.currentCropVariant.cropArea.width*this.imageOriginalSizeFactor),i=Math.round(this.currentCropVariant.cropArea.height*this.imageOriginalSizeFactor);this.cropInfo.text(`${a}×${i} px`)},this.cropStartHandler=()=>{this.currentCropVariant.focusArea&&(this.focusArea.draggable("option","disabled",!0),this.focusArea.resizable("option","disabled",!0))},this.cropEndHandler=()=>{this.currentCropVariant.focusArea&&(this.focusArea.draggable("option","disabled",!1),this.focusArea.resizable("option","disabled",!1))}}static isEmptyArea(t){return $.isEmptyObject(t)}static wait(t,e){window.setTimeout(t,e)}static toCssPercent(t){return 100*t+"%"}static serializeCropVariants(t){return JSON.stringify(t,((t,e)=>"id"===t||"title"===t||"allowedAspectRatios"===t||"coverAreas"===t?void 0:e))}initializeTrigger(){$(".t3js-image-manipulation-trigger").off("click").on("click",(t=>{t.preventDefault(),this.trigger=$(t.currentTarget),this.show()}))}async initializeCropperModal(){const t=this.currentModal.querySelector(this.cropImageSelector);await new Promise((e=>{t.complete?e():t.addEventListener("load",(()=>e()))})),this.init()}show(){const t=this.trigger.data("modalTitle"),e=this.trigger.data("buttonPreviewText"),r=this.trigger.data("buttonDismissText"),a=this.trigger.data("buttonSaveText"),i=this.trigger.data("url"),o=this.trigger.data("payload");this.currentModal=Modal.advanced({additionalCssClasses:["modal-image-manipulation","cropper"],buttons:[{btnClass:"btn-default float-start",name:"preview",icon:"actions-view",text:e},{btnClass:"btn-default",name:"dismiss",icon:"actions-close",text:r},{btnClass:"btn-primary",name:"save",icon:"actions-document-save",text:a}],content:html`<div class="modal-loading"><typo3-backend-spinner size="default"></typo3-backend-spinner></div>`,size:Modal.sizes.full,style:Modal.styles.dark,title:t,staticBackdrop:!0}),this.currentModal.addEventListener("typo3-modal-shown",(()=>{new AjaxRequest(i).post(o).then((async t=>{const e=await t.resolve();this.currentModal.templateResultContent=html`${unsafeHTML(e)}`,this.currentModal.updateComplete.then((()=>this.initializeCropperModal()))}))})),this.currentModal.addEventListener("typo3-modal-hide",(()=>{this.destroy()}))}init(){const t=this.currentModal.querySelector(this.cropImageSelector),e=this.trigger.attr("data-crop-variants");if(!e)throw new TypeError("ImageManipulation: No cropVariants data found for image");this.data=$.isEmptyObject(this.data)?JSON.parse(e):this.data,this.cropVariantTriggers=$(this.currentModal).find(".t3js-crop-variant-trigger"),this.activeCropVariantTrigger=$(this.currentModal).find(".t3js-crop-variant-trigger.is-active"),this.cropInfo=$(this.currentModal).find(this.cropInfoSelector),this.saveButton=$(this.currentModal).find("button[name=save]"),this.previewButton=$(this.currentModal).find("button[name=preview]"),this.dismissButton=$(this.currentModal).find("button[name=dismiss]"),this.resetButton=$(this.currentModal).find("button[name=reset]"),this.aspectRatioTrigger=$(this.currentModal).find("label[data-method=setAspectRatio]"),this.currentCropVariant=this.data[this.activeCropVariantTrigger.attr("data-crop-variant-id")],this.cropVariantTriggers.off("click").on("click",(t=>{if($(t.currentTarget).hasClass("is-active"))return t.stopPropagation(),void t.preventDefault();this.activeCropVariantTrigger.removeClass("is-active"),$(t.currentTarget).addClass("is-active"),this.activeCropVariantTrigger=$(t.currentTarget);const e=this.data[this.activeCropVariantTrigger.attr("data-crop-variant-id")],r=this.cropper.getImageData();e.cropArea=this.convertRelativeToAbsoluteCropArea(e.cropArea,r),this.currentCropVariant=$.extend(!0,{},e),this.update(e)})),this.aspectRatioTrigger.off("click").on("click",(t=>{const e=$(t.currentTarget).attr("data-bs-option"),r=$.extend(!0,{},this.currentCropVariant),a=r.allowedAspectRatios[e];this.setAspectRatio(a),this.setCropArea(r.cropArea),this.currentCropVariant=$.extend(!0,{},r,{selectedRatio:e}),this.update(this.currentCropVariant)})),this.saveButton.off("click").on("click",(()=>{this.save(this.data)})),this.trigger.attr("data-preview-url")?this.previewButton.off("click").on("click",(()=>{this.openPreview(this.data)})):this.previewButton.hide(),this.dismissButton.off("click").on("click",(()=>{this.currentModal.hideModal()})),this.resetButton.off("click").on("click",(t=>{const e=this.cropper.getImageData(),r=$(t.currentTarget).attr("data-crop-variant");if(t.preventDefault(),t.stopPropagation(),!r)throw new TypeError("TYPO3 Cropper: No cropVariant data attribute found on reset element.");const a=JSON.parse(r),i=this.convertRelativeToAbsoluteCropArea(a.cropArea,e);this.currentCropVariant=$.extend(!0,{},a,{cropArea:i}),this.update(this.currentCropVariant)})),ImageManipulation.isEmptyArea(this.currentCropVariant.cropArea)&&(this.defaultOpts=$.extend({autoCropArea:1},this.defaultOpts)),this.cropper=new Cropper(t,$.extend(this.defaultOpts,{ready:this.cropBuiltHandler,crop:this.cropMoveHandler,cropend:this.cropEndHandler,cropstart:this.cropStartHandler,data:this.currentCropVariant.cropArea})),this.update(this.currentCropVariant)}update(t){const e=$.extend(!0,{},t),r=t.allowedAspectRatios[t.selectedRatio];$(this.currentModal).find("[data-bs-option]").removeClass("active"),$(this.currentModal).find(`[data-bs-option="${t.selectedRatio}"]`).addClass("active"),this.setAspectRatio(r),this.setCropArea(e.cropArea),this.currentCropVariant=$.extend(!0,{},e,t),this.cropBox.find(this.coverAreaSelector).remove(),this.cropBox.has(this.focusAreaSelector).length&&(this.focusArea.resizable("destroy").draggable("destroy"),this.focusArea.remove()),t.focusArea&&(ImageManipulation.isEmptyArea(t.focusArea)&&(this.currentCropVariant.focusArea=$.extend(!0,{},this.defaultFocusArea)),this.initFocusArea(this.cropBox),this.scaleAndMoveFocusArea(this.currentCropVariant.focusArea)),t.coverAreas&&this.initCoverAreas(this.cropBox,this.currentCropVariant.coverAreas),this.updatePreviewThumbnail(this.currentCropVariant,this.activeCropVariantTrigger)}initFocusArea(t){this.focusArea=$('<div id="t3js-cropper-focus-area" class="cropper-focus-area"></div>'),t.append(this.focusArea),this.focusArea.draggable({containment:t,create:()=>{const t="undefined"!=typeof window&&void 0!==window.document,e=!(!t||!window.document.documentElement)&&"ontouchstart"in window.document.documentElement,r=!!t&&"PointerEvent"in window,a=r?"pointerdown":e?"touchstart":"mousedown",i=r?"pointerup pointercancel":e?"touchend touchcancel":"mouseup";this.focusArea.on(a,(()=>{this.cropper.disable()})),this.focusArea.on(i,(()=>{this.cropper.enable()})),this.scaleAndMoveFocusArea(this.currentCropVariant.focusArea)},drag:()=>{const{left:e,top:r}=t.offset(),{left:a,top:i}=this.focusArea.offset(),{focusArea:o,coverAreas:s}=this.currentCropVariant;o.x=(a-e)/t.width(),o.y=(i-r)/t.height(),this.updatePreviewThumbnail(this.currentCropVariant,this.activeCropVariantTrigger),this.checkFocusAndCoverAreasCollision(o,s)?this.focusArea.addClass("has-nodrop"):this.focusArea.removeClass("has-nodrop")},revert:()=>{const{left:e,top:r}=t.offset(),{left:a,top:i}=this.focusArea.offset(),{focusArea:o,coverAreas:s}=this.currentCropVariant;return!!this.checkFocusAndCoverAreasCollision(o,s)&&(this.focusArea.removeClass("has-nodrop"),ImageManipulation.wait((()=>{o.x=(a-e)/t.width(),o.y=(i-r)/t.height(),this.updateCropVariantData(this.currentCropVariant)}),250),!0)},revertDuration:200,stop:()=>{const{left:e,top:r}=t.offset(),{left:a,top:i}=this.focusArea.offset(),{focusArea:o}=this.currentCropVariant;o.x=(a-e)/t.width(),o.y=(i-r)/t.height(),this.scaleAndMoveFocusArea(o)}}).resizable({containment:t,handles:"all",resize:()=>{const{left:e,top:r}=t.offset(),{left:a,top:i}=this.focusArea.offset(),{focusArea:o,coverAreas:s}=this.currentCropVariant;o.height=this.focusArea.height()/t.height(),o.width=this.focusArea.width()/t.width(),o.x=(a-e)/t.width(),o.y=(i-r)/t.height(),this.updatePreviewThumbnail(this.currentCropVariant,this.activeCropVariantTrigger),this.checkFocusAndCoverAreasCollision(o,s)?this.focusArea.addClass("has-nodrop"):this.focusArea.removeClass("has-nodrop")},stop:(e,r)=>{const{left:a,top:i}=t.offset(),{left:o,top:s}=this.focusArea.offset(),{focusArea:n,coverAreas:c}=this.currentCropVariant;this.checkFocusAndCoverAreasCollision(n,c)?r.element.animate($.extend(r.originalPosition,r.originalSize),250,(()=>{n.height=this.focusArea.height()/t.height(),n.width=this.focusArea.width()/t.width(),n.x=(o-a)/t.width(),n.y=(s-i)/t.height(),this.scaleAndMoveFocusArea(n),this.focusArea.removeClass("has-nodrop")})):this.scaleAndMoveFocusArea(n)}})}initCoverAreas(t,e){e.forEach((e=>{const r=$('<div class="cropper-cover-area t3js-cropper-cover-area"></div>');t.append(r),r.css({height:ImageManipulation.toCssPercent(e.height),left:ImageManipulation.toCssPercent(e.x),top:ImageManipulation.toCssPercent(e.y),width:ImageManipulation.toCssPercent(e.width)})}))}updatePreviewThumbnail(t,e){let r;const a=e.find(".t3js-cropper-preview-thumbnail-crop-area"),i=e.find(".t3js-cropper-preview-thumbnail-crop-image"),o=e.find(".t3js-cropper-preview-thumbnail-focus-area"),s=this.cropper.getImageData();a.css({height:ImageManipulation.toCssPercent(t.cropArea.height/s.naturalHeight),left:ImageManipulation.toCssPercent(t.cropArea.x/s.naturalWidth),top:ImageManipulation.toCssPercent(t.cropArea.y/s.naturalHeight),width:ImageManipulation.toCssPercent(t.cropArea.width/s.naturalWidth)}),t.focusArea&&o.css({height:ImageManipulation.toCssPercent(t.focusArea.height),left:ImageManipulation.toCssPercent(t.focusArea.x),top:ImageManipulation.toCssPercent(t.focusArea.y),width:ImageManipulation.toCssPercent(t.focusArea.width)}),r=a.css(["width","height","left","top"]),i.css({height:parseFloat(r.height)*(1/(t.cropArea.height/s.naturalHeight))+"px",margin:-1*parseFloat(r.left)+"px",marginTop:-1*parseFloat(r.top)+"px",width:parseFloat(r.width)*(1/(t.cropArea.width/s.naturalWidth))+"px"})}scaleAndMoveFocusArea(t){this.focusArea.css({height:ImageManipulation.toCssPercent(t.height),left:ImageManipulation.toCssPercent(t.x),top:ImageManipulation.toCssPercent(t.y),width:ImageManipulation.toCssPercent(t.width)}),this.currentCropVariant.focusArea=t,this.updatePreviewThumbnail(this.currentCropVariant,this.activeCropVariantTrigger),this.updateCropVariantData(this.currentCropVariant)}updateCropVariantData(t){const e=this.cropper.getImageData(),r=this.convertAbsoluteToRelativeCropArea(t.cropArea,e);this.data[t.id]=$.extend(!0,{},t,{cropArea:r})}setAspectRatio(t){this.cropper.setAspectRatio(t.value)}setCropArea(t){const e=this.currentCropVariant.allowedAspectRatios[this.currentCropVariant.selectedRatio];0===e.value?this.cropper.setData({height:t.height,width:t.width,x:t.x,y:t.y}):this.cropper.setData({height:t.height,width:t.height*e.value,x:t.x,y:t.y})}checkFocusAndCoverAreasCollision(t,e){return!!e&&e.some((e=>t.x<e.x+e.width&&t.x+t.width>e.x&&t.y<e.y+e.height&&t.height+t.y>e.y))}convertAbsoluteToRelativeCropArea(t,e){const{height:r,width:a,x:i,y:o}=t;return{height:r/e.naturalHeight,width:a/e.naturalWidth,x:i/e.naturalWidth,y:o/e.naturalHeight}}convertRelativeToAbsoluteCropArea(t,e){const{height:r,width:a,x:i,y:o}=t;return{height:r*e.naturalHeight,width:a*e.naturalWidth,x:i*e.naturalWidth,y:o*e.naturalHeight}}setPreviewImages(t){const e=this.cropper.image,r=this.cropper.getImageData();Object.keys(t).forEach((a=>{const i=t[a],o=this.convertRelativeToAbsoluteCropArea(i.cropArea,r),s=this.trigger.closest(".form-group").find(`.t3js-image-manipulation-preview[data-crop-variant-id="${a}"]`),n=this.trigger.closest(".form-group").find(`.t3js-image-manipulation-selected-ratio[data-crop-variant-id="${a}"]`);if(0===s.length)return;let c=s.width(),h=s.data("preview-height");const p=o.width/o.height,d=c/p;d>h?c=h*p:h=d,c>o.width&&(c=o.width,h=o.height);const l=c/o.width,u=$("<div />").html('<img src="'+e.src+'">'),g=$(this.currentModal).find(`.t3-js-ratio-title[data-ratio-id="${i.id}${i.selectedRatio}"]`);n.text(g.text()),u.addClass("cropper-preview-container"),s.empty().append(u),u.wrap('<span class="thumbnail thumbnail-status"></span>'),u.width(c).height(h).find("img").css({height:r.naturalHeight*l,left:-o.x*l,top:-o.y*l,width:r.naturalWidth*l})}))}openPreview(t){const e=ImageManipulation.serializeCropVariants(t);let r=this.trigger.attr("data-preview-url");r=r+(r.includes("?")?"&":"?")+"cropVariants="+encodeURIComponent(e),window.open(r,"TYPO3ImageManipulationPreview")}save(t){const e=ImageManipulation.serializeCropVariants(t),r=$(`#${this.trigger.attr("data-field")}`);this.trigger.attr("data-crop-variants",JSON.stringify(t)),this.setPreviewImages(t),r.val(e),FormEngineValidation.markFieldAsChanged(r),this.currentModal.hideModal()}destroy(){this.currentModal&&(this.cropper instanceof Cropper&&this.cropper.destroy(),this.initialized=!1,this.cropper=null,this.currentModal=null,this.data=null)}}export default new ImageManipulation; \ No newline at end of file +import $ from"jquery";import{html}from"lit";import{unsafeHTML}from"lit/directives/unsafe-html.js";import"jquery-ui/draggable.js";import"jquery-ui/resizable.js";import FormEngineValidation from"@typo3/backend/form-engine-validation.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Cropper from"cropperjs";import{default as Modal}from"@typo3/backend/modal.js";import"@typo3/backend/element/spinner-element.js";class ImageManipulation{constructor(){this.initialized=!1,this.cropImageContainerSelector="#t3js-crop-image-container",this.cropImageSelector="#t3js-crop-image",this.coverAreaSelector=".t3js-cropper-cover-area",this.cropInfoSelector=".t3js-cropper-info-crop",this.focusAreaSelector="#t3js-cropper-focus-area",this.cropBox=$(),this.defaultFocusArea={height:1/3,width:1/3,x:0,y:0},this.defaultOpts={autoCrop:!0,autoCropArea:.7,dragMode:"crop",guides:!0,responsive:!0,viewMode:1,zoomable:!1,checkCrossOrigin:!1},this.cropBuiltHandler=()=>{this.initialized=!0;const t=this.cropper.getImageData(),e=this.currentModal.querySelector(this.cropImageSelector);$(this.currentModal).find(".cropper-canvas img").removeClass("cropper-hide"),this.imageOriginalSizeFactor=parseInt(e.dataset.originalWidth,10)/t.naturalWidth,this.cropVariantTriggers.each(((e,r)=>{const a=$(r).attr("data-crop-variant-id"),i=this.convertRelativeToAbsoluteCropArea(this.data[a].cropArea,t),o=$.extend(!0,{},this.data[a],{cropArea:i});this.updatePreviewThumbnail(o,$(r))})),this.currentCropVariant.cropArea=this.convertRelativeToAbsoluteCropArea(this.currentCropVariant.cropArea,t),this.cropBox=$(this.currentModal).find(".cropper-crop-box"),this.setCropArea(this.currentCropVariant.cropArea),this.currentCropVariant.coverAreas&&this.initCoverAreas(this.cropBox,this.currentCropVariant.coverAreas),this.currentCropVariant.focusArea&&(ImageManipulation.isEmptyArea(this.currentCropVariant.focusArea)&&(this.currentCropVariant.focusArea=$.extend(!0,{},this.defaultFocusArea)),this.initFocusArea(this.cropBox),this.scaleAndMoveFocusArea(this.currentCropVariant.focusArea)),this.currentCropVariant.selectedRatio&&$(this.currentModal).find(`[data-bs-option='${this.currentCropVariant.selectedRatio}']`).addClass("active")},this.cropMoveHandler=t=>{if(!this.initialized)return;let e=t.detail.width,r=t.detail.height;(e<15||r<15)&&(e=Math.max(15,r),r=Math.max(15,e),this.cropper.setData({width:e,height:r})),this.currentCropVariant.cropArea=$.extend(!0,this.currentCropVariant.cropArea,{width:Math.floor(e),height:Math.floor(r),x:Math.floor(t.detail.x),y:Math.floor(t.detail.y)}),this.updatePreviewThumbnail(this.currentCropVariant,this.activeCropVariantTrigger),this.updateCropVariantData(this.currentCropVariant);const a=Math.round(this.currentCropVariant.cropArea.width*this.imageOriginalSizeFactor),i=Math.round(this.currentCropVariant.cropArea.height*this.imageOriginalSizeFactor);this.cropInfo.text(`${a}×${i} px`)},this.cropStartHandler=()=>{this.currentCropVariant.focusArea&&(this.focusArea.draggable("option","disabled",!0),this.focusArea.resizable("option","disabled",!0))},this.cropEndHandler=()=>{this.currentCropVariant.focusArea&&(this.focusArea.draggable("option","disabled",!1),this.focusArea.resizable("option","disabled",!1))}}static isEmptyArea(t){return $.isEmptyObject(t)}static wait(t,e){window.setTimeout(t,e)}static toCssPercent(t){return 100*t+"%"}static serializeCropVariants(t){return JSON.stringify(t,((t,e)=>"id"===t||"title"===t||"allowedAspectRatios"===t||"coverAreas"===t?void 0:e))}initializeTrigger(){$(".t3js-image-manipulation-trigger").off("click").on("click",(t=>{t.preventDefault(),this.trigger=$(t.currentTarget),this.show()}))}async initializeCropperModal(){const t=this.currentModal.querySelector(this.cropImageSelector);await new Promise((e=>{t.complete?e():t.addEventListener("load",(()=>e()))})),this.init()}show(){const t=this.trigger.data("modalTitle"),e=this.trigger.data("buttonPreviewText"),r=this.trigger.data("buttonDismissText"),a=this.trigger.data("buttonSaveText"),i=this.trigger.data("url"),o=this.trigger.data("payload");this.currentModal=Modal.advanced({additionalCssClasses:["modal-image-manipulation","cropper"],buttons:[{btnClass:"btn-default float-start",name:"preview",icon:"actions-view",text:e},{btnClass:"btn-default",name:"dismiss",icon:"actions-close",text:r},{btnClass:"btn-primary",name:"save",icon:"actions-document-save",text:a}],content:html`<div class="modal-loading"><typo3-backend-spinner size="default"></typo3-backend-spinner></div>`,size:Modal.sizes.full,style:Modal.styles.dark,title:t,staticBackdrop:!0}),this.currentModal.addEventListener("typo3-modal-shown",(()=>{new AjaxRequest(i).post(o).then((async t=>{const e=await t.resolve();this.currentModal.templateResultContent=html`${unsafeHTML(e)}`,this.currentModal.updateComplete.then((()=>this.initializeCropperModal()))}))})),this.currentModal.addEventListener("typo3-modal-hide",(()=>{this.destroy()}))}init(){const t=this.currentModal.querySelector(this.cropImageSelector),e=this.trigger.attr("data-crop-variants");if(!e)throw new TypeError("ImageManipulation: No cropVariants data found for image");this.data=$.isEmptyObject(this.data)?JSON.parse(e):this.data,this.cropVariantTriggers=$(this.currentModal).find(".t3js-crop-variant-trigger"),this.activeCropVariantTrigger=$(this.currentModal).find(".t3js-crop-variant-trigger.is-active"),this.cropInfo=$(this.currentModal).find(this.cropInfoSelector),this.saveButton=$(this.currentModal).find("button[name=save]"),this.previewButton=$(this.currentModal).find("button[name=preview]"),this.dismissButton=$(this.currentModal).find("button[name=dismiss]"),this.resetButton=$(this.currentModal).find("button[name=reset]"),this.aspectRatioTrigger=$(this.currentModal).find("label[data-method=setAspectRatio]"),this.currentCropVariant=this.data[this.activeCropVariantTrigger.attr("data-crop-variant-id")],this.cropVariantTriggers.off("click").on("click",(t=>{if($(t.currentTarget).hasClass("is-active"))return t.stopPropagation(),void t.preventDefault();this.activeCropVariantTrigger.removeClass("is-active"),$(t.currentTarget).addClass("is-active"),this.activeCropVariantTrigger=$(t.currentTarget);const e=this.data[this.activeCropVariantTrigger.attr("data-crop-variant-id")],r=this.cropper.getImageData();e.cropArea=this.convertRelativeToAbsoluteCropArea(e.cropArea,r),this.currentCropVariant=$.extend(!0,{},e),this.update(e)})),this.aspectRatioTrigger.off("click").on("click",(t=>{const e=$(t.currentTarget).attr("data-bs-option"),r=$.extend(!0,{},this.currentCropVariant),a=r.allowedAspectRatios[e];this.setAspectRatio(a),this.setCropArea(r.cropArea),this.currentCropVariant=$.extend(!0,{},r,{selectedRatio:e}),this.update(this.currentCropVariant)})),this.saveButton.off("click").on("click",(()=>{this.save(this.data)})),this.trigger.attr("data-preview-url")?this.previewButton.off("click").on("click",(()=>{this.openPreview(this.data)})):this.previewButton.hide(),this.dismissButton.off("click").on("click",(()=>{this.currentModal.hideModal()})),this.resetButton.off("click").on("click",(t=>{const e=this.cropper.getImageData(),r=$(t.currentTarget).attr("data-crop-variant");if(t.preventDefault(),t.stopPropagation(),!r)throw new TypeError("TYPO3 Cropper: No cropVariant data attribute found on reset element.");const a=JSON.parse(r),i=this.convertRelativeToAbsoluteCropArea(a.cropArea,e);this.currentCropVariant=$.extend(!0,{},a,{cropArea:i}),this.update(this.currentCropVariant)})),ImageManipulation.isEmptyArea(this.currentCropVariant.cropArea)&&(this.defaultOpts=$.extend({autoCropArea:1},this.defaultOpts)),this.cropper=new Cropper(t,$.extend(this.defaultOpts,{ready:this.cropBuiltHandler,crop:this.cropMoveHandler,cropend:this.cropEndHandler,cropstart:this.cropStartHandler,data:this.currentCropVariant.cropArea})),this.update(this.currentCropVariant)}update(t){const e=$.extend(!0,{},t),r=t.allowedAspectRatios[t.selectedRatio];$(this.currentModal).find("[data-bs-option]").removeClass("active"),$(this.currentModal).find(`[data-bs-option="${t.selectedRatio}"]`).addClass("active"),this.setAspectRatio(r),this.setCropArea(e.cropArea),this.currentCropVariant=$.extend(!0,{},e,t),this.cropBox.find(this.coverAreaSelector).remove(),this.cropBox.has(this.focusAreaSelector).length&&(this.focusArea.resizable("destroy").draggable("destroy"),this.focusArea.remove()),t.focusArea&&(ImageManipulation.isEmptyArea(t.focusArea)&&(this.currentCropVariant.focusArea=$.extend(!0,{},this.defaultFocusArea)),this.initFocusArea(this.cropBox),this.scaleAndMoveFocusArea(this.currentCropVariant.focusArea)),t.coverAreas&&this.initCoverAreas(this.cropBox,this.currentCropVariant.coverAreas),this.updatePreviewThumbnail(this.currentCropVariant,this.activeCropVariantTrigger)}initFocusArea(t){this.focusArea=$('<div id="t3js-cropper-focus-area" class="cropper-focus-area"></div>'),t.append(this.focusArea),this.focusArea.draggable({containment:t,create:()=>{const t="undefined"!=typeof window&&void 0!==window.document,e=!(!t||!window.document.documentElement)&&"ontouchstart"in window.document.documentElement,r=!!t&&"PointerEvent"in window,a=r?"pointerdown":e?"touchstart":"mousedown",i=r?"pointerup pointercancel":e?"touchend touchcancel":"mouseup";this.focusArea.on(a,(()=>{this.cropper.disable()})),this.focusArea.on(i,(()=>{this.cropper.enable()})),this.scaleAndMoveFocusArea(this.currentCropVariant.focusArea)},drag:()=>{const{left:e,top:r}=t.offset(),{left:a,top:i}=this.focusArea.offset(),{focusArea:o,coverAreas:s}=this.currentCropVariant;o.x=(a-e)/t.width(),o.y=(i-r)/t.height(),this.updatePreviewThumbnail(this.currentCropVariant,this.activeCropVariantTrigger),this.checkFocusAndCoverAreasCollision(o,s)?this.focusArea.addClass("has-nodrop"):this.focusArea.removeClass("has-nodrop")},revert:()=>{const{left:e,top:r}=t.offset(),{left:a,top:i}=this.focusArea.offset(),{focusArea:o,coverAreas:s}=this.currentCropVariant;return!!this.checkFocusAndCoverAreasCollision(o,s)&&(this.focusArea.removeClass("has-nodrop"),ImageManipulation.wait((()=>{o.x=(a-e)/t.width(),o.y=(i-r)/t.height(),this.updateCropVariantData(this.currentCropVariant)}),250),!0)},revertDuration:200,stop:()=>{const{left:e,top:r}=t.offset(),{left:a,top:i}=this.focusArea.offset(),{focusArea:o}=this.currentCropVariant;o.x=(a-e)/t.width(),o.y=(i-r)/t.height(),this.scaleAndMoveFocusArea(o)}}).resizable({containment:t,handles:"all",resize:()=>{const{left:e,top:r}=t.offset(),{left:a,top:i}=this.focusArea.offset(),{focusArea:o,coverAreas:s}=this.currentCropVariant;o.height=this.focusArea.height()/t.height(),o.width=this.focusArea.width()/t.width(),o.x=(a-e)/t.width(),o.y=(i-r)/t.height(),this.updatePreviewThumbnail(this.currentCropVariant,this.activeCropVariantTrigger),this.checkFocusAndCoverAreasCollision(o,s)?this.focusArea.addClass("has-nodrop"):this.focusArea.removeClass("has-nodrop")},stop:(e,r)=>{const{left:a,top:i}=t.offset(),{left:o,top:s}=this.focusArea.offset(),{focusArea:n,coverAreas:c}=this.currentCropVariant;this.checkFocusAndCoverAreasCollision(n,c)?r.element.animate($.extend(r.originalPosition,r.originalSize),250,(()=>{n.height=this.focusArea.height()/t.height(),n.width=this.focusArea.width()/t.width(),n.x=(o-a)/t.width(),n.y=(s-i)/t.height(),this.scaleAndMoveFocusArea(n),this.focusArea.removeClass("has-nodrop")})):this.scaleAndMoveFocusArea(n)}})}initCoverAreas(t,e){e.forEach((e=>{const r=$('<div class="cropper-cover-area t3js-cropper-cover-area"></div>');t.append(r),r.css({height:ImageManipulation.toCssPercent(e.height),left:ImageManipulation.toCssPercent(e.x),top:ImageManipulation.toCssPercent(e.y),width:ImageManipulation.toCssPercent(e.width)})}))}updatePreviewThumbnail(t,e){const r=e.find(".t3js-cropper-preview-thumbnail-crop-area"),a=e.find(".t3js-cropper-preview-thumbnail-crop-image"),i=e.find(".t3js-cropper-preview-thumbnail-focus-area"),o=this.cropper.getImageData();r.css({height:ImageManipulation.toCssPercent(t.cropArea.height/o.naturalHeight),left:ImageManipulation.toCssPercent(t.cropArea.x/o.naturalWidth),top:ImageManipulation.toCssPercent(t.cropArea.y/o.naturalHeight),width:ImageManipulation.toCssPercent(t.cropArea.width/o.naturalWidth)}),t.focusArea&&i.css({height:ImageManipulation.toCssPercent(t.focusArea.height),left:ImageManipulation.toCssPercent(t.focusArea.x),top:ImageManipulation.toCssPercent(t.focusArea.y),width:ImageManipulation.toCssPercent(t.focusArea.width)});const s=r.css(["width","height","left","top"]);a.css({height:parseFloat(s.height)*(1/(t.cropArea.height/o.naturalHeight))+"px",margin:-1*parseFloat(s.left)+"px",marginTop:-1*parseFloat(s.top)+"px",width:parseFloat(s.width)*(1/(t.cropArea.width/o.naturalWidth))+"px"})}scaleAndMoveFocusArea(t){this.focusArea.css({height:ImageManipulation.toCssPercent(t.height),left:ImageManipulation.toCssPercent(t.x),top:ImageManipulation.toCssPercent(t.y),width:ImageManipulation.toCssPercent(t.width)}),this.currentCropVariant.focusArea=t,this.updatePreviewThumbnail(this.currentCropVariant,this.activeCropVariantTrigger),this.updateCropVariantData(this.currentCropVariant)}updateCropVariantData(t){const e=this.cropper.getImageData(),r=this.convertAbsoluteToRelativeCropArea(t.cropArea,e);this.data[t.id]=$.extend(!0,{},t,{cropArea:r})}setAspectRatio(t){this.cropper.setAspectRatio(t.value)}setCropArea(t){const e=this.currentCropVariant.allowedAspectRatios[this.currentCropVariant.selectedRatio];0===e.value?this.cropper.setData({height:t.height,width:t.width,x:t.x,y:t.y}):this.cropper.setData({height:t.height,width:t.height*e.value,x:t.x,y:t.y})}checkFocusAndCoverAreasCollision(t,e){return!!e&&e.some((e=>t.x<e.x+e.width&&t.x+t.width>e.x&&t.y<e.y+e.height&&t.height+t.y>e.y))}convertAbsoluteToRelativeCropArea(t,e){const{height:r,width:a,x:i,y:o}=t;return{height:r/e.naturalHeight,width:a/e.naturalWidth,x:i/e.naturalWidth,y:o/e.naturalHeight}}convertRelativeToAbsoluteCropArea(t,e){const{height:r,width:a,x:i,y:o}=t;return{height:r*e.naturalHeight,width:a*e.naturalWidth,x:i*e.naturalWidth,y:o*e.naturalHeight}}setPreviewImages(t){const e=this.cropper.image,r=this.cropper.getImageData();Object.keys(t).forEach((a=>{const i=t[a],o=this.convertRelativeToAbsoluteCropArea(i.cropArea,r),s=this.trigger.closest(".form-group").find(`.t3js-image-manipulation-preview[data-crop-variant-id="${a}"]`),n=this.trigger.closest(".form-group").find(`.t3js-image-manipulation-selected-ratio[data-crop-variant-id="${a}"]`);if(0===s.length)return;let c=s.width(),h=s.data("preview-height");const p=o.width/o.height,d=c/p;d>h?c=h*p:h=d,c>o.width&&(c=o.width,h=o.height);const l=c/o.width,u=$("<div />").html('<img src="'+e.src+'">'),g=$(this.currentModal).find(`.t3-js-ratio-title[data-ratio-id="${i.id}${i.selectedRatio}"]`);n.text(g.text()),u.addClass("cropper-preview-container"),s.empty().append(u),u.wrap('<span class="thumbnail thumbnail-status"></span>'),u.width(c).height(h).find("img").css({height:r.naturalHeight*l,left:-o.x*l,top:-o.y*l,width:r.naturalWidth*l})}))}openPreview(t){const e=ImageManipulation.serializeCropVariants(t);let r=this.trigger.attr("data-preview-url");r=r+(r.includes("?")?"&":"?")+"cropVariants="+encodeURIComponent(e),window.open(r,"TYPO3ImageManipulationPreview")}save(t){const e=ImageManipulation.serializeCropVariants(t),r=$(`#${this.trigger.attr("data-field")}`);this.trigger.attr("data-crop-variants",JSON.stringify(t)),this.setPreviewImages(t),r.val(e),FormEngineValidation.markFieldAsChanged(r),this.currentModal.hideModal()}destroy(){this.currentModal&&(this.cropper instanceof Cropper&&this.cropper.destroy(),this.initialized=!1,this.cropper=null,this.currentModal=null,this.data=null)}}export default new ImageManipulation; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/input/clearable.js b/typo3/sysext/backend/Resources/Public/JavaScript/input/clearable.js index 45d789e31cfc..44ff54b0714b 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/input/clearable.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/input/clearable.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -class Clearable{static createCloseButton(){const e=document.createElement("button");return e.type="button",e.tabIndex=-1,e.innerHTML='<span class="t3js-icon icon icon-size-small icon-state-default icon-actions-close" data-identifier="actions-close">\n <span class="icon-markup">\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">\n <path\n d="M11.9 5.5L9.4 8l2.5 2.5c.2.2.2.5 0\n .7l-.7.7c-.2.2-.5.2-.7 0L8 9.4l-2.5 2.5c-.2.2-.5.2-.7\n 0l-.7-.7c-.2-.2-.2-.5 0-.7L6.6 8 4.1 5.5c-.2-.2-.2-.5\n 0-.7l.7-.7c.2-.2.5-.2.7 0L8 6.6l2.5-2.5c.2-.2.5-.2.7\n 0l.7.7c.2.2.2.5 0 .7z"\n class="icon-color"/>\n </svg>\n </span>\n </span>',e.style.visibility="hidden",e.classList.add("close"),e}constructor(){"function"!=typeof HTMLInputElement.prototype.clearable&&this.registerClearable()}registerClearable(){HTMLInputElement.prototype.clearable=function(e={}){if(this.isClearable)return;if("object"!=typeof e)throw new Error("Passed options must be an object, "+typeof e+" given");const t=document.createElement("div");t.classList.add("form-control-clearable"),this.parentNode.insertBefore(t,this),t.appendChild(this);const n=Clearable.createCloseButton(),s=()=>{n.style.visibility=0===this.value.length?"hidden":"visible"};n.addEventListener("click",(t=>{t.preventDefault(),this.value="","function"==typeof e.onClear&&e.onClear(this),this.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0})),s()})),t.appendChild(n),this.addEventListener("focus",s),this.addEventListener("keyup",s),s(),this.isClearable=!0}}}export default new Clearable; \ No newline at end of file +class Clearable{constructor(){"function"!=typeof HTMLInputElement.prototype.clearable&&this.registerClearable()}static createCloseButton(){const e=document.createElement("button");return e.type="button",e.tabIndex=-1,e.innerHTML='<span class="t3js-icon icon icon-size-small icon-state-default icon-actions-close" data-identifier="actions-close">\n <span class="icon-markup">\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">\n <path\n d="M11.9 5.5L9.4 8l2.5 2.5c.2.2.2.5 0\n .7l-.7.7c-.2.2-.5.2-.7 0L8 9.4l-2.5 2.5c-.2.2-.5.2-.7\n 0l-.7-.7c-.2-.2-.2-.5 0-.7L6.6 8 4.1 5.5c-.2-.2-.2-.5\n 0-.7l.7-.7c.2-.2.5-.2.7 0L8 6.6l2.5-2.5c.2-.2.5-.2.7\n 0l.7.7c.2.2.2.5 0 .7z"\n class="icon-color"/>\n </svg>\n </span>\n </span>',e.style.visibility="hidden",e.classList.add("close"),e}registerClearable(){HTMLInputElement.prototype.clearable=function(e={}){if(this.isClearable)return;if("object"!=typeof e)throw new Error("Passed options must be an object, "+typeof e+" given");const t=document.createElement("div");t.classList.add("form-control-clearable"),this.parentNode.insertBefore(t,this),t.appendChild(this);const n=Clearable.createCloseButton(),s=()=>{n.style.visibility=0===this.value.length?"hidden":"visible"};n.addEventListener("click",(t=>{t.preventDefault(),this.value="","function"==typeof e.onClear&&e.onClear(this),this.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0})),s()})),t.appendChild(n),this.addEventListener("focus",s),this.addEventListener("keyup",s),s(),this.isClearable=!0}}}export default new Clearable; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/layout-module/drag-drop.js b/typo3/sysext/backend/Resources/Public/JavaScript/layout-module/drag-drop.js index 21f0482b9381..71d2b4d6a9ff 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/layout-module/drag-drop.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/layout-module/drag-drop.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import interact from"interactjs";import DocumentService from"@typo3/core/document-service.js";import DataHandler from"@typo3/backend/ajax-data-handler.js";import Icons from"@typo3/backend/icons.js";import RegularEvent from"@typo3/core/event/regular-event.js";class DragDrop{constructor(){DocumentService.ready().then((()=>{DragDrop.initialize()}))}static initialize(){const e=document.querySelector(".module");new RegularEvent("wheel",(t=>{e.scrollLeft+=t.deltaX,e.scrollTop+=t.deltaY})).delegateTo(document,".draggable-dragging"),interact(DragDrop.draggableContentIdentifier).draggable({allowFrom:DragDrop.draggableContentHandleIdentifier,onstart:DragDrop.onDragStart,onmove:DragDrop.onDragMove,onend:DragDrop.onDragEnd}).pointerEvents({allowFrom:DragDrop.draggableContentHandleIdentifier}).on("move",(function(e){const t=e.interaction,r=e.currentTarget;if(t.pointerIsDown&&!t.interacting()&&"false"!=r.getAttribute("clone")){const a=r.cloneNode(!0);a.setAttribute("data-dragdrop-clone","true"),r.parentNode.insertBefore(a,r.nextSibling),t.start({name:"drag"},e.interactable,r)}})),interact(DragDrop.dropZoneIdentifier).dropzone({accept:this.draggableContentIdentifier,ondrop:DragDrop.onDrop,checker:(e,t,r,a,o)=>{const n=o.getBoundingClientRect();return t.pageX>=n.left&&t.pageX<=n.left+n.width&&t.pageY>=n.top&&t.pageY<=n.top+n.height}}).on("dragenter",(e=>{e.target.classList.add(DragDrop.dropPossibleHoverClass)})).on("dragleave",(e=>{e.target.classList.remove(DragDrop.dropPossibleHoverClass)}))}static onDragStart(e){e.target.dataset.dragStartX=(e.client.x-e.rect.left).toString(),e.target.dataset.dragStartY=(e.client.y-e.rect.top).toString(),e.target.style.width=getComputedStyle(e.target).getPropertyValue("width"),e.target.classList.add("draggable-dragging");const t=document.createElement("div");t.classList.add("draggable-copy-message"),t.textContent=TYPO3.lang["dragdrop.copy.message"],e.target.append(t),e.target.closest(DragDrop.columnIdentifier).classList.remove("active"),e.target.querySelector(DragDrop.dropZoneIdentifier).hidden=!0,document.querySelectorAll(DragDrop.dropZoneIdentifier).forEach((e=>{const t=e.parentElement.querySelector(DragDrop.addContentIdentifier);null!==t&&(t.hidden=!0,e.classList.add(DragDrop.validDropZoneClass))}))}static onDragMove(e){const t=document.querySelector(".module");e.target.style.left=e.client.x-parseInt(e.target.dataset.dragStartX,10)+"px",e.target.style.top=e.client.y-parseInt(e.target.dataset.dragStartY,10)+"px",e.delta.x<0&&e.pageX-20<0?t.scrollLeft-=20:e.delta.x>0&&e.pageX+20>t.offsetWidth&&(t.scrollLeft+=20),e.delta.y<0&&e.pageY-20-document.querySelector(".t3js-module-docheader").clientHeight<0?t.scrollTop-=20:e.delta.y>0&&e.pageY+20>t.offsetHeight&&(t.scrollTop+=20)}static onDragEnd(e){e.target.dataset.dragStartX="",e.target.dataset.dragStartY="",e.target.classList.remove("draggable-dragging"),e.target.style.width="unset",e.target.style.left="unset",e.target.style.top="unset",e.target.closest(DragDrop.columnIdentifier).classList.add("active"),e.target.querySelector(DragDrop.dropZoneIdentifier).hidden=!1,e.target.querySelector(".draggable-copy-message").remove(),document.querySelectorAll(DragDrop.dropZoneIdentifier+"."+DragDrop.validDropZoneClass).forEach((e=>{const t=e.parentElement.querySelector(DragDrop.addContentIdentifier);null!==t&&(t.hidden=!1),e.classList.remove(DragDrop.validDropZoneClass)})),document.querySelectorAll(DragDrop.draggableContentCloneIdentifier).forEach((e=>{e.remove()}))}static onDrop(e){const t=e.target,r=e.relatedTarget,a=DragDrop.getColumnPositionForElement(t),o=parseInt(r.dataset.uid,10);if("number"==typeof o&&o>0){let n={};const s=t.closest(DragDrop.contentIdentifier).dataset.uid;let l;l=void 0===s?parseInt(t.closest("[data-page]").dataset.page,10):0-parseInt(s,10);let d=parseInt(r.dataset.languageUid,10);-1!==d&&(d=parseInt(t.closest("[data-language-uid]").dataset.languageUid,10));let g=0;0!==l&&(g=a);const i=e.dragEvent.ctrlKey||t.classList.contains("t3js-paste-copy"),c=i?"copy":"move";n.cmd={tt_content:{[o]:{[c]:{action:"paste",target:l,update:{colPos:g,sys_language_uid:d}}}}},DragDrop.ajaxAction(t,r,n,i).then((()=>{const e=document.querySelector(`.t3-page-column-lang-name[data-language-uid="${d}"]`);if(null===e)return;const t=e.dataset.flagIdentifier,a=e.dataset.languageTitle;Icons.getIcon(t,Icons.sizes.small).then((e=>{const t=r.querySelector(".t3js-flag");t.title=a,t.innerHTML=e}))}))}}static ajaxAction(e,t,r,a){const o=Object.keys(r.cmd).shift(),n=parseInt(Object.keys(r.cmd[o]).shift(),10),s={component:"dragdrop",action:a?"copy":"move",table:o,uid:n};return DataHandler.process(r,s).then((r=>{if(r.hasErrors)throw r.messages;e.parentElement.classList.contains(DragDrop.contentIdentifier.substring(1))?e.closest(DragDrop.contentIdentifier).after(t):e.closest(DragDrop.dropZoneIdentifier).after(t),a&&self.location.reload()}))}static getColumnPositionForElement(e){const t=e.closest("[data-colpos]");return null!==t&&void 0!==t.dataset.colpos&&parseInt(t.dataset.colpos,10)}}DragDrop.contentIdentifier=".t3js-page-ce",DragDrop.draggableContentIdentifier=".t3js-page-ce-sortable",DragDrop.draggableContentHandleIdentifier=".t3js-page-ce-draghandle",DragDrop.draggableContentCloneIdentifier="[data-dragdrop-clone]",DragDrop.dropZoneIdentifier=".t3js-page-ce-dropzone-available",DragDrop.columnIdentifier=".t3js-page-column",DragDrop.validDropZoneClass="active",DragDrop.dropPossibleHoverClass="t3-page-ce-dropzone-possible",DragDrop.addContentIdentifier=".t3js-page-new-ce";export default new DragDrop; \ No newline at end of file +import interact from"interactjs";import DocumentService from"@typo3/core/document-service.js";import DataHandler from"@typo3/backend/ajax-data-handler.js";import Icons from"@typo3/backend/icons.js";import RegularEvent from"@typo3/core/event/regular-event.js";class DragDrop{constructor(){DocumentService.ready().then((()=>{DragDrop.initialize()}))}static initialize(){const e=document.querySelector(".module");new RegularEvent("wheel",(t=>{e.scrollLeft+=t.deltaX,e.scrollTop+=t.deltaY})).delegateTo(document,".draggable-dragging"),interact(DragDrop.draggableContentIdentifier).draggable({allowFrom:DragDrop.draggableContentHandleIdentifier,onstart:DragDrop.onDragStart,onmove:DragDrop.onDragMove,onend:DragDrop.onDragEnd}).pointerEvents({allowFrom:DragDrop.draggableContentHandleIdentifier}).on("move",(function(e){const t=e.interaction,r=e.currentTarget;if(t.pointerIsDown&&!t.interacting()&&"false"!=r.getAttribute("clone")){const a=r.cloneNode(!0);a.setAttribute("data-dragdrop-clone","true"),r.parentNode.insertBefore(a,r.nextSibling),t.start({name:"drag"},e.interactable,r)}})),interact(DragDrop.dropZoneIdentifier).dropzone({accept:this.draggableContentIdentifier,ondrop:DragDrop.onDrop,checker:(e,t,r,a,o)=>{const n=o.getBoundingClientRect();return t.pageX>=n.left&&t.pageX<=n.left+n.width&&t.pageY>=n.top&&t.pageY<=n.top+n.height}}).on("dragenter",(e=>{e.target.classList.add(DragDrop.dropPossibleHoverClass)})).on("dragleave",(e=>{e.target.classList.remove(DragDrop.dropPossibleHoverClass)}))}static onDragStart(e){e.target.dataset.dragStartX=(e.client.x-e.rect.left).toString(),e.target.dataset.dragStartY=(e.client.y-e.rect.top).toString(),e.target.style.width=getComputedStyle(e.target).getPropertyValue("width"),e.target.classList.add("draggable-dragging");const t=document.createElement("div");t.classList.add("draggable-copy-message"),t.textContent=TYPO3.lang["dragdrop.copy.message"],e.target.append(t),e.target.closest(DragDrop.columnIdentifier).classList.remove("active"),e.target.querySelector(DragDrop.dropZoneIdentifier).hidden=!0,document.querySelectorAll(DragDrop.dropZoneIdentifier).forEach((e=>{const t=e.parentElement.querySelector(DragDrop.addContentIdentifier);null!==t&&(t.hidden=!0,e.classList.add(DragDrop.validDropZoneClass))}))}static onDragMove(e){const t=document.querySelector(".module");e.target.style.left=e.client.x-parseInt(e.target.dataset.dragStartX,10)+"px",e.target.style.top=e.client.y-parseInt(e.target.dataset.dragStartY,10)+"px",e.delta.x<0&&e.pageX-20<0?t.scrollLeft-=20:e.delta.x>0&&e.pageX+20>t.offsetWidth&&(t.scrollLeft+=20),e.delta.y<0&&e.pageY-20-document.querySelector(".t3js-module-docheader").clientHeight<0?t.scrollTop-=20:e.delta.y>0&&e.pageY+20>t.offsetHeight&&(t.scrollTop+=20)}static onDragEnd(e){e.target.dataset.dragStartX="",e.target.dataset.dragStartY="",e.target.classList.remove("draggable-dragging"),e.target.style.width="unset",e.target.style.left="unset",e.target.style.top="unset",e.target.closest(DragDrop.columnIdentifier).classList.add("active"),e.target.querySelector(DragDrop.dropZoneIdentifier).hidden=!1,e.target.querySelector(".draggable-copy-message").remove(),document.querySelectorAll(DragDrop.dropZoneIdentifier+"."+DragDrop.validDropZoneClass).forEach((e=>{const t=e.parentElement.querySelector(DragDrop.addContentIdentifier);null!==t&&(t.hidden=!1),e.classList.remove(DragDrop.validDropZoneClass)})),document.querySelectorAll(DragDrop.draggableContentCloneIdentifier).forEach((e=>{e.remove()}))}static onDrop(e){const t=e.target,r=e.relatedTarget,a=DragDrop.getColumnPositionForElement(t),o=parseInt(r.dataset.uid,10);if("number"==typeof o&&o>0){const n={},s=t.closest(DragDrop.contentIdentifier).dataset.uid;let l;l=void 0===s?parseInt(t.closest("[data-page]").dataset.page,10):0-parseInt(s,10);let d=parseInt(r.dataset.languageUid,10);-1!==d&&(d=parseInt(t.closest("[data-language-uid]").dataset.languageUid,10));let g=0;0!==l&&(g=a);const i=e.dragEvent.ctrlKey||t.classList.contains("t3js-paste-copy"),c=i?"copy":"move";n.cmd={tt_content:{[o]:{[c]:{action:"paste",target:l,update:{colPos:g,sys_language_uid:d}}}}},DragDrop.ajaxAction(t,r,n,i).then((()=>{const e=document.querySelector(`.t3-page-column-lang-name[data-language-uid="${d}"]`);if(null===e)return;const t=e.dataset.flagIdentifier,a=e.dataset.languageTitle;Icons.getIcon(t,Icons.sizes.small).then((e=>{const t=r.querySelector(".t3js-flag");t.title=a,t.innerHTML=e}))}))}}static ajaxAction(e,t,r,a){const o=Object.keys(r.cmd).shift(),n=parseInt(Object.keys(r.cmd[o]).shift(),10),s={component:"dragdrop",action:a?"copy":"move",table:o,uid:n};return DataHandler.process(r,s).then((r=>{if(r.hasErrors)throw r.messages;e.parentElement.classList.contains(DragDrop.contentIdentifier.substring(1))?e.closest(DragDrop.contentIdentifier).after(t):e.closest(DragDrop.dropZoneIdentifier).after(t),a&&self.location.reload()}))}static getColumnPositionForElement(e){const t=e.closest("[data-colpos]");return null!==t&&void 0!==t.dataset.colpos&&parseInt(t.dataset.colpos,10)}}DragDrop.contentIdentifier=".t3js-page-ce",DragDrop.draggableContentIdentifier=".t3js-page-ce-sortable",DragDrop.draggableContentHandleIdentifier=".t3js-page-ce-draghandle",DragDrop.draggableContentCloneIdentifier="[data-dragdrop-clone]",DragDrop.dropZoneIdentifier=".t3js-page-ce-dropzone-available",DragDrop.columnIdentifier=".t3js-page-column",DragDrop.validDropZoneClass="active",DragDrop.dropPossibleHoverClass="t3-page-ce-dropzone-possible",DragDrop.addContentIdentifier=".t3js-page-new-ce";export default new DragDrop; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/layout-module/paste.js b/typo3/sysext/backend/Resources/Public/JavaScript/layout-module/paste.js index b791e1aff013..00ae74d43825 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/layout-module/paste.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/layout-module/paste.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import DataHandler from"@typo3/backend/ajax-data-handler.js";import{default as Modal}from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import"@typo3/backend/element/icon-element.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";class Paste{constructor(t,e,a){this.itemOnClipboardUid=0,this.itemOnClipboardTitle="",this.copyMode="",this.elementIdentifier=".t3js-page-ce",this.pasteAfterLinkTemplate="",this.pasteIntoLinkTemplate="",this.itemOnClipboardUid=t,this.itemOnClipboardTitle=e,this.copyMode=a,DocumentService.ready().then((()=>{$(".t3js-page-columns").length&&(this.generateButtonTemplates(),this.activatePasteIcons(),this.initializeEvents())}))}static determineColumn(t){const e=t.closest("[data-colpos]");return e.length&&"undefined"!==e.data("colpos")?e.data("colpos"):0}initializeEvents(){$(document).on("click",".t3js-paste",(t=>{t.preventDefault(),this.activatePasteModal($(t.currentTarget))}))}generateButtonTemplates(){this.itemOnClipboardUid&&(this.pasteAfterLinkTemplate='<button type="button" class="t3js-paste t3js-paste'+(this.copyMode?"-"+this.copyMode:"")+' t3js-paste-after btn btn-default btn-sm" title="'+TYPO3.lang?.pasteAfterRecord+'"><typo3-backend-icon identifier="actions-document-paste-into" size="small"></typo3-backend-icon></button>',this.pasteIntoLinkTemplate='<button type="button" class="t3js-paste t3js-paste'+(this.copyMode?"-"+this.copyMode:"")+' t3js-paste-into btn btn-default btn-sm" title="'+TYPO3.lang?.pasteIntoColumn+'"><typo3-backend-icon identifier="actions-document-paste-into" size="small"></typo3-backend-icon></button>')}activatePasteIcons(){this.pasteAfterLinkTemplate&&this.pasteIntoLinkTemplate&&document.querySelectorAll(".t3js-page-new-ce").forEach((t=>{let e=t.parentElement.dataset.page?this.pasteIntoLinkTemplate:this.pasteAfterLinkTemplate;t.append(document.createRange().createContextualFragment(e))}))}activatePasteModal(t){const e=(TYPO3.lang["paste.modal.title.paste"]||"Paste record")+': "'+this.itemOnClipboardTitle+'"',a=TYPO3.lang["paste.modal.paste"]||"Do you want to paste the record to this position?";let n=[];n=[{text:TYPO3.lang["paste.modal.button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",trigger:(t,e)=>e.hideModal()},{text:TYPO3.lang["paste.modal.button.paste"]||"Paste",btnClass:"btn-"+Severity.getCssClass(SeverityEnum.warning),trigger:(e,a)=>{a.hideModal(),this.execute(t)}}],Modal.show(e,a,SeverityEnum.warning,n)}execute(t){const e=Paste.determineColumn(t),a=t.closest(this.elementIdentifier),n=a.data("uid");let s;s=void 0===n?parseInt(a.data("page"),10):0-parseInt(n,10);const o={CB:{paste:"tt_content|"+s,pad:"normal",update:{colPos:e,sys_language_uid:parseInt(t.closest("[data-language-uid]").data("language-uid"),10)}}};DataHandler.process(o).then((t=>{t.hasErrors||window.location.reload()}))}}export default Paste; \ No newline at end of file +import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import DataHandler from"@typo3/backend/ajax-data-handler.js";import{default as Modal}from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import"@typo3/backend/element/icon-element.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";class Paste{constructor(t,e,a){this.itemOnClipboardUid=0,this.itemOnClipboardTitle="",this.copyMode="",this.elementIdentifier=".t3js-page-ce",this.pasteAfterLinkTemplate="",this.pasteIntoLinkTemplate="",this.itemOnClipboardUid=t,this.itemOnClipboardTitle=e,this.copyMode=a,DocumentService.ready().then((()=>{$(".t3js-page-columns").length&&(this.generateButtonTemplates(),this.activatePasteIcons(),this.initializeEvents())}))}static determineColumn(t){const e=t.closest("[data-colpos]");return e.length&&"undefined"!==e.data("colpos")?e.data("colpos"):0}initializeEvents(){$(document).on("click",".t3js-paste",(t=>{t.preventDefault(),this.activatePasteModal($(t.currentTarget))}))}generateButtonTemplates(){this.itemOnClipboardUid&&(this.pasteAfterLinkTemplate='<button type="button" class="t3js-paste t3js-paste'+(this.copyMode?"-"+this.copyMode:"")+' t3js-paste-after btn btn-default btn-sm" title="'+TYPO3.lang?.pasteAfterRecord+'"><typo3-backend-icon identifier="actions-document-paste-into" size="small"></typo3-backend-icon></button>',this.pasteIntoLinkTemplate='<button type="button" class="t3js-paste t3js-paste'+(this.copyMode?"-"+this.copyMode:"")+' t3js-paste-into btn btn-default btn-sm" title="'+TYPO3.lang?.pasteIntoColumn+'"><typo3-backend-icon identifier="actions-document-paste-into" size="small"></typo3-backend-icon></button>')}activatePasteIcons(){this.pasteAfterLinkTemplate&&this.pasteIntoLinkTemplate&&document.querySelectorAll(".t3js-page-new-ce").forEach((t=>{const e=t.parentElement.dataset.page?this.pasteIntoLinkTemplate:this.pasteAfterLinkTemplate;t.append(document.createRange().createContextualFragment(e))}))}activatePasteModal(t){const e=(TYPO3.lang["paste.modal.title.paste"]||"Paste record")+': "'+this.itemOnClipboardTitle+'"',a=TYPO3.lang["paste.modal.paste"]||"Do you want to paste the record to this position?";let n=[];n=[{text:TYPO3.lang["paste.modal.button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",trigger:(t,e)=>e.hideModal()},{text:TYPO3.lang["paste.modal.button.paste"]||"Paste",btnClass:"btn-"+Severity.getCssClass(SeverityEnum.warning),trigger:(e,a)=>{a.hideModal(),this.execute(t)}}],Modal.show(e,a,SeverityEnum.warning,n)}execute(t){const e=Paste.determineColumn(t),a=t.closest(this.elementIdentifier),n=a.data("uid");let s;s=void 0===n?parseInt(a.data("page"),10):0-parseInt(n,10);const o={CB:{paste:"tt_content|"+s,pad:"normal",update:{colPos:e,sys_language_uid:parseInt(t.closest("[data-language-uid]").data("language-uid"),10)}}};DataHandler.process(o).then((t=>{t.hasErrors||window.location.reload()}))}}export default Paste; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/live-search/element/result/item/item-container.js b/typo3/sysext/backend/Resources/Public/JavaScript/live-search/element/result/item/item-container.js index e2db4013f983..00a096e9d02d 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/live-search/element/result/item/item-container.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/live-search/element/result/item/item-container.js @@ -12,7 +12,7 @@ */ var __decorate=function(e,t,r,n){var l,i=arguments.length,o=i<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,r):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,r,n);else for(var s=e.length-1;s>=0;s--)(l=e[s])&&(o=(i<3?l(o):i>3?l(t,r,o):l(t,r))||o);return i>3&&o&&Object.defineProperty(t,r,o),o};import"@typo3/backend/element/spinner-element.js";import LiveSearchConfigurator from"@typo3/backend/live-search/live-search-configurator.js";import{css,html,LitElement}from"lit";import{customElement,property}from"lit/decorators.js";import{until}from"lit/directives/until.js";import"@typo3/backend/live-search/element/provider/default-result-item.js";import"@typo3/backend/live-search/element/result/item/item.js";export const componentName="typo3-backend-live-search-result-item-container";let ItemContainer=class extends LitElement{constructor(){super(...arguments),this.results=null}connectedCallback(){super.connectedCallback(),this.addEventListener("scroll",this.onScroll)}disconnectedCallback(){this.removeEventListener("scroll",this.onScroll),super.disconnectedCallback()}createRenderRoot(){return this}render(){const e={};return this.results.forEach((t=>{t.typeLabel in e?e[t.typeLabel].push(t):e[t.typeLabel]=[t]})),html`<typo3-backend-live-search-result-list> ${this.renderGroupedResults(e)} - </typo3-backend-live-search-result-list>`}renderGroupedResults(e){const t=[];for(let[r,n]of Object.entries(e)){let e=n.length;t.push(html`<h6 class="livesearch-result-item-group-label">${r} (${e})</h6>`),t.push(...n.map((e=>html`${until(this.renderResultItem(e),html`<typo3-backend-spinner></typo3-backend-spinner>`)}`)))}return html`${t}`}async renderResultItem(e){const t=LiveSearchConfigurator.getRenderers();let r;return void 0!==t[e.provider]?(await import(t[e.provider].module),r=t[e.provider].callback(e)):r=html`<typo3-backend-live-search-result-item-default + </typo3-backend-live-search-result-list>`}renderGroupedResults(e){const t=[];for(const[r,n]of Object.entries(e)){const e=n.length;t.push(html`<h6 class="livesearch-result-item-group-label">${r} (${e})</h6>`),t.push(...n.map((e=>html`${until(this.renderResultItem(e),html`<typo3-backend-spinner></typo3-backend-spinner>`)}`)))}return html`${t}`}async renderResultItem(e){const t=LiveSearchConfigurator.getRenderers();let r;return void 0!==t[e.provider]?(await import(t[e.provider].module),r=t[e.provider].callback(e)):r=html`<typo3-backend-live-search-result-item-default title="${e.typeLabel}: ${e.itemTitle}" .icon="${e.icon}" .itemTitle="${e.itemTitle}" diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/live-search/live-search-shortcut.js b/typo3/sysext/backend/Resources/Public/JavaScript/live-search/live-search-shortcut.js index 7dd4913b00eb..2d2ea35c7bc1 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/live-search/live-search-shortcut.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/live-search/live-search-shortcut.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{BroadcastMessage}from"@typo3/backend/broadcast-message.js";import BroadcastService from"@typo3/backend/broadcast-service.js";import RegularEvent from"@typo3/core/event/regular-event.js";import Modal from"@typo3/backend/modal.js";var MODIFIER_KEYS;!function(e){e.META="Meta",e.CTRL="Control"}(MODIFIER_KEYS||(MODIFIER_KEYS={}));class LiveSearchShortcut{constructor(){const e=navigator.platform.toLowerCase().startsWith("mac")?MODIFIER_KEYS.META:MODIFIER_KEYS.CTRL;new RegularEvent("keydown",(t=>{if(t.repeat)return;if((e===MODIFIER_KEYS.META&&t.metaKey||e===MODIFIER_KEYS.CTRL&&t.ctrlKey)&&["k","K"].includes(t.key)){if(Modal.currentModal)return;t.preventDefault(),document.dispatchEvent(new CustomEvent("typo3:live-search:trigger-open")),BroadcastService.post(new BroadcastMessage("live-search","trigger-open",{}))}})).bindTo(document)}}export default new LiveSearchShortcut; \ No newline at end of file +import{BroadcastMessage}from"@typo3/backend/broadcast-message.js";import BroadcastService from"@typo3/backend/broadcast-service.js";import RegularEvent from"@typo3/core/event/regular-event.js";import Modal from"@typo3/backend/modal.js";var ModifierKeys;!function(e){e.META="Meta",e.CTRL="Control"}(ModifierKeys||(ModifierKeys={}));class LiveSearchShortcut{constructor(){const e=navigator.platform.toLowerCase().startsWith("mac")?ModifierKeys.META:ModifierKeys.CTRL;new RegularEvent("keydown",(r=>{if(r.repeat)return;if((e===ModifierKeys.META&&r.metaKey||e===ModifierKeys.CTRL&&r.ctrlKey)&&["k","K"].includes(r.key)){if(Modal.currentModal)return;r.preventDefault(),document.dispatchEvent(new CustomEvent("typo3:live-search:trigger-open")),BroadcastService.post(new BroadcastMessage("live-search","trigger-open",{}))}})).bindTo(document)}}export default new LiveSearchShortcut; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/login-refresh.js b/typo3/sysext/backend/Resources/Public/JavaScript/login-refresh.js index 3f521a6f7113..7f6f262620f1 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/login-refresh.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/login-refresh.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Notification from"@typo3/backend/notification.js";var MarkupIdentifiers;!function(e){e.loginrefresh="t3js-modal-loginrefresh",e.lockedModal="t3js-modal-backendlocked",e.loginFormModal="t3js-modal-backendloginform"}(MarkupIdentifiers||(MarkupIdentifiers={}));class LoginRefresh{constructor(){this.options={modalConfig:{backdrop:"static"}},this.intervalTime=60,this.intervalId=null,this.backendIsLocked=!1,this.isTimingOut=!1,this.$timeoutModal=null,this.$backendLockedModal=null,this.$loginForm=null,this.loginFramesetUrl="",this.logoutUrl="",this.submitForm=e=>{e.preventDefault();const o=this.$loginForm.find("form"),t=o.find("input[name=p_field]"),i=o.find("input[name=userident]"),s=t.val();if(""===s&&""===i.val())return Notification.error(TYPO3.lang["mess.refresh_login_failed"],TYPO3.lang["mess.refresh_login_emptyPassword"]),void t.focus();s&&(i.val(s),t.val(""));const a={login_status:"login"};for(let e of o.serializeArray())a[e.name]=e.value;new AjaxRequest(o.attr("action")).post(a).then((async e=>{(await e.resolve()).login.success?this.hideLoginForm():(Notification.error(TYPO3.lang["mess.refresh_login_failed"],TYPO3.lang["mess.refresh_login_failed_message"]),t.focus())}))},this.checkActiveSession=()=>{new AjaxRequest(TYPO3.settings.ajaxUrls.login_timedout).get().then((async e=>{const o=await e.resolve();o.login.locked?this.backendIsLocked||(this.backendIsLocked=!0,this.showBackendLockedModal()):this.backendIsLocked&&(this.backendIsLocked=!1,this.hideBackendLockedModal()),this.backendIsLocked||(o.login.timed_out||o.login.will_time_out)&&(o.login.timed_out?this.showLoginForm():this.showTimeoutModal())}))}}initialize(e){"object"==typeof e&&this.applyOptions(e),this.initializeTimeoutModal(),this.initializeBackendLockedModal(),this.initializeLoginForm(),this.startTask()}startTask(){if(null!==this.intervalId)return;let e=1e3*this.intervalTime;this.intervalId=setInterval(this.checkActiveSession,e)}stopTask(){clearInterval(this.intervalId),this.intervalId=null}setIntervalTime(e){this.intervalTime=Math.min(e,86400)}setLogoutUrl(e){this.logoutUrl=e}setLoginFramesetUrl(e){this.loginFramesetUrl=e}showTimeoutModal(){this.isTimingOut=!0,this.$timeoutModal.modal(this.options.modalConfig),this.$timeoutModal.modal("show"),this.fillProgressbar(this.$timeoutModal)}hideTimeoutModal(){this.isTimingOut=!1,this.$timeoutModal.modal("hide")}showBackendLockedModal(){this.$backendLockedModal.modal(this.options.modalConfig),this.$backendLockedModal.modal("show")}hideBackendLockedModal(){this.$backendLockedModal.modal("hide")}showLoginForm(){new AjaxRequest(TYPO3.settings.ajaxUrls.logout).get().then((()=>{TYPO3.configuration.showRefreshLoginPopup?this.showLoginPopup():(this.$loginForm.modal(this.options.modalConfig),this.$loginForm.modal("show"))}))}showLoginPopup(){const e=window.open(this.loginFramesetUrl,"relogin_"+Math.random().toString(16).slice(2),"height=450,width=700,status=0,menubar=0,location=1");e&&e.focus()}hideLoginForm(){this.$loginForm.modal("hide")}initializeBackendLockedModal(){this.$backendLockedModal=this.generateModal(MarkupIdentifiers.lockedModal),this.$backendLockedModal.find(".modal-header h4").text(TYPO3.lang["mess.please_wait"]),this.$backendLockedModal.find(".modal-body").append($("<p />").text(TYPO3.lang["mess.be_locked"])),this.$backendLockedModal.find(".modal-footer").remove(),$("body").append(this.$backendLockedModal)}initializeTimeoutModal(){this.$timeoutModal=this.generateModal(MarkupIdentifiers.loginrefresh),this.$timeoutModal.addClass("modal-severity-notice"),this.$timeoutModal.find(".modal-header h4").text(TYPO3.lang["mess.login_about_to_expire_title"]),this.$timeoutModal.find(".modal-body").append($("<p />").text(TYPO3.lang["mess.login_about_to_expire"]),$("<div />",{class:"progress"}).append($("<div />",{class:"progress-bar progress-bar-warning progress-bar-striped progress-bar-animated",role:"progressbar","aria-valuemin":"0","aria-valuemax":"100"}).append($("<span />",{class:"visually-hidden"})))),this.$timeoutModal.find(".modal-footer").append($("<button />",{class:"btn btn-default","data-action":"logout"}).text(TYPO3.lang["mess.refresh_login_logout_button"]).on("click",(()=>{top.location.href=this.logoutUrl})),$("<button />",{class:"btn btn-primary t3js-active","data-action":"refreshSession"}).text(TYPO3.lang["mess.refresh_login_refresh_button"]).on("click",(()=>{new AjaxRequest(TYPO3.settings.ajaxUrls.login_refresh).get().then((async e=>{const o=await e.resolve();this.hideTimeoutModal(),o.refresh.success||this.showLoginForm()}))}))),this.registerDefaultModalEvents(this.$timeoutModal),$("body").append(this.$timeoutModal)}initializeLoginForm(){if(TYPO3.configuration.showRefreshLoginPopup)return;this.$loginForm=this.generateModal(MarkupIdentifiers.loginFormModal),this.$loginForm.addClass("modal-notice");let e=String(TYPO3.lang["mess.refresh_login_title"]).replace("%s",TYPO3.configuration.username);this.$loginForm.find(".modal-header h4").text(e),this.$loginForm.find(".modal-body").append($("<p />").text(TYPO3.lang["mess.login_expired"]),$("<form />",{id:"beLoginRefresh",method:"POST",action:TYPO3.settings.ajaxUrls.login}).append($("<div />").append($("<input />",{type:"text",name:"username",class:"d-none",value:TYPO3.configuration.username}),$("<input />",{type:"hidden",name:"userident",id:"t3-loginrefresh-userident"})),$("<div />",{class:"form-group"}).append($("<input />",{type:"password",name:"p_field",autofocus:"autofocus",class:"form-control",placeholder:TYPO3.lang["mess.refresh_login_password"]})))),this.$loginForm.find(".modal-body .d-none").attr("autocomplete","username"),this.$loginForm.find(".modal-body .form-control").attr("autocomplete","current-password"),this.$loginForm.find(".modal-footer").append($("<a />",{href:this.logoutUrl,class:"btn btn-default"}).text(TYPO3.lang["mess.refresh_exit_button"]),$("<button />",{type:"submit",class:"btn btn-primary","data-action":"refreshSession",form:"beLoginRefresh"}).text(TYPO3.lang["mess.refresh_login_button"]).on("click",(()=>{this.$loginForm.find("form").trigger("submit")}))),this.registerDefaultModalEvents(this.$loginForm).on("submit",this.submitForm),$("body").append(this.$loginForm)}generateModal(e){return $("<div />",{id:e,class:"t3js-modal "+e+" modal modal-type-default modal-severity-notice modal-style-light modal-size-small fade"}).append($("<div />",{class:"modal-dialog"}).append($("<div />",{class:"modal-content"}).append($("<div />",{class:"modal-header"}).append($("<h4 />",{class:"modal-title"})),$("<div />",{class:"modal-body"}),$("<div />",{class:"modal-footer"}))))}fillProgressbar(e){if(!this.isTimingOut)return;let o=0;const t=e.find(".progress-bar"),i=t.children(".visually-hidden"),s=setInterval((()=>{const e=o>=100;!this.isTimingOut||e?(clearInterval(s),e&&(this.hideTimeoutModal(),this.showLoginForm()),o=0):o+=1;const a=o+"%";t.css("width",a),i.text(a)}),300)}registerDefaultModalEvents(e){return e.on("hidden.bs.modal",(()=>{this.startTask()})).on("shown.bs.modal",(()=>{this.stopTask(),this.$timeoutModal.find(".modal-footer .t3js-active").first().focus()})),e}applyOptions(e){void 0!==e.intervalTime&&this.setIntervalTime(e.intervalTime),void 0!==e.loginFramesetUrl&&this.setLoginFramesetUrl(e.loginFramesetUrl),void 0!==e.logoutUrl&&this.setLogoutUrl(e.logoutUrl)}}let loginRefreshObject;try{window.opener&&window.opener.TYPO3&&window.opener.TYPO3.LoginRefresh&&(loginRefreshObject=window.opener.TYPO3.LoginRefresh),parent&&parent.window.TYPO3&&parent.window.TYPO3.LoginRefresh&&(loginRefreshObject=parent.window.TYPO3.LoginRefresh),top&&top.TYPO3&&top.TYPO3.LoginRefresh&&(loginRefreshObject=top.TYPO3.LoginRefresh)}catch{}loginRefreshObject||(loginRefreshObject=new LoginRefresh,"undefined"!=typeof TYPO3&&(TYPO3.LoginRefresh=loginRefreshObject));export default loginRefreshObject; \ No newline at end of file +import $ from"jquery";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Notification from"@typo3/backend/notification.js";var MarkupIdentifiers;!function(e){e.loginrefresh="t3js-modal-loginrefresh",e.lockedModal="t3js-modal-backendlocked",e.loginFormModal="t3js-modal-backendloginform"}(MarkupIdentifiers||(MarkupIdentifiers={}));class LoginRefresh{constructor(){this.options={modalConfig:{backdrop:"static"}},this.intervalTime=60,this.intervalId=null,this.backendIsLocked=!1,this.isTimingOut=!1,this.$timeoutModal=null,this.$backendLockedModal=null,this.$loginForm=null,this.loginFramesetUrl="",this.logoutUrl="",this.submitForm=e=>{e.preventDefault();const o=this.$loginForm.find("form"),t=o.find("input[name=p_field]"),i=o.find("input[name=userident]"),s=t.val();if(""===s&&""===i.val())return Notification.error(TYPO3.lang["mess.refresh_login_failed"],TYPO3.lang["mess.refresh_login_emptyPassword"]),void t.focus();s&&(i.val(s),t.val(""));const a={login_status:"login"};for(const e of o.serializeArray())a[e.name]=e.value;new AjaxRequest(o.attr("action")).post(a).then((async e=>{(await e.resolve()).login.success?this.hideLoginForm():(Notification.error(TYPO3.lang["mess.refresh_login_failed"],TYPO3.lang["mess.refresh_login_failed_message"]),t.focus())}))},this.checkActiveSession=()=>{new AjaxRequest(TYPO3.settings.ajaxUrls.login_timedout).get().then((async e=>{const o=await e.resolve();o.login.locked?this.backendIsLocked||(this.backendIsLocked=!0,this.showBackendLockedModal()):this.backendIsLocked&&(this.backendIsLocked=!1,this.hideBackendLockedModal()),this.backendIsLocked||(o.login.timed_out||o.login.will_time_out)&&(o.login.timed_out?this.showLoginForm():this.showTimeoutModal())}))}}initialize(e){"object"==typeof e&&this.applyOptions(e),this.initializeTimeoutModal(),this.initializeBackendLockedModal(),this.initializeLoginForm(),this.startTask()}startTask(){if(null!==this.intervalId)return;const e=1e3*this.intervalTime;this.intervalId=setInterval(this.checkActiveSession,e)}stopTask(){clearInterval(this.intervalId),this.intervalId=null}setIntervalTime(e){this.intervalTime=Math.min(e,86400)}setLogoutUrl(e){this.logoutUrl=e}setLoginFramesetUrl(e){this.loginFramesetUrl=e}showTimeoutModal(){this.isTimingOut=!0,this.$timeoutModal.modal(this.options.modalConfig),this.$timeoutModal.modal("show"),this.fillProgressbar(this.$timeoutModal)}hideTimeoutModal(){this.isTimingOut=!1,this.$timeoutModal.modal("hide")}showBackendLockedModal(){this.$backendLockedModal.modal(this.options.modalConfig),this.$backendLockedModal.modal("show")}hideBackendLockedModal(){this.$backendLockedModal.modal("hide")}showLoginForm(){new AjaxRequest(TYPO3.settings.ajaxUrls.logout).get().then((()=>{TYPO3.configuration.showRefreshLoginPopup?this.showLoginPopup():(this.$loginForm.modal(this.options.modalConfig),this.$loginForm.modal("show"))}))}showLoginPopup(){const e=window.open(this.loginFramesetUrl,"relogin_"+Math.random().toString(16).slice(2),"height=450,width=700,status=0,menubar=0,location=1");e&&e.focus()}hideLoginForm(){this.$loginForm.modal("hide")}initializeBackendLockedModal(){this.$backendLockedModal=this.generateModal(MarkupIdentifiers.lockedModal),this.$backendLockedModal.find(".modal-header h4").text(TYPO3.lang["mess.please_wait"]),this.$backendLockedModal.find(".modal-body").append($("<p />").text(TYPO3.lang["mess.be_locked"])),this.$backendLockedModal.find(".modal-footer").remove(),$("body").append(this.$backendLockedModal)}initializeTimeoutModal(){this.$timeoutModal=this.generateModal(MarkupIdentifiers.loginrefresh),this.$timeoutModal.addClass("modal-severity-notice"),this.$timeoutModal.find(".modal-header h4").text(TYPO3.lang["mess.login_about_to_expire_title"]),this.$timeoutModal.find(".modal-body").append($("<p />").text(TYPO3.lang["mess.login_about_to_expire"]),$("<div />",{class:"progress"}).append($("<div />",{class:"progress-bar progress-bar-warning progress-bar-striped progress-bar-animated",role:"progressbar","aria-valuemin":"0","aria-valuemax":"100"}).append($("<span />",{class:"visually-hidden"})))),this.$timeoutModal.find(".modal-footer").append($("<button />",{class:"btn btn-default","data-action":"logout"}).text(TYPO3.lang["mess.refresh_login_logout_button"]).on("click",(()=>{top.location.href=this.logoutUrl})),$("<button />",{class:"btn btn-primary t3js-active","data-action":"refreshSession"}).text(TYPO3.lang["mess.refresh_login_refresh_button"]).on("click",(()=>{new AjaxRequest(TYPO3.settings.ajaxUrls.login_refresh).get().then((async e=>{const o=await e.resolve();this.hideTimeoutModal(),o.refresh.success||this.showLoginForm()}))}))),this.registerDefaultModalEvents(this.$timeoutModal),$("body").append(this.$timeoutModal)}initializeLoginForm(){if(TYPO3.configuration.showRefreshLoginPopup)return;this.$loginForm=this.generateModal(MarkupIdentifiers.loginFormModal),this.$loginForm.addClass("modal-notice");const e=String(TYPO3.lang["mess.refresh_login_title"]).replace("%s",TYPO3.configuration.username);this.$loginForm.find(".modal-header h4").text(e),this.$loginForm.find(".modal-body").append($("<p />").text(TYPO3.lang["mess.login_expired"]),$("<form />",{id:"beLoginRefresh",method:"POST",action:TYPO3.settings.ajaxUrls.login}).append($("<div />").append($("<input />",{type:"text",name:"username",class:"d-none",value:TYPO3.configuration.username}),$("<input />",{type:"hidden",name:"userident",id:"t3-loginrefresh-userident"})),$("<div />",{class:"form-group"}).append($("<input />",{type:"password",name:"p_field",autofocus:"autofocus",class:"form-control",placeholder:TYPO3.lang["mess.refresh_login_password"]})))),this.$loginForm.find(".modal-body .d-none").attr("autocomplete","username"),this.$loginForm.find(".modal-body .form-control").attr("autocomplete","current-password"),this.$loginForm.find(".modal-footer").append($("<a />",{href:this.logoutUrl,class:"btn btn-default"}).text(TYPO3.lang["mess.refresh_exit_button"]),$("<button />",{type:"submit",class:"btn btn-primary","data-action":"refreshSession",form:"beLoginRefresh"}).text(TYPO3.lang["mess.refresh_login_button"]).on("click",(()=>{this.$loginForm.find("form").trigger("submit")}))),this.registerDefaultModalEvents(this.$loginForm).on("submit",this.submitForm),$("body").append(this.$loginForm)}generateModal(e){return $("<div />",{id:e,class:"t3js-modal "+e+" modal modal-type-default modal-severity-notice modal-style-light modal-size-small fade"}).append($("<div />",{class:"modal-dialog"}).append($("<div />",{class:"modal-content"}).append($("<div />",{class:"modal-header"}).append($("<h4 />",{class:"modal-title"})),$("<div />",{class:"modal-body"}),$("<div />",{class:"modal-footer"}))))}fillProgressbar(e){if(!this.isTimingOut)return;let o=0;const t=e.find(".progress-bar"),i=t.children(".visually-hidden"),s=setInterval((()=>{const e=o>=100;!this.isTimingOut||e?(clearInterval(s),e&&(this.hideTimeoutModal(),this.showLoginForm()),o=0):o+=1;const a=o+"%";t.css("width",a),i.text(a)}),300)}registerDefaultModalEvents(e){return e.on("hidden.bs.modal",(()=>{this.startTask()})).on("shown.bs.modal",(()=>{this.stopTask(),this.$timeoutModal.find(".modal-footer .t3js-active").first().focus()})),e}applyOptions(e){void 0!==e.intervalTime&&this.setIntervalTime(e.intervalTime),void 0!==e.loginFramesetUrl&&this.setLoginFramesetUrl(e.loginFramesetUrl),void 0!==e.logoutUrl&&this.setLogoutUrl(e.logoutUrl)}}let loginRefreshObject;try{window.opener&&window.opener.TYPO3&&window.opener.TYPO3.LoginRefresh&&(loginRefreshObject=window.opener.TYPO3.LoginRefresh),parent&&parent.window.TYPO3&&parent.window.TYPO3.LoginRefresh&&(loginRefreshObject=parent.window.TYPO3.LoginRefresh),top&&top.TYPO3&&top.TYPO3.LoginRefresh&&(loginRefreshObject=top.TYPO3.LoginRefresh)}catch{}loginRefreshObject||(loginRefreshObject=new LoginRefresh,"undefined"!=typeof TYPO3&&(TYPO3.LoginRefresh=loginRefreshObject));export default loginRefreshObject; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/modal.js b/typo3/sysext/backend/Resources/Public/JavaScript/modal.js index da75ea5b6035..9fbefb1d1a48 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/modal.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/modal.js @@ -10,7 +10,7 @@ * * The TYPO3 project - inspiring people to share! */ -var Identifiers,__decorate=function(t,e,a,o){var s,n=arguments.length,i=n<3?e:null===o?o=Object.getOwnPropertyDescriptor(e,a):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)i=Reflect.decorate(t,e,a,o);else for(var l=t.length-1;l>=0;l--)(s=t[l])&&(i=(n<3?s(i):n>3?s(e,a,i):s(e,a))||i);return n>3&&i&&Object.defineProperty(e,a,i),i};import{Modal as BootstrapModal}from"bootstrap";import{html,nothing,LitElement}from"lit";import{customElement,property,state}from"lit/decorators.js";import{unsafeHTML}from"lit/directives/unsafe-html.js";import{classMap}from"lit/directives/class-map.js";import{styleMap}from"lit/directives/style-map.js";import{ifDefined}from"lit/directives/if-defined.js";import{classesArrayToClassInfo}from"@typo3/core/lit-helper.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Severity from"@typo3/backend/severity.js";import"@typo3/backend/element/icon-element.js";import"@typo3/backend/element/spinner-element.js";!function(t){t.modal=".t3js-modal",t.content=".t3js-modal-content",t.close=".t3js-modal-close",t.body=".t3js-modal-body",t.footer=".t3js-modal-footer"}(Identifiers||(Identifiers={}));export var Sizes;!function(t){t.small="small",t.default="default",t.medium="medium",t.large="large",t.full="full"}(Sizes||(Sizes={}));export var Styles;!function(t){t.default="default",t.light="light",t.dark="dark"}(Styles||(Styles={}));export var Types;!function(t){t.default="default",t.template="template",t.ajax="ajax",t.iframe="iframe"}(Types||(Types={}));export var PostActionModalBehavior;!function(t){t[t.KEEP_OPEN=0]="KEEP_OPEN",t[t.CLOSE=1]="CLOSE"}(PostActionModalBehavior||(PostActionModalBehavior={}));let ModalElement=class extends LitElement{constructor(){super(...arguments),this.modalTitle="",this.content="",this.type=Types.default,this.severity=SeverityEnum.notice,this.variant=Styles.default,this.size=Sizes.default,this.zindex=5e3,this.staticBackdrop=!1,this.additionalCssClasses=[],this.buttons=[],this.templateResultContent=null,this.activeButton=null,this.bootstrapModal=null,this.callback=null,this.ajaxCallback=null,this.userData={}}setContent(t){this.templateResultContent=t}hideModal(){this.bootstrapModal&&this.bootstrapModal.hide()}createRenderRoot(){return this}firstUpdated(){this.bootstrapModal=new BootstrapModal(this.renderRoot.querySelector(Identifiers.modal),{}),this.bootstrapModal.show(),this.callback&&this.callback(this)}updated(t){t.has("templateResultContent")&&this.dispatchEvent(new CustomEvent("modal-updated",{bubbles:!0}))}render(){const t={zIndex:this.zindex.toString()},e=classesArrayToClassInfo([`modal-type-${this.type}`,`modal-severity-${Severity.getCssClass(this.severity)}`,`modal-style-${this.variant}`,`modal-size-${this.size}`,...this.additionalCssClasses]);return html` +var Identifiers,__decorate=function(t,e,a,o){var s,n=arguments.length,i=n<3?e:null===o?o=Object.getOwnPropertyDescriptor(e,a):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)i=Reflect.decorate(t,e,a,o);else for(var l=t.length-1;l>=0;l--)(s=t[l])&&(i=(n<3?s(i):n>3?s(e,a,i):s(e,a))||i);return n>3&&i&&Object.defineProperty(e,a,i),i};import{Modal as BootstrapModal}from"bootstrap";import{html,nothing,LitElement}from"lit";import{customElement,property,state}from"lit/decorators.js";import{unsafeHTML}from"lit/directives/unsafe-html.js";import{classMap}from"lit/directives/class-map.js";import{styleMap}from"lit/directives/style-map.js";import{ifDefined}from"lit/directives/if-defined.js";import{classesArrayToClassInfo}from"@typo3/core/lit-helper.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Severity from"@typo3/backend/severity.js";import"@typo3/backend/element/icon-element.js";import"@typo3/backend/element/spinner-element.js";!function(t){t.modal=".t3js-modal",t.content=".t3js-modal-content",t.close=".t3js-modal-close",t.body=".t3js-modal-body",t.footer=".t3js-modal-footer"}(Identifiers||(Identifiers={}));export var Sizes;!function(t){t.small="small",t.default="default",t.medium="medium",t.large="large",t.full="full"}(Sizes||(Sizes={}));export var Styles;!function(t){t.default="default",t.light="light",t.dark="dark"}(Styles||(Styles={}));export var Types;!function(t){t.default="default",t.template="template",t.ajax="ajax",t.iframe="iframe"}(Types||(Types={}));let ModalElement=class extends LitElement{constructor(){super(...arguments),this.modalTitle="",this.content="",this.type=Types.default,this.severity=SeverityEnum.notice,this.variant=Styles.default,this.size=Sizes.default,this.zindex=5e3,this.staticBackdrop=!1,this.additionalCssClasses=[],this.buttons=[],this.templateResultContent=null,this.activeButton=null,this.bootstrapModal=null,this.callback=null,this.ajaxCallback=null,this.userData={}}setContent(t){this.templateResultContent=t}hideModal(){this.bootstrapModal&&this.bootstrapModal.hide()}createRenderRoot(){return this}firstUpdated(){this.bootstrapModal=new BootstrapModal(this.renderRoot.querySelector(Identifiers.modal),{}),this.bootstrapModal.show(),this.callback&&this.callback(this)}updated(t){t.has("templateResultContent")&&this.dispatchEvent(new CustomEvent("modal-updated",{bubbles:!0}))}render(){const t={zIndex:this.zindex.toString()},e=classesArrayToClassInfo([`modal-type-${this.type}`,`modal-severity-${Severity.getCssClass(this.severity)}`,`modal-style-${this.variant}`,`modal-size-${this.size}`,...this.additionalCssClasses]);return html` <div tabindex="-1" class="modal fade t3js-modal ${classMap(e)}" @@ -41,7 +41,7 @@ var Identifiers,__decorate=function(t,e,a,o){var s,n=arguments.length,i=n<3?e:nu </div> </div> </div> - `}_buttonClick(t,e){const a=t.currentTarget;e.action?(this.activeButton=e,e.action.execute(a).then(((t=PostActionModalBehavior.CLOSE)=>{this.activeButton=null;Object.values(PostActionModalBehavior).includes(t)||(console.warn(`postActionBehavior ${t} provided but expected any of ${Object.values(PostActionModalBehavior).join(",")}. Falling back to PostActionModalBehavior.CLOSE`),t=PostActionModalBehavior.CLOSE),t===PostActionModalBehavior.CLOSE&&this.bootstrapModal.hide()}))):e.trigger&&e.trigger(t,this),a.dispatchEvent(new CustomEvent("button.clicked",{bubbles:!0}))}renderAjaxBody(){return null===this.templateResultContent?(new AjaxRequest(this.content).get().then((async t=>{const e=await t.raw().text();this.templateResultContent=html`${unsafeHTML(e)}`,this.updateComplete.then((()=>{this.ajaxCallback&&this.ajaxCallback(this),this.dispatchEvent(new CustomEvent("modal-loaded"))}))})).catch((async t=>{const e=await t.raw().text();this.templateResultContent=e?html`${unsafeHTML(e)}`:html`<p><strong>Oops, received a ${t.response.status} response from </strong> <span class="text-break">${this.content}</span>.</p>`})),html`<div class="modal-loading"><typo3-backend-spinner size="default"></typo3-backend-spinner></div>`):this.templateResultContent}renderModalBody(){if(this.type===Types.ajax)return this.renderAjaxBody();if(this.type===Types.iframe){const t=t=>{const e=t.currentTarget;this.modalTitle=e.contentDocument.title,e.contentDocument.body.classList.add("with-overflow")};return html` + `}_buttonClick(t,e){const a=t.currentTarget;e.action?(this.activeButton=e,e.action.execute(a).then((()=>this.bootstrapModal.hide()))):e.trigger&&e.trigger(t,this),a.dispatchEvent(new CustomEvent("button.clicked",{bubbles:!0}))}renderAjaxBody(){return null===this.templateResultContent?(new AjaxRequest(this.content).get().then((async t=>{const e=await t.raw().text();this.templateResultContent=html`${unsafeHTML(e)}`,this.updateComplete.then((()=>{this.ajaxCallback&&this.ajaxCallback(this),this.dispatchEvent(new CustomEvent("modal-loaded"))}))})).catch((async t=>{const e=await t.raw().text();this.templateResultContent=e?html`${unsafeHTML(e)}`:html`<p><strong>Oops, received a ${t.response.status} response from </strong> <span class="text-break">${this.content}</span>.</p>`})),html`<div class="modal-loading"><typo3-backend-spinner size="default"></typo3-backend-spinner></div>`):this.templateResultContent}renderModalBody(){if(this.type===Types.ajax)return this.renderAjaxBody();if(this.type===Types.iframe){const t=t=>{const e=t.currentTarget;this.modalTitle=e.contentDocument.title,e.contentDocument.body.classList.add("with-overflow")};return html` <iframe src="${this.content}" name="modal_frame" class="modal-iframe t3js-modal-iframe" @load=${t}></iframe> `}return this.type===Types.template?this.templateResultContent:html`<p>${this.content}</p>`}renderModalButton(t){const e={btn:!0,[t.btnClass||"btn-default"]:!0,"t3js-active":t.active,disabled:this.activeButton&&this.activeButton!==t};return html` <button class=${classMap(e)} @@ -50,4 +50,4 @@ var Identifiers,__decorate=function(t,e,a,o){var s,n=arguments.length,i=n<3?e:nu ${t.icon?html`<typo3-backend-icon identifier="${t.icon}" size="small"></typo3-backend-icon>`:nothing} ${t.text} </button> - `}trigger(t){this.dispatchEvent(new CustomEvent(t,{bubbles:!0,composed:!0}))}};__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"modalTitle",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"content",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"type",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"severity",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"variant",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"size",void 0),__decorate([property({type:Number,reflect:!0})],ModalElement.prototype,"zindex",void 0),__decorate([property({type:Boolean})],ModalElement.prototype,"staticBackdrop",void 0),__decorate([property({type:Array})],ModalElement.prototype,"additionalCssClasses",void 0),__decorate([property({type:Array,attribute:!1})],ModalElement.prototype,"buttons",void 0),__decorate([state()],ModalElement.prototype,"templateResultContent",void 0),__decorate([state()],ModalElement.prototype,"activeButton",void 0),ModalElement=__decorate([customElement("typo3-backend-modal")],ModalElement);export{ModalElement};class Modal{constructor(){this.sizes=Sizes,this.styles=Styles,this.types=Types,this.currentModal=null,this.instances=[],this.defaultConfiguration={type:Types.default,title:"Information",content:"No content provided, please check your <code>Modal</code> configuration.",severity:SeverityEnum.notice,buttons:[],style:Styles.default,size:Sizes.default,additionalCssClasses:[],callback:null,ajaxCallback:null,staticBackdrop:!1},this.initializeMarkupTrigger(document)}static createModalResponseEventFromElement(t,e){return t.dataset.eventName?new CustomEvent(t.dataset.eventName,{bubbles:!0,detail:{result:e,payload:t.dataset.eventPayload||null}}):null}dismiss(){this.currentModal&&this.currentModal.hideModal()}confirm(t,e,a=SeverityEnum.warning,o=[],s){0===o.length&&o.push({text:TYPO3.lang["button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(a),name:"ok"});const n=this.advanced({title:t,content:e,severity:a,buttons:o,additionalCssClasses:s});return n.addEventListener("button.clicked",(t=>{const e=t.target;"cancel"===e.getAttribute("name")?e.dispatchEvent(new CustomEvent("confirm.button.cancel",{bubbles:!0})):"ok"===e.getAttribute("name")&&e.dispatchEvent(new CustomEvent("confirm.button.ok",{bubbles:!0}))})),n}loadUrl(t,e=SeverityEnum.info,a,o,s){return this.advanced({type:Types.ajax,title:t,severity:e,buttons:a,ajaxCallback:s,content:o})}show(t,e,a=SeverityEnum.info,o,s){return this.advanced({type:Types.default,title:t,content:e,severity:a,buttons:o,additionalCssClasses:s})}advanced(t){return t.type="string"==typeof t.type&&t.type in Types?t.type:this.defaultConfiguration.type,t.title="string"==typeof t.title?t.title:this.defaultConfiguration.title,t.content="string"==typeof t.content||"object"==typeof t.content?t.content:this.defaultConfiguration.content,t.severity=void 0!==t.severity?t.severity:this.defaultConfiguration.severity,t.buttons=t.buttons||this.defaultConfiguration.buttons,t.size="string"==typeof t.size&&t.size in Sizes?t.size:this.defaultConfiguration.size,t.style="string"==typeof t.style&&t.style in Styles?t.style:this.defaultConfiguration.style,t.additionalCssClasses=t.additionalCssClasses||this.defaultConfiguration.additionalCssClasses,t.callback="function"==typeof t.callback?t.callback:this.defaultConfiguration.callback,t.ajaxCallback="function"==typeof t.ajaxCallback?t.ajaxCallback:this.defaultConfiguration.ajaxCallback,t.staticBackdrop=t.staticBackdrop||this.defaultConfiguration.staticBackdrop,this.generate(t)}setButtons(t){return this.currentModal.buttons=t,this.currentModal}initializeMarkupTrigger(t){new RegularEvent("click",((t,e)=>{t.preventDefault();const a=e.dataset.bsContent||TYPO3.lang["message.confirmation"]||"Are you sure?";let o=SeverityEnum.info;if(e.dataset.severity in SeverityEnum){const t=e.dataset.severity;o=SeverityEnum[t]}let s=e.dataset.url||null;if(null!==s){const t=s.includes("?")?"&":"?";s=s+t+new URLSearchParams(e.dataset).toString()}this.advanced({type:null!==s?Types.ajax:Types.default,title:e.dataset.title||"Alert",content:null!==s?s:a,severity:o,staticBackdrop:void 0!==e.dataset.staticBackdrop,buttons:[{text:e.dataset.buttonCloseText||TYPO3.lang["button.close"]||"Close",active:!0,btnClass:"btn-default",trigger:(t,a)=>{a.hideModal();const o=Modal.createModalResponseEventFromElement(e,!1);null!==o&&e.dispatchEvent(o)}},{text:e.dataset.buttonOkText||TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(o),trigger:(t,a)=>{a.hideModal();const o=Modal.createModalResponseEventFromElement(e,!0);null!==o&&e.dispatchEvent(o);let s=e.dataset.uri||e.dataset.href||e.getAttribute("href");s&&"#"!==s&&(e.ownerDocument.location.href=s),"submit"===e.getAttribute("type")&&(e.closest("form")?.submit(),"BUTTON"===e.tagName&&e.hasAttribute("form")&&e.ownerDocument.querySelector("form#"+e.getAttribute("form"))?.submit()),e.dataset.targetForm&&e.ownerDocument.querySelector("form#"+e.dataset.targetForm)?.submit()}}]})})).delegateTo(t,".t3js-modal-trigger")}generate(t){const e=document.createElement("typo3-backend-modal");return e.type=t.type,"string"==typeof t.content?e.content=t.content:t.type===Types.default&&(e.type=Types.template,e.templateResultContent=t.content),e.severity=t.severity,e.variant=t.style,e.size=t.size,e.modalTitle=t.title,e.additionalCssClasses=t.additionalCssClasses,e.buttons=t.buttons,e.staticBackdrop=t.staticBackdrop,t.callback&&(e.callback=t.callback),t.ajaxCallback&&(e.ajaxCallback=t.ajaxCallback),e.addEventListener("typo3-modal-shown",(()=>{const t=e.nextElementSibling,a=1e3+10*this.instances.length;e.zindex=a;const o=a-5;t.style.zIndex=o.toString(),e.querySelector(`${Identifiers.footer} .t3js-active`)?.focus()})),e.addEventListener("typo3-modal-hide",(()=>{if(this.instances.length>0){const t=this.instances.length-1;this.instances.splice(t,1),this.currentModal=this.instances[t-1]}})),e.addEventListener("typo3-modal-hidden",(t=>{e.remove(),this.instances.length>0&&document.body.classList.add("modal-open")})),e.addEventListener("typo3-modal-show",(()=>{this.currentModal=e,this.instances.push(e)})),document.body.appendChild(e),e}}let modalObject=null;try{parent&&parent.window.TYPO3&&parent.window.TYPO3.Modal?(parent.window.TYPO3.Modal.initializeMarkupTrigger(document),modalObject=parent.window.TYPO3.Modal):top&&top.TYPO3.Modal&&(top.TYPO3.Modal.initializeMarkupTrigger(document),modalObject=top.TYPO3.Modal)}catch{}modalObject||(modalObject=new Modal,TYPO3.Modal=modalObject);export default modalObject; \ No newline at end of file + `}trigger(t){this.dispatchEvent(new CustomEvent(t,{bubbles:!0,composed:!0}))}};__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"modalTitle",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"content",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"type",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"severity",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"variant",void 0),__decorate([property({type:String,reflect:!0})],ModalElement.prototype,"size",void 0),__decorate([property({type:Number,reflect:!0})],ModalElement.prototype,"zindex",void 0),__decorate([property({type:Boolean})],ModalElement.prototype,"staticBackdrop",void 0),__decorate([property({type:Array})],ModalElement.prototype,"additionalCssClasses",void 0),__decorate([property({type:Array,attribute:!1})],ModalElement.prototype,"buttons",void 0),__decorate([state()],ModalElement.prototype,"templateResultContent",void 0),__decorate([state()],ModalElement.prototype,"activeButton",void 0),ModalElement=__decorate([customElement("typo3-backend-modal")],ModalElement);export{ModalElement};class Modal{constructor(){this.sizes=Sizes,this.styles=Styles,this.types=Types,this.currentModal=null,this.instances=[],this.defaultConfiguration={type:Types.default,title:"Information",content:"No content provided, please check your <code>Modal</code> configuration.",severity:SeverityEnum.notice,buttons:[],style:Styles.default,size:Sizes.default,additionalCssClasses:[],callback:null,ajaxCallback:null,staticBackdrop:!1},this.initializeMarkupTrigger(document)}static createModalResponseEventFromElement(t,e){return t.dataset.eventName?new CustomEvent(t.dataset.eventName,{bubbles:!0,detail:{result:e,payload:t.dataset.eventPayload||null}}):null}dismiss(){this.currentModal&&this.currentModal.hideModal()}confirm(t,e,a=SeverityEnum.warning,o=[],s){0===o.length&&o.push({text:TYPO3.lang["button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(a),name:"ok"});const n=this.advanced({title:t,content:e,severity:a,buttons:o,additionalCssClasses:s});return n.addEventListener("button.clicked",(t=>{const e=t.target;"cancel"===e.getAttribute("name")?e.dispatchEvent(new CustomEvent("confirm.button.cancel",{bubbles:!0})):"ok"===e.getAttribute("name")&&e.dispatchEvent(new CustomEvent("confirm.button.ok",{bubbles:!0}))})),n}loadUrl(t,e=SeverityEnum.info,a,o,s){return this.advanced({type:Types.ajax,title:t,severity:e,buttons:a,ajaxCallback:s,content:o})}show(t,e,a=SeverityEnum.info,o,s){return this.advanced({type:Types.default,title:t,content:e,severity:a,buttons:o,additionalCssClasses:s})}advanced(t){return t.type="string"==typeof t.type&&t.type in Types?t.type:this.defaultConfiguration.type,t.title="string"==typeof t.title?t.title:this.defaultConfiguration.title,t.content="string"==typeof t.content||"object"==typeof t.content?t.content:this.defaultConfiguration.content,t.severity=void 0!==t.severity?t.severity:this.defaultConfiguration.severity,t.buttons=t.buttons||this.defaultConfiguration.buttons,t.size="string"==typeof t.size&&t.size in Sizes?t.size:this.defaultConfiguration.size,t.style="string"==typeof t.style&&t.style in Styles?t.style:this.defaultConfiguration.style,t.additionalCssClasses=t.additionalCssClasses||this.defaultConfiguration.additionalCssClasses,t.callback="function"==typeof t.callback?t.callback:this.defaultConfiguration.callback,t.ajaxCallback="function"==typeof t.ajaxCallback?t.ajaxCallback:this.defaultConfiguration.ajaxCallback,t.staticBackdrop=t.staticBackdrop||this.defaultConfiguration.staticBackdrop,this.generate(t)}setButtons(t){return this.currentModal.buttons=t,this.currentModal}initializeMarkupTrigger(t){new RegularEvent("click",((t,e)=>{t.preventDefault();const a=e.dataset.bsContent||TYPO3.lang["message.confirmation"]||"Are you sure?";let o=SeverityEnum.info;if(e.dataset.severity in SeverityEnum){const t=e.dataset.severity;o=SeverityEnum[t]}let s=e.dataset.url||null;if(null!==s){const t=s.includes("?")?"&":"?";s=s+t+new URLSearchParams(e.dataset).toString()}this.advanced({type:null!==s?Types.ajax:Types.default,title:e.dataset.title||"Alert",content:null!==s?s:a,severity:o,staticBackdrop:void 0!==e.dataset.staticBackdrop,buttons:[{text:e.dataset.buttonCloseText||TYPO3.lang["button.close"]||"Close",active:!0,btnClass:"btn-default",trigger:(t,a)=>{a.hideModal();const o=Modal.createModalResponseEventFromElement(e,!1);null!==o&&e.dispatchEvent(o)}},{text:e.dataset.buttonOkText||TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(o),trigger:(t,a)=>{a.hideModal();const o=Modal.createModalResponseEventFromElement(e,!0);null!==o&&e.dispatchEvent(o);const s=e.dataset.uri||e.dataset.href||e.getAttribute("href");s&&"#"!==s&&(e.ownerDocument.location.href=s),"submit"===e.getAttribute("type")&&(e.closest("form")?.submit(),"BUTTON"===e.tagName&&e.hasAttribute("form")&&e.ownerDocument.querySelector("form#"+e.getAttribute("form"))?.submit()),e.dataset.targetForm&&e.ownerDocument.querySelector("form#"+e.dataset.targetForm)?.submit()}}]})})).delegateTo(t,".t3js-modal-trigger")}generate(t){const e=document.createElement("typo3-backend-modal");return e.type=t.type,"string"==typeof t.content?e.content=t.content:t.type===Types.default&&(e.type=Types.template,e.templateResultContent=t.content),e.severity=t.severity,e.variant=t.style,e.size=t.size,e.modalTitle=t.title,e.additionalCssClasses=t.additionalCssClasses,e.buttons=t.buttons,e.staticBackdrop=t.staticBackdrop,t.callback&&(e.callback=t.callback),t.ajaxCallback&&(e.ajaxCallback=t.ajaxCallback),e.addEventListener("typo3-modal-shown",(()=>{const t=e.nextElementSibling,a=1e3+10*this.instances.length;e.zindex=a;const o=a-5;t.style.zIndex=o.toString(),e.querySelector(`${Identifiers.footer} .t3js-active`)?.focus()})),e.addEventListener("typo3-modal-hide",(()=>{if(this.instances.length>0){const t=this.instances.length-1;this.instances.splice(t,1),this.currentModal=this.instances[t-1]}})),e.addEventListener("typo3-modal-hidden",(()=>{e.remove(),this.instances.length>0&&document.body.classList.add("modal-open")})),e.addEventListener("typo3-modal-show",(()=>{this.currentModal=e,this.instances.push(e)})),document.body.appendChild(e),e}}let modalObject=null;try{parent&&parent.window.TYPO3&&parent.window.TYPO3.Modal?(parent.window.TYPO3.Modal.initializeMarkupTrigger(document),modalObject=parent.window.TYPO3.Modal):top&&top.TYPO3.Modal&&(top.TYPO3.Modal.initializeMarkupTrigger(document),modalObject=top.TYPO3.Modal)}catch{}modalObject||(modalObject=new Modal,TYPO3.Modal=modalObject);export default modalObject; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/module-menu.js b/typo3/sysext/backend/Resources/Public/JavaScript/module-menu.js index 1f47414955e9..bb96d21007c4 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/module-menu.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/module-menu.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{ScaffoldIdentifierEnum}from"@typo3/backend/enum/viewport/scaffold-identifier.js";import{ModuleSelector,ModuleUtility}from"@typo3/backend/module.js";import $ from"jquery";import PersistentStorage from"@typo3/backend/storage/persistent.js";import Viewport from"@typo3/backend/viewport.js";import ClientRequest from"@typo3/backend/event/client-request.js";import TriggerRequest from"@typo3/backend/event/trigger-request.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";var ModuleMenuSelector;!function(e){e.menu="[data-modulemenu]",e.item="[data-modulemenu-identifier]",e.collapsible='[data-modulemenu-collapsible="true"]'}(ModuleMenuSelector||(ModuleMenuSelector={}));class ModuleMenu{constructor(){this.loadedModule=null,$((()=>this.initialize()))}static getModuleMenuItemFromElement(e){return{identifier:e.dataset.modulemenuIdentifier,level:e.parentElement.dataset.modulemenuLevel?parseInt(e.parentElement.dataset.modulemenuLevel,10):null,collapsible:"true"===e.dataset.modulemenuCollapsible,expanded:"true"===e.attributes.getNamedItem("aria-expanded")?.value,element:e}}static getCollapsedMainMenuItems(){return PersistentStorage.isset("modulemenu")?JSON.parse(PersistentStorage.get("modulemenu")):{}}static addCollapsedMainMenuItem(e){const t=ModuleMenu.getCollapsedMainMenuItems();t[e]=!0,PersistentStorage.set("modulemenu",JSON.stringify(t))}static removeCollapseMainMenuItem(e){const t=this.getCollapsedMainMenuItems();delete t[e],PersistentStorage.set("modulemenu",JSON.stringify(t))}static includeId(e,t){if(!e.navigationComponentId)return t;let o="";o="@typo3/backend/page-tree/page-tree-element"===e.navigationComponentId?"web":e.name.split("_")[0];const n=ModuleStateStorage.current(o);return n.selection&&(t="id="+n.selection+"&"+t),t}static toggleMenu(e){const t=document.querySelector(ScaffoldIdentifierEnum.scaffold),o="scaffold-modulemenu-expanded";void 0===e&&(e=t.classList.contains(o)),t.classList.toggle(o,!e),e||t.classList.remove("scaffold-toolbar-expanded"),PersistentStorage.set("BackendComponents.States.typo3-module-menu",{collapsed:e})}static toggleModuleGroup(e){const t=ModuleMenu.getModuleMenuItemFromElement(e),o=t.element.closest(".modulemenu-group"),n=o.querySelector(".modulemenu-group-container");t.expanded?ModuleMenu.addCollapsedMainMenuItem(t.identifier):ModuleMenu.removeCollapseMainMenuItem(t.identifier),o.classList.toggle("modulemenu-group-collapsed",t.expanded),o.classList.toggle("modulemenu-group-expanded",!t.expanded),e.setAttribute("aria-expanded",(!t.expanded).toString()),$(n).stop().slideToggle()}static highlightModule(e){document.querySelector(ModuleMenuSelector.menu).querySelectorAll(ModuleMenuSelector.item).forEach((e=>{e.classList.remove("modulemenu-action-active"),e.removeAttribute("aria-current")}));document.querySelector(".t3js-scaffold-toolbar").querySelectorAll(ModuleSelector.link+".dropdown-item").forEach((e=>{e.classList.remove("active"),e.removeAttribute("aria-current")}));const t=ModuleUtility.getFromName(e);this.highlightModuleMenuItem(t,!0)}static highlightModuleMenuItem(e,t=!0){const o=document.querySelector(ModuleMenuSelector.menu).querySelectorAll(ModuleMenuSelector.item+'[data-modulemenu-identifier="'+e.name+'"]');o.forEach((e=>{e.classList.add("modulemenu-action-active"),t&&e.setAttribute("aria-current","location")}));const n=document.querySelector(".t3js-scaffold-toolbar").querySelectorAll(ModuleSelector.link+'[data-moduleroute-identifier="'+e.name+'"].dropdown-item');n.forEach((e=>{e.classList.add("active"),t&&e.setAttribute("aria-current","location")})),(o.length>0||n.length>0)&&(t=!1),""!==e.parent&&this.highlightModuleMenuItem(ModuleUtility.getFromName(e.parent),t)}static getPreviousItem(e){let t=e.parentElement.previousElementSibling;return null===t?ModuleMenu.getLastItem(e):t.firstElementChild}static getNextItem(e){let t=e.parentElement.nextElementSibling;return null===t?ModuleMenu.getFirstItem(e):t.firstElementChild}static getFirstItem(e){return e.parentElement.parentElement.firstElementChild.firstElementChild}static getLastItem(e){return e.parentElement.parentElement.lastElementChild.firstElementChild}static getParentItem(e){return e.parentElement.parentElement.parentElement.firstElementChild}static getFirstChildItem(e){return e.nextElementSibling.firstElementChild.firstElementChild}refreshMenu(){new AjaxRequest(TYPO3.settings.ajaxUrls.modulemenu).get().then((async e=>{const t=await e.resolve();document.getElementById("modulemenu").outerHTML=t.menu,this.initializeModuleMenuEvents(),this.loadedModule&&ModuleMenu.highlightModule(this.loadedModule)}))}getCurrentModule(){return this.loadedModule}reloadFrames(){Viewport.ContentContainer.refresh()}showModule(e,t,o=null){t=t||"";const n=ModuleUtility.getFromName(e);return this.loadModuleComponents(n,t,new ClientRequest("typo3.showModule",o))}initialize(){if(null===document.querySelector(ModuleMenuSelector.menu))return;let e=$.Deferred();e.resolve(),e.then((()=>{this.initializeModuleMenuEvents(),Viewport.Topbar.Toolbar.registerEvent((()=>{document.querySelector(".t3js-scaffold-toolbar")&&this.initializeTopBarEvents()}))}))}keyboardNavigation(e,t){const o=ModuleMenu.getModuleMenuItemFromElement(t);let n=null;switch(e.code){case"ArrowUp":n=ModuleMenu.getPreviousItem(o.element);break;case"ArrowDown":n=ModuleMenu.getNextItem(o.element);break;case"ArrowLeft":o.level>1&&(n=ModuleMenu.getParentItem(o.element));break;case"ArrowRight":o.collapsible&&(o.expanded||ModuleMenu.toggleModuleGroup(o.element),n=ModuleMenu.getFirstChildItem(o.element));break;case"Home":if(e.ctrlKey&&o.level>1){n=document.querySelector(ModuleMenuSelector.menu+" "+ModuleMenuSelector.item);break}n=ModuleMenu.getFirstItem(o.element);break;case"End":n=e.ctrlKey&&o.level>1?ModuleMenu.getLastItem(document.querySelector(ModuleMenuSelector.menu+" "+ModuleMenuSelector.item)):ModuleMenu.getLastItem(o.element);break;case"Space":case"Enter":("Space"===e.code||o.collapsible)&&e.preventDefault(),o.collapsible&&(ModuleMenu.toggleModuleGroup(o.element),"true"===o.element.attributes.getNamedItem("aria-expanded").value&&(n=ModuleMenu.getFirstChildItem(o.element)));break;case"Esc":case"Escape":o.level>1&&(n=ModuleMenu.getParentItem(o.element),ModuleMenu.toggleModuleGroup(n));break;default:n=null}null!==n&&n.focus()}initializeModuleMenuEvents(){const e=document.querySelector(ModuleMenuSelector.menu);new RegularEvent("keydown",this.keyboardNavigation).delegateTo(e,ModuleMenuSelector.item),new RegularEvent("click",((e,t)=>{e.preventDefault();const o=ModuleUtility.getRouteFromElement(t);this.showModule(o.identifier,o.params,e)})).delegateTo(e,ModuleSelector.link),new RegularEvent("click",((e,t)=>{e.preventDefault(),ModuleMenu.toggleModuleGroup(t)})).delegateTo(e,ModuleMenuSelector.collapsible)}initializeTopBarEvents(){const e=document.querySelector(".t3js-scaffold-toolbar");new RegularEvent("click",((e,t)=>{e.preventDefault();const o=ModuleUtility.getRouteFromElement(t);this.showModule(o.identifier,o.params,e)})).delegateTo(e,ModuleSelector.link),new RegularEvent("click",(e=>{e.preventDefault(),ModuleMenu.toggleMenu()})).bindTo(document.querySelector(".t3js-topbar-button-modulemenu")),new RegularEvent("click",(e=>{e.preventDefault(),ModuleMenu.toggleMenu(!0)})).bindTo(document.querySelector(".t3js-scaffold-content-overlay"));const t=e=>{const t=e.detail.module;if(!t||this.loadedModule===t)return;const o=ModuleUtility.getFromName(t);o.link&&(ModuleMenu.highlightModule(t),this.loadedModule=t,o.navigationComponentId?Viewport.NavigationContainer.showComponent(o.navigationComponentId):Viewport.NavigationContainer.hide(!1))};document.addEventListener("typo3-module-load",t),document.addEventListener("typo3-module-loaded",t)}loadModuleComponents(e,t,o){const n=e.name,l=Viewport.ContentContainer.beforeSetUrl(o);return l.then((()=>{e.navigationComponentId?Viewport.NavigationContainer.showComponent(e.navigationComponentId):Viewport.NavigationContainer.hide(!0),ModuleMenu.highlightModule(n),this.loadedModule=n,t=ModuleMenu.includeId(e,t),this.openInContentContainer(n,e.link,t,new TriggerRequest("typo3.loadModuleComponents",o))})),l}openInContentContainer(e,t,o,n){const l=t+(o?(t.includes("?")?"&":"?")+o:"");return Viewport.ContentContainer.setUrl(l,new TriggerRequest("typo3.openInContentFrame",n),e)}}top.TYPO3.ModuleMenu||(top.TYPO3.ModuleMenu={App:new ModuleMenu});const moduleMenuApp=top.TYPO3.ModuleMenu;export default moduleMenuApp; \ No newline at end of file +import{ScaffoldIdentifierEnum}from"@typo3/backend/enum/viewport/scaffold-identifier.js";import{ModuleSelector,ModuleUtility}from"@typo3/backend/module.js";import $ from"jquery";import PersistentStorage from"@typo3/backend/storage/persistent.js";import Viewport from"@typo3/backend/viewport.js";import ClientRequest from"@typo3/backend/event/client-request.js";import TriggerRequest from"@typo3/backend/event/trigger-request.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";var ModuleMenuSelector;!function(e){e.menu="[data-modulemenu]",e.item="[data-modulemenu-identifier]",e.collapsible='[data-modulemenu-collapsible="true"]'}(ModuleMenuSelector||(ModuleMenuSelector={}));class ModuleMenu{constructor(){this.loadedModule=null,$((()=>this.initialize()))}static getModuleMenuItemFromElement(e){return{identifier:e.dataset.modulemenuIdentifier,level:e.parentElement.dataset.modulemenuLevel?parseInt(e.parentElement.dataset.modulemenuLevel,10):null,collapsible:"true"===e.dataset.modulemenuCollapsible,expanded:"true"===e.attributes.getNamedItem("aria-expanded")?.value,element:e}}static getCollapsedMainMenuItems(){return PersistentStorage.isset("modulemenu")?JSON.parse(PersistentStorage.get("modulemenu")):{}}static addCollapsedMainMenuItem(e){const t=ModuleMenu.getCollapsedMainMenuItems();t[e]=!0,PersistentStorage.set("modulemenu",JSON.stringify(t))}static removeCollapseMainMenuItem(e){const t=this.getCollapsedMainMenuItems();delete t[e],PersistentStorage.set("modulemenu",JSON.stringify(t))}static includeId(e,t){if(!e.navigationComponentId)return t;let o="";o="@typo3/backend/page-tree/page-tree-element"===e.navigationComponentId?"web":e.name.split("_")[0];const n=ModuleStateStorage.current(o);return n.selection&&(t="id="+n.selection+"&"+t),t}static toggleMenu(e){const t=document.querySelector(ScaffoldIdentifierEnum.scaffold),o="scaffold-modulemenu-expanded";void 0===e&&(e=t.classList.contains(o)),t.classList.toggle(o,!e),e||t.classList.remove("scaffold-toolbar-expanded"),PersistentStorage.set("BackendComponents.States.typo3-module-menu",{collapsed:e})}static toggleModuleGroup(e){const t=ModuleMenu.getModuleMenuItemFromElement(e),o=t.element.closest(".modulemenu-group"),n=o.querySelector(".modulemenu-group-container");t.expanded?ModuleMenu.addCollapsedMainMenuItem(t.identifier):ModuleMenu.removeCollapseMainMenuItem(t.identifier),o.classList.toggle("modulemenu-group-collapsed",t.expanded),o.classList.toggle("modulemenu-group-expanded",!t.expanded),e.setAttribute("aria-expanded",(!t.expanded).toString()),$(n).stop().slideToggle()}static highlightModule(e){document.querySelector(ModuleMenuSelector.menu).querySelectorAll(ModuleMenuSelector.item).forEach((e=>{e.classList.remove("modulemenu-action-active"),e.removeAttribute("aria-current")}));document.querySelector(".t3js-scaffold-toolbar").querySelectorAll(ModuleSelector.link+".dropdown-item").forEach((e=>{e.classList.remove("active"),e.removeAttribute("aria-current")}));const t=ModuleUtility.getFromName(e);this.highlightModuleMenuItem(t,!0)}static highlightModuleMenuItem(e,t=!0){const o=document.querySelector(ModuleMenuSelector.menu).querySelectorAll(ModuleMenuSelector.item+'[data-modulemenu-identifier="'+e.name+'"]');o.forEach((e=>{e.classList.add("modulemenu-action-active"),t&&e.setAttribute("aria-current","location")}));const n=document.querySelector(".t3js-scaffold-toolbar").querySelectorAll(ModuleSelector.link+'[data-moduleroute-identifier="'+e.name+'"].dropdown-item');n.forEach((e=>{e.classList.add("active"),t&&e.setAttribute("aria-current","location")})),(o.length>0||n.length>0)&&(t=!1),""!==e.parent&&this.highlightModuleMenuItem(ModuleUtility.getFromName(e.parent),t)}static getPreviousItem(e){const t=e.parentElement.previousElementSibling;return null===t?ModuleMenu.getLastItem(e):t.firstElementChild}static getNextItem(e){const t=e.parentElement.nextElementSibling;return null===t?ModuleMenu.getFirstItem(e):t.firstElementChild}static getFirstItem(e){return e.parentElement.parentElement.firstElementChild.firstElementChild}static getLastItem(e){return e.parentElement.parentElement.lastElementChild.firstElementChild}static getParentItem(e){return e.parentElement.parentElement.parentElement.firstElementChild}static getFirstChildItem(e){return e.nextElementSibling.firstElementChild.firstElementChild}refreshMenu(){new AjaxRequest(TYPO3.settings.ajaxUrls.modulemenu).get().then((async e=>{const t=await e.resolve();document.getElementById("modulemenu").outerHTML=t.menu,this.initializeModuleMenuEvents(),this.loadedModule&&ModuleMenu.highlightModule(this.loadedModule)}))}getCurrentModule(){return this.loadedModule}reloadFrames(){Viewport.ContentContainer.refresh()}showModule(e,t,o=null){t=t||"";const n=ModuleUtility.getFromName(e);return this.loadModuleComponents(n,t,new ClientRequest("typo3.showModule",o))}initialize(){if(null===document.querySelector(ModuleMenuSelector.menu))return;const e=$.Deferred();e.resolve(),e.then((()=>{this.initializeModuleMenuEvents(),Viewport.Topbar.Toolbar.registerEvent((()=>{document.querySelector(".t3js-scaffold-toolbar")&&this.initializeTopBarEvents()}))}))}keyboardNavigation(e,t){const o=ModuleMenu.getModuleMenuItemFromElement(t);let n=null;switch(e.code){case"ArrowUp":n=ModuleMenu.getPreviousItem(o.element);break;case"ArrowDown":n=ModuleMenu.getNextItem(o.element);break;case"ArrowLeft":o.level>1&&(n=ModuleMenu.getParentItem(o.element));break;case"ArrowRight":o.collapsible&&(o.expanded||ModuleMenu.toggleModuleGroup(o.element),n=ModuleMenu.getFirstChildItem(o.element));break;case"Home":if(e.ctrlKey&&o.level>1){n=document.querySelector(ModuleMenuSelector.menu+" "+ModuleMenuSelector.item);break}n=ModuleMenu.getFirstItem(o.element);break;case"End":n=e.ctrlKey&&o.level>1?ModuleMenu.getLastItem(document.querySelector(ModuleMenuSelector.menu+" "+ModuleMenuSelector.item)):ModuleMenu.getLastItem(o.element);break;case"Space":case"Enter":("Space"===e.code||o.collapsible)&&e.preventDefault(),o.collapsible&&(ModuleMenu.toggleModuleGroup(o.element),"true"===o.element.attributes.getNamedItem("aria-expanded").value&&(n=ModuleMenu.getFirstChildItem(o.element)));break;case"Esc":case"Escape":o.level>1&&(n=ModuleMenu.getParentItem(o.element),ModuleMenu.toggleModuleGroup(n));break;default:n=null}null!==n&&n.focus()}initializeModuleMenuEvents(){const e=document.querySelector(ModuleMenuSelector.menu);new RegularEvent("keydown",this.keyboardNavigation).delegateTo(e,ModuleMenuSelector.item),new RegularEvent("click",((e,t)=>{e.preventDefault();const o=ModuleUtility.getRouteFromElement(t);this.showModule(o.identifier,o.params,e)})).delegateTo(e,ModuleSelector.link),new RegularEvent("click",((e,t)=>{e.preventDefault(),ModuleMenu.toggleModuleGroup(t)})).delegateTo(e,ModuleMenuSelector.collapsible)}initializeTopBarEvents(){const e=document.querySelector(".t3js-scaffold-toolbar");new RegularEvent("click",((e,t)=>{e.preventDefault();const o=ModuleUtility.getRouteFromElement(t);this.showModule(o.identifier,o.params,e)})).delegateTo(e,ModuleSelector.link),new RegularEvent("click",(e=>{e.preventDefault(),ModuleMenu.toggleMenu()})).bindTo(document.querySelector(".t3js-topbar-button-modulemenu")),new RegularEvent("click",(e=>{e.preventDefault(),ModuleMenu.toggleMenu(!0)})).bindTo(document.querySelector(".t3js-scaffold-content-overlay"));const t=e=>{const t=e.detail.module;if(!t||this.loadedModule===t)return;const o=ModuleUtility.getFromName(t);o.link&&(ModuleMenu.highlightModule(t),this.loadedModule=t,o.navigationComponentId?Viewport.NavigationContainer.showComponent(o.navigationComponentId):Viewport.NavigationContainer.hide(!1))};document.addEventListener("typo3-module-load",t),document.addEventListener("typo3-module-loaded",t)}loadModuleComponents(e,t,o){const n=e.name,l=Viewport.ContentContainer.beforeSetUrl(o);return l.then((()=>{e.navigationComponentId?Viewport.NavigationContainer.showComponent(e.navigationComponentId):Viewport.NavigationContainer.hide(!0),ModuleMenu.highlightModule(n),this.loadedModule=n,t=ModuleMenu.includeId(e,t),this.openInContentContainer(n,e.link,t,new TriggerRequest("typo3.loadModuleComponents",o))})),l}openInContentContainer(e,t,o,n){const l=t+(o?(t.includes("?")?"&":"?")+o:"");return Viewport.ContentContainer.setUrl(l,new TriggerRequest("typo3.openInContentFrame",n),e)}}top.TYPO3.ModuleMenu||(top.TYPO3.ModuleMenu={App:new ModuleMenu});const moduleMenuApp=top.TYPO3.ModuleMenu;export default moduleMenuApp; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/module/router.js b/typo3/sysext/backend/Resources/Public/JavaScript/module/router.js index b88200389599..da53c49fa399 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/module/router.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/module/router.js @@ -10,7 +10,7 @@ * * The TYPO3 project - inspiring people to share! */ -var __decorate=function(t,e,o,r){var i,l=arguments.length,n=l<3?e:null===r?r=Object.getOwnPropertyDescriptor(e,o):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)n=Reflect.decorate(t,e,o,r);else for(var a=t.length-1;a>=0;a--)(i=t[a])&&(n=(l<3?i(n):l>3?i(e,o,n):i(e,o))||n);return l>3&&n&&Object.defineProperty(e,o,n),n};import{html,css,LitElement}from"lit";import{customElement,property,query}from"lit/decorators.js";import{ModuleUtility}from"@typo3/backend/module.js";const IFRAME_COMPONENT="@typo3/backend/module/iframe",alwaysUpdate=(t,e)=>!0;let ModuleRouter=class extends LitElement{constructor(){super(),this.module="",this.endpoint="",this.addEventListener("typo3-module-load",(({target:t,detail:e})=>{const o=t.getAttribute("slot");this.pushState({slotName:o,detail:e})})),this.addEventListener("typo3-module-loaded",(({detail:t})=>{this.updateBrowserState(t)})),this.addEventListener("typo3-iframe-load",(({detail:t})=>{let e={slotName:IFRAME_COMPONENT,detail:t};if(e.detail.url.includes(this.stateTrackerUrl+"?state=")){const t=e.detail.url.split("?state=");e=JSON.parse(decodeURIComponent(t[1]||"{}"))}this.slotElement.getAttribute("name")!==e.slotName&&this.slotElement.setAttribute("name",e.slotName),this.markActive(e.slotName,this.slotElement.getAttribute("name")===IFRAME_COMPONENT?null:e.detail.url,!1),this.updateBrowserState(e.detail),this.parentElement.dispatchEvent(new CustomEvent("typo3-module-load",{bubbles:!0,composed:!0,detail:e.detail}))})),this.addEventListener("typo3-iframe-loaded",(({detail:t})=>{this.updateBrowserState(t),this.parentElement.dispatchEvent(new CustomEvent("typo3-module-loaded",{bubbles:!0,composed:!0,detail:t}))}))}render(){const t=ModuleUtility.getFromName(this.module).component||IFRAME_COMPONENT;return html`<slot name="${t}"></slot>`}updated(){const t=ModuleUtility.getFromName(this.module).component||IFRAME_COMPONENT;this.markActive(t,this.endpoint)}async markActive(t,e,o=!0){const r=await this.getModuleElement(t);e&&(o||r.getAttribute("endpoint")!==e)&&r.setAttribute("endpoint",e),r.hasAttribute("active")||r.setAttribute("active","");for(let t=r.previousElementSibling;null!==t;t=t.previousElementSibling)t.removeAttribute("active");for(let t=r.nextElementSibling;null!==t;t=t.nextElementSibling)t.removeAttribute("active")}async getModuleElement(t){let e=this.querySelector(`*[slot="${t}"]`);if(null!==e)return e;try{const o=await import(t+".js");e=document.createElement(o.componentName)}catch(e){throw console.error({msg:`Error importing ${t} as backend module`,err:e}),e}return e.setAttribute("slot",t),this.appendChild(e),e}async pushState(t){const e=this.stateTrackerUrl+"?state="+encodeURIComponent(JSON.stringify(t));(await this.getModuleElement(IFRAME_COMPONENT)).setAttribute("endpoint",e)}updateBrowserState(t){const e=new URL(t.url||"",window.location.origin),o=new URLSearchParams(e.search),r="title"in t?t.title:"";if(null!==r){const t=[this.sitename];""!==r&&t.unshift(r),this.sitenameFirst&&t.reverse(),document.title=t.join(" · ")}if(!o.has("token"))return;o.delete("token"),e.search=o.toString();const i=e.toString();window.history.replaceState(t,"",i)}};ModuleRouter.styles=css` +var __decorate=function(t,e,o,r){var i,l=arguments.length,n=l<3?e:null===r?r=Object.getOwnPropertyDescriptor(e,o):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)n=Reflect.decorate(t,e,o,r);else for(var a=t.length-1;a>=0;a--)(i=t[a])&&(n=(l<3?i(n):l>3?i(e,o,n):i(e,o))||n);return l>3&&n&&Object.defineProperty(e,o,n),n};import{html,css,LitElement}from"lit";import{customElement,property,query}from"lit/decorators.js";import{ModuleUtility}from"@typo3/backend/module.js";const IFRAME_COMPONENT="@typo3/backend/module/iframe",alwaysUpdate=()=>!0;let ModuleRouter=class extends LitElement{constructor(){super(),this.module="",this.endpoint="",this.addEventListener("typo3-module-load",(({target:t,detail:e})=>{const o=t.getAttribute("slot");this.pushState({slotName:o,detail:e})})),this.addEventListener("typo3-module-loaded",(({detail:t})=>{this.updateBrowserState(t)})),this.addEventListener("typo3-iframe-load",(({detail:t})=>{let e={slotName:IFRAME_COMPONENT,detail:t};if(e.detail.url.includes(this.stateTrackerUrl+"?state=")){const t=e.detail.url.split("?state=");e=JSON.parse(decodeURIComponent(t[1]||"{}"))}this.slotElement.getAttribute("name")!==e.slotName&&this.slotElement.setAttribute("name",e.slotName),this.markActive(e.slotName,this.slotElement.getAttribute("name")===IFRAME_COMPONENT?null:e.detail.url,!1),this.updateBrowserState(e.detail),this.parentElement.dispatchEvent(new CustomEvent("typo3-module-load",{bubbles:!0,composed:!0,detail:e.detail}))})),this.addEventListener("typo3-iframe-loaded",(({detail:t})=>{this.updateBrowserState(t),this.parentElement.dispatchEvent(new CustomEvent("typo3-module-loaded",{bubbles:!0,composed:!0,detail:t}))}))}render(){const t=ModuleUtility.getFromName(this.module).component||IFRAME_COMPONENT;return html`<slot name="${t}"></slot>`}updated(){const t=ModuleUtility.getFromName(this.module).component||IFRAME_COMPONENT;this.markActive(t,this.endpoint)}async markActive(t,e,o=!0){const r=await this.getModuleElement(t);e&&(o||r.getAttribute("endpoint")!==e)&&r.setAttribute("endpoint",e),r.hasAttribute("active")||r.setAttribute("active","");for(let t=r.previousElementSibling;null!==t;t=t.previousElementSibling)t.removeAttribute("active");for(let t=r.nextElementSibling;null!==t;t=t.nextElementSibling)t.removeAttribute("active")}async getModuleElement(t){let e=this.querySelector(`*[slot="${t}"]`);if(null!==e)return e;try{const o=await import(t+".js");e=document.createElement(o.componentName)}catch(e){throw console.error({msg:`Error importing ${t} as backend module`,err:e}),e}return e.setAttribute("slot",t),this.appendChild(e),e}async pushState(t){const e=this.stateTrackerUrl+"?state="+encodeURIComponent(JSON.stringify(t));(await this.getModuleElement(IFRAME_COMPONENT)).setAttribute("endpoint",e)}updateBrowserState(t){const e=new URL(t.url||"",window.location.origin),o=new URLSearchParams(e.search),r="title"in t?t.title:"";if(null!==r){const t=[this.sitename];""!==r&&t.unshift(r),this.sitenameFirst&&t.reverse(),document.title=t.join(" · ")}if(!o.has("token"))return;o.delete("token"),e.search=o.toString();const i=e.toString();window.history.replaceState(t,"",i)}};ModuleRouter.styles=css` :host { width: 100%; min-height: 100%; diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/multi-record-selection.js b/typo3/sysext/backend/Resources/Public/JavaScript/multi-record-selection.js index a486632e3785..89aa65b19e0b 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/multi-record-selection.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/multi-record-selection.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import Notification from"@typo3/backend/notification.js";import DocumentService from"@typo3/core/document-service.js";import RegularEvent from"@typo3/core/event/regular-event.js";export var MultiRecordSelectionSelectors;var Buttons,CheckboxActions,CheckboxState;!function(e){e.actionsSelector=".t3js-multi-record-selection-actions",e.checkboxSelector=".t3js-multi-record-selection-check",e.checkboxActionsSelector=".t3js-multi-record-selection-check-actions",e.checkboxActionsToggleSelector=".t3js-multi-record-selection-check-actions-toggle",e.elementSelector="[data-multi-record-selection-element]"}(MultiRecordSelectionSelectors||(MultiRecordSelectionSelectors={})),function(e){e.actionButton="button[data-multi-record-selection-action]",e.checkboxActionButton="button[data-multi-record-selection-check-action]"}(Buttons||(Buttons={})),function(e){e.checkAll="check-all",e.checkNone="check-none",e.toggle="toggle"}(CheckboxActions||(CheckboxActions={})),function(e){e.any="",e.checked=":checked",e.unchecked=":not(:checked)"}(CheckboxState||(CheckboxState={}));class MultiRecordSelection{constructor(){this.lastChecked=null,DocumentService.ready().then((()=>{MultiRecordSelection.restoreTemporaryState(),this.registerActions(),this.registerActionsEventHandlers(),this.registerCheckboxActions(),this.registerCheckboxKeyboardActions(),this.registerCheckboxTableRowSelectionAction(),this.registerToggleCheckboxActions(),this.registerDispatchCheckboxStateChangedEvent(),this.registerCheckboxStateChangedEventHandler()}))}static getCheckboxes(e=CheckboxState.any,t=""){return document.querySelectorAll(MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.checkboxSelector+e,t))}static getCombinedSelector(e,t){return""!==t?['[data-multi-record-selection-identifier="'+t+'"]',e].join(" "):e}static getIdentifier(e){return e.closest("[data-multi-record-selection-identifier]")?.dataset.multiRecordSelectionIdentifier||""}static changeCheckboxState(e,t){e.checked===t||e.dataset.manuallyChanged||(e.checked=t,e.dispatchEvent(new CustomEvent("multiRecordSelection:checkbox:state:changed",{detail:{identifier:MultiRecordSelection.getIdentifier(e)},bubbles:!0,cancelable:!1})))}static restoreTemporaryState(){const e=MultiRecordSelection.getCheckboxes(CheckboxState.checked);if(!e.length)return;let t=!1,c=[];e.forEach((e=>{e.closest(MultiRecordSelectionSelectors.elementSelector)?.classList.add(MultiRecordSelection.activeClass);const o=MultiRecordSelection.getIdentifier(e);""===o||c.includes(o)||(c.push(o),t=!0,MultiRecordSelection.toggleActionsState(o))})),t||MultiRecordSelection.toggleActionsState()}static toggleActionsState(e=""){const t=document.querySelectorAll(MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.actionsSelector,e));if(!t.length)return;if(!MultiRecordSelection.getCheckboxes(CheckboxState.checked,e).length)return void t.forEach((e=>MultiRecordSelection.changeActionContainerVisibility(e,!1)));t.forEach((e=>MultiRecordSelection.changeActionContainerVisibility(e)));const c=document.querySelectorAll([MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.actionsSelector,e),Buttons.actionButton].join(" "));c.length&&c.forEach((t=>{if(!t.dataset.multiRecordSelectionActionConfig)return;const c=JSON.parse(t.dataset.multiRecordSelectionActionConfig);if(!c.idField)return;t.classList.add(this.disabledClass);const o=MultiRecordSelection.getCheckboxes(CheckboxState.checked,e);for(let e=0;e<o.length;e++)if(o[e].closest(MultiRecordSelectionSelectors.elementSelector)?.dataset[c.idField]){t.classList.remove(this.disabledClass);break}}))}static changeActionContainerVisibility(e,t=!0){const c=e.closest(".multi-record-selection-panel")?.children;if(t){if(c)for(let e=0;e<c.length;e++)c[e].classList.add("hidden");e.classList.remove("hidden")}else{if(c)for(let e=0;e<c.length;e++)c[e].classList.remove("hidden");e.classList.add("hidden")}}static unsetManuallyChangedAttribute(e){MultiRecordSelection.getCheckboxes(CheckboxState.any,e).forEach((e=>{e.removeAttribute("data-manually-changed")}))}registerActions(){new RegularEvent("click",((e,t)=>{t.dataset.multiRecordSelectionAction;const c=MultiRecordSelection.getIdentifier(t),o=JSON.parse(t.dataset.multiRecordSelectionActionConfig||"{}"),i=MultiRecordSelection.getCheckboxes(CheckboxState.checked,c);i.length&&t.dispatchEvent(new CustomEvent("multiRecordSelection:action:"+t.dataset.multiRecordSelectionAction,{detail:{identifier:c,checkboxes:i,configuration:o},bubbles:!0,cancelable:!1}))})).delegateTo(document,[MultiRecordSelectionSelectors.actionsSelector,Buttons.actionButton].join(" "))}registerActionsEventHandlers(){new RegularEvent("multiRecordSelection:actions:show",(e=>{const t=e.detail?.identifier||"",c=document.querySelectorAll(MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.actionsSelector,t));c.length&&c.forEach((e=>MultiRecordSelection.changeActionContainerVisibility(e)))})).bindTo(document),new RegularEvent("multiRecordSelection:actions:hide",(e=>{const t=e.detail?.identifier||"",c=document.querySelectorAll(MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.actionsSelector,t));c.length&&c.forEach((e=>MultiRecordSelection.changeActionContainerVisibility(e,!1)))})).bindTo(document)}registerCheckboxActions(){new RegularEvent("click",((e,t)=>{if(e.preventDefault(),!t.dataset.multiRecordSelectionCheckAction)return;const c=MultiRecordSelection.getIdentifier(t),o=MultiRecordSelection.getCheckboxes(CheckboxState.any,c);if(o.length){switch(MultiRecordSelection.unsetManuallyChangedAttribute(c),t.dataset.multiRecordSelectionCheckAction){case CheckboxActions.checkAll:o.forEach((e=>{MultiRecordSelection.changeCheckboxState(e,!0)}));break;case CheckboxActions.checkNone:o.forEach((e=>{MultiRecordSelection.changeCheckboxState(e,!1)}));break;case CheckboxActions.toggle:o.forEach((e=>{MultiRecordSelection.changeCheckboxState(e,!e.checked)}));break;default:Notification.warning("Unknown checkbox action")}MultiRecordSelection.unsetManuallyChangedAttribute(c)}})).delegateTo(document,[MultiRecordSelectionSelectors.checkboxActionsSelector,Buttons.checkboxActionButton].join(" "))}registerCheckboxKeyboardActions(){new RegularEvent("click",((e,t)=>this.handleCheckboxKeyboardActions(e,t))).delegateTo(document,MultiRecordSelectionSelectors.checkboxSelector)}registerCheckboxTableRowSelectionAction(){new RegularEvent("click",((e,t)=>{const c=e.target.tagName;if("TH"!==c&&"TD"!==c)return;const o=t.querySelector(MultiRecordSelectionSelectors.checkboxSelector);null!==o&&(MultiRecordSelection.changeCheckboxState(o,!o.checked),this.handleCheckboxKeyboardActions(e,o,!1))})).delegateTo(document,MultiRecordSelectionSelectors.elementSelector),new RegularEvent("mousedown",(e=>(e.shiftKey||e.altKey||e.ctrlKey)&&e.preventDefault())).delegateTo(document,MultiRecordSelectionSelectors.elementSelector)}registerDispatchCheckboxStateChangedEvent(){new RegularEvent("change",((e,t)=>{t.dispatchEvent(new CustomEvent("multiRecordSelection:checkbox:state:changed",{detail:{identifier:MultiRecordSelection.getIdentifier(t)},bubbles:!0,cancelable:!1}))})).delegateTo(document,MultiRecordSelectionSelectors.checkboxSelector)}registerCheckboxStateChangedEventHandler(){new RegularEvent("multiRecordSelection:checkbox:state:changed",(e=>{const t=e.target,c=e.detail?.identifier||"";t.checked?t.closest(MultiRecordSelectionSelectors.elementSelector).classList.add(MultiRecordSelection.activeClass):t.closest(MultiRecordSelectionSelectors.elementSelector).classList.remove(MultiRecordSelection.activeClass),MultiRecordSelection.toggleActionsState(c)})).bindTo(document)}registerToggleCheckboxActions(){new RegularEvent("click",((e,t)=>{const c=MultiRecordSelection.getIdentifier(t),o=document.querySelector([MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.checkboxActionsSelector,c),'button[data-multi-record-selection-check-action="'+CheckboxActions.checkAll+'"]'].join(" "));null!==o&&o.classList.toggle("disabled",!MultiRecordSelection.getCheckboxes(CheckboxState.unchecked,c).length);const i=document.querySelector([MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.checkboxActionsSelector,c),'button[data-multi-record-selection-check-action="'+CheckboxActions.checkNone+'"]'].join(" "));null!==i&&i.classList.toggle("disabled",!MultiRecordSelection.getCheckboxes(CheckboxState.checked,c).length);const l=document.querySelector([MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.checkboxActionsSelector,c),'button[data-multi-record-selection-check-action="'+CheckboxActions.toggle+'"]'].join(" "));null!==l&&l.classList.toggle("disabled",!MultiRecordSelection.getCheckboxes(CheckboxState.any,c).length)})).delegateTo(document,MultiRecordSelectionSelectors.checkboxActionsToggleSelector)}handleCheckboxKeyboardActions(e,t,c=!0){const o=MultiRecordSelection.getIdentifier(t);if(this.lastChecked&&document.body.contains(this.lastChecked)&&MultiRecordSelection.getIdentifier(this.lastChecked)===o&&(e.shiftKey||e.altKey||e.ctrlKey)){if(c&&MultiRecordSelection.unsetManuallyChangedAttribute(o),e.shiftKey){const e=Array.from(MultiRecordSelection.getCheckboxes(CheckboxState.any,o)),c=e.indexOf(t),i=e.indexOf(this.lastChecked);e.slice(Math.min(c,i),Math.max(c,i)+1).forEach((e=>{e!==t&&MultiRecordSelection.changeCheckboxState(e,t.checked)}))}this.lastChecked=t,(e.altKey||e.ctrlKey)&&MultiRecordSelection.getCheckboxes(CheckboxState.any,o).forEach((e=>{e!==t&&MultiRecordSelection.changeCheckboxState(e,!e.checked)})),MultiRecordSelection.unsetManuallyChangedAttribute(o)}else this.lastChecked=t}}MultiRecordSelection.activeClass="active",MultiRecordSelection.disabledClass="disabled";export default new MultiRecordSelection; \ No newline at end of file +import Notification from"@typo3/backend/notification.js";import DocumentService from"@typo3/core/document-service.js";import RegularEvent from"@typo3/core/event/regular-event.js";export var MultiRecordSelectionSelectors;var Buttons,CheckboxActions,CheckboxState;!function(e){e.actionsSelector=".t3js-multi-record-selection-actions",e.checkboxSelector=".t3js-multi-record-selection-check",e.checkboxActionsSelector=".t3js-multi-record-selection-check-actions",e.checkboxActionsToggleSelector=".t3js-multi-record-selection-check-actions-toggle",e.elementSelector="[data-multi-record-selection-element]"}(MultiRecordSelectionSelectors||(MultiRecordSelectionSelectors={})),function(e){e.actionButton="button[data-multi-record-selection-action]",e.checkboxActionButton="button[data-multi-record-selection-check-action]"}(Buttons||(Buttons={})),function(e){e.checkAll="check-all",e.checkNone="check-none",e.toggle="toggle"}(CheckboxActions||(CheckboxActions={})),function(e){e.any="",e.checked=":checked",e.unchecked=":not(:checked)"}(CheckboxState||(CheckboxState={}));class MultiRecordSelection{constructor(){this.lastChecked=null,DocumentService.ready().then((()=>{MultiRecordSelection.restoreTemporaryState(),this.registerActions(),this.registerActionsEventHandlers(),this.registerCheckboxActions(),this.registerCheckboxKeyboardActions(),this.registerCheckboxTableRowSelectionAction(),this.registerToggleCheckboxActions(),this.registerDispatchCheckboxStateChangedEvent(),this.registerCheckboxStateChangedEventHandler()}))}static getCheckboxes(e=CheckboxState.any,t=""){return document.querySelectorAll(MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.checkboxSelector+e,t))}static getCombinedSelector(e,t){return""!==t?['[data-multi-record-selection-identifier="'+t+'"]',e].join(" "):e}static getIdentifier(e){return e.closest("[data-multi-record-selection-identifier]")?.dataset.multiRecordSelectionIdentifier||""}static changeCheckboxState(e,t){e.checked===t||e.dataset.manuallyChanged||(e.checked=t,e.dispatchEvent(new CustomEvent("multiRecordSelection:checkbox:state:changed",{detail:{identifier:MultiRecordSelection.getIdentifier(e)},bubbles:!0,cancelable:!1})))}static restoreTemporaryState(){const e=MultiRecordSelection.getCheckboxes(CheckboxState.checked);if(!e.length)return;let t=!1;const c=[];e.forEach((e=>{e.closest(MultiRecordSelectionSelectors.elementSelector)?.classList.add(MultiRecordSelection.activeClass);const o=MultiRecordSelection.getIdentifier(e);""===o||c.includes(o)||(c.push(o),t=!0,MultiRecordSelection.toggleActionsState(o))})),t||MultiRecordSelection.toggleActionsState()}static toggleActionsState(e=""){const t=document.querySelectorAll(MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.actionsSelector,e));if(!t.length)return;if(!MultiRecordSelection.getCheckboxes(CheckboxState.checked,e).length)return void t.forEach((e=>MultiRecordSelection.changeActionContainerVisibility(e,!1)));t.forEach((e=>MultiRecordSelection.changeActionContainerVisibility(e)));const c=document.querySelectorAll([MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.actionsSelector,e),Buttons.actionButton].join(" "));c.length&&c.forEach((t=>{if(!t.dataset.multiRecordSelectionActionConfig)return;const c=JSON.parse(t.dataset.multiRecordSelectionActionConfig);if(!c.idField)return;t.classList.add(this.disabledClass);const o=MultiRecordSelection.getCheckboxes(CheckboxState.checked,e);for(let e=0;e<o.length;e++)if(o[e].closest(MultiRecordSelectionSelectors.elementSelector)?.dataset[c.idField]){t.classList.remove(this.disabledClass);break}}))}static changeActionContainerVisibility(e,t=!0){const c=e.closest(".multi-record-selection-panel")?.children;if(t){if(c)for(let e=0;e<c.length;e++)c[e].classList.add("hidden");e.classList.remove("hidden")}else{if(c)for(let e=0;e<c.length;e++)c[e].classList.remove("hidden");e.classList.add("hidden")}}static unsetManuallyChangedAttribute(e){MultiRecordSelection.getCheckboxes(CheckboxState.any,e).forEach((e=>{e.removeAttribute("data-manually-changed")}))}registerActions(){new RegularEvent("click",((e,t)=>{t.dataset.multiRecordSelectionAction;const c=MultiRecordSelection.getIdentifier(t),o=JSON.parse(t.dataset.multiRecordSelectionActionConfig||"{}"),i=MultiRecordSelection.getCheckboxes(CheckboxState.checked,c);i.length&&t.dispatchEvent(new CustomEvent("multiRecordSelection:action:"+t.dataset.multiRecordSelectionAction,{detail:{identifier:c,checkboxes:i,configuration:o},bubbles:!0,cancelable:!1}))})).delegateTo(document,[MultiRecordSelectionSelectors.actionsSelector,Buttons.actionButton].join(" "))}registerActionsEventHandlers(){new RegularEvent("multiRecordSelection:actions:show",(e=>{const t=e.detail?.identifier||"",c=document.querySelectorAll(MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.actionsSelector,t));c.length&&c.forEach((e=>MultiRecordSelection.changeActionContainerVisibility(e)))})).bindTo(document),new RegularEvent("multiRecordSelection:actions:hide",(e=>{const t=e.detail?.identifier||"",c=document.querySelectorAll(MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.actionsSelector,t));c.length&&c.forEach((e=>MultiRecordSelection.changeActionContainerVisibility(e,!1)))})).bindTo(document)}registerCheckboxActions(){new RegularEvent("click",((e,t)=>{if(e.preventDefault(),!t.dataset.multiRecordSelectionCheckAction)return;const c=MultiRecordSelection.getIdentifier(t),o=MultiRecordSelection.getCheckboxes(CheckboxState.any,c);if(o.length){switch(MultiRecordSelection.unsetManuallyChangedAttribute(c),t.dataset.multiRecordSelectionCheckAction){case CheckboxActions.checkAll:o.forEach((e=>{MultiRecordSelection.changeCheckboxState(e,!0)}));break;case CheckboxActions.checkNone:o.forEach((e=>{MultiRecordSelection.changeCheckboxState(e,!1)}));break;case CheckboxActions.toggle:o.forEach((e=>{MultiRecordSelection.changeCheckboxState(e,!e.checked)}));break;default:Notification.warning("Unknown checkbox action")}MultiRecordSelection.unsetManuallyChangedAttribute(c)}})).delegateTo(document,[MultiRecordSelectionSelectors.checkboxActionsSelector,Buttons.checkboxActionButton].join(" "))}registerCheckboxKeyboardActions(){new RegularEvent("click",((e,t)=>this.handleCheckboxKeyboardActions(e,t))).delegateTo(document,MultiRecordSelectionSelectors.checkboxSelector)}registerCheckboxTableRowSelectionAction(){new RegularEvent("click",((e,t)=>{const c=e.target.tagName;if("TH"!==c&&"TD"!==c)return;const o=t.querySelector(MultiRecordSelectionSelectors.checkboxSelector);null!==o&&(MultiRecordSelection.changeCheckboxState(o,!o.checked),this.handleCheckboxKeyboardActions(e,o,!1))})).delegateTo(document,MultiRecordSelectionSelectors.elementSelector),new RegularEvent("mousedown",(e=>(e.shiftKey||e.altKey||e.ctrlKey)&&e.preventDefault())).delegateTo(document,MultiRecordSelectionSelectors.elementSelector)}registerDispatchCheckboxStateChangedEvent(){new RegularEvent("change",((e,t)=>{t.dispatchEvent(new CustomEvent("multiRecordSelection:checkbox:state:changed",{detail:{identifier:MultiRecordSelection.getIdentifier(t)},bubbles:!0,cancelable:!1}))})).delegateTo(document,MultiRecordSelectionSelectors.checkboxSelector)}registerCheckboxStateChangedEventHandler(){new RegularEvent("multiRecordSelection:checkbox:state:changed",(e=>{const t=e.target,c=e.detail?.identifier||"";t.checked?t.closest(MultiRecordSelectionSelectors.elementSelector).classList.add(MultiRecordSelection.activeClass):t.closest(MultiRecordSelectionSelectors.elementSelector).classList.remove(MultiRecordSelection.activeClass),MultiRecordSelection.toggleActionsState(c)})).bindTo(document)}registerToggleCheckboxActions(){new RegularEvent("click",((e,t)=>{const c=MultiRecordSelection.getIdentifier(t),o=document.querySelector([MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.checkboxActionsSelector,c),'button[data-multi-record-selection-check-action="'+CheckboxActions.checkAll+'"]'].join(" "));null!==o&&o.classList.toggle("disabled",!MultiRecordSelection.getCheckboxes(CheckboxState.unchecked,c).length);const i=document.querySelector([MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.checkboxActionsSelector,c),'button[data-multi-record-selection-check-action="'+CheckboxActions.checkNone+'"]'].join(" "));null!==i&&i.classList.toggle("disabled",!MultiRecordSelection.getCheckboxes(CheckboxState.checked,c).length);const l=document.querySelector([MultiRecordSelection.getCombinedSelector(MultiRecordSelectionSelectors.checkboxActionsSelector,c),'button[data-multi-record-selection-check-action="'+CheckboxActions.toggle+'"]'].join(" "));null!==l&&l.classList.toggle("disabled",!MultiRecordSelection.getCheckboxes(CheckboxState.any,c).length)})).delegateTo(document,MultiRecordSelectionSelectors.checkboxActionsToggleSelector)}handleCheckboxKeyboardActions(e,t,c=!0){const o=MultiRecordSelection.getIdentifier(t);if(this.lastChecked&&document.body.contains(this.lastChecked)&&MultiRecordSelection.getIdentifier(this.lastChecked)===o&&(e.shiftKey||e.altKey||e.ctrlKey)){if(c&&MultiRecordSelection.unsetManuallyChangedAttribute(o),e.shiftKey){const e=Array.from(MultiRecordSelection.getCheckboxes(CheckboxState.any,o)),c=e.indexOf(t),i=e.indexOf(this.lastChecked);e.slice(Math.min(c,i),Math.max(c,i)+1).forEach((e=>{e!==t&&MultiRecordSelection.changeCheckboxState(e,t.checked)}))}this.lastChecked=t,(e.altKey||e.ctrlKey)&&MultiRecordSelection.getCheckboxes(CheckboxState.any,o).forEach((e=>{e!==t&&MultiRecordSelection.changeCheckboxState(e,!e.checked)})),MultiRecordSelection.unsetManuallyChangedAttribute(o)}else this.lastChecked=t}}MultiRecordSelection.activeClass="active",MultiRecordSelection.disabledClass="disabled";export default new MultiRecordSelection; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/multi-step-wizard.js b/typo3/sysext/backend/Resources/Public/JavaScript/multi-step-wizard.js index 1490f23de708..8f9ef1611ff0 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/multi-step-wizard.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/multi-step-wizard.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{SeverityEnum}from"@typo3/backend/enum/severity.js";import $ from"jquery";import Modal from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import Icons from"@typo3/backend/icons.js";class MultiStepWizard{constructor(){this.setup={slides:[],settings:{},forceSelection:!0,$carousel:null},this.originalSetup=$.extend(!0,{},this.setup)}set(t,e){return this.setup.settings[t]=e,this}addSlide(t,e,s="",i=SeverityEnum.info,r,a){const l={identifier:t,title:e,content:s,severity:i,progressBarTitle:r,callback:a};return this.setup.slides.push(l),this}addFinalProcessingSlide(t){return t||(t=()=>{this.dismiss()}),Icons.getIcon("spinner-circle",Icons.sizes.default,null,null).then((e=>{let s=$("<div />",{class:"text-center"}).append(e);this.addSlide("final-processing-slide",top.TYPO3.lang["wizard.processing.title"],s[0].outerHTML,Severity.info,null,t)}))}show(){let t=this.generateSlides(),e=this.setup.slides[0];Modal.advanced({title:e.title,content:t,severity:e.severity,staticBackdrop:!0,buttons:[{text:top.TYPO3.lang["wizard.button.cancel"],active:!0,btnClass:"btn-default float-start",name:"cancel",trigger:()=>{this.getComponent().trigger("wizard-dismiss")}},{text:top.TYPO3.lang["wizard.button.prev"],btnClass:"btn-"+Severity.getCssClass(e.severity),name:"prev"},{text:top.TYPO3.lang["wizard.button.next"],btnClass:"btn-"+Severity.getCssClass(e.severity),name:"next"}],additionalCssClasses:["modal-multi-step-wizard"],callback:()=>{this.addButtonContainer(),this.addProgressBar(),this.initializeEvents()}});this.getComponent().on("wizard-visible",(()=>{this.runSlideCallback(e,this.setup.$carousel.find(".carousel-item").first())})).on("wizard-dismissed",(()=>{this.setup=$.extend(!0,{},this.originalSetup)}))}getComponent(){return null===this.setup.$carousel&&this.generateSlides(),this.setup.$carousel}dismiss(){Modal.dismiss()}lockNextStep(){let t=this.setup.$carousel.closest(".modal").find('button[name="next"]');return t.prop("disabled",!0),t}unlockNextStep(){let t=this.setup.$carousel.closest(".modal").find('button[name="next"]');return t.prop("disabled",!1),t}lockPrevStep(){let t=this.setup.$carousel.closest(".modal").find('button[name="prev"]');return t.prop("disabled",!0),t}unlockPrevStep(){let t=this.setup.$carousel.closest(".modal").find('button[name="prev"]');return t.prop("disabled",!1),t}triggerStepButton(t){let e=this.setup.$carousel.closest(".modal").find('button[name="'+t+'"]');return e.length>0&&!0!==e.prop("disabled")&&e.trigger("click"),e}blurCancelStep(){let t=this.setup.$carousel.closest(".modal").find('button[name="cancel"]');return t.trigger("blur"),t}initializeEvents(){let t=this.setup.$carousel.closest(".modal");this.initializeSlideNextEvent(t),this.initializeSlidePrevEvent(t),this.setup.$carousel.on("slide.bs.carousel",(e=>{"left"===e.direction?this.nextSlideChanges(t):this.prevSlideChanges(t)})).on("slid.bs.carousel",(t=>{let e=this.setup.$carousel.data("currentIndex"),s=this.setup.slides[e];this.runSlideCallback(s,$(t.relatedTarget)),this.setup.forceSelection&&this.lockNextStep()}));let e=this.getComponent();e.on("wizard-dismiss",this.dismiss),Modal.currentModal.addEventListener("typo3-modal-hidden",(()=>{e.trigger("wizard-dismissed")})),Modal.currentModal.addEventListener("typo3-modal-shown",(()=>{e.trigger("wizard-visible")}))}initializeSlideNextEvent(t){t.find(".modal-footer").find('button[name="next"]').off().on("click",(()=>{this.setup.$carousel.carousel("next")}))}initializeSlidePrevEvent(t){t.find(".modal-footer").find('button[name="prev"]').off().on("click",(()=>{this.setup.$carousel.carousel("prev")}))}nextSlideChanges(t){this.initializeSlideNextEvent(t);const e=t.find(".modal-title"),s=t.find(".modal-footer"),i=this.setup.$carousel.data("currentSlide")+1,r=this.setup.$carousel.data("currentIndex"),a=r+1;e.text(this.setup.slides[a].title),this.setup.$carousel.data("currentSlide",i),this.setup.$carousel.data("currentIndex",a);const l=s.find(".progress-bar");l.eq(r).width("0%"),l.eq(a).width(this.setup.$carousel.data("initialStep")*i+"%").removeClass("inactive"),this.updateCurrentSeverity(t,r,a)}prevSlideChanges(t){this.initializeSlidePrevEvent(t);const e=t.find(".modal-title"),s=t.find(".modal-footer"),i=s.find('button[name="next"]'),r=this.setup.$carousel.data("currentSlide")-1,a=this.setup.$carousel.data("currentIndex"),l=a-1;this.setup.$carousel.data("currentSlide",r),this.setup.$carousel.data("currentIndex",l),e.text(this.setup.slides[l].title),s.find(".progress-bar.last-step").width(this.setup.$carousel.data("initialStep")+"%").text(this.getProgressBarTitle(this.setup.$carousel.data("slideCount")-1)),i.text(top.TYPO3.lang["wizard.button.next"]);const n=s.find(".progress-bar");n.eq(a).width(this.setup.$carousel.data("initialStep")+"%").addClass("inactive"),n.eq(l).width(this.setup.$carousel.data("initialStep")*r+"%").removeClass("inactive"),this.updateCurrentSeverity(t,a,l)}updateCurrentSeverity(t,e,s){t.find(".modal-footer").find('button[name="next"]').removeClass("btn-"+Severity.getCssClass(this.setup.slides[e].severity)).addClass("btn-"+Severity.getCssClass(this.setup.slides[s].severity)),t.removeClass("modal-severity-"+Severity.getCssClass(this.setup.slides[e].severity)).addClass("modal-severity-"+Severity.getCssClass(this.setup.slides[s].severity))}getProgressBarTitle(t){let e;return e=null===this.setup.slides[t].progressBarTitle?0===t?top.TYPO3.lang["wizard.progressStep.start"]:t>=this.setup.$carousel.data("slideCount")-1?top.TYPO3.lang["wizard.progressStep.finish"]:top.TYPO3.lang["wizard.progressStep"]+String(t+1):this.setup.slides[t].progressBarTitle,e}runSlideCallback(t,e){"function"==typeof t.callback&&t.callback(e,this.setup.settings,t.identifier)}addProgressBar(){let t,e=this.setup.$carousel.find(".carousel-item").length,s=Math.max(1,e),i=this.setup.$carousel.closest(".modal").find(".modal-footer");if(t=Math.round(100/s),this.setup.$carousel.data("initialStep",t).data("slideCount",s).data("realSlideCount",e).data("currentIndex",0).data("currentSlide",1),s>1){i.prepend($("<div />",{class:"progress"}));for(let e=0;e<this.setup.slides.length;++e){let s;s=0===e?"progress-bar first-step":e===this.setup.$carousel.data("slideCount")-1?"progress-bar last-step inactive":"progress-bar step inactive",i.find(".progress").append($("<div />",{role:"progressbar",class:s,"aria-valuemin":0,"aria-valuenow":t,"aria-valuemax":100}).width(t+"%").text(this.getProgressBarTitle(e)))}}}addButtonContainer(){this.setup.$carousel.closest(".modal").find(".modal-footer .btn").wrapAll('<div class="modal-btn-group" />')}generateSlides(){if(null!==this.setup.$carousel)return this.setup.$carousel;let t='<div class="carousel slide" data-bs-ride="false"><div class="carousel-inner" role="listbox">';for(let e=0;e<this.setup.slides.length;++e){let s=this.setup.slides[e],i=s.content;"object"==typeof i&&(i=i.html()),t+='<div class="carousel-item" data-bs-slide="'+s.identifier+'" data-step="'+e+'">'+i+"</div>"}return t+="</div></div>",this.setup.$carousel=$(t),this.setup.$carousel.find(".carousel-item").first().addClass("active"),this.setup.$carousel}}let multistepWizardObject;try{window.opener&&window.opener.TYPO3&&window.opener.TYPO3.MultiStepWizard&&(multistepWizardObject=window.opener.TYPO3.MultiStepWizard),parent&&parent.window.TYPO3&&parent.window.TYPO3.MultiStepWizard&&(multistepWizardObject=parent.window.TYPO3.MultiStepWizard),top&&top.TYPO3&&top.TYPO3.MultiStepWizard&&(multistepWizardObject=top.TYPO3.MultiStepWizard)}catch(t){}multistepWizardObject||(multistepWizardObject=new MultiStepWizard,"undefined"!=typeof TYPO3&&(TYPO3.MultiStepWizard=multistepWizardObject));export default multistepWizardObject; \ No newline at end of file +import{SeverityEnum}from"@typo3/backend/enum/severity.js";import $ from"jquery";import Modal from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import Icons from"@typo3/backend/icons.js";class MultiStepWizard{constructor(){this.setup={slides:[],settings:{},forceSelection:!0,$carousel:null},this.originalSetup=$.extend(!0,{},this.setup)}set(t,e){return this.setup.settings[t]=e,this}addSlide(t,e,s="",i=SeverityEnum.info,r,a){const l={identifier:t,title:e,content:s,severity:i,progressBarTitle:r,callback:a};return this.setup.slides.push(l),this}addFinalProcessingSlide(t){return t||(t=()=>{this.dismiss()}),Icons.getIcon("spinner-circle",Icons.sizes.default,null,null).then((e=>{const s=$("<div />",{class:"text-center"}).append(e);this.addSlide("final-processing-slide",top.TYPO3.lang["wizard.processing.title"],s[0].outerHTML,Severity.info,null,t)}))}show(){const t=this.generateSlides(),e=this.setup.slides[0];Modal.advanced({title:e.title,content:t,severity:e.severity,staticBackdrop:!0,buttons:[{text:top.TYPO3.lang["wizard.button.cancel"],active:!0,btnClass:"btn-default float-start",name:"cancel",trigger:()=>{this.getComponent().trigger("wizard-dismiss")}},{text:top.TYPO3.lang["wizard.button.prev"],btnClass:"btn-"+Severity.getCssClass(e.severity),name:"prev"},{text:top.TYPO3.lang["wizard.button.next"],btnClass:"btn-"+Severity.getCssClass(e.severity),name:"next"}],additionalCssClasses:["modal-multi-step-wizard"],callback:()=>{this.addButtonContainer(),this.addProgressBar(),this.initializeEvents()}}),this.getComponent().on("wizard-visible",(()=>{this.runSlideCallback(e,this.setup.$carousel.find(".carousel-item").first())})).on("wizard-dismissed",(()=>{this.setup=$.extend(!0,{},this.originalSetup)}))}getComponent(){return null===this.setup.$carousel&&this.generateSlides(),this.setup.$carousel}dismiss(){Modal.dismiss()}lockNextStep(){const t=this.setup.$carousel.closest(".modal").find('button[name="next"]');return t.prop("disabled",!0),t}unlockNextStep(){const t=this.setup.$carousel.closest(".modal").find('button[name="next"]');return t.prop("disabled",!1),t}lockPrevStep(){const t=this.setup.$carousel.closest(".modal").find('button[name="prev"]');return t.prop("disabled",!0),t}unlockPrevStep(){const t=this.setup.$carousel.closest(".modal").find('button[name="prev"]');return t.prop("disabled",!1),t}triggerStepButton(t){const e=this.setup.$carousel.closest(".modal").find('button[name="'+t+'"]');return e.length>0&&!0!==e.prop("disabled")&&e.trigger("click"),e}blurCancelStep(){const t=this.setup.$carousel.closest(".modal").find('button[name="cancel"]');return t.trigger("blur"),t}initializeEvents(){const t=this.setup.$carousel.closest(".modal");this.initializeSlideNextEvent(t),this.initializeSlidePrevEvent(t),this.setup.$carousel.on("slide.bs.carousel",(e=>{"left"===e.direction?this.nextSlideChanges(t):this.prevSlideChanges(t)})).on("slid.bs.carousel",(t=>{const e=this.setup.$carousel.data("currentIndex"),s=this.setup.slides[e];this.runSlideCallback(s,$(t.relatedTarget)),this.setup.forceSelection&&this.lockNextStep()}));const e=this.getComponent();e.on("wizard-dismiss",this.dismiss),Modal.currentModal.addEventListener("typo3-modal-hidden",(()=>{e.trigger("wizard-dismissed")})),Modal.currentModal.addEventListener("typo3-modal-shown",(()=>{e.trigger("wizard-visible")}))}initializeSlideNextEvent(t){t.find(".modal-footer").find('button[name="next"]').off().on("click",(()=>{this.setup.$carousel.carousel("next")}))}initializeSlidePrevEvent(t){t.find(".modal-footer").find('button[name="prev"]').off().on("click",(()=>{this.setup.$carousel.carousel("prev")}))}nextSlideChanges(t){this.initializeSlideNextEvent(t);const e=t.find(".modal-title"),s=t.find(".modal-footer"),i=this.setup.$carousel.data("currentSlide")+1,r=this.setup.$carousel.data("currentIndex"),a=r+1;e.text(this.setup.slides[a].title),this.setup.$carousel.data("currentSlide",i),this.setup.$carousel.data("currentIndex",a);const l=s.find(".progress-bar");l.eq(r).width("0%"),l.eq(a).width(this.setup.$carousel.data("initialStep")*i+"%").removeClass("inactive"),this.updateCurrentSeverity(t,r,a)}prevSlideChanges(t){this.initializeSlidePrevEvent(t);const e=t.find(".modal-title"),s=t.find(".modal-footer"),i=s.find('button[name="next"]'),r=this.setup.$carousel.data("currentSlide")-1,a=this.setup.$carousel.data("currentIndex"),l=a-1;this.setup.$carousel.data("currentSlide",r),this.setup.$carousel.data("currentIndex",l),e.text(this.setup.slides[l].title),s.find(".progress-bar.last-step").width(this.setup.$carousel.data("initialStep")+"%").text(this.getProgressBarTitle(this.setup.$carousel.data("slideCount")-1)),i.text(top.TYPO3.lang["wizard.button.next"]);const n=s.find(".progress-bar");n.eq(a).width(this.setup.$carousel.data("initialStep")+"%").addClass("inactive"),n.eq(l).width(this.setup.$carousel.data("initialStep")*r+"%").removeClass("inactive"),this.updateCurrentSeverity(t,a,l)}updateCurrentSeverity(t,e,s){t.find(".modal-footer").find('button[name="next"]').removeClass("btn-"+Severity.getCssClass(this.setup.slides[e].severity)).addClass("btn-"+Severity.getCssClass(this.setup.slides[s].severity)),t.removeClass("modal-severity-"+Severity.getCssClass(this.setup.slides[e].severity)).addClass("modal-severity-"+Severity.getCssClass(this.setup.slides[s].severity))}getProgressBarTitle(t){let e;return e=null===this.setup.slides[t].progressBarTitle?0===t?top.TYPO3.lang["wizard.progressStep.start"]:t>=this.setup.$carousel.data("slideCount")-1?top.TYPO3.lang["wizard.progressStep.finish"]:top.TYPO3.lang["wizard.progressStep"]+String(t+1):this.setup.slides[t].progressBarTitle,e}runSlideCallback(t,e){"function"==typeof t.callback&&t.callback(e,this.setup.settings,t.identifier)}addProgressBar(){const t=this.setup.$carousel.find(".carousel-item").length,e=Math.max(1,t),s=Math.round(100/e),i=this.setup.$carousel.closest(".modal").find(".modal-footer");if(this.setup.$carousel.data("initialStep",s).data("slideCount",e).data("realSlideCount",t).data("currentIndex",0).data("currentSlide",1),e>1){i.prepend($("<div />",{class:"progress"}));for(let t=0;t<this.setup.slides.length;++t){let e;e=0===t?"progress-bar first-step":t===this.setup.$carousel.data("slideCount")-1?"progress-bar last-step inactive":"progress-bar step inactive",i.find(".progress").append($("<div />",{role:"progressbar",class:e,"aria-valuemin":0,"aria-valuenow":s,"aria-valuemax":100}).width(s+"%").text(this.getProgressBarTitle(t)))}}}addButtonContainer(){this.setup.$carousel.closest(".modal").find(".modal-footer .btn").wrapAll('<div class="modal-btn-group" />')}generateSlides(){if(null!==this.setup.$carousel)return this.setup.$carousel;let t='<div class="carousel slide" data-bs-ride="false"><div class="carousel-inner" role="listbox">';for(let e=0;e<this.setup.slides.length;++e){const s=this.setup.slides[e];let i=s.content;"object"==typeof i&&(i=i.html()),t+='<div class="carousel-item" data-bs-slide="'+s.identifier+'" data-step="'+e+'">'+i+"</div>"}return t+="</div></div>",this.setup.$carousel=$(t),this.setup.$carousel.find(".carousel-item").first().addClass("active"),this.setup.$carousel}}let multistepWizardObject;try{window.opener&&window.opener.TYPO3&&window.opener.TYPO3.MultiStepWizard&&(multistepWizardObject=window.opener.TYPO3.MultiStepWizard),parent&&parent.window.TYPO3&&parent.window.TYPO3.MultiStepWizard&&(multistepWizardObject=parent.window.TYPO3.MultiStepWizard),top&&top.TYPO3&&top.TYPO3.MultiStepWizard&&(multistepWizardObject=top.TYPO3.MultiStepWizard)}catch(t){}multistepWizardObject||(multistepWizardObject=new MultiStepWizard,"undefined"!=typeof TYPO3&&(TYPO3.MultiStepWizard=multistepWizardObject));export default multistepWizardObject; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/new-content-element-wizard.js b/typo3/sysext/backend/Resources/Public/JavaScript/new-content-element-wizard.js index 0e593001fe9d..158d742987f0 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/new-content-element-wizard.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/new-content-element-wizard.js @@ -10,7 +10,7 @@ * * The TYPO3 project - inspiring people to share! */ -var __decorate=function(e,t,o,i){var r,a=arguments.length,n=a<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,o):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)n=Reflect.decorate(e,t,o,i);else for(var s=e.length-1;s>=0;s--)(r=e[s])&&(n=(a<3?r(n):a>3?r(t,o,n):r(t,o))||n);return a>3&&n&&Object.defineProperty(t,o,n),n};import{customElement,property}from"lit/decorators.js";import{html,css,LitElement,nothing}from"lit";import Modal from"@typo3/backend/modal.js";import"@typo3/backend/element/icon-element.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{lll}from"@typo3/core/lit-helper.js";import Notification from"@typo3/backend/notification.js";import Viewport from"@typo3/backend/viewport.js";import RegularEvent from"@typo3/core/event/regular-event.js";class Item{constructor(e,t,o,i,r,a,n,s){this.identifier=e,this.label=t,this.description=o,this.icon=i,this.url=r,this.requestType=a,this.defaultValues=n,this.saveAndClose=s,this.visible=!0}static fromData(e){return new Item(e.identifier,e.label,e.description,e.icon,e.url,e.requestType??"location",e.defaultValues??[],e.saveAndClose??!1)}reset(){this.visible=!0}}class Category{constructor(e,t,o){this.identifier=e,this.label=t,this.items=o,this.disabled=!1}static fromData(e){return new Category(e.identifier,e.label,e.items.map((e=>Item.fromData(e))))}reset(){this.disabled=!1,this.items.forEach((e=>{e.reset()}))}activeItems(){return this.items.filter((e=>e.visible))??[]}}class Categories{constructor(e){this.items=e}static fromData(e){return new Categories(Object.values(e).map((e=>Category.fromData(e))))}reset(){this.items.forEach((e=>{e.reset()}))}categoriesWithItems(){return this.items.filter((e=>e.activeItems().length>0))??[]}}let NewContentElementWizard=class extends LitElement{constructor(){super(),this.categories=new Categories([]),this.selectedCategory=null,this.searchTerm="",this.messages=[],this.toggleMenu=!1}firstUpdated(){let e=document.createElement("link");e.setAttribute("rel","stylesheet"),e.setAttribute("href",TYPO3.settings.cssUrls.backend),this.shadowRoot.appendChild(e);this.renderRoot.querySelector('input[name="search"]').focus(),this.selectAvailableCategory()}selectAvailableCategory(){0===this.categories.categoriesWithItems().filter((e=>e===this.selectedCategory)).length&&(this.selectedCategory=this.categories.categoriesWithItems()[0]??null),this.messages=[],null===this.selectedCategory&&(this.messages=[{message:lll("newContentElement.filter.noResults"),severity:"info"}])}filter(e){this.searchTerm=e,this.categories.reset(),this.categories.items.forEach((e=>{const t=e.label.trim().replace(/\s+/g," ");!(""!==this.searchTerm&&!RegExp(this.searchTerm,"i").test(t))||e.items.forEach((e=>{const t=e.label.trim().replace(/\s+/g," ")+e.description.trim().replace(/\s+/g," ");e.visible=!(""!==this.searchTerm&&!RegExp(this.searchTerm,"i").test(t))})),e.disabled=0===e.items.filter((e=>e.visible)).length})),this.selectAvailableCategory()}render(){return html` +var __decorate=function(e,t,o,i){var r,a=arguments.length,n=a<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,o):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)n=Reflect.decorate(e,t,o,i);else for(var s=e.length-1;s>=0;s--)(r=e[s])&&(n=(a<3?r(n):a>3?r(t,o,n):r(t,o))||n);return a>3&&n&&Object.defineProperty(t,o,n),n};import{customElement,property}from"lit/decorators.js";import{html,css,LitElement,nothing}from"lit";import Modal from"@typo3/backend/modal.js";import"@typo3/backend/element/icon-element.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{lll}from"@typo3/core/lit-helper.js";import Notification from"@typo3/backend/notification.js";import Viewport from"@typo3/backend/viewport.js";import RegularEvent from"@typo3/core/event/regular-event.js";class Item{constructor(e,t,o,i,r,a,n,s){this.identifier=e,this.label=t,this.description=o,this.icon=i,this.url=r,this.requestType=a,this.defaultValues=n,this.saveAndClose=s,this.visible=!0}static fromData(e){return new Item(e.identifier,e.label,e.description,e.icon,e.url,e.requestType??"location",e.defaultValues??[],e.saveAndClose??!1)}reset(){this.visible=!0}}class Category{constructor(e,t,o){this.identifier=e,this.label=t,this.items=o,this.disabled=!1}static fromData(e){return new Category(e.identifier,e.label,e.items.map((e=>Item.fromData(e))))}reset(){this.disabled=!1,this.items.forEach((e=>{e.reset()}))}activeItems(){return this.items.filter((e=>e.visible))??[]}}class Categories{constructor(e){this.items=e}static fromData(e){return new Categories(Object.values(e).map((e=>Category.fromData(e))))}reset(){this.items.forEach((e=>{e.reset()}))}categoriesWithItems(){return this.items.filter((e=>e.activeItems().length>0))??[]}}let NewContentElementWizard=class extends LitElement{constructor(){super(),this.categories=new Categories([]),this.selectedCategory=null,this.searchTerm="",this.messages=[],this.toggleMenu=!1}firstUpdated(){const e=document.createElement("link");e.setAttribute("rel","stylesheet"),e.setAttribute("href",TYPO3.settings.cssUrls.backend),this.shadowRoot.appendChild(e);this.renderRoot.querySelector('input[name="search"]').focus(),this.selectAvailableCategory()}selectAvailableCategory(){0===this.categories.categoriesWithItems().filter((e=>e===this.selectedCategory)).length&&(this.selectedCategory=this.categories.categoriesWithItems()[0]??null),this.messages=[],null===this.selectedCategory&&(this.messages=[{message:lll("newContentElement.filter.noResults"),severity:"info"}])}filter(e){this.searchTerm=e,this.categories.reset(),this.categories.items.forEach((e=>{const t=e.label.trim().replace(/\s+/g," ");!(""!==this.searchTerm&&!RegExp(this.searchTerm,"i").test(t))||e.items.forEach((e=>{const t=e.label.trim().replace(/\s+/g," ")+e.description.trim().replace(/\s+/g," ");e.visible=!(""!==this.searchTerm&&!RegExp(this.searchTerm,"i").test(t))})),e.disabled=0===e.items.filter((e=>e.visible)).length})),this.selectAvailableCategory()}render(){return html` <div class="element"> ${this.renderFilter()} ${this.renderMessages()} diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/new-multiple-pages.js b/typo3/sysext/backend/Resources/Public/JavaScript/new-multiple-pages.js index 3d49d03c56ee..66ecef6d9c59 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/new-multiple-pages.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/new-multiple-pages.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DocumentService from"@typo3/core/document-service.js";import RegularEvent from"@typo3/core/event/regular-event.js";var Identifiers;!function(e){e.containerSelector=".t3js-newmultiplepages-container",e.addMoreFieldsButtonSelector=".t3js-newmultiplepages-createnewfields",e.pageTitleSelector=".t3js-newmultiplepages-page-title",e.doktypeSelector=".t3js-newmultiplepages-select-doktype",e.resetFieldsSelector=".t3js-newmultiplepages-reset-fields",e.templateRow=".t3js-newmultiplepages-newlinetemplate"}(Identifiers||(Identifiers={}));class NewMultiplePages{constructor(){this.lineCounter=5,DocumentService.ready().then((()=>{this.initializeEvents()}))}initializeEvents(){new RegularEvent("click",this.createNewFormFields.bind(this)).delegateTo(document,Identifiers.addMoreFieldsButtonSelector),new RegularEvent("change",this.actOnPageTitleChange).delegateTo(document,Identifiers.pageTitleSelector),new RegularEvent("change",this.actOnTypeSelectChange).delegateTo(document,Identifiers.doktypeSelector),new RegularEvent("click",this.resetFieldAttributes).delegateTo(document,Identifiers.resetFieldsSelector)}createNewFormFields(){const e=document.querySelector(Identifiers.containerSelector),t=document.querySelector(Identifiers.templateRow)?.innerHTML||"";if(null!==e&&""!==t){for(let i=0;i<5;i++){const n=this.lineCounter+i+1;e.innerHTML+=t.replace(/\[0\]/g,(this.lineCounter+i).toString()).replace(/\[1\]/g,n.toString())}this.lineCounter+=5}}actOnPageTitleChange(){this.setAttribute("value",this.value)}actOnTypeSelectChange(){for(let e of this.options)e.removeAttribute("selected");const e=this.options[this.selectedIndex],t=document.querySelector(this.dataset.target);null!==e&&null!==t&&(e.setAttribute("selected","selected"),t.innerHTML=e.dataset.icon)}resetFieldAttributes(){document.querySelectorAll(Identifiers.containerSelector+" "+Identifiers.pageTitleSelector).forEach((e=>{e.removeAttribute("value")})),document.querySelectorAll(Identifiers.containerSelector+" "+Identifiers.doktypeSelector).forEach((e=>{for(const t of e)t.removeAttribute("selected");const t=e.options[0]?.dataset.icon,i=document.querySelector(e.dataset.target);t&&null!==i&&(i.innerHTML=t)}))}}export default new NewMultiplePages; \ No newline at end of file +import DocumentService from"@typo3/core/document-service.js";import RegularEvent from"@typo3/core/event/regular-event.js";var Identifiers;!function(e){e.containerSelector=".t3js-newmultiplepages-container",e.addMoreFieldsButtonSelector=".t3js-newmultiplepages-createnewfields",e.pageTitleSelector=".t3js-newmultiplepages-page-title",e.doktypeSelector=".t3js-newmultiplepages-select-doktype",e.resetFieldsSelector=".t3js-newmultiplepages-reset-fields",e.templateRow=".t3js-newmultiplepages-newlinetemplate"}(Identifiers||(Identifiers={}));class NewMultiplePages{constructor(){this.lineCounter=5,DocumentService.ready().then((()=>{this.initializeEvents()}))}initializeEvents(){new RegularEvent("click",this.createNewFormFields.bind(this)).delegateTo(document,Identifiers.addMoreFieldsButtonSelector),new RegularEvent("change",this.actOnPageTitleChange).delegateTo(document,Identifiers.pageTitleSelector),new RegularEvent("change",this.actOnTypeSelectChange).delegateTo(document,Identifiers.doktypeSelector),new RegularEvent("click",this.resetFieldAttributes).delegateTo(document,Identifiers.resetFieldsSelector)}createNewFormFields(){const e=document.querySelector(Identifiers.containerSelector),t=document.querySelector(Identifiers.templateRow)?.innerHTML||"";if(null!==e&&""!==t){for(let i=0;i<5;i++){const n=this.lineCounter+i+1;e.innerHTML+=t.replace(/\[0\]/g,(this.lineCounter+i).toString()).replace(/\[1\]/g,n.toString())}this.lineCounter+=5}}actOnPageTitleChange(){this.setAttribute("value",this.value)}actOnTypeSelectChange(){for(const e of this.options)e.removeAttribute("selected");const e=this.options[this.selectedIndex],t=document.querySelector(this.dataset.target);null!==e&&null!==t&&(e.setAttribute("selected","selected"),t.innerHTML=e.dataset.icon)}resetFieldAttributes(){document.querySelectorAll(Identifiers.containerSelector+" "+Identifiers.pageTitleSelector).forEach((e=>{e.removeAttribute("value")})),document.querySelectorAll(Identifiers.containerSelector+" "+Identifiers.doktypeSelector).forEach((e=>{for(const t of e)t.removeAttribute("selected");const t=e.options[0]?.dataset.icon,i=document.querySelector(e.dataset.target);t&&null!==i&&(i.innerHTML=t)}))}}export default new NewMultiplePages; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/notification.js b/typo3/sysext/backend/Resources/Public/JavaScript/notification.js index 4dfcf6bced81..ab089429f98f 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/notification.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/notification.js @@ -15,7 +15,7 @@ var __decorate=function(t,e,i,o){var s,a=arguments.length,n=a<3?e:null===o?o=Obj id="${ifDefined(this.notificationId||void 0)}" class="${"alert alert-"+t+" alert-dismissible fade"+(this.visible?" in":"")}" role="alert"> - <button type="button" class="close" @click="${async t=>this.close()}"> + <button type="button" class="close" @click="${async()=>this.close()}"> <span aria-hidden="true"><typo3-backend-icon identifier="actions-close" size="small"></typo3-backend-icon></span> <span class="visually-hidden">Close</span> </button> diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/page-actions.js b/typo3/sysext/backend/Resources/Public/JavaScript/page-actions.js index f7f507bc2493..8c67a92e1493 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/page-actions.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/page-actions.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DocumentService from"@typo3/core/document-service.js";import RegularEvent from"@typo3/core/event/regular-event.js";import PersistentStorage from"@typo3/backend/storage/persistent.js";import"@typo3/backend/element/icon-element.js";var IdentifierEnum;!function(e){e.hiddenElements=".t3js-hidden-record"}(IdentifierEnum||(IdentifierEnum={}));class PageActions{constructor(){DocumentService.ready().then((()=>{const e=document.getElementById("checkShowHidden");null!==e&&new RegularEvent("change",this.toggleContentElementVisibility).bindTo(e)}))}toggleContentElementVisibility(e){const t=e.target,n=document.querySelectorAll(IdentifierEnum.hiddenElements),i=document.createElement("span");i.classList.add("form-check-spinner"),i.append(document.createRange().createContextualFragment('<typo3-backend-icon identifier="spinner-circle" size="small"></typo3-backend-icon>')),t.hidden=!0,t.insertAdjacentElement("afterend",i);for(let e of n){e.style.display="block";const n=e.scrollHeight;e.style.display="",t.checked?e.style.height=n+"px":requestAnimationFrame((function(){e.style.height=n+"px",requestAnimationFrame((function(){e.style.height="0px"}))}))}PersistentStorage.set("moduleData.web_layout.showHidden",t.checked?"1":"0").then((()=>{t.hidden=!1,i.remove()}))}}export default new PageActions; \ No newline at end of file +import DocumentService from"@typo3/core/document-service.js";import RegularEvent from"@typo3/core/event/regular-event.js";import PersistentStorage from"@typo3/backend/storage/persistent.js";import"@typo3/backend/element/icon-element.js";var IdentifierEnum;!function(e){e.hiddenElements=".t3js-hidden-record"}(IdentifierEnum||(IdentifierEnum={}));class PageActions{constructor(){DocumentService.ready().then((()=>{const e=document.getElementById("checkShowHidden");null!==e&&new RegularEvent("change",this.toggleContentElementVisibility).bindTo(e)}))}toggleContentElementVisibility(e){const t=e.target,n=document.querySelectorAll(IdentifierEnum.hiddenElements),i=document.createElement("span");i.classList.add("form-check-spinner"),i.append(document.createRange().createContextualFragment('<typo3-backend-icon identifier="spinner-circle" size="small"></typo3-backend-icon>')),t.hidden=!0,t.insertAdjacentElement("afterend",i);for(const e of n){e.style.display="block";const n=e.scrollHeight;e.style.display="",t.checked?e.style.height=n+"px":requestAnimationFrame((function(){e.style.height=n+"px",requestAnimationFrame((function(){e.style.height="0px"}))}))}PersistentStorage.set("moduleData.web_layout.showHidden",t.checked?"1":"0").then((()=>{t.hidden=!1,i.remove()}))}}export default new PageActions; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/page-link-handler.js b/typo3/sysext/backend/Resources/Public/JavaScript/page-link-handler.js index 34fbc49db8bc..49bff0129d94 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/page-link-handler.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/page-link-handler.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import LinkBrowser from"@typo3/backend/link-browser.js";import RegularEvent from"@typo3/core/event/regular-event.js";class PageLinkHandler{constructor(){this.linkPageByTextfield=()=>{let e=document.getElementById("luid").value;if(!e)return;const t=parseInt(e,10);isNaN(t)||(e="t3://page?uid="+t),LinkBrowser.finalizeFunction(e)},new RegularEvent("click",((e,t)=>{e.preventDefault(),LinkBrowser.finalizeFunction(t.getAttribute("href"))})).delegateTo(document,"a.t3js-pageLink"),new RegularEvent("click",((e,t)=>{e.preventDefault(),LinkBrowser.finalizeFunction(document.body.dataset.currentLink)})).delegateTo(document,"input.t3js-linkCurrent"),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.linkPageByTextfield()})).delegateTo(document,"input.t3js-pageLink")}}export default new PageLinkHandler; \ No newline at end of file +import LinkBrowser from"@typo3/backend/link-browser.js";import RegularEvent from"@typo3/core/event/regular-event.js";class PageLinkHandler{constructor(){this.linkPageByTextfield=()=>{let e=document.getElementById("luid").value;if(!e)return;const t=parseInt(e,10);isNaN(t)||(e="t3://page?uid="+t),LinkBrowser.finalizeFunction(e)},new RegularEvent("click",((e,t)=>{e.preventDefault(),LinkBrowser.finalizeFunction(t.getAttribute("href"))})).delegateTo(document,"a.t3js-pageLink"),new RegularEvent("click",(e=>{e.preventDefault(),LinkBrowser.finalizeFunction(document.body.dataset.currentLink)})).delegateTo(document,"input.t3js-linkCurrent"),new RegularEvent("click",(e=>{e.preventDefault(),this.linkPageByTextfield()})).delegateTo(document,"input.t3js-pageLink")}}export default new PageLinkHandler; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/page-tree/page-tree-element.js b/typo3/sysext/backend/Resources/Public/JavaScript/page-tree/page-tree-element.js index 28a82f3c509e..9fff8c245bcd 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/page-tree/page-tree-element.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/page-tree/page-tree-element.js @@ -10,7 +10,7 @@ * * The TYPO3 project - inspiring people to share! */ -var __decorate=function(e,t,o,i){var n,s=arguments.length,r=s<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,o):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,o,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(r=(s<3?n(r):s>3?n(t,o,r):n(t,o))||r);return s>3&&r&&Object.defineProperty(t,o,r),r};import{html,LitElement,nothing}from"lit";import{customElement,property,query}from"lit/decorators.js";import{until}from"lit/directives/until.js";import{lll}from"@typo3/core/lit-helper.js";import{PageTree}from"@typo3/backend/page-tree/page-tree.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Persistent from"@typo3/backend/storage/persistent.js";import{ModuleUtility}from"@typo3/backend/module.js";import ContextMenu from"@typo3/backend/context-menu.js";import*as d3selection from"d3-selection";import{KeyTypesEnum as KeyTypes}from"@typo3/backend/enum/key-types.js";import{Toolbar}from"@typo3/backend/svg-tree.js";import{DragDrop,DraggablePositionEnum}from"@typo3/backend/tree/drag-drop.js";import Modal from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";export const navigationComponentName="typo3-backend-navigation-component-pagetree";let EditablePageTree=class extends PageTree{selectFirstNode(){this.selectNode(this.nodes[0],!0),this.focusNode(this.nodes[0])}sendChangeCommand(e){let t="",o=0;if(e.target&&(o=e.target.identifier,"after"===e.position&&(o=-o)),"new"===e.command)t="&data[pages][NEW_1][pid]="+o+"&data[pages][NEW_1][title]="+encodeURIComponent(e.name)+"&data[pages][NEW_1][doktype]="+e.type;else if("edit"===e.command)t="&data[pages]["+e.uid+"]["+e.nameSourceField+"]="+encodeURIComponent(e.title);else if("delete"===e.command){const o=ModuleStateStorage.current("web");e.uid===o.identifier&&this.selectFirstNode(),t="&cmd[pages]["+e.uid+"][delete]=1"}else t="cmd[pages]["+e.uid+"]["+e.command+"]="+o;this.requestTreeUpdate(t).then((e=>{e&&e.hasErrors?(this.errorNotification(e.messages,!1),this.nodesContainer.selectAll(".node").remove(),this.updateVisibleNodes(),this.nodesRemovePlaceholder()):this.refreshOrFilterTree()}))}focusNode(e){this.nodeIsEdit||super.focusNode(e)}nodesUpdate(e){return super.nodesUpdate.call(this,e).call(this.initializeDragForNode())}updateNodeBgClass(e){return super.updateNodeBgClass.call(this,e).call(this.initializeDragForNode())}initializeDragForNode(){return this.dragDrop.connectDragHandler(new PageTreeNodeDragHandler(this,this.dragDrop))}removeEditedText(){const e=d3selection.selectAll(".node-edit");if(e.size())try{e.remove(),this.nodeIsEdit=!1}catch(e){}}appendTextElement(e){let t=0;return super.appendTextElement(e).on("click",((e,o)=>{if("0"===o.identifier)return this.selectNode(o,!0),void this.focusNode(o);1==++t&&setTimeout((()=>{1===t?(this.selectNode(o,!0),this.focusNode(o)):this.editNodeLabel(o),t=0}),300)}))}sendEditNodeLabelCommand(e){const t="&data[pages]["+e.identifier+"]["+e.nameSourceField+"]="+encodeURIComponent(e.newName);this.requestTreeUpdate(t,e).then((t=>{t&&t.hasErrors?this.errorNotification(t.messages,!1):e.name=e.newName,this.refreshOrFilterTree()}))}requestTreeUpdate(e,t=null){return this.nodesAddPlaceholder(t),new AjaxRequest(top.TYPO3.settings.ajaxUrls.record_process).post(e,{headers:{"Content-Type":"application/x-www-form-urlencoded","X-Requested-With":"XMLHttpRequest"}}).then((e=>e.resolve())).catch((e=>{this.errorNotification(e,!0)}))}editNodeLabel(e){e.allowEdit&&(this.disableFocusedNodes(),e.focused=!0,this.updateVisibleNodes(),this.removeEditedText(),this.nodeIsEdit=!0,d3selection.select(this.svg.node().parentNode).append("input").attr("class","node-edit").style("top",e.y+this.settings.marginTop+"px").style("left",e.x+this.textPosition+5+"px").style("width","calc(100% - "+(e.x+this.textPosition+5)+"px)").style("height",this.settings.nodeHeight+"px").attr("type","text").attr("value",e.name).on("keydown",(t=>{const o=t.keyCode;if(o===KeyTypes.ENTER||o===KeyTypes.TAB){const o=t.target.value.trim();this.nodeIsEdit=!1,this.removeEditedText(),o.length&&o!==e.name&&(e.nameSourceField=e.nameSourceField||"title",e.newName=o,this.sendEditNodeLabelCommand(e))}else o===KeyTypes.ESCAPE&&(this.nodeIsEdit=!1,this.removeEditedText());this.focusNode(e)})).on("blur",(t=>{if(!this.nodeIsEdit)return;const o=t.target.value.trim();o.length&&o!==e.name&&(e.nameSourceField=e.nameSourceField||"title",e.newName=o,this.sendEditNodeLabelCommand(e)),this.removeEditedText(),this.focusNode(e)})).node().select())}};EditablePageTree=__decorate([customElement("typo3-backend-navigation-component-pagetree-tree")],EditablePageTree);export{EditablePageTree};let PageTreeNavigationComponent=class extends LitElement{constructor(){super(...arguments),this.mountPointPath=null,this.configuration=null,this.refresh=()=>{this.tree.refreshOrFilterTree()},this.setMountPoint=e=>{this.setTemporaryMountPoint(e.detail.pageId)},this.selectFirstNode=()=>{this.tree.selectFirstNode()},this.toggleExpandState=e=>{const t=e.detail.node;t&&Persistent.set("BackendComponents.States.Pagetree.stateHash."+t.stateIdentifier,t.expanded?"1":"0")},this.loadContent=e=>{const t=e.detail.node;if(!t?.checked)return;if(ModuleStateStorage.update("web",t.identifier,!0,t.stateIdentifier.split("_")[0]),!1===e.detail.propagate)return;const o=top.TYPO3.ModuleMenu.App;let i=ModuleUtility.getFromName(o.getCurrentModule()).link;i+=i.includes("?")?"&":"?",top.TYPO3.Backend.ContentContainer.setUrl(i+"id="+t.identifier)},this.showContextMenu=e=>{const t=e.detail.node;t&&ContextMenu.show(t.itemType,parseInt(t.identifier,10),"tree","","",this.tree.getElementFromNode(t))},this.selectActiveNode=e=>{const t=ModuleStateStorage.current("web").selection;let o=e.detail.nodes;e.detail.nodes=o.map((e=>(e.stateIdentifier===t&&(e.checked=!0),e)))}}connectedCallback(){super.connectedCallback(),document.addEventListener("typo3:pagetree:refresh",this.refresh),document.addEventListener("typo3:pagetree:mountPoint",this.setMountPoint),document.addEventListener("typo3:pagetree:selectFirstNode",this.selectFirstNode)}disconnectedCallback(){document.removeEventListener("typo3:pagetree:refresh",this.refresh),document.removeEventListener("typo3:pagetree:mountPoint",this.setMountPoint),document.removeEventListener("typo3:pagetree:selectFirstNode",this.selectFirstNode),super.disconnectedCallback()}createRenderRoot(){return this}render(){return html` +var __decorate=function(e,t,o,i){var n,s=arguments.length,r=s<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,o):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,o,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(r=(s<3?n(r):s>3?n(t,o,r):n(t,o))||r);return s>3&&r&&Object.defineProperty(t,o,r),r};import{html,LitElement,nothing}from"lit";import{customElement,property,query}from"lit/decorators.js";import{until}from"lit/directives/until.js";import{lll}from"@typo3/core/lit-helper.js";import{PageTree}from"@typo3/backend/page-tree/page-tree.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Persistent from"@typo3/backend/storage/persistent.js";import{ModuleUtility}from"@typo3/backend/module.js";import ContextMenu from"@typo3/backend/context-menu.js";import*as d3selection from"d3-selection";import{KeyTypesEnum as KeyTypes}from"@typo3/backend/enum/key-types.js";import{Toolbar}from"@typo3/backend/svg-tree.js";import{DragDrop,DraggablePositionEnum}from"@typo3/backend/tree/drag-drop.js";import Modal from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";export const navigationComponentName="typo3-backend-navigation-component-pagetree";let EditablePageTree=class extends PageTree{selectFirstNode(){this.selectNode(this.nodes[0],!0),this.focusNode(this.nodes[0])}sendChangeCommand(e){let t="",o=0;if(e.target&&(o=e.target.identifier,"after"===e.position&&(o=-o)),"new"===e.command)t="&data[pages][NEW_1][pid]="+o+"&data[pages][NEW_1][title]="+encodeURIComponent(e.name)+"&data[pages][NEW_1][doktype]="+e.type;else if("edit"===e.command)t="&data[pages]["+e.uid+"]["+e.nameSourceField+"]="+encodeURIComponent(e.title);else if("delete"===e.command){const o=ModuleStateStorage.current("web");e.uid===o.identifier&&this.selectFirstNode(),t="&cmd[pages]["+e.uid+"][delete]=1"}else t="cmd[pages]["+e.uid+"]["+e.command+"]="+o;this.requestTreeUpdate(t).then((e=>{e&&e.hasErrors?(this.errorNotification(e.messages,!1),this.nodesContainer.selectAll(".node").remove(),this.updateVisibleNodes(),this.nodesRemovePlaceholder()):this.refreshOrFilterTree()}))}focusNode(e){this.nodeIsEdit||super.focusNode(e)}nodesUpdate(e){return super.nodesUpdate.call(this,e).call(this.initializeDragForNode())}updateNodeBgClass(e){return super.updateNodeBgClass.call(this,e).call(this.initializeDragForNode())}initializeDragForNode(){return this.dragDrop.connectDragHandler(new PageTreeNodeDragHandler(this,this.dragDrop))}removeEditedText(){const e=d3selection.selectAll(".node-edit");if(e.size())try{e.remove(),this.nodeIsEdit=!1}catch(e){}}appendTextElement(e){let t=0;return super.appendTextElement(e).on("click",((e,o)=>{if("0"===o.identifier)return this.selectNode(o,!0),void this.focusNode(o);1==++t&&setTimeout((()=>{1===t?(this.selectNode(o,!0),this.focusNode(o)):this.editNodeLabel(o),t=0}),300)}))}sendEditNodeLabelCommand(e){const t="&data[pages]["+e.identifier+"]["+e.nameSourceField+"]="+encodeURIComponent(e.newName);this.requestTreeUpdate(t,e).then((t=>{t&&t.hasErrors?this.errorNotification(t.messages,!1):e.name=e.newName,this.refreshOrFilterTree()}))}requestTreeUpdate(e,t=null){return this.nodesAddPlaceholder(t),new AjaxRequest(top.TYPO3.settings.ajaxUrls.record_process).post(e,{headers:{"Content-Type":"application/x-www-form-urlencoded","X-Requested-With":"XMLHttpRequest"}}).then((e=>e.resolve())).catch((e=>{this.errorNotification(e,!0)}))}editNodeLabel(e){e.allowEdit&&(this.disableFocusedNodes(),e.focused=!0,this.updateVisibleNodes(),this.removeEditedText(),this.nodeIsEdit=!0,d3selection.select(this.svg.node().parentNode).append("input").attr("class","node-edit").style("top",e.y+this.settings.marginTop+"px").style("left",e.x+this.textPosition+5+"px").style("width","calc(100% - "+(e.x+this.textPosition+5)+"px)").style("height",this.settings.nodeHeight+"px").attr("type","text").attr("value",e.name).on("keydown",(t=>{const o=t.keyCode;if(o===KeyTypes.ENTER||o===KeyTypes.TAB){const o=t.target.value.trim();this.nodeIsEdit=!1,this.removeEditedText(),o.length&&o!==e.name&&(e.nameSourceField=e.nameSourceField||"title",e.newName=o,this.sendEditNodeLabelCommand(e))}else o===KeyTypes.ESCAPE&&(this.nodeIsEdit=!1,this.removeEditedText());this.focusNode(e)})).on("blur",(t=>{if(!this.nodeIsEdit)return;const o=t.target.value.trim();o.length&&o!==e.name&&(e.nameSourceField=e.nameSourceField||"title",e.newName=o,this.sendEditNodeLabelCommand(e)),this.removeEditedText(),this.focusNode(e)})).node().select())}};EditablePageTree=__decorate([customElement("typo3-backend-navigation-component-pagetree-tree")],EditablePageTree);export{EditablePageTree};let PageTreeNavigationComponent=class extends LitElement{constructor(){super(...arguments),this.mountPointPath=null,this.configuration=null,this.refresh=()=>{this.tree.refreshOrFilterTree()},this.setMountPoint=e=>{this.setTemporaryMountPoint(e.detail.pageId)},this.selectFirstNode=()=>{this.tree.selectFirstNode()},this.toggleExpandState=e=>{const t=e.detail.node;t&&Persistent.set("BackendComponents.States.Pagetree.stateHash."+t.stateIdentifier,t.expanded?"1":"0")},this.loadContent=e=>{const t=e.detail.node;if(!t?.checked)return;if(ModuleStateStorage.update("web",t.identifier,!0,t.stateIdentifier.split("_")[0]),!1===e.detail.propagate)return;const o=top.TYPO3.ModuleMenu.App;let i=ModuleUtility.getFromName(o.getCurrentModule()).link;i+=i.includes("?")?"&":"?",top.TYPO3.Backend.ContentContainer.setUrl(i+"id="+t.identifier)},this.showContextMenu=e=>{const t=e.detail.node;t&&ContextMenu.show(t.itemType,parseInt(t.identifier,10),"tree","","",this.tree.getElementFromNode(t))},this.selectActiveNode=e=>{const t=ModuleStateStorage.current("web").selection,o=e.detail.nodes;e.detail.nodes=o.map((e=>(e.stateIdentifier===t&&(e.checked=!0),e)))}}connectedCallback(){super.connectedCallback(),document.addEventListener("typo3:pagetree:refresh",this.refresh),document.addEventListener("typo3:pagetree:mountPoint",this.setMountPoint),document.addEventListener("typo3:pagetree:selectFirstNode",this.selectFirstNode)}disconnectedCallback(){document.removeEventListener("typo3:pagetree:refresh",this.refresh),document.removeEventListener("typo3:pagetree:mountPoint",this.setMountPoint),document.removeEventListener("typo3:pagetree:selectFirstNode",this.selectFirstNode),super.disconnectedCallback()}createRenderRoot(){return this}render(){return html` <div id="typo3-pagetree" class="svg-tree"> ${until(this.renderTree(),this.renderLoader())} </div> @@ -78,4 +78,4 @@ var __decorate=function(e,t,o,i){var n,s=arguments.length,r=s<3?t:null===i?i=Obj </ul> </div> </div> - `}dragToolbar(e,t){return t.connectDragHandler(new ToolbarDragHandler(e,this.tree,t))}};__decorate([property({type:EditablePageTree})],PageTreeToolbar.prototype,"tree",void 0),PageTreeToolbar=__decorate([customElement("typo3-backend-navigation-component-pagetree-toolbar")],PageTreeToolbar);class PageTreeDragDrop extends DragDrop{getDropCommandDetails(e,t="",o=null){const i=this.tree.nodes,n=o.identifier;let s=this.tree.settings.nodeDragPosition,r=e||o;if(n===r.identifier&&"delete"!==t)return null;if(s===DraggablePositionEnum.BEFORE){const t=i.indexOf(e),o=this.setNodePositionAndTarget(t);if(null===o)return null;s=o.position,r=o.target}return{node:o,uid:n,target:r,position:s,command:t}}updateStateOfHoveredNode(e){const t=this.tree.svg.select(".node-over");if(t.size()&&this.tree.isOverSvg){this.createPositioningLine();let o=d3selection.pointer(e,t.node())[1];o<3?(this.updatePositioningLine(this.tree.hoveredNode),0===this.tree.hoveredNode.depth?this.addNodeDdClass("nodrop"):this.tree.hoveredNode.firstChild?this.addNodeDdClass("ok-above"):this.addNodeDdClass("ok-between"),this.tree.settings.nodeDragPosition=DraggablePositionEnum.BEFORE):o>17?(this.hidePositioningLine(),this.tree.hoveredNode.expanded&&this.tree.hoveredNode.hasChildren?(this.addNodeDdClass("ok-append"),this.tree.settings.nodeDragPosition=DraggablePositionEnum.INSIDE):(this.updatePositioningLine(this.tree.hoveredNode),this.tree.hoveredNode.lastChild?this.addNodeDdClass("ok-below"):this.addNodeDdClass("ok-between"),this.tree.settings.nodeDragPosition=DraggablePositionEnum.AFTER)):(this.hidePositioningLine(),this.addNodeDdClass("ok-append"),this.tree.settings.nodeDragPosition=DraggablePositionEnum.INSIDE)}else this.hidePositioningLine(),this.addNodeDdClass("nodrop")}setNodePositionAndTarget(e){const t=this.tree.nodes,o=t[e].depth;e>0&&e--;const i=t[e].depth,n=this.tree.nodes[e];if(i===o)return{position:DraggablePositionEnum.AFTER,target:n};if(i<o)return{position:DraggablePositionEnum.INSIDE,target:n};for(let i=e;i>=0;i--){if(t[i].depth===o)return{position:DraggablePositionEnum.AFTER,target:this.tree.nodes[i]};if(t[i].depth<o)return{position:DraggablePositionEnum.AFTER,target:t[i]}}return null}isDropAllowed(e,t){return!!this.tree.settings.allowDragMove&&(!!this.tree.isOverSvg&&(!!this.tree.hoveredNode&&(!t.isOver&&!this.isTheSameNode(e,t))))}}class ToolbarDragHandler{constructor(e,t,o){this.dragStarted=!1,this.startPageX=0,this.startPageY=0,this.id="",this.name="",this.icon="",this.id=e.nodeType,this.name=e.title,this.icon=e.icon,this.tree=t,this.dragDrop=o}onDragStart(e,t){return this.dragStarted=!1,this.startPageX=e.pageX,this.startPageY=e.pageY,!0}onDragOver(e,t){return!!this.dragDrop.isDragNodeDistanceMore(e,this)&&(this.dragStarted=!0,this.dragDrop.getDraggable()||this.dragDrop.createDraggable("#icon-"+this.icon,this.name),this.dragDrop.openNodeTimeout(),this.dragDrop.updateDraggablePosition(e),this.dragDrop.updateStateOfHoveredNode(e),!0)}onDrop(e,t){return!!this.dragStarted&&(this.dragDrop.cleanupDrop(),!!this.dragDrop.isDropAllowed(this.tree.hoveredNode,t)&&(this.addNewNode({type:this.id,name:this.name,icon:this.icon,position:this.tree.settings.nodeDragPosition,target:this.tree.hoveredNode}),!0))}addNewNode(e){const t=e.target;let o=this.tree.nodes.indexOf(t);const i={};if(this.tree.disableFocusedNodes(),i.focused=!0,this.tree.updateVisibleNodes(),i.command="new",i.type=e.type,i.identifier="-1",i.target=t,i.parents=t.parents,i.parentsStateIdentifier=t.parentsStateIdentifier,i.depth=t.depth,i.position=e.position,i.name=void 0!==e.title?e.title:TYPO3.lang["tree.defaultPageTitle"],i.y=i.y||i.target.y,i.x=i.x||i.target.x,this.tree.nodeIsEdit=!0,e.position===DraggablePositionEnum.INSIDE&&(i.depth++,i.parents.unshift(o),i.parentsStateIdentifier.unshift(this.tree.nodes[o].stateIdentifier),this.tree.nodes[o].hasChildren=!0,this.tree.showChildren(this.tree.nodes[o])),e.position!==DraggablePositionEnum.INSIDE&&e.position!==DraggablePositionEnum.AFTER||o++,e.icon&&(i.icon=e.icon),i.position===DraggablePositionEnum.BEFORE){const e=this.dragDrop.setNodePositionAndTarget(o);null!==e&&(i.position=e.position,i.target=e.target)}this.tree.nodes.splice(o,0,i),this.tree.setParametersNode(),this.tree.prepareDataForVisibleNodes(),this.tree.updateVisibleNodes(),this.tree.removeEditedText(),d3selection.select(this.tree.svg.node().parentNode).append("input").attr("class","node-edit").style("top",i.y+this.tree.settings.marginTop+"px").style("left",i.x+this.tree.textPosition+5+"px").style("width","calc(100% - "+(i.x+this.tree.textPosition+5)+"px)").style("height",this.tree.settings.nodeHeight+"px").attr("text","text").attr("value",i.name).on("keydown",(e=>{const t=e.target,o=e.keyCode;if(13===o||9===o){this.tree.nodeIsEdit=!1;const e=t.value.trim();e.length?(i.name=e,this.tree.removeEditedText(),this.tree.sendChangeCommand(i)):this.removeNode(i)}else 27===o&&(this.tree.nodeIsEdit=!1,this.removeNode(i))})).on("blur",(e=>{if(this.tree.nodeIsEdit&&this.tree.nodes.indexOf(i)>-1){const t=e.target.value.trim();t.length?(i.name=t,this.tree.removeEditedText(),this.tree.sendChangeCommand(i)):this.removeNode(i)}})).node().select()}removeNode(e){let t=this.tree.nodes.indexOf(e);this.tree.nodes[t-1].depth==e.depth||this.tree.nodes[t+1]&&this.tree.nodes[t+1].depth==e.depth||(this.tree.nodes[t-1].hasChildren=!1),this.tree.nodes.splice(t,1),this.tree.setParametersNode(),this.tree.prepareDataForVisibleNodes(),this.tree.updateVisibleNodes(),this.tree.removeEditedText()}}class PageTreeNodeDragHandler{constructor(e,t){this.dragStarted=!1,this.startPageX=0,this.startPageY=0,this.nodeIsOverDelete=!1,this.tree=e,this.dragDrop=t}onDragStart(e,t){return!0===this.tree.settings.allowDragMove&&0!==t.depth&&(this.dropZoneDelete=null,t.allowDelete&&(this.dropZoneDelete=this.tree.nodesContainer.select('.node[data-state-id="'+t.stateIdentifier+'"]').append("g").attr("class","nodes-drop-zone").attr("height",this.tree.settings.nodeHeight),this.nodeIsOverDelete=!1,this.dropZoneDelete.append("rect").attr("height",this.tree.settings.nodeHeight).attr("width","50px").attr("x",0).attr("y",0).on("mouseover",(()=>{this.nodeIsOverDelete=!0})).on("mouseout",(()=>{this.nodeIsOverDelete=!1})),this.dropZoneDelete.append("text").text(TYPO3.lang.deleteItem).attr("x",5).attr("y",this.tree.settings.nodeHeight/2+4),this.dropZoneDelete.node().dataset.open="false",this.dropZoneDelete.node().style.transform=this.getDropZoneCloseTransform(t)),this.startPageX=e.pageX,this.startPageY=e.pageY,this.dragStarted=!1,!0)}onDragOver(e,t){return!!this.dragDrop.isDragNodeDistanceMore(e,this)&&(this.dragStarted=!0,!0===this.tree.settings.allowDragMove&&0!==t.depth&&(this.dragDrop.getDraggable()||this.dragDrop.createDraggableFromExistingNode(t),this.tree.settings.nodeDragPosition=!1,this.dragDrop.openNodeTimeout(),this.dragDrop.updateDraggablePosition(e),this.dragDrop.isDropAllowed(this.tree.hoveredNode,t)?this.tree.hoveredNode?this.dropZoneDelete&&"false"!==this.dropZoneDelete.node().dataset.open?this.animateDropZone("hide",this.dropZoneDelete.node(),t):this.dragDrop.updateStateOfHoveredNode(e):(this.dragDrop.addNodeDdClass("nodrop"),this.dragDrop.hidePositioningLine()):(this.dragDrop.addNodeDdClass("nodrop"),this.tree.isOverSvg||this.dragDrop.hidePositioningLine(),this.dropZoneDelete&&"true"!==this.dropZoneDelete.node().dataset.open&&this.tree.isOverSvg&&this.animateDropZone("show",this.dropZoneDelete.node(),t)),!0))}onDrop(e,t){if(this.dropZoneDelete&&"true"===this.dropZoneDelete.node().dataset.open){const e=this.dropZoneDelete;this.animateDropZone("hide",this.dropZoneDelete.node(),t,(()=>{e.remove(),this.dropZoneDelete=null}))}else this.dropZoneDelete&&"false"===this.dropZoneDelete.node().dataset.open?(this.dropZoneDelete.remove(),this.dropZoneDelete=null):this.dropZoneDelete=null;if(!this.dragStarted||!0!==this.tree.settings.allowDragMove||0===t.depth)return!1;if(this.dragDrop.cleanupDrop(),this.dragDrop.isDropAllowed(this.tree.hoveredNode,t)){const e=this.dragDrop.getDropCommandDetails(this.tree.hoveredNode,"",t);if(null===e)return!1;let o=e.position===DraggablePositionEnum.INSIDE?TYPO3.lang["mess.move_into"]:TYPO3.lang["mess.move_after"];o=o.replace("%s",e.node.name).replace("%s",e.target.name);const i=Modal.confirm(TYPO3.lang.move_page,o,Severity.warning,[{text:TYPO3.lang["labels.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:TYPO3.lang["cm.copy"]||"Copy",btnClass:"btn-warning",name:"copy"},{text:TYPO3.lang["labels.move"]||"Move",btnClass:"btn-warning",name:"move"}]);i.addEventListener("button.clicked",(t=>{const o=t.target;"move"===o.name?(e.command="move",this.tree.sendChangeCommand(e)):"copy"===o.name&&(e.command="copy",this.tree.sendChangeCommand(e)),i.hideModal()}))}else if(this.nodeIsOverDelete){const e=this.dragDrop.getDropCommandDetails(this.tree.hoveredNode,"delete",t);if(null===e)return!1;if(this.tree.settings.displayDeleteConfirmation){Modal.confirm(TYPO3.lang["mess.delete.title"],TYPO3.lang["mess.delete"].replace("%s",e.node.name),Severity.warning,[{text:TYPO3.lang["labels.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:TYPO3.lang.delete||"Delete",btnClass:"btn-warning",name:"delete"}]).addEventListener("button.clicked",(t=>{"delete"===t.target.name&&this.tree.sendChangeCommand(e),Modal.dismiss()}))}else this.tree.sendChangeCommand(e)}return!0}getDropZoneOpenTransform(e){return"translate("+((parseFloat(this.tree.svg.style("width"))||300)-58-e.x)+"px, "+this.tree.settings.nodeHeight/2*-1+"px)"}getDropZoneCloseTransform(e){return"translate("+((parseFloat(this.tree.svg.style("width"))||300)-e.x)+"px, "+this.tree.settings.nodeHeight/2*-1+"px)"}animateDropZone(e,t,o,i=null){t.classList.add("animating"),t.dataset.open="show"===e?"true":"false";let n=[{transform:this.getDropZoneCloseTransform(o)},{transform:this.getDropZoneOpenTransform(o)}];"show"!==e&&(n=n.reverse());const s=function(){t.style.transform=n[1].transform,t.classList.remove("animating"),i&&i()};"animate"in t?t.animate(n,{duration:300,easing:"cubic-bezier(.02, .01, .47, 1)"}).onfinish=s:s()}} \ No newline at end of file + `}dragToolbar(e,t){return t.connectDragHandler(new ToolbarDragHandler(e,this.tree,t))}};__decorate([property({type:EditablePageTree})],PageTreeToolbar.prototype,"tree",void 0),PageTreeToolbar=__decorate([customElement("typo3-backend-navigation-component-pagetree-toolbar")],PageTreeToolbar);class PageTreeDragDrop extends DragDrop{getDropCommandDetails(e,t="",o=null){const i=this.tree.nodes,n=o.identifier;let s=this.tree.settings.nodeDragPosition,r=e||o;if(n===r.identifier&&"delete"!==t)return null;if(s===DraggablePositionEnum.BEFORE){const t=i.indexOf(e),o=this.setNodePositionAndTarget(t);if(null===o)return null;s=o.position,r=o.target}return{node:o,uid:n,target:r,position:s,command:t}}updateStateOfHoveredNode(e){const t=this.tree.svg.select(".node-over");if(t.size()&&this.tree.isOverSvg){this.createPositioningLine();const o=d3selection.pointer(e,t.node())[1];o<3?(this.updatePositioningLine(this.tree.hoveredNode),0===this.tree.hoveredNode.depth?this.addNodeDdClass("nodrop"):this.tree.hoveredNode.firstChild?this.addNodeDdClass("ok-above"):this.addNodeDdClass("ok-between"),this.tree.settings.nodeDragPosition=DraggablePositionEnum.BEFORE):o>17?(this.hidePositioningLine(),this.tree.hoveredNode.expanded&&this.tree.hoveredNode.hasChildren?(this.addNodeDdClass("ok-append"),this.tree.settings.nodeDragPosition=DraggablePositionEnum.INSIDE):(this.updatePositioningLine(this.tree.hoveredNode),this.tree.hoveredNode.lastChild?this.addNodeDdClass("ok-below"):this.addNodeDdClass("ok-between"),this.tree.settings.nodeDragPosition=DraggablePositionEnum.AFTER)):(this.hidePositioningLine(),this.addNodeDdClass("ok-append"),this.tree.settings.nodeDragPosition=DraggablePositionEnum.INSIDE)}else this.hidePositioningLine(),this.addNodeDdClass("nodrop")}setNodePositionAndTarget(e){const t=this.tree.nodes,o=t[e].depth;e>0&&e--;const i=t[e].depth,n=this.tree.nodes[e];if(i===o)return{position:DraggablePositionEnum.AFTER,target:n};if(i<o)return{position:DraggablePositionEnum.INSIDE,target:n};for(let i=e;i>=0;i--){if(t[i].depth===o)return{position:DraggablePositionEnum.AFTER,target:this.tree.nodes[i]};if(t[i].depth<o)return{position:DraggablePositionEnum.AFTER,target:t[i]}}return null}isDropAllowed(e,t){return!!this.tree.settings.allowDragMove&&(!!this.tree.isOverSvg&&(!!this.tree.hoveredNode&&(!t.isOver&&!this.isTheSameNode(e,t))))}}class ToolbarDragHandler{constructor(e,t,o){this.dragStarted=!1,this.startPageX=0,this.startPageY=0,this.id="",this.name="",this.icon="",this.id=e.nodeType,this.name=e.title,this.icon=e.icon,this.tree=t,this.dragDrop=o}onDragStart(e){return this.dragStarted=!1,this.startPageX=e.pageX,this.startPageY=e.pageY,!0}onDragOver(e){return!!this.dragDrop.isDragNodeDistanceMore(e,this)&&(this.dragStarted=!0,this.dragDrop.getDraggable()||this.dragDrop.createDraggable("#icon-"+this.icon,this.name),this.dragDrop.openNodeTimeout(),this.dragDrop.updateDraggablePosition(e),this.dragDrop.updateStateOfHoveredNode(e),!0)}onDrop(e,t){return!!this.dragStarted&&(this.dragDrop.cleanupDrop(),!!this.dragDrop.isDropAllowed(this.tree.hoveredNode,t)&&(this.addNewNode({type:this.id,name:this.name,icon:this.icon,position:this.tree.settings.nodeDragPosition,target:this.tree.hoveredNode}),!0))}addNewNode(e){const t=e.target;let o=this.tree.nodes.indexOf(t);const i={};if(this.tree.disableFocusedNodes(),i.focused=!0,this.tree.updateVisibleNodes(),i.command="new",i.type=e.type,i.identifier="-1",i.target=t,i.parents=t.parents,i.parentsStateIdentifier=t.parentsStateIdentifier,i.depth=t.depth,i.position=e.position,i.name=void 0!==e.title?e.title:TYPO3.lang["tree.defaultPageTitle"],i.y=i.y||i.target.y,i.x=i.x||i.target.x,this.tree.nodeIsEdit=!0,e.position===DraggablePositionEnum.INSIDE&&(i.depth++,i.parents.unshift(o),i.parentsStateIdentifier.unshift(this.tree.nodes[o].stateIdentifier),this.tree.nodes[o].hasChildren=!0,this.tree.showChildren(this.tree.nodes[o])),e.position!==DraggablePositionEnum.INSIDE&&e.position!==DraggablePositionEnum.AFTER||o++,e.icon&&(i.icon=e.icon),i.position===DraggablePositionEnum.BEFORE){const e=this.dragDrop.setNodePositionAndTarget(o);null!==e&&(i.position=e.position,i.target=e.target)}this.tree.nodes.splice(o,0,i),this.tree.setParametersNode(),this.tree.prepareDataForVisibleNodes(),this.tree.updateVisibleNodes(),this.tree.removeEditedText(),d3selection.select(this.tree.svg.node().parentNode).append("input").attr("class","node-edit").style("top",i.y+this.tree.settings.marginTop+"px").style("left",i.x+this.tree.textPosition+5+"px").style("width","calc(100% - "+(i.x+this.tree.textPosition+5)+"px)").style("height",this.tree.settings.nodeHeight+"px").attr("text","text").attr("value",i.name).on("keydown",(e=>{const t=e.target,o=e.keyCode;if(13===o||9===o){this.tree.nodeIsEdit=!1;const e=t.value.trim();e.length?(i.name=e,this.tree.removeEditedText(),this.tree.sendChangeCommand(i)):this.removeNode(i)}else 27===o&&(this.tree.nodeIsEdit=!1,this.removeNode(i))})).on("blur",(e=>{if(this.tree.nodeIsEdit&&this.tree.nodes.indexOf(i)>-1){const t=e.target.value.trim();t.length?(i.name=t,this.tree.removeEditedText(),this.tree.sendChangeCommand(i)):this.removeNode(i)}})).node().select()}removeNode(e){const t=this.tree.nodes.indexOf(e);this.tree.nodes[t-1].depth==e.depth||this.tree.nodes[t+1]&&this.tree.nodes[t+1].depth==e.depth||(this.tree.nodes[t-1].hasChildren=!1),this.tree.nodes.splice(t,1),this.tree.setParametersNode(),this.tree.prepareDataForVisibleNodes(),this.tree.updateVisibleNodes(),this.tree.removeEditedText()}}class PageTreeNodeDragHandler{constructor(e,t){this.dragStarted=!1,this.startPageX=0,this.startPageY=0,this.nodeIsOverDelete=!1,this.tree=e,this.dragDrop=t}onDragStart(e,t){return!0===this.tree.settings.allowDragMove&&0!==t.depth&&(this.dropZoneDelete=null,t.allowDelete&&(this.dropZoneDelete=this.tree.nodesContainer.select('.node[data-state-id="'+t.stateIdentifier+'"]').append("g").attr("class","nodes-drop-zone").attr("height",this.tree.settings.nodeHeight),this.nodeIsOverDelete=!1,this.dropZoneDelete.append("rect").attr("height",this.tree.settings.nodeHeight).attr("width","50px").attr("x",0).attr("y",0).on("mouseover",(()=>{this.nodeIsOverDelete=!0})).on("mouseout",(()=>{this.nodeIsOverDelete=!1})),this.dropZoneDelete.append("text").text(TYPO3.lang.deleteItem).attr("x",5).attr("y",this.tree.settings.nodeHeight/2+4),this.dropZoneDelete.node().dataset.open="false",this.dropZoneDelete.node().style.transform=this.getDropZoneCloseTransform(t)),this.startPageX=e.pageX,this.startPageY=e.pageY,this.dragStarted=!1,!0)}onDragOver(e,t){return!!this.dragDrop.isDragNodeDistanceMore(e,this)&&(this.dragStarted=!0,!0===this.tree.settings.allowDragMove&&0!==t.depth&&(this.dragDrop.getDraggable()||this.dragDrop.createDraggableFromExistingNode(t),this.tree.settings.nodeDragPosition=!1,this.dragDrop.openNodeTimeout(),this.dragDrop.updateDraggablePosition(e),this.dragDrop.isDropAllowed(this.tree.hoveredNode,t)?this.tree.hoveredNode?this.dropZoneDelete&&"false"!==this.dropZoneDelete.node().dataset.open?this.animateDropZone("hide",this.dropZoneDelete.node(),t):this.dragDrop.updateStateOfHoveredNode(e):(this.dragDrop.addNodeDdClass("nodrop"),this.dragDrop.hidePositioningLine()):(this.dragDrop.addNodeDdClass("nodrop"),this.tree.isOverSvg||this.dragDrop.hidePositioningLine(),this.dropZoneDelete&&"true"!==this.dropZoneDelete.node().dataset.open&&this.tree.isOverSvg&&this.animateDropZone("show",this.dropZoneDelete.node(),t)),!0))}onDrop(e,t){if(this.dropZoneDelete&&"true"===this.dropZoneDelete.node().dataset.open){const e=this.dropZoneDelete;this.animateDropZone("hide",this.dropZoneDelete.node(),t,(()=>{e.remove(),this.dropZoneDelete=null}))}else this.dropZoneDelete&&"false"===this.dropZoneDelete.node().dataset.open?(this.dropZoneDelete.remove(),this.dropZoneDelete=null):this.dropZoneDelete=null;if(!this.dragStarted||!0!==this.tree.settings.allowDragMove||0===t.depth)return!1;if(this.dragDrop.cleanupDrop(),this.dragDrop.isDropAllowed(this.tree.hoveredNode,t)){const e=this.dragDrop.getDropCommandDetails(this.tree.hoveredNode,"",t);if(null===e)return!1;let o=e.position===DraggablePositionEnum.INSIDE?TYPO3.lang["mess.move_into"]:TYPO3.lang["mess.move_after"];o=o.replace("%s",e.node.name).replace("%s",e.target.name);const i=Modal.confirm(TYPO3.lang.move_page,o,Severity.warning,[{text:TYPO3.lang["labels.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:TYPO3.lang["cm.copy"]||"Copy",btnClass:"btn-warning",name:"copy"},{text:TYPO3.lang["labels.move"]||"Move",btnClass:"btn-warning",name:"move"}]);i.addEventListener("button.clicked",(t=>{const o=t.target;"move"===o.name?(e.command="move",this.tree.sendChangeCommand(e)):"copy"===o.name&&(e.command="copy",this.tree.sendChangeCommand(e)),i.hideModal()}))}else if(this.nodeIsOverDelete){const e=this.dragDrop.getDropCommandDetails(this.tree.hoveredNode,"delete",t);if(null===e)return!1;if(this.tree.settings.displayDeleteConfirmation){Modal.confirm(TYPO3.lang["mess.delete.title"],TYPO3.lang["mess.delete"].replace("%s",e.node.name),Severity.warning,[{text:TYPO3.lang["labels.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:TYPO3.lang.delete||"Delete",btnClass:"btn-warning",name:"delete"}]).addEventListener("button.clicked",(t=>{"delete"===t.target.name&&this.tree.sendChangeCommand(e),Modal.dismiss()}))}else this.tree.sendChangeCommand(e)}return!0}getDropZoneOpenTransform(e){return"translate("+((parseFloat(this.tree.svg.style("width"))||300)-58-e.x)+"px, "+this.tree.settings.nodeHeight/2*-1+"px)"}getDropZoneCloseTransform(e){return"translate("+((parseFloat(this.tree.svg.style("width"))||300)-e.x)+"px, "+this.tree.settings.nodeHeight/2*-1+"px)"}animateDropZone(e,t,o,i=null){t.classList.add("animating"),t.dataset.open="show"===e?"true":"false";let n=[{transform:this.getDropZoneCloseTransform(o)},{transform:this.getDropZoneOpenTransform(o)}];"show"!==e&&(n=n.reverse());const s=function(){t.style.transform=n[1].transform,t.classList.remove("animating"),i&&i()};"animate"in t?t.animate(n,{duration:300,easing:"cubic-bezier(.02, .01, .47, 1)"}).onfinish=s:s()}} \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/page-tree/page-tree.js b/typo3/sysext/backend/Resources/Public/JavaScript/page-tree/page-tree.js index d8feb42b0572..211f6252bf6f 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/page-tree/page-tree.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/page-tree/page-tree.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{SvgTree}from"@typo3/backend/svg-tree.js";export class PageTree extends SvgTree{constructor(){super(),this.networkErrorTitle=TYPO3.lang.pagetree_networkErrorTitle,this.networkErrorMessage=TYPO3.lang.pagetree_networkErrorDesc,this.settings.defaultProperties={hasChildren:!1,nameSourceField:"title",itemType:"pages",prefix:"",suffix:"",locked:!1,loaded:!1,overlayIcon:"",selectable:!0,expanded:!1,checked:!1,backgroundColor:"",stopPageTree:!1,class:"",readableRootline:"",isMountPoint:!1}}showChildren(e){this.loadChildrenOfNode(e),super.showChildren(e)}nodesUpdate(e){let t=(e=super.nodesUpdate(e)).append("svg").attr("class","node-stop").attr("y",super.settings.icon.size/2*-1).attr("x",super.settings.icon.size/2*-1).attr("height",super.settings.icon.size).attr("width",super.settings.icon.size).attr("visibility",(e=>e.stopPageTree&&0!==e.depth?"visible":"hidden")).on("click",((e,t)=>{document.dispatchEvent(new CustomEvent("typo3:pagetree:mountPoint",{detail:{pageId:parseInt(t.identifier,10)}}))}));return t.append("rect").attr("height",super.settings.icon.size).attr("width",super.settings.icon.size).attr("fill","rgba(0,0,0,0)"),t.append("use").attr("transform-origin","50% 50%").attr("href","#icon-actions-caret-right"),e}getToggleVisibility(e){return e.stopPageTree&&0!==e.depth?"hidden":e.hasChildren?"visible":"hidden"}loadChildrenOfNode(e){e.loaded||(this.nodesAddPlaceholder(),new AjaxRequest(this.settings.dataUrl+"&pid="+e.identifier+"&mount="+e.mountPoint+"&pidDepth="+e.depth).get({cache:"no-cache"}).then((e=>e.resolve())).then((t=>{let s=Array.isArray(t)?t:[];s.shift();const i=this.nodes.indexOf(e)+1;s.forEach(((e,t)=>{this.nodes.splice(i+t,0,e)})),e.loaded=!0,this.setParametersNode(),this.prepareDataForVisibleNodes(),this.updateVisibleNodes(),this.nodesRemovePlaceholder(),this.focusNode(e)})).catch((e=>{throw this.errorNotification(e,!1),this.nodesRemovePlaceholder(),e})))}} \ No newline at end of file +import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{SvgTree}from"@typo3/backend/svg-tree.js";export class PageTree extends SvgTree{constructor(){super(),this.networkErrorTitle=TYPO3.lang.pagetree_networkErrorTitle,this.networkErrorMessage=TYPO3.lang.pagetree_networkErrorDesc,this.settings.defaultProperties={hasChildren:!1,nameSourceField:"title",itemType:"pages",prefix:"",suffix:"",locked:!1,loaded:!1,overlayIcon:"",selectable:!0,expanded:!1,checked:!1,backgroundColor:"",stopPageTree:!1,class:"",readableRootline:"",isMountPoint:!1}}showChildren(e){this.loadChildrenOfNode(e),super.showChildren(e)}nodesUpdate(e){const t=(e=super.nodesUpdate(e)).append("svg").attr("class","node-stop").attr("y",super.settings.icon.size/2*-1).attr("x",super.settings.icon.size/2*-1).attr("height",super.settings.icon.size).attr("width",super.settings.icon.size).attr("visibility",(e=>e.stopPageTree&&0!==e.depth?"visible":"hidden")).on("click",((e,t)=>{document.dispatchEvent(new CustomEvent("typo3:pagetree:mountPoint",{detail:{pageId:parseInt(t.identifier,10)}}))}));return t.append("rect").attr("height",super.settings.icon.size).attr("width",super.settings.icon.size).attr("fill","rgba(0,0,0,0)"),t.append("use").attr("transform-origin","50% 50%").attr("href","#icon-actions-caret-right"),e}getToggleVisibility(e){return e.stopPageTree&&0!==e.depth?"hidden":e.hasChildren?"visible":"hidden"}loadChildrenOfNode(e){e.loaded||(this.nodesAddPlaceholder(),new AjaxRequest(this.settings.dataUrl+"&pid="+e.identifier+"&mount="+e.mountPoint+"&pidDepth="+e.depth).get({cache:"no-cache"}).then((e=>e.resolve())).then((t=>{const s=Array.isArray(t)?t:[];s.shift();const i=this.nodes.indexOf(e)+1;s.forEach(((e,t)=>{this.nodes.splice(i+t,0,e)})),e.loaded=!0,this.setParametersNode(),this.prepareDataForVisibleNodes(),this.updateVisibleNodes(),this.nodesRemovePlaceholder(),this.focusNode(e)})).catch((e=>{throw this.errorNotification(e,!1),this.nodesRemovePlaceholder(),e})))}} \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/record-download-button.js b/typo3/sysext/backend/Resources/Public/JavaScript/record-download-button.js index 89a1150e4f80..f66229e34ab3 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/record-download-button.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/record-download-button.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -var Selectors,__decorate=function(t,e,o,r){var n,l=arguments.length,a=l<3?e:null===r?r=Object.getOwnPropertyDescriptor(e,o):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(t,e,o,r);else for(var s=t.length-1;s>=0;s--)(n=t[s])&&(a=(l<3?n(a):l>3?n(e,o,a):n(e,o))||a);return l>3&&a&&Object.defineProperty(e,o,a),a};import{html,css,LitElement}from"lit";import{customElement,property}from"lit/decorators.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import Severity from"@typo3/backend/severity.js";import Modal from"@typo3/backend/modal.js";import{lll}from"@typo3/core/lit-helper.js";!function(t){t.formatSelector=".t3js-record-download-format-selector",t.formatOptions=".t3js-record-download-format-option"}(Selectors||(Selectors={}));let RecordDownloadButton=class extends LitElement{constructor(){super(),this.addEventListener("click",(t=>{t.preventDefault(),this.showDownloadConfigurationModal()})),this.addEventListener("keydown",(t=>{"Enter"!==t.key&&" "!==t.key||(t.preventDefault(),this.showDownloadConfigurationModal())}))}connectedCallback(){this.hasAttribute("role")||this.setAttribute("role","button"),this.hasAttribute("tabindex")||this.setAttribute("tabindex","0")}render(){return html`<slot></slot>`}showDownloadConfigurationModal(){if(!this.url)return;const t=Modal.advanced({content:this.url,title:this.title||"Download records",severity:SeverityEnum.notice,size:Modal.sizes.small,type:Modal.types.ajax,buttons:[{text:this.close||lll("button.close")||"Close",active:!0,btnClass:"btn-default",name:"cancel",trigger:()=>t.hideModal()},{text:this.ok||lll("button.ok")||"Download",btnClass:"btn-"+Severity.getCssClass(SeverityEnum.info),name:"download",trigger:()=>{const e=t.querySelector("form");e&&e.submit(),t.hideModal()}}],ajaxCallback:()=>{const e=t.querySelector(Selectors.formatSelector),o=t.querySelectorAll(Selectors.formatOptions);null!==e&&o.length&&e.addEventListener("change",(t=>{const e=t.target.value;o.forEach((t=>{t.dataset.formatname!==e?t.classList.add("hide"):t.classList.remove("hide")}))}))}})}};RecordDownloadButton.styles=[css`:host { cursor: pointer; appearance: button; }`],__decorate([property({type:String})],RecordDownloadButton.prototype,"url",void 0),__decorate([property({type:String})],RecordDownloadButton.prototype,"title",void 0),__decorate([property({type:String})],RecordDownloadButton.prototype,"ok",void 0),__decorate([property({type:String})],RecordDownloadButton.prototype,"close",void 0),RecordDownloadButton=__decorate([customElement("typo3-recordlist-record-download-button")],RecordDownloadButton); \ No newline at end of file +var Selectors,__decorate=function(t,e,o,r){var n,l=arguments.length,a=l<3?e:null===r?r=Object.getOwnPropertyDescriptor(e,o):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(t,e,o,r);else for(var s=t.length-1;s>=0;s--)(n=t[s])&&(a=(l<3?n(a):l>3?n(e,o,a):n(e,o))||a);return l>3&&a&&Object.defineProperty(e,o,a),a};import{html,css,LitElement}from"lit";import{customElement,property}from"lit/decorators.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import Severity from"@typo3/backend/severity.js";import Modal from"@typo3/backend/modal.js";import{lll}from"@typo3/core/lit-helper.js";!function(t){t.formatSelector=".t3js-record-download-format-selector",t.formatOptions=".t3js-record-download-format-option"}(Selectors||(Selectors={}));let RecordDownloadButton=class extends LitElement{constructor(){super(),this.addEventListener("click",(t=>{t.preventDefault(),this.showDownloadConfigurationModal()})),this.addEventListener("keydown",(t=>{"Enter"!==t.key&&" "!==t.key||(t.preventDefault(),this.showDownloadConfigurationModal())}))}connectedCallback(){this.hasAttribute("role")||this.setAttribute("role","button"),this.hasAttribute("tabindex")||this.setAttribute("tabindex","0")}render(){return html`<slot></slot>`}showDownloadConfigurationModal(){if(!this.url)return;const t=Modal.advanced({content:this.url,title:this.title||"Download records",severity:SeverityEnum.notice,size:Modal.sizes.small,type:Modal.types.ajax,buttons:[{text:this.close||lll("button.close")||"Close",active:!0,btnClass:"btn-default",name:"cancel",trigger:()=>t.hideModal()},{text:this.ok||lll("button.ok")||"Download",btnClass:"btn-"+Severity.getCssClass(SeverityEnum.info),name:"download",trigger:()=>{const e=t.querySelector("form");e&&e.submit(),t.hideModal()}}],ajaxCallback:()=>{const e=t.querySelector(Selectors.formatSelector),o=t.querySelectorAll(Selectors.formatOptions);null!==e&&o.length&&e.addEventListener("change",(t=>{const e=t.target.value;o.forEach((t=>{t.dataset.formatname!==e?t.classList.add("hide"):t.classList.remove("hide")}))}))}})}};RecordDownloadButton.styles=[css`:host { cursor: pointer; appearance: button; }`],__decorate([property({type:String})],RecordDownloadButton.prototype,"url",void 0),__decorate([property({type:String})],RecordDownloadButton.prototype,"title",void 0),__decorate([property({type:String})],RecordDownloadButton.prototype,"ok",void 0),__decorate([property({type:String})],RecordDownloadButton.prototype,"close",void 0),RecordDownloadButton=__decorate([customElement("typo3-recordlist-record-download-button")],RecordDownloadButton);export{RecordDownloadButton}; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/record-link-handler.js b/typo3/sysext/backend/Resources/Public/JavaScript/record-link-handler.js index 478c68aae140..c656be71ffe4 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/record-link-handler.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/record-link-handler.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import LinkBrowser from"@typo3/backend/link-browser.js";import RegularEvent from"@typo3/core/event/regular-event.js";class RecordLinkHandler{constructor(){new RegularEvent("click",((e,t)=>{e.preventDefault();const n=t.closest("span").dataset;LinkBrowser.finalizeFunction(document.body.dataset.identifier+n.uid)})).delegateTo(document,"[data-close]"),new RegularEvent("click",((e,t)=>{e.preventDefault(),LinkBrowser.finalizeFunction(document.body.dataset.currentLink)})).delegateTo(document,"input.t3js-linkCurrent")}}export default new RecordLinkHandler; \ No newline at end of file +import LinkBrowser from"@typo3/backend/link-browser.js";import RegularEvent from"@typo3/core/event/regular-event.js";class RecordLinkHandler{constructor(){new RegularEvent("click",((e,t)=>{e.preventDefault();const n=t.closest("span").dataset;LinkBrowser.finalizeFunction(document.body.dataset.identifier+n.uid)})).delegateTo(document,"[data-close]"),new RegularEvent("click",(e=>{e.preventDefault(),LinkBrowser.finalizeFunction(document.body.dataset.currentLink)})).delegateTo(document,"input.t3js-linkCurrent")}}export default new RecordLinkHandler; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/recordlist.js b/typo3/sysext/backend/Resources/Public/JavaScript/recordlist.js index c1dc85f4f163..7b9fb950cbde 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/recordlist.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/recordlist.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import Icons from"@typo3/backend/icons.js";import PersistentStorage from"@typo3/backend/storage/persistent.js";import RegularEvent from"@typo3/core/event/regular-event.js";import DocumentService from"@typo3/core/document-service.js";import{default as Modal}from"@typo3/backend/modal.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import Severity from"@typo3/backend/severity.js";import{MultiRecordSelectionSelectors}from"@typo3/backend/multi-record-selection.js";class Recordlist{constructor(){this.identifier={entity:".t3js-entity",toggle:".t3js-toggle-recordlist",localize:".t3js-action-localize",icons:{collapse:"actions-view-list-collapse",expand:"actions-view-list-expand",editMultiple:".t3js-record-edit-multiple"}},this.toggleClick=(e,t)=>{e.preventDefault();const i=$(t),n=i.data("table"),o=$(i.data("bs-target")),a="expanded"===o.data("state"),l=i.find(".collapseIcon"),r=a?this.identifier.icons.expand:this.identifier.icons.collapse;Icons.getIcon(r,Icons.sizes.small).then((e=>{l.html(e)}));let s={};PersistentStorage.isset("moduleData.web_list.collapsedTables")&&(s=PersistentStorage.get("moduleData.web_list.collapsedTables"));const d={};d[n]=a?1:0,$.extend(s,d),PersistentStorage.set("moduleData.web_list.collapsedTables",s).then((()=>{o.data("state",a?"collapsed":"expanded")}))},this.onEditMultiple=e=>{e.preventDefault();let t="",i="",n="",o=[];if("multiRecordSelection:action:edit"===e.type){const n=e.detail,a=n.configuration;if(i=a.returnUrl||"",t=a.tableName||"",""===t)return;n.checkboxes.forEach((e=>{const t=e.closest(MultiRecordSelectionSelectors.elementSelector);null!==t&&t.dataset[a.idField]&&o.push(t.dataset[a.idField])}))}else{const a=e.currentTarget,l=a.closest("[data-table]");if(null===l)return;if(t=l.dataset.table||"",""===t)return;i=a.dataset.returnUrl||"",n=a.dataset.columnsOnly||"";const r=l.querySelectorAll(this.identifier.entity+'[data-uid][data-table="'+t+'"] td.col-selector input[type="checkbox"]:checked');if(r.length)r.forEach((e=>{o.push(e.closest(this.identifier.entity+'[data-uid][data-table="'+t+'"]').dataset.uid)}));else{const e=l.querySelectorAll(this.identifier.entity+'[data-uid][data-table="'+t+'"]');if(!e.length)return;e.forEach((e=>{o.push(e.dataset.uid)}))}}if(!o.length)return;let a=top.TYPO3.settings.FormEngine.moduleUrl+"&edit["+t+"]["+o.join(",")+"]=edit&returnUrl="+Recordlist.getReturnUrl(i);""!==n&&(a+="&columnsOnly="+n),window.location.href=a},this.disableButton=e=>{$(e.currentTarget).prop("disable",!0).addClass("disabled")},this.deleteRow=e=>{const t=$(`table[data-table="${e.table}"]`),i=t.find(`tr[data-uid="${e.uid}"]`),n=t.closest(".panel"),o=n.find(".panel-heading"),a=t.find(`[data-l10nparent="${e.uid}"]`),l=$().add(i).add(a);if(l.fadeTo("slow",.4,(()=>{l.slideUp("slow",(()=>{l.remove(),0===t.find("tbody tr").length&&n.slideUp("slow")}))})),"0"===i.data("l10nparent")||""===i.data("l10nparent")){const e=Number(o.find(".t3js-table-total-items").html());o.find(".t3js-table-total-items").text(e-1)}"pages"===e.table&&top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))},this.registerPaginationEvents=()=>{document.querySelectorAll(".t3js-recordlist-paging").forEach((e=>{e.addEventListener("keyup",(t=>{t.preventDefault();let i=parseInt(e.value,10);i<parseInt(e.min,10)&&(i=parseInt(e.min,10)),i>parseInt(e.max,10)&&(i=parseInt(e.max,10)),"Enter"===t.key&&i!==parseInt(e.dataset.currentpage,10)&&(window.location.href=e.dataset.currenturl+i.toString())}))}))},new RegularEvent("click",this.toggleClick).delegateTo(document,this.identifier.toggle),$(document).on("click",this.identifier.icons.editMultiple,this.onEditMultiple),$(document).on("click",this.identifier.localize,this.disableButton),DocumentService.ready().then((()=>{this.registerPaginationEvents()})),new RegularEvent("typo3:datahandler:process",this.handleDataHandlerResult.bind(this)).bindTo(document),new RegularEvent("multiRecordSelection:action:edit",this.onEditMultiple).bindTo(document),new RegularEvent("multiRecordSelection:action:delete",this.deleteMultiple).bindTo(document),new RegularEvent("multiRecordSelection:action:copyMarked",(e=>{Recordlist.submitClipboardFormWithCommand("copyMarked",e.target)})).bindTo(document),new RegularEvent("multiRecordSelection:action:removeMarked",(e=>{Recordlist.submitClipboardFormWithCommand("removeMarked",e.target)})).bindTo(document)}static submitClipboardFormWithCommand(e,t){const i=t.closest("form");if(!i)return;const n=i.querySelector('input[name="cmd"]');n&&(n.value=e,i.submit())}static getReturnUrl(e){return""===e&&(e=top.list_frame.document.location.pathname+top.list_frame.document.location.search),encodeURIComponent(e)}handleDataHandlerResult(e){const t=e.detail.payload;t.hasErrors||"datahandler"!==t.component&&"delete"===t.action&&this.deleteRow(t)}deleteMultiple(e){e.preventDefault();const t=e.detail.configuration;Modal.advanced({title:t.title||"Delete",content:t.content||"Are you sure you want to delete those records?",severity:SeverityEnum.warning,buttons:[{text:TYPO3.lang["button.close"]||"Close",active:!0,btnClass:"btn-default",trigger:(e,t)=>t.hideModal()},{text:t.ok||TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(SeverityEnum.warning),trigger:(t,i)=>{i.hideModal(),Recordlist.submitClipboardFormWithCommand("delete",e.target)}}]})}}export default new Recordlist; \ No newline at end of file +import $ from"jquery";import Icons from"@typo3/backend/icons.js";import PersistentStorage from"@typo3/backend/storage/persistent.js";import RegularEvent from"@typo3/core/event/regular-event.js";import DocumentService from"@typo3/core/document-service.js";import{default as Modal}from"@typo3/backend/modal.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import Severity from"@typo3/backend/severity.js";import{MultiRecordSelectionSelectors}from"@typo3/backend/multi-record-selection.js";class Recordlist{constructor(){this.identifier={entity:".t3js-entity",toggle:".t3js-toggle-recordlist",localize:".t3js-action-localize",icons:{collapse:"actions-view-list-collapse",expand:"actions-view-list-expand",editMultiple:".t3js-record-edit-multiple"}},this.toggleClick=(e,t)=>{e.preventDefault();const i=$(t),n=i.data("table"),o=$(i.data("bs-target")),a="expanded"===o.data("state"),l=i.find(".collapseIcon"),r=a?this.identifier.icons.expand:this.identifier.icons.collapse;Icons.getIcon(r,Icons.sizes.small).then((e=>{l.html(e)}));let s={};PersistentStorage.isset("moduleData.web_list.collapsedTables")&&(s=PersistentStorage.get("moduleData.web_list.collapsedTables"));const d={};d[n]=a?1:0,$.extend(s,d),PersistentStorage.set("moduleData.web_list.collapsedTables",s).then((()=>{o.data("state",a?"collapsed":"expanded")}))},this.onEditMultiple=e=>{e.preventDefault();let t="",i="",n="";const o=[];if("multiRecordSelection:action:edit"===e.type){const n=e.detail,a=n.configuration;if(i=a.returnUrl||"",t=a.tableName||"",""===t)return;n.checkboxes.forEach((e=>{const t=e.closest(MultiRecordSelectionSelectors.elementSelector);null!==t&&t.dataset[a.idField]&&o.push(t.dataset[a.idField])}))}else{const a=e.currentTarget,l=a.closest("[data-table]");if(null===l)return;if(t=l.dataset.table||"",""===t)return;i=a.dataset.returnUrl||"",n=a.dataset.columnsOnly||"";const r=l.querySelectorAll(this.identifier.entity+'[data-uid][data-table="'+t+'"] td.col-selector input[type="checkbox"]:checked');if(r.length)r.forEach((e=>{o.push(e.closest(this.identifier.entity+'[data-uid][data-table="'+t+'"]').dataset.uid)}));else{const e=l.querySelectorAll(this.identifier.entity+'[data-uid][data-table="'+t+'"]');if(!e.length)return;e.forEach((e=>{o.push(e.dataset.uid)}))}}if(!o.length)return;let a=top.TYPO3.settings.FormEngine.moduleUrl+"&edit["+t+"]["+o.join(",")+"]=edit&returnUrl="+Recordlist.getReturnUrl(i);""!==n&&(a+="&columnsOnly="+n),window.location.href=a},this.disableButton=e=>{$(e.currentTarget).prop("disable",!0).addClass("disabled")},this.deleteRow=e=>{const t=$(`table[data-table="${e.table}"]`),i=t.find(`tr[data-uid="${e.uid}"]`),n=t.closest(".panel"),o=n.find(".panel-heading"),a=t.find(`[data-l10nparent="${e.uid}"]`),l=$().add(i).add(a);if(l.fadeTo("slow",.4,(()=>{l.slideUp("slow",(()=>{l.remove(),0===t.find("tbody tr").length&&n.slideUp("slow")}))})),"0"===i.data("l10nparent")||""===i.data("l10nparent")){const e=Number(o.find(".t3js-table-total-items").html());o.find(".t3js-table-total-items").text(e-1)}"pages"===e.table&&top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))},this.registerPaginationEvents=()=>{document.querySelectorAll(".t3js-recordlist-paging").forEach((e=>{e.addEventListener("keyup",(t=>{t.preventDefault();let i=parseInt(e.value,10);i<parseInt(e.min,10)&&(i=parseInt(e.min,10)),i>parseInt(e.max,10)&&(i=parseInt(e.max,10)),"Enter"===t.key&&i!==parseInt(e.dataset.currentpage,10)&&(window.location.href=e.dataset.currenturl+i.toString())}))}))},new RegularEvent("click",this.toggleClick).delegateTo(document,this.identifier.toggle),$(document).on("click",this.identifier.icons.editMultiple,this.onEditMultiple),$(document).on("click",this.identifier.localize,this.disableButton),DocumentService.ready().then((()=>{this.registerPaginationEvents()})),new RegularEvent("typo3:datahandler:process",this.handleDataHandlerResult.bind(this)).bindTo(document),new RegularEvent("multiRecordSelection:action:edit",this.onEditMultiple).bindTo(document),new RegularEvent("multiRecordSelection:action:delete",this.deleteMultiple).bindTo(document),new RegularEvent("multiRecordSelection:action:copyMarked",(e=>{Recordlist.submitClipboardFormWithCommand("copyMarked",e.target)})).bindTo(document),new RegularEvent("multiRecordSelection:action:removeMarked",(e=>{Recordlist.submitClipboardFormWithCommand("removeMarked",e.target)})).bindTo(document)}static submitClipboardFormWithCommand(e,t){const i=t.closest("form");if(!i)return;const n=i.querySelector('input[name="cmd"]');n&&(n.value=e,i.submit())}static getReturnUrl(e){return""===e&&(e=top.list_frame.document.location.pathname+top.list_frame.document.location.search),encodeURIComponent(e)}handleDataHandlerResult(e){const t=e.detail.payload;t.hasErrors||"datahandler"!==t.component&&"delete"===t.action&&this.deleteRow(t)}deleteMultiple(e){e.preventDefault();const t=e.detail.configuration;Modal.advanced({title:t.title||"Delete",content:t.content||"Are you sure you want to delete those records?",severity:SeverityEnum.warning,buttons:[{text:TYPO3.lang["button.close"]||"Close",active:!0,btnClass:"btn-default",trigger:(e,t)=>t.hideModal()},{text:t.ok||TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(SeverityEnum.warning),trigger:(t,i)=>{i.hideModal(),Recordlist.submitClipboardFormWithCommand("delete",e.target)}}]})}}export default new Recordlist; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/severity.js b/typo3/sysext/backend/Resources/Public/JavaScript/severity.js index afbfe87a08e0..0cb86dd1c9be 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/severity.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/severity.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{SeverityEnum}from"@typo3/backend/enum/severity.js";class Severity{static getCssClass(e){let t;switch(e){case SeverityEnum.notice:t="notice";break;case SeverityEnum.ok:t="success";break;case SeverityEnum.warning:t="warning";break;case SeverityEnum.error:t="danger";break;case SeverityEnum.info:default:t="info"}return t}}let severityObject;Severity.notice=SeverityEnum.notice,Severity.info=SeverityEnum.info,Severity.ok=SeverityEnum.ok,Severity.warning=SeverityEnum.warning,Severity.error=SeverityEnum.error;try{window.opener&&window.opener.TYPO3&&window.opener.TYPO3.Severity&&(severityObject=window.opener.TYPO3.Severity),parent&&parent.window.TYPO3&&parent.window.TYPO3.Severity&&(severityObject=parent.window.TYPO3.Severity),top&&top.TYPO3&&top.TYPO3.Severity&&(severityObject=top.TYPO3.Severity)}catch{}severityObject||(severityObject=Severity,"undefined"!=typeof TYPO3&&(TYPO3.Severity=severityObject));export default severityObject; \ No newline at end of file +import{SeverityEnum}from"@typo3/backend/enum/severity.js";export default class Severity{static getCssClass(e){let t;switch(e){case SeverityEnum.notice:t="notice";break;case SeverityEnum.ok:t="success";break;case SeverityEnum.warning:t="warning";break;case SeverityEnum.error:t="danger";break;case SeverityEnum.info:default:t="info"}return t}}let severityObject;Severity.notice=SeverityEnum.notice,Severity.info=SeverityEnum.info,Severity.ok=SeverityEnum.ok,Severity.warning=SeverityEnum.warning,Severity.error=SeverityEnum.error;try{window.opener&&window.opener.TYPO3&&window.opener.TYPO3.Severity&&(severityObject=window.opener.TYPO3.Severity),parent&&parent.window.TYPO3&&parent.window.TYPO3.Severity&&(severityObject=parent.window.TYPO3.Severity),top&&top.TYPO3&&top.TYPO3.Severity&&(severityObject=top.TYPO3.Severity)}catch{}severityObject||(severityObject=Severity,"undefined"!=typeof TYPO3&&(TYPO3.Severity=severityObject)); \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/storage/module-state-storage.js b/typo3/sysext/backend/Resources/Public/JavaScript/storage/module-state-storage.js index f332290e68f0..e992873b17f6 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/storage/module-state-storage.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/storage/module-state-storage.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -export class ModuleStateStorage{static update(t,e,r,o){if("number"==typeof e)e=e.toString(10);else if("string"!=typeof e)throw new SyntaxError("identifier must be of type string");if("number"==typeof o)o=o.toString(10);else if("string"!=typeof o&&null!=o)throw new SyntaxError("mount must be of type string");const i=ModuleStateStorage.assignProperties({mount:o,identifier:e,selected:r},ModuleStateStorage.fetch(t));ModuleStateStorage.commit(t,i)}static updateWithCurrentMount(t,e,r){ModuleStateStorage.update(t,e,r,ModuleStateStorage.current(t).mount)}static current(t){return ModuleStateStorage.fetch(t)||ModuleStateStorage.createCurrentState()}static purge(){Object.keys(sessionStorage).filter((t=>t.startsWith(ModuleStateStorage.prefix))).forEach((t=>sessionStorage.removeItem(t)))}static fetch(t){const e=sessionStorage.getItem(ModuleStateStorage.prefix+t);return null===e?null:JSON.parse(e)}static commit(t,e){sessionStorage.setItem(ModuleStateStorage.prefix+t,JSON.stringify(e))}static assignProperties(t,e){let r=Object.assign(ModuleStateStorage.createCurrentState(),e);return t.mount&&(r.mount=t.mount),t.identifier&&(r.identifier=t.identifier),t.selected&&(r.selection=r.identifier),r}static createCurrentState(){return{mount:null,identifier:"",selection:null}}}ModuleStateStorage.prefix="t3-module-state-",window.ModuleStateStorage=ModuleStateStorage; \ No newline at end of file +export class ModuleStateStorage{static update(t,e,r,o){if("number"==typeof e)e=e.toString(10);else if("string"!=typeof e)throw new SyntaxError("identifier must be of type string");if("number"==typeof o)o=o.toString(10);else if("string"!=typeof o&&null!=o)throw new SyntaxError("mount must be of type string");const i=ModuleStateStorage.assignProperties({mount:o,identifier:e,selected:r},ModuleStateStorage.fetch(t));ModuleStateStorage.commit(t,i)}static updateWithCurrentMount(t,e,r){ModuleStateStorage.update(t,e,r,ModuleStateStorage.current(t).mount)}static current(t){return ModuleStateStorage.fetch(t)||ModuleStateStorage.createCurrentState()}static purge(){Object.keys(sessionStorage).filter((t=>t.startsWith(ModuleStateStorage.prefix))).forEach((t=>sessionStorage.removeItem(t)))}static fetch(t){const e=sessionStorage.getItem(ModuleStateStorage.prefix+t);return null===e?null:JSON.parse(e)}static commit(t,e){sessionStorage.setItem(ModuleStateStorage.prefix+t,JSON.stringify(e))}static assignProperties(t,e){const r=Object.assign(ModuleStateStorage.createCurrentState(),e);return t.mount&&(r.mount=t.mount),t.identifier&&(r.identifier=t.identifier),t.selected&&(r.selection=r.identifier),r}static createCurrentState(){return{mount:null,identifier:"",selection:null}}}ModuleStateStorage.prefix="t3-module-state-",window.ModuleStateStorage=ModuleStateStorage; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/svg-tree.js b/typo3/sysext/backend/Resources/Public/JavaScript/svg-tree.js index 890bacb0d8ac..fbdc8d7846cb 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/svg-tree.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/svg-tree.js @@ -10,7 +10,7 @@ * * The TYPO3 project - inspiring people to share! */ -var __decorate=function(e,t,s,i){var n,o=arguments.length,r=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,s):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,s,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(r=(o<3?n(r):o>3?n(t,s,r):n(t,s))||r);return o>3&&r&&Object.defineProperty(t,s,r),r};import{html,LitElement}from"lit";import{customElement,property,state}from"lit/decorators.js";import*as d3selection from"d3-selection";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Notification from"@typo3/backend/notification.js";import{KeyTypesEnum as KeyTypes}from"@typo3/backend/enum/key-types.js";import Icons from"@typo3/backend/icons.js";import{MarkupIdentifiers}from"@typo3/backend/enum/icon-types.js";import{lll}from"@typo3/core/lit-helper.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import"@typo3/backend/element/icon-element.js";export class SvgTree extends LitElement{constructor(){super(...arguments),this.setup=null,this.settings={showIcons:!1,marginTop:15,nodeHeight:26,icon:{size:16,containerSize:20},indentWidth:20,width:300,duration:400,dataUrl:"",filterUrl:"",defaultProperties:{},expandUpToLevel:null,actions:[]},this.isOverSvg=!1,this.svg=null,this.container=null,this.nodesContainer=null,this.nodesBgContainer=null,this.hoveredNode=null,this.nodes=[],this.textPosition=10,this.icons={},this.nodesActionsContainer=null,this.iconsContainer=null,this.linksContainer=null,this.data=new class{constructor(){this.links=[],this.nodes=[]}},this.viewportHeight=0,this.scrollBottom=0,this.searchTerm=null,this.unfilteredNodes="",this.networkErrorTitle=top.TYPO3.lang.tree_networkError,this.networkErrorMessage=top.TYPO3.lang.tree_networkErrorDescription,this.windowResized=()=>{this.getClientRects().length>0&&this.updateView()}}doSetup(e){Object.assign(this.settings,e),this.settings.showIcons&&(this.textPosition+=this.settings.icon.containerSize),this.svg=d3selection.select(this).select("svg"),this.container=this.svg.select(".nodes-wrapper"),this.nodesBgContainer=this.container.select(".nodes-bg"),this.nodesActionsContainer=this.container.select(".nodes-actions"),this.linksContainer=this.container.select(".links"),this.nodesContainer=this.container.select(".nodes"),this.iconsContainer=this.svg.select("defs"),this.updateScrollPosition(),this.loadCommonIcons(),this.loadData(),this.dispatchEvent(new Event("svg-tree:initialized"))}loadCommonIcons(){this.fetchIcon("actions-chevron-right",!1),this.fetchIcon("overlay-backenduser",!1),this.fetchIcon("actions-caret-right",!1),this.fetchIcon("actions-link",!1)}focusElement(e){if(null===e)return;e.parentNode.querySelectorAll("[tabindex]").forEach((e=>{e.setAttribute("tabindex","-1")})),e.setAttribute("tabindex","0"),e.focus()}focusNode(e){this.disableFocusedNodes(),e.focused=!0,this.focusElement(this.getElementFromNode(e))}getNodeFromElement(e){return null!==e&&"stateId"in e.dataset?this.getNodeByIdentifier(e.dataset.stateId):null}getElementFromNode(e){return this.querySelector("#identifier-"+this.getNodeStateIdentifier(e))}loadData(){this.nodesAddPlaceholder(),new AjaxRequest(this.settings.dataUrl).get({cache:"no-cache"}).then((e=>e.resolve())).then((e=>{const t=Array.isArray(e)?e:[];this.replaceData(t),this.nodesRemovePlaceholder(),this.updateScrollPosition(),this.updateVisibleNodes()})).catch((e=>{throw this.errorNotification(e,!1),this.nodesRemovePlaceholder(),e}))}replaceData(e){this.setParametersNode(e),this.prepareDataForVisibleNodes(),this.nodesContainer.selectAll(".node").remove(),this.nodesBgContainer.selectAll(".node-bg").remove(),this.nodesActionsContainer.selectAll(".node-action").remove(),this.linksContainer.selectAll(".link").remove(),this.updateVisibleNodes()}setParametersNode(e=null){1===(e=(e=e||this.nodes).map(((t,s)=>{if(void 0===t.command&&(t=Object.assign({},this.settings.defaultProperties,t)),t.expanded=null!==this.settings.expandUpToLevel?t.depth<this.settings.expandUpToLevel:Boolean(t.expanded),t.parents=[],t.parentsStateIdentifier=[],t.depth>0){let i=t.depth;for(let n=s;n>=0;n--){let s=e[n];s.depth<i&&(t.parents.push(n),t.parentsStateIdentifier.push(e[n].stateIdentifier),i=s.depth)}}return void 0===t.checked&&(t.checked=!1),void 0===t.focused&&(t.focused=!1),t}))).filter((e=>0===e.depth)).length&&(e[0].expanded=!0);const t=new CustomEvent("typo3:svg-tree:nodes-prepared",{detail:{nodes:e},bubbles:!1});this.dispatchEvent(t),this.nodes=t.detail.nodes}nodesRemovePlaceholder(){const e=this.querySelector(".node-loader");e&&(e.style.display="none");const t=this.closest(".svg-tree")?.querySelector(".svg-tree-loader");t&&(t.style.display="none")}nodesAddPlaceholder(e=null){if(e){const t=this.querySelector(".node-loader");t&&(t.style.top=""+(e.y+this.settings.marginTop),t.style.display="block")}else{const e=this.closest(".svg-tree")?.querySelector(".svg-tree-loader");e&&(e.style.display="block")}}hideChildren(e){e.expanded=!1,this.setExpandedState(e),this.dispatchEvent(new CustomEvent("typo3:svg-tree:expand-toggle",{detail:{node:e}}))}showChildren(e){e.expanded=!0,this.setExpandedState(e),this.dispatchEvent(new CustomEvent("typo3:svg-tree:expand-toggle",{detail:{node:e}}))}setExpandedState(e){const t=this.getElementFromNode(e);t&&(e.hasChildren?t.setAttribute("aria-expanded",e.expanded?"true":"false"):t.removeAttribute("aria-expanded"))}refreshTree(){this.loadData()}refreshOrFilterTree(){""!==this.searchTerm?this.filter(this.searchTerm):this.refreshTree()}prepareDataForVisibleNodes(){const e={};this.nodes.forEach(((t,s)=>{t.expanded||(e[s]=!0)})),this.data.nodes=this.nodes.filter((t=>!0!==t.hidden&&!t.parents.some((t=>Boolean(e[t]))))),this.data.links=[];let t=0;this.data.nodes.forEach(((e,s)=>{e.x=e.depth*this.settings.indentWidth,e.readableRootline&&(t+=this.settings.nodeHeight),e.y=s*this.settings.nodeHeight+t,void 0!==e.parents[0]&&this.data.links.push({source:this.nodes[e.parents[0]],target:e}),this.settings.showIcons&&(this.fetchIcon(e.icon),this.fetchIcon(e.overlayIcon))})),this.svg.attr("height",this.data.nodes.length*this.settings.nodeHeight+this.settings.nodeHeight/2+t)}fetchIcon(e,t=!0){e&&(e in this.icons||(this.icons[e]={identifier:e,icon:null},Icons.getIcon(e,Icons.sizes.small,null,null,MarkupIdentifiers.inline).then((s=>{let i=s.match(/<svg[\s\S]*<\/svg>/i);if(i){let t=document.createRange().createContextualFragment(i[0]);this.icons[e].icon=t.firstElementChild}t&&this.updateVisibleNodes()}))))}updateVisibleNodes(){const e=Math.ceil(this.viewportHeight/this.settings.nodeHeight+1),t=Math.floor(Math.max(this.scrollTop-2*this.settings.nodeHeight,0)/this.settings.nodeHeight),s=this.data.nodes.slice(t,t+e),i=this.querySelector('[tabindex="0"]'),n=s.find((e=>e.focused)),o=s.find((e=>e.checked));let r=this.nodesContainer.selectAll(".node").data(s,(e=>e.stateIdentifier));const a=this.nodesBgContainer.selectAll(".node-bg").data(s,(e=>e.stateIdentifier)),d=this.nodesActionsContainer.selectAll(".node-action").data(s,(e=>e.stateIdentifier));r.exit().remove(),a.exit().remove(),d.exit().remove(),this.updateNodeActions(d);const l=this.updateNodeBgClass(a);l.attr("class",((e,t)=>this.getNodeBgClass(e,t,l))).attr("style",(e=>e.backgroundColor?"fill: "+e.backgroundColor+";":"")),this.updateLinks(),r=this.enterSvgElements(r),r.attr("tabindex",((e,t)=>{if(void 0!==n){if(n===e)return"0"}else if(void 0!==o){if(o===e)return"0"}else if(null===i){if(0===t)return"0"}else if(d3selection.select(i).datum()===e)return"0";return"-1"})).attr("transform",this.getNodeTransform).select(".node-name").html((e=>this.getNodeLabel(e))),r.select(".node-toggle").attr("class",this.getToggleClass).attr("visibility",this.getToggleVisibility),this.settings.showIcons&&(r.select("use.node-icon").attr("xlink:href",this.getIconId),r.select("use.node-icon-overlay").attr("xlink:href",this.getIconOverlayId),r.select("use.node-icon-locked").attr("xlink:href",(e=>"#icon-"+(e.locked?"overlay-backenduser":""))))}updateNodeBgClass(e){let t=this.settings.nodeHeight;return t-=1,e.enter().append("rect").merge(e).attr("width","100%").attr("height",t).attr("data-state-id",this.getNodeStateIdentifier).attr("transform",(e=>this.getNodeBackgroundTransform(e,this.settings.indentWidth,this.settings.nodeHeight))).on("mouseover",((e,t)=>this.onMouseOverNode(t))).on("mouseout",((e,t)=>this.onMouseOutOfNode(t))).on("click",((e,t)=>{this.selectNode(t,!0),this.focusNode(t),this.updateVisibleNodes()})).on("contextmenu",((e,t)=>{e.preventDefault(),this.dispatchEvent(new CustomEvent("typo3:svg-tree:node-context",{detail:{node:t}}))}))}getIconId(e){return"#icon-"+e.icon}getIconOverlayId(e){return"#icon-"+e.overlayIcon}selectNode(e,t=!0){this.isNodeSelectable(e)&&(this.disableSelectedNodes(),this.disableFocusedNodes(),e.checked=!0,e.focused=!0,this.dispatchEvent(new CustomEvent("typo3:svg-tree:node-selected",{detail:{node:e,propagate:t}})),this.updateVisibleNodes())}filter(e){"string"==typeof e&&(this.searchTerm=e),this.nodesAddPlaceholder(),this.searchTerm&&this.settings.filterUrl?new AjaxRequest(this.settings.filterUrl+"&q="+this.searchTerm).get({cache:"no-cache"}).then((e=>e.resolve())).then((e=>{let t=Array.isArray(e)?e:[];t.length>0&&(""===this.unfilteredNodes&&(this.unfilteredNodes=JSON.stringify(this.nodes)),this.replaceData(t)),this.nodesRemovePlaceholder()})).catch((e=>{throw this.errorNotification(e,!1),this.nodesRemovePlaceholder(),e})):this.resetFilter()}resetFilter(){if(this.searchTerm="",this.unfilteredNodes.length>0){let e=this.getSelectedNodes()[0];if(void 0===e)return void this.refreshTree();this.nodes=JSON.parse(this.unfilteredNodes),this.unfilteredNodes="";const t=this.getNodeByIdentifier(e.stateIdentifier);t?(this.selectNode(t,!1),this.focusNode(t),this.nodesRemovePlaceholder()):this.refreshTree()}else this.refreshTree();this.prepareDataForVisibleNodes(),this.updateVisibleNodes()}errorNotification(e=null,t=!1){if(Array.isArray(e))e.forEach((e=>{Notification.error(e.title,e.message)}));else{let t=this.networkErrorTitle;e&&e.target&&(e.target.status||e.target.statusText)&&(t+=" - "+(e.target.status||"")+" "+(e.target.statusText||"")),Notification.error(t,this.networkErrorMessage)}t&&this.loadData()}connectedCallback(){super.connectedCallback(),this.addEventListener("resize",this.updateViewRequested),this.addEventListener("scroll",this.updateViewRequested),this.addEventListener("svg-tree:visible",this.updateViewRequested),window.addEventListener("resize",this.windowResized)}disconnectedCallback(){this.removeEventListener("resize",this.updateViewRequested),this.removeEventListener("scroll",this.updateViewRequested),this.removeEventListener("svg-tree:visible",this.updateViewRequested),window.removeEventListener("resize",this.windowResized),super.disconnectedCallback()}getSelectedNodes(){return this.nodes.filter((e=>e.checked))}getFocusedNodes(){return this.nodes.filter((e=>e.focused))}disableFocusedNodes(){this.getFocusedNodes().forEach((e=>{!0===e.focused&&(e.focused=!1)}))}createRenderRoot(){return this}render(){return html` +var __decorate=function(e,t,s,i){var n,o=arguments.length,r=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,s):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,s,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(r=(o<3?n(r):o>3?n(t,s,r):n(t,s))||r);return o>3&&r&&Object.defineProperty(t,s,r),r};import{html,LitElement}from"lit";import{customElement,property,state}from"lit/decorators.js";import*as d3selection from"d3-selection";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Notification from"@typo3/backend/notification.js";import{KeyTypesEnum as KeyTypes}from"@typo3/backend/enum/key-types.js";import Icons from"@typo3/backend/icons.js";import{MarkupIdentifiers}from"@typo3/backend/enum/icon-types.js";import{lll}from"@typo3/core/lit-helper.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import"@typo3/backend/element/icon-element.js";export class SvgTree extends LitElement{constructor(){super(...arguments),this.setup=null,this.settings={showIcons:!1,marginTop:15,nodeHeight:26,icon:{size:16,containerSize:20},indentWidth:20,width:300,duration:400,dataUrl:"",filterUrl:"",defaultProperties:{},expandUpToLevel:null,actions:[]},this.isOverSvg=!1,this.svg=null,this.container=null,this.nodesContainer=null,this.nodesBgContainer=null,this.hoveredNode=null,this.nodes=[],this.textPosition=10,this.icons={},this.nodesActionsContainer=null,this.iconsContainer=null,this.linksContainer=null,this.data=new class{constructor(){this.links=[],this.nodes=[]}},this.viewportHeight=0,this.scrollBottom=0,this.searchTerm=null,this.unfilteredNodes="",this.networkErrorTitle=top.TYPO3.lang.tree_networkError,this.networkErrorMessage=top.TYPO3.lang.tree_networkErrorDescription,this.windowResized=()=>{this.getClientRects().length>0&&this.updateView()}}doSetup(e){Object.assign(this.settings,e),this.settings.showIcons&&(this.textPosition+=this.settings.icon.containerSize),this.svg=d3selection.select(this).select("svg"),this.container=this.svg.select(".nodes-wrapper"),this.nodesBgContainer=this.container.select(".nodes-bg"),this.nodesActionsContainer=this.container.select(".nodes-actions"),this.linksContainer=this.container.select(".links"),this.nodesContainer=this.container.select(".nodes"),this.iconsContainer=this.svg.select("defs"),this.updateScrollPosition(),this.loadCommonIcons(),this.loadData(),this.dispatchEvent(new Event("svg-tree:initialized"))}loadCommonIcons(){this.fetchIcon("actions-chevron-right",!1),this.fetchIcon("overlay-backenduser",!1),this.fetchIcon("actions-caret-right",!1),this.fetchIcon("actions-link",!1)}focusElement(e){if(null===e)return;e.parentNode.querySelectorAll("[tabindex]").forEach((e=>{e.setAttribute("tabindex","-1")})),e.setAttribute("tabindex","0"),e.focus()}focusNode(e){this.disableFocusedNodes(),e.focused=!0,this.focusElement(this.getElementFromNode(e))}getNodeFromElement(e){return null!==e&&"stateId"in e.dataset?this.getNodeByIdentifier(e.dataset.stateId):null}getElementFromNode(e){return this.querySelector("#identifier-"+this.getNodeStateIdentifier(e))}loadData(){this.nodesAddPlaceholder(),new AjaxRequest(this.settings.dataUrl).get({cache:"no-cache"}).then((e=>e.resolve())).then((e=>{const t=Array.isArray(e)?e:[];this.replaceData(t),this.nodesRemovePlaceholder(),this.updateScrollPosition(),this.updateVisibleNodes()})).catch((e=>{throw this.errorNotification(e,!1),this.nodesRemovePlaceholder(),e}))}replaceData(e){this.setParametersNode(e),this.prepareDataForVisibleNodes(),this.nodesContainer.selectAll(".node").remove(),this.nodesBgContainer.selectAll(".node-bg").remove(),this.nodesActionsContainer.selectAll(".node-action").remove(),this.linksContainer.selectAll(".link").remove(),this.updateVisibleNodes()}setParametersNode(e=null){1===(e=(e=e||this.nodes).map(((t,s)=>{if(void 0===t.command&&(t=Object.assign({},this.settings.defaultProperties,t)),t.expanded=null!==this.settings.expandUpToLevel?t.depth<this.settings.expandUpToLevel:Boolean(t.expanded),t.parents=[],t.parentsStateIdentifier=[],t.depth>0){let i=t.depth;for(let n=s;n>=0;n--){const s=e[n];s.depth<i&&(t.parents.push(n),t.parentsStateIdentifier.push(e[n].stateIdentifier),i=s.depth)}}return void 0===t.checked&&(t.checked=!1),void 0===t.focused&&(t.focused=!1),t}))).filter((e=>0===e.depth)).length&&(e[0].expanded=!0);const t=new CustomEvent("typo3:svg-tree:nodes-prepared",{detail:{nodes:e},bubbles:!1});this.dispatchEvent(t),this.nodes=t.detail.nodes}nodesRemovePlaceholder(){const e=this.querySelector(".node-loader");e&&(e.style.display="none");const t=this.closest(".svg-tree")?.querySelector(".svg-tree-loader");t&&(t.style.display="none")}nodesAddPlaceholder(e=null){if(e){const t=this.querySelector(".node-loader");t&&(t.style.top=""+(e.y+this.settings.marginTop),t.style.display="block")}else{const e=this.closest(".svg-tree")?.querySelector(".svg-tree-loader");e&&(e.style.display="block")}}hideChildren(e){e.expanded=!1,this.setExpandedState(e),this.dispatchEvent(new CustomEvent("typo3:svg-tree:expand-toggle",{detail:{node:e}}))}showChildren(e){e.expanded=!0,this.setExpandedState(e),this.dispatchEvent(new CustomEvent("typo3:svg-tree:expand-toggle",{detail:{node:e}}))}setExpandedState(e){const t=this.getElementFromNode(e);t&&(e.hasChildren?t.setAttribute("aria-expanded",e.expanded?"true":"false"):t.removeAttribute("aria-expanded"))}refreshTree(){this.loadData()}refreshOrFilterTree(){""!==this.searchTerm?this.filter(this.searchTerm):this.refreshTree()}prepareDataForVisibleNodes(){const e={};this.nodes.forEach(((t,s)=>{t.expanded||(e[s]=!0)})),this.data.nodes=this.nodes.filter((t=>!0!==t.hidden&&!t.parents.some((t=>Boolean(e[t]))))),this.data.links=[];let t=0;this.data.nodes.forEach(((e,s)=>{e.x=e.depth*this.settings.indentWidth,e.readableRootline&&(t+=this.settings.nodeHeight),e.y=s*this.settings.nodeHeight+t,void 0!==e.parents[0]&&this.data.links.push({source:this.nodes[e.parents[0]],target:e}),this.settings.showIcons&&(this.fetchIcon(e.icon),this.fetchIcon(e.overlayIcon))})),this.svg.attr("height",this.data.nodes.length*this.settings.nodeHeight+this.settings.nodeHeight/2+t)}fetchIcon(e,t=!0){e&&(e in this.icons||(this.icons[e]={identifier:e,icon:null},Icons.getIcon(e,Icons.sizes.small,null,null,MarkupIdentifiers.inline).then((s=>{const i=s.match(/<svg[\s\S]*<\/svg>/i);if(i){const t=document.createRange().createContextualFragment(i[0]);this.icons[e].icon=t.firstElementChild}t&&this.updateVisibleNodes()}))))}updateVisibleNodes(){const e=Math.ceil(this.viewportHeight/this.settings.nodeHeight+1),t=Math.floor(Math.max(this.scrollTop-2*this.settings.nodeHeight,0)/this.settings.nodeHeight),s=this.data.nodes.slice(t,t+e),i=this.querySelector('[tabindex="0"]'),n=s.find((e=>e.focused)),o=s.find((e=>e.checked));let r=this.nodesContainer.selectAll(".node").data(s,(e=>e.stateIdentifier));const a=this.nodesBgContainer.selectAll(".node-bg").data(s,(e=>e.stateIdentifier)),d=this.nodesActionsContainer.selectAll(".node-action").data(s,(e=>e.stateIdentifier));r.exit().remove(),a.exit().remove(),d.exit().remove(),this.updateNodeActions(d);const l=this.updateNodeBgClass(a);l.attr("class",((e,t)=>this.getNodeBgClass(e,t,l))).attr("style",(e=>e.backgroundColor?"fill: "+e.backgroundColor+";":"")),this.updateLinks(),r=this.enterSvgElements(r),r.attr("tabindex",((e,t)=>{if(void 0!==n){if(n===e)return"0"}else if(void 0!==o){if(o===e)return"0"}else if(null===i){if(0===t)return"0"}else if(d3selection.select(i).datum()===e)return"0";return"-1"})).attr("transform",this.getNodeTransform).select(".node-name").html((e=>this.getNodeLabel(e))),r.select(".node-toggle").attr("class",this.getToggleClass).attr("visibility",this.getToggleVisibility),this.settings.showIcons&&(r.select("use.node-icon").attr("xlink:href",this.getIconId),r.select("use.node-icon-overlay").attr("xlink:href",this.getIconOverlayId),r.select("use.node-icon-locked").attr("xlink:href",(e=>"#icon-"+(e.locked?"overlay-backenduser":""))))}updateNodeBgClass(e){let t=this.settings.nodeHeight;t-=1;return e.enter().append("rect").merge(e).attr("width","100%").attr("height",t).attr("data-state-id",this.getNodeStateIdentifier).attr("transform",(e=>this.getNodeBackgroundTransform(e,this.settings.indentWidth,this.settings.nodeHeight))).on("mouseover",((e,t)=>this.onMouseOverNode(t))).on("mouseout",((e,t)=>this.onMouseOutOfNode(t))).on("click",((e,t)=>{this.selectNode(t,!0),this.focusNode(t),this.updateVisibleNodes()})).on("contextmenu",((e,t)=>{e.preventDefault(),this.dispatchEvent(new CustomEvent("typo3:svg-tree:node-context",{detail:{node:t}}))}))}getIconId(e){return"#icon-"+e.icon}getIconOverlayId(e){return"#icon-"+e.overlayIcon}selectNode(e,t=!0){this.isNodeSelectable(e)&&(this.disableSelectedNodes(),this.disableFocusedNodes(),e.checked=!0,e.focused=!0,this.dispatchEvent(new CustomEvent("typo3:svg-tree:node-selected",{detail:{node:e,propagate:t}})),this.updateVisibleNodes())}filter(e){"string"==typeof e&&(this.searchTerm=e),this.nodesAddPlaceholder(),this.searchTerm&&this.settings.filterUrl?new AjaxRequest(this.settings.filterUrl+"&q="+this.searchTerm).get({cache:"no-cache"}).then((e=>e.resolve())).then((e=>{const t=Array.isArray(e)?e:[];t.length>0&&(""===this.unfilteredNodes&&(this.unfilteredNodes=JSON.stringify(this.nodes)),this.replaceData(t)),this.nodesRemovePlaceholder()})).catch((e=>{throw this.errorNotification(e,!1),this.nodesRemovePlaceholder(),e})):this.resetFilter()}resetFilter(){if(this.searchTerm="",this.unfilteredNodes.length>0){const e=this.getSelectedNodes()[0];if(void 0===e)return void this.refreshTree();this.nodes=JSON.parse(this.unfilteredNodes),this.unfilteredNodes="";const t=this.getNodeByIdentifier(e.stateIdentifier);t?(this.selectNode(t,!1),this.focusNode(t),this.nodesRemovePlaceholder()):this.refreshTree()}else this.refreshTree();this.prepareDataForVisibleNodes(),this.updateVisibleNodes()}errorNotification(e=null,t=!1){if(Array.isArray(e))e.forEach((e=>{Notification.error(e.title,e.message)}));else{let t=this.networkErrorTitle;e&&e.target&&(e.target.status||e.target.statusText)&&(t+=" - "+(e.target.status||"")+" "+(e.target.statusText||"")),Notification.error(t,this.networkErrorMessage)}t&&this.loadData()}connectedCallback(){super.connectedCallback(),this.addEventListener("resize",this.updateViewRequested),this.addEventListener("scroll",this.updateViewRequested),this.addEventListener("svg-tree:visible",this.updateViewRequested),window.addEventListener("resize",this.windowResized)}disconnectedCallback(){this.removeEventListener("resize",this.updateViewRequested),this.removeEventListener("scroll",this.updateViewRequested),this.removeEventListener("svg-tree:visible",this.updateViewRequested),window.removeEventListener("resize",this.windowResized),super.disconnectedCallback()}getSelectedNodes(){return this.nodes.filter((e=>e.checked))}getFocusedNodes(){return this.nodes.filter((e=>e.focused))}disableFocusedNodes(){this.getFocusedNodes().forEach((e=>{!0===e.focused&&(e.focused=!1)}))}createRenderRoot(){return this}render(){return html` <div class="node-loader"> <typo3-backend-icon identifier="spinner-circle-light" size="small"></typo3-backend-icon> </div> @@ -27,7 +27,7 @@ var __decorate=function(e,t,s,i){var n,o=arguments.length,r=o<3?t:null===i?i=Obj </g> <defs></defs> </svg> - `}firstUpdated(){this.svg=d3selection.select(this.querySelector("svg")),this.container=d3selection.select(this.querySelector(".nodes-wrapper")).attr("transform","translate("+this.settings.indentWidth/2+","+this.settings.nodeHeight/2+")"),this.nodesBgContainer=d3selection.select(this.querySelector(".nodes-bg")),this.nodesActionsContainer=d3selection.select(this.querySelector(".nodes-actions")),this.linksContainer=d3selection.select(this.querySelector(".links")),this.nodesContainer=d3selection.select(this.querySelector(".nodes")),this.doSetup(this.setup||{}),this.updateView()}updateViewRequested(){this.updateView()}updateView(){this.updateScrollPosition(),this.updateVisibleNodes(),this.settings.actions&&this.settings.actions.length&&this.nodesActionsContainer.attr("transform","translate("+(this.querySelector("svg").clientWidth-16-16*this.settings.actions.length)+",0)")}disableSelectedNodes(){this.getSelectedNodes().forEach((e=>{!0===e.checked&&(e.checked=!1)}))}updateNodeActions(e){return this.settings.actions&&this.settings.actions.length?(this.nodesActionsContainer.selectAll(".node-action").selectChildren().remove(),e.enter().append("g").merge(e).attr("class","node-action").on("mouseover",((e,t)=>this.onMouseOverNode(t))).on("mouseout",((e,t)=>this.onMouseOutOfNode(t))).attr("data-state-id",this.getNodeStateIdentifier).attr("transform",(e=>this.getNodeActionTransform(e,this.settings.indentWidth,this.settings.nodeHeight)))):e.enter()}createIconAreaForAction(e,t){const s=e.append("svg").attr("class","node-icon-container").attr("height",this.settings.icon.containerSize).attr("width",this.settings.icon.containerSize).attr("x","0").attr("y","0");s.append("rect").attr("height",this.settings.icon.containerSize).attr("width",this.settings.icon.containerSize).attr("y","0").attr("x","0").attr("class","node-icon-click");s.append("svg").attr("height",this.settings.icon.size).attr("width",this.settings.icon.size).attr("y",(this.settings.icon.containerSize-this.settings.icon.size)/2).attr("x",(this.settings.icon.containerSize-this.settings.icon.size)/2).attr("class","node-icon-inner").append("use").attr("class","node-icon").attr("xlink:href","#icon-"+t)}isNodeSelectable(e){return!0}appendTextElement(e){return e.append("text").attr("dx",this.textPosition).attr("dy",5).attr("class","node-name").on("click",((e,t)=>{this.selectNode(t,!0),this.focusNode(t),this.updateVisibleNodes()}))}nodesUpdate(e){return(e=e.enter().append("g").attr("class","node").attr("id",(e=>"identifier-"+e.stateIdentifier)).attr("role","treeitem").attr("aria-owns",(e=>e.hasChildren?"group-identifier-"+e.stateIdentifier:null)).attr("aria-level",this.getNodeDepth).attr("aria-setsize",this.getNodeSetsize).attr("aria-posinset",this.getNodePositionInSet).attr("aria-expanded",(e=>e.hasChildren?e.expanded:null)).attr("transform",this.getNodeTransform).attr("data-state-id",this.getNodeStateIdentifier).attr("title",this.getNodeTitle).on("mouseover",((e,t)=>this.onMouseOverNode(t))).on("mouseout",((e,t)=>this.onMouseOutOfNode(t))).on("contextmenu",((e,t)=>{e.preventDefault(),this.dispatchEvent(new CustomEvent("typo3:svg-tree:node-context",{detail:{node:t}}))}))).append("text").text((e=>e.readableRootline)).attr("class","node-rootline").attr("dx",0).attr("dy",this.settings.nodeHeight/2*-1).attr("visibility",(e=>e.readableRootline?"visible":"hidden")),e}getNodeIdentifier(e){return e.identifier}getNodeDepth(e){return e.depth}getNodeSetsize(e){return e.siblingsCount}getNodePositionInSet(e){return e.siblingsPosition}getNodeStateIdentifier(e){return e.stateIdentifier}getNodeLabel(e){let t=(e.prefix||"")+e.name+(e.suffix||"");const s=document.createElement("div");if(s.textContent=t,t=s.innerHTML,this.searchTerm){const e=new RegExp(this.searchTerm,"gi");t=t.replace(e,'<tspan class="node-highlight-text">$&</tspan>')}return t}getNodeByIdentifier(e){return this.nodes.find((t=>t.stateIdentifier===e))}getNodeBgClass(e,t,s){let i="node-bg",n=null,o=null;return"object"==typeof s&&(n=s.data()[t-1],o=s.data()[t+1]),e.checked&&(i+=" node-selected"),e.focused&&(i+=" node-focused"),(n&&e.depth>n.depth||!n)&&(e.firstChild=!0,i+=" node-first-child"),(o&&e.depth>o.depth||!o)&&(e.lastChild=!0,i+=" node-last-child"),e.class&&(i+=" "+e.class),i}getNodeTitle(e){return e.tip?e.tip:"uid="+e.identifier}getToggleVisibility(e){return e.hasChildren?"visible":"hidden"}getToggleClass(e){return"node-toggle node-toggle--"+(e.expanded?"expanded":"collapsed")+" chevron "+(e.expanded?"expanded":"collapsed")}getLinkPath(e){const t=e.target.x,s=e.target.y,i=[];return i.push("M"+e.source.x+" "+e.source.y),i.push("V"+s),e.target.hasChildren?i.push("H"+(t-2)):i.push("H"+(t+this.settings.indentWidth/4-2)),i.join(" ")}getNodeTransform(e){return"translate("+(e.x||0)+","+(e.y||0)+")"}getNodeBackgroundTransform(e,t,s){let i=t/2*-1,n=(e.y||0)-s/2;return n+=.5,"translate("+i+", "+n+")"}getNodeActionTransform(e,t,s){return"translate("+t/2*-1+", "+((e.y||0)-s/2)+")"}clickOnIcon(e){this.dispatchEvent(new CustomEvent("typo3:svg-tree:node-context",{detail:{node:e}}))}handleNodeToggle(e){e.expanded?this.hideChildren(e):this.showChildren(e),this.prepareDataForVisibleNodes(),this.updateVisibleNodes()}enterSvgElements(e){if(this.settings.showIcons){const e=Object.values(this.icons).filter((e=>""!==e.icon&&null!==e.icon)),t=this.iconsContainer.selectAll(".icon-def").data(e,(e=>e.identifier));t.exit().remove(),t.enter().append("g").attr("class","icon-def").attr("id",(e=>"icon-"+e.identifier)).append((e=>{if(e.icon instanceof SVGElement)return e.icon;const t="<svg>"+e.icon+"</svg>";return(new DOMParser).parseFromString(t,"image/svg+xml").documentElement.firstChild}))}const t=this.nodesUpdate(e);let s=t.append("svg").attr("class","node-toggle").attr("y",this.settings.icon.size/2*-1).attr("x",this.settings.icon.size/2*-1).attr("visibility",this.getToggleVisibility).attr("height",this.settings.icon.size).attr("width",this.settings.icon.size).on("click",((e,t)=>this.handleNodeToggle(t)));if(s.append("use").attr("class","node-toggle-icon").attr("href","#icon-actions-chevron-right"),s.append("rect").attr("class","node-toggle-spacer").attr("height",this.settings.icon.size).attr("width",this.settings.icon.size).attr("fill","transparent"),this.settings.showIcons){const e=t.append("svg").attr("class","node-icon-container").attr("height","20").attr("width","20").attr("x","6").attr("y","-10").on("click",((e,t)=>{e.preventDefault(),this.clickOnIcon(t)}));e.append("rect").style("opacity",0).attr("width","20").attr("height","20").attr("y","0").attr("x","0").attr("class","node-icon-click");const s=e.append("svg").attr("height","16").attr("width","16").attr("y","2").attr("x","2").attr("class","node-icon-inner");s.append("use").attr("class","node-icon").attr("data-uid",this.getNodeIdentifier);s.append("svg").attr("height","11").attr("width","11").attr("y","5").attr("x","5").append("use").attr("class","node-icon-overlay");s.append("svg").attr("height","11").attr("width","11").attr("y","5").attr("x","5").append("use").attr("class","node-icon-locked")}return t.append("title").text(this.getNodeTitle),this.appendTextElement(t),e.merge(t)}onMouseOverNode(e){e.isOver=!0,this.hoveredNode=e;let t=this.svg.select('.nodes-bg .node-bg[data-state-id="'+e.stateIdentifier+'"]');t.size()&&t.classed("node-over",!0);let s=this.nodesActionsContainer.select('.node-action[data-state-id="'+e.stateIdentifier+'"]');s.size()&&(s.classed("node-action-over",!0),s.attr("fill",t.style("fill")))}onMouseOutOfNode(e){e.isOver=!1,this.hoveredNode=null;let t=this.svg.select('.nodes-bg .node-bg[data-state-id="'+e.stateIdentifier+'"]');t.size()&&t.classed("node-over node-alert",!1);let s=this.nodesActionsContainer.select('.node-action[data-state-id="'+e.stateIdentifier+'"]');s.size()&&s.classed("node-action-over",!1)}updateScrollPosition(){this.viewportHeight=this.getBoundingClientRect().height,this.scrollBottom=this.scrollTop+this.viewportHeight+this.viewportHeight/2}handleKeyboardInteraction(e){const t=e.target;let s=d3selection.select(t).datum();if(-1===[KeyTypes.ENTER,KeyTypes.SPACE,KeyTypes.END,KeyTypes.HOME,KeyTypes.LEFT,KeyTypes.UP,KeyTypes.RIGHT,KeyTypes.DOWN].indexOf(e.keyCode))return;e.preventDefault();const i=t.parentNode;switch(e.keyCode){case KeyTypes.END:this.scrollTop=this.lastElementChild.getBoundingClientRect().height+this.settings.nodeHeight-this.viewportHeight,i.scrollIntoView({behavior:"smooth",block:"end"}),this.focusNode(this.getNodeFromElement(i.lastElementChild)),this.updateVisibleNodes();break;case KeyTypes.HOME:this.scrollTo({top:this.nodes[0].y,behavior:"smooth"}),this.prepareDataForVisibleNodes(),this.focusNode(this.getNodeFromElement(i.firstElementChild)),this.updateVisibleNodes();break;case KeyTypes.LEFT:if(s.expanded)s.hasChildren&&(this.hideChildren(s),this.prepareDataForVisibleNodes(),this.updateVisibleNodes());else if(s.parents.length>0){let e=this.nodes[s.parents[0]];this.scrollNodeIntoVisibleArea(e,"up"),this.focusNode(e),this.updateVisibleNodes()}break;case KeyTypes.UP:this.scrollNodeIntoVisibleArea(s,"up"),t.previousSibling&&(this.focusNode(this.getNodeFromElement(t.previousSibling)),this.updateVisibleNodes());break;case KeyTypes.RIGHT:s.expanded?(this.scrollNodeIntoVisibleArea(s,"down"),this.focusNode(this.getNodeFromElement(t.nextSibling)),this.updateVisibleNodes()):s.hasChildren&&(this.showChildren(s),this.prepareDataForVisibleNodes(),this.focusNode(this.getNodeFromElement(t)),this.updateVisibleNodes());break;case KeyTypes.DOWN:this.scrollNodeIntoVisibleArea(s,"down"),t.nextSibling&&(this.focusNode(this.getNodeFromElement(t.nextSibling)),this.updateVisibleNodes());break;case KeyTypes.ENTER:case KeyTypes.SPACE:this.selectNode(s,!0),this.focusNode(s)}}scrollNodeIntoVisibleArea(e,t="up"){let s=this.scrollTop;if("up"===t&&s>e.y-this.settings.nodeHeight)s=e.y-this.settings.nodeHeight;else{if(!("down"===t&&s+this.viewportHeight<=e.y+3*this.settings.nodeHeight))return;s+=this.settings.nodeHeight}this.scrollTo({top:s,behavior:"smooth"}),this.updateVisibleNodes()}updateLinks(){const e=this.data.links.filter((e=>e.source.y<=this.scrollBottom&&e.target.y>=this.scrollTop-this.settings.nodeHeight)).map((e=>(e.source.owns=e.source.owns||[],e.source.owns.push("identifier-"+e.target.stateIdentifier),e))),t=this.linksContainer.selectAll(".link").data(e);t.exit().remove(),t.enter().append("path").attr("class","link").attr("id",this.getGroupIdentifier).attr("role",(e=>1===e.target.siblingsPosition&&e.source.owns.length>0?"group":null)).attr("aria-owns",(e=>1===e.target.siblingsPosition&&e.source.owns.length>0?e.source.owns.join(" "):null)).merge(t).attr("d",(e=>this.getLinkPath(e)))}getGroupIdentifier(e){return 1===e.target.siblingsPosition?"group-identifier-"+e.source.stateIdentifier:null}}__decorate([property({type:Object})],SvgTree.prototype,"setup",void 0),__decorate([state()],SvgTree.prototype,"settings",void 0);let Toolbar=class extends LitElement{constructor(){super(...arguments),this.tree=null,this.settings={searchInput:".search-input",filterTimeout:450}}createRenderRoot(){return this}firstUpdated(){const e=this.querySelector(this.settings.searchInput);e&&new DebounceEvent("input",(e=>{const t=e.target;this.tree.filter(t.value.trim())}),this.settings.filterTimeout).bindTo(e)}render(){return html` + `}firstUpdated(){this.svg=d3selection.select(this.querySelector("svg")),this.container=d3selection.select(this.querySelector(".nodes-wrapper")).attr("transform","translate("+this.settings.indentWidth/2+","+this.settings.nodeHeight/2+")"),this.nodesBgContainer=d3selection.select(this.querySelector(".nodes-bg")),this.nodesActionsContainer=d3selection.select(this.querySelector(".nodes-actions")),this.linksContainer=d3selection.select(this.querySelector(".links")),this.nodesContainer=d3selection.select(this.querySelector(".nodes")),this.doSetup(this.setup||{}),this.updateView()}updateViewRequested(){this.updateView()}updateView(){this.updateScrollPosition(),this.updateVisibleNodes(),this.settings.actions&&this.settings.actions.length&&this.nodesActionsContainer.attr("transform","translate("+(this.querySelector("svg").clientWidth-16-16*this.settings.actions.length)+",0)")}disableSelectedNodes(){this.getSelectedNodes().forEach((e=>{!0===e.checked&&(e.checked=!1)}))}updateNodeActions(e){return this.settings.actions&&this.settings.actions.length?(this.nodesActionsContainer.selectAll(".node-action").selectChildren().remove(),e.enter().append("g").merge(e).attr("class","node-action").on("mouseover",((e,t)=>this.onMouseOverNode(t))).on("mouseout",((e,t)=>this.onMouseOutOfNode(t))).attr("data-state-id",this.getNodeStateIdentifier).attr("transform",(e=>this.getNodeActionTransform(e,this.settings.indentWidth,this.settings.nodeHeight)))):e.enter()}createIconAreaForAction(e,t){const s=e.append("svg").attr("class","node-icon-container").attr("height",this.settings.icon.containerSize).attr("width",this.settings.icon.containerSize).attr("x","0").attr("y","0");s.append("rect").attr("height",this.settings.icon.containerSize).attr("width",this.settings.icon.containerSize).attr("y","0").attr("x","0").attr("class","node-icon-click");s.append("svg").attr("height",this.settings.icon.size).attr("width",this.settings.icon.size).attr("y",(this.settings.icon.containerSize-this.settings.icon.size)/2).attr("x",(this.settings.icon.containerSize-this.settings.icon.size)/2).attr("class","node-icon-inner").append("use").attr("class","node-icon").attr("xlink:href","#icon-"+t)}isNodeSelectable(e){return!0}appendTextElement(e){return e.append("text").attr("dx",this.textPosition).attr("dy",5).attr("class","node-name").on("click",((e,t)=>{this.selectNode(t,!0),this.focusNode(t),this.updateVisibleNodes()}))}nodesUpdate(e){return(e=e.enter().append("g").attr("class","node").attr("id",(e=>"identifier-"+e.stateIdentifier)).attr("role","treeitem").attr("aria-owns",(e=>e.hasChildren?"group-identifier-"+e.stateIdentifier:null)).attr("aria-level",this.getNodeDepth).attr("aria-setsize",this.getNodeSetsize).attr("aria-posinset",this.getNodePositionInSet).attr("aria-expanded",(e=>e.hasChildren?e.expanded:null)).attr("transform",this.getNodeTransform).attr("data-state-id",this.getNodeStateIdentifier).attr("title",this.getNodeTitle).on("mouseover",((e,t)=>this.onMouseOverNode(t))).on("mouseout",((e,t)=>this.onMouseOutOfNode(t))).on("contextmenu",((e,t)=>{e.preventDefault(),this.dispatchEvent(new CustomEvent("typo3:svg-tree:node-context",{detail:{node:t}}))}))).append("text").text((e=>e.readableRootline)).attr("class","node-rootline").attr("dx",0).attr("dy",this.settings.nodeHeight/2*-1).attr("visibility",(e=>e.readableRootline?"visible":"hidden")),e}getNodeIdentifier(e){return e.identifier}getNodeDepth(e){return e.depth}getNodeSetsize(e){return e.siblingsCount}getNodePositionInSet(e){return e.siblingsPosition}getNodeStateIdentifier(e){return e.stateIdentifier}getNodeLabel(e){let t=(e.prefix||"")+e.name+(e.suffix||"");const s=document.createElement("div");if(s.textContent=t,t=s.innerHTML,this.searchTerm){const e=new RegExp(this.searchTerm,"gi");t=t.replace(e,'<tspan class="node-highlight-text">$&</tspan>')}return t}getNodeByIdentifier(e){return this.nodes.find((t=>t.stateIdentifier===e))}getNodeBgClass(e,t,s){let i="node-bg",n=null,o=null;return"object"==typeof s&&(n=s.data()[t-1],o=s.data()[t+1]),e.checked&&(i+=" node-selected"),e.focused&&(i+=" node-focused"),(n&&e.depth>n.depth||!n)&&(e.firstChild=!0,i+=" node-first-child"),(o&&e.depth>o.depth||!o)&&(e.lastChild=!0,i+=" node-last-child"),e.class&&(i+=" "+e.class),i}getNodeTitle(e){return e.tip?e.tip:"uid="+e.identifier}getToggleVisibility(e){return e.hasChildren?"visible":"hidden"}getToggleClass(e){return"node-toggle node-toggle--"+(e.expanded?"expanded":"collapsed")+" chevron "+(e.expanded?"expanded":"collapsed")}getLinkPath(e){const t=e.target.x,s=e.target.y,i=[];return i.push("M"+e.source.x+" "+e.source.y),i.push("V"+s),e.target.hasChildren?i.push("H"+(t-2)):i.push("H"+(t+this.settings.indentWidth/4-2)),i.join(" ")}getNodeTransform(e){return"translate("+(e.x||0)+","+(e.y||0)+")"}getNodeBackgroundTransform(e,t,s){const i=t/2*-1;let n=(e.y||0)-s/2;return n+=.5,"translate("+i+", "+n+")"}getNodeActionTransform(e,t,s){return"translate("+t/2*-1+", "+((e.y||0)-s/2)+")"}clickOnIcon(e){this.dispatchEvent(new CustomEvent("typo3:svg-tree:node-context",{detail:{node:e}}))}handleNodeToggle(e){e.expanded?this.hideChildren(e):this.showChildren(e),this.prepareDataForVisibleNodes(),this.updateVisibleNodes()}enterSvgElements(e){if(this.settings.showIcons){const e=Object.values(this.icons).filter((e=>""!==e.icon&&null!==e.icon)),t=this.iconsContainer.selectAll(".icon-def").data(e,(e=>e.identifier));t.exit().remove(),t.enter().append("g").attr("class","icon-def").attr("id",(e=>"icon-"+e.identifier)).append((e=>{if(e.icon instanceof SVGElement)return e.icon;const t="<svg>"+e.icon+"</svg>";return(new DOMParser).parseFromString(t,"image/svg+xml").documentElement.firstChild}))}const t=this.nodesUpdate(e),s=t.append("svg").attr("class","node-toggle").attr("y",this.settings.icon.size/2*-1).attr("x",this.settings.icon.size/2*-1).attr("visibility",this.getToggleVisibility).attr("height",this.settings.icon.size).attr("width",this.settings.icon.size).on("click",((e,t)=>this.handleNodeToggle(t)));if(s.append("use").attr("class","node-toggle-icon").attr("href","#icon-actions-chevron-right"),s.append("rect").attr("class","node-toggle-spacer").attr("height",this.settings.icon.size).attr("width",this.settings.icon.size).attr("fill","transparent"),this.settings.showIcons){const e=t.append("svg").attr("class","node-icon-container").attr("height","20").attr("width","20").attr("x","6").attr("y","-10").on("click",((e,t)=>{e.preventDefault(),this.clickOnIcon(t)}));e.append("rect").style("opacity",0).attr("width","20").attr("height","20").attr("y","0").attr("x","0").attr("class","node-icon-click");const s=e.append("svg").attr("height","16").attr("width","16").attr("y","2").attr("x","2").attr("class","node-icon-inner");s.append("use").attr("class","node-icon").attr("data-uid",this.getNodeIdentifier);s.append("svg").attr("height","11").attr("width","11").attr("y","5").attr("x","5").append("use").attr("class","node-icon-overlay");s.append("svg").attr("height","11").attr("width","11").attr("y","5").attr("x","5").append("use").attr("class","node-icon-locked")}return t.append("title").text(this.getNodeTitle),this.appendTextElement(t),e.merge(t)}onMouseOverNode(e){e.isOver=!0,this.hoveredNode=e;const t=this.svg.select('.nodes-bg .node-bg[data-state-id="'+e.stateIdentifier+'"]');t.size()&&t.classed("node-over",!0);const s=this.nodesActionsContainer.select('.node-action[data-state-id="'+e.stateIdentifier+'"]');s.size()&&(s.classed("node-action-over",!0),s.attr("fill",t.style("fill")))}onMouseOutOfNode(e){e.isOver=!1,this.hoveredNode=null;const t=this.svg.select('.nodes-bg .node-bg[data-state-id="'+e.stateIdentifier+'"]');t.size()&&t.classed("node-over node-alert",!1);const s=this.nodesActionsContainer.select('.node-action[data-state-id="'+e.stateIdentifier+'"]');s.size()&&s.classed("node-action-over",!1)}updateScrollPosition(){this.viewportHeight=this.getBoundingClientRect().height,this.scrollBottom=this.scrollTop+this.viewportHeight+this.viewportHeight/2}handleKeyboardInteraction(e){const t=e.target,s=d3selection.select(t).datum();if(-1===[KeyTypes.ENTER,KeyTypes.SPACE,KeyTypes.END,KeyTypes.HOME,KeyTypes.LEFT,KeyTypes.UP,KeyTypes.RIGHT,KeyTypes.DOWN].indexOf(e.keyCode))return;e.preventDefault();const i=t.parentNode;switch(e.keyCode){case KeyTypes.END:this.scrollTop=this.lastElementChild.getBoundingClientRect().height+this.settings.nodeHeight-this.viewportHeight,i.scrollIntoView({behavior:"smooth",block:"end"}),this.focusNode(this.getNodeFromElement(i.lastElementChild)),this.updateVisibleNodes();break;case KeyTypes.HOME:this.scrollTo({top:this.nodes[0].y,behavior:"smooth"}),this.prepareDataForVisibleNodes(),this.focusNode(this.getNodeFromElement(i.firstElementChild)),this.updateVisibleNodes();break;case KeyTypes.LEFT:if(s.expanded)s.hasChildren&&(this.hideChildren(s),this.prepareDataForVisibleNodes(),this.updateVisibleNodes());else if(s.parents.length>0){const e=this.nodes[s.parents[0]];this.scrollNodeIntoVisibleArea(e,"up"),this.focusNode(e),this.updateVisibleNodes()}break;case KeyTypes.UP:this.scrollNodeIntoVisibleArea(s,"up"),t.previousSibling&&(this.focusNode(this.getNodeFromElement(t.previousSibling)),this.updateVisibleNodes());break;case KeyTypes.RIGHT:s.expanded?(this.scrollNodeIntoVisibleArea(s,"down"),this.focusNode(this.getNodeFromElement(t.nextSibling)),this.updateVisibleNodes()):s.hasChildren&&(this.showChildren(s),this.prepareDataForVisibleNodes(),this.focusNode(this.getNodeFromElement(t)),this.updateVisibleNodes());break;case KeyTypes.DOWN:this.scrollNodeIntoVisibleArea(s,"down"),t.nextSibling&&(this.focusNode(this.getNodeFromElement(t.nextSibling)),this.updateVisibleNodes());break;case KeyTypes.ENTER:case KeyTypes.SPACE:this.selectNode(s,!0),this.focusNode(s)}}scrollNodeIntoVisibleArea(e,t="up"){let s=this.scrollTop;if("up"===t&&s>e.y-this.settings.nodeHeight)s=e.y-this.settings.nodeHeight;else{if(!("down"===t&&s+this.viewportHeight<=e.y+3*this.settings.nodeHeight))return;s+=this.settings.nodeHeight}this.scrollTo({top:s,behavior:"smooth"}),this.updateVisibleNodes()}updateLinks(){const e=this.data.links.filter((e=>e.source.y<=this.scrollBottom&&e.target.y>=this.scrollTop-this.settings.nodeHeight)).map((e=>(e.source.owns=e.source.owns||[],e.source.owns.push("identifier-"+e.target.stateIdentifier),e))),t=this.linksContainer.selectAll(".link").data(e);t.exit().remove(),t.enter().append("path").attr("class","link").attr("id",this.getGroupIdentifier).attr("role",(e=>1===e.target.siblingsPosition&&e.source.owns.length>0?"group":null)).attr("aria-owns",(e=>1===e.target.siblingsPosition&&e.source.owns.length>0?e.source.owns.join(" "):null)).merge(t).attr("d",(e=>this.getLinkPath(e)))}getGroupIdentifier(e){return 1===e.target.siblingsPosition?"group-identifier-"+e.source.stateIdentifier:null}}__decorate([property({type:Object})],SvgTree.prototype,"setup",void 0),__decorate([state()],SvgTree.prototype,"settings",void 0);let Toolbar=class extends LitElement{constructor(){super(...arguments),this.tree=null,this.settings={searchInput:".search-input",filterTimeout:450}}createRenderRoot(){return this}firstUpdated(){const e=this.querySelector(this.settings.searchInput);e&&new DebounceEvent("input",(e=>{const t=e.target;this.tree.filter(t.value.trim())}),this.settings.filterTimeout).bindTo(e)}render(){return html` <div class="tree-toolbar"> <div class="svg-toolbar__menu"> <div class="svg-toolbar__search"> diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/switch-user.js b/typo3/sysext/backend/Resources/Public/JavaScript/switch-user.js index 89d595243e98..7773e47da24c 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/switch-user.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/switch-user.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -var Modes,__decorate=function(t,e,r,o){var i,s=arguments.length,c=s<3?e:null===o?o=Object.getOwnPropertyDescriptor(e,r):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)c=Reflect.decorate(t,e,r,o);else for(var n=t.length-1;n>=0;n--)(i=t[n])&&(c=(s<3?i(c):s>3?i(e,r,c):i(e,r))||c);return s>3&&c&&Object.defineProperty(e,r,c),c};import{html,LitElement}from"lit";import{customElement,property}from"lit/decorators.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Notification from"@typo3/backend/notification.js";!function(t){t.switch="switch",t.exit="exit"}(Modes||(Modes={}));let SwitchUser=class extends LitElement{constructor(){super(),this.mode=Modes.switch,this.addEventListener("click",(t=>{t.preventDefault(),this.mode===Modes.switch?this.handleSwitchUser():this.mode===Modes.exit&&this.handleExitSwitchUser()}))}render(){return html`<slot></slot>`}handleSwitchUser(){this.targetUser?new AjaxRequest(TYPO3.settings.ajaxUrls.switch_user).post({targetUser:this.targetUser}).then((async t=>{const e=await t.resolve();!0===e.success&&e.url?top.window.location.href=e.url:Notification.error("Switching to user went wrong.")})):Notification.error("Switching to user went wrong.")}handleExitSwitchUser(){new AjaxRequest(TYPO3.settings.ajaxUrls.switch_user_exit).post({}).then((async t=>{const e=await t.resolve();!0===e.success&&e.url?top.window.location.href=e.url:Notification.error("Exiting current user went wrong.")}))}};__decorate([property({type:String})],SwitchUser.prototype,"targetUser",void 0),__decorate([property({type:Modes})],SwitchUser.prototype,"mode",void 0),SwitchUser=__decorate([customElement("typo3-backend-switch-user")],SwitchUser); \ No newline at end of file +var Modes,__decorate=function(t,e,r,o){var i,s=arguments.length,c=s<3?e:null===o?o=Object.getOwnPropertyDescriptor(e,r):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)c=Reflect.decorate(t,e,r,o);else for(var n=t.length-1;n>=0;n--)(i=t[n])&&(c=(s<3?i(c):s>3?i(e,r,c):i(e,r))||c);return s>3&&c&&Object.defineProperty(e,r,c),c};import{html,LitElement}from"lit";import{customElement,property}from"lit/decorators.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Notification from"@typo3/backend/notification.js";!function(t){t.switch="switch",t.exit="exit"}(Modes||(Modes={}));let SwitchUser=class extends LitElement{constructor(){super(),this.mode=Modes.switch,this.addEventListener("click",(t=>{t.preventDefault(),this.mode===Modes.switch?this.handleSwitchUser():this.mode===Modes.exit&&this.handleExitSwitchUser()}))}render(){return html`<slot></slot>`}handleSwitchUser(){this.targetUser?new AjaxRequest(TYPO3.settings.ajaxUrls.switch_user).post({targetUser:this.targetUser}).then((async t=>{const e=await t.resolve();!0===e.success&&e.url?top.window.location.href=e.url:Notification.error("Switching to user went wrong.")})):Notification.error("Switching to user went wrong.")}handleExitSwitchUser(){new AjaxRequest(TYPO3.settings.ajaxUrls.switch_user_exit).post({}).then((async t=>{const e=await t.resolve();!0===e.success&&e.url?top.window.location.href=e.url:Notification.error("Exiting current user went wrong.")}))}};__decorate([property({type:String})],SwitchUser.prototype,"targetUser",void 0),__decorate([property({type:Modes})],SwitchUser.prototype,"mode",void 0),SwitchUser=__decorate([customElement("typo3-backend-switch-user")],SwitchUser);export{SwitchUser}; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/tabs.js b/typo3/sysext/backend/Resources/Public/JavaScript/tabs.js index a7381fb203f5..1b296150c70b 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/tabs.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/tabs.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{Tab}from"bootstrap";import BrowserSession from"@typo3/backend/storage/browser-session.js";import Client from"@typo3/backend/storage/client.js";import DocumentService from"@typo3/core/document-service.js";class Tabs{static receiveActiveTab(e){return BrowserSession.get(e)||""}static storeActiveTab(e,t){BrowserSession.set(e,t)}constructor(){DocumentService.ready().then((()=>{document.querySelectorAll(".t3js-tabs").forEach((e=>{const t=Tabs.receiveActiveTab(e.id);t&&new Tab(document.querySelector('a[href="'+t+'"]')).show();"1"===e.dataset.storeLastTab&&e.addEventListener("show.bs.tab",(e=>{const t=e.currentTarget.id,r=e.target.hash;Tabs.storeActiveTab(t,r)}))}))})),Client.unsetByPrefix("tabs-")}}export default new Tabs; \ No newline at end of file +import{Tab}from"bootstrap";import BrowserSession from"@typo3/backend/storage/browser-session.js";import Client from"@typo3/backend/storage/client.js";import DocumentService from"@typo3/core/document-service.js";class Tabs{constructor(){DocumentService.ready().then((()=>{document.querySelectorAll(".t3js-tabs").forEach((e=>{const t=Tabs.receiveActiveTab(e.id);t&&new Tab(document.querySelector('a[href="'+t+'"]')).show();"1"===e.dataset.storeLastTab&&e.addEventListener("show.bs.tab",(e=>{const t=e.currentTarget.id,r=e.target.hash;Tabs.storeActiveTab(t,r)}))}))})),Client.unsetByPrefix("tabs-")}static receiveActiveTab(e){return BrowserSession.get(e)||""}static storeActiveTab(e,t){BrowserSession.set(e,t)}}export default new Tabs; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/toolbar/live-search.js b/typo3/sysext/backend/Resources/Public/JavaScript/toolbar/live-search.js index deb7a7f58b35..ee1173571afc 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/toolbar/live-search.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/toolbar/live-search.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{lll}from"@typo3/core/lit-helper.js";import Modal from"@typo3/backend/modal.js";import"@typo3/backend/element/icon-element.js";import"@typo3/backend/input/clearable.js";import"@typo3/backend/live-search/element/search-option-item.js";import"@typo3/backend/live-search/element/show-all.js";import"@typo3/backend/live-search/live-search-shortcut.js";import DocumentService from"@typo3/core/document-service.js";import RegularEvent from"@typo3/core/event/regular-event.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import BrowserSession from"@typo3/backend/storage/browser-session.js";import{componentName as resultContainerComponentName}from"@typo3/backend/live-search/element/result/result-container.js";var Identifiers;!function(e){e.toolbarItem=".t3js-topbar-button-search",e.searchOptionDropdownToggle=".t3js-search-provider-dropdown-toggle"}(Identifiers||(Identifiers={}));class LiveSearch{constructor(){this.search=async e=>{let t=null;if(""!==e.get("query").toString()){document.querySelector(resultContainerComponentName).loading=!0;const o=await new AjaxRequest(TYPO3.settings.ajaxUrls.livesearch).post(e);t=await o.raw().json()}this.updateSearchResults(t)},DocumentService.ready().then((()=>{this.registerEvents()}))}registerEvents(){new RegularEvent("click",(()=>{this.openSearchModal()})).delegateTo(document,Identifiers.toolbarItem),new RegularEvent("typo3:live-search:trigger-open",(()=>{Modal.currentModal||this.openSearchModal()})).bindTo(document)}openSearchModal(){const e=new URL(TYPO3.settings.ajaxUrls.livesearch_form,window.location.origin);e.searchParams.set("query",BrowserSession.get("livesearch-term")??"");const t=Object.entries(BrowserSession.getByPrefix("livesearch-option-")).filter((e=>"1"===e[1])).map((e=>{const t=e[0].replace("livesearch-option-",""),[o,r]=t.split("-",2);return{key:o,value:r}})),o=this.composeSearchOptions(t);for(const[t,r]of Object.entries(o))for(let o of r)e.searchParams.append(`${t}[]`,o);const r=Modal.advanced({type:Modal.types.ajax,content:e.toString(),title:lll("labels.search"),severity:SeverityEnum.notice,size:Modal.sizes.medium});r.addEventListener("typo3-modal-shown",(()=>{const e=r.querySelector("typo3-backend-live-search"),t=e.querySelector('input[type="search"]'),o=t.closest("form");new RegularEvent("submit",(e=>{e.preventDefault();const t=new FormData(o);this.search(t).then((()=>{const e=t.get("query").toString();BrowserSession.set("livesearch-term",e)}));const r=o.querySelector("[data-active-options-counter]");let n=parseInt(r.dataset.activeOptionsCounter,10);r.querySelector("output").textContent=n.toString(10),r.classList.toggle("hidden",0===n)})).bindTo(o),t.clearable({onClear:()=>{o.requestSubmit()}}),t.focus(),t.select();const n=document.querySelector("typo3-backend-live-search-result-container");new RegularEvent("live-search:item-chosen",(()=>{Modal.dismiss()})).bindTo(n),new RegularEvent("typo3:live-search:option-invoked",(e=>{const t=o.querySelector("[data-active-options-counter]");let r=parseInt(t.dataset.activeOptionsCounter,10);r=e.detail.active?r+1:r-1,t.dataset.activeOptionsCounter=r.toString(10)})).bindTo(e),new RegularEvent("hide.bs.dropdown",(()=>{o.requestSubmit()})).bindTo(r.querySelector(Identifiers.searchOptionDropdownToggle)),new DebounceEvent("input",(()=>{o.requestSubmit()})).bindTo(t),new RegularEvent("keydown",this.handleKeyDown).bindTo(t),o.requestSubmit()}))}composeSearchOptions(e){const t={};return e.forEach((e=>{void 0===t[e.key]&&(t[e.key]=[]),t[e.key].push(e.value)})),t}handleKeyDown(e){if("ArrowDown"!==e.key)return;e.preventDefault();document.querySelector("typo3-backend-live-search").querySelector("typo3-backend-live-search-result-item")?.focus()}updateSearchResults(e){document.querySelector("typo3-backend-live-search-show-all").parentElement.hidden=null===e||0===e.length;const t=document.querySelector("typo3-backend-live-search-result-container");t.results=e,t.loading=!1}}export default top.TYPO3.LiveSearch??new LiveSearch; \ No newline at end of file +import{lll}from"@typo3/core/lit-helper.js";import Modal from"@typo3/backend/modal.js";import"@typo3/backend/element/icon-element.js";import"@typo3/backend/input/clearable.js";import"@typo3/backend/live-search/element/search-option-item.js";import"@typo3/backend/live-search/element/show-all.js";import"@typo3/backend/live-search/live-search-shortcut.js";import DocumentService from"@typo3/core/document-service.js";import RegularEvent from"@typo3/core/event/regular-event.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import BrowserSession from"@typo3/backend/storage/browser-session.js";import{componentName as resultContainerComponentName}from"@typo3/backend/live-search/element/result/result-container.js";var Identifiers;!function(e){e.toolbarItem=".t3js-topbar-button-search",e.searchOptionDropdownToggle=".t3js-search-provider-dropdown-toggle"}(Identifiers||(Identifiers={}));class LiveSearch{constructor(){this.search=async e=>{let t=null;if(""!==e.get("query").toString()){document.querySelector(resultContainerComponentName).loading=!0;const o=await new AjaxRequest(TYPO3.settings.ajaxUrls.livesearch).post(e);t=await o.raw().json()}this.updateSearchResults(t)},DocumentService.ready().then((()=>{this.registerEvents()}))}registerEvents(){new RegularEvent("click",(()=>{this.openSearchModal()})).delegateTo(document,Identifiers.toolbarItem),new RegularEvent("typo3:live-search:trigger-open",(()=>{Modal.currentModal||this.openSearchModal()})).bindTo(document)}openSearchModal(){const e=new URL(TYPO3.settings.ajaxUrls.livesearch_form,window.location.origin);e.searchParams.set("query",BrowserSession.get("livesearch-term")??"");const t=Object.entries(BrowserSession.getByPrefix("livesearch-option-")).filter((e=>"1"===e[1])).map((e=>{const t=e[0].replace("livesearch-option-",""),[o,r]=t.split("-",2);return{key:o,value:r}})),o=this.composeSearchOptions(t);for(const[t,r]of Object.entries(o))for(const o of r)e.searchParams.append(`${t}[]`,o);const r=Modal.advanced({type:Modal.types.ajax,content:e.toString(),title:lll("labels.search"),severity:SeverityEnum.notice,size:Modal.sizes.medium});r.addEventListener("typo3-modal-shown",(()=>{const e=r.querySelector("typo3-backend-live-search"),t=e.querySelector('input[type="search"]'),o=t.closest("form");new RegularEvent("submit",(e=>{e.preventDefault();const t=new FormData(o);this.search(t).then((()=>{const e=t.get("query").toString();BrowserSession.set("livesearch-term",e)}));const r=o.querySelector("[data-active-options-counter]"),n=parseInt(r.dataset.activeOptionsCounter,10);r.querySelector("output").textContent=n.toString(10),r.classList.toggle("hidden",0===n)})).bindTo(o),t.clearable({onClear:()=>{o.requestSubmit()}}),t.focus(),t.select();const n=document.querySelector("typo3-backend-live-search-result-container");new RegularEvent("live-search:item-chosen",(()=>{Modal.dismiss()})).bindTo(n),new RegularEvent("typo3:live-search:option-invoked",(e=>{const t=o.querySelector("[data-active-options-counter]");let r=parseInt(t.dataset.activeOptionsCounter,10);r=e.detail.active?r+1:r-1,t.dataset.activeOptionsCounter=r.toString(10)})).bindTo(e),new RegularEvent("hide.bs.dropdown",(()=>{o.requestSubmit()})).bindTo(r.querySelector(Identifiers.searchOptionDropdownToggle)),new DebounceEvent("input",(()=>{o.requestSubmit()})).bindTo(t),new RegularEvent("keydown",this.handleKeyDown).bindTo(t),o.requestSubmit()}))}composeSearchOptions(e){const t={};return e.forEach((e=>{void 0===t[e.key]&&(t[e.key]=[]),t[e.key].push(e.value)})),t}handleKeyDown(e){if("ArrowDown"!==e.key)return;e.preventDefault();document.querySelector("typo3-backend-live-search").querySelector("typo3-backend-live-search-result-item")?.focus()}updateSearchResults(e){document.querySelector("typo3-backend-live-search-show-all").parentElement.hidden=null===e||0===e.length;const t=document.querySelector("typo3-backend-live-search-result-container");t.results=e,t.loading=!1}}export default top.TYPO3.LiveSearch??new LiveSearch; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/tooltip.js b/typo3/sysext/backend/Resources/Public/JavaScript/tooltip.js index d04201d81b28..190995419291 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/tooltip.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/tooltip.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{Tooltip as BootstrapTooltip}from"bootstrap";import DocumentService from"@typo3/core/document-service.js";class Tooltip{static applyAttributes(t,o){for(const[e,i]of Object.entries(t))o.setAttribute(e,i)}constructor(){DocumentService.ready().then((()=>{console.warn("Tooltip has been deprecated since TYPO3 v12 and will be removed with v13. Rely on browser title instead."),this.initialize('[data-bs-toggle="tooltip"]')}))}initialize(t,o={}){0===Object.entries(o).length&&(o={container:"body",trigger:"hover",delay:{show:500,hide:100}});const e=document.querySelectorAll(t);for(const t of e)BootstrapTooltip.getOrCreateInstance(t,o)}show(t,o){const e={"data-bs-placement":"auto",title:o};if(t instanceof NodeList)for(const o of t)Tooltip.applyAttributes(e,o),BootstrapTooltip.getInstance(o).show();else if(t instanceof HTMLElement)return Tooltip.applyAttributes(e,t),void BootstrapTooltip.getInstance(t).show()}hide(t){if(t instanceof NodeList)for(const o of t){const t=BootstrapTooltip.getInstance(o);null!==t&&t.hide()}else t instanceof HTMLElement&&BootstrapTooltip.getInstance(t).hide()}}const tooltipObject=new Tooltip;TYPO3.Tooltip=tooltipObject;export default tooltipObject; \ No newline at end of file +import{Tooltip as BootstrapTooltip}from"bootstrap";import DocumentService from"@typo3/core/document-service.js";class Tooltip{constructor(){DocumentService.ready().then((()=>{console.warn("Tooltip has been deprecated since TYPO3 v12 and will be removed with v13. Rely on browser title instead."),this.initialize('[data-bs-toggle="tooltip"]')}))}static applyAttributes(t,o){for(const[e,i]of Object.entries(t))o.setAttribute(e,i)}initialize(t,o={}){0===Object.entries(o).length&&(o={container:"body",trigger:"hover",delay:{show:500,hide:100}});const e=document.querySelectorAll(t);for(const t of e)BootstrapTooltip.getOrCreateInstance(t,o)}show(t,o){const e={"data-bs-placement":"auto",title:o};if(t instanceof NodeList)for(const o of t)Tooltip.applyAttributes(e,o),BootstrapTooltip.getInstance(o).show();else if(t instanceof HTMLElement)return Tooltip.applyAttributes(e,t),void BootstrapTooltip.getInstance(t).show()}hide(t){if(t instanceof NodeList)for(const o of t){const t=BootstrapTooltip.getInstance(o);null!==t&&t.hide()}else t instanceof HTMLElement&&BootstrapTooltip.getInstance(t).hide()}}const tooltipObject=new Tooltip;TYPO3.Tooltip=tooltipObject;export default tooltipObject; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/tree/drag-drop.js b/typo3/sysext/backend/Resources/Public/JavaScript/tree/drag-drop.js index 466de20bd562..e630b283edbf 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/tree/drag-drop.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/tree/drag-drop.js @@ -20,4 +20,4 @@ import{html}from"lit";import{renderNodes}from"@typo3/core/lit-helper.js";import* </span> <span class="node-dd__name">${t}</span> </div> - </div>`}}export var DraggablePositionEnum;!function(e){e.INSIDE="inside",e.BEFORE="before",e.AFTER="after"}(DraggablePositionEnum||(DraggablePositionEnum={}));export class DragDrop{constructor(e){this.timeout={},this.minimalDistance=10,this.tree=e}static setDragStart(){document.querySelectorAll("iframe").forEach((e=>e.style.pointerEvents="none"))}static setDragEnd(){document.querySelectorAll("iframe").forEach((e=>e.style.pointerEvents=""))}connectDragHandler(e){return d3drag.drag().filter((e=>e instanceof MouseEvent)).clickDistance(5).on("start",(function(t){e.onDragStart(t.sourceEvent,t.subject)&&DragDrop.setDragStart()})).on("drag",(function(t){e.onDragOver(t.sourceEvent,t.subject)})).on("end",(function(t){DragDrop.setDragEnd(),e.onDrop(t.sourceEvent,t.subject)}))}createDraggable(e,t){let r=this.tree.svg.node();const o=renderNodes(DraggableTemplate.get(e,t));r.after(...o),this.tree.svg.node().querySelector(".nodes-wrapper")?.classList.add("nodes-wrapper--dragging")}createDraggableFromExistingNode(e){this.createDraggable(this.tree.getIconId(e),e.name);this.tree.svg.node().querySelector('.node-bg[data-state-id="'+e.stateIdentifier+'"]')?.classList.add("node-bg--dragging")}getDraggable(){return this.tree.svg.node().parentNode.querySelector(".node-dd")||null}updateDraggablePosition(e){let t=18,r=15;e&&e.pageX&&(t+=e.pageX),e&&e.pageY&&(r+=e.pageY),document.querySelectorAll(".node-dd").forEach((e=>{e.style.top=r+"px",e.style.left=t+"px",e.style.display="block"}))}openNodeTimeout(){null!==this.tree.hoveredNode&&this.tree.hoveredNode.hasChildren&&!this.tree.hoveredNode.expanded?this.timeout.node!=this.tree.hoveredNode&&(this.timeout.node=this.tree.hoveredNode,clearTimeout(this.timeout.time),this.timeout.time=setTimeout((()=>{this.tree.hoveredNode&&(this.tree.showChildren(this.tree.hoveredNode),this.tree.prepareDataForVisibleNodes(),this.tree.updateVisibleNodes())}),1e3)):clearTimeout(this.timeout.time)}addNodeDdClass(e){const t=this.tree.svg.node().querySelector(".nodes-wrapper"),r=this.getDraggable();r&&this.applyNodeClassNames(r,"node-dd--",e),t&&this.applyNodeClassNames(t,"nodes-wrapper--",e)}cleanupDrop(){this.tree.svg.node().querySelector(".nodes-wrapper").classList.remove("nodes-wrapper--nodrop","nodes-wrapper--ok-append","nodes-wrapper--ok-below","nodes-wrapper--ok-between","nodes-wrapper--ok-above","nodes-wrapper--dragging"),this.tree.nodesBgContainer.node().querySelector(".node-bg.node-bg--dragging")?.classList.remove("node-bg--dragging"),this.hidePositioningLine(),this.tree.svg.node().parentNode.querySelector(".node-dd").remove()}createPositioningLine(){this.tree.nodesBgContainer.selectAll(".node-bg__border").empty()&&this.tree.nodesBgContainer.append("rect").attr("class","node-bg__border").attr("height","1px").attr("width","100%")}updatePositioningLine(e){this.tree.nodesBgContainer.selectAll(".node-bg__border").attr("transform","translate("+this.tree.settings.indentWidth/2*-1+", "+(e.y-this.tree.settings.nodeHeight/2)+")").style("display","block")}hidePositioningLine(){this.tree.nodesBgContainer.selectAll(".node-bg__border").style("display","none")}isTheSameNode(e,t){return e&&-1!==e.parentsStateIdentifier.indexOf(t.stateIdentifier)}isDragNodeDistanceMore(e,t){return t.dragStarted||t.startPageX-this.minimalDistance>e.pageX||t.startPageX+this.minimalDistance<e.pageX||t.startPageY-this.minimalDistance>e.pageY||t.startPageY+this.minimalDistance<e.pageY}applyNodeClassNames(e,t,r){const o=["nodrop","ok-append","ok-below","ok-between","ok-above"].filter((e=>e!==r)).map((e=>t+e));e.classList.remove(...o),e.classList.contains(t+r)||e.classList.add(t+r)}} \ No newline at end of file + </div>`}}export var DraggablePositionEnum;!function(e){e.INSIDE="inside",e.BEFORE="before",e.AFTER="after"}(DraggablePositionEnum||(DraggablePositionEnum={}));export class DragDrop{constructor(e){this.timeout={},this.minimalDistance=10,this.tree=e}static setDragStart(){document.querySelectorAll("iframe").forEach((e=>e.style.pointerEvents="none"))}static setDragEnd(){document.querySelectorAll("iframe").forEach((e=>e.style.pointerEvents=""))}connectDragHandler(e){return d3drag.drag().filter((e=>e instanceof MouseEvent)).clickDistance(5).on("start",(function(t){e.onDragStart(t.sourceEvent,t.subject)&&DragDrop.setDragStart()})).on("drag",(function(t){e.onDragOver(t.sourceEvent,t.subject)})).on("end",(function(t){DragDrop.setDragEnd(),e.onDrop(t.sourceEvent,t.subject)}))}createDraggable(e,t){const r=this.tree.svg.node(),o=renderNodes(DraggableTemplate.get(e,t));r.after(...o),this.tree.svg.node().querySelector(".nodes-wrapper")?.classList.add("nodes-wrapper--dragging")}createDraggableFromExistingNode(e){this.createDraggable(this.tree.getIconId(e),e.name);this.tree.svg.node().querySelector('.node-bg[data-state-id="'+e.stateIdentifier+'"]')?.classList.add("node-bg--dragging")}getDraggable(){return this.tree.svg.node().parentNode.querySelector(".node-dd")||null}updateDraggablePosition(e){let t=18,r=15;e&&e.pageX&&(t+=e.pageX),e&&e.pageY&&(r+=e.pageY),document.querySelectorAll(".node-dd").forEach((e=>{e.style.top=r+"px",e.style.left=t+"px",e.style.display="block"}))}openNodeTimeout(){null!==this.tree.hoveredNode&&this.tree.hoveredNode.hasChildren&&!this.tree.hoveredNode.expanded?this.timeout.node!=this.tree.hoveredNode&&(this.timeout.node=this.tree.hoveredNode,clearTimeout(this.timeout.time),this.timeout.time=setTimeout((()=>{this.tree.hoveredNode&&(this.tree.showChildren(this.tree.hoveredNode),this.tree.prepareDataForVisibleNodes(),this.tree.updateVisibleNodes())}),1e3)):clearTimeout(this.timeout.time)}addNodeDdClass(e){const t=this.tree.svg.node().querySelector(".nodes-wrapper"),r=this.getDraggable();r&&this.applyNodeClassNames(r,"node-dd--",e),t&&this.applyNodeClassNames(t,"nodes-wrapper--",e)}cleanupDrop(){this.tree.svg.node().querySelector(".nodes-wrapper").classList.remove("nodes-wrapper--nodrop","nodes-wrapper--ok-append","nodes-wrapper--ok-below","nodes-wrapper--ok-between","nodes-wrapper--ok-above","nodes-wrapper--dragging"),this.tree.nodesBgContainer.node().querySelector(".node-bg.node-bg--dragging")?.classList.remove("node-bg--dragging"),this.hidePositioningLine(),this.tree.svg.node().parentNode.querySelector(".node-dd").remove()}createPositioningLine(){this.tree.nodesBgContainer.selectAll(".node-bg__border").empty()&&this.tree.nodesBgContainer.append("rect").attr("class","node-bg__border").attr("height","1px").attr("width","100%")}updatePositioningLine(e){this.tree.nodesBgContainer.selectAll(".node-bg__border").attr("transform","translate("+this.tree.settings.indentWidth/2*-1+", "+(e.y-this.tree.settings.nodeHeight/2)+")").style("display","block")}hidePositioningLine(){this.tree.nodesBgContainer.selectAll(".node-bg__border").style("display","none")}isTheSameNode(e,t){return e&&-1!==e.parentsStateIdentifier.indexOf(t.stateIdentifier)}isDragNodeDistanceMore(e,t){return t.dragStarted||t.startPageX-this.minimalDistance>e.pageX||t.startPageX+this.minimalDistance<e.pageX||t.startPageY-this.minimalDistance>e.pageY||t.startPageY+this.minimalDistance<e.pageY}applyNodeClassNames(e,t,r){const o=["nodrop","ok-append","ok-below","ok-between","ok-above"].filter((e=>e!==r)).map((e=>t+e));e.classList.remove(...o),e.classList.contains(t+r)||e.classList.add(t+r)}} \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/tree/file-storage-browser.js b/typo3/sysext/backend/Resources/Public/JavaScript/tree/file-storage-browser.js index ffdf27b44067..a9d47450b8a8 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/tree/file-storage-browser.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/tree/file-storage-browser.js @@ -10,7 +10,7 @@ * * The TYPO3 project - inspiring people to share! */ -var __decorate=function(e,t,r,o){var i,n=arguments.length,s=n<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,r):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,o);else for(var a=e.length-1;a>=0;a--)(i=e[a])&&(s=(n<3?i(s):n>3?i(t,r,s):i(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s};import{html,LitElement}from"lit";import{customElement,query}from"lit/decorators.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import ElementBrowser from"@typo3/backend/element-browser.js";import LinkBrowser from"@typo3/backend/link-browser.js";import"@typo3/backend/element/icon-element.js";import Persistent from"@typo3/backend/storage/persistent.js";import{FileStorageTree}from"@typo3/backend/tree/file-storage-tree.js";const componentName="typo3-backend-component-filestorage-browser";let FileStorageBrowserTree=class extends FileStorageTree{updateNodeActions(e){const t=super.updateNodeActions(e);if(this.settings.actions.includes("link")){const e=t.append("g").on("click",((e,t)=>{this.linkItem(t)}));this.createIconAreaForAction(e,"actions-link")}else if(this.settings.actions.includes("select")){const e=t.append("g").on("click",((e,t)=>{this.selectItem(t)}));this.createIconAreaForAction(e,"actions-link")}return t}linkItem(e){LinkBrowser.finalizeFunction("t3://folder?storage="+e.storage+"&identifier="+e.pathIdentifier)}selectItem(e){ElementBrowser.insertElement(e.itemType,e.identifier,e.name,e.identifier,!0)}};FileStorageBrowserTree=__decorate([customElement("typo3-backend-component-filestorage-browser-tree")],FileStorageBrowserTree);let FileStorageBrowser=class extends LitElement{constructor(){super(...arguments),this.activeFolder="",this.actions=[],this.triggerRender=()=>{this.tree.dispatchEvent(new Event("svg-tree:visible"))},this.selectActiveNode=e=>{let t=e.detail.nodes;e.detail.nodes=t.map((e=>(decodeURIComponent(e.identifier)===this.activeFolder&&(e.checked=!0),e)))},this.toggleExpandState=e=>{const t=e.detail.node;t&&Persistent.set("BackendComponents.States.FileStorageTree.stateHash."+t.stateIdentifier,t.expanded?"1":"0")},this.loadFolderDetails=e=>{const t=e.detail.node;if(!t.checked)return;let r=document.location.href+"&contentOnly=1&expandFolder="+t.identifier;new AjaxRequest(r).get().then((e=>e.resolve())).then((e=>{document.querySelector(".element-browser-main-content .element-browser-body").innerHTML=e}))}}connectedCallback(){super.connectedCallback(),document.addEventListener("typo3:navigation:resized",this.triggerRender)}disconnectedCallback(){document.removeEventListener("typo3:navigation:resized",this.triggerRender),super.disconnectedCallback()}firstUpdated(){this.activeFolder=this.getAttribute("active-folder")||""}createRenderRoot(){return this}render(){this.hasAttribute("tree-actions")&&this.getAttribute("tree-actions").length&&(this.actions=JSON.parse(this.getAttribute("tree-actions")));const e={dataUrl:top.TYPO3.settings.ajaxUrls.filestorage_tree_data,filterUrl:top.TYPO3.settings.ajaxUrls.filestorage_tree_filter,showIcons:!0,actions:this.actions};return html` +var __decorate=function(e,t,r,o){var i,n=arguments.length,s=n<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,r):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,o);else for(var a=e.length-1;a>=0;a--)(i=e[a])&&(s=(n<3?i(s):n>3?i(t,r,s):i(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s};import{html,LitElement}from"lit";import{customElement,query}from"lit/decorators.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import ElementBrowser from"@typo3/backend/element-browser.js";import LinkBrowser from"@typo3/backend/link-browser.js";import"@typo3/backend/element/icon-element.js";import Persistent from"@typo3/backend/storage/persistent.js";import{FileStorageTree}from"@typo3/backend/tree/file-storage-tree.js";const componentName="typo3-backend-component-filestorage-browser";let FileStorageBrowserTree=class extends FileStorageTree{updateNodeActions(e){const t=super.updateNodeActions(e);if(this.settings.actions.includes("link")){const e=t.append("g").on("click",((e,t)=>{this.linkItem(t)}));this.createIconAreaForAction(e,"actions-link")}else if(this.settings.actions.includes("select")){const e=t.append("g").on("click",((e,t)=>{this.selectItem(t)}));this.createIconAreaForAction(e,"actions-link")}return t}linkItem(e){LinkBrowser.finalizeFunction("t3://folder?storage="+e.storage+"&identifier="+e.pathIdentifier)}selectItem(e){ElementBrowser.insertElement(e.itemType,e.identifier,e.name,e.identifier,!0)}};FileStorageBrowserTree=__decorate([customElement("typo3-backend-component-filestorage-browser-tree")],FileStorageBrowserTree);let FileStorageBrowser=class extends LitElement{constructor(){super(...arguments),this.activeFolder="",this.actions=[],this.triggerRender=()=>{this.tree.dispatchEvent(new Event("svg-tree:visible"))},this.selectActiveNode=e=>{const t=e.detail.nodes;e.detail.nodes=t.map((e=>(decodeURIComponent(e.identifier)===this.activeFolder&&(e.checked=!0),e)))},this.toggleExpandState=e=>{const t=e.detail.node;t&&Persistent.set("BackendComponents.States.FileStorageTree.stateHash."+t.stateIdentifier,t.expanded?"1":"0")},this.loadFolderDetails=e=>{const t=e.detail.node;if(!t.checked)return;const r=document.location.href+"&contentOnly=1&expandFolder="+t.identifier;new AjaxRequest(r).get().then((e=>e.resolve())).then((e=>{document.querySelector(".element-browser-main-content .element-browser-body").innerHTML=e}))}}connectedCallback(){super.connectedCallback(),document.addEventListener("typo3:navigation:resized",this.triggerRender)}disconnectedCallback(){document.removeEventListener("typo3:navigation:resized",this.triggerRender),super.disconnectedCallback()}firstUpdated(){this.activeFolder=this.getAttribute("active-folder")||""}createRenderRoot(){return this}render(){this.hasAttribute("tree-actions")&&this.getAttribute("tree-actions").length&&(this.actions=JSON.parse(this.getAttribute("tree-actions")));const e={dataUrl:top.TYPO3.settings.ajaxUrls.filestorage_tree_data,filterUrl:top.TYPO3.settings.ajaxUrls.filestorage_tree_filter,showIcons:!0,actions:this.actions};return html` <div class="svg-tree"> <div> <typo3-backend-tree-toolbar .tree="${this.tree}" class="svg-toolbar"></typo3-backend-tree-toolbar> diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/tree/file-storage-tree-container.js b/typo3/sysext/backend/Resources/Public/JavaScript/tree/file-storage-tree-container.js index f21659d81727..313c1c497fd1 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/tree/file-storage-tree-container.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/tree/file-storage-tree-container.js @@ -10,7 +10,7 @@ * * The TYPO3 project - inspiring people to share! */ -var __decorate=function(e,t,r,o){var i,n=arguments.length,s=n<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,r):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,o);else for(var a=e.length-1;a>=0;a--)(i=e[a])&&(s=(n<3?i(s):n>3?i(t,r,s):i(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s};import{html,LitElement}from"lit";import{customElement,query}from"lit/decorators.js";import"@typo3/backend/element/icon-element.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import{FileStorageTree}from"@typo3/backend/tree/file-storage-tree.js";import{DragDrop,DraggablePositionEnum}from"@typo3/backend/tree/drag-drop.js";import ContextMenu from"@typo3/backend/context-menu.js";import Notification from"@typo3/backend/notification.js";import Persistent from"@typo3/backend/storage/persistent.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";import{ModuleUtility}from"@typo3/backend/module.js";import{FileListDragDropEvent}from"@typo3/filelist/file-list-dragdrop.js";import{Resource}from"@typo3/backend/resource/resource.js";export const navigationComponentName="typo3-backend-navigation-component-filestoragetree";let EditableFileStorageTree=class extends FileStorageTree{constructor(){super(),this.handleDragOver=e=>{const t=e.target,r=this.getNodeFromElement(t);r&&(this.hoveredNode&&r.stateIdentifier!==this.hoveredNode.stateIdentifier&&this.onMouseOutOfNode(this.hoveredNode),r.isOver||this.onMouseOverNode(r)),e.preventDefault()},this.handleDrop=e=>{const t=e.target.closest("[data-state-id]"),r=this.getNodeFromElement(t);if(r){const t=FileResource.fromTreeNode(r),o=FileOperationCollection.fromDataTransfer(e.dataTransfer,t),i=o.getConflictingOperationsForTreeNode(r);if(i.length>0)return void i.forEach((e=>{Notification.showMessage(TYPO3.lang["drop.conflict"],TYPO3.lang["mess.drop.conflict"].replace("%s",e.resource.name).replace("%s",decodeURIComponent(r.identifier)),SeverityEnum.error)}));this.actionHandler.initiateDropAction(o)}e.preventDefault()},this.actionHandler=new FileStorageTreeActions(this)}connectedCallback(){super.connectedCallback(),document.addEventListener("dragover",this.handleDragOver),document.addEventListener("drop",this.handleDrop)}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener("dragover",this.handleDragOver),document.removeEventListener("drop",this.handleDrop)}updateNodeBgClass(e){return super.updateNodeBgClass.call(this,e).call(this.initializeDragForNode())}nodesUpdate(e){return super.nodesUpdate.call(this,e).call(this.initializeDragForNode())}initializeDragForNode(){return this.actionHandler.connectDragHandler(new FileStorageTreeNodeDragHandler(this,this.actionHandler))}};EditableFileStorageTree=__decorate([customElement("typo3-backend-navigation-component-filestorage-tree")],EditableFileStorageTree);let FileStorageTreeNavigationComponent=class extends LitElement{constructor(){super(...arguments),this.refresh=()=>{this.tree.refreshOrFilterTree()},this.selectFirstNode=()=>{const e=this.tree.nodes[0];e&&this.tree.selectNode(e,!0)},this.treeUpdateRequested=e=>{const t=encodeURIComponent(e.detail.payload.identifier);let r=this.tree.nodes.filter((e=>e.identifier===t))[0];r&&0===this.tree.getSelectedNodes().filter((e=>e.identifier===r.identifier)).length&&this.tree.selectNode(r,!1)},this.toggleExpandState=e=>{const t=e.detail.node;t&&Persistent.set("BackendComponents.States.FileStorageTree.stateHash."+t.stateIdentifier,t.expanded?"1":"0")},this.loadContent=e=>{const t=e.detail.node;if(!t?.checked)return;if(ModuleStateStorage.update("file",t.identifier,!0),!1===e.detail.propagate)return;const r=top.TYPO3.ModuleMenu.App;let o=ModuleUtility.getFromName(r.getCurrentModule()).link;o+=o.includes("?")?"&":"?",top.TYPO3.Backend.ContentContainer.setUrl(o+"id="+t.identifier)},this.showContextMenu=e=>{const t=e.detail.node;t&&ContextMenu.show(t.itemType,decodeURIComponent(t.identifier),"tree","","",this.tree.getElementFromNode(t))},this.selectActiveNode=e=>{const t=ModuleStateStorage.current("file").selection;let r=e.detail.nodes;e.detail.nodes=r.map((e=>(e.identifier===t&&(e.checked=!0),e)))}}connectedCallback(){super.connectedCallback(),document.addEventListener("typo3:filestoragetree:refresh",this.refresh),document.addEventListener("typo3:filestoragetree:selectFirstNode",this.selectFirstNode),document.addEventListener("typo3:filelist:treeUpdateRequested",this.treeUpdateRequested)}disconnectedCallback(){document.removeEventListener("typo3:filestoragetree:refresh",this.refresh),document.removeEventListener("typo3:filestoragetree:selectFirstNode",this.selectFirstNode),document.removeEventListener("typo3:filelist:treeUpdateRequested",this.treeUpdateRequested),super.disconnectedCallback()}createRenderRoot(){return this}render(){const e={dataUrl:top.TYPO3.settings.ajaxUrls.filestorage_tree_data,filterUrl:top.TYPO3.settings.ajaxUrls.filestorage_tree_filter,showIcons:!0};return html` +var __decorate=function(e,t,r,o){var i,n=arguments.length,s=n<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,r):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,r,o);else for(var a=e.length-1;a>=0;a--)(i=e[a])&&(s=(n<3?i(s):n>3?i(t,r,s):i(t,r))||s);return n>3&&s&&Object.defineProperty(t,r,s),s};import{html,LitElement}from"lit";import{customElement,query}from"lit/decorators.js";import"@typo3/backend/element/icon-element.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import{FileStorageTree}from"@typo3/backend/tree/file-storage-tree.js";import{DragDrop,DraggablePositionEnum}from"@typo3/backend/tree/drag-drop.js";import ContextMenu from"@typo3/backend/context-menu.js";import Notification from"@typo3/backend/notification.js";import Persistent from"@typo3/backend/storage/persistent.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";import{ModuleUtility}from"@typo3/backend/module.js";import{FileListDragDropEvent}from"@typo3/filelist/file-list-dragdrop.js";import{Resource}from"@typo3/backend/resource/resource.js";export const navigationComponentName="typo3-backend-navigation-component-filestoragetree";let EditableFileStorageTree=class extends FileStorageTree{constructor(){super(),this.handleDragOver=e=>{const t=e.target,r=this.getNodeFromElement(t);r&&(this.hoveredNode&&r.stateIdentifier!==this.hoveredNode.stateIdentifier&&this.onMouseOutOfNode(this.hoveredNode),r.isOver||this.onMouseOverNode(r)),e.preventDefault()},this.handleDrop=e=>{const t=e.target.closest("[data-state-id]"),r=this.getNodeFromElement(t);if(r){const t=FileResource.fromTreeNode(r),o=FileOperationCollection.fromDataTransfer(e.dataTransfer,t),i=o.getConflictingOperationsForTreeNode(r);if(i.length>0)return void i.forEach((e=>{Notification.showMessage(TYPO3.lang["drop.conflict"],TYPO3.lang["mess.drop.conflict"].replace("%s",e.resource.name).replace("%s",decodeURIComponent(r.identifier)),SeverityEnum.error)}));this.actionHandler.initiateDropAction(o)}e.preventDefault()},this.actionHandler=new FileStorageTreeActions(this)}connectedCallback(){super.connectedCallback(),document.addEventListener("dragover",this.handleDragOver),document.addEventListener("drop",this.handleDrop)}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener("dragover",this.handleDragOver),document.removeEventListener("drop",this.handleDrop)}updateNodeBgClass(e){return super.updateNodeBgClass.call(this,e).call(this.initializeDragForNode())}nodesUpdate(e){return super.nodesUpdate.call(this,e).call(this.initializeDragForNode())}initializeDragForNode(){return this.actionHandler.connectDragHandler(new FileStorageTreeNodeDragHandler(this,this.actionHandler))}};EditableFileStorageTree=__decorate([customElement("typo3-backend-navigation-component-filestorage-tree")],EditableFileStorageTree);let FileStorageTreeNavigationComponent=class extends LitElement{constructor(){super(...arguments),this.refresh=()=>{this.tree.refreshOrFilterTree()},this.selectFirstNode=()=>{const e=this.tree.nodes[0];e&&this.tree.selectNode(e,!0)},this.treeUpdateRequested=e=>{const t=encodeURIComponent(e.detail.payload.identifier),r=this.tree.nodes.filter((e=>e.identifier===t))[0];r&&0===this.tree.getSelectedNodes().filter((e=>e.identifier===r.identifier)).length&&this.tree.selectNode(r,!1)},this.toggleExpandState=e=>{const t=e.detail.node;t&&Persistent.set("BackendComponents.States.FileStorageTree.stateHash."+t.stateIdentifier,t.expanded?"1":"0")},this.loadContent=e=>{const t=e.detail.node;if(!t?.checked)return;if(ModuleStateStorage.update("file",t.identifier,!0),!1===e.detail.propagate)return;const r=top.TYPO3.ModuleMenu.App;let o=ModuleUtility.getFromName(r.getCurrentModule()).link;o+=o.includes("?")?"&":"?",top.TYPO3.Backend.ContentContainer.setUrl(o+"id="+t.identifier)},this.showContextMenu=e=>{const t=e.detail.node;t&&ContextMenu.show(t.itemType,decodeURIComponent(t.identifier),"tree","","",this.tree.getElementFromNode(t))},this.selectActiveNode=e=>{const t=ModuleStateStorage.current("file").selection,r=e.detail.nodes;e.detail.nodes=r.map((e=>(e.identifier===t&&(e.checked=!0),e)))}}connectedCallback(){super.connectedCallback(),document.addEventListener("typo3:filestoragetree:refresh",this.refresh),document.addEventListener("typo3:filestoragetree:selectFirstNode",this.selectFirstNode),document.addEventListener("typo3:filelist:treeUpdateRequested",this.treeUpdateRequested)}disconnectedCallback(){document.removeEventListener("typo3:filestoragetree:refresh",this.refresh),document.removeEventListener("typo3:filestoragetree:selectFirstNode",this.selectFirstNode),document.removeEventListener("typo3:filelist:treeUpdateRequested",this.treeUpdateRequested),super.disconnectedCallback()}createRenderRoot(){return this}render(){const e={dataUrl:top.TYPO3.settings.ajaxUrls.filestorage_tree_data,filterUrl:top.TYPO3.settings.ajaxUrls.filestorage_tree_filter,showIcons:!0};return html` <div id="typo3-filestoragetree" class="svg-tree"> <div> <typo3-backend-tree-toolbar .tree="${this.tree}" id="filestoragetree-toolbar" class="svg-toolbar"></typo3-backend-tree-toolbar> @@ -22,4 +22,4 @@ var __decorate=function(e,t,r,o){var i,n=arguments.length,s=n<3?t:null===o?o=Obj <typo3-backend-icon identifier="spinner-circle-light" size="large"></typo3-backend-icon> </div> </div> - `}firstUpdated(){this.toolbar.tree=this.tree,this.tree.addEventListener("typo3:svg-tree:expand-toggle",this.toggleExpandState),this.tree.addEventListener("typo3:svg-tree:node-selected",this.loadContent),this.tree.addEventListener("typo3:svg-tree:node-context",this.showContextMenu),this.tree.addEventListener("typo3:svg-tree:nodes-prepared",this.selectActiveNode)}};__decorate([query(".svg-tree-wrapper")],FileStorageTreeNavigationComponent.prototype,"tree",void 0),__decorate([query("typo3-backend-tree-toolbar")],FileStorageTreeNavigationComponent.prototype,"toolbar",void 0),FileStorageTreeNavigationComponent=__decorate([customElement(navigationComponentName)],FileStorageTreeNavigationComponent);export{FileStorageTreeNavigationComponent};class FileStorageTreeActions extends DragDrop{isInSameParentNode(e,t){return e.stateIdentifier==t.stateIdentifier||e.parentsStateIdentifier[0]==t.stateIdentifier||t.parentsStateIdentifier.includes(e.stateIdentifier)}getDropCommandDetails(e,t){const r=this.tree.nodes,o=t.identifier;let i=this.tree.settings.nodeDragPosition,n=e||t;if(o===n.identifier)return null;if(i===DraggablePositionEnum.BEFORE){const t=r.indexOf(e),o=this.setNodePositionAndTarget(t);if(null===o)return null;i=o.position,n=o.target}return{node:t,identifier:o,target:n,position:i}}setNodePositionAndTarget(e){const t=this.tree.nodes,r=t[e].depth;e>0&&e--;const o=t[e].depth,i=this.tree.nodes[e];if(o===r)return{position:DraggablePositionEnum.AFTER,target:i};if(o<r)return{position:DraggablePositionEnum.INSIDE,target:i};for(let o=e;o>=0;o--){if(t[o].depth===r)return{position:DraggablePositionEnum.AFTER,target:this.tree.nodes[o]};if(t[o].depth<r)return{position:DraggablePositionEnum.AFTER,target:t[o]}}return null}updateStateOfHoveredNode(e){this.tree.settings.nodeDragPosition=!1,this.tree.hoveredNode&&this.tree.isOverSvg?e.isOver||this.isTheSameNode(this.tree.hoveredNode,e)||this.isInSameParentNode(e,this.tree.hoveredNode)?this.addNodeDdClass("nodrop"):(this.addNodeDdClass("ok-append"),this.tree.settings.nodeDragPosition=DraggablePositionEnum.INSIDE):this.addNodeDdClass("nodrop")}isDropAllowed(e,t){return!t.isOver&&(!this.isTheSameNode(e,t)&&!!this.tree.isOverSvg)}initiateDropAction(e){const t={action:"transfer",resources:e.getResources(),target:e.target};top.document.dispatchEvent(new CustomEvent(FileListDragDropEvent.transfer,{detail:t}))}}class FileStorageTreeNodeDragHandler{constructor(e,t){this.dragStarted=!1,this.startPageX=0,this.startPageY=0,this.tree=e,this.actionHandler=t}onDragStart(e,t){return 0!==t.depth&&(this.startPageX=e.pageX,this.startPageY=e.pageY,this.dragStarted=!1,!0)}onDragOver(e,t){return!!this.actionHandler.isDragNodeDistanceMore(e,this)&&(this.dragStarted=!0,0!==t.depth&&(this.actionHandler.getDraggable()||this.actionHandler.createDraggableFromExistingNode(t),this.actionHandler.openNodeTimeout(),this.actionHandler.updateDraggablePosition(e),this.actionHandler.updateStateOfHoveredNode(t),!0))}onDrop(e,t){if(!this.dragStarted||0===t.depth)return!1;if(this.actionHandler.cleanupDrop(),this.actionHandler.isDropAllowed(this.tree.hoveredNode,t)){let e=this.actionHandler.getDropCommandDetails(this.tree.hoveredNode,t);if(null===e)return!1;const r=FileOperationCollection.fromNodePositionOptions(e),o=r.getConflictingOperationsForTreeNode(e.target);if(o.length>0)return o.forEach((t=>{Notification.showMessage(TYPO3.lang["drop.conflict"],TYPO3.lang["mess.drop.conflict"].replace("%s",t.resource.name).replace("%s",decodeURIComponent(e.target.identifier)),SeverityEnum.error)})),!1;this.actionHandler.initiateDropAction(r)}return!0}}class FileOperation{constructor(e,t=DraggablePositionEnum.INSIDE){this.resource=e,this.position=t}hasConflictWithTreeNode(e){return"folder"===this.resource.type&&(e.stateIdentifier===this.resource.stateIdentifier||e.parentsStateIdentifier[0]==this.resource.stateIdentifier||e.parentsStateIdentifier.includes(this.resource.stateIdentifier))}}class FileResource extends Resource{static fromTreeNode(e){return new FileResource(decodeURIComponent(e.type),decodeURIComponent(e.identifier),decodeURIComponent(e.stateIdentifier),decodeURIComponent(e.name))}}class FileOperationCollection{constructor(e,t){this.operations=e,this.target=t}static fromDataTransfer(e,t){return FileOperationCollection.fromArray(JSON.parse(e.getData("application/json")),t)}static fromArray(e,t){const r=[];for(let t of e)r.push(new FileOperation(t,DraggablePositionEnum.INSIDE));return new FileOperationCollection(r,t)}static fromNodePositionOptions(e){const t=FileResource.fromTreeNode(e.node),r=FileResource.fromTreeNode(e.target),o=[new FileOperation(t,e.position)];return new FileOperationCollection(o,r)}getConflictingOperationsForTreeNode(e){return this.operations.filter((t=>t.hasConflictWithTreeNode(e)))}getResources(){const e=[];return this.operations.forEach((t=>{e.push(t.resource)})),e}} \ No newline at end of file + `}firstUpdated(){this.toolbar.tree=this.tree,this.tree.addEventListener("typo3:svg-tree:expand-toggle",this.toggleExpandState),this.tree.addEventListener("typo3:svg-tree:node-selected",this.loadContent),this.tree.addEventListener("typo3:svg-tree:node-context",this.showContextMenu),this.tree.addEventListener("typo3:svg-tree:nodes-prepared",this.selectActiveNode)}};__decorate([query(".svg-tree-wrapper")],FileStorageTreeNavigationComponent.prototype,"tree",void 0),__decorate([query("typo3-backend-tree-toolbar")],FileStorageTreeNavigationComponent.prototype,"toolbar",void 0),FileStorageTreeNavigationComponent=__decorate([customElement(navigationComponentName)],FileStorageTreeNavigationComponent);export{FileStorageTreeNavigationComponent};class FileStorageTreeActions extends DragDrop{isInSameParentNode(e,t){return e.stateIdentifier==t.stateIdentifier||e.parentsStateIdentifier[0]==t.stateIdentifier||t.parentsStateIdentifier.includes(e.stateIdentifier)}getDropCommandDetails(e,t){const r=this.tree.nodes,o=t.identifier;let i=this.tree.settings.nodeDragPosition,n=e||t;if(o===n.identifier)return null;if(i===DraggablePositionEnum.BEFORE){const t=r.indexOf(e),o=this.setNodePositionAndTarget(t);if(null===o)return null;i=o.position,n=o.target}return{node:t,identifier:o,target:n,position:i}}setNodePositionAndTarget(e){const t=this.tree.nodes,r=t[e].depth;e>0&&e--;const o=t[e].depth,i=this.tree.nodes[e];if(o===r)return{position:DraggablePositionEnum.AFTER,target:i};if(o<r)return{position:DraggablePositionEnum.INSIDE,target:i};for(let o=e;o>=0;o--){if(t[o].depth===r)return{position:DraggablePositionEnum.AFTER,target:this.tree.nodes[o]};if(t[o].depth<r)return{position:DraggablePositionEnum.AFTER,target:t[o]}}return null}updateStateOfHoveredNode(e){this.tree.settings.nodeDragPosition=!1,this.tree.hoveredNode&&this.tree.isOverSvg?e.isOver||this.isTheSameNode(this.tree.hoveredNode,e)||this.isInSameParentNode(e,this.tree.hoveredNode)?this.addNodeDdClass("nodrop"):(this.addNodeDdClass("ok-append"),this.tree.settings.nodeDragPosition=DraggablePositionEnum.INSIDE):this.addNodeDdClass("nodrop")}isDropAllowed(e,t){return!t.isOver&&(!this.isTheSameNode(e,t)&&!!this.tree.isOverSvg)}initiateDropAction(e){const t={action:"transfer",resources:e.getResources(),target:e.target};top.document.dispatchEvent(new CustomEvent(FileListDragDropEvent.transfer,{detail:t}))}}class FileStorageTreeNodeDragHandler{constructor(e,t){this.dragStarted=!1,this.startPageX=0,this.startPageY=0,this.tree=e,this.actionHandler=t}onDragStart(e,t){return 0!==t.depth&&(this.startPageX=e.pageX,this.startPageY=e.pageY,this.dragStarted=!1,!0)}onDragOver(e,t){return!!this.actionHandler.isDragNodeDistanceMore(e,this)&&(this.dragStarted=!0,0!==t.depth&&(this.actionHandler.getDraggable()||this.actionHandler.createDraggableFromExistingNode(t),this.actionHandler.openNodeTimeout(),this.actionHandler.updateDraggablePosition(e),this.actionHandler.updateStateOfHoveredNode(t),!0))}onDrop(e,t){if(!this.dragStarted||0===t.depth)return!1;if(this.actionHandler.cleanupDrop(),this.actionHandler.isDropAllowed(this.tree.hoveredNode,t)){const e=this.actionHandler.getDropCommandDetails(this.tree.hoveredNode,t);if(null===e)return!1;const r=FileOperationCollection.fromNodePositionOptions(e),o=r.getConflictingOperationsForTreeNode(e.target);if(o.length>0)return o.forEach((t=>{Notification.showMessage(TYPO3.lang["drop.conflict"],TYPO3.lang["mess.drop.conflict"].replace("%s",t.resource.name).replace("%s",decodeURIComponent(e.target.identifier)),SeverityEnum.error)})),!1;this.actionHandler.initiateDropAction(r)}return!0}}class FileOperation{constructor(e,t=DraggablePositionEnum.INSIDE){this.resource=e,this.position=t}hasConflictWithTreeNode(e){return"folder"===this.resource.type&&(e.stateIdentifier===this.resource.stateIdentifier||e.parentsStateIdentifier[0]==this.resource.stateIdentifier||e.parentsStateIdentifier.includes(this.resource.stateIdentifier))}}class FileResource extends Resource{static fromTreeNode(e){return new FileResource(decodeURIComponent(e.type),decodeURIComponent(e.identifier),decodeURIComponent(e.stateIdentifier),decodeURIComponent(e.name))}}class FileOperationCollection{constructor(e,t){this.operations=e,this.target=t}static fromDataTransfer(e,t){return FileOperationCollection.fromArray(JSON.parse(e.getData("application/json")),t)}static fromArray(e,t){const r=[];for(const t of e)r.push(new FileOperation(t,DraggablePositionEnum.INSIDE));return new FileOperationCollection(r,t)}static fromNodePositionOptions(e){const t=FileResource.fromTreeNode(e.node),r=FileResource.fromTreeNode(e.target),o=[new FileOperation(t,e.position)];return new FileOperationCollection(o,r)}getConflictingOperationsForTreeNode(e){return this.operations.filter((t=>t.hasConflictWithTreeNode(e)))}getResources(){const e=[];return this.operations.forEach((t=>{e.push(t.resource)})),e}} \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/tree/file-storage-tree.js b/typo3/sysext/backend/Resources/Public/JavaScript/tree/file-storage-tree.js index fa74311e3390..e1304330e027 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/tree/file-storage-tree.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/tree/file-storage-tree.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{SvgTree}from"@typo3/backend/svg-tree.js";export class FileStorageTree extends SvgTree{constructor(){super(),this.settings.defaultProperties={hasChildren:!1,nameSourceField:"title",itemType:"sys_file",prefix:"",suffix:"",locked:!1,loaded:!1,overlayIcon:"",selectable:!0,expanded:!1,checked:!1,backgroundColor:"",class:"",readableRootline:""}}showChildren(e){this.loadChildrenOfNode(e),super.showChildren(e)}getNodeTitle(e){return decodeURIComponent(e.name)}loadChildrenOfNode(e){if(e.loaded)return this.prepareDataForVisibleNodes(),void this.updateVisibleNodes();this.nodesAddPlaceholder(),new AjaxRequest(this.settings.dataUrl+"&parent="+e.identifier+"¤tDepth="+e.depth).get({cache:"no-cache"}).then((e=>e.resolve())).then((t=>{let o=Array.isArray(t)?t:[];const r=this.nodes.indexOf(e)+1;o.forEach(((e,t)=>{this.nodes.splice(r+t,0,e)})),e.loaded=!0,this.setParametersNode(),this.prepareDataForVisibleNodes(),this.updateVisibleNodes(),this.nodesRemovePlaceholder(),this.focusNode(e)})).catch((e=>{throw this.errorNotification(e,!1),this.nodesRemovePlaceholder(),e}))}} \ No newline at end of file +import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{SvgTree}from"@typo3/backend/svg-tree.js";export class FileStorageTree extends SvgTree{constructor(){super(),this.settings.defaultProperties={hasChildren:!1,nameSourceField:"title",itemType:"sys_file",prefix:"",suffix:"",locked:!1,loaded:!1,overlayIcon:"",selectable:!0,expanded:!1,checked:!1,backgroundColor:"",class:"",readableRootline:""}}showChildren(e){this.loadChildrenOfNode(e),super.showChildren(e)}getNodeTitle(e){return decodeURIComponent(e.name)}loadChildrenOfNode(e){if(e.loaded)return this.prepareDataForVisibleNodes(),void this.updateVisibleNodes();this.nodesAddPlaceholder(),new AjaxRequest(this.settings.dataUrl+"&parent="+e.identifier+"¤tDepth="+e.depth).get({cache:"no-cache"}).then((e=>e.resolve())).then((t=>{const o=Array.isArray(t)?t:[],r=this.nodes.indexOf(e)+1;o.forEach(((e,t)=>{this.nodes.splice(r+t,0,e)})),e.loaded=!0,this.setParametersNode(),this.prepareDataForVisibleNodes(),this.updateVisibleNodes(),this.nodesRemovePlaceholder(),this.focusNode(e)})).catch((e=>{throw this.errorNotification(e,!1),this.nodesRemovePlaceholder(),e}))}} \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/tree/page-browser.js b/typo3/sysext/backend/Resources/Public/JavaScript/tree/page-browser.js index e1123cdbf776..33cd6c29bab1 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/tree/page-browser.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/tree/page-browser.js @@ -10,7 +10,7 @@ * * The TYPO3 project - inspiring people to share! */ -var __decorate=function(e,t,n,i){var r,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,i);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s};import{html,LitElement,nothing}from"lit";import{customElement,property,query}from"lit/decorators.js";import{until}from"lit/directives/until.js";import{lll}from"@typo3/core/lit-helper.js";import{PageTree}from"@typo3/backend/page-tree/page-tree.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import ElementBrowser from"@typo3/backend/element-browser.js";import LinkBrowser from"@typo3/backend/link-browser.js";import"@typo3/backend/element/icon-element.js";import Persistent from"@typo3/backend/storage/persistent.js";const componentName="typo3-backend-component-page-browser";let PageBrowserTree=class extends PageTree{appendTextElement(e){return super.appendTextElement(e).attr("opacity",(e=>this.settings.actions.includes("link")?this.isLinkable(e)?1:.5:1))}updateNodeActions(e){const t=super.updateNodeActions(e);if(this.settings.actions.includes("link")){const e=this.nodesActionsContainer.selectAll(".node-action").append("g").attr("visibility",(e=>this.isLinkable(e)?"visible":"hidden")).on("click",((e,t)=>{this.linkItem(t)}));this.createIconAreaForAction(e,"actions-link")}else if(this.settings.actions.includes("select")){const e=t.append("g").on("click",((e,t)=>{this.selectItem(t)}));this.createIconAreaForAction(e,"actions-link")}return t}linkItem(e){LinkBrowser.finalizeFunction("t3://page?uid="+e.identifier)}isLinkable(e){return!1===["199","254","255"].includes(String(e.type))}selectItem(e){ElementBrowser.insertElement(e.itemType,e.identifier,e.name,e.identifier,!0)}};PageBrowserTree=__decorate([customElement("typo3-backend-component-page-browser-tree")],PageBrowserTree);let PageBrowser=class extends LitElement{constructor(){super(...arguments),this.mountPointPath=null,this.activePageId=0,this.actions=[],this.configuration=null,this.triggerRender=()=>{this.tree.dispatchEvent(new Event("svg-tree:visible"))},this.selectActivePageInTree=e=>{let t=e.detail.nodes;e.detail.nodes=t.map((e=>(parseInt(e.identifier,10)===this.activePageId&&(e.checked=!0),e)))},this.toggleExpandState=e=>{const t=e.detail.node;t&&Persistent.set("BackendComponents.States.Pagetree.stateHash."+t.stateIdentifier,t.expanded?"1":"0")},this.loadRecordsOfPage=e=>{const t=e.detail.node;if(!t.checked)return;let n=document.location.href+"&contentOnly=1&expandPage="+t.identifier;new AjaxRequest(n).get().then((e=>e.resolve())).then((e=>{document.querySelector(".element-browser-main-content .element-browser-body").innerHTML=e}))},this.setMountPoint=e=>{this.setTemporaryMountPoint(e.detail.pageId)}}connectedCallback(){super.connectedCallback(),document.addEventListener("typo3:navigation:resized",this.triggerRender),document.addEventListener("typo3:pagetree:mountPoint",this.setMountPoint)}disconnectedCallback(){document.removeEventListener("typo3:navigation:resized",this.triggerRender),document.removeEventListener("typo3:pagetree:mountPoint",this.setMountPoint),super.disconnectedCallback()}firstUpdated(){this.activePageId=parseInt(this.getAttribute("active-page"),10),this.actions=JSON.parse(this.getAttribute("tree-actions"))}createRenderRoot(){return this}getConfiguration(){if(null!==this.configuration)return Promise.resolve(this.configuration);const e=top.TYPO3.settings.ajaxUrls.page_tree_browser_configuration,t=this.hasAttribute("alternative-entry-points")?JSON.parse(this.getAttribute("alternative-entry-points")):[];let n=new AjaxRequest(e);return t.length&&(n=n.withQueryArguments("alternativeEntryPoints="+encodeURIComponent(t))),n.get().then((async e=>{const t=await e.resolve("json");return t.actions=this.actions,this.configuration=t,this.mountPointPath=t.temporaryMountPoint||null,t}))}render(){return html` +var __decorate=function(e,t,n,i){var r,o=arguments.length,s=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,i);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(s=(o<3?r(s):o>3?r(t,n,s):r(t,n))||s);return o>3&&s&&Object.defineProperty(t,n,s),s};import{html,LitElement,nothing}from"lit";import{customElement,property,query}from"lit/decorators.js";import{until}from"lit/directives/until.js";import{lll}from"@typo3/core/lit-helper.js";import{PageTree}from"@typo3/backend/page-tree/page-tree.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import ElementBrowser from"@typo3/backend/element-browser.js";import LinkBrowser from"@typo3/backend/link-browser.js";import"@typo3/backend/element/icon-element.js";import Persistent from"@typo3/backend/storage/persistent.js";const componentName="typo3-backend-component-page-browser";let PageBrowserTree=class extends PageTree{appendTextElement(e){return super.appendTextElement(e).attr("opacity",(e=>this.settings.actions.includes("link")?this.isLinkable(e)?1:.5:1))}updateNodeActions(e){const t=super.updateNodeActions(e);if(this.settings.actions.includes("link")){const e=this.nodesActionsContainer.selectAll(".node-action").append("g").attr("visibility",(e=>this.isLinkable(e)?"visible":"hidden")).on("click",((e,t)=>{this.linkItem(t)}));this.createIconAreaForAction(e,"actions-link")}else if(this.settings.actions.includes("select")){const e=t.append("g").on("click",((e,t)=>{this.selectItem(t)}));this.createIconAreaForAction(e,"actions-link")}return t}linkItem(e){LinkBrowser.finalizeFunction("t3://page?uid="+e.identifier)}isLinkable(e){return!1===["199","254","255"].includes(String(e.type))}selectItem(e){ElementBrowser.insertElement(e.itemType,e.identifier,e.name,e.identifier,!0)}};PageBrowserTree=__decorate([customElement("typo3-backend-component-page-browser-tree")],PageBrowserTree);let PageBrowser=class extends LitElement{constructor(){super(...arguments),this.mountPointPath=null,this.activePageId=0,this.actions=[],this.configuration=null,this.triggerRender=()=>{this.tree.dispatchEvent(new Event("svg-tree:visible"))},this.selectActivePageInTree=e=>{const t=e.detail.nodes;e.detail.nodes=t.map((e=>(parseInt(e.identifier,10)===this.activePageId&&(e.checked=!0),e)))},this.toggleExpandState=e=>{const t=e.detail.node;t&&Persistent.set("BackendComponents.States.Pagetree.stateHash."+t.stateIdentifier,t.expanded?"1":"0")},this.loadRecordsOfPage=e=>{const t=e.detail.node;if(!t.checked)return;const n=document.location.href+"&contentOnly=1&expandPage="+t.identifier;new AjaxRequest(n).get().then((e=>e.resolve())).then((e=>{document.querySelector(".element-browser-main-content .element-browser-body").innerHTML=e}))},this.setMountPoint=e=>{this.setTemporaryMountPoint(e.detail.pageId)}}connectedCallback(){super.connectedCallback(),document.addEventListener("typo3:navigation:resized",this.triggerRender),document.addEventListener("typo3:pagetree:mountPoint",this.setMountPoint)}disconnectedCallback(){document.removeEventListener("typo3:navigation:resized",this.triggerRender),document.removeEventListener("typo3:pagetree:mountPoint",this.setMountPoint),super.disconnectedCallback()}firstUpdated(){this.activePageId=parseInt(this.getAttribute("active-page"),10),this.actions=JSON.parse(this.getAttribute("tree-actions"))}createRenderRoot(){return this}getConfiguration(){if(null!==this.configuration)return Promise.resolve(this.configuration);const e=top.TYPO3.settings.ajaxUrls.page_tree_browser_configuration,t=this.hasAttribute("alternative-entry-points")?JSON.parse(this.getAttribute("alternative-entry-points")):[];let n=new AjaxRequest(e);return t.length&&(n=n.withQueryArguments("alternativeEntryPoints="+encodeURIComponent(t))),n.get().then((async e=>{const t=await e.resolve("json");return t.actions=this.actions,this.configuration=t,this.mountPointPath=t.temporaryMountPoint||null,t}))}render(){return html` <div class="svg-tree"> ${until(this.renderTree(),this.renderLoader())} </div> diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/url-link-handler.js b/typo3/sysext/backend/Resources/Public/JavaScript/url-link-handler.js index 783f33e8c4cf..2edea14cba3d 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/url-link-handler.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/url-link-handler.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import LinkBrowser from"@typo3/backend/link-browser.js";import RegularEvent from"@typo3/core/event/regular-event.js";class UrlLinkHandler{constructor(){new RegularEvent("submit",((e,r)=>{e.preventDefault();let l=r.querySelector('[name="lurl"]').value.trim();""!==l&&LinkBrowser.finalizeFunction(l)})).delegateTo(document,"#lurlform")}}export default new UrlLinkHandler; \ No newline at end of file +import LinkBrowser from"@typo3/backend/link-browser.js";import RegularEvent from"@typo3/core/event/regular-event.js";class UrlLinkHandler{constructor(){new RegularEvent("submit",((e,r)=>{e.preventDefault();const n=r.querySelector('[name="lurl"]').value.trim();""!==n&&LinkBrowser.finalizeFunction(n)})).delegateTo(document,"#lurlform")}}export default new UrlLinkHandler; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/utility/collapse-state-search.js b/typo3/sysext/backend/Resources/Public/JavaScript/utility/collapse-state-search.js index 3b2586ce76c4..9f720c7d46da 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/utility/collapse-state-search.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/utility/collapse-state-search.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import Client from"@typo3/backend/storage/client.js";import"@typo3/backend/input/clearable.js";import DocumentService from"@typo3/core/document-service.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import RegularEvent from"@typo3/core/event/regular-event.js";import Mark from"@typo3/backend/contrib/mark.js";class CollapseStateSearch{constructor(){this.searchValueSelector=".t3js-collapse-search-term",this.searchValue="",this.markInstances=[],DocumentService.ready().then((()=>{if(this.treeContainers=document.querySelectorAll(".t3js-collapse-states-search-tree"),0!==this.treeContainers.length){this.numberOfSearchMatchesContainer=document.querySelectorAll(".t3js-collapse-states-search-numberOfSearchMatches"),this.searchField=document.querySelector(this.searchValueSelector),this.searchForm=this.searchField.closest("form"),this.searchSessionKey=this.searchField.dataset.persistCollapseSearchKey,this.searchValue=Client.get(this.searchSessionKey)??"",this.registerEvents();for(let e=0;e<this.treeContainers.length;e++)this.markInstances[e]=new Mark(this.treeContainers[e]);""!==this.searchValue&&(this.searchField.value=this.searchValue,this.searchField.dispatchEvent(new Event("keyup")),this.searchForm.requestSubmit())}}))}registerEvents(){this.searchField.clearable({onClear:e=>{e.closest("form").requestSubmit()}}),new DebounceEvent("input",(()=>{this.searchForm.requestSubmit()})).bindTo(this.searchField),new RegularEvent("submit",(e=>{e.preventDefault();for(let e=0;e<this.treeContainers.length;e++)this.filterTree(this.searchField.value,this.treeContainers[e],this.numberOfSearchMatchesContainer[e],this.markInstances[e])})).bindTo(this.searchForm)}filterTree(e,t,s,r){if(e=e.toLowerCase(),r.unmark(),Client.set(this.searchSessionKey,e),e.length<3)return void s.classList.add("hidden");const a=new Set,n=[...this.findNodesByIdentifier(e,t),...this.findNodesByValue(e,t),...this.findNodesByComment(e,t),...this.findNodesByConstantSubstitution(e,t)];s.innerText=String(TYPO3.lang["collapse-state-search.numberOfSearchMatches"]).replace("%s",String(n.length)),s.classList.remove("hidden"),n.forEach((e=>{if(null===e)return;const t=e.parentElement.querySelector('[data-bs-toggle="collapse"]')?.dataset.bsTarget;void 0!==t&&a.add(t.substring(1));const s=this.parents(e,".collapse");for(let e of s)a.add(e.id)}));const o=Array.from(t.querySelectorAll(".collapse"));for(let e of o){const t=e.classList.contains("show"),s=e.id;if(a.has(s)){if(!t){const t=document.querySelector('[data-bs-target="#'+s+'"]');t.classList.remove("collapsed"),t.setAttribute("aria-expanded","true"),e.classList.add("show")}}else if(t){const t=document.querySelector('[data-bs-target="#'+s+'"]');t.classList.add("collapsed"),t.setAttribute("aria-expanded","false"),e.classList.remove("show")}}r.mark(e,{element:"strong",className:"text-danger"})}findNodesByIdentifier(e,t){return Array.from(t.querySelectorAll(".list-tree-label")).filter((t=>t.textContent.toLowerCase().includes(e)))}findNodesByValue(e,t){return Array.from(t.querySelectorAll(".list-tree-value")).filter((t=>t.textContent.toLowerCase().includes(e))).map((e=>e.previousElementSibling))}findNodesByComment(e,t){return Array.from(t.querySelectorAll(".list-tree-comment")).filter((t=>t.textContent.toLowerCase().includes(e)))}findNodesByConstantSubstitution(e,t){return Array.from(t.querySelectorAll(".list-tree-constant-substitution")).filter((t=>t.textContent.toLowerCase().includes(e)))}parents(e,t){const s=[];let r;for(;null!==(r=e.parentElement.closest(t));)e=r,s.push(r);return s}}export default new CollapseStateSearch; \ No newline at end of file +import Client from"@typo3/backend/storage/client.js";import"@typo3/backend/input/clearable.js";import DocumentService from"@typo3/core/document-service.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import RegularEvent from"@typo3/core/event/regular-event.js";import Mark from"@typo3/backend/contrib/mark.js";class CollapseStateSearch{constructor(){this.searchValueSelector=".t3js-collapse-search-term",this.searchValue="",this.markInstances=[],DocumentService.ready().then((()=>{if(this.treeContainers=document.querySelectorAll(".t3js-collapse-states-search-tree"),0!==this.treeContainers.length){this.numberOfSearchMatchesContainer=document.querySelectorAll(".t3js-collapse-states-search-numberOfSearchMatches"),this.searchField=document.querySelector(this.searchValueSelector),this.searchForm=this.searchField.closest("form"),this.searchSessionKey=this.searchField.dataset.persistCollapseSearchKey,this.searchValue=Client.get(this.searchSessionKey)??"",this.registerEvents();for(let e=0;e<this.treeContainers.length;e++)this.markInstances[e]=new Mark(this.treeContainers[e]);""!==this.searchValue&&(this.searchField.value=this.searchValue,this.searchField.dispatchEvent(new Event("keyup")),this.searchForm.requestSubmit())}}))}registerEvents(){this.searchField.clearable({onClear:e=>{e.closest("form").requestSubmit()}}),new DebounceEvent("input",(()=>{this.searchForm.requestSubmit()})).bindTo(this.searchField),new RegularEvent("submit",(e=>{e.preventDefault();for(let e=0;e<this.treeContainers.length;e++)this.filterTree(this.searchField.value,this.treeContainers[e],this.numberOfSearchMatchesContainer[e],this.markInstances[e])})).bindTo(this.searchForm)}filterTree(e,t,s,r){if(e=e.toLowerCase(),r.unmark(),Client.set(this.searchSessionKey,e),e.length<3)return void s.classList.add("hidden");const a=new Set,n=[...this.findNodesByIdentifier(e,t),...this.findNodesByValue(e,t),...this.findNodesByComment(e,t),...this.findNodesByConstantSubstitution(e,t)];s.innerText=String(TYPO3.lang["collapse-state-search.numberOfSearchMatches"]).replace("%s",String(n.length)),s.classList.remove("hidden"),n.forEach((e=>{if(null===e)return;const t=e.parentElement.querySelector('[data-bs-toggle="collapse"]')?.dataset.bsTarget;void 0!==t&&a.add(t.substring(1));const s=this.parents(e,".collapse");for(const e of s)a.add(e.id)}));const o=Array.from(t.querySelectorAll(".collapse"));for(const e of o){const t=e.classList.contains("show"),s=e.id;if(a.has(s)){if(!t){const t=document.querySelector('[data-bs-target="#'+s+'"]');t.classList.remove("collapsed"),t.setAttribute("aria-expanded","true"),e.classList.add("show")}}else if(t){const t=document.querySelector('[data-bs-target="#'+s+'"]');t.classList.add("collapsed"),t.setAttribute("aria-expanded","false"),e.classList.remove("show")}}r.mark(e,{element:"strong",className:"text-danger"})}findNodesByIdentifier(e,t){return Array.from(t.querySelectorAll(".list-tree-label")).filter((t=>t.textContent.toLowerCase().includes(e)))}findNodesByValue(e,t){return Array.from(t.querySelectorAll(".list-tree-value")).filter((t=>t.textContent.toLowerCase().includes(e))).map((e=>e.previousElementSibling))}findNodesByComment(e,t){return Array.from(t.querySelectorAll(".list-tree-comment")).filter((t=>t.textContent.toLowerCase().includes(e)))}findNodesByConstantSubstitution(e,t){return Array.from(t.querySelectorAll(".list-tree-constant-substitution")).filter((t=>t.textContent.toLowerCase().includes(e)))}parents(e,t){const s=[];let r;for(;null!==(r=e.parentElement.closest(t));)e=r,s.push(r);return s}}export default new CollapseStateSearch; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/viewport/navigation-container.js b/typo3/sysext/backend/Resources/Public/JavaScript/viewport/navigation-container.js index b6d30289aabd..624672443c8d 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/viewport/navigation-container.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/viewport/navigation-container.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{ScaffoldIdentifierEnum}from"@typo3/backend/enum/viewport/scaffold-identifier.js";import{AbstractContainer}from"@typo3/backend/viewport/abstract-container.js";import TriggerRequest from"@typo3/backend/event/trigger-request.js";class NavigationContainer extends AbstractContainer{constructor(t){super(t),this.activeComponentId=""}get parent(){return document.querySelector(ScaffoldIdentifierEnum.scaffold)}get container(){return document.querySelector(ScaffoldIdentifierEnum.contentNavigation)}get switcher(){return document.querySelector(ScaffoldIdentifierEnum.contentNavigationSwitcher)}showComponent(t){const e=this.container;if(this.show(t),t===this.activeComponentId)return;if(""!==this.activeComponentId){let t=e.querySelector("#navigationComponent-"+this.activeComponentId.replace(/[/@]/g,"_"));t&&(t.style.display="none")}const n="navigationComponent-"+t.replace(/[/@]/g,"_");if(1===e.querySelectorAll('[data-component="'+t+'"]').length)return this.show(t),void(this.activeComponentId=t);import(t+".js").then((o=>{if("string"==typeof o.navigationComponentName){const i=o.navigationComponentName,a=document.createElement(i);a.setAttribute("id",n),a.classList.add("scaffold-content-navigation-component"),a.dataset.component=t,e.append(a)}else{e.insertAdjacentHTML("beforeend",'<div class="scaffold-content-navigation-component" data-component="'+t+'" id="'+n+'"></div>');Object.values(o)[0].initialize("#"+n)}this.show(t),this.activeComponentId=t}))}hide(t){const e=this.parent,n=this.switcher;e.classList.remove("scaffold-content-navigation-expanded"),e.classList.remove("scaffold-content-navigation-available"),t&&n&&(n.style.display="none")}show(t){const e=this.parent,n=this.container,o=this.switcher;if(n.querySelectorAll(ScaffoldIdentifierEnum.contentNavigationDataComponent).forEach((t=>t.style.display="none")),void 0!==typeof t){e.classList.add("scaffold-content-navigation-expanded"),e.classList.add("scaffold-content-navigation-available");const o=n.querySelector('[data-component="'+t+'"]');o&&(o.style.display=null)}o&&(o.style.display=null)}setUrl(t,e){const n=this.consumerScope.invoke(new TriggerRequest("typo3.setUrl",e));return n.then((()=>{this.parent.classList.add("scaffold-content-navigation-expanded")})),n}}export default NavigationContainer; \ No newline at end of file +import{ScaffoldIdentifierEnum}from"@typo3/backend/enum/viewport/scaffold-identifier.js";import{AbstractContainer}from"@typo3/backend/viewport/abstract-container.js";import TriggerRequest from"@typo3/backend/event/trigger-request.js";class NavigationContainer extends AbstractContainer{constructor(t){super(t),this.activeComponentId=""}get parent(){return document.querySelector(ScaffoldIdentifierEnum.scaffold)}get container(){return document.querySelector(ScaffoldIdentifierEnum.contentNavigation)}get switcher(){return document.querySelector(ScaffoldIdentifierEnum.contentNavigationSwitcher)}showComponent(t){const e=this.container;if(this.show(t),t===this.activeComponentId)return;if(""!==this.activeComponentId){const t=e.querySelector("#navigationComponent-"+this.activeComponentId.replace(/[/@]/g,"_"));t&&(t.style.display="none")}const n="navigationComponent-"+t.replace(/[/@]/g,"_");if(1===e.querySelectorAll('[data-component="'+t+'"]').length)return this.show(t),void(this.activeComponentId=t);import(t+".js").then((o=>{if("string"==typeof o.navigationComponentName){const i=o.navigationComponentName,a=document.createElement(i);a.setAttribute("id",n),a.classList.add("scaffold-content-navigation-component"),a.dataset.component=t,e.append(a)}else{e.insertAdjacentHTML("beforeend",'<div class="scaffold-content-navigation-component" data-component="'+t+'" id="'+n+'"></div>');Object.values(o)[0].initialize("#"+n)}this.show(t),this.activeComponentId=t}))}hide(t){const e=this.parent,n=this.switcher;e.classList.remove("scaffold-content-navigation-expanded"),e.classList.remove("scaffold-content-navigation-available"),t&&n&&(n.style.display="none")}show(t){const e=this.parent,n=this.container,o=this.switcher;if(n.querySelectorAll(ScaffoldIdentifierEnum.contentNavigationDataComponent).forEach((t=>t.style.display="none")),void 0!==typeof t){e.classList.add("scaffold-content-navigation-expanded"),e.classList.add("scaffold-content-navigation-available");const o=n.querySelector('[data-component="'+t+'"]');o&&(o.style.display=null)}o&&(o.style.display=null)}setUrl(t,e){const n=this.consumerScope.invoke(new TriggerRequest("typo3.setUrl",e));return n.then((()=>{this.parent.classList.add("scaffold-content-navigation-expanded")})),n}}export default NavigationContainer; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/viewport/resizable-navigation.js b/typo3/sysext/backend/Resources/Public/JavaScript/viewport/resizable-navigation.js index 4ee45154cdc2..038c8530a859 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/viewport/resizable-navigation.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/viewport/resizable-navigation.js @@ -10,7 +10,7 @@ * * The TYPO3 project - inspiring people to share! */ -var __decorate=function(t,e,i,n){var o,a=arguments.length,s=a<3?e:null===n?n=Object.getOwnPropertyDescriptor(e,i):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(t,e,i,n);else for(var r=t.length-1;r>=0;r--)(o=t[r])&&(s=(a<3?o(s):a>3?o(e,i,s):o(e,i))||s);return a>3&&s&&Object.defineProperty(e,i,s),s};import{html,LitElement}from"lit";import{customElement,property,state}from"lit/decorators.js";import{lll}from"@typo3/core/lit-helper.js";import Persistent from"@typo3/backend/storage/persistent.js";import"@typo3/backend/element/icon-element.js";const selectorConverter={fromAttribute:t=>document.querySelector(t)};let ResizableNavigation=class extends LitElement{constructor(){super(...arguments),this.minimumWidth=250,this.resizing=!1,this.toggleNavigation=t=>{t instanceof MouseEvent&&2===t.button||(t.stopPropagation(),this.parentContainer.classList.toggle("scaffold-content-navigation-expanded"))},this.fallbackNavigationSizeIfNeeded=t=>{let e=t.currentTarget;0!==this.getNavigationWidth()&&e.outerWidth<this.getNavigationWidth()+this.getNavigationPosition().left+this.minimumWidth&&this.autoNavigationWidth()},this.handleMouseMove=t=>{this.resizeNavigation(t.clientX)},this.handleTouchMove=t=>{this.resizeNavigation(t.changedTouches[0].clientX)},this.resizeNavigation=t=>{let e=Math.round(t)-Math.round(this.getNavigationPosition().left);this.setNavigationWidth(e)},this.startResizeNavigation=t=>{t instanceof MouseEvent&&2===t.button||(t.stopPropagation(),this.resizing=!0,document.addEventListener("mousemove",this.handleMouseMove,!1),document.addEventListener("mouseup",this.stopResizeNavigation,!1),document.addEventListener("touchmove",this.handleTouchMove,!1),document.addEventListener("touchend",this.stopResizeNavigation,!1))},this.stopResizeNavigation=()=>{this.resizing=!1,document.removeEventListener("mousemove",this.handleMouseMove,!1),document.removeEventListener("mouseup",this.stopResizeNavigation,!1),document.removeEventListener("touchmove",this.handleTouchMove,!1),document.removeEventListener("touchend",this.stopResizeNavigation,!1),Persistent.set(this.persistenceIdentifier,this.getNavigationWidth()),document.dispatchEvent(new CustomEvent("typo3:navigation:resized"))}}connectedCallback(){super.connectedCallback();const t=this.initialWidth||parseInt(Persistent.get(this.persistenceIdentifier),10);this.setNavigationWidth(t),window.addEventListener("resize",this.fallbackNavigationSizeIfNeeded,{passive:!0})}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("resize",this.fallbackNavigationSizeIfNeeded)}createRenderRoot(){return this}async firstUpdated(){await new Promise((t=>setTimeout(t,0))),this.querySelector(".scaffold-content-navigation-switcher-btn").addEventListener("touchstart",this.toggleNavigation,{passive:!0}),this.querySelector(".scaffold-content-navigation-drag").addEventListener("touchstart",this.startResizeNavigation,{passive:!0})}render(){return html` +var __decorate=function(t,e,i,n){var o,a=arguments.length,s=a<3?e:null===n?n=Object.getOwnPropertyDescriptor(e,i):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(t,e,i,n);else for(var r=t.length-1;r>=0;r--)(o=t[r])&&(s=(a<3?o(s):a>3?o(e,i,s):o(e,i))||s);return a>3&&s&&Object.defineProperty(e,i,s),s};import{html,LitElement}from"lit";import{customElement,property,state}from"lit/decorators.js";import{lll}from"@typo3/core/lit-helper.js";import Persistent from"@typo3/backend/storage/persistent.js";import"@typo3/backend/element/icon-element.js";const selectorConverter={fromAttribute:t=>document.querySelector(t)};let ResizableNavigation=class extends LitElement{constructor(){super(...arguments),this.minimumWidth=250,this.resizing=!1,this.toggleNavigation=t=>{t instanceof MouseEvent&&2===t.button||(t.stopPropagation(),this.parentContainer.classList.toggle("scaffold-content-navigation-expanded"))},this.fallbackNavigationSizeIfNeeded=t=>{const e=t.currentTarget;0!==this.getNavigationWidth()&&e.outerWidth<this.getNavigationWidth()+this.getNavigationPosition().left+this.minimumWidth&&this.autoNavigationWidth()},this.handleMouseMove=t=>{this.resizeNavigation(t.clientX)},this.handleTouchMove=t=>{this.resizeNavigation(t.changedTouches[0].clientX)},this.resizeNavigation=t=>{const e=Math.round(t)-Math.round(this.getNavigationPosition().left);this.setNavigationWidth(e)},this.startResizeNavigation=t=>{t instanceof MouseEvent&&2===t.button||(t.stopPropagation(),this.resizing=!0,document.addEventListener("mousemove",this.handleMouseMove,!1),document.addEventListener("mouseup",this.stopResizeNavigation,!1),document.addEventListener("touchmove",this.handleTouchMove,!1),document.addEventListener("touchend",this.stopResizeNavigation,!1))},this.stopResizeNavigation=()=>{this.resizing=!1,document.removeEventListener("mousemove",this.handleMouseMove,!1),document.removeEventListener("mouseup",this.stopResizeNavigation,!1),document.removeEventListener("touchmove",this.handleTouchMove,!1),document.removeEventListener("touchend",this.stopResizeNavigation,!1),Persistent.set(this.persistenceIdentifier,this.getNavigationWidth()),document.dispatchEvent(new CustomEvent("typo3:navigation:resized"))}}connectedCallback(){super.connectedCallback();const t=this.initialWidth||parseInt(Persistent.get(this.persistenceIdentifier),10);this.setNavigationWidth(t),window.addEventListener("resize",this.fallbackNavigationSizeIfNeeded,{passive:!0})}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("resize",this.fallbackNavigationSizeIfNeeded)}createRenderRoot(){return this}async firstUpdated(){await new Promise((t=>setTimeout(t,0))),this.querySelector(".scaffold-content-navigation-switcher-btn").addEventListener("touchstart",this.toggleNavigation,{passive:!0}),this.querySelector(".scaffold-content-navigation-drag").addEventListener("touchstart",this.startResizeNavigation,{passive:!0})}render(){return html` <div class="scaffold-content-navigation-switcher"> <button @mouseup="${this.toggleNavigation}" class="btn btn-default btn-borderless scaffold-content-navigation-switcher-btn scaffold-content-navigation-switcher-open" role="button" title="${lll("viewport_navigation_show")}"> <typo3-backend-icon identifier="actions-chevron-right" size="small"></typo3-backend-icon> @@ -20,4 +20,4 @@ var __decorate=function(t,e,i,n){var o,a=arguments.length,s=a<3?e:null===n?n=Obj </button> </div> <div @mousedown="${this.startResizeNavigation}" class="scaffold-content-navigation-drag ${this.resizing?"resizing":""}"></div> - `}getNavigationPosition(){return this.navigationContainer.getBoundingClientRect()}getNavigationWidth(){return this.navigationContainer.offsetWidth}autoNavigationWidth(){this.navigationContainer.style.width="auto"}setNavigationWidth(t){const e=Math.round(this.parentContainer.getBoundingClientRect().width/2);t>e&&(t=e),t=t>this.minimumWidth?t:this.minimumWidth,this.navigationContainer.style.width=t+"px"}};__decorate([property({type:Number,attribute:"minimum-width"})],ResizableNavigation.prototype,"minimumWidth",void 0),__decorate([property({type:Number,attribute:"initial-width"})],ResizableNavigation.prototype,"initialWidth",void 0),__decorate([property({type:String,attribute:"persistence-identifier"})],ResizableNavigation.prototype,"persistenceIdentifier",void 0),__decorate([property({attribute:"parent",converter:selectorConverter})],ResizableNavigation.prototype,"parentContainer",void 0),__decorate([property({attribute:"navigation",converter:selectorConverter})],ResizableNavigation.prototype,"navigationContainer",void 0),__decorate([state()],ResizableNavigation.prototype,"resizing",void 0),ResizableNavigation=__decorate([customElement("typo3-backend-navigation-switcher")],ResizableNavigation); \ No newline at end of file + `}getNavigationPosition(){return this.navigationContainer.getBoundingClientRect()}getNavigationWidth(){return this.navigationContainer.offsetWidth}autoNavigationWidth(){this.navigationContainer.style.width="auto"}setNavigationWidth(t){const e=Math.round(this.parentContainer.getBoundingClientRect().width/2);t>e&&(t=e),t=t>this.minimumWidth?t:this.minimumWidth,this.navigationContainer.style.width=t+"px"}};__decorate([property({type:Number,attribute:"minimum-width"})],ResizableNavigation.prototype,"minimumWidth",void 0),__decorate([property({type:Number,attribute:"initial-width"})],ResizableNavigation.prototype,"initialWidth",void 0),__decorate([property({type:String,attribute:"persistence-identifier"})],ResizableNavigation.prototype,"persistenceIdentifier",void 0),__decorate([property({attribute:"parent",converter:selectorConverter})],ResizableNavigation.prototype,"parentContainer",void 0),__decorate([property({attribute:"navigation",converter:selectorConverter})],ResizableNavigation.prototype,"navigationContainer",void 0),__decorate([state()],ResizableNavigation.prototype,"resizing",void 0),ResizableNavigation=__decorate([customElement("typo3-backend-navigation-switcher")],ResizableNavigation);export{ResizableNavigation}; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/window-manager.js b/typo3/sysext/backend/Resources/Public/JavaScript/window-manager.js index 109e4e761f1c..8f76929c67ce 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/window-manager.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/window-manager.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import Utility from"@typo3/backend/utility.js";class WindowManager{constructor(){this.windows={},this.open=(...n)=>this._localOpen.apply(this,n),this.globalOpen=(...n)=>this._localOpen.apply(this,n),this.localOpen=(n,o,e="newTYPO3frontendWindow",i="")=>this._localOpen(n,o,e,i)}_localOpen(n,o,e="newTYPO3frontendWindow",i=""){if(!n)return null;null===o?o=!window.opener:void 0===o&&(o=!0);const t=this.windows[e]??window.open("",e,i),a="Window"===t.constructor.name&&!t.closed?t.location.href:null;if(Utility.urlsPointToSameServerSideResource(n,a))return t.location.replace(n),t.location.reload(),t;const w=window.open(n,e,i);return this.windows[e]=w,o&&w.focus(),w}}const windowManager=new WindowManager;top.TYPO3.WindowManager||(top.document===window.document?top.TYPO3.WindowManager=windowManager:top.TYPO3.WindowManager=new WindowManager);export default windowManager; \ No newline at end of file +import Utility from"@typo3/backend/utility.js";class WindowManager{constructor(){this.windows={},this.localOpen=(n,o,e="newTYPO3frontendWindow",i="")=>this._localOpen(n,o,e,i)}open(...n){return this._localOpen.apply(null,n)}globalOpen(...n){return this._localOpen.apply(null,n)}_localOpen(n,o,e="newTYPO3frontendWindow",i=""){if(!n)return null;null===o?o=!window.opener:void 0===o&&(o=!0);const t=this.windows[e]??window.open("",e,i),a="Window"===t.constructor.name&&!t.closed?t.location.href:null;if(Utility.urlsPointToSameServerSideResource(n,a))return t.location.replace(n),t.location.reload(),t;const l=window.open(n,e,i);return this.windows[e]=l,o&&l.focus(),l}}const windowManager=new WindowManager;top.TYPO3.WindowManager||(top.document===window.document?top.TYPO3.WindowManager=windowManager:top.TYPO3.WindowManager=new WindowManager);export default windowManager; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/wizard.js b/typo3/sysext/backend/Resources/Public/JavaScript/wizard.js index c332c24e3f37..7f88975fe5ad 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/wizard.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/wizard.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{SeverityEnum}from"@typo3/backend/enum/severity.js";import $ from"jquery";import{default as Modal}from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import Icons from"@typo3/backend/icons.js";class Wizard{constructor(){this.setup={slides:[],settings:{},forceSelection:!0,$carousel:null},this.originalSetup=$.extend(!0,{},this.setup)}set(e,t){return this.setup.settings[e]=t,this}addSlide(e,t,s="",i=SeverityEnum.info,a){const r={identifier:e,title:t,content:s,severity:i,callback:a};return this.setup.slides.push(r),this}addFinalProcessingSlide(e){return e||(e=()=>{this.dismiss()}),Icons.getIcon("spinner-circle-dark",Icons.sizes.large,null,null).then((t=>{let s=$("<div />",{class:"text-center"}).append(t);this.addSlide("final-processing-slide",top.TYPO3.lang["wizard.processing.title"],s[0].outerHTML,Severity.info,e)}))}show(){let e=this.generateSlides(),t=this.setup.slides[0];const s=Modal.advanced({title:t.title,content:e,severity:t.severity,staticBackdrop:!0,buttons:[{text:top.TYPO3.lang["wizard.button.cancel"],active:!0,btnClass:"btn-default",name:"cancel",trigger:()=>{this.getComponent().trigger("wizard-dismiss")}},{text:top.TYPO3.lang["wizard.button.next"],btnClass:"btn-"+Severity.getCssClass(t.severity),name:"next"}],callback:()=>{this.addProgressBar(),this.initializeEvents(s)}});this.setup.forceSelection&&this.lockNextStep(),this.getComponent().on("wizard-visible",(()=>{this.runSlideCallback(t,this.setup.$carousel.find(".carousel-item").first())})).on("wizard-dismissed",(()=>{this.setup=$.extend(!0,{},this.originalSetup)}))}getComponent(){return null===this.setup.$carousel&&this.generateSlides(),this.setup.$carousel}dismiss(){Modal.dismiss()}lockNextStep(){let e=this.setup.$carousel.closest(".modal").find('button[name="next"]');return e.prop("disabled",!0),e}unlockNextStep(){let e=this.setup.$carousel.closest(".modal").find('button[name="next"]');return e.prop("disabled",!1),e}setForceSelection(e){this.setup.forceSelection=e}initializeEvents(e){let t=this.setup.$carousel.closest(".modal"),s=t.find(".modal-title"),i=t.find(".modal-footer"),a=i.find('button[name="next"]');a.on("click",(()=>{this.setup.$carousel.carousel("next")})),this.setup.$carousel.on("slide.bs.carousel",(()=>{let e=this.setup.$carousel.data("currentSlide")+1,r=this.setup.$carousel.data("currentIndex")+1;s.text(this.setup.slides[r].title),this.setup.$carousel.data("currentSlide",e),this.setup.$carousel.data("currentIndex",r),e>=this.setup.$carousel.data("realSlideCount")?(t.find(".modal-header .close").remove(),i.slideUp()):i.find(".progress-bar").width(this.setup.$carousel.data("initialStep")*e+"%").text(top.TYPO3.lang["wizard.progress"].replace("{0}",e).replace("{1}",this.setup.$carousel.data("slideCount"))),a.removeClass("btn-"+Severity.getCssClass(this.setup.slides[r-1].severity)).addClass("btn-"+Severity.getCssClass(this.setup.slides[r].severity)),t.removeClass("modal-severity-"+Severity.getCssClass(this.setup.slides[r-1].severity)).addClass("modal-severity-"+Severity.getCssClass(this.setup.slides[r].severity))})).on("slid.bs.carousel",(e=>{let t=this.setup.$carousel.data("currentIndex"),s=this.setup.slides[t];this.runSlideCallback(s,$(e.relatedTarget)),this.setup.forceSelection&&this.lockNextStep()}));let r=this.getComponent();r.on("wizard-dismiss",this.dismiss),e.addEventListener("typo3-modal-hidden",(()=>{r.trigger("wizard-dismissed")})),e.addEventListener("typo3-modal-shown",(()=>{r.trigger("wizard-visible")}))}runSlideCallback(e,t){"function"==typeof e.callback&&e.callback(t,this.setup.settings,e.identifier)}addProgressBar(){let e,t=this.setup.$carousel.find(".carousel-item").length,s=Math.max(1,t),i=this.setup.$carousel.closest(".modal").find(".modal-footer");e=Math.round(100/s),this.setup.$carousel.data("initialStep",e).data("slideCount",s).data("realSlideCount",t).data("currentIndex",0).data("currentSlide",1),s>1&&i.prepend($("<div />",{class:"progress"}).append($("<div />",{role:"progressbar",class:"progress-bar","aria-valuemin":0,"aria-valuenow":e,"aria-valuemax":100}).width(e+"%").text(top.TYPO3.lang["wizard.progress"].replace("{0}","1").replace("{1}",s))))}generateSlides(){if(null!==this.setup.$carousel)return this.setup.$carousel;let e='<div class="carousel slide" data-bs-ride="false"><div class="carousel-inner" role="listbox">';for(let t of Object.values(this.setup.slides)){let s=t.content;"object"==typeof s&&(s=s.html()),e+='<div class="carousel-item" data-bs-slide="'+t.identifier+'">'+s+"</div>"}return e+="</div></div>",this.setup.$carousel=$(e),this.setup.$carousel.find(".carousel-item").first().addClass("active"),this.setup.$carousel}}let wizardObject;try{window.opener&&window.opener.TYPO3&&window.opener.TYPO3.Wizard&&(wizardObject=window.opener.TYPO3.Wizard),parent&&parent.window.TYPO3&&parent.window.TYPO3.Wizard&&(wizardObject=parent.window.TYPO3.Wizard),top&&top.TYPO3&&top.TYPO3.Wizard&&(wizardObject=top.TYPO3.Wizard)}catch{}wizardObject||(wizardObject=new Wizard,"undefined"!=typeof TYPO3&&(TYPO3.Wizard=wizardObject));export default wizardObject; \ No newline at end of file +import{SeverityEnum}from"@typo3/backend/enum/severity.js";import $ from"jquery";import{default as Modal}from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import Icons from"@typo3/backend/icons.js";class Wizard{constructor(){this.setup={slides:[],settings:{},forceSelection:!0,$carousel:null},this.originalSetup=$.extend(!0,{},this.setup)}set(e,t){return this.setup.settings[e]=t,this}addSlide(e,t,s="",i=SeverityEnum.info,a){const r={identifier:e,title:t,content:s,severity:i,callback:a};return this.setup.slides.push(r),this}addFinalProcessingSlide(e){return e||(e=()=>{this.dismiss()}),Icons.getIcon("spinner-circle-dark",Icons.sizes.large,null,null).then((t=>{const s=$("<div />",{class:"text-center"}).append(t);this.addSlide("final-processing-slide",top.TYPO3.lang["wizard.processing.title"],s[0].outerHTML,Severity.info,e)}))}show(){const e=this.generateSlides(),t=this.setup.slides[0],s=Modal.advanced({title:t.title,content:e,severity:t.severity,staticBackdrop:!0,buttons:[{text:top.TYPO3.lang["wizard.button.cancel"],active:!0,btnClass:"btn-default",name:"cancel",trigger:()=>{this.getComponent().trigger("wizard-dismiss")}},{text:top.TYPO3.lang["wizard.button.next"],btnClass:"btn-"+Severity.getCssClass(t.severity),name:"next"}],callback:()=>{this.addProgressBar(),this.initializeEvents(s)}});this.setup.forceSelection&&this.lockNextStep(),this.getComponent().on("wizard-visible",(()=>{this.runSlideCallback(t,this.setup.$carousel.find(".carousel-item").first())})).on("wizard-dismissed",(()=>{this.setup=$.extend(!0,{},this.originalSetup)}))}getComponent(){return null===this.setup.$carousel&&this.generateSlides(),this.setup.$carousel}dismiss(){Modal.dismiss()}lockNextStep(){const e=this.setup.$carousel.closest(".modal").find('button[name="next"]');return e.prop("disabled",!0),e}unlockNextStep(){const e=this.setup.$carousel.closest(".modal").find('button[name="next"]');return e.prop("disabled",!1),e}setForceSelection(e){this.setup.forceSelection=e}initializeEvents(e){const t=this.setup.$carousel.closest(".modal"),s=t.find(".modal-title"),i=t.find(".modal-footer"),a=i.find('button[name="next"]');a.on("click",(()=>{this.setup.$carousel.carousel("next")})),this.setup.$carousel.on("slide.bs.carousel",(()=>{const e=this.setup.$carousel.data("currentSlide")+1,r=this.setup.$carousel.data("currentIndex")+1;s.text(this.setup.slides[r].title),this.setup.$carousel.data("currentSlide",e),this.setup.$carousel.data("currentIndex",r),e>=this.setup.$carousel.data("realSlideCount")?(t.find(".modal-header .close").remove(),i.slideUp()):i.find(".progress-bar").width(this.setup.$carousel.data("initialStep")*e+"%").text(top.TYPO3.lang["wizard.progress"].replace("{0}",e).replace("{1}",this.setup.$carousel.data("slideCount"))),a.removeClass("btn-"+Severity.getCssClass(this.setup.slides[r-1].severity)).addClass("btn-"+Severity.getCssClass(this.setup.slides[r].severity)),t.removeClass("modal-severity-"+Severity.getCssClass(this.setup.slides[r-1].severity)).addClass("modal-severity-"+Severity.getCssClass(this.setup.slides[r].severity))})).on("slid.bs.carousel",(e=>{const t=this.setup.$carousel.data("currentIndex"),s=this.setup.slides[t];this.runSlideCallback(s,$(e.relatedTarget)),this.setup.forceSelection&&this.lockNextStep()}));const r=this.getComponent();r.on("wizard-dismiss",this.dismiss),e.addEventListener("typo3-modal-hidden",(()=>{r.trigger("wizard-dismissed")})),e.addEventListener("typo3-modal-shown",(()=>{r.trigger("wizard-visible")}))}runSlideCallback(e,t){"function"==typeof e.callback&&e.callback(t,this.setup.settings,e.identifier)}addProgressBar(){const e=this.setup.$carousel.find(".carousel-item").length,t=Math.max(1,e),s=Math.round(100/t),i=this.setup.$carousel.closest(".modal").find(".modal-footer");this.setup.$carousel.data("initialStep",s).data("slideCount",t).data("realSlideCount",e).data("currentIndex",0).data("currentSlide",1),t>1&&i.prepend($("<div />",{class:"progress"}).append($("<div />",{role:"progressbar",class:"progress-bar","aria-valuemin":0,"aria-valuenow":s,"aria-valuemax":100}).width(s+"%").text(top.TYPO3.lang["wizard.progress"].replace("{0}","1").replace("{1}",t))))}generateSlides(){if(null!==this.setup.$carousel)return this.setup.$carousel;let e='<div class="carousel slide" data-bs-ride="false"><div class="carousel-inner" role="listbox">';for(const t of Object.values(this.setup.slides)){let s=t.content;"object"==typeof s&&(s=s.html()),e+='<div class="carousel-item" data-bs-slide="'+t.identifier+'">'+s+"</div>"}return e+="</div></div>",this.setup.$carousel=$(e),this.setup.$carousel.find(".carousel-item").first().addClass("active"),this.setup.$carousel}}let wizardObject;try{window.opener&&window.opener.TYPO3&&window.opener.TYPO3.Wizard&&(wizardObject=window.opener.TYPO3.Wizard),parent&&parent.window.TYPO3&&parent.window.TYPO3.Wizard&&(wizardObject=parent.window.TYPO3.Wizard),top&&top.TYPO3&&top.TYPO3.Wizard&&(wizardObject=top.TYPO3.Wizard)}catch{}wizardObject||(wizardObject=new Wizard,"undefined"!=typeof TYPO3&&(TYPO3.Wizard=wizardObject));export default wizardObject; \ No newline at end of file diff --git a/typo3/sysext/backend/Tests/JavaScript/notification-test.js b/typo3/sysext/backend/Tests/JavaScript/notification-test.js index 37946c13f5b2..f1321bdad551 100644 --- a/typo3/sysext/backend/Tests/JavaScript/notification-test.js +++ b/typo3/sysext/backend/Tests/JavaScript/notification-test.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DeferredAction from"@typo3/backend/action-button/deferred-action.js";import ImmediateAction from"@typo3/backend/action-button/immediate-action.js";import Notification from"@typo3/backend/notification.js";import Icons from"@typo3/backend/icons.js";describe("TYPO3/CMS/Backend/Notification:",(()=>{beforeEach((()=>{const e=document.getElementById("alert-container");for(;null!==e&&e.firstChild;)e.removeChild(e.firstChild);spyOn(Icons,"getIcon").and.callFake((()=>Promise.resolve("X")))})),describe("can render notifications with dismiss after 1000ms",(()=>{for(let e of[{method:Notification.notice,title:"Notice message",message:"This notification describes a notice",class:"alert-notice"},{method:Notification.info,title:"Info message",message:"This notification describes an informative action",class:"alert-info"},{method:Notification.success,title:"Success message",message:"This notification describes a successful action",class:"alert-success"},{method:Notification.warning,title:"Warning message",message:"This notification describes a harmful action",class:"alert-warning"},{method:Notification.error,title:"Error message",message:"This notification describes an erroneous action",class:"alert-danger"}])it("can render a notification of type "+e.class,(async()=>{e.method(e.title,e.message,1),await document.querySelector("#alert-container typo3-notification-message:last-child").updateComplete;const t="div.alert."+e.class,o=document.querySelector(t);expect(o).not.toBe(null),expect(o.querySelector(".alert-title").textContent).toEqual(e.title),expect(o.querySelector(".alert-message").textContent).toEqual(e.message),await new Promise((e=>window.setTimeout(e,2e3))),expect(document.querySelector(t)).toBe(null)}))})),it("can render action buttons",(async()=>{Notification.info("Info message","Some text",1,[{label:"My action",action:new ImmediateAction((e=>e))},{label:"My other action",action:new DeferredAction((e=>e))}]),await document.querySelector("#alert-container typo3-notification-message:last-child").updateComplete;const e=document.querySelector("div.alert");expect(e.querySelector(".alert-actions")).not.toBe(null),expect(e.querySelectorAll(".alert-actions a").length).toEqual(2),expect(e.querySelectorAll(".alert-actions a")[0].textContent).toEqual("My action"),expect(e.querySelectorAll(".alert-actions a")[1].textContent).toEqual("My other action")})),it("immediate action is called",(async()=>{const e={callback:()=>{}};spyOn(e,"callback").and.callThrough(),Notification.info("Info message","Some text",1,[{label:"My immediate action",action:new ImmediateAction(e.callback)}]),await document.querySelector("#alert-container typo3-notification-message:last-child").updateComplete;document.querySelector("div.alert").querySelector(".alert-actions a").click(),await document.querySelector("#alert-container typo3-notification-message:last-child").updateComplete,expect(e.callback).toHaveBeenCalled()}))})); \ No newline at end of file +import DeferredAction from"@typo3/backend/action-button/deferred-action.js";import ImmediateAction from"@typo3/backend/action-button/immediate-action.js";import Notification from"@typo3/backend/notification.js";import Icons from"@typo3/backend/icons.js";describe("TYPO3/CMS/Backend/Notification:",(()=>{beforeEach((()=>{const e=document.getElementById("alert-container");for(;null!==e&&e.firstChild;)e.removeChild(e.firstChild);spyOn(Icons,"getIcon").and.callFake((()=>Promise.resolve("X")))})),describe("can render notifications with dismiss after 1000ms",(()=>{for(const e of[{method:Notification.notice,title:"Notice message",message:"This notification describes a notice",class:"alert-notice"},{method:Notification.info,title:"Info message",message:"This notification describes an informative action",class:"alert-info"},{method:Notification.success,title:"Success message",message:"This notification describes a successful action",class:"alert-success"},{method:Notification.warning,title:"Warning message",message:"This notification describes a harmful action",class:"alert-warning"},{method:Notification.error,title:"Error message",message:"This notification describes an erroneous action",class:"alert-danger"}])it("can render a notification of type "+e.class,(async()=>{e.method(e.title,e.message,1),await document.querySelector("#alert-container typo3-notification-message:last-child").updateComplete;const t="div.alert."+e.class,o=document.querySelector(t);expect(o).not.toBe(null),expect(o.querySelector(".alert-title").textContent).toEqual(e.title),expect(o.querySelector(".alert-message").textContent).toEqual(e.message),await new Promise((e=>window.setTimeout(e,2e3))),expect(document.querySelector(t)).toBe(null)}))})),it("can render action buttons",(async()=>{Notification.info("Info message","Some text",1,[{label:"My action",action:new ImmediateAction((e=>e))},{label:"My other action",action:new DeferredAction((e=>e))}]),await document.querySelector("#alert-container typo3-notification-message:last-child").updateComplete;const e=document.querySelector("div.alert");expect(e.querySelector(".alert-actions")).not.toBe(null),expect(e.querySelectorAll(".alert-actions a").length).toEqual(2),expect(e.querySelectorAll(".alert-actions a")[0].textContent).toEqual("My action"),expect(e.querySelectorAll(".alert-actions a")[1].textContent).toEqual("My other action")})),it("immediate action is called",(async()=>{const e={callback:()=>{}};spyOn(e,"callback").and.callThrough(),Notification.info("Info message","Some text",1,[{label:"My immediate action",action:new ImmediateAction(e.callback)}]),await document.querySelector("#alert-container typo3-notification-message:last-child").updateComplete;document.querySelector("div.alert").querySelector(".alert-actions a").click(),await document.querySelector("#alert-container typo3-notification-message:last-child").updateComplete,expect(e.callback).toHaveBeenCalled()}))})); \ No newline at end of file diff --git a/typo3/sysext/beuser/Resources/Public/JavaScript/permissions.js b/typo3/sysext/beuser/Resources/Public/JavaScript/permissions.js index 2097093f444b..9871b366b44a 100644 --- a/typo3/sysext/beuser/Resources/Public/JavaScript/permissions.js +++ b/typo3/sysext/beuser/Resources/Public/JavaScript/permissions.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import RegularEvent from"@typo3/core/event/regular-event.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";class Permissions{constructor(){this.options={containerSelector:"#typo3-permissionList",editControllerSelector:"#PermissionControllerEdit"},this.ajaxUrl=TYPO3.settings.ajaxUrls.user_access_permissions,this.initializeCheckboxGroups(),this.initializeEvents()}static setPermissionCheckboxes(e,t){const a=document.querySelectorAll(`input[type="checkbox"][name^="${e}"]`);for(let e of a){const a=parseInt(e.value,10);e.checked=(t&a)===a}}static updatePermissionValue(e,t){let a=0;const o=document.querySelectorAll(`input[type="checkbox"][name^="${e}"]:checked`);for(let e of o)a|=parseInt(e.value,10);document.forms.namedItem("editform")[t].value=a|("check[perms_user]"===e?1:0)}setPermissions(e){let t=e.dataset.page,a=e.dataset.who;new AjaxRequest(this.ajaxUrl).post({page:t,who:a,permissions:e.dataset.permissions,mode:e.dataset.mode,bits:e.dataset.bits}).then((async e=>{const o=await e.resolve();document.getElementById(t+"_"+a).outerHTML=o}))}toggleEditLock(e){let t=e.dataset.page;new AjaxRequest(this.ajaxUrl).post({action:"toggle_edit_lock",page:t,editLockState:e.dataset.lockstate}).then((async e=>{document.getElementById("el_"+t).outerHTML=await e.resolve()}))}changeOwner(e){let t=e.dataset.page;const a=document.getElementById("o_"+t);new AjaxRequest(this.ajaxUrl).post({action:"change_owner",page:t,ownerUid:e.dataset.owner,newOwnerUid:a.getElementsByTagName("select")[0].value}).then((async e=>{a.outerHTML=await e.resolve()}))}showChangeOwnerSelector(e){let t=e.dataset.page;new AjaxRequest(this.ajaxUrl).post({action:"show_change_owner_selector",page:t,ownerUid:e.dataset.owner,username:e.dataset.username}).then((async e=>{document.getElementById("o_"+t).outerHTML=await e.resolve()}))}restoreOwner(e){const t=e.dataset.page,a=e.dataset.username??e.dataset.ifNotSet,o=document.createElement("span");o.setAttribute("id",`o_${t}`);const s=document.createElement("button");s.classList.add("ug_selector","changeowner","btn","btn-sm","btn-link"),s.setAttribute("type","button"),s.setAttribute("data-page",t),s.setAttribute("data-owner",e.dataset.owner),s.setAttribute("data-username",a),s.innerText=a,o.appendChild(s);const n=document.getElementById("o_"+t);n.parentNode.replaceChild(o,n)}restoreGroup(e){const t=e.dataset.page,a=e.dataset.groupname??e.dataset.ifNotSet,o=document.createElement("span");o.setAttribute("id",`g_${t}`);const s=document.createElement("button");s.classList.add("ug_selector","changegroup","btn","btn-sm","btn-link"),s.setAttribute("type","button"),s.setAttribute("data-page",t),s.setAttribute("data-group-id",e.dataset.groupId),s.setAttribute("data-groupname",a),s.innerText=a,o.appendChild(s);const n=document.getElementById("g_"+t);n.parentNode.replaceChild(o,n)}changeGroup(e){let t=e.dataset.page;const a=document.getElementById("g_"+t);new AjaxRequest(this.ajaxUrl).post({action:"change_group",page:t,groupUid:e.dataset.groupId,newGroupUid:a.getElementsByTagName("select")[0].value}).then((async e=>{a.outerHTML=await e.resolve()}))}showChangeGroupSelector(e){let t=e.dataset.page;new AjaxRequest(this.ajaxUrl).post({action:"show_change_group_selector",page:t,groupUid:e.dataset.groupId,groupname:e.dataset.groupname}).then((async e=>{document.getElementById("g_"+t).outerHTML=await e.resolve()}))}initializeCheckboxGroups(){document.querySelectorAll("[data-checkbox-group]").forEach((e=>{const t=e.dataset.checkboxGroup,a=parseInt(e.value,10);Permissions.setPermissionCheckboxes(t,a)}))}initializeEvents(){const e=document.querySelector(this.options.containerSelector),t=document.querySelector(this.options.editControllerSelector);null!==e&&(new RegularEvent("click",((e,t)=>{e.preventDefault(),this.setPermissions(t)})).delegateTo(e,".change-permission"),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.toggleEditLock(t)})).delegateTo(e,".editlock"),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.showChangeOwnerSelector(t)})).delegateTo(e,".changeowner"),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.showChangeGroupSelector(t)})).delegateTo(e,".changegroup"),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.restoreOwner(t)})).delegateTo(e,".restoreowner"),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.changeOwner(t)})).delegateTo(e,".saveowner"),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.restoreGroup(t)})).delegateTo(e,".restoregroup"),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.changeGroup(t)})).delegateTo(e,".savegroup")),null!==t&&new RegularEvent("click",((e,t)=>{const a=t.dataset.checkChangePermissions.split(",").map((e=>e.trim()));Permissions.updatePermissionValue.apply(this,a)})).delegateTo(t,"[data-check-change-permissions]")}}export default new Permissions; \ No newline at end of file +import RegularEvent from"@typo3/core/event/regular-event.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";class Permissions{constructor(){this.options={containerSelector:"#typo3-permissionList",editControllerSelector:"#PermissionControllerEdit"},this.ajaxUrl=TYPO3.settings.ajaxUrls.user_access_permissions,this.initializeCheckboxGroups(),this.initializeEvents()}static setPermissionCheckboxes(e,t){const a=document.querySelectorAll(`input[type="checkbox"][name^="${e}"]`);for(const e of a){const a=parseInt(e.value,10);e.checked=(t&a)===a}}static updatePermissionValue(e,t){let a=0;const o=document.querySelectorAll(`input[type="checkbox"][name^="${e}"]:checked`);for(const e of o)a|=parseInt(e.value,10);document.forms.namedItem("editform")[t].value=a|("check[perms_user]"===e?1:0)}setPermissions(e){const t=e.dataset.page,a=e.dataset.who;new AjaxRequest(this.ajaxUrl).post({page:t,who:a,permissions:e.dataset.permissions,mode:e.dataset.mode,bits:e.dataset.bits}).then((async e=>{const o=await e.resolve();document.getElementById(t+"_"+a).outerHTML=o}))}toggleEditLock(e){const t=e.dataset.page;new AjaxRequest(this.ajaxUrl).post({action:"toggle_edit_lock",page:t,editLockState:e.dataset.lockstate}).then((async e=>{document.getElementById("el_"+t).outerHTML=await e.resolve()}))}changeOwner(e){const t=e.dataset.page,a=document.getElementById("o_"+t);new AjaxRequest(this.ajaxUrl).post({action:"change_owner",page:t,ownerUid:e.dataset.owner,newOwnerUid:a.getElementsByTagName("select")[0].value}).then((async e=>{a.outerHTML=await e.resolve()}))}showChangeOwnerSelector(e){const t=e.dataset.page;new AjaxRequest(this.ajaxUrl).post({action:"show_change_owner_selector",page:t,ownerUid:e.dataset.owner,username:e.dataset.username}).then((async e=>{document.getElementById("o_"+t).outerHTML=await e.resolve()}))}restoreOwner(e){const t=e.dataset.page,a=e.dataset.username??e.dataset.ifNotSet,o=document.createElement("span");o.setAttribute("id",`o_${t}`);const s=document.createElement("button");s.classList.add("ug_selector","changeowner","btn","btn-sm","btn-link"),s.setAttribute("type","button"),s.setAttribute("data-page",t),s.setAttribute("data-owner",e.dataset.owner),s.setAttribute("data-username",a),s.innerText=a,o.appendChild(s);const n=document.getElementById("o_"+t);n.parentNode.replaceChild(o,n)}restoreGroup(e){const t=e.dataset.page,a=e.dataset.groupname??e.dataset.ifNotSet,o=document.createElement("span");o.setAttribute("id",`g_${t}`);const s=document.createElement("button");s.classList.add("ug_selector","changegroup","btn","btn-sm","btn-link"),s.setAttribute("type","button"),s.setAttribute("data-page",t),s.setAttribute("data-group-id",e.dataset.groupId),s.setAttribute("data-groupname",a),s.innerText=a,o.appendChild(s);const n=document.getElementById("g_"+t);n.parentNode.replaceChild(o,n)}changeGroup(e){const t=e.dataset.page,a=document.getElementById("g_"+t);new AjaxRequest(this.ajaxUrl).post({action:"change_group",page:t,groupUid:e.dataset.groupId,newGroupUid:a.getElementsByTagName("select")[0].value}).then((async e=>{a.outerHTML=await e.resolve()}))}showChangeGroupSelector(e){const t=e.dataset.page;new AjaxRequest(this.ajaxUrl).post({action:"show_change_group_selector",page:t,groupUid:e.dataset.groupId,groupname:e.dataset.groupname}).then((async e=>{document.getElementById("g_"+t).outerHTML=await e.resolve()}))}initializeCheckboxGroups(){document.querySelectorAll("[data-checkbox-group]").forEach((e=>{const t=e.dataset.checkboxGroup,a=parseInt(e.value,10);Permissions.setPermissionCheckboxes(t,a)}))}initializeEvents(){const e=document.querySelector(this.options.containerSelector),t=document.querySelector(this.options.editControllerSelector);null!==e&&(new RegularEvent("click",((e,t)=>{e.preventDefault(),this.setPermissions(t)})).delegateTo(e,".change-permission"),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.toggleEditLock(t)})).delegateTo(e,".editlock"),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.showChangeOwnerSelector(t)})).delegateTo(e,".changeowner"),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.showChangeGroupSelector(t)})).delegateTo(e,".changegroup"),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.restoreOwner(t)})).delegateTo(e,".restoreowner"),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.changeOwner(t)})).delegateTo(e,".saveowner"),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.restoreGroup(t)})).delegateTo(e,".restoregroup"),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.changeGroup(t)})).delegateTo(e,".savegroup")),null!==t&&new RegularEvent("click",((e,t)=>{const a=t.dataset.checkChangePermissions.split(",").map((e=>e.trim()));Permissions.updatePermissionValue.apply(this,a)})).delegateTo(t,"[data-check-change-permissions]")}}export default new Permissions; \ No newline at end of file diff --git a/typo3/sysext/core/Resources/Public/JavaScript/ajax/input-transformer.js b/typo3/sysext/core/Resources/Public/JavaScript/ajax/input-transformer.js index cd1999ab2d3a..d1b307d6b0cb 100644 --- a/typo3/sysext/core/Resources/Public/JavaScript/ajax/input-transformer.js +++ b/typo3/sysext/core/Resources/Public/JavaScript/ajax/input-transformer.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -export class InputTransformer{static byHeader(t,e={}){return e.hasOwnProperty("Content-Type")&&e["Content-Type"].includes("application/json")?JSON.stringify(t):InputTransformer.toFormData(t)}static toFormData(t){const e=InputTransformer.filter(InputTransformer.flattenObject(t)),r=new FormData;for(const[t,n]of Object.entries(e))r.set(t,n);return r}static toSearchParams(t){if("string"==typeof t)return t;if(t instanceof Array)return t.join("&");const e=InputTransformer.filter(InputTransformer.flattenObject(t)),r=new URLSearchParams;for(const[t,n]of Object.entries(e))r.set(t,n);return decodeURI(r.toString())}static flattenObject(t,e=""){return Object.keys(t).reduce(((r,n)=>{const a=e.length?e+"[":"",o=e.length?"]":"";return"object"==typeof t[n]?Object.assign(r,InputTransformer.flattenObject(t[n],a+n+o)):r[a+n+o]=t[n],r}),{})}static filter(t){return Object.keys(t).forEach((e=>{void 0===t[e]&&delete t[e]})),t}} \ No newline at end of file +export class InputTransformer{static byHeader(t,e={}){return"Content-Type"in e&&e["Content-Type"].includes("application/json")?JSON.stringify(t):InputTransformer.toFormData(t)}static toFormData(t){const e=InputTransformer.filter(InputTransformer.flattenObject(t)),r=new FormData;for(const[t,n]of Object.entries(e))r.set(t,n);return r}static toSearchParams(t){if("string"==typeof t)return t;if(t instanceof Array)return t.join("&");const e=InputTransformer.filter(InputTransformer.flattenObject(t)),r=new URLSearchParams;for(const[t,n]of Object.entries(e))r.set(t,n);return decodeURI(r.toString())}static flattenObject(t,e=""){return Object.keys(t).reduce(((r,n)=>{const a=e.length?e+"[":"",o=e.length?"]":"";return"object"==typeof t[n]?Object.assign(r,InputTransformer.flattenObject(t[n],a+n+o)):r[a+n+o]=t[n],r}),{})}static filter(t){return Object.keys(t).forEach((e=>{void 0===t[e]&&delete t[e]})),t}} \ No newline at end of file diff --git a/typo3/sysext/core/Resources/Public/JavaScript/authentication/mfa-provider/totp.js b/typo3/sysext/core/Resources/Public/JavaScript/authentication/mfa-provider/totp.js index 978ec3603f31..1c8444320f2a 100644 --- a/typo3/sysext/core/Resources/Public/JavaScript/authentication/mfa-provider/totp.js +++ b/typo3/sysext/core/Resources/Public/JavaScript/authentication/mfa-provider/totp.js @@ -10,7 +10,7 @@ * * The TYPO3 project - inspiring people to share! */ -var Selectors,__decorate=function(t,e,o,r){var l,p=arguments.length,n=p<3?e:null===r?r=Object.getOwnPropertyDescriptor(e,o):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)n=Reflect.decorate(t,e,o,r);else for(var i=t.length-1;i>=0;i--)(l=t[i])&&(n=(p<3?l(n):p>3?l(e,o,n):l(e,o))||n);return p>3&&n&&Object.defineProperty(e,o,n),n};import{html,LitElement}from"lit";import{customElement,property}from"lit/decorators.js";import Modal from"@typo3/backend/modal.js";!function(t){t.modalBody=".t3js-modal-body"}(Selectors||(Selectors={}));let MfaTotpUrlButton=class extends LitElement{constructor(){super(),this.addEventListener("click",(t=>{t.preventDefault(),this.showTotpAuthUrlModal()}))}render(){return html`<slot></slot>`}showTotpAuthUrlModal(){Modal.advanced({title:this.title,content:html` +var __decorate=function(t,e,o,r){var p,l=arguments.length,n=l<3?e:null===r?r=Object.getOwnPropertyDescriptor(e,o):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)n=Reflect.decorate(t,e,o,r);else for(var i=t.length-1;i>=0;i--)(p=t[i])&&(n=(l<3?p(n):l>3?p(e,o,n):p(e,o))||n);return l>3&&n&&Object.defineProperty(e,o,n),n};import{html,LitElement}from"lit";import{customElement,property}from"lit/decorators.js";import Modal from"@typo3/backend/modal.js";let MfaTotpUrlButton=class extends LitElement{constructor(){super(),this.addEventListener("click",(t=>{t.preventDefault(),this.showTotpAuthUrlModal()}))}render(){return html`<slot></slot>`}showTotpAuthUrlModal(){Modal.advanced({title:this.title,content:html` <p>${this.description}</p> <pre>${this.url}</pre> - `,buttons:[{trigger:()=>Modal.dismiss(),text:this.ok||"OK",active:!0,btnClass:"btn-default",name:"ok"}]})}};__decorate([property({type:String})],MfaTotpUrlButton.prototype,"url",void 0),__decorate([property({type:String})],MfaTotpUrlButton.prototype,"title",void 0),__decorate([property({type:String})],MfaTotpUrlButton.prototype,"description",void 0),__decorate([property({type:String})],MfaTotpUrlButton.prototype,"ok",void 0),MfaTotpUrlButton=__decorate([customElement("typo3-mfa-totp-url-info-button")],MfaTotpUrlButton); \ No newline at end of file + `,buttons:[{trigger:()=>Modal.dismiss(),text:this.ok||"OK",active:!0,btnClass:"btn-default",name:"ok"}]})}};__decorate([property({type:String})],MfaTotpUrlButton.prototype,"url",void 0),__decorate([property({type:String})],MfaTotpUrlButton.prototype,"title",void 0),__decorate([property({type:String})],MfaTotpUrlButton.prototype,"description",void 0),__decorate([property({type:String})],MfaTotpUrlButton.prototype,"ok",void 0),MfaTotpUrlButton=__decorate([customElement("typo3-mfa-totp-url-info-button")],MfaTotpUrlButton);export{MfaTotpUrlButton}; \ No newline at end of file diff --git a/typo3/sysext/core/Resources/Public/JavaScript/event/request-animation-frame-event.js b/typo3/sysext/core/Resources/Public/JavaScript/event/request-animation-frame-event.js index 4a4bfd2d1c57..8ad5b3bb026a 100644 --- a/typo3/sysext/core/Resources/Public/JavaScript/event/request-animation-frame-event.js +++ b/typo3/sysext/core/Resources/Public/JavaScript/event/request-animation-frame-event.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import RegularEvent from"@typo3/core/event/regular-event.js";class RequestAnimationFrameEvent extends RegularEvent{constructor(e,t){super(e,t),this.callback=this.req(this.callback)}req(e){let t=null;return()=>{const n=this,a=arguments;t&&window.cancelAnimationFrame(t),t=window.requestAnimationFrame((function(){e.apply(n,a)}))}}}export default RequestAnimationFrameEvent; \ No newline at end of file +import RegularEvent from"@typo3/core/event/regular-event.js";class RequestAnimationFrameEvent extends RegularEvent{constructor(e,t){super(e,t),this.callback=this.req(this.callback)}req(e){let t=null;return(...n)=>{t&&window.cancelAnimationFrame(t),t=window.requestAnimationFrame((()=>{e.apply(this,n)}))}}}export default RequestAnimationFrameEvent; \ No newline at end of file diff --git a/typo3/sysext/core/Resources/Public/JavaScript/java-script-item-processor.js b/typo3/sysext/core/Resources/Public/JavaScript/java-script-item-processor.js index 64c983abe444..5c0cfd335ea8 100644 --- a/typo3/sysext/core/Resources/Public/JavaScript/java-script-item-processor.js +++ b/typo3/sysext/core/Resources/Public/JavaScript/java-script-item-processor.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -const FLAG_USE_REQUIRE_JS=1,FLAG_USE_IMPORTMAP=2,FLAG_USE_TOP_WINDOW=16,deniedProperties=["__proto__","prototype","constructor"],allowedJavaScriptItemTypes=["assign","invoke","instance"];let useShim=!1;const moduleImporter=e=>useShim?window.importShim(e):import(e).catch((()=>(useShim=!0,moduleImporter(e))));export function loadModule(e){if(!e.name)throw new Error("JavaScript module name is required");if(2==(2&e.flags)){if(16&e.flags){const t=new CustomEvent("typo3:import-javascript-module",{detail:{specifier:e.name,importPromise:null}});return top.document.dispatchEvent(t),t.detail.importPromise||Promise.reject(new Error("Top-level import failed"))}return moduleImporter(e.name)}if(1==(1&e.flags))return new Promise(((t,r)=>{(16==(16&e.flags)?top.window:window).require([e.name],(e=>t(e)),(e=>r(e)))}));throw new Error("Unknown JavaScript module type")}export function resolveSubjectRef(e,t){const r=t.exportName;return"string"==typeof r?e[r]:1==(1&t.flags)?e:e.default}export function executeJavaScriptModuleInstruction(e){if(!e.name)throw new Error("JavaScript module name is required");if(!e.items)return loadModule(e);const t=e.items.filter((e=>allowedJavaScriptItemTypes.includes(e.type))).map((t=>"assign"===t.type?r=>{mergeRecursive(resolveSubjectRef(r,e),t.assignments)}:"invoke"===t.type?r=>{const o=resolveSubjectRef(r,e);return"method"in t&&t.method?o[t.method].apply(o,t.args):o(...t.args)}:"instance"===t.type?r=>{const o=[null].concat(t.args),n=resolveSubjectRef(r,e);return new(n.bind.apply(n,o))}:e=>{}));return loadModule(e).then((e=>t.map((t=>t.call(null,e)))))}function isObjectInstance(e){return e instanceof Object&&!(e instanceof Array)}function mergeRecursive(e,t){Object.keys(t).forEach((r=>{if(-1!==deniedProperties.indexOf(r))throw new Error("Property "+r+" is not allowed");isObjectInstance(t[r])&&void 0!==e[r]?mergeRecursive(e[r],t[r]):Object.assign(e,{[r]:t[r]})}))}export class JavaScriptItemProcessor{constructor(){this.invokableNames=["globalAssignment","javaScriptModuleInstruction"]}processItems(e){e.forEach((e=>this.invoke(e.type,e.payload)))}invoke(e,t){if(!this.invokableNames.includes(e)||"function"!=typeof this[e])throw new Error('Unknown handler name "'+e+'"');this[e].call(this,t)}globalAssignment(e){mergeRecursive(window,e)}javaScriptModuleInstruction(e){executeJavaScriptModuleInstruction(e)}} \ No newline at end of file +const FLAG_USE_REQUIRE_JS=1,FLAG_USE_IMPORTMAP=2,FLAG_USE_TOP_WINDOW=16,deniedProperties=["__proto__","prototype","constructor"],allowedJavaScriptItemTypes=["assign","invoke","instance"];let useShim=!1;const moduleImporter=e=>useShim?window.importShim(e):import(e).catch((()=>(useShim=!0,moduleImporter(e))));export function loadModule(e){if(!e.name)throw new Error("JavaScript module name is required");if(2==(2&e.flags)){if(16&e.flags){const t=new CustomEvent("typo3:import-javascript-module",{detail:{specifier:e.name,importPromise:null}});return top.document.dispatchEvent(t),t.detail.importPromise||Promise.reject(new Error("Top-level import failed"))}return moduleImporter(e.name)}if(1==(1&e.flags))return new Promise(((t,r)=>{(16==(16&e.flags)?top.window:window).require([e.name],(e=>t(e)),(e=>r(e)))}));throw new Error("Unknown JavaScript module type")}export function resolveSubjectRef(e,t){const r=t.exportName;return"string"==typeof r?e[r]:1==(1&t.flags)?e:e.default}export function executeJavaScriptModuleInstruction(e){if(!e.name)throw new Error("JavaScript module name is required");if(!e.items)return loadModule(e);const t=e.items.filter((e=>allowedJavaScriptItemTypes.includes(e.type))).map((t=>"assign"===t.type?r=>{mergeRecursive(resolveSubjectRef(r,e),t.assignments)}:"invoke"===t.type?r=>{const o=resolveSubjectRef(r,e);return"method"in t&&t.method?o[t.method](...t.args):o(...t.args)}:"instance"===t.type?r=>{const o=[null].concat(t.args);return new(resolveSubjectRef(r,e).bind(...o))}:()=>{}));return loadModule(e).then((e=>t.map((t=>t.call(null,e)))))}function isObjectInstance(e){return e instanceof Object&&!(e instanceof Array)}function mergeRecursive(e,t){Object.keys(t).forEach((r=>{if(-1!==deniedProperties.indexOf(r))throw new Error("Property "+r+" is not allowed");isObjectInstance(t[r])&&void 0!==e[r]?mergeRecursive(e[r],t[r]):Object.assign(e,{[r]:t[r]})}))}export class JavaScriptItemProcessor{constructor(){this.invokableNames=["globalAssignment","javaScriptModuleInstruction"]}processItems(e){e.forEach((e=>this.invoke(e.type,e.payload)))}invoke(e,t){if(!this.invokableNames.includes(e)||"function"!=typeof this[e])throw new Error('Unknown handler name "'+e+'"');this[e].call(this,t)}globalAssignment(e){mergeRecursive(window,e)}javaScriptModuleInstruction(e){executeJavaScriptModuleInstruction(e)}} \ No newline at end of file diff --git a/typo3/sysext/core/Resources/Public/JavaScript/security-utility.js b/typo3/sysext/core/Resources/Public/JavaScript/security-utility.js index d3635b6f5131..ade3ee211961 100644 --- a/typo3/sysext/core/Resources/Public/JavaScript/security-utility.js +++ b/typo3/sysext/core/Resources/Public/JavaScript/security-utility.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -class SecurityUtility{constructor(e=document){this.documentRef=e}getRandomHexValue(e){if(e<=0||e!==Math.ceil(e))throw new SyntaxError("Length must be a positive integer");const t=new Uint8Array(Math.ceil(e/2));return crypto.getRandomValues(t),Array.from(t).map((e=>e.toString(16).padStart(2,"0"))).join("").substr(0,e)}encodeHtml(e,t=!0){let r=this.createAnvil();return t||(e=e.replace(/&[#A-Za-z0-9]+;/g,(e=>(r.innerHTML=e,r.innerText)))),r.innerText=e,r.innerHTML.replace(/"/g,""").replace(/'/g,"'")}stripHtml(e){return(new DOMParser).parseFromString(e,"text/html").body.textContent||""}debug(e){e!==this.encodeHtml(e)&&console.warn("XSS?!",e)}createAnvil(){return this.documentRef.createElement("span")}}export default SecurityUtility; \ No newline at end of file +class SecurityUtility{constructor(e=document){this.documentRef=e}getRandomHexValue(e){if(e<=0||e!==Math.ceil(e))throw new SyntaxError("Length must be a positive integer");const t=new Uint8Array(Math.ceil(e/2));return crypto.getRandomValues(t),Array.from(t).map((e=>e.toString(16).padStart(2,"0"))).join("").substr(0,e)}encodeHtml(e,t=!0){const r=this.createAnvil();return t||(e=e.replace(/&[#A-Za-z0-9]+;/g,(e=>(r.innerHTML=e,r.innerText)))),r.innerText=e,r.innerHTML.replace(/"/g,""").replace(/'/g,"'")}stripHtml(e){return(new DOMParser).parseFromString(e,"text/html").body.textContent||""}debug(e){e!==this.encodeHtml(e)&&console.warn("XSS?!",e)}createAnvil(){return this.documentRef.createElement("span")}}export default SecurityUtility; \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Recycler/RecyclerModuleCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Recycler/RecyclerModuleCest.php index 6db7671d6b1d..d053b9bbfbb6 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/Recycler/RecyclerModuleCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/Recycler/RecyclerModuleCest.php @@ -96,6 +96,7 @@ final class RecyclerModuleCest // Close all notifications to avoid click interceptions $I->click('#alert-container .close'); + $I->switchToContentFrame(); $I->click('a[title="Edit page properties"]'); $I->click('a[title="Delete"]'); diff --git a/typo3/sysext/core/Tests/JavaScript/ajax/ajax-request-test.js b/typo3/sysext/core/Tests/JavaScript/ajax/ajax-request-test.js index 05c4d06ebde1..e9d9aa55cbc4 100644 --- a/typo3/sysext/core/Tests/JavaScript/ajax/ajax-request-test.js +++ b/typo3/sysext/core/Tests/JavaScript/ajax/ajax-request-test.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import AjaxRequest from"@typo3/core/ajax/ajax-request.js";describe("@typo3/core/ajax/ajax-request",(()=>{let e;beforeEach((()=>{const t=new Promise(((t,o)=>{e={resolve:t,reject:o}}));spyOn(window,"fetch").and.returnValue(t)})),it("sends GET request",(()=>{new AjaxRequest("https://example.com").get(),expect(window.fetch).toHaveBeenCalledWith("https://example.com/",jasmine.objectContaining({method:"GET"}))}));for(let e of["POST","PUT","DELETE"])describe(`send a ${e} request`,(()=>{for(let t of function*(){yield["object as payload",e,{foo:"bar",bar:"baz",nested:{works:"yes"}},()=>{const e=new FormData;return e.set("foo","bar"),e.set("bar","baz"),e.set("nested[works]","yes"),e},{}],yield["JSON object as payload",e,{foo:"bar",bar:"baz",nested:{works:"yes"}},()=>JSON.stringify({foo:"bar",bar:"baz",nested:{works:"yes"}}),{"Content-Type":"application/json"}],yield["JSON string as payload",e,JSON.stringify({foo:"bar",bar:"baz",nested:{works:"yes"}}),()=>JSON.stringify({foo:"bar",bar:"baz",nested:{works:"yes"}}),{"Content-Type":"application/json"}]}()){let[e,o,a,r,n]=t;const s=o.toLowerCase();it(`with ${e}`,(e=>{new AjaxRequest("https://example.com")[s](a,{headers:n}),expect(window.fetch).toHaveBeenCalledWith("https://example.com/",jasmine.objectContaining({method:o,body:r()})),e()}))}}));describe("send GET requests",(()=>{for(let t of function*(){yield["plaintext","foobar huselpusel",{},(e,t)=>{expect("string"==typeof e).toBeTruthy(),expect(e).toEqual(t)}],yield["JSON",JSON.stringify({foo:"bar",baz:"bencer"}),{"Content-Type":"application/json"},(e,t)=>{expect("object"==typeof e).toBeTruthy(),expect(JSON.stringify(e)).toEqual(t)}],yield["JSON with utf-8",JSON.stringify({foo:"bar",baz:"bencer"}),{"Content-Type":"application/json; charset=utf-8"},(e,t)=>{expect("object"==typeof e).toBeTruthy(),expect(JSON.stringify(e)).toEqual(t)}]}()){let[o,a,r,n]=t;it("receives a "+o+" response",(t=>{const o=new Response(a,{headers:r});e.resolve(o),new AjaxRequest("https://example.com").get().then((async e=>{const o=await e.resolve();expect(window.fetch).toHaveBeenCalledWith("https://example.com/",jasmine.objectContaining({method:"GET"})),n(o,a),t()}))}))}})),describe("send requests with different input urls",(()=>{for(let e of function*(){yield["absolute url with domain","https://example.com",{},"https://example.com/"],yield["absolute url with domain, with query parameter","https://example.com",{foo:"bar",bar:{baz:"bencer"}},"https://example.com/?foo=bar&bar[baz]=bencer"],yield["absolute url without domain","/foo/bar",{},window.location.origin+"/foo/bar"],yield["absolute url without domain, with query parameter","/foo/bar",{foo:"bar",bar:{baz:"bencer"}},window.location.origin+"/foo/bar?foo=bar&bar[baz]=bencer"],yield["relative url without domain","foo/bar",{},window.location.origin+"/foo/bar"],yield["relative url without domain, with query parameter","foo/bar",{foo:"bar",bar:{baz:"bencer"}},window.location.origin+"/foo/bar?foo=bar&bar[baz]=bencer"],yield["fallback to current script if not defined","?foo=bar&baz=bencer",{},window.location.origin+window.location.pathname+"?foo=bar&baz=bencer"]}()){let[t,o,a,r]=e;it("with "+t,(()=>{new AjaxRequest(o).withQueryArguments(a).get(),expect(window.fetch).toHaveBeenCalledWith(r,jasmine.objectContaining({method:"GET"}))}))}})),describe("send requests with query arguments",(()=>{for(let e of function*(){yield["single level of arguments",{foo:"bar",bar:"baz"},"https://example.com/?foo=bar&bar=baz"],yield["nested arguments",{foo:"bar",bar:{baz:"bencer"}},"https://example.com/?foo=bar&bar[baz]=bencer"],yield["string argument","hello=world&foo=bar","https://example.com/?hello=world&foo=bar"],yield["array of arguments",["foo=bar","husel=pusel"],"https://example.com/?foo=bar&husel=pusel"],yield["object with array",{foo:["bar","baz"]},"https://example.com/?foo[0]=bar&foo[1]=baz"],yield["complex object",{foo:"bar",nested:{husel:"pusel",bar:"baz",array:["5","6"]},array:["1","2"]},"https://example.com/?foo=bar&nested[husel]=pusel&nested[bar]=baz&nested[array][0]=5&nested[array][1]=6&array[0]=1&array[1]=2"],yield["complex, deeply nested object",{foo:"bar",nested:{husel:"pusel",bar:"baz",array:["5","6"],deep_nested:{husel:"pusel",bar:"baz",array:["5","6"]}},array:["1","2"]},"https://example.com/?foo=bar&nested[husel]=pusel&nested[bar]=baz&nested[array][0]=5&nested[array][1]=6&nested[deep_nested][husel]=pusel&nested[deep_nested][bar]=baz&nested[deep_nested][array][0]=5&nested[deep_nested][array][1]=6&array[0]=1&array[1]=2"]}()){let[t,o,a]=e;it("with "+t,(()=>{new AjaxRequest("https://example.com/").withQueryArguments(o).get(),expect(window.fetch).toHaveBeenCalledWith(a,jasmine.objectContaining({method:"GET"}))}))}}))})); \ No newline at end of file +import AjaxRequest from"@typo3/core/ajax/ajax-request.js";describe("@typo3/core/ajax/ajax-request",(()=>{let e;beforeEach((()=>{const t=new Promise(((t,o)=>{e={resolve:t,reject:o}}));spyOn(window,"fetch").and.returnValue(t)})),it("sends GET request",(()=>{new AjaxRequest("https://example.com").get(),expect(window.fetch).toHaveBeenCalledWith("https://example.com/",jasmine.objectContaining({method:"GET"}))}));for(const e of["POST","PUT","DELETE"])describe(`send a ${e} request`,(()=>{for(const t of function*(){yield["object as payload",e,{foo:"bar",bar:"baz",nested:{works:"yes"}},()=>{const e=new FormData;return e.set("foo","bar"),e.set("bar","baz"),e.set("nested[works]","yes"),e},{}],yield["JSON object as payload",e,{foo:"bar",bar:"baz",nested:{works:"yes"}},()=>JSON.stringify({foo:"bar",bar:"baz",nested:{works:"yes"}}),{"Content-Type":"application/json"}],yield["JSON string as payload",e,JSON.stringify({foo:"bar",bar:"baz",nested:{works:"yes"}}),()=>JSON.stringify({foo:"bar",bar:"baz",nested:{works:"yes"}}),{"Content-Type":"application/json"}]}()){const[e,o,a,r,n]=t,s=o.toLowerCase();it(`with ${e}`,(e=>{new AjaxRequest("https://example.com")[s](a,{headers:n}),expect(window.fetch).toHaveBeenCalledWith("https://example.com/",jasmine.objectContaining({method:o,body:r()})),e()}))}}));describe("send GET requests",(()=>{for(const t of function*(){yield["plaintext","foobar huselpusel",{},(e,t)=>{expect("string"==typeof e).toBeTruthy(),expect(e).toEqual(t)}],yield["JSON",JSON.stringify({foo:"bar",baz:"bencer"}),{"Content-Type":"application/json"},(e,t)=>{expect("object"==typeof e).toBeTruthy(),expect(JSON.stringify(e)).toEqual(t)}],yield["JSON with utf-8",JSON.stringify({foo:"bar",baz:"bencer"}),{"Content-Type":"application/json; charset=utf-8"},(e,t)=>{expect("object"==typeof e).toBeTruthy(),expect(JSON.stringify(e)).toEqual(t)}]}()){const[o,a,r,n]=t;it("receives a "+o+" response",(t=>{const o=new Response(a,{headers:r});e.resolve(o),new AjaxRequest("https://example.com").get().then((async e=>{const o=await e.resolve();expect(window.fetch).toHaveBeenCalledWith("https://example.com/",jasmine.objectContaining({method:"GET"})),n(o,a),t()}))}))}})),describe("send requests with different input urls",(()=>{for(const e of function*(){yield["absolute url with domain","https://example.com",{},"https://example.com/"],yield["absolute url with domain, with query parameter","https://example.com",{foo:"bar",bar:{baz:"bencer"}},"https://example.com/?foo=bar&bar[baz]=bencer"],yield["absolute url without domain","/foo/bar",{},window.location.origin+"/foo/bar"],yield["absolute url without domain, with query parameter","/foo/bar",{foo:"bar",bar:{baz:"bencer"}},window.location.origin+"/foo/bar?foo=bar&bar[baz]=bencer"],yield["relative url without domain","foo/bar",{},window.location.origin+"/foo/bar"],yield["relative url without domain, with query parameter","foo/bar",{foo:"bar",bar:{baz:"bencer"}},window.location.origin+"/foo/bar?foo=bar&bar[baz]=bencer"],yield["fallback to current script if not defined","?foo=bar&baz=bencer",{},window.location.origin+window.location.pathname+"?foo=bar&baz=bencer"]}()){const[t,o,a,r]=e;it("with "+t,(()=>{new AjaxRequest(o).withQueryArguments(a).get(),expect(window.fetch).toHaveBeenCalledWith(r,jasmine.objectContaining({method:"GET"}))}))}})),describe("send requests with query arguments",(()=>{for(const e of function*(){yield["single level of arguments",{foo:"bar",bar:"baz"},"https://example.com/?foo=bar&bar=baz"],yield["nested arguments",{foo:"bar",bar:{baz:"bencer"}},"https://example.com/?foo=bar&bar[baz]=bencer"],yield["string argument","hello=world&foo=bar","https://example.com/?hello=world&foo=bar"],yield["array of arguments",["foo=bar","husel=pusel"],"https://example.com/?foo=bar&husel=pusel"],yield["object with array",{foo:["bar","baz"]},"https://example.com/?foo[0]=bar&foo[1]=baz"],yield["complex object",{foo:"bar",nested:{husel:"pusel",bar:"baz",array:["5","6"]},array:["1","2"]},"https://example.com/?foo=bar&nested[husel]=pusel&nested[bar]=baz&nested[array][0]=5&nested[array][1]=6&array[0]=1&array[1]=2"],yield["complex, deeply nested object",{foo:"bar",nested:{husel:"pusel",bar:"baz",array:["5","6"],deep_nested:{husel:"pusel",bar:"baz",array:["5","6"]}},array:["1","2"]},"https://example.com/?foo=bar&nested[husel]=pusel&nested[bar]=baz&nested[array][0]=5&nested[array][1]=6&nested[deep_nested][husel]=pusel&nested[deep_nested][bar]=baz&nested[deep_nested][array][0]=5&nested[deep_nested][array][1]=6&array[0]=1&array[1]=2"]}()){const[t,o,a]=e;it("with "+t,(()=>{new AjaxRequest("https://example.com/").withQueryArguments(o).get(),expect(window.fetch).toHaveBeenCalledWith(a,jasmine.objectContaining({method:"GET"}))}))}}))})); \ No newline at end of file diff --git a/typo3/sysext/core/Tests/JavaScript/security-utility-test.js b/typo3/sysext/core/Tests/JavaScript/security-utility-test.js index c00b974e7fdc..dbac3c87d2ce 100644 --- a/typo3/sysext/core/Tests/JavaScript/security-utility-test.js +++ b/typo3/sysext/core/Tests/JavaScript/security-utility-test.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import SecurityUtility from"@typo3/core/security-utility.js";describe("@typo3/core/security-utility",(()=>{it("generates random hex value",(()=>{for(let t of function*(){yield 1,yield 20,yield 39}()){const e=(new SecurityUtility).getRandomHexValue(t);expect(e.length).toBe(t)}})),it("throws SyntaxError on invalid length",(()=>{for(let t of function*(){yield 0,yield-90,yield 10.3}())expect((()=>(new SecurityUtility).getRandomHexValue(t))).toThrowError(SyntaxError)})),it("encodes HTML",(()=>{expect((new SecurityUtility).encodeHtml("<>\"'&")).toBe("<>"'&")})),it("removes HTML from string",(()=>{expect((new SecurityUtility).stripHtml('<img src="" onerror="alert(\'1\')">oh noes')).toBe("oh noes"),expect((new SecurityUtility).encodeHtml("<>\"'&")).toBe("<>"'&")}))})); \ No newline at end of file +import SecurityUtility from"@typo3/core/security-utility.js";describe("@typo3/core/security-utility",(()=>{it("generates random hex value",(()=>{for(const t of function*(){yield 1,yield 20,yield 39}()){const e=(new SecurityUtility).getRandomHexValue(t);expect(e.length).toBe(t)}})),it("throws SyntaxError on invalid length",(()=>{for(const t of function*(){yield 0,yield-90,yield 10.3}())expect((()=>(new SecurityUtility).getRandomHexValue(t))).toThrowError(SyntaxError)})),it("encodes HTML",(()=>{expect((new SecurityUtility).encodeHtml("<>\"'&")).toBe("<>"'&")})),it("removes HTML from string",(()=>{expect((new SecurityUtility).stripHtml('<img src="" onerror="alert(\'1\')">oh noes')).toBe("oh noes"),expect((new SecurityUtility).encodeHtml("<>\"'&")).toBe("<>"'&")}))})); \ No newline at end of file diff --git a/typo3/sysext/dashboard/Resources/Public/JavaScript/chart-initializer.js b/typo3/sysext/dashboard/Resources/Public/JavaScript/chart-initializer.js index 7fb93ff78759..b6094a3a8294 100644 --- a/typo3/sysext/dashboard/Resources/Public/JavaScript/chart-initializer.js +++ b/typo3/sysext/dashboard/Resources/Public/JavaScript/chart-initializer.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{Chart,ArcElement,LineElement,BarElement,PointElement,BarController,BubbleController,DoughnutController,LineController,PieController,PolarAreaController,RadarController,ScatterController,CategoryScale,LinearScale,LogarithmicScale,RadialLinearScale,TimeScale,TimeSeriesScale,Decimation,Filler,Legend,Title,Tooltip,SubTitle}from"@typo3/dashboard/contrib/chartjs.js";import RegularEvent from"@typo3/core/event/regular-event.js";class ChartInitializer{constructor(){this.selector=".dashboard-item",this.initialize()}initialize(){Chart.register(ArcElement,LineElement,BarElement,PointElement,BarController,BubbleController,DoughnutController,LineController,PieController,PolarAreaController,RadarController,ScatterController,CategoryScale,LinearScale,LogarithmicScale,RadialLinearScale,TimeScale,TimeSeriesScale,Decimation,Filler,Legend,Title,Tooltip,SubTitle),new RegularEvent("widgetContentRendered",(function(e){e.preventDefault();const r=e.detail;if(void 0===r||void 0===r.graphConfig)return;let t,l=this.querySelector("canvas");null!==l&&(t=l.getContext("2d")),void 0!==t&&new Chart(t,r.graphConfig)})).delegateTo(document,this.selector)}}export default new ChartInitializer; \ No newline at end of file +import{Chart,ArcElement,LineElement,BarElement,PointElement,BarController,BubbleController,DoughnutController,LineController,PieController,PolarAreaController,RadarController,ScatterController,CategoryScale,LinearScale,LogarithmicScale,RadialLinearScale,TimeScale,TimeSeriesScale,Decimation,Filler,Legend,Title,Tooltip,SubTitle}from"@typo3/dashboard/contrib/chartjs.js";import RegularEvent from"@typo3/core/event/regular-event.js";class ChartInitializer{constructor(){this.selector=".dashboard-item",this.initialize()}initialize(){Chart.register(ArcElement,LineElement,BarElement,PointElement,BarController,BubbleController,DoughnutController,LineController,PieController,PolarAreaController,RadarController,ScatterController,CategoryScale,LinearScale,LogarithmicScale,RadialLinearScale,TimeScale,TimeSeriesScale,Decimation,Filler,Legend,Title,Tooltip,SubTitle),new RegularEvent("widgetContentRendered",(function(e){e.preventDefault();const r=e.detail;if(void 0===r||void 0===r.graphConfig)return;const t=this.querySelector("canvas");let l;null!==t&&(l=t.getContext("2d")),void 0!==l&&new Chart(l,r.graphConfig)})).delegateTo(document,this.selector)}}export default new ChartInitializer; \ No newline at end of file diff --git a/typo3/sysext/dashboard/Resources/Public/JavaScript/grid.js b/typo3/sysext/dashboard/Resources/Public/JavaScript/grid.js index 82dc27d855ac..d55ce104fd15 100644 --- a/typo3/sysext/dashboard/Resources/Public/JavaScript/grid.js +++ b/typo3/sysext/dashboard/Resources/Public/JavaScript/grid.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import Muuri from"muuri";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import RegularEvent from"@typo3/core/event/regular-event.js";class Grid{constructor(){this.selector=".dashboard-grid",this.initialize()}initialize(){const e={dragEnabled:!0,dragSortHeuristics:{sortInterval:50,minDragDistance:10,minBounceBackAngle:1},layoutDuration:400,layoutEasing:"ease",dragPlaceholder:{enabled:!0,duration:400,createElement:e=>e.getElement().cloneNode(!0)},dragSortPredicate:{action:"move",threshold:30},dragHandle:".js-dashboard-move-widget",dragReleaseDuration:400,dragReleaseEasing:"ease",layout:{fillGaps:!1,rounding:!1}};if(null!==document.querySelector(this.selector)){const t=new Muuri(this.selector,e);t.on("dragStart",(()=>{document.querySelectorAll(".dashboard-item").forEach((e=>{e.classList.remove("dashboard-item--enableSelect")}))})),t.on("dragReleaseEnd",(()=>{document.querySelectorAll(".dashboard-item").forEach((e=>{e.classList.add("dashboard-item--enableSelect")})),this.saveItems(t)})),new RegularEvent("widgetContentRendered",(()=>{t.refreshItems().layout()})).delegateTo(document,".dashboard-item")}}saveItems(e){let t=e.getItems().map((function(e){return[e.getElement().getAttribute("data-widget-key"),e.getElement().getAttribute("data-widget-hash")]}));new AjaxRequest(TYPO3.settings.ajaxUrls.dashboard_save_widget_positions).post({widgets:t}).then((async e=>{await e.resolve()}))}}export default new Grid; \ No newline at end of file +import Muuri from"muuri";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import RegularEvent from"@typo3/core/event/regular-event.js";class Grid{constructor(){this.selector=".dashboard-grid",this.initialize()}initialize(){const e={dragEnabled:!0,dragSortHeuristics:{sortInterval:50,minDragDistance:10,minBounceBackAngle:1},layoutDuration:400,layoutEasing:"ease",dragPlaceholder:{enabled:!0,duration:400,createElement:e=>e.getElement().cloneNode(!0)},dragSortPredicate:{action:"move",threshold:30},dragHandle:".js-dashboard-move-widget",dragReleaseDuration:400,dragReleaseEasing:"ease",layout:{fillGaps:!1,rounding:!1}};if(null!==document.querySelector(this.selector)){const t=new Muuri(this.selector,e);t.on("dragStart",(()=>{document.querySelectorAll(".dashboard-item").forEach((e=>{e.classList.remove("dashboard-item--enableSelect")}))})),t.on("dragReleaseEnd",(()=>{document.querySelectorAll(".dashboard-item").forEach((e=>{e.classList.add("dashboard-item--enableSelect")})),this.saveItems(t)})),new RegularEvent("widgetContentRendered",(()=>{t.refreshItems().layout()})).delegateTo(document,".dashboard-item")}}saveItems(e){const t=e.getItems().map((function(e){return[e.getElement().getAttribute("data-widget-key"),e.getElement().getAttribute("data-widget-hash")]}));new AjaxRequest(TYPO3.settings.ajaxUrls.dashboard_save_widget_positions).post({widgets:t}).then((async e=>{await e.resolve()}))}}export default new Grid; \ No newline at end of file diff --git a/typo3/sysext/dashboard/Resources/Public/JavaScript/widget-content-collector.js b/typo3/sysext/dashboard/Resources/Public/JavaScript/widget-content-collector.js index 4948b1b34657..a94957d7241f 100644 --- a/typo3/sysext/dashboard/Resources/Public/JavaScript/widget-content-collector.js +++ b/typo3/sysext/dashboard/Resources/Public/JavaScript/widget-content-collector.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import RegularEvent from"@typo3/core/event/regular-event.js";class WidgetContentCollector{constructor(){this.selector=".dashboard-item",this.initialize()}initialize(){this.registerEvents();document.querySelectorAll(this.selector).forEach((e=>{let t;t=new Event("widgetRefresh",{bubbles:!0}),e.dispatchEvent(t)}))}registerEvents(){new RegularEvent("widgetRefresh",((e,t)=>{e.preventDefault(),this.getContentForWidget(t)})).delegateTo(document,this.selector)}getContentForWidget(e){const t=e.querySelector(".widget-waiting"),n=e.querySelector(".widget-content"),s=e.querySelector(".widget-error");t.classList.remove("hide"),n.classList.add("hide"),s.classList.add("hide");new AjaxRequest(TYPO3.settings.ajaxUrls.dashboard_get_widget_content).withQueryArguments({widget:e.dataset.widgetKey}).get().then((async s=>{const i=await s.resolve();let r;null!==n&&(n.innerHTML=i.content,n.classList.remove("hide")),null!==t&&t.classList.add("hide");const o={bubbles:!0};r=Object.keys(i.eventdata).length>0?new CustomEvent("widgetContentRendered",{...o,detail:i.eventdata}):new Event("widgetContentRendered",o),e.dispatchEvent(r)})).catch((n=>{null!==s&&s.classList.remove("hide"),null!==t&&t.classList.add("hide"),console.warn(`Error while retrieving widget [${e.dataset.widgetKey}] content: ${n.message}`)}))}}export default new WidgetContentCollector; \ No newline at end of file +import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import RegularEvent from"@typo3/core/event/regular-event.js";class WidgetContentCollector{constructor(){this.selector=".dashboard-item",this.initialize()}initialize(){this.registerEvents();document.querySelectorAll(this.selector).forEach((e=>{const t=new Event("widgetRefresh",{bubbles:!0});e.dispatchEvent(t)}))}registerEvents(){new RegularEvent("widgetRefresh",((e,t)=>{e.preventDefault(),this.getContentForWidget(t)})).delegateTo(document,this.selector)}getContentForWidget(e){const t=e.querySelector(".widget-waiting"),n=e.querySelector(".widget-content"),s=e.querySelector(".widget-error");t.classList.remove("hide"),n.classList.add("hide"),s.classList.add("hide");new AjaxRequest(TYPO3.settings.ajaxUrls.dashboard_get_widget_content).withQueryArguments({widget:e.dataset.widgetKey}).get().then((async s=>{const i=await s.resolve();let r;null!==n&&(n.innerHTML=i.content,n.classList.remove("hide")),null!==t&&t.classList.add("hide");const o={bubbles:!0};r=Object.keys(i.eventdata).length>0?new CustomEvent("widgetContentRendered",{...o,detail:i.eventdata}):new Event("widgetContentRendered",o),e.dispatchEvent(r)})).catch((n=>{null!==s&&s.classList.remove("hide"),null!==t&&t.classList.add("hide"),console.warn(`Error while retrieving widget [${e.dataset.widgetKey}] content: ${n.message}`)}))}}export default new WidgetContentCollector; \ No newline at end of file diff --git a/typo3/sysext/dashboard/Resources/Public/JavaScript/widget-refresh.js b/typo3/sysext/dashboard/Resources/Public/JavaScript/widget-refresh.js index c34f6f268718..9da2e645212f 100644 --- a/typo3/sysext/dashboard/Resources/Public/JavaScript/widget-refresh.js +++ b/typo3/sysext/dashboard/Resources/Public/JavaScript/widget-refresh.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -var Selectors,__decorate=function(e,t,r,o){var s,c=arguments.length,l=c<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,r):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)l=Reflect.decorate(e,t,r,o);else for(var n=e.length-1;n>=0;n--)(s=e[n])&&(l=(c<3?s(l):c>3?s(t,r,l):s(t,r))||l);return c>3&&l&&Object.defineProperty(t,r,l),l};import{html,LitElement}from"lit";import{customElement}from"lit/decorators.js";!function(e){e.dashboardItem=".dashboard-item"}(Selectors||(Selectors={}));let WidgetRefresh=class extends LitElement{connectedCallback(){super.connectedCallback(),this.addEventListener("click",this.onRefresh)}disconnectedCallback(){this.removeEventListener("click",this.onRefresh),super.disconnectedCallback()}render(){return html`<slot></slot>`}onRefresh(e){e.preventDefault(),this.closest(Selectors.dashboardItem).dispatchEvent(new Event("widgetRefresh",{bubbles:!0})),this.querySelector("button").blur()}};WidgetRefresh=__decorate([customElement("typo3-dashboard-widget-refresh")],WidgetRefresh); \ No newline at end of file +var Selectors,__decorate=function(e,t,r,o){var s,c=arguments.length,l=c<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,r):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)l=Reflect.decorate(e,t,r,o);else for(var n=e.length-1;n>=0;n--)(s=e[n])&&(l=(c<3?s(l):c>3?s(t,r,l):s(t,r))||l);return c>3&&l&&Object.defineProperty(t,r,l),l};import{html,LitElement}from"lit";import{customElement}from"lit/decorators.js";!function(e){e.dashboardItem=".dashboard-item"}(Selectors||(Selectors={}));let WidgetRefresh=class extends LitElement{connectedCallback(){super.connectedCallback(),this.addEventListener("click",this.onRefresh)}disconnectedCallback(){this.removeEventListener("click",this.onRefresh),super.disconnectedCallback()}render(){return html`<slot></slot>`}onRefresh(e){e.preventDefault(),this.closest(Selectors.dashboardItem).dispatchEvent(new Event("widgetRefresh",{bubbles:!0})),this.querySelector("button").blur()}};WidgetRefresh=__decorate([customElement("typo3-dashboard-widget-refresh")],WidgetRefresh);export{WidgetRefresh}; \ No newline at end of file diff --git a/typo3/sysext/extensionmanager/Resources/Public/JavaScript/main.js b/typo3/sysext/extensionmanager/Resources/Public/JavaScript/main.js index 804f33d38d7a..9e1b51e78f8e 100644 --- a/typo3/sysext/extensionmanager/Resources/Public/JavaScript/main.js +++ b/typo3/sysext/extensionmanager/Resources/Public/JavaScript/main.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import BrowserSession from"@typo3/backend/storage/browser-session.js";import NProgress from"nprogress";import{default as Modal}from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import SecurityUtility from"@typo3/core/security-utility.js";import ExtensionManagerRepository from"@typo3/extensionmanager/repository.js";import ExtensionManagerUpdate from"@typo3/extensionmanager/update.js";import ExtensionManagerUploadForm from"@typo3/extensionmanager/upload-form.js";import Tablesort from"tablesort";import"tablesort.dotsep.js";import"@typo3/backend/input/clearable.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import RegularEvent from"@typo3/core/event/regular-event.js";const securityUtility=new SecurityUtility;var ExtensionManagerIdentifier;!function(e){e.extensionlist="typo3-extension-list",e.searchField="#Tx_Extensionmanager_extensionkey"}(ExtensionManagerIdentifier||(ExtensionManagerIdentifier={}));class ExtensionManager{constructor(){this.searchFilterSessionKey="tx-extensionmanager-local-filter";const e=this;DocumentService.ready().then((()=>{this.Update=new ExtensionManagerUpdate,this.UploadForm=new ExtensionManagerUploadForm,this.Repository=new ExtensionManagerRepository;const t=document.getElementById(ExtensionManagerIdentifier.extensionlist);let n;if(null!==t&&(new Tablesort(t),new RegularEvent("click",(function(t){t.preventDefault(),Modal.confirm(TYPO3.lang["extensionList.removalConfirmation.title"],TYPO3.lang["extensionList.removalConfirmation.question"],Severity.error,[{text:TYPO3.lang["button.cancel"],active:!0,btnClass:"btn-default",trigger:()=>{Modal.dismiss()}},{text:TYPO3.lang["button.remove"],btnClass:"btn-danger",trigger:()=>{e.removeExtensionFromDisk(this),Modal.dismiss()}}])})).delegateTo(t,".removeExtension")),$(document).on("click",".onClickMaskExtensionManager",(()=>{NProgress.start()})).on("click","a[data-action=update-extension]",(e=>{e.preventDefault(),NProgress.start(),new AjaxRequest($(e.currentTarget).attr("href")).get().then(this.updateExtension)})).on("change","input[name=unlockDependencyIgnoreButton]",(e=>{$(".t3js-dependencies").toggleClass("disabled",!$(e.currentTarget).prop("checked"))})),null!==(n=document.querySelector(ExtensionManagerIdentifier.searchField))){const e=BrowserSession.get(this.searchFilterSessionKey);null!==e&&(n.value=e,this.filterExtensions(e)),new RegularEvent("submit",(e=>{e.preventDefault()})).bindTo(n.closest("form")),new DebounceEvent("input",(e=>{const t=e.target;BrowserSession.set(this.searchFilterSessionKey,t.value),this.filterExtensions(t.value)}),100).bindTo(n),n.clearable({onClear:()=>{BrowserSession.unset(this.searchFilterSessionKey),this.filterExtensions("")}})}$(document).on("click",".t3-button-action-installdistribution",(()=>{NProgress.start()})),this.Repository.initDom(),this.Update.initializeEvents(),this.UploadForm.initializeEvents()}))}filterExtensions(e){const t=document.querySelectorAll("[data-filterable]"),n=[];t.forEach((e=>{const t=Array.from(e.parentElement.children);n.push(t.indexOf(e))}));document.querySelectorAll("#typo3-extension-list tbody tr").forEach((t=>{const o=n.map((e=>t.children.item(e))),r=[];o.forEach((e=>{r.push(e.textContent.trim().replace(/\s+/g," "))})),t.classList.toggle("hidden",""!==e&&!RegExp(e,"i").test(r.join(":")))}))}removeExtensionFromDisk(e){NProgress.start(),new AjaxRequest(e.href).get().then((()=>{location.reload()})).finally((()=>{NProgress.done()}))}async updateExtension(e){let t=0;const n=await e.resolve(),o=$("<form>");for(let[e,r]of Object.entries(n.updateComments)){const n=$("<input>").attr({type:"radio",name:"version"}).val(e);0===t&&n.attr("checked","checked"),o.append([$("<h3>").append([n," "+securityUtility.encodeHtml(e)]),$("<div>").append(r.replace(/(\r\n|\n\r|\r|\n)/g,"\n").split(/\n/).map((e=>securityUtility.encodeHtml(e))).join("<br>"))]),t++}const r=$("<div>").append([$("<h1>").text(TYPO3.lang["extensionList.updateConfirmation.title"]),$("<h2>").text(TYPO3.lang["extensionList.updateConfirmation.message"]),o]);NProgress.done(),Modal.confirm(TYPO3.lang["extensionList.updateConfirmation.questionVersionComments"],r,Severity.warning,[{text:TYPO3.lang["button.cancel"],active:!0,btnClass:"btn-default",trigger:(e,t)=>t.hideModal()},{text:TYPO3.lang["button.updateExtension"],btnClass:"btn-warning",trigger:(e,t)=>{NProgress.start(),new AjaxRequest(n.url).withQueryArguments({version:$("input:radio[name=version]:checked",t).val()}).get().finally((()=>{location.reload()})),t.hideModal()}}])}}let extensionManagerObject=new ExtensionManager;void 0===TYPO3.ExtensionManager&&(TYPO3.ExtensionManager=extensionManagerObject);export default extensionManagerObject; \ No newline at end of file +import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import BrowserSession from"@typo3/backend/storage/browser-session.js";import NProgress from"nprogress";import{default as Modal}from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import SecurityUtility from"@typo3/core/security-utility.js";import ExtensionManagerRepository from"@typo3/extensionmanager/repository.js";import ExtensionManagerUpdate from"@typo3/extensionmanager/update.js";import ExtensionManagerUploadForm from"@typo3/extensionmanager/upload-form.js";import Tablesort from"tablesort";import"tablesort.dotsep.js";import"@typo3/backend/input/clearable.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import RegularEvent from"@typo3/core/event/regular-event.js";const securityUtility=new SecurityUtility;var ExtensionManagerIdentifier;!function(e){e.extensionlist="typo3-extension-list",e.searchField="#Tx_Extensionmanager_extensionkey"}(ExtensionManagerIdentifier||(ExtensionManagerIdentifier={}));class ExtensionManager{constructor(){this.searchFilterSessionKey="tx-extensionmanager-local-filter",DocumentService.ready().then((()=>{this.Update=new ExtensionManagerUpdate,this.UploadForm=new ExtensionManagerUploadForm,this.Repository=new ExtensionManagerRepository;const e=document.getElementById(ExtensionManagerIdentifier.extensionlist);let t;if(null!==e&&(new Tablesort(e),new RegularEvent("click",((e,t)=>{e.preventDefault(),Modal.confirm(TYPO3.lang["extensionList.removalConfirmation.title"],TYPO3.lang["extensionList.removalConfirmation.question"],Severity.error,[{text:TYPO3.lang["button.cancel"],active:!0,btnClass:"btn-default",trigger:()=>{Modal.dismiss()}},{text:TYPO3.lang["button.remove"],btnClass:"btn-danger",trigger:()=>{this.removeExtensionFromDisk(t),Modal.dismiss()}}])})).delegateTo(e,".removeExtension")),$(document).on("click",".onClickMaskExtensionManager",(()=>{NProgress.start()})).on("click","a[data-action=update-extension]",(e=>{e.preventDefault(),NProgress.start(),new AjaxRequest($(e.currentTarget).attr("href")).get().then(this.updateExtension)})).on("change","input[name=unlockDependencyIgnoreButton]",(e=>{$(".t3js-dependencies").toggleClass("disabled",!$(e.currentTarget).prop("checked"))})),null!==(t=document.querySelector(ExtensionManagerIdentifier.searchField))){const e=BrowserSession.get(this.searchFilterSessionKey);null!==e&&(t.value=e,this.filterExtensions(e)),new RegularEvent("submit",(e=>{e.preventDefault()})).bindTo(t.closest("form")),new DebounceEvent("input",(e=>{const t=e.target;BrowserSession.set(this.searchFilterSessionKey,t.value),this.filterExtensions(t.value)}),100).bindTo(t),t.clearable({onClear:()=>{BrowserSession.unset(this.searchFilterSessionKey),this.filterExtensions("")}})}$(document).on("click",".t3-button-action-installdistribution",(()=>{NProgress.start()})),this.Repository.initDom(),this.Update.initializeEvents(),this.UploadForm.initializeEvents()}))}filterExtensions(e){const t=document.querySelectorAll("[data-filterable]"),n=[];t.forEach((e=>{const t=Array.from(e.parentElement.children);n.push(t.indexOf(e))}));document.querySelectorAll("#typo3-extension-list tbody tr").forEach((t=>{const o=n.map((e=>t.children.item(e))),r=[];o.forEach((e=>{r.push(e.textContent.trim().replace(/\s+/g," "))})),t.classList.toggle("hidden",""!==e&&!RegExp(e,"i").test(r.join(":")))}))}removeExtensionFromDisk(e){NProgress.start(),new AjaxRequest(e.href).get().then((()=>{location.reload()})).finally((()=>{NProgress.done()}))}async updateExtension(e){let t=0;const n=await e.resolve(),o=$("<form>");for(const[e,r]of Object.entries(n.updateComments)){const n=$("<input>").attr({type:"radio",name:"version"}).val(e);0===t&&n.attr("checked","checked"),o.append([$("<h3>").append([n," "+securityUtility.encodeHtml(e)]),$("<div>").append(r.replace(/(\r\n|\n\r|\r|\n)/g,"\n").split(/\n/).map((e=>securityUtility.encodeHtml(e))).join("<br>"))]),t++}const r=$("<div>").append([$("<h1>").text(TYPO3.lang["extensionList.updateConfirmation.title"]),$("<h2>").text(TYPO3.lang["extensionList.updateConfirmation.message"]),o]);NProgress.done(),Modal.confirm(TYPO3.lang["extensionList.updateConfirmation.questionVersionComments"],r,Severity.warning,[{text:TYPO3.lang["button.cancel"],active:!0,btnClass:"btn-default",trigger:(e,t)=>t.hideModal()},{text:TYPO3.lang["button.updateExtension"],btnClass:"btn-warning",trigger:(e,t)=>{NProgress.start(),new AjaxRequest(n.url).withQueryArguments({version:$("input:radio[name=version]:checked",t).val()}).get().finally((()=>{location.reload()})),t.hideModal()}}])}}const extensionManagerObject=new ExtensionManager;void 0===TYPO3.ExtensionManager&&(TYPO3.ExtensionManager=extensionManagerObject);export default extensionManagerObject; \ No newline at end of file diff --git a/typo3/sysext/extensionmanager/Resources/Public/JavaScript/repository.js b/typo3/sysext/extensionmanager/Resources/Public/JavaScript/repository.js index 503ff27b2613..754c8f318009 100644 --- a/typo3/sysext/extensionmanager/Resources/Public/JavaScript/repository.js +++ b/typo3/sysext/extensionmanager/Resources/Public/JavaScript/repository.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import NProgress from"nprogress";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import Severity from"@typo3/backend/severity.js";import Tablesort from"tablesort";import"@typo3/backend/input/clearable.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import RegularEvent from"@typo3/core/event/regular-event.js";class Repository{constructor(){this.downloadPath="",this.getDependencies=async e=>{const t=await e.resolve();NProgress.done(),t.hasDependencies?Modal.confirm(t.title,$(t.message),Severity.info,[{text:TYPO3.lang["button.cancel"],active:!0,btnClass:"btn-default",trigger:()=>{Modal.dismiss()}},{text:TYPO3.lang["button.resolveDependencies"],btnClass:"btn-primary",trigger:()=>{this.getResolveDependenciesAndInstallResult(t.url+"&downloadPath="+this.downloadPath),Modal.dismiss()}}]):t.hasErrors?Notification.error(t.title,t.message,15):this.getResolveDependenciesAndInstallResult(t.url+"&downloadPath="+this.downloadPath)}}initDom(){NProgress.configure({parent:".module-loading-indicator",showSpinner:!1});const e=document.getElementById("terVersionTable"),t=document.getElementById("terSearchTable");null!==e&&new Tablesort(e),null!==t&&new Tablesort(t),this.bindDownload(),this.bindSearchFieldResetter()}bindDownload(){const e=this;new RegularEvent("click",(function(t){t.preventDefault();const n=this.closest("form"),o=n.dataset.href;e.downloadPath=n.querySelector("input.downloadPath:checked").value,NProgress.start(),new AjaxRequest(o).get().then(e.getDependencies)})).delegateTo(document,".downloadFromTer form.download button[type=submit]")}getResolveDependenciesAndInstallResult(e){NProgress.start(),new AjaxRequest(e).get().then((async e=>{const t=await e.raw().json();if(t.errorCount>0){const e=Modal.confirm(t.errorTitle,$(t.errorMessage),Severity.error,[{text:TYPO3.lang["button.cancel"],active:!0,btnClass:"btn-default",trigger:()=>{Modal.dismiss()}},{text:TYPO3.lang["button.resolveDependenciesIgnore"],btnClass:"btn-danger disabled t3js-dependencies",trigger:e=>{$(e.currentTarget).hasClass("disabled")||(this.getResolveDependenciesAndInstallResult(t.skipDependencyUri),Modal.dismiss())}}]);e.addEventListener("typo3-modal-shown",(()=>{const t=e.querySelector(".t3js-dependencies");e.querySelector('input[name="unlockDependencyIgnoreButton"]').addEventListener("change",(e=>{e.currentTarget.checked?t?.classList.remove("disabled"):t?.classList.add("disabled")}))}))}else{let e=TYPO3.lang["extensionList.dependenciesResolveDownloadSuccess.message"+t.installationTypeLanguageKey].replace(/\{0\}/g,t.extension);e+="\n"+TYPO3.lang["extensionList.dependenciesResolveDownloadSuccess.header"]+": ";for(let[n,o]of Object.entries(t.result)){e+="\n\n"+TYPO3.lang["extensionList.dependenciesResolveDownloadSuccess.item"]+" "+n+": ";for(let t of o)e+="\n* "+t}Notification.info(TYPO3.lang["extensionList.dependenciesResolveFlashMessage.title"+t.installationTypeLanguageKey].replace(/\{0\}/g,t.extension),e,15),top.TYPO3.ModuleMenu.App.refreshMenu()}})).finally((()=>{NProgress.done()}))}bindSearchFieldResetter(){let e;if(null!==(e=document.querySelector('.typo3-extensionmanager-searchTerForm input[type="text"]'))){const t=""!==e.value;e.clearable({onClear:e=>{t&&e.closest("form").submit()}})}}}export default Repository; \ No newline at end of file +import $ from"jquery";import NProgress from"nprogress";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import Severity from"@typo3/backend/severity.js";import Tablesort from"tablesort";import"@typo3/backend/input/clearable.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import RegularEvent from"@typo3/core/event/regular-event.js";class Repository{constructor(){this.downloadPath="",this.getDependencies=async e=>{const t=await e.resolve();NProgress.done(),t.hasDependencies?Modal.confirm(t.title,$(t.message),Severity.info,[{text:TYPO3.lang["button.cancel"],active:!0,btnClass:"btn-default",trigger:()=>{Modal.dismiss()}},{text:TYPO3.lang["button.resolveDependencies"],btnClass:"btn-primary",trigger:()=>{this.getResolveDependenciesAndInstallResult(t.url+"&downloadPath="+this.downloadPath),Modal.dismiss()}}]):t.hasErrors?Notification.error(t.title,t.message,15):this.getResolveDependenciesAndInstallResult(t.url+"&downloadPath="+this.downloadPath)}}initDom(){NProgress.configure({parent:".module-loading-indicator",showSpinner:!1});const e=document.getElementById("terVersionTable"),t=document.getElementById("terSearchTable");null!==e&&new Tablesort(e),null!==t&&new Tablesort(t),this.bindDownload(),this.bindSearchFieldResetter()}bindDownload(){new RegularEvent("click",((e,t)=>{e.preventDefault();const n=t.closest("form"),s=n.dataset.href;this.downloadPath=n.querySelector("input.downloadPath:checked").value,NProgress.start(),new AjaxRequest(s).get().then(this.getDependencies)})).delegateTo(document,".downloadFromTer form.download button[type=submit]")}getResolveDependenciesAndInstallResult(e){NProgress.start(),new AjaxRequest(e).get().then((async e=>{const t=await e.raw().json();if(t.errorCount>0){const e=Modal.confirm(t.errorTitle,$(t.errorMessage),Severity.error,[{text:TYPO3.lang["button.cancel"],active:!0,btnClass:"btn-default",trigger:()=>{Modal.dismiss()}},{text:TYPO3.lang["button.resolveDependenciesIgnore"],btnClass:"btn-danger disabled t3js-dependencies",trigger:e=>{$(e.currentTarget).hasClass("disabled")||(this.getResolveDependenciesAndInstallResult(t.skipDependencyUri),Modal.dismiss())}}]);e.addEventListener("typo3-modal-shown",(()=>{const t=e.querySelector(".t3js-dependencies");e.querySelector('input[name="unlockDependencyIgnoreButton"]').addEventListener("change",(e=>{e.currentTarget.checked?t?.classList.remove("disabled"):t?.classList.add("disabled")}))}))}else{let e=TYPO3.lang["extensionList.dependenciesResolveDownloadSuccess.message"+t.installationTypeLanguageKey].replace(/\{0\}/g,t.extension);e+="\n"+TYPO3.lang["extensionList.dependenciesResolveDownloadSuccess.header"]+": ";for(const[n,s]of Object.entries(t.result)){e+="\n\n"+TYPO3.lang["extensionList.dependenciesResolveDownloadSuccess.item"]+" "+n+": ";for(const t of s)e+="\n* "+t}Notification.info(TYPO3.lang["extensionList.dependenciesResolveFlashMessage.title"+t.installationTypeLanguageKey].replace(/\{0\}/g,t.extension),e,15),top.TYPO3.ModuleMenu.App.refreshMenu()}})).finally((()=>{NProgress.done()}))}bindSearchFieldResetter(){let e;if(null!==(e=document.querySelector('.typo3-extensionmanager-searchTerForm input[type="text"]'))){const t=""!==e.value;e.clearable({onClear:e=>{t&&e.closest("form").submit()}})}}}export default Repository; \ No newline at end of file diff --git a/typo3/sysext/filelist/Resources/Public/JavaScript/browse-files.js b/typo3/sysext/filelist/Resources/Public/JavaScript/browse-files.js index cedf4246cc8c..c3fa1faaf96c 100644 --- a/typo3/sysext/filelist/Resources/Public/JavaScript/browse-files.js +++ b/typo3/sysext/filelist/Resources/Public/JavaScript/browse-files.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{MessageUtility}from"@typo3/backend/utility/message-utility.js";import ElementBrowser from"@typo3/backend/element-browser.js";import NProgress from"nprogress";import RegularEvent from"@typo3/core/event/regular-event.js";var Icons=TYPO3.Icons;import{FileListActionEvent,FileListActionSelector,FileListActionUtility}from"@typo3/filelist/file-list-actions.js";import InfoWindow from"@typo3/backend/info-window.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";class BrowseFiles{constructor(){this.importSelection=e=>{e.preventDefault();const t=e.target,n=e.detail.checkboxes;if(!n.length)return;const i=[];if(n.forEach((e=>{if(e.checked){const t=e.closest(FileListActionSelector.elementSelector),n=FileListActionUtility.getResourceForElement(t);"file"===n.type&&n.uid&&i.unshift(n)}})),!i.length)return;Icons.getIcon("spinner-circle",Icons.sizes.small,null,null,Icons.markupIdentifiers.inline).then((e=>{t.classList.add("disabled"),t.innerHTML=e})),NProgress.configure({parent:".element-browser-main-content",showSpinner:!1}),NProgress.start();const o=1/i.length;BrowseFiles.handleNext(i),new RegularEvent("message",(e=>{if(!MessageUtility.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;"typo3:foreignRelation:inserted"===e.data.actionName&&(i.length>0?(NProgress.inc(o),BrowseFiles.handleNext(i)):(NProgress.done(),ElementBrowser.focusOpenerAndClose()))})).bindTo(window)},new RegularEvent(FileListActionEvent.primary,(e=>{e.preventDefault(),document.dispatchEvent(new CustomEvent(FileListActionEvent.select,{detail:{resource:e.detail.resource}}))})).bindTo(document),new RegularEvent(FileListActionEvent.select,(e=>{e.preventDefault();const t=e.detail.resource;"file"===t.type&&BrowseFiles.insertElement(t.name,t.uid,!0),"folder"===t.type&&this.loadContent(t)})).bindTo(document),new RegularEvent(FileListActionEvent.show,(e=>{e.preventDefault();const t=e.detail.resource;InfoWindow.showItem("_"+t.type.toUpperCase(),t.identifier)})).bindTo(document),new RegularEvent("multiRecordSelection:action:import",this.importSelection).bindTo(document)}static insertElement(e,t,n){return ElementBrowser.insertElement("sys_file",String(t),e,String(t),n)}static handleNext(e){if(e.length>0){const t=e.pop();BrowseFiles.insertElement(t.name,Number(t.uid))}}loadContent(e){if("folder"!==e.type)return;let t=document.location.href+"&contentOnly=1&expandFolder="+e.identifier;new AjaxRequest(t).get().then((e=>e.resolve())).then((e=>{document.querySelector(".element-browser-main-content .element-browser-body").innerHTML=e}))}}export default new BrowseFiles; \ No newline at end of file +import{MessageUtility}from"@typo3/backend/utility/message-utility.js";import ElementBrowser from"@typo3/backend/element-browser.js";import NProgress from"nprogress";import RegularEvent from"@typo3/core/event/regular-event.js";import Icons from"@typo3/backend/icons.js";import{FileListActionEvent,FileListActionSelector,FileListActionUtility}from"@typo3/filelist/file-list-actions.js";import InfoWindow from"@typo3/backend/info-window.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";class BrowseFiles{constructor(){this.importSelection=e=>{e.preventDefault();const t=e.target,n=e.detail.checkboxes;if(!n.length)return;const i=[];if(n.forEach((e=>{if(e.checked){const t=e.closest(FileListActionSelector.elementSelector),n=FileListActionUtility.getResourceForElement(t);"file"===n.type&&n.uid&&i.unshift(n)}})),!i.length)return;Icons.getIcon("spinner-circle",Icons.sizes.small,null,null,Icons.markupIdentifiers.inline).then((e=>{t.classList.add("disabled"),t.innerHTML=e})),NProgress.configure({parent:".element-browser-main-content",showSpinner:!1}),NProgress.start();const o=1/i.length;BrowseFiles.handleNext(i),new RegularEvent("message",(e=>{if(!MessageUtility.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;"typo3:foreignRelation:inserted"===e.data.actionName&&(i.length>0?(NProgress.inc(o),BrowseFiles.handleNext(i)):(NProgress.done(),ElementBrowser.focusOpenerAndClose()))})).bindTo(window)},new RegularEvent(FileListActionEvent.primary,(e=>{e.preventDefault(),document.dispatchEvent(new CustomEvent(FileListActionEvent.select,{detail:{resource:e.detail.resource}}))})).bindTo(document),new RegularEvent(FileListActionEvent.select,(e=>{e.preventDefault();const t=e.detail.resource;"file"===t.type&&BrowseFiles.insertElement(t.name,t.uid,!0),"folder"===t.type&&this.loadContent(t)})).bindTo(document),new RegularEvent(FileListActionEvent.show,(e=>{e.preventDefault();const t=e.detail.resource;InfoWindow.showItem("_"+t.type.toUpperCase(),t.identifier)})).bindTo(document),new RegularEvent("multiRecordSelection:action:import",this.importSelection).bindTo(document)}static insertElement(e,t,n){return ElementBrowser.insertElement("sys_file",String(t),e,String(t),n)}static handleNext(e){if(e.length>0){const t=e.pop();BrowseFiles.insertElement(t.name,Number(t.uid))}}loadContent(e){if("folder"!==e.type)return;const t=document.location.href+"&contentOnly=1&expandFolder="+e.identifier;new AjaxRequest(t).get().then((e=>e.resolve())).then((e=>{document.querySelector(".element-browser-main-content .element-browser-body").innerHTML=e}))}}export default new BrowseFiles; \ No newline at end of file diff --git a/typo3/sysext/filelist/Resources/Public/JavaScript/file-list-dragdrop.js b/typo3/sysext/filelist/Resources/Public/JavaScript/file-list-dragdrop.js index 347ac97394fc..79abbe2f1fd3 100644 --- a/typo3/sysext/filelist/Resources/Public/JavaScript/file-list-dragdrop.js +++ b/typo3/sysext/filelist/Resources/Public/JavaScript/file-list-dragdrop.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import RegularEvent from"@typo3/core/event/regular-event.js";import Viewport from"@typo3/backend/viewport.js";import{MultiRecordSelectionSelectors}from"@typo3/backend/multi-record-selection.js";import{FileListActionSelector,FileListActionUtility}from"@typo3/filelist/file-list-actions.js";export var FileListDragDropEvent;!function(e){e.transfer="typo3:filelist:resource:dragdrop:transfer"}(FileListDragDropEvent||(FileListDragDropEvent={}));class FileListDragDrop{constructor(){this.dragPreviewId="dragpreview",this.currentAnimationRequestId=null,this.previewSize=32,this.rootDocument=top.document;const e=FileListActionSelector.elementSelector+'[draggable="true"]',t=new Image;t.src="data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=",new RegularEvent("dragstart",((e,r)=>{const i=[],o=document.querySelectorAll(MultiRecordSelectionSelectors.checkboxSelector+":checked");if(o.length)o.forEach((e=>{if(e.checked){const t=e.closest(FileListActionSelector.elementSelector);t.dataset.filelistDragdropTransferItem="true";const r=FileListActionUtility.getResourceForElement(t);i.push(r)}}));else{const e=r.closest(FileListActionSelector.elementSelector);e.dataset.filelistDragdropTransferItem="true";const t=FileListActionUtility.getResourceForElement(e);i.push(t)}const n=this.createPreview(i);e.dataTransfer.setDragImage(t,0,0),e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("application/json",JSON.stringify(i));const s=this.determinePreviewPosition(e);this.updatePreviewPosition(n,s)})).delegateTo(document,e),new RegularEvent("drag",(e=>{if(e.stopPropagation(),0===e.screenX&&0===e.screenY)return;const t=this.rootDocument.getElementById(this.dragPreviewId),r=this.determinePreviewPosition(e);let i=t.getBoundingClientRect();r.left===i.left&&r.top===i.top||this.updatePreviewPosition(t,r)}),{capture:!0,passive:!0}).delegateTo(document,e),new RegularEvent("dragover",((e,t)=>{e.stopPropagation();const r=FileListActionUtility.getResourceForElement(t);this.isDropAllowedOnResoruce(r)&&(e.dataTransfer.dropEffect="move",e.preventDefault(),t.classList.add("success"))}),{capture:!0}).delegateTo(document,e),new RegularEvent("drop",((e,t)=>{e.stopPropagation();const r={action:"transfer",resources:JSON.parse(e.dataTransfer.getData("application/json")??"{}"),target:FileListActionUtility.getResourceForElement(t)};top.document.dispatchEvent(new CustomEvent(FileListDragDropEvent.transfer,{detail:r}))}),{capture:!0,passive:!0}).delegateTo(document,e),new RegularEvent("dragend",(e=>{e.stopPropagation(),this.reset()}),{capture:!0,passive:!0}).delegateTo(document,e),new RegularEvent("dragleave",((e,t)=>{e.stopPropagation(),t.classList.remove("success")}),{capture:!0,passive:!0}).delegateTo(document,e)}createPreview(e){this.rootDocument.getElementById(this.dragPreviewId)?.remove();const t=document.createElement("div");t.id=this.dragPreviewId,t.setAttribute("inert","true"),t.classList.add("resource-dragpreview"),this.rootDocument.body.appendChild(t);const r=e.filter((e=>null!==e.thumbnail)).slice(0,3);if(r.length>0){const e=document.createElement("div");e.classList.add("resource-dragpreview-thumbnails"),t.appendChild(e),r.forEach((t=>{const r=new Image;r.src=t.thumbnail,r.height=this.previewSize,r.width=this.previewSize,e.appendChild(r)}))}const i=e.length-r.length;if(i>0){const e=document.createElement("div");e.classList.add("resource-dragpreview-counter"),e.textContent=(r.length>0?"+":"")+i.toString(),t.appendChild(e)}return t}updatePreviewPosition(e,t){this.currentAnimationRequestId&&window.cancelAnimationFrame(this.currentAnimationRequestId),this.currentAnimationRequestId=window.requestAnimationFrame((()=>{e.style.transform="translate("+Math.round(t.left)+"px, "+Math.round(t.top)+"px)"}))}determinePreviewPosition(e){let t=e.clientX+16,r=e.clientY+16;const i=Viewport.ContentContainer.get();if(e.view===i){const e=i.frameElement.getBoundingClientRect();t+=e.left,r+=e.top}return{left:t,top:r}}reset(){document.querySelectorAll(FileListActionSelector.elementSelector).forEach((e=>{delete e.dataset.filelistDragdropTransferItem,e.classList.remove("success")})),this.rootDocument.getElementById(this.dragPreviewId)?.remove()}isDropAllowedOnResoruce(e){return!("filelistDragdropTransferItem"in document.querySelector(FileListActionSelector.elementSelector+'[data-filelist-identifier="'+e.identifier+'"]').dataset)&&"folder"===e.type}}export default new FileListDragDrop; \ No newline at end of file +import RegularEvent from"@typo3/core/event/regular-event.js";import Viewport from"@typo3/backend/viewport.js";import{MultiRecordSelectionSelectors}from"@typo3/backend/multi-record-selection.js";import{FileListActionSelector,FileListActionUtility}from"@typo3/filelist/file-list-actions.js";export var FileListDragDropEvent;!function(e){e.transfer="typo3:filelist:resource:dragdrop:transfer"}(FileListDragDropEvent||(FileListDragDropEvent={}));class FileListDragDrop{constructor(){this.dragPreviewId="dragpreview",this.currentAnimationRequestId=null,this.previewSize=32,this.rootDocument=top.document;const e=FileListActionSelector.elementSelector+'[draggable="true"]',t=new Image;t.src="data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=",new RegularEvent("dragstart",((e,r)=>{const i=[],o=document.querySelectorAll(MultiRecordSelectionSelectors.checkboxSelector+":checked");if(o.length)o.forEach((e=>{if(e.checked){const t=e.closest(FileListActionSelector.elementSelector);t.dataset.filelistDragdropTransferItem="true";const r=FileListActionUtility.getResourceForElement(t);i.push(r)}}));else{const e=r.closest(FileListActionSelector.elementSelector);e.dataset.filelistDragdropTransferItem="true";const t=FileListActionUtility.getResourceForElement(e);i.push(t)}const n=this.createPreview(i);e.dataTransfer.setDragImage(t,0,0),e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("application/json",JSON.stringify(i));const s=this.determinePreviewPosition(e);this.updatePreviewPosition(n,s)})).delegateTo(document,e),new RegularEvent("drag",(e=>{if(e.stopPropagation(),0===e.screenX&&0===e.screenY)return;const t=this.rootDocument.getElementById(this.dragPreviewId),r=this.determinePreviewPosition(e),i=t.getBoundingClientRect();r.left===i.left&&r.top===i.top||this.updatePreviewPosition(t,r)}),{capture:!0,passive:!0}).delegateTo(document,e),new RegularEvent("dragover",((e,t)=>{e.stopPropagation();const r=FileListActionUtility.getResourceForElement(t);this.isDropAllowedOnResoruce(r)&&(e.dataTransfer.dropEffect="move",e.preventDefault(),t.classList.add("success"))}),{capture:!0}).delegateTo(document,e),new RegularEvent("drop",((e,t)=>{e.stopPropagation();const r={action:"transfer",resources:JSON.parse(e.dataTransfer.getData("application/json")??"{}"),target:FileListActionUtility.getResourceForElement(t)};top.document.dispatchEvent(new CustomEvent(FileListDragDropEvent.transfer,{detail:r}))}),{capture:!0,passive:!0}).delegateTo(document,e),new RegularEvent("dragend",(e=>{e.stopPropagation(),this.reset()}),{capture:!0,passive:!0}).delegateTo(document,e),new RegularEvent("dragleave",((e,t)=>{e.stopPropagation(),t.classList.remove("success")}),{capture:!0,passive:!0}).delegateTo(document,e)}createPreview(e){this.rootDocument.getElementById(this.dragPreviewId)?.remove();const t=document.createElement("div");t.id=this.dragPreviewId,t.setAttribute("inert","true"),t.classList.add("resource-dragpreview"),this.rootDocument.body.appendChild(t);const r=e.filter((e=>null!==e.thumbnail)).slice(0,3);if(r.length>0){const e=document.createElement("div");e.classList.add("resource-dragpreview-thumbnails"),t.appendChild(e),r.forEach((t=>{const r=new Image;r.src=t.thumbnail,r.height=this.previewSize,r.width=this.previewSize,e.appendChild(r)}))}const i=e.length-r.length;if(i>0){const e=document.createElement("div");e.classList.add("resource-dragpreview-counter"),e.textContent=(r.length>0?"+":"")+i.toString(),t.appendChild(e)}return t}updatePreviewPosition(e,t){this.currentAnimationRequestId&&window.cancelAnimationFrame(this.currentAnimationRequestId),this.currentAnimationRequestId=window.requestAnimationFrame((()=>{e.style.transform="translate("+Math.round(t.left)+"px, "+Math.round(t.top)+"px)"}))}determinePreviewPosition(e){let t=e.clientX+16,r=e.clientY+16;const i=Viewport.ContentContainer.get();if(e.view===i){const e=i.frameElement.getBoundingClientRect();t+=e.left,r+=e.top}return{left:t,top:r}}reset(){document.querySelectorAll(FileListActionSelector.elementSelector).forEach((e=>{delete e.dataset.filelistDragdropTransferItem,e.classList.remove("success")})),this.rootDocument.getElementById(this.dragPreviewId)?.remove()}isDropAllowedOnResoruce(e){return!("filelistDragdropTransferItem"in document.querySelector(FileListActionSelector.elementSelector+'[data-filelist-identifier="'+e.identifier+'"]').dataset)&&"folder"===e.type}}export default new FileListDragDrop; \ No newline at end of file diff --git a/typo3/sysext/filelist/Resources/Public/JavaScript/file-list.js b/typo3/sysext/filelist/Resources/Public/JavaScript/file-list.js index 495f6ba7ad09..3dfcd851177f 100644 --- a/typo3/sysext/filelist/Resources/Public/JavaScript/file-list.js +++ b/typo3/sysext/filelist/Resources/Public/JavaScript/file-list.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{lll}from"@typo3/core/lit-helper.js";import DocumentService from"@typo3/core/document-service.js";import Notification from"@typo3/backend/notification.js";import InfoWindow from"@typo3/backend/info-window.js";import{BroadcastMessage}from"@typo3/backend/broadcast-message.js";import broadcastService from"@typo3/backend/broadcast-service.js";import{FileListActionEvent,FileListActionSelector,FileListActionUtility}from"@typo3/filelist/file-list-actions.js";import NProgress from"nprogress";import Icons from"@typo3/backend/icons.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";import{default as Modal}from"@typo3/backend/modal.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import Severity from"@typo3/backend/severity.js";import{MultiRecordSelectionSelectors}from"@typo3/backend/multi-record-selection.js";import ContextMenu from"@typo3/backend/context-menu.js";var Selectors;!function(e){e.fileListFormSelector='form[name="fileListForm"]',e.commandSelector='input[name="cmd"]',e.searchFieldSelector='input[name="searchTerm"]',e.pointerFieldSelector='input[name="pointer"]'}(Selectors||(Selectors={}));export const fileListOpenElementBrowser="typo3:filelist:openElementBrowser";export default class Filelist{constructor(){this.downloadFilesAndFolders=e=>{e.preventDefault();const t=e.target,o=e.detail,i=o.configuration,r=[];o.checkboxes.forEach((e=>{if(e.checked){const t=e.closest(FileListActionSelector.elementSelector),o=FileListActionUtility.getResourceForElement(t);r.unshift(o.identifier)}})),r.length?this.triggerDownload(r,i.downloadUrl,t):Notification.warning(lll("file_download.invalidSelection"))},Filelist.processTriggers(),new RegularEvent(fileListOpenElementBrowser,(e=>{const t=new URL(e.detail.actionUrl,window.location.origin);t.searchParams.set("expandFolder",e.detail.identifier),t.searchParams.set("mode",e.detail.mode);Modal.advanced({type:Modal.types.iframe,content:t.toString(),size:Modal.sizes.large}).addEventListener("typo3-modal-hidden",(()=>{top.list_frame.document.location.reload()}))})).bindTo(document),new RegularEvent(FileListActionEvent.primary,(e=>{const t=e.detail;if("file"===t.resource.type&&(window.location.href=top.TYPO3.settings.FormEngine.moduleUrl+"&edit[sys_file_metadata]["+t.resource.metaUid+"]=edit&returnUrl="+Filelist.getReturnUrl("")),"folder"===t.resource.type){let e=Filelist.parseQueryParameters(document.location);e.id=t.resource.identifier;let o="";Object.keys(e).forEach((t=>{""!==e[t]&&(o=o+"&"+t+"="+e[t])})),window.location.href=window.location.pathname+"?"+o.substring(1)}})).bindTo(document),new RegularEvent(FileListActionEvent.primaryContextmenu,(e=>{const t=e.detail;ContextMenu.show("sys_file",t.resource.identifier,"","","",t.trigger)})).bindTo(document),new RegularEvent(FileListActionEvent.show,(e=>{const t=e.detail;Filelist.openInfoPopup("_"+t.resource.type.toUpperCase(),t.resource.identifier)})).bindTo(document),new RegularEvent(FileListActionEvent.download,(e=>{const t=e.detail;this.triggerDownload([t.resource.identifier],t.url,t.trigger)})).bindTo(document),DocumentService.ready().then((()=>{new RegularEvent("click",((e,t)=>{e.preventDefault(),document.dispatchEvent(new CustomEvent(fileListOpenElementBrowser,{detail:{actionUrl:t.href,identifier:t.dataset.identifier,mode:t.dataset.mode}}))})).delegateTo(document,".t3js-element-browser")})),new RegularEvent("multiRecordSelection:action:edit",this.editFileMetadata).bindTo(document),new RegularEvent("multiRecordSelection:action:delete",this.deleteMultiple).bindTo(document),new RegularEvent("multiRecordSelection:action:download",this.downloadFilesAndFolders).bindTo(document),new RegularEvent("multiRecordSelection:action:copyMarked",(e=>{Filelist.submitClipboardFormWithCommand("copyMarked",e.target)})).bindTo(document),new RegularEvent("multiRecordSelection:action:removeMarked",(e=>{Filelist.submitClipboardFormWithCommand("removeMarked",e.target)})).bindTo(document);const e=""!==document.querySelector([Selectors.fileListFormSelector,Selectors.searchFieldSelector].join(" "))?.value;new RegularEvent("search",(t=>{const o=t.target;""===o.value&&e&&o.closest(Selectors.fileListFormSelector)?.submit()})).delegateTo(document,Selectors.searchFieldSelector)}static submitClipboardFormWithCommand(e,t){const o=t.closest(Selectors.fileListFormSelector);if(!o)return;const i=o.querySelector(Selectors.commandSelector);if(i){if(i.value=e,"copyMarked"===e||"removeMarked"===e){const e=o.querySelector(Selectors.pointerFieldSelector),t=Filelist.parseQueryParameters(document.location).pointer;e&&t&&(e.value=t)}o.submit()}}static openInfoPopup(e,t){InfoWindow.showItem(e,t)}static processTriggers(){const e=document.querySelector(".filelist-main");if(null===e)return;const t=encodeURIComponent(e.dataset.filelistCurrentIdentifier);ModuleStateStorage.update("file",t,!0,void 0),Filelist.emitTreeUpdateRequest(e.dataset.filelistCurrentIdentifier)}static emitTreeUpdateRequest(e){const t=new BroadcastMessage("filelist","treeUpdateRequested",{type:"folder",identifier:e});broadcastService.post(t)}static parseQueryParameters(e){let t={};if(e&&Object.prototype.hasOwnProperty.call(e,"search")){let o=e.search.substr(1).split("&");for(let e=0;e<o.length;e++){const i=o[e].split("=");t[decodeURIComponent(i[0])]=decodeURIComponent(i[1])}}return t}static getReturnUrl(e){return""===e&&(e=top.list_frame.document.location.pathname+top.list_frame.document.location.search),encodeURIComponent(e)}deleteMultiple(e){e.preventDefault();const t=e.detail.configuration;Modal.advanced({title:t.title||"Delete",content:t.content||"Are you sure you want to delete those files and folders?",severity:SeverityEnum.warning,buttons:[{text:TYPO3.lang["button.close"]||"Close",active:!0,btnClass:"btn-default",trigger:(e,t)=>t.hideModal()},{text:t.ok||TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(SeverityEnum.warning),trigger:(t,o)=>{Filelist.submitClipboardFormWithCommand("delete",e.target),o.hideModal()}}]})}editFileMetadata(e){e.preventDefault();const t=e.detail,o=t.configuration;if(!o||!o.idField||!o.table)return;const i=[];t.checkboxes.forEach((e=>{const t=e.closest(MultiRecordSelectionSelectors.elementSelector);null!==t&&t.dataset[o.idField]&&i.push(t.dataset[o.idField])})),i.length?window.location.href=top.TYPO3.settings.FormEngine.moduleUrl+"&edit["+o.table+"]["+i.join(",")+"]=edit&returnUrl="+Filelist.getReturnUrl(o.returnUrl||""):Notification.warning("The selected elements can not be edited.")}triggerDownload(e,t,o){Notification.info(lll("file_download.prepare"),"",2);const i=o?.innerHTML;o&&(o.setAttribute("disabled","disabled"),Icons.getIcon("spinner-circle-dark",Icons.sizes.small).then((e=>{o.innerHTML=e}))),NProgress.configure({parent:"#typo3-filelist",showSpinner:!1}).start(),new AjaxRequest(t).post({items:e}).then((async e=>{let t=e.response.headers.get("Content-Disposition");if(!t){const t=await e.resolve();return void(!1===t.success&&t.status?Notification.warning(lll("file_download."+t.status),lll("file_download."+t.status+".message"),10):Notification.error(lll("file_download.error")))}t=t.substring(t.indexOf(" filename=")+10);const o=await e.raw().arrayBuffer(),i=new Blob([o],{type:e.raw().headers.get("Content-Type")}),r=URL.createObjectURL(i),n=document.createElement("a");n.href=r,n.download=t,document.body.appendChild(n),n.click(),URL.revokeObjectURL(r),document.body.removeChild(n),Notification.success(lll("file_download.success"),"",2)})).catch((()=>{Notification.error(lll("file_download.error"))})).finally((()=>{NProgress.done(),o&&(o.removeAttribute("disabled"),o.innerHTML=i)}))}} \ No newline at end of file +import{lll}from"@typo3/core/lit-helper.js";import DocumentService from"@typo3/core/document-service.js";import Notification from"@typo3/backend/notification.js";import InfoWindow from"@typo3/backend/info-window.js";import{BroadcastMessage}from"@typo3/backend/broadcast-message.js";import broadcastService from"@typo3/backend/broadcast-service.js";import{FileListActionEvent,FileListActionSelector,FileListActionUtility}from"@typo3/filelist/file-list-actions.js";import NProgress from"nprogress";import Icons from"@typo3/backend/icons.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";import{default as Modal}from"@typo3/backend/modal.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import Severity from"@typo3/backend/severity.js";import{MultiRecordSelectionSelectors}from"@typo3/backend/multi-record-selection.js";import ContextMenu from"@typo3/backend/context-menu.js";var Selectors;!function(e){e.fileListFormSelector='form[name="fileListForm"]',e.commandSelector='input[name="cmd"]',e.searchFieldSelector='input[name="searchTerm"]',e.pointerFieldSelector='input[name="pointer"]'}(Selectors||(Selectors={}));export const fileListOpenElementBrowser="typo3:filelist:openElementBrowser";export default class Filelist{constructor(){this.downloadFilesAndFolders=e=>{e.preventDefault();const t=e.target,o=e.detail,i=o.configuration,r=[];o.checkboxes.forEach((e=>{if(e.checked){const t=e.closest(FileListActionSelector.elementSelector),o=FileListActionUtility.getResourceForElement(t);r.unshift(o.identifier)}})),r.length?this.triggerDownload(r,i.downloadUrl,t):Notification.warning(lll("file_download.invalidSelection"))},Filelist.processTriggers(),new RegularEvent(fileListOpenElementBrowser,(e=>{const t=new URL(e.detail.actionUrl,window.location.origin);t.searchParams.set("expandFolder",e.detail.identifier),t.searchParams.set("mode",e.detail.mode);Modal.advanced({type:Modal.types.iframe,content:t.toString(),size:Modal.sizes.large}).addEventListener("typo3-modal-hidden",(()=>{top.list_frame.document.location.reload()}))})).bindTo(document),new RegularEvent(FileListActionEvent.primary,(e=>{const t=e.detail;if("file"===t.resource.type&&(window.location.href=top.TYPO3.settings.FormEngine.moduleUrl+"&edit[sys_file_metadata]["+t.resource.metaUid+"]=edit&returnUrl="+Filelist.getReturnUrl("")),"folder"===t.resource.type){const e=Filelist.parseQueryParameters(document.location);e.id=t.resource.identifier;let o="";Object.keys(e).forEach((t=>{""!==e[t]&&(o=o+"&"+t+"="+e[t])})),window.location.href=window.location.pathname+"?"+o.substring(1)}})).bindTo(document),new RegularEvent(FileListActionEvent.primaryContextmenu,(e=>{const t=e.detail;ContextMenu.show("sys_file",t.resource.identifier,"","","",t.trigger)})).bindTo(document),new RegularEvent(FileListActionEvent.show,(e=>{const t=e.detail;Filelist.openInfoPopup("_"+t.resource.type.toUpperCase(),t.resource.identifier)})).bindTo(document),new RegularEvent(FileListActionEvent.download,(e=>{const t=e.detail;this.triggerDownload([t.resource.identifier],t.url,t.trigger)})).bindTo(document),DocumentService.ready().then((()=>{new RegularEvent("click",((e,t)=>{e.preventDefault(),document.dispatchEvent(new CustomEvent(fileListOpenElementBrowser,{detail:{actionUrl:t.href,identifier:t.dataset.identifier,mode:t.dataset.mode}}))})).delegateTo(document,".t3js-element-browser")})),new RegularEvent("multiRecordSelection:action:edit",this.editFileMetadata).bindTo(document),new RegularEvent("multiRecordSelection:action:delete",this.deleteMultiple).bindTo(document),new RegularEvent("multiRecordSelection:action:download",this.downloadFilesAndFolders).bindTo(document),new RegularEvent("multiRecordSelection:action:copyMarked",(e=>{Filelist.submitClipboardFormWithCommand("copyMarked",e.target)})).bindTo(document),new RegularEvent("multiRecordSelection:action:removeMarked",(e=>{Filelist.submitClipboardFormWithCommand("removeMarked",e.target)})).bindTo(document);const e=""!==document.querySelector([Selectors.fileListFormSelector,Selectors.searchFieldSelector].join(" "))?.value;new RegularEvent("search",(t=>{const o=t.target;""===o.value&&e&&o.closest(Selectors.fileListFormSelector)?.submit()})).delegateTo(document,Selectors.searchFieldSelector)}static submitClipboardFormWithCommand(e,t){const o=t.closest(Selectors.fileListFormSelector);if(!o)return;const i=o.querySelector(Selectors.commandSelector);if(i){if(i.value=e,"copyMarked"===e||"removeMarked"===e){const e=o.querySelector(Selectors.pointerFieldSelector),t=Filelist.parseQueryParameters(document.location).pointer;e&&t&&(e.value=t)}o.submit()}}static openInfoPopup(e,t){InfoWindow.showItem(e,t)}static processTriggers(){const e=document.querySelector(".filelist-main");if(null===e)return;const t=encodeURIComponent(e.dataset.filelistCurrentIdentifier);ModuleStateStorage.update("file",t,!0,void 0),Filelist.emitTreeUpdateRequest(e.dataset.filelistCurrentIdentifier)}static emitTreeUpdateRequest(e){const t=new BroadcastMessage("filelist","treeUpdateRequested",{type:"folder",identifier:e});broadcastService.post(t)}static parseQueryParameters(e){const t={};if(e&&Object.prototype.hasOwnProperty.call(e,"search")){const o=e.search.substr(1).split("&");for(let e=0;e<o.length;e++){const i=o[e].split("=");t[decodeURIComponent(i[0])]=decodeURIComponent(i[1])}}return t}static getReturnUrl(e){return""===e&&(e=top.list_frame.document.location.pathname+top.list_frame.document.location.search),encodeURIComponent(e)}deleteMultiple(e){e.preventDefault();const t=e.detail.configuration;Modal.advanced({title:t.title||"Delete",content:t.content||"Are you sure you want to delete those files and folders?",severity:SeverityEnum.warning,buttons:[{text:TYPO3.lang["button.close"]||"Close",active:!0,btnClass:"btn-default",trigger:(e,t)=>t.hideModal()},{text:t.ok||TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(SeverityEnum.warning),trigger:(t,o)=>{Filelist.submitClipboardFormWithCommand("delete",e.target),o.hideModal()}}]})}editFileMetadata(e){e.preventDefault();const t=e.detail,o=t.configuration;if(!o||!o.idField||!o.table)return;const i=[];t.checkboxes.forEach((e=>{const t=e.closest(MultiRecordSelectionSelectors.elementSelector);null!==t&&t.dataset[o.idField]&&i.push(t.dataset[o.idField])})),i.length?window.location.href=top.TYPO3.settings.FormEngine.moduleUrl+"&edit["+o.table+"]["+i.join(",")+"]=edit&returnUrl="+Filelist.getReturnUrl(o.returnUrl||""):Notification.warning("The selected elements can not be edited.")}triggerDownload(e,t,o){Notification.info(lll("file_download.prepare"),"",2);const i=o?.innerHTML;o&&(o.setAttribute("disabled","disabled"),Icons.getIcon("spinner-circle-dark",Icons.sizes.small).then((e=>{o.innerHTML=e}))),NProgress.configure({parent:"#typo3-filelist",showSpinner:!1}).start(),new AjaxRequest(t).post({items:e}).then((async e=>{let t=e.response.headers.get("Content-Disposition");if(!t){const t=await e.resolve();return void(!1===t.success&&t.status?Notification.warning(lll("file_download."+t.status),lll("file_download."+t.status+".message"),10):Notification.error(lll("file_download.error")))}t=t.substring(t.indexOf(" filename=")+10);const o=await e.raw().arrayBuffer(),i=new Blob([o],{type:e.raw().headers.get("Content-Type")}),r=URL.createObjectURL(i),n=document.createElement("a");n.href=r,n.download=t,document.body.appendChild(n),n.click(),URL.revokeObjectURL(r),document.body.removeChild(n),Notification.success(lll("file_download.success"),"",2)})).catch((()=>{Notification.error(lll("file_download.error"))})).finally((()=>{NProgress.done(),o&&(o.removeAttribute("disabled"),o.innerHTML=i)}))}} \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/installer.js b/typo3/sysext/install/Resources/Public/JavaScript/installer.js index 2187e30596f3..c5851890e1d4 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/installer.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/installer.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import PasswordStrength from"@typo3/install/module/password-strength.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import"@typo3/backend/element/icon-element.js";class Installer{constructor(){this.selectorBody=".t3js-body",this.selectorModuleContent=".t3js-module-content",this.selectorMainContent=".t3js-installer-content",this.selectorProgressBar=".t3js-installer-progress",this.selectorDatabaseConnectOutput=".t3js-installer-databaseConnect-output",this.selectorDatabaseSelectOutput=".t3js-installer-databaseSelect-output",this.selectorDatabaseDataOutput=".t3js-installer-databaseData-output",this.initializeEvents(),DocumentService.ready().then((()=>{this.initialize()}))}initializeEvents(){$(document).on("click",".t3js-installer-environmentFolders-retry",(e=>{e.preventDefault(),this.showEnvironmentAndFolders()})),$(document).on("click",".t3js-installer-environmentFolders-execute",(e=>{e.preventDefault(),this.executeEnvironmentAndFolders()})),$(document).on("click",".t3js-installer-databaseConnect-execute",(e=>{e.preventDefault(),this.executeDatabaseConnect()})),$(document).on("click",".t3js-installer-databaseSelect-execute",(e=>{e.preventDefault(),this.executeDatabaseSelect()})),$(document).on("click",".t3js-installer-databaseData-execute",(e=>{e.preventDefault(),this.executeDatabaseData()})),$(document).on("click",".t3js-installer-defaultConfiguration-execute",(e=>{e.preventDefault(),this.executeDefaultConfiguration()})),$(document).on("click",".t3-install-form-password-toggle",(e=>{e.preventDefault();const t=$(e.currentTarget),a=$(t.data("toggleTarget")),s=t.find(t.data("toggleIcon"));"password"===a.attr("type")?(s.replaceWith('<typo3-backend-icon identifier="actions-eye" size="small"></typo3-backend-icon>'),a.attr("type","text")):(a.attr("type","password"),s.replaceWith('<typo3-backend-icon identifier="actions-lock" size="small"></typo3-backend-icon>'))})),$(document).on("keyup",".t3-install-form-password-strength",(()=>{PasswordStrength.initialize(".t3-install-form-password-strength")})),$(document).on("change","#t3js-connect-database-driver",(e=>{let t=$(e.currentTarget).val();$(".t3-install-driver-data").hide(),$(".t3-install-driver-data input").attr("disabled","disabled"),$("#"+t+" input").attr("disabled",null),$("#"+t).show()}))}initialize(){this.setProgress(0),this.getMainLayout()}getUrl(e){let t=location.href;return t=t.replace(location.search,""),void 0!==e&&(t=t+"?install[action]="+e),t}setProgress(e){let t=$(this.selectorProgressBar),a=0;0!==e&&(a=e/5*100,t.find(".progress-bar").empty().text(e+" / 5 - "+a+"% Complete")),t.find(".progress-bar").css("width",a+"%").attr("aria-valuenow",a)}getMainLayout(){new AjaxRequest(this.getUrl("mainLayout")).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();$(this.selectorBody).empty().append(t.html),this.checkInstallerAvailable()}))}checkInstallerAvailable(){new AjaxRequest(this.getUrl("checkInstallerAvailable")).get({cache:"no-cache"}).then((async e=>{(await e.resolve()).success?this.checkEnvironmentAndFolders():this.showInstallerNotAvailable()}))}showInstallerNotAvailable(){let e=$(this.selectorMainContent);new AjaxRequest(this.getUrl("showInstallerNotAvailable")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success&&e.empty().append(a.html)}))}checkEnvironmentAndFolders(){this.setProgress(1),new AjaxRequest(this.getUrl("checkEnvironmentAndFolders")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkTrustedHostsPattern():this.showEnvironmentAndFolders()}))}showEnvironmentAndFolders(){let e=$(this.selectorMainContent);new AjaxRequest(this.getUrl("showEnvironmentAndFolders")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();if(!0===a.success){e.empty().html(a.html);let t=$(".t3js-installer-environment-details"),s=!1;Array.isArray(a.environmentStatusErrors)&&a.environmentStatusErrors.forEach((e=>{s=!0;let a=InfoBox.render(e.severity,e.title,e.message);t.append(a)})),Array.isArray(a.environmentStatusWarnings)&&a.environmentStatusWarnings.forEach((e=>{s=!0;let a=InfoBox.render(e.severity,e.title,e.message);t.append(a)})),Array.isArray(a.structureErrors)&&a.structureErrors.forEach((e=>{s=!0;let a=InfoBox.render(e.severity,e.title,e.message);t.append(a)})),s?(t.show(),$(".t3js-installer-environmentFolders-bad").show()):$(".t3js-installer-environmentFolders-good").show()}}))}executeEnvironmentAndFolders(){new AjaxRequest(this.getUrl("executeEnvironmentAndFolders")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success&&this.checkTrustedHostsPattern()}))}checkTrustedHostsPattern(){new AjaxRequest(this.getUrl("checkTrustedHostsPattern")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.executeSilentConfigurationUpdate():this.executeAdjustTrustedHostsPattern()}))}executeAdjustTrustedHostsPattern(){new AjaxRequest(this.getUrl("executeAdjustTrustedHostsPattern")).get({cache:"no-cache"}).then((()=>{this.executeSilentConfigurationUpdate()}))}executeSilentConfigurationUpdate(){new AjaxRequest(this.getUrl("executeSilentConfigurationUpdate")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.executeSilentTemplateFileUpdate():this.executeSilentConfigurationUpdate()}))}executeSilentTemplateFileUpdate(){new AjaxRequest(this.getUrl("executeSilentTemplateFileUpdate")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkDatabaseConnect():this.executeSilentTemplateFileUpdate()}))}checkDatabaseConnect(){this.setProgress(2),new AjaxRequest(this.getUrl("checkDatabaseConnect")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkDatabaseSelect():this.showDatabaseConnect()}))}showDatabaseConnect(){let e=$(this.selectorMainContent);new AjaxRequest(this.getUrl("showDatabaseConnect")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success&&(e.empty().html(a.html),$("#t3js-connect-database-driver").trigger("change"))}))}executeDatabaseConnect(){let e=$(this.selectorDatabaseConnectOutput),t={"install[action]":"executeDatabaseConnect","install[token]":$(this.selectorModuleContent).data("installer-database-connect-execute-token")};for(let e of $(this.selectorBody+" form").serializeArray())t[e.name]=e.value;new AjaxRequest(this.getUrl()).post(t).then((async t=>{const a=await t.resolve();!0===a.success?this.checkDatabaseSelect():Array.isArray(a.status)&&(e.empty(),a.status.forEach((t=>{let a=InfoBox.render(t.severity,t.title,t.message);e.append(a)})))}))}checkDatabaseSelect(){this.setProgress(3),new AjaxRequest(this.getUrl("checkDatabaseSelect")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkDatabaseData():this.showDatabaseSelect()}))}showDatabaseSelect(){let e=$(this.selectorMainContent);new AjaxRequest(this.getUrl("showDatabaseSelect")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success&&e.empty().html(a.html)}))}executeDatabaseSelect(){let e=$(this.selectorDatabaseSelectOutput),t={"install[action]":"executeDatabaseSelect","install[token]":$(this.selectorModuleContent).data("installer-database-select-execute-token")};for(let e of $(this.selectorBody+" form").serializeArray())t[e.name]=e.value;new AjaxRequest(this.getUrl()).post(t).then((async t=>{const a=await t.resolve();!0===a.success?this.checkDatabaseRequirements():Array.isArray(a.status)&&a.status.forEach((t=>{let a=InfoBox.render(t.severity,t.title,t.message);e.empty().append(a)}))}))}checkDatabaseRequirements(){let e=$(this.selectorDatabaseSelectOutput),t={"install[action]":"checkDatabaseRequirements","install[token]":$(this.selectorModuleContent).data("installer-database-check-requirements-execute-token")};for(let e of $(this.selectorBody+" form").serializeArray())t[e.name]=e.value;new AjaxRequest(this.getUrl()).post(t).then((async t=>{const a=await t.resolve();!0===a.success?this.checkDatabaseData():Array.isArray(a.status)&&(e.empty(),a.status.forEach((t=>{let a=InfoBox.render(t.severity,t.title,t.message);e.append(a)})))}))}checkDatabaseData(){this.setProgress(4),new AjaxRequest(this.getUrl("checkDatabaseData")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.showDefaultConfiguration():this.showDatabaseData()}))}showDatabaseData(){let e=$(this.selectorMainContent);new AjaxRequest(this.getUrl("showDatabaseData")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success&&e.empty().html(a.html)}))}executeDatabaseData(){let e=$(this.selectorDatabaseDataOutput),t={"install[action]":"executeDatabaseData","install[token]":$(this.selectorModuleContent).data("installer-database-data-execute-token")};for(let e of $(this.selectorBody+" form").serializeArray())t[e.name]=e.value;let a=ProgressBar.render(Severity.loading,"Loading...","");e.empty().html(a),new AjaxRequest(this.getUrl()).post(t).then((async t=>{const a=await t.resolve();!0===a.success?this.showDefaultConfiguration():Array.isArray(a.status)&&(e.empty(),a.status.forEach((t=>{let a=InfoBox.render(t.severity,t.title,t.message);e.append(a)})))}))}showDefaultConfiguration(){let e=$(this.selectorMainContent);this.setProgress(5),new AjaxRequest(this.getUrl("showDefaultConfiguration")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success&&e.empty().html(a.html)}))}executeDefaultConfiguration(){let e={"install[action]":"executeDefaultConfiguration","install[token]":$(this.selectorModuleContent).data("installer-default-configuration-execute-token")};for(let t of $(this.selectorBody+" form").serializeArray())e[t.name]=t.value;new AjaxRequest(this.getUrl()).post(e).then((async e=>{const t=await e.resolve();top.location.href=t.redirect}))}}export default new Installer; \ No newline at end of file +import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import PasswordStrength from"@typo3/install/module/password-strength.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import"@typo3/backend/element/icon-element.js";class Installer{constructor(){this.selectorBody=".t3js-body",this.selectorModuleContent=".t3js-module-content",this.selectorMainContent=".t3js-installer-content",this.selectorProgressBar=".t3js-installer-progress",this.selectorDatabaseConnectOutput=".t3js-installer-databaseConnect-output",this.selectorDatabaseSelectOutput=".t3js-installer-databaseSelect-output",this.selectorDatabaseDataOutput=".t3js-installer-databaseData-output",this.initializeEvents(),DocumentService.ready().then((()=>{this.initialize()}))}initializeEvents(){$(document).on("click",".t3js-installer-environmentFolders-retry",(e=>{e.preventDefault(),this.showEnvironmentAndFolders()})),$(document).on("click",".t3js-installer-environmentFolders-execute",(e=>{e.preventDefault(),this.executeEnvironmentAndFolders()})),$(document).on("click",".t3js-installer-databaseConnect-execute",(e=>{e.preventDefault(),this.executeDatabaseConnect()})),$(document).on("click",".t3js-installer-databaseSelect-execute",(e=>{e.preventDefault(),this.executeDatabaseSelect()})),$(document).on("click",".t3js-installer-databaseData-execute",(e=>{e.preventDefault(),this.executeDatabaseData()})),$(document).on("click",".t3js-installer-defaultConfiguration-execute",(e=>{e.preventDefault(),this.executeDefaultConfiguration()})),$(document).on("click",".t3-install-form-password-toggle",(e=>{e.preventDefault();const t=$(e.currentTarget),s=$(t.data("toggleTarget")),a=t.find(t.data("toggleIcon"));"password"===s.attr("type")?(a.replaceWith('<typo3-backend-icon identifier="actions-eye" size="small"></typo3-backend-icon>'),s.attr("type","text")):(s.attr("type","password"),a.replaceWith('<typo3-backend-icon identifier="actions-lock" size="small"></typo3-backend-icon>'))})),$(document).on("keyup",".t3-install-form-password-strength",(()=>{PasswordStrength.initialize(".t3-install-form-password-strength")})),$(document).on("change","#t3js-connect-database-driver",(e=>{const t=$(e.currentTarget).val();$(".t3-install-driver-data").hide(),$(".t3-install-driver-data input").attr("disabled","disabled"),$("#"+t+" input").attr("disabled",null),$("#"+t).show()}))}initialize(){this.setProgress(0),this.getMainLayout()}getUrl(e){let t=location.href;return t=t.replace(location.search,""),void 0!==e&&(t=t+"?install[action]="+e),t}setProgress(e){const t=$(this.selectorProgressBar);let s=0;0!==e&&(s=e/5*100,t.find(".progress-bar").empty().text(e+" / 5 - "+s+"% Complete")),t.find(".progress-bar").css("width",s+"%").attr("aria-valuenow",s)}getMainLayout(){new AjaxRequest(this.getUrl("mainLayout")).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();$(this.selectorBody).empty().append(t.html),this.checkInstallerAvailable()}))}checkInstallerAvailable(){new AjaxRequest(this.getUrl("checkInstallerAvailable")).get({cache:"no-cache"}).then((async e=>{(await e.resolve()).success?this.checkEnvironmentAndFolders():this.showInstallerNotAvailable()}))}showInstallerNotAvailable(){const e=$(this.selectorMainContent);new AjaxRequest(this.getUrl("showInstallerNotAvailable")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();!0===s.success&&e.empty().append(s.html)}))}checkEnvironmentAndFolders(){this.setProgress(1),new AjaxRequest(this.getUrl("checkEnvironmentAndFolders")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkTrustedHostsPattern():this.showEnvironmentAndFolders()}))}showEnvironmentAndFolders(){const e=$(this.selectorMainContent);new AjaxRequest(this.getUrl("showEnvironmentAndFolders")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();if(!0===s.success){e.empty().html(s.html);const t=$(".t3js-installer-environment-details");let a=!1;Array.isArray(s.environmentStatusErrors)&&s.environmentStatusErrors.forEach((e=>{a=!0;const s=InfoBox.render(e.severity,e.title,e.message);t.append(s)})),Array.isArray(s.environmentStatusWarnings)&&s.environmentStatusWarnings.forEach((e=>{a=!0;const s=InfoBox.render(e.severity,e.title,e.message);t.append(s)})),Array.isArray(s.structureErrors)&&s.structureErrors.forEach((e=>{a=!0;const s=InfoBox.render(e.severity,e.title,e.message);t.append(s)})),a?(t.show(),$(".t3js-installer-environmentFolders-bad").show()):$(".t3js-installer-environmentFolders-good").show()}}))}executeEnvironmentAndFolders(){new AjaxRequest(this.getUrl("executeEnvironmentAndFolders")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success&&this.checkTrustedHostsPattern()}))}checkTrustedHostsPattern(){new AjaxRequest(this.getUrl("checkTrustedHostsPattern")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.executeSilentConfigurationUpdate():this.executeAdjustTrustedHostsPattern()}))}executeAdjustTrustedHostsPattern(){new AjaxRequest(this.getUrl("executeAdjustTrustedHostsPattern")).get({cache:"no-cache"}).then((()=>{this.executeSilentConfigurationUpdate()}))}executeSilentConfigurationUpdate(){new AjaxRequest(this.getUrl("executeSilentConfigurationUpdate")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.executeSilentTemplateFileUpdate():this.executeSilentConfigurationUpdate()}))}executeSilentTemplateFileUpdate(){new AjaxRequest(this.getUrl("executeSilentTemplateFileUpdate")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkDatabaseConnect():this.executeSilentTemplateFileUpdate()}))}checkDatabaseConnect(){this.setProgress(2),new AjaxRequest(this.getUrl("checkDatabaseConnect")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkDatabaseSelect():this.showDatabaseConnect()}))}showDatabaseConnect(){const e=$(this.selectorMainContent);new AjaxRequest(this.getUrl("showDatabaseConnect")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();!0===s.success&&(e.empty().html(s.html),$("#t3js-connect-database-driver").trigger("change"))}))}executeDatabaseConnect(){const e=$(this.selectorDatabaseConnectOutput),t={"install[action]":"executeDatabaseConnect","install[token]":$(this.selectorModuleContent).data("installer-database-connect-execute-token")};for(const e of $(this.selectorBody+" form").serializeArray())t[e.name]=e.value;new AjaxRequest(this.getUrl()).post(t).then((async t=>{const s=await t.resolve();!0===s.success?this.checkDatabaseSelect():Array.isArray(s.status)&&(e.empty(),s.status.forEach((t=>{const s=InfoBox.render(t.severity,t.title,t.message);e.append(s)})))}))}checkDatabaseSelect(){this.setProgress(3),new AjaxRequest(this.getUrl("checkDatabaseSelect")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkDatabaseData():this.showDatabaseSelect()}))}showDatabaseSelect(){const e=$(this.selectorMainContent);new AjaxRequest(this.getUrl("showDatabaseSelect")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();!0===s.success&&e.empty().html(s.html)}))}executeDatabaseSelect(){const e=$(this.selectorDatabaseSelectOutput),t={"install[action]":"executeDatabaseSelect","install[token]":$(this.selectorModuleContent).data("installer-database-select-execute-token")};for(const e of $(this.selectorBody+" form").serializeArray())t[e.name]=e.value;new AjaxRequest(this.getUrl()).post(t).then((async t=>{const s=await t.resolve();!0===s.success?this.checkDatabaseRequirements():Array.isArray(s.status)&&s.status.forEach((t=>{const s=InfoBox.render(t.severity,t.title,t.message);e.empty().append(s)}))}))}checkDatabaseRequirements(){const e=$(this.selectorDatabaseSelectOutput),t={"install[action]":"checkDatabaseRequirements","install[token]":$(this.selectorModuleContent).data("installer-database-check-requirements-execute-token")};for(const e of $(this.selectorBody+" form").serializeArray())t[e.name]=e.value;new AjaxRequest(this.getUrl()).post(t).then((async t=>{const s=await t.resolve();!0===s.success?this.checkDatabaseData():Array.isArray(s.status)&&(e.empty(),s.status.forEach((t=>{const s=InfoBox.render(t.severity,t.title,t.message);e.append(s)})))}))}checkDatabaseData(){this.setProgress(4),new AjaxRequest(this.getUrl("checkDatabaseData")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.showDefaultConfiguration():this.showDatabaseData()}))}showDatabaseData(){const e=$(this.selectorMainContent);new AjaxRequest(this.getUrl("showDatabaseData")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();!0===s.success&&e.empty().html(s.html)}))}executeDatabaseData(){const e=$(this.selectorDatabaseDataOutput),t={"install[action]":"executeDatabaseData","install[token]":$(this.selectorModuleContent).data("installer-database-data-execute-token")};for(const e of $(this.selectorBody+" form").serializeArray())t[e.name]=e.value;const s=ProgressBar.render(Severity.loading,"Loading...","");e.empty().append(s),new AjaxRequest(this.getUrl()).post(t).then((async t=>{const s=await t.resolve();!0===s.success?this.showDefaultConfiguration():Array.isArray(s.status)&&(e.empty(),s.status.forEach((t=>{const s=InfoBox.render(t.severity,t.title,t.message);e.append(s)})))}))}showDefaultConfiguration(){const e=$(this.selectorMainContent);this.setProgress(5),new AjaxRequest(this.getUrl("showDefaultConfiguration")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();!0===s.success&&e.empty().html(s.html)}))}executeDefaultConfiguration(){const e={"install[action]":"executeDefaultConfiguration","install[token]":$(this.selectorModuleContent).data("installer-default-configuration-execute-token")};for(const t of $(this.selectorBody+" form").serializeArray())e[t.name]=t.value;new AjaxRequest(this.getUrl()).post(e).then((async e=>{const t=await e.resolve();top.location.href=t.redirect}))}}export default new Installer; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/environment/environment-check.js b/typo3/sysext/install/Resources/Public/JavaScript/module/environment/environment-check.js index 6755779d411e..9c8548ad3e14 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/module/environment/environment-check.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/module/environment/environment-check.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class EnvironmentCheck extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorGridderBadge=".t3js-environmentCheck-badge",this.selectorExecuteTrigger=".t3js-environmentCheck-execute",this.selectorOutputContainer=".t3js-environmentCheck-output"}initialize(e){this.currentModal=e,this.runTests(),e.on("click",this.selectorExecuteTrigger,(e=>{e.preventDefault(),this.runTests()}))}runTests(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=$(this.selectorGridderBadge);t.text("").hide();const r=ProgressBar.render(Severity.loading,"Loading...","");e.find(this.selectorOutputContainer).empty().append(r),new AjaxRequest(Router.getUrl("environmentCheckGetStatus")).get({cache:"no-cache"}).then((async r=>{const o=await r.resolve();e.empty().append(o.html),Modal.setButtons(o.buttons);let s=0,n=0;if(!0===o.success&&"object"==typeof o.status){for(let t of Object.values(o.status))for(let r of t){1===r.severity&&s++,2===r.severity&&n++;const t=InfoBox.render(r.severity,r.title,r.message);e.find(this.selectorOutputContainer).append(t)}n>0?t.removeClass("badge-warning").addClass("badge-danger").text(n).show():s>0&&t.removeClass("badge-error").addClass("badge-warning").text(s).show()}else Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}}export default new EnvironmentCheck; \ No newline at end of file +import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class EnvironmentCheck extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorGridderBadge=".t3js-environmentCheck-badge",this.selectorExecuteTrigger=".t3js-environmentCheck-execute",this.selectorOutputContainer=".t3js-environmentCheck-output"}initialize(e){this.currentModal=e,this.runTests(),e.on("click",this.selectorExecuteTrigger,(e=>{e.preventDefault(),this.runTests()}))}runTests(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=$(this.selectorGridderBadge);t.text("").hide();const r=ProgressBar.render(Severity.loading,"Loading...","");e.find(this.selectorOutputContainer).empty().append(r),new AjaxRequest(Router.getUrl("environmentCheckGetStatus")).get({cache:"no-cache"}).then((async r=>{const o=await r.resolve();e.empty().append(o.html),Modal.setButtons(o.buttons);let s=0,n=0;if(!0===o.success&&"object"==typeof o.status){for(const t of Object.values(o.status))for(const r of t){1===r.severity&&s++,2===r.severity&&n++;const t=InfoBox.render(r.severity,r.title,r.message);e.find(this.selectorOutputContainer).append(t)}n>0?t.removeClass("badge-warning").addClass("badge-danger").text(n).show():s>0&&t.removeClass("badge-error").addClass("badge-warning").text(s).show()}else Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}}export default new EnvironmentCheck; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/environment/folder-structure.js b/typo3/sysext/install/Resources/Public/JavaScript/module/environment/folder-structure.js index b48eddd81f41..f9c551129b8a 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/module/environment/folder-structure.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/module/environment/folder-structure.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class FolderStructure extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorGridderBadge=".t3js-folderStructure-badge",this.selectorOutputContainer=".t3js-folderStructure-output",this.selectorErrorContainer=".t3js-folderStructure-errors",this.selectorErrorList=".t3js-folderStructure-errors-list",this.selectorErrorFixTrigger=".t3js-folderStructure-errors-fix",this.selectorOkContainer=".t3js-folderStructure-ok",this.selectorOkList=".t3js-folderStructure-ok-list",this.selectorPermissionContainer=".t3js-folderStructure-permissions"}static removeLoadingMessage(e){e.find(".alert-loading").remove()}initialize(e){this.currentModal=e,this.getStatus(),e.on("click",this.selectorErrorFixTrigger,(e=>{e.preventDefault(),this.fix()}))}getStatus(){const e=this.getModalBody(),t=$(this.selectorGridderBadge);t.text("").hide(),e.find(this.selectorOutputContainer).empty().append(ProgressBar.render(Severity.loading,"Loading...","")),new AjaxRequest(Router.getUrl("folderStructureGetStatus")).get({cache:"no-cache"}).then((async r=>{const o=await r.resolve();if(e.empty().append(o.html),Modal.setButtons(o.buttons),!0===o.success&&Array.isArray(o.errorStatus)){let r=0;o.errorStatus.length>0?(e.find(this.selectorErrorContainer).show(),e.find(this.selectorErrorList).empty(),o.errorStatus.forEach((o=>{r++,t.text(r).show();const s=InfoBox.render(o.severity,o.title,o.message);e.find(this.selectorErrorList).append(s)}))):e.find(this.selectorErrorContainer).hide()}!0===o.success&&Array.isArray(o.okStatus)&&(o.okStatus.length>0?(e.find(this.selectorOkContainer).show(),e.find(this.selectorOkList).empty(),o.okStatus.forEach((t=>{const r=InfoBox.render(t.severity,t.title,t.message);e.find(this.selectorOkList).append(r)}))):e.find(this.selectorOkContainer).hide());let s=o.folderStructureFilePermissionStatus;e.find(this.selectorPermissionContainer).empty().append(InfoBox.render(s.severity,s.title,s.message)),s=o.folderStructureDirectoryPermissionStatus,e.find(this.selectorPermissionContainer).append(InfoBox.render(s.severity,s.title,s.message))}),(t=>{Router.handleAjaxError(t,e)}))}fix(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=this.findInModal(this.selectorOutputContainer),r=ProgressBar.render(Severity.loading,"Loading...","");t.empty().html(r),new AjaxRequest(Router.getUrl("folderStructureFix")).get({cache:"no-cache"}).then((async e=>{const r=await e.resolve();FolderStructure.removeLoadingMessage(t),!0===r.success&&Array.isArray(r.fixedStatus)?(r.fixedStatus.length>0?r.fixedStatus.forEach((e=>{t.append(InfoBox.render(e.severity,e.title,e.message))})):t.append(InfoBox.render(Severity.warning,"Nothing fixed","")),this.getStatus()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}}export default new FolderStructure; \ No newline at end of file +import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class FolderStructure extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorGridderBadge=".t3js-folderStructure-badge",this.selectorOutputContainer=".t3js-folderStructure-output",this.selectorErrorContainer=".t3js-folderStructure-errors",this.selectorErrorList=".t3js-folderStructure-errors-list",this.selectorErrorFixTrigger=".t3js-folderStructure-errors-fix",this.selectorOkContainer=".t3js-folderStructure-ok",this.selectorOkList=".t3js-folderStructure-ok-list",this.selectorPermissionContainer=".t3js-folderStructure-permissions"}static removeLoadingMessage(e){e.find(".alert-loading").remove()}initialize(e){this.currentModal=e,this.getStatus(),e.on("click",this.selectorErrorFixTrigger,(e=>{e.preventDefault(),this.fix()}))}getStatus(){const e=this.getModalBody(),t=$(this.selectorGridderBadge);t.text("").hide(),e.find(this.selectorOutputContainer).empty().append(ProgressBar.render(Severity.loading,"Loading...","")),new AjaxRequest(Router.getUrl("folderStructureGetStatus")).get({cache:"no-cache"}).then((async r=>{const o=await r.resolve();if(e.empty().append(o.html),Modal.setButtons(o.buttons),!0===o.success&&Array.isArray(o.errorStatus)){let r=0;o.errorStatus.length>0?(e.find(this.selectorErrorContainer).show(),e.find(this.selectorErrorList).empty(),o.errorStatus.forEach((o=>{r++,t.text(r).show();const s=InfoBox.render(o.severity,o.title,o.message);e.find(this.selectorErrorList).append(s)}))):e.find(this.selectorErrorContainer).hide()}!0===o.success&&Array.isArray(o.okStatus)&&(o.okStatus.length>0?(e.find(this.selectorOkContainer).show(),e.find(this.selectorOkList).empty(),o.okStatus.forEach((t=>{const r=InfoBox.render(t.severity,t.title,t.message);e.find(this.selectorOkList).append(r)}))):e.find(this.selectorOkContainer).hide());let s=o.folderStructureFilePermissionStatus;e.find(this.selectorPermissionContainer).empty().append(InfoBox.render(s.severity,s.title,s.message)),s=o.folderStructureDirectoryPermissionStatus,e.find(this.selectorPermissionContainer).append(InfoBox.render(s.severity,s.title,s.message))}),(t=>{Router.handleAjaxError(t,e)}))}fix(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=this.findInModal(this.selectorOutputContainer),r=ProgressBar.render(Severity.loading,"Loading...","");t.empty().append(r),new AjaxRequest(Router.getUrl("folderStructureFix")).get({cache:"no-cache"}).then((async e=>{const r=await e.resolve();FolderStructure.removeLoadingMessage(t),!0===r.success&&Array.isArray(r.fixedStatus)?(r.fixedStatus.length>0?r.fixedStatus.forEach((e=>{t.append(InfoBox.render(e.severity,e.title,e.message))})):t.append(InfoBox.render(Severity.warning,"Nothing fixed","")),this.getStatus()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}}export default new FolderStructure; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/environment/image-processing.js b/typo3/sysext/install/Resources/Public/JavaScript/module/environment/image-processing.js index 9317b4a8c065..1a319bc58dec 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/module/environment/image-processing.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/module/environment/image-processing.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class ImageProcessing extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorExecuteTrigger=".t3js-imageProcessing-execute",this.selectorTestContainer=".t3js-imageProcessing-twinContainer",this.selectorTwinImageTemplate=".t3js-imageProcessing-twinImage-template",this.selectorCommandContainer=".t3js-imageProcessing-command",this.selectorCommandText=".t3js-imageProcessing-command-text",this.selectorTwinImages=".t3js-imageProcessing-images"}initialize(e){this.currentModal=e,this.getData(),e.on("click",this.selectorExecuteTrigger,(e=>{e.preventDefault(),this.runTests()}))}getData(){const e=this.getModalBody();new AjaxRequest(Router.getUrl("imageProcessingGetData")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();!0===s.success?(e.empty().append(s.html),Modal.setButtons(s.buttons),this.runTests()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}runTests(){const e=this.getModalBody(),t=this.findInModal(this.selectorExecuteTrigger);this.setModalButtonsState(!1);const s=this.findInModal(this.selectorTwinImageTemplate),o=[];e.find(this.selectorTestContainer).each(((t,r)=>{const a=$(r),n=a.data("test"),i=InfoBox.render(Severity.loading,"Loading...","");a.empty().html(i);const c=new AjaxRequest(Router.getUrl(n)).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();if(!0===t.success){a.empty(),Array.isArray(t.status)&&t.status.forEach((e=>{const t=InfoBox.render(e.severity,e.title,e.message);a.append(t)}));const e=s.clone();if(e.removeClass("t3js-imageProcessing-twinImage-template"),!0===t.fileExists&&(e.find("img.reference").attr("src",t.referenceFile),e.find("img.result").attr("src",t.outputFile),e.find(this.selectorTwinImages).show()),Array.isArray(t.command)&&t.command.length>0){e.find(this.selectorCommandContainer).show();const s=[];t.command.forEach((e=>{s.push("<strong>Command:</strong>\n"+e[1]),3===e.length&&s.push("<strong>Result:</strong>\n"+e[2])})),e.find(this.selectorCommandText).html(s.join("\n"))}a.append(e)}}),(t=>{Router.handleAjaxError(t,e)}));o.push(c)})),Promise.all(o).then((()=>{t.removeClass("disabled").prop("disabled",!1)}))}}export default new ImageProcessing; \ No newline at end of file +import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class ImageProcessing extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorExecuteTrigger=".t3js-imageProcessing-execute",this.selectorTestContainer=".t3js-imageProcessing-twinContainer",this.selectorTwinImageTemplate=".t3js-imageProcessing-twinImage-template",this.selectorCommandContainer=".t3js-imageProcessing-command",this.selectorCommandText=".t3js-imageProcessing-command-text",this.selectorTwinImages=".t3js-imageProcessing-images"}initialize(e){this.currentModal=e,this.getData(),e.on("click",this.selectorExecuteTrigger,(e=>{e.preventDefault(),this.runTests()}))}getData(){const e=this.getModalBody();new AjaxRequest(Router.getUrl("imageProcessingGetData")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();!0===s.success?(e.empty().append(s.html),Modal.setButtons(s.buttons),this.runTests()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}runTests(){const e=this.getModalBody(),t=this.findInModal(this.selectorExecuteTrigger);this.setModalButtonsState(!1);const s=this.findInModal(this.selectorTwinImageTemplate),o=[];e.find(this.selectorTestContainer).each(((t,r)=>{const a=$(r),n=a.data("test"),i=InfoBox.render(Severity.loading,"Loading...","");a.empty().append(i);const c=new AjaxRequest(Router.getUrl(n)).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();if(!0===t.success){a.empty(),Array.isArray(t.status)&&t.status.forEach((e=>{const t=InfoBox.render(e.severity,e.title,e.message);a.append(t)}));const e=s.clone();if(e.removeClass("t3js-imageProcessing-twinImage-template"),!0===t.fileExists&&(e.find("img.reference").attr("src",t.referenceFile),e.find("img.result").attr("src",t.outputFile),e.find(this.selectorTwinImages).show()),Array.isArray(t.command)&&t.command.length>0){e.find(this.selectorCommandContainer).show();const s=[];t.command.forEach((e=>{s.push("<strong>Command:</strong>\n"+e[1]),3===e.length&&s.push("<strong>Result:</strong>\n"+e[2])})),e.find(this.selectorCommandText).html(s.join("\n"))}a.append(e)}}),(t=>{Router.handleAjaxError(t,e)}));o.push(c)})),Promise.all(o).then((()=>{t.removeClass("disabled").prop("disabled",!1)}))}}export default new ImageProcessing; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/environment/mail-test.js b/typo3/sysext/install/Resources/Public/JavaScript/module/environment/mail-test.js index 9fba58d1baf9..dee82bbed595 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/module/environment/mail-test.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/module/environment/mail-test.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import"bootstrap";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class MailTest extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorOutputContainer=".t3js-mailTest-output",this.selectorMailTestButton=".t3js-mailTest-execute"}initialize(t){this.currentModal=t,this.getData(),t.on("click",this.selectorMailTestButton,(t=>{t.preventDefault(),this.send()})),t.on("submit","form",(t=>{t.preventDefault(),this.send()}))}getData(){const t=this.getModalBody();new AjaxRequest(Router.getUrl("mailTestGetData")).get({cache:"no-cache"}).then((async e=>{const o=await e.resolve();!0===o.success?(t.empty().append(o.html),Modal.setButtons(o.buttons)):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)}))}send(){this.setModalButtonsState(!1);const t=this.getModuleContent().data("mail-test-token"),e=this.findInModal(this.selectorOutputContainer),o=ProgressBar.render(Severity.loading,"Loading...","");e.empty().html(o),new AjaxRequest(Router.getUrl()).post({install:{action:"mailTest",token:t,email:this.findInModal(".t3js-mailTest-email").val()}}).then((async t=>{const o=await t.resolve();e.empty(),Array.isArray(o.status)?o.status.forEach((t=>{const o=InfoBox.render(t.severity,t.title,t.message);e.html(o)})):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(()=>{Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")})).finally((()=>{this.setModalButtonsState(!0)}))}}export default new MailTest; \ No newline at end of file +import"bootstrap";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class MailTest extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorOutputContainer=".t3js-mailTest-output",this.selectorMailTestButton=".t3js-mailTest-execute"}initialize(e){this.currentModal=e,this.getData(),e.on("click",this.selectorMailTestButton,(e=>{e.preventDefault(),this.send()})),e.on("submit","form",(e=>{e.preventDefault(),this.send()}))}getData(){const e=this.getModalBody();new AjaxRequest(Router.getUrl("mailTestGetData")).get({cache:"no-cache"}).then((async t=>{const o=await t.resolve();!0===o.success?(e.empty().append(o.html),Modal.setButtons(o.buttons)):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}send(){this.setModalButtonsState(!1);const e=this.getModuleContent().data("mail-test-token"),t=this.findInModal(this.selectorOutputContainer),o=ProgressBar.render(Severity.loading,"Loading...","");t.empty().append(o),new AjaxRequest(Router.getUrl()).post({install:{action:"mailTest",token:e,email:this.findInModal(".t3js-mailTest-email").val()}}).then((async e=>{const o=await e.resolve();t.empty(),Array.isArray(o.status)?o.status.forEach((e=>{const o=InfoBox.render(e.severity,e.title,e.message);t.empty().append(o)})):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(()=>{Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")})).finally((()=>{this.setModalButtonsState(!0)}))}}export default new MailTest; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/maintenance/language-packs.js b/typo3/sysext/install/Resources/Public/JavaScript/module/maintenance/language-packs.js index 0b3a51209652..8d06d2dc1a0c 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/module/maintenance/language-packs.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/module/maintenance/language-packs.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import SecurityUtility from"@typo3/core/security-utility.js";import FlashMessage from"@typo3/install/renderable/flash-message.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class LanguagePacks extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorOutputContainer=".t3js-languagePacks-output",this.selectorContentContainer=".t3js-languagePacks-mainContent",this.selectorActivateLanguage=".t3js-languagePacks-activateLanguage",this.selectorActivateLanguageIcon="#t3js-languagePacks-activate-icon",this.selectorAddLanguageToggle=".t3js-languagePacks-addLanguage-toggle",this.selectorLanguageInactive=".t3js-languagePacks-inactive",this.selectorDeactivateLanguage=".t3js-languagePacks-deactivateLanguage",this.selectorDeactivateLanguageIcon="#t3js-languagePacks-deactivate-icon",this.selectorUpdate=".t3js-languagePacks-update",this.selectorLanguageUpdateIcon="#t3js-languagePacks-languageUpdate-icon",this.selectorNotifications=".t3js-languagePacks-notifications",this.activeLanguages=[],this.activeExtensions=[],this.packsUpdateDetails={toHandle:0,handled:0,updated:0,new:0,failed:0,skipped:0},this.notifications=[]}static pluralize(t,a="pack",e="s",s=0){return 1!==t&&1!==s?a+e:a}initialize(t){this.currentModal=t,this.getData(),t.on("click",this.selectorAddLanguageToggle,(()=>{t.find(this.selectorContentContainer+" "+this.selectorLanguageInactive).toggle()})),t.on("click",this.selectorActivateLanguage,(t=>{const a=$(t.target).closest(this.selectorActivateLanguage).data("iso");t.preventDefault(),this.activateLanguage(a)})),t.on("click",this.selectorDeactivateLanguage,(t=>{const a=$(t.target).closest(this.selectorDeactivateLanguage).data("iso");t.preventDefault(),this.deactivateLanguage(a)})),t.on("click",this.selectorUpdate,(t=>{const a=$(t.target).closest(this.selectorUpdate).data("iso"),e=$(t.target).closest(this.selectorUpdate).data("extension");t.preventDefault(),this.updatePacks(a,e)}))}getData(){const t=this.getModalBody();new AjaxRequest(Router.getUrl("languagePacksGetData")).get({cache:"no-cache"}).then((async a=>{const e=await a.resolve();if(!0===e.success){this.activeLanguages=e.activeLanguages,this.activeExtensions=e.activeExtensions,t.empty().append(e.html);const a=t.parent().find(this.selectorContentContainer);a.empty(),a.append(this.languageMatrixHtml(e)),a.append(this.extensionMatrixHtml(e))}else{const t=InfoBox.render(Severity.error,"Something went wrong","");this.addNotification(t)}this.renderNotifications()}),(a=>{Router.handleAjaxError(a,t)}))}activateLanguage(t){const a=this.getModalBody(),e=this.findInModal(this.selectorOutputContainer),s=ProgressBar.render(Severity.loading,"Loading...","");e.empty().append(s),this.getNotificationBox().empty(),new AjaxRequest(Router.getUrl()).post({install:{action:"languagePacksActivateLanguage",token:this.getModuleContent().data("language-packs-activate-language-token"),iso:t}}).then((async t=>{const a=await t.resolve();if(e.empty(),!0===a.success&&Array.isArray(a.status))a.status.forEach((t=>{const a=InfoBox.render(t.severity,t.title,t.message);this.addNotification(a)}));else{const t=FlashMessage.render(Severity.error,"Something went wrong","");this.addNotification(t)}this.getData()}),(t=>{Router.handleAjaxError(t,a)}))}deactivateLanguage(t){const a=this.getModalBody(),e=this.findInModal(this.selectorOutputContainer),s=ProgressBar.render(Severity.loading,"Loading...","");e.empty().append(s),this.getNotificationBox().empty(),new AjaxRequest(Router.getUrl()).post({install:{action:"languagePacksDeactivateLanguage",token:this.getModuleContent().data("language-packs-deactivate-language-token"),iso:t}}).then((async t=>{const a=await t.resolve();if(e.empty(),!0===a.success&&Array.isArray(a.status))a.status.forEach((t=>{const a=InfoBox.render(t.severity,t.title,t.message);this.addNotification(a)}));else{const t=FlashMessage.render(Severity.error,"Something went wrong","");this.addNotification(t)}this.getData()}),(t=>{Router.handleAjaxError(t,a)}))}updatePacks(t,a){const e=this.findInModal(this.selectorOutputContainer),s=this.findInModal(this.selectorContentContainer),n=void 0===t?this.activeLanguages:[t];let i=!0,o=this.activeExtensions;void 0!==a&&(o=[a],i=!1),this.packsUpdateDetails={toHandle:n.length*o.length,handled:0,updated:0,new:0,failed:0,skipped:0},e.empty().append($("<div>",{class:"progress"}).append($("<div>",{class:"progress-bar progress-bar-info",role:"progressbar","aria-valuenow":0,"aria-valuemin":0,"aria-valuemax":100,style:"width: 0;"}).append($("<span>",{class:"text-nowrap"}).text("0 of "+this.packsUpdateDetails.toHandle+" language "+LanguagePacks.pluralize(this.packsUpdateDetails.toHandle)+" updated")))),s.empty(),n.forEach((t=>{o.forEach((a=>{this.getNotificationBox().empty(),new AjaxRequest(Router.getUrl()).post({install:{action:"languagePacksUpdatePack",token:this.getModuleContent().data("language-packs-update-pack-token"),iso:t,extension:a}}).then((async t=>{const a=await t.resolve();!0===a.success?(this.packsUpdateDetails.handled++,"new"===a.packResult?this.packsUpdateDetails.new++:"update"===a.packResult?this.packsUpdateDetails.updated++:"skipped"===a.packResult?this.packsUpdateDetails.skipped++:this.packsUpdateDetails.failed++,this.packUpdateDone(i,n)):(this.packsUpdateDetails.handled++,this.packsUpdateDetails.failed++,this.packUpdateDone(i,n))}),(()=>{this.packsUpdateDetails.handled++,this.packsUpdateDetails.failed++,this.packUpdateDone(i,n)}))}))}))}packUpdateDone(t,a){const e=this.getModalBody(),s=this.findInModal(this.selectorOutputContainer);if(this.packsUpdateDetails.handled===this.packsUpdateDetails.toHandle){const s=InfoBox.render(Severity.ok,"Language packs updated",this.packsUpdateDetails.new+" new language "+LanguagePacks.pluralize(this.packsUpdateDetails.new)+" downloaded, "+this.packsUpdateDetails.updated+" language "+LanguagePacks.pluralize(this.packsUpdateDetails.updated)+" updated, "+this.packsUpdateDetails.skipped+" language "+LanguagePacks.pluralize(this.packsUpdateDetails.skipped)+" skipped, "+this.packsUpdateDetails.failed+" language "+LanguagePacks.pluralize(this.packsUpdateDetails.failed)+" not available");this.addNotification(s),!0===t?new AjaxRequest(Router.getUrl()).post({install:{action:"languagePacksUpdateIsoTimes",token:this.getModuleContent().data("language-packs-update-iso-times-token"),isos:a}}).then((async t=>{if(!0===(await t.resolve()).success)this.getData();else{const t=FlashMessage.render(Severity.error,"Something went wrong","");this.addNotification(t)}}),(t=>{Router.handleAjaxError(t,e)})):this.getData()}else{const t=this.packsUpdateDetails.handled/this.packsUpdateDetails.toHandle*100;s.find(".progress-bar").css("width",t+"%").attr("aria-valuenow",t).find("span").text(this.packsUpdateDetails.handled+" of "+this.packsUpdateDetails.toHandle+" language "+LanguagePacks.pluralize(this.packsUpdateDetails.handled,"pack","s",this.packsUpdateDetails.toHandle)+" updated")}}languageMatrixHtml(t){const a=this.findInModal(this.selectorActivateLanguageIcon).html(),e=this.findInModal(this.selectorDeactivateLanguageIcon).html(),s=this.findInModal(this.selectorLanguageUpdateIcon).html(),n=$("<div>"),i=$("<tbody>");return t.languages.forEach((t=>{const n=t.active,o=$("<tr>");n?i.append(o.append($("<td>").text(" "+t.name).prepend($("<div />",{class:"btn-group"}).append($("<a>",{class:"btn btn-default t3js-languagePacks-deactivateLanguage","data-iso":t.iso,title:"Deactivate"}).append(e),$("<a>",{class:"btn btn-default t3js-languagePacks-update","data-iso":t.iso,title:"Download language packs"}).append(s))))):i.append(o.addClass("t3-languagePacks-inactive t3js-languagePacks-inactive").css({display:"none"}).append($("<td>").text(" "+t.name).prepend($("<div />",{class:"btn-group"}).append($("<a>",{class:"btn btn-default t3js-languagePacks-activateLanguage","data-iso":t.iso,title:"Activate"}).append(a))))),o.append($("<td>").text(t.iso),$("<td>").text(t.dependencies.join(", ")),$("<td>").text(null===t.lastUpdate?"":t.lastUpdate)),i.append(o)})),n.append($("<h3>").text("Active languages"),$("<table>",{class:"table table-striped table-bordered"}).append($("<thead>").append($("<tr>").append($("<th>").append($("<div />",{class:"btn-group"}).append($("<button>",{class:"btn btn-default t3js-languagePacks-addLanguage-toggle",type:"button"}).append($("<span>").append(a)," Add language"),$("<button>",{class:"btn btn-default disabled update-all t3js-languagePacks-update",type:"button",disabled:"disabled"}).append($("<span>").append(s)," Update all"))),$("<th>").text("Locale"),$("<th>").text("Dependencies"),$("<th>").text("Last update"))),i)),Array.isArray(this.activeLanguages)&&this.activeLanguages.length&&n.find(".update-all").removeClass("disabled").removeAttr("disabled"),n.html()}extensionMatrixHtml(t){const a=new SecurityUtility,e=this.findInModal(this.selectorLanguageUpdateIcon).html();let s,n="",i=0;const o=$("<div>"),d=$("<tr>");d.append($("<th>").text("Extension"),$("<th>").text("Key")),t.activeLanguages.forEach((t=>{d.append($("<th>").append($("<a>",{class:"btn btn-default t3js-languagePacks-update","data-iso":t,title:"Download and update all language packs"}).append($("<span>").append(e)," "+t)))}));const l=$("<tbody>");return t.extensions.forEach((o=>{i++,s=void 0!==o.icon?$("<span>").append($("<img>",{style:"max-height: 16px; max-width: 16px;",src:o.icon,alt:o.title}),$("<span>").text(" "+o.title)):$("<span>").text(o.title);const d=$("<tr>");d.append($("<td>").html(s.html()),$("<td>").text(o.key)),t.activeLanguages.forEach((t=>{let s=!1;if(o.packs.forEach((i=>{if(i.iso!==t)return;s=!0;const l=$("<td>");d.append(l),n=!0!==i.exists?null!==i.lastUpdate?"No language pack available for "+i.iso+" when tried at "+i.lastUpdate+". Click to re-try.":"Language pack not downloaded. Click to download":null===i.lastUpdate?"Downloaded. Click to renew":"Language pack downloaded at "+i.lastUpdate+". Click to renew",l.append($("<a>",{class:"btn btn-default t3js-languagePacks-update","data-extension":o.key,"data-iso":i.iso,title:a.encodeHtml(n)}).append(e))})),!s){const t=$("<td>");d.append(t).append(" ")}})),l.append(d)})),o.append($("<h3>").text("Translation status"),$("<table>",{class:"table table-striped table-bordered"}).append($("<thead>").append(d),l)),0===i?InfoBox.render(Severity.ok,"Language packs have been found for every installed extension.","To download the latest changes, use the refresh button in the list above."):o.html()}getNotificationBox(){return this.findInModal(this.selectorNotifications)}addNotification(t){this.notifications.push(t)}renderNotifications(){const t=this.getNotificationBox();for(let a of this.notifications)t.append(a);this.notifications=[]}}export default new LanguagePacks; \ No newline at end of file +import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import SecurityUtility from"@typo3/core/security-utility.js";import FlashMessage from"@typo3/install/renderable/flash-message.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class LanguagePacks extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorOutputContainer=".t3js-languagePacks-output",this.selectorContentContainer=".t3js-languagePacks-mainContent",this.selectorActivateLanguage=".t3js-languagePacks-activateLanguage",this.selectorActivateLanguageIcon="#t3js-languagePacks-activate-icon",this.selectorAddLanguageToggle=".t3js-languagePacks-addLanguage-toggle",this.selectorLanguageInactive=".t3js-languagePacks-inactive",this.selectorDeactivateLanguage=".t3js-languagePacks-deactivateLanguage",this.selectorDeactivateLanguageIcon="#t3js-languagePacks-deactivate-icon",this.selectorUpdate=".t3js-languagePacks-update",this.selectorLanguageUpdateIcon="#t3js-languagePacks-languageUpdate-icon",this.selectorNotifications=".t3js-languagePacks-notifications",this.activeLanguages=[],this.activeExtensions=[],this.packsUpdateDetails={toHandle:0,handled:0,updated:0,new:0,failed:0,skipped:0},this.notifications=[]}static pluralize(t,a="pack",e="s",s=0){return 1!==t&&1!==s?a+e:a}initialize(t){this.currentModal=t,this.getData(),t.on("click",this.selectorAddLanguageToggle,(()=>{t.find(this.selectorContentContainer+" "+this.selectorLanguageInactive).toggle()})),t.on("click",this.selectorActivateLanguage,(t=>{const a=$(t.target).closest(this.selectorActivateLanguage).data("iso");t.preventDefault(),this.activateLanguage(a)})),t.on("click",this.selectorDeactivateLanguage,(t=>{const a=$(t.target).closest(this.selectorDeactivateLanguage).data("iso");t.preventDefault(),this.deactivateLanguage(a)})),t.on("click",this.selectorUpdate,(t=>{const a=$(t.target).closest(this.selectorUpdate).data("iso"),e=$(t.target).closest(this.selectorUpdate).data("extension");t.preventDefault(),this.updatePacks(a,e)}))}getData(){const t=this.getModalBody();new AjaxRequest(Router.getUrl("languagePacksGetData")).get({cache:"no-cache"}).then((async a=>{const e=await a.resolve();if(!0===e.success){this.activeLanguages=e.activeLanguages,this.activeExtensions=e.activeExtensions,t.empty().append(e.html);const a=t.parent().find(this.selectorContentContainer);a.empty(),a.append(this.languageMatrixHtml(e)),a.append(this.extensionMatrixHtml(e))}else{const t=InfoBox.render(Severity.error,"Something went wrong","");this.addNotification(t)}this.renderNotifications()}),(a=>{Router.handleAjaxError(a,t)}))}activateLanguage(t){const a=this.getModalBody(),e=this.findInModal(this.selectorOutputContainer),s=ProgressBar.render(Severity.loading,"Loading...","");e.empty().append(s),this.getNotificationBox().empty(),new AjaxRequest(Router.getUrl()).post({install:{action:"languagePacksActivateLanguage",token:this.getModuleContent().data("language-packs-activate-language-token"),iso:t}}).then((async t=>{const a=await t.resolve();if(e.empty(),!0===a.success&&Array.isArray(a.status))a.status.forEach((t=>{const a=InfoBox.render(t.severity,t.title,t.message);this.addNotification(a)}));else{const t=FlashMessage.render(Severity.error,"Something went wrong","");this.addNotification(t)}this.getData()}),(t=>{Router.handleAjaxError(t,a)}))}deactivateLanguage(t){const a=this.getModalBody(),e=this.findInModal(this.selectorOutputContainer),s=ProgressBar.render(Severity.loading,"Loading...","");e.empty().append(s),this.getNotificationBox().empty(),new AjaxRequest(Router.getUrl()).post({install:{action:"languagePacksDeactivateLanguage",token:this.getModuleContent().data("language-packs-deactivate-language-token"),iso:t}}).then((async t=>{const a=await t.resolve();if(e.empty(),!0===a.success&&Array.isArray(a.status))a.status.forEach((t=>{const a=InfoBox.render(t.severity,t.title,t.message);this.addNotification(a)}));else{const t=FlashMessage.render(Severity.error,"Something went wrong","");this.addNotification(t)}this.getData()}),(t=>{Router.handleAjaxError(t,a)}))}updatePacks(t,a){const e=this.findInModal(this.selectorOutputContainer),s=this.findInModal(this.selectorContentContainer),n=void 0===t?this.activeLanguages:[t];let i=!0,o=this.activeExtensions;void 0!==a&&(o=[a],i=!1),this.packsUpdateDetails={toHandle:n.length*o.length,handled:0,updated:0,new:0,failed:0,skipped:0},e.empty().append($("<div>",{class:"progress"}).append($("<div>",{class:"progress-bar progress-bar-info",role:"progressbar","aria-valuenow":0,"aria-valuemin":0,"aria-valuemax":100,style:"width: 0;"}).append($("<span>",{class:"text-nowrap"}).text("0 of "+this.packsUpdateDetails.toHandle+" language "+LanguagePacks.pluralize(this.packsUpdateDetails.toHandle)+" updated")))),s.empty(),n.forEach((t=>{o.forEach((a=>{this.getNotificationBox().empty(),new AjaxRequest(Router.getUrl()).post({install:{action:"languagePacksUpdatePack",token:this.getModuleContent().data("language-packs-update-pack-token"),iso:t,extension:a}}).then((async t=>{const a=await t.resolve();!0===a.success?(this.packsUpdateDetails.handled++,"new"===a.packResult?this.packsUpdateDetails.new++:"update"===a.packResult?this.packsUpdateDetails.updated++:"skipped"===a.packResult?this.packsUpdateDetails.skipped++:this.packsUpdateDetails.failed++,this.packUpdateDone(i,n)):(this.packsUpdateDetails.handled++,this.packsUpdateDetails.failed++,this.packUpdateDone(i,n))}),(()=>{this.packsUpdateDetails.handled++,this.packsUpdateDetails.failed++,this.packUpdateDone(i,n)}))}))}))}packUpdateDone(t,a){const e=this.getModalBody(),s=this.findInModal(this.selectorOutputContainer);if(this.packsUpdateDetails.handled===this.packsUpdateDetails.toHandle){const s=InfoBox.render(Severity.ok,"Language packs updated",this.packsUpdateDetails.new+" new language "+LanguagePacks.pluralize(this.packsUpdateDetails.new)+" downloaded, "+this.packsUpdateDetails.updated+" language "+LanguagePacks.pluralize(this.packsUpdateDetails.updated)+" updated, "+this.packsUpdateDetails.skipped+" language "+LanguagePacks.pluralize(this.packsUpdateDetails.skipped)+" skipped, "+this.packsUpdateDetails.failed+" language "+LanguagePacks.pluralize(this.packsUpdateDetails.failed)+" not available");this.addNotification(s),!0===t?new AjaxRequest(Router.getUrl()).post({install:{action:"languagePacksUpdateIsoTimes",token:this.getModuleContent().data("language-packs-update-iso-times-token"),isos:a}}).then((async t=>{if(!0===(await t.resolve()).success)this.getData();else{const t=FlashMessage.render(Severity.error,"Something went wrong","");this.addNotification(t)}}),(t=>{Router.handleAjaxError(t,e)})):this.getData()}else{const t=this.packsUpdateDetails.handled/this.packsUpdateDetails.toHandle*100;s.find(".progress-bar").css("width",t+"%").attr("aria-valuenow",t).find("span").text(this.packsUpdateDetails.handled+" of "+this.packsUpdateDetails.toHandle+" language "+LanguagePacks.pluralize(this.packsUpdateDetails.handled,"pack","s",this.packsUpdateDetails.toHandle)+" updated")}}languageMatrixHtml(t){const a=this.findInModal(this.selectorActivateLanguageIcon).html(),e=this.findInModal(this.selectorDeactivateLanguageIcon).html(),s=this.findInModal(this.selectorLanguageUpdateIcon).html(),n=$("<div>"),i=$("<tbody>");return t.languages.forEach((t=>{const n=t.active,o=$("<tr>");n?i.append(o.append($("<td>").text(" "+t.name).prepend($("<div />",{class:"btn-group"}).append($("<a>",{class:"btn btn-default t3js-languagePacks-deactivateLanguage","data-iso":t.iso,title:"Deactivate"}).append(e),$("<a>",{class:"btn btn-default t3js-languagePacks-update","data-iso":t.iso,title:"Download language packs"}).append(s))))):i.append(o.addClass("t3-languagePacks-inactive t3js-languagePacks-inactive").css({display:"none"}).append($("<td>").text(" "+t.name).prepend($("<div />",{class:"btn-group"}).append($("<a>",{class:"btn btn-default t3js-languagePacks-activateLanguage","data-iso":t.iso,title:"Activate"}).append(a))))),o.append($("<td>").text(t.iso),$("<td>").text(t.dependencies.join(", ")),$("<td>").text(null===t.lastUpdate?"":t.lastUpdate)),i.append(o)})),n.append($("<h3>").text("Active languages"),$("<table>",{class:"table table-striped table-bordered"}).append($("<thead>").append($("<tr>").append($("<th>").append($("<div />",{class:"btn-group"}).append($("<button>",{class:"btn btn-default t3js-languagePacks-addLanguage-toggle",type:"button"}).append($("<span>").append(a)," Add language"),$("<button>",{class:"btn btn-default disabled update-all t3js-languagePacks-update",type:"button",disabled:"disabled"}).append($("<span>").append(s)," Update all"))),$("<th>").text("Locale"),$("<th>").text("Dependencies"),$("<th>").text("Last update"))),i)),Array.isArray(this.activeLanguages)&&this.activeLanguages.length&&n.find(".update-all").removeClass("disabled").removeAttr("disabled"),n.html()}extensionMatrixHtml(t){const a=new SecurityUtility,e=this.findInModal(this.selectorLanguageUpdateIcon).html();let s,n="",i=0;const o=$("<div>"),d=$("<tr>");d.append($("<th>").text("Extension"),$("<th>").text("Key")),t.activeLanguages.forEach((t=>{d.append($("<th>").append($("<a>",{class:"btn btn-default t3js-languagePacks-update","data-iso":t,title:"Download and update all language packs"}).append($("<span>").append(e)," "+t)))}));const l=$("<tbody>");return t.extensions.forEach((o=>{i++,s=void 0!==o.icon?$("<span>").append($("<img>",{style:"max-height: 16px; max-width: 16px;",src:o.icon,alt:o.title}),$("<span>").text(" "+o.title)):$("<span>").text(o.title);const d=$("<tr>");d.append($("<td>").html(s.html()),$("<td>").text(o.key)),t.activeLanguages.forEach((t=>{let s=!1;if(o.packs.forEach((i=>{if(i.iso!==t)return;s=!0;const l=$("<td>");d.append(l),n=!0!==i.exists?null!==i.lastUpdate?"No language pack available for "+i.iso+" when tried at "+i.lastUpdate+". Click to re-try.":"Language pack not downloaded. Click to download":null===i.lastUpdate?"Downloaded. Click to renew":"Language pack downloaded at "+i.lastUpdate+". Click to renew",l.append($("<a>",{class:"btn btn-default t3js-languagePacks-update","data-extension":o.key,"data-iso":i.iso,title:a.encodeHtml(n)}).append(e))})),!s){const t=$("<td>");d.append(t).append(" ")}})),l.append(d)})),o.append($("<h3>").text("Translation status"),$("<table>",{class:"table table-striped table-bordered"}).append($("<thead>").append(d),l)),0===i?InfoBox.render(Severity.ok,"Language packs have been found for every installed extension.","To download the latest changes, use the refresh button in the list above."):o.html()}getNotificationBox(){return this.findInModal(this.selectorNotifications)}addNotification(t){this.notifications.push(t)}renderNotifications(){const t=this.getNotificationBox();for(const a of this.notifications)t.append(a);this.notifications=[]}}export default new LanguagePacks; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/settings/extension-configuration.js b/typo3/sysext/install/Resources/Public/JavaScript/module/settings/extension-configuration.js index 158b244a729a..f3a501ecaa07 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/module/settings/extension-configuration.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/module/settings/extension-configuration.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import"bootstrap";import $ from"jquery";import"@typo3/install/renderable/clearable.js";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import ModuleMenu from"@typo3/backend/module-menu.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Router from"@typo3/install/router.js";import{topLevelModuleImport}from"@typo3/backend/utility/top-level-module-import.js";class ExtensionConfiguration extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorFormListener=".t3js-extensionConfiguration-form",this.selectorSearchInput=".t3js-extensionConfiguration-search"}initialize(t){this.currentModal=t,this.getContent(),t.on("keydown",(e=>{const a=t.find(this.selectorSearchInput);e.ctrlKey||e.metaKey?"f"===String.fromCharCode(e.which).toLowerCase()&&(e.preventDefault(),a.trigger("focus")):27===e.keyCode&&(e.preventDefault(),a.val("").trigger("focus"))})),t.on("keyup",this.selectorSearchInput,(e=>{const a=$(e.target).val(),o=t.find(this.selectorSearchInput);t.find(".search-item").each(((t,e)=>{const o=$(e);$(":contains("+a+")",o).length>0||$('input[value*="'+a+'"]',o).length>0?o.removeClass("hidden").addClass("searchhit"):o.removeClass("searchhit").addClass("hidden")})),t.find(".searchhit").collapse("show");const r=o.get(0);r.clearable(),r.focus()})),t.on("submit",this.selectorFormListener,(t=>{t.preventDefault(),this.write($(t.currentTarget))}))}getContent(){const t=this.getModalBody();new AjaxRequest(Router.getUrl("extensionConfigurationGetContent")).get({cache:"no-cache"}).then((async e=>{const a=await e.resolve();!0===a.success&&(t.html(a.html),this.initializeWrap(),this.initializeColorPicker())}),(e=>{Router.handleAjaxError(e,t)}))}initializeColorPicker(){window.location!==window.parent.location?topLevelModuleImport("@typo3/backend/color-picker.js").then((({default:t})=>{parent.document.querySelectorAll(".t3js-color-input").forEach((e=>t.initialize(e)))})):import("@typo3/backend/color-picker.js").then((({default:t})=>{document.querySelectorAll(".t3js-color-input").forEach((e=>t.initialize(e)))}))}write(t){const e=this.getModalBody(),a=this.getModuleContent().data("extension-configuration-write-token"),o={};for(let e of t.serializeArray())o[e.name]=e.value;new AjaxRequest(Router.getUrl()).post({install:{token:a,action:"extensionConfigurationWrite",extensionKey:t.attr("data-extensionKey"),extensionConfiguration:o}}).then((async t=>{const e=await t.resolve();!0===e.success&&Array.isArray(e.status)?(e.status.forEach((t=>{Notification.showMessage(t.title,t.message,t.severity)})),"backend"===$("body").data("context")&&ModuleMenu.App.refreshMenu()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}initializeWrap(){this.findInModal(".t3js-emconf-offset").each(((t,e)=>{const a=$(e),o=a.parent(),r=a.attr("id"),i=a.attr("value").split(",");a.attr("data-offsetfield-x","#"+r+"_offset_x").attr("data-offsetfield-y","#"+r+"_offset_y").wrap('<div class="hidden"></div>');const n=$("<div>",{class:"form-multigroup-item"}).append($("<div>",{class:"input-group"}).append($("<div>",{class:"input-group-addon"}).text("x"),$("<input>",{id:r+"_offset_x",class:"form-control t3js-emconf-offsetfield","data-target":"#"+r,value:i[0]?.trim()}))),s=$("<div>",{class:"form-multigroup-item"}).append($("<div>",{class:"input-group"}).append($("<div>",{class:"input-group-addon"}).text("y"),$("<input>",{id:r+"_offset_y",class:"form-control t3js-emconf-offsetfield","data-target":"#"+r,value:i[1]?.trim()}))),l=$("<div>",{class:"form-multigroup-wrap"}).append(n,s);o.append(l),o.find(".t3js-emconf-offsetfield").on("keyup",(t=>{const e=o.find($(t.currentTarget).data("target"));e.val(o.find(e.data("offsetfield-x")).val()+","+o.find(e.data("offsetfield-y")).val())}))})),this.findInModal(".t3js-emconf-wrap").each(((t,e)=>{const a=$(e),o=a.parent(),r=a.attr("id"),i=a.attr("value").split("|");a.attr("data-wrapfield-start","#"+r+"_wrap_start").attr("data-wrapfield-end","#"+r+"_wrap_end").wrap('<div class="hidden"></div>');const n=$("<div>",{class:"form-multigroup-wrap"}).append($("<div>",{class:"form-multigroup-item"}).append($("<input>",{id:r+"_wrap_start",class:"form-control t3js-emconf-wrapfield","data-target":"#"+r,value:i[0]?.trim()})),$("<div>",{class:"form-multigroup-item"}).append($("<input>",{id:r+"_wrap_end",class:"form-control t3js-emconf-wrapfield","data-target":"#"+r,value:i[1]?.trim()})));o.append(n),o.find(".t3js-emconf-wrapfield").on("keyup",(t=>{const e=o.find($(t.currentTarget).data("target"));e.val(o.find(e.data("wrapfield-start")).val()+"|"+o.find(e.data("wrapfield-end")).val())}))}))}}export default new ExtensionConfiguration; \ No newline at end of file +import"bootstrap";import $ from"jquery";import"@typo3/install/renderable/clearable.js";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import ModuleMenu from"@typo3/backend/module-menu.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Router from"@typo3/install/router.js";import{topLevelModuleImport}from"@typo3/backend/utility/top-level-module-import.js";class ExtensionConfiguration extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorFormListener=".t3js-extensionConfiguration-form",this.selectorSearchInput=".t3js-extensionConfiguration-search"}initialize(t){this.currentModal=t,this.getContent(),t.on("keydown",(e=>{const a=t.find(this.selectorSearchInput);e.ctrlKey||e.metaKey?"f"===String.fromCharCode(e.which).toLowerCase()&&(e.preventDefault(),a.trigger("focus")):27===e.keyCode&&(e.preventDefault(),a.val("").trigger("focus"))})),t.on("keyup",this.selectorSearchInput,(e=>{const a=$(e.target).val(),o=t.find(this.selectorSearchInput);t.find(".search-item").each(((t,e)=>{const o=$(e);$(":contains("+a+")",o).length>0||$('input[value*="'+a+'"]',o).length>0?o.removeClass("hidden").addClass("searchhit"):o.removeClass("searchhit").addClass("hidden")})),t.find(".searchhit").collapse("show");const r=o.get(0);r.clearable(),r.focus()})),t.on("submit",this.selectorFormListener,(t=>{t.preventDefault(),this.write($(t.currentTarget))}))}getContent(){const t=this.getModalBody();new AjaxRequest(Router.getUrl("extensionConfigurationGetContent")).get({cache:"no-cache"}).then((async e=>{const a=await e.resolve();!0===a.success&&(t.html(a.html),this.initializeWrap(),this.initializeColorPicker())}),(e=>{Router.handleAjaxError(e,t)}))}initializeColorPicker(){window.location!==window.parent.location?topLevelModuleImport("@typo3/backend/color-picker.js").then((({default:t})=>{parent.document.querySelectorAll(".t3js-color-input").forEach((e=>t.initialize(e)))})):import("@typo3/backend/color-picker.js").then((({default:t})=>{document.querySelectorAll(".t3js-color-input").forEach((e=>t.initialize(e)))}))}write(t){const e=this.getModalBody(),a=this.getModuleContent().data("extension-configuration-write-token"),o={};for(const e of t.serializeArray())o[e.name]=e.value;new AjaxRequest(Router.getUrl()).post({install:{token:a,action:"extensionConfigurationWrite",extensionKey:t.attr("data-extensionKey"),extensionConfiguration:o}}).then((async t=>{const e=await t.resolve();!0===e.success&&Array.isArray(e.status)?(e.status.forEach((t=>{Notification.showMessage(t.title,t.message,t.severity)})),"backend"===$("body").data("context")&&ModuleMenu.App.refreshMenu()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}initializeWrap(){this.findInModal(".t3js-emconf-offset").each(((t,e)=>{const a=$(e),o=a.parent(),r=a.attr("id"),i=a.attr("value").split(",");a.attr("data-offsetfield-x","#"+r+"_offset_x").attr("data-offsetfield-y","#"+r+"_offset_y").wrap('<div class="hidden"></div>');const n=$("<div>",{class:"form-multigroup-item"}).append($("<div>",{class:"input-group"}).append($("<div>",{class:"input-group-addon"}).text("x"),$("<input>",{id:r+"_offset_x",class:"form-control t3js-emconf-offsetfield","data-target":"#"+r,value:i[0]?.trim()}))),s=$("<div>",{class:"form-multigroup-item"}).append($("<div>",{class:"input-group"}).append($("<div>",{class:"input-group-addon"}).text("y"),$("<input>",{id:r+"_offset_y",class:"form-control t3js-emconf-offsetfield","data-target":"#"+r,value:i[1]?.trim()}))),l=$("<div>",{class:"form-multigroup-wrap"}).append(n,s);o.append(l),o.find(".t3js-emconf-offsetfield").on("keyup",(t=>{const e=o.find($(t.currentTarget).data("target"));e.val(o.find(e.data("offsetfield-x")).val()+","+o.find(e.data("offsetfield-y")).val())}))})),this.findInModal(".t3js-emconf-wrap").each(((t,e)=>{const a=$(e),o=a.parent(),r=a.attr("id"),i=a.attr("value").split("|");a.attr("data-wrapfield-start","#"+r+"_wrap_start").attr("data-wrapfield-end","#"+r+"_wrap_end").wrap('<div class="hidden"></div>');const n=$("<div>",{class:"form-multigroup-wrap"}).append($("<div>",{class:"form-multigroup-item"}).append($("<input>",{id:r+"_wrap_start",class:"form-control t3js-emconf-wrapfield","data-target":"#"+r,value:i[0]?.trim()})),$("<div>",{class:"form-multigroup-item"}).append($("<input>",{id:r+"_wrap_end",class:"form-control t3js-emconf-wrapfield","data-target":"#"+r,value:i[1]?.trim()})));o.append(n),o.find(".t3js-emconf-wrapfield").on("keyup",(t=>{const e=o.find($(t.currentTarget).data("target"));e.val(o.find(e.data("wrapfield-start")).val()+"|"+o.find(e.data("wrapfield-end")).val())}))}))}}export default new ExtensionConfiguration; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/settings/features.js b/typo3/sysext/install/Resources/Public/JavaScript/module/settings/features.js index 54f52698d4d0..d199a8f648ab 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/module/settings/features.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/module/settings/features.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Router from"@typo3/install/router.js";class Features extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorSaveTrigger=".t3js-features-save"}initialize(e){this.currentModal=e,this.getContent(),e.on("click",this.selectorSaveTrigger,(e=>{e.preventDefault(),this.save()}))}getContent(){const e=this.getModalBody();new AjaxRequest(Router.getUrl("featuresGetContent")).get({cache:"no-cache"}).then((async t=>{const o=await t.resolve();!0===o.success&&"undefined"!==o.html&&o.html.length>0?(e.empty().append(o.html),Modal.setButtons(o.buttons)):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}save(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=this.getModuleContent().data("features-save-token"),o={};for(let e of this.findInModal("form").serializeArray())o[e.name]=e.value;o["install[action]"]="featuresSave",o["install[token]"]=t,new AjaxRequest(Router.getUrl()).post(o).then((async e=>{const t=await e.resolve();!0===t.success&&Array.isArray(t.status)?(t.status.forEach((e=>{Notification.showMessage(e.title,e.message,e.severity)})),this.getContent()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}}export default new Features; \ No newline at end of file +import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Router from"@typo3/install/router.js";class Features extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorSaveTrigger=".t3js-features-save"}initialize(e){this.currentModal=e,this.getContent(),e.on("click",this.selectorSaveTrigger,(e=>{e.preventDefault(),this.save()}))}getContent(){const e=this.getModalBody();new AjaxRequest(Router.getUrl("featuresGetContent")).get({cache:"no-cache"}).then((async t=>{const o=await t.resolve();!0===o.success&&"undefined"!==o.html&&o.html.length>0?(e.empty().append(o.html),Modal.setButtons(o.buttons)):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}save(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=this.getModuleContent().data("features-save-token"),o={};for(const e of this.findInModal("form").serializeArray())o[e.name]=e.value;o["install[action]"]="featuresSave",o["install[token]"]=t,new AjaxRequest(Router.getUrl()).post(o).then((async e=>{const t=await e.resolve();!0===t.success&&Array.isArray(t.status)?(t.status.forEach((e=>{Notification.showMessage(e.title,e.message,e.severity)})),this.getContent()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}}export default new Features; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/settings/presets.js b/typo3/sysext/install/Resources/Public/JavaScript/module/settings/presets.js index 2d6bc54e0467..c115b9a58cee 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/module/settings/presets.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/module/settings/presets.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Router from"@typo3/install/router.js";class Presets extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorActivateTrigger=".t3js-presets-activate",this.selectorImageExecutable=".t3js-presets-image-executable",this.selectorImageExecutableTrigger=".t3js-presets-image-executable-trigger"}initialize(e){this.currentModal=e,this.getContent(),e.on("click",this.selectorImageExecutableTrigger,(e=>{e.preventDefault(),this.getCustomImagePathContent()})),e.on("click",this.selectorActivateTrigger,(e=>{e.preventDefault(),this.activate()})),e.find(".t3js-custom-preset").on("input",".t3js-custom-preset",(e=>{$("#"+$(e.currentTarget).data("radio")).prop("checked",!0)}))}getContent(){const e=this.getModalBody();new AjaxRequest(Router.getUrl("presetsGetContent")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();!0===s.success&&"undefined"!==s.html&&s.html.length>0?(e.empty().append(s.html),Modal.setButtons(s.buttons)):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}getCustomImagePathContent(){const e=this.getModalBody(),t=this.getModuleContent().data("presets-content-token");new AjaxRequest(Router.getUrl()).post({install:{token:t,action:"presetsGetContent",values:{Image:{additionalSearchPath:this.findInModal(this.selectorImageExecutable).val()}}}}).then((async t=>{const s=await t.resolve();!0===s.success&&"undefined"!==s.html&&s.html.length>0?e.empty().append(s.html):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}activate(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=this.getModuleContent().data("presets-activate-token"),s={};for(let e of this.findInModal("form").serializeArray())s[e.name]=e.value;s["install[action]"]="presetsActivate",s["install[token]"]=t,new AjaxRequest(Router.getUrl()).post(s).then((async e=>{const t=await e.resolve();!0===t.success&&Array.isArray(t.status)?t.status.forEach((e=>{Notification.showMessage(e.title,e.message,e.severity)})):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)})).finally((()=>{this.setModalButtonsState(!0)}))}}export default new Presets; \ No newline at end of file +import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Router from"@typo3/install/router.js";class Presets extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorActivateTrigger=".t3js-presets-activate",this.selectorImageExecutable=".t3js-presets-image-executable",this.selectorImageExecutableTrigger=".t3js-presets-image-executable-trigger"}initialize(e){this.currentModal=e,this.getContent(),e.on("click",this.selectorImageExecutableTrigger,(e=>{e.preventDefault(),this.getCustomImagePathContent()})),e.on("click",this.selectorActivateTrigger,(e=>{e.preventDefault(),this.activate()})),e.find(".t3js-custom-preset").on("input",".t3js-custom-preset",(e=>{$("#"+$(e.currentTarget).data("radio")).prop("checked",!0)}))}getContent(){const e=this.getModalBody();new AjaxRequest(Router.getUrl("presetsGetContent")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();!0===s.success&&"undefined"!==s.html&&s.html.length>0?(e.empty().append(s.html),Modal.setButtons(s.buttons)):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}getCustomImagePathContent(){const e=this.getModalBody(),t=this.getModuleContent().data("presets-content-token");new AjaxRequest(Router.getUrl()).post({install:{token:t,action:"presetsGetContent",values:{Image:{additionalSearchPath:this.findInModal(this.selectorImageExecutable).val()}}}}).then((async t=>{const s=await t.resolve();!0===s.success&&"undefined"!==s.html&&s.html.length>0?e.empty().append(s.html):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}activate(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=this.getModuleContent().data("presets-activate-token"),s={};for(const e of this.findInModal("form").serializeArray())s[e.name]=e.value;s["install[action]"]="presetsActivate",s["install[token]"]=t,new AjaxRequest(Router.getUrl()).post(s).then((async e=>{const t=await e.resolve();!0===t.success&&Array.isArray(t.status)?t.status.forEach((e=>{Notification.showMessage(e.title,e.message,e.severity)})):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)})).finally((()=>{this.setModalButtonsState(!0)}))}}export default new Presets; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/settings/system-maintainer.js b/typo3/sysext/install/Resources/Public/JavaScript/module/settings/system-maintainer.js index ede0a8e88b86..3b5525f211d7 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/module/settings/system-maintainer.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/module/settings/system-maintainer.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import{topLevelModuleImport}from"@typo3/backend/utility/top-level-module-import.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Router from"@typo3/install/router.js";class SystemMaintainer extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorWriteTrigger=".t3js-systemMaintainer-write",this.selectorChosenContainer=".t3js-systemMaintainer-chosen",this.selectorChosenField=".t3js-systemMaintainer-chosen-select"}initialize(t){this.currentModal=t;window.location!==window.parent.location?topLevelModuleImport("@typo3/install/chosen.jquery.min.js").then((()=>{this.getList()})):import("@typo3/install/chosen.jquery.min.js").then((()=>{this.getList()})),t.on("click",this.selectorWriteTrigger,(t=>{t.preventDefault(),this.write()}))}getList(){const t=this.getModalBody();new AjaxRequest(Router.getUrl("systemMaintainerGetList")).get({cache:"no-cache"}).then((async e=>{const s=await e.resolve();if(!0===s.success){t.html(s.html),Modal.setButtons(s.buttons),Array.isArray(s.users)&&s.users.forEach((e=>{let s=e.username;e.disable&&(s="[DISABLED] "+s);const o=$("<option>",{value:e.uid}).text(s);e.isSystemMaintainer&&o.attr("selected","selected"),t.find(this.selectorChosenField).append(o)}));const e={".t3js-systemMaintainer-chosen-select":{width:"100%",placeholder_text_multiple:"users"}};for(const s in e)e.hasOwnProperty(s)&&t.find(s).chosen(e[s]);t.find(this.selectorChosenContainer).show(),t.find(this.selectorChosenField).trigger("chosen:updated")}}),(e=>{Router.handleAjaxError(e,t)}))}write(){this.setModalButtonsState(!1);const t=this.getModalBody(),e=this.getModuleContent().data("system-maintainer-write-token"),s=this.findInModal(this.selectorChosenField).val();new AjaxRequest(Router.getUrl()).post({install:{users:s,token:e,action:"systemMaintainerWrite"}}).then((async t=>{const e=await t.resolve();!0===e.success?Array.isArray(e.status)&&e.status.forEach((t=>{Notification.success(t.title,t.message)})):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)})).finally((()=>{this.setModalButtonsState(!0)}))}}export default new SystemMaintainer; \ No newline at end of file +import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import{topLevelModuleImport}from"@typo3/backend/utility/top-level-module-import.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Router from"@typo3/install/router.js";class SystemMaintainer extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorWriteTrigger=".t3js-systemMaintainer-write",this.selectorChosenContainer=".t3js-systemMaintainer-chosen",this.selectorChosenField=".t3js-systemMaintainer-chosen-select"}initialize(t){this.currentModal=t;window.location!==window.parent.location?topLevelModuleImport("@typo3/install/chosen.jquery.min.js").then((()=>{this.getList()})):import("@typo3/install/chosen.jquery.min.js").then((()=>{this.getList()})),t.on("click",this.selectorWriteTrigger,(t=>{t.preventDefault(),this.write()}))}getList(){const t=this.getModalBody();new AjaxRequest(Router.getUrl("systemMaintainerGetList")).get({cache:"no-cache"}).then((async e=>{const s=await e.resolve();if(!0===s.success){t.html(s.html),Modal.setButtons(s.buttons),Array.isArray(s.users)&&s.users.forEach((e=>{let s=e.username;e.disable&&(s="[DISABLED] "+s);const o=$("<option>",{value:e.uid}).text(s);e.isSystemMaintainer&&o.attr("selected","selected"),t.find(this.selectorChosenField).append(o)}));const e={".t3js-systemMaintainer-chosen-select":{width:"100%",placeholder_text_multiple:"users"}};for(const s in e)s in e&&t.find(s).chosen(e[s]);t.find(this.selectorChosenContainer).show(),t.find(this.selectorChosenField).trigger("chosen:updated")}}),(e=>{Router.handleAjaxError(e,t)}))}write(){this.setModalButtonsState(!1);const t=this.getModalBody(),e=this.getModuleContent().data("system-maintainer-write-token"),s=this.findInModal(this.selectorChosenField).val();new AjaxRequest(Router.getUrl()).post({install:{users:s,token:e,action:"systemMaintainerWrite"}}).then((async t=>{const e=await t.resolve();!0===e.success?Array.isArray(e.status)&&e.status.forEach((t=>{Notification.success(t.title,t.message)})):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)})).finally((()=>{this.setModalButtonsState(!0)}))}}export default new SystemMaintainer; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/core-update.js b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/core-update.js index b94206cfbd2c..85262335c708 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/core-update.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/core-update.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import FlashMessage from"@typo3/install/renderable/flash-message.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class CoreUpdate extends AbstractInteractableModule{constructor(){super(...arguments),this.actionQueue={coreUpdateIsUpdateAvailable:{loadingMessage:"Checking for possible regular or security update",finishMessage:void 0,nextActionName:void 0},coreUpdateCheckPreConditions:{loadingMessage:"Checking if update is possible",finishMessage:"System can be updated",nextActionName:"coreUpdateDownload"},coreUpdateDownload:{loadingMessage:"Downloading new core",finishMessage:void 0,nextActionName:"coreUpdateVerifyChecksum"},coreUpdateVerifyChecksum:{loadingMessage:"Verifying checksum of downloaded core",finishMessage:void 0,nextActionName:"coreUpdateUnpack"},coreUpdateUnpack:{loadingMessage:"Unpacking core",finishMessage:void 0,nextActionName:"coreUpdateMove"},coreUpdateMove:{loadingMessage:"Moving core",finishMessage:void 0,nextActionName:"coreUpdateActivate"},coreUpdateActivate:{loadingMessage:"Activating core",finishMessage:"Core updated - please reload your browser",nextActionName:void 0}},this.selectorOutput=".t3js-coreUpdate-output",this.updateButton=".t3js-coreUpdate-button",this.buttonTemplate=null}initialize(e){this.currentModal=e,this.getData().then((()=>{this.buttonTemplate=this.findInModal(this.updateButton).clone()})),e.on("click",".t3js-coreUpdate-init",(e=>{e.preventDefault();const t=$(e.currentTarget).attr("data-action");switch(this.findInModal(this.selectorOutput).empty(),t){case"checkForUpdate":this.callAction("coreUpdateIsUpdateAvailable");break;case"updateDevelopment":this.update("development");break;case"updateRegular":this.update("regular");break;default:throw'Unknown update action "'+t+'"'}}))}getData(){const e=this.getModalBody();return new AjaxRequest(Router.getUrl("coreUpdateGetData")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success?(e.empty().append(a.html),Modal.setButtons(a.buttons)):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}update(e){"development"!==e&&(e="regular"),this.callAction("coreUpdateCheckPreConditions",e)}callAction(e,t){const a={install:{action:e}};void 0!==t&&(a.install.type=t),this.addLoadingMessage(this.actionQueue[e].loadingMessage),new AjaxRequest(Router.getUrl()).withQueryArguments(a).get({cache:"no-cache"}).then((async a=>{const o=await a.resolve();!0===this.handleResult(o,this.actionQueue[e].finishMessage)&&void 0!==this.actionQueue[e].nextActionName&&this.callAction(this.actionQueue[e].nextActionName,t)}),(e=>{Router.handleAjaxError(e,this.getModalBody())}))}handleResult(e,t){const a=e.success;return this.removeLoadingMessage(),e.status&&"object"==typeof e.status&&this.showStatusMessages(e.status),e.action&&"object"==typeof e.action&&this.showActionButton(e.action),a&&t&&this.addMessage(Severity.ok,t),a}addLoadingMessage(e){const t=FlashMessage.render(Severity.loading,e);this.findInModal(this.selectorOutput).append(t)}removeLoadingMessage(){this.findInModal(this.selectorOutput).find(".alert-loading").remove()}showStatusMessages(e){for(let t of e)this.addMessage(t.severity,t.title??"",t.message??"")}showActionButton(e){let t=!1,a=!1;e.title&&(t=e.title),e.action&&(a=e.action);const o=this.buttonTemplate;a&&o.attr("data-action",a),t&&o.text(t),this.findInModal(this.updateButton).replaceWith(o)}addMessage(e,t,a){const o=FlashMessage.render(e,t,a);this.findInModal(this.selectorOutput).append(o)}}export default new CoreUpdate; \ No newline at end of file +import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import FlashMessage from"@typo3/install/renderable/flash-message.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class CoreUpdate extends AbstractInteractableModule{constructor(){super(...arguments),this.actionQueue={coreUpdateIsUpdateAvailable:{loadingMessage:"Checking for possible regular or security update",finishMessage:void 0,nextActionName:void 0},coreUpdateCheckPreConditions:{loadingMessage:"Checking if update is possible",finishMessage:"System can be updated",nextActionName:"coreUpdateDownload"},coreUpdateDownload:{loadingMessage:"Downloading new core",finishMessage:void 0,nextActionName:"coreUpdateVerifyChecksum"},coreUpdateVerifyChecksum:{loadingMessage:"Verifying checksum of downloaded core",finishMessage:void 0,nextActionName:"coreUpdateUnpack"},coreUpdateUnpack:{loadingMessage:"Unpacking core",finishMessage:void 0,nextActionName:"coreUpdateMove"},coreUpdateMove:{loadingMessage:"Moving core",finishMessage:void 0,nextActionName:"coreUpdateActivate"},coreUpdateActivate:{loadingMessage:"Activating core",finishMessage:"Core updated - please reload your browser",nextActionName:void 0}},this.selectorOutput=".t3js-coreUpdate-output",this.updateButton=".t3js-coreUpdate-button",this.buttonTemplate=null}initialize(e){this.currentModal=e,this.getData().then((()=>{this.buttonTemplate=this.findInModal(this.updateButton).clone()})),e.on("click",".t3js-coreUpdate-init",(e=>{e.preventDefault();const t=$(e.currentTarget).attr("data-action");switch(this.findInModal(this.selectorOutput).empty(),t){case"checkForUpdate":this.callAction("coreUpdateIsUpdateAvailable");break;case"updateDevelopment":this.update("development");break;case"updateRegular":this.update("regular");break;default:throw'Unknown update action "'+t+'"'}}))}getData(){const e=this.getModalBody();return new AjaxRequest(Router.getUrl("coreUpdateGetData")).get({cache:"no-cache"}).then((async t=>{const a=await t.resolve();!0===a.success?(e.empty().append(a.html),Modal.setButtons(a.buttons)):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}update(e){"development"!==e&&(e="regular"),this.callAction("coreUpdateCheckPreConditions",e)}callAction(e,t){const a={install:{action:e}};void 0!==t&&(a.install.type=t),this.addLoadingMessage(this.actionQueue[e].loadingMessage),new AjaxRequest(Router.getUrl()).withQueryArguments(a).get({cache:"no-cache"}).then((async a=>{const o=await a.resolve();!0===this.handleResult(o,this.actionQueue[e].finishMessage)&&void 0!==this.actionQueue[e].nextActionName&&this.callAction(this.actionQueue[e].nextActionName,t)}),(e=>{Router.handleAjaxError(e,this.getModalBody())}))}handleResult(e,t){const a=e.success;return this.removeLoadingMessage(),e.status&&"object"==typeof e.status&&this.showStatusMessages(e.status),e.action&&"object"==typeof e.action&&this.showActionButton(e.action),a&&t&&this.addMessage(Severity.ok,t),a}addLoadingMessage(e){const t=FlashMessage.render(Severity.loading,e);this.findInModal(this.selectorOutput).append(t)}removeLoadingMessage(){this.findInModal(this.selectorOutput).find(".alert-loading").remove()}showStatusMessages(e){for(const t of e)this.addMessage(t.severity,t.title??"",t.message??"")}showActionButton(e){const t=this.buttonTemplate;e.action&&t.attr("data-action",e.action),e.title&&t.text(e.title),this.findInModal(this.updateButton).replaceWith(t)}addMessage(e,t,a){const o=FlashMessage.render(e,t,a);this.findInModal(this.selectorOutput).append(o)}}export default new CoreUpdate; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/extension-compat-tester.js b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/extension-compat-tester.js index c0b2dbee8296..8a88cc331be6 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/extension-compat-tester.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/extension-compat-tester.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class ExtensionCompatTester extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorCheckTrigger=".t3js-extensionCompatTester-check",this.selectorUninstallTrigger=".t3js-extensionCompatTester-uninstall",this.selectorOutputContainer=".t3js-extensionCompatTester-output"}initialize(e){this.currentModal=e,this.getLoadedExtensionList(),e.on("click",this.selectorCheckTrigger,(()=>{this.findInModal(this.selectorUninstallTrigger).addClass("hidden"),this.findInModal(this.selectorOutputContainer).empty(),this.getLoadedExtensionList()})),e.on("click",this.selectorUninstallTrigger,(e=>{this.uninstallExtension($(e.target).data("extension"))}))}getLoadedExtensionList(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=this.findInModal(this.selectorOutputContainer);if(t.length){const e=ProgressBar.render(Severity.loading,"Loading...","");t.append(e)}new AjaxRequest(Router.getUrl("extensionCompatTesterLoadedExtensionList")).get({cache:"no-cache"}).then((async t=>{const o=await t.resolve();e.empty().append(o.html),Modal.setButtons(o.buttons);const n=this.findInModal(this.selectorOutputContainer),s=ProgressBar.render(Severity.loading,"Loading...","");n.append(s),!0===o.success?this.loadExtLocalconf().then((()=>{n.append(InfoBox.render(Severity.ok,"ext_localconf.php of all loaded extensions successfully loaded","")),this.loadExtTables().then((()=>{n.append(InfoBox.render(Severity.ok,"ext_tables.php of all loaded extensions successfully loaded",""))}),(async e=>{this.renderFailureMessages("ext_tables.php",(await e.response.json()).brokenExtensions,n)})).finally((()=>{this.unlockModal()}))}),(async e=>{this.renderFailureMessages("ext_localconf.php",(await e.response.json()).brokenExtensions,n),n.append(InfoBox.render(Severity.notice,"Skipped scanning ext_tables.php files due to previous errors","")),this.unlockModal()})):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}unlockModal(){this.findInModal(this.selectorOutputContainer).find(".alert-loading").remove(),this.findInModal(this.selectorCheckTrigger).removeClass("disabled").prop("disabled",!1)}renderFailureMessages(e,t,o){for(let n of t){let t;n.isProtected||(t=$("<button />",{class:"btn btn-danger t3js-extensionCompatTester-uninstall"}).attr("data-extension",n.name).text('Uninstall extension "'+n.name+'"')),o.append(InfoBox.render(Severity.error,"Loading "+e+' of extension "'+n.name+'" failed',n.isProtected?"Extension is mandatory and cannot be uninstalled.":""),t)}this.unlockModal()}loadExtLocalconf(){const e=this.getModuleContent().data("extension-compat-tester-load-ext_localconf-token");return new AjaxRequest(Router.getUrl()).post({install:{action:"extensionCompatTesterLoadExtLocalconf",token:e}})}loadExtTables(){const e=this.getModuleContent().data("extension-compat-tester-load-ext_tables-token");return new AjaxRequest(Router.getUrl()).post({install:{action:"extensionCompatTesterLoadExtTables",token:e}})}uninstallExtension(e){const t=this.getModuleContent().data("extension-compat-tester-uninstall-extension-token"),o=this.getModalBody(),n=$(this.selectorOutputContainer),s=ProgressBar.render(Severity.loading,"Loading...","");n.append(s),new AjaxRequest(Router.getUrl()).post({install:{action:"extensionCompatTesterUninstallExtension",token:t,extension:e}}).then((async e=>{const t=await e.resolve();t.success?(Array.isArray(t.status)&&t.status.forEach((e=>{const t=InfoBox.render(e.severity,e.title,e.message);o.find(this.selectorOutputContainer).empty().append(t)})),this.findInModal(this.selectorUninstallTrigger).addClass("hidden"),this.getLoadedExtensionList()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,o)}))}}export default new ExtensionCompatTester; \ No newline at end of file +import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class ExtensionCompatTester extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorCheckTrigger=".t3js-extensionCompatTester-check",this.selectorUninstallTrigger=".t3js-extensionCompatTester-uninstall",this.selectorOutputContainer=".t3js-extensionCompatTester-output"}initialize(e){this.currentModal=e,this.getLoadedExtensionList(),e.on("click",this.selectorCheckTrigger,(()=>{this.findInModal(this.selectorUninstallTrigger).addClass("hidden"),this.findInModal(this.selectorOutputContainer).empty(),this.getLoadedExtensionList()})),e.on("click",this.selectorUninstallTrigger,(e=>{this.uninstallExtension($(e.target).data("extension"))}))}getLoadedExtensionList(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=this.findInModal(this.selectorOutputContainer);if(t.length){const e=ProgressBar.render(Severity.loading,"Loading...","");t.append(e)}new AjaxRequest(Router.getUrl("extensionCompatTesterLoadedExtensionList")).get({cache:"no-cache"}).then((async t=>{const o=await t.resolve();e.empty().append(o.html),Modal.setButtons(o.buttons);const n=this.findInModal(this.selectorOutputContainer),s=ProgressBar.render(Severity.loading,"Loading...","");n.append(s),!0===o.success?this.loadExtLocalconf().then((()=>{n.append(InfoBox.render(Severity.ok,"ext_localconf.php of all loaded extensions successfully loaded","")),this.loadExtTables().then((()=>{n.append(InfoBox.render(Severity.ok,"ext_tables.php of all loaded extensions successfully loaded",""))}),(async e=>{this.renderFailureMessages("ext_tables.php",(await e.response.json()).brokenExtensions,n)})).finally((()=>{this.unlockModal()}))}),(async e=>{this.renderFailureMessages("ext_localconf.php",(await e.response.json()).brokenExtensions,n),n.append(InfoBox.render(Severity.notice,"Skipped scanning ext_tables.php files due to previous errors","")),this.unlockModal()})):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(t=>{Router.handleAjaxError(t,e)}))}unlockModal(){this.findInModal(this.selectorOutputContainer).find(".alert-loading").remove(),this.findInModal(this.selectorCheckTrigger).removeClass("disabled").prop("disabled",!1)}renderFailureMessages(e,t,o){for(const n of t){let t;n.isProtected||(t=$("<button />",{class:"btn btn-danger t3js-extensionCompatTester-uninstall"}).attr("data-extension",n.name).text('Uninstall extension "'+n.name+'"')),o.append(InfoBox.render(Severity.error,"Loading "+e+' of extension "'+n.name+'" failed',n.isProtected?"Extension is mandatory and cannot be uninstalled.":""),t)}this.unlockModal()}loadExtLocalconf(){const e=this.getModuleContent().data("extension-compat-tester-load-ext_localconf-token");return new AjaxRequest(Router.getUrl()).post({install:{action:"extensionCompatTesterLoadExtLocalconf",token:e}})}loadExtTables(){const e=this.getModuleContent().data("extension-compat-tester-load-ext_tables-token");return new AjaxRequest(Router.getUrl()).post({install:{action:"extensionCompatTesterLoadExtTables",token:e}})}uninstallExtension(e){const t=this.getModuleContent().data("extension-compat-tester-uninstall-extension-token"),o=this.getModalBody(),n=$(this.selectorOutputContainer),s=ProgressBar.render(Severity.loading,"Loading...","");n.append(s),new AjaxRequest(Router.getUrl()).post({install:{action:"extensionCompatTesterUninstallExtension",token:t,extension:e}}).then((async e=>{const t=await e.resolve();t.success?(Array.isArray(t.status)&&t.status.forEach((e=>{const t=InfoBox.render(e.severity,e.title,e.message);o.find(this.selectorOutputContainer).empty().append(t)})),this.findInModal(this.selectorUninstallTrigger).addClass("hidden"),this.getLoadedExtensionList()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,o)}))}}export default new ExtensionCompatTester; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/extension-scanner.js b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/extension-scanner.js index cf00f6036aba..f7f59bba8e34 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/extension-scanner.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/extension-scanner.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import"bootstrap";import $ from"jquery";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxQueue from"@typo3/install/ajax/ajax-queue.js";import Router from"@typo3/install/router.js";class ExtensionScanner extends AbstractInteractableModule{constructor(){super(...arguments),this.listOfAffectedRestFileHashes=[],this.selectorExtensionContainer=".t3js-extensionScanner-extension",this.selectorNumberOfFiles=".t3js-extensionScanner-number-of-files",this.selectorScanSingleTrigger=".t3js-extensionScanner-scan-single",this.selectorExtensionScanButton=".t3js-extensionScanner-scan-all"}initialize(e){this.currentModal=e,this.getData(),e.on("show.bs.collapse",this.selectorExtensionContainer,(e=>{const n=$(e.currentTarget);if(void 0===n.data("scanned")){const e=n.data("extension");this.scanSingleExtension(e),n.data("scanned",!0)}})).on("typo3-modal-hide",(()=>{AjaxQueue.flush()})).on("click",this.selectorScanSingleTrigger,(e=>{e.preventDefault();const n=$(e.currentTarget).closest(this.selectorExtensionContainer).data("extension");this.scanSingleExtension(n)})).on("click",this.selectorExtensionScanButton,(n=>{n.preventDefault(),this.setModalButtonsState(!1);const t=e.find(this.selectorExtensionContainer);this.scanAll(t)}))}getData(){const e=this.getModalBody();new AjaxRequest(Router.getUrl("extensionScannerGetData")).get().then((async n=>{const t=await n.resolve();!0===t.success?(e.empty().append(t.html),Modal.setButtons(t.buttons)):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(n=>{Router.handleAjaxError(n,e)}))}getExtensionSelector(e){return this.selectorExtensionContainer+"-"+e}scanAll(e){this.findInModal(this.selectorExtensionContainer).removeClass("panel-danger panel-warning panel-success").find(".panel-progress-bar").css("width",0).attr("aria-valuenow",0).find("span").text("0%"),this.setProgressForAll(),e.each(((e,n)=>{const t=$(n),s=t.data("extension");this.scanSingleExtension(s),t.data("scanned",!0)}))}setStatusMessageForScan(e,n,t){this.findInModal(this.getExtensionSelector(e)).find(this.selectorNumberOfFiles).text("Checked "+n+" of "+t+" files")}setProgressForScan(e,n,t){const s=n/t*100;this.findInModal(this.getExtensionSelector(e)).find(".panel-progress-bar").css("width",s+"%").attr("aria-valuenow",s).find("span").text(s+"%")}setProgressForAll(){const e=this.findInModal(this.selectorExtensionContainer).length,n=this.findInModal(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-success").length+this.findInModal(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-warning").length+this.findInModal(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-danger").length,t=n/e*100,s=this.getModalBody();this.findInModal(".t3js-extensionScanner-progress-all-extension .progress-bar").css("width",t+"%").attr("aria-valuenow",t).find("span").text(n+" of "+e+" scanned"),n===e&&(this.findInModal(this.selectorExtensionScanButton).removeClass("disabled").prop("disabled",!1),Notification.success("Scan finished","All extensions have been scanned."),new AjaxRequest(Router.getUrl()).post({install:{action:"extensionScannerMarkFullyScannedRestFiles",token:this.getModuleContent().data("extension-scanner-mark-fully-scanned-rest-files-token"),hashes:Array.from(new Set(this.listOfAffectedRestFileHashes))}}).then((async e=>{const n=await e.resolve();!0===n.success&&Notification.success("Marked not affected files","Marked "+n.markedAsNotAffected+" ReST files as not affected.")}),(e=>{Router.handleAjaxError(e,s)})))}scanSingleExtension(e){const n=this.getModuleContent().data("extension-scanner-files-token"),t=this.getModalBody(),s=this.findInModal(this.getExtensionSelector(e));let a=!1;s.addClass("panel-default"),s.removeClass("panel-danger panel-warning panel-success t3js-extensionscan-finished"),s.data("hasRun","true"),s.find(".t3js-extensionScanner-scan-single").text("Scanning...").attr("disabled","disabled"),s.find(".t3js-extensionScanner-extension-body-loc").empty().text("0"),s.find(".t3js-extensionScanner-extension-body-ignored-files").empty().text("0"),s.find(".t3js-extensionScanner-extension-body-ignored-lines").empty().text("0"),this.setProgressForAll(),new AjaxRequest(Router.getUrl()).post({install:{action:"extensionScannerFiles",token:n,extension:e}}).then((async n=>{const i=await n.resolve();if(!0===i.success&&Array.isArray(i.files)){const n=i.files.length;if(n<=0)return void Notification.warning("No files found","The extension "+e+" contains no scannable files");this.setStatusMessageForScan(e,0,n),s.find(".t3js-extensionScanner-extension-body").text(""),s.addClass("panel-has-progress");let o=0;i.files.forEach((i=>{AjaxQueue.add({method:"POST",data:{install:{action:"extensionScannerScanFile",token:this.getModuleContent().data("extension-scanner-scan-file-token"),extension:e,file:i}},url:Router.getUrl(),onfulfilled:async r=>{const l=await r.resolve();if(o++,this.setStatusMessageForScan(e,o,n),this.setProgressForScan(e,o,n),l.success&&Array.isArray(l.matches)&&l.matches.forEach((e=>{a=!0;const n=t.find("#t3js-extensionScanner-file-hit-template").find(".panel").clone();n.find(".t3js-extensionScanner-hit-file-panel-head").attr("href","#collapse"+e.uniqueId),n.find(".t3js-extensionScanner-hit-file-panel-body").attr("id","collapse"+e.uniqueId),n.find(".t3js-extensionScanner-hit-filename").text(i),n.find(".t3js-extensionScanner-hit-message").text(e.message),"strong"===e.indicator?n.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge badge-danger" title="Reliable match, false positive unlikely">strong</span>'):n.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge badge-warning" title="Probable match, but can be a false positive">weak</span>'),!0===e.silenced&&n.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge badge-info" title="Match has been annotated by extension author as false positive match">silenced</span>'),n.find(".t3js-extensionScanner-hit-file-lineContent").empty().text(e.lineContent),n.find(".t3js-extensionScanner-hit-file-line").empty().text(e.line+": "),Array.isArray(e.restFiles)&&e.restFiles.forEach((e=>{const s=t.find("#t3js-extensionScanner-file-hit-rest-template").find(".panel").clone();s.find(".t3js-extensionScanner-hit-rest-panel-head").attr("href","#collapse"+e.uniqueId),s.find(".t3js-extensionScanner-hit-rest-panel-head .badge").empty().text(e.version),s.find(".t3js-extensionScanner-hit-rest-panel-body").attr("id","collapse"+e.uniqueId),s.find(".t3js-extensionScanner-hit-rest-headline").text(e.headline),s.find(".t3js-extensionScanner-hit-rest-body").text(e.content),s.addClass("panel-"+e.class),n.find(".t3js-extensionScanner-hit-file-rest-container").append(s),this.listOfAffectedRestFileHashes.push(e.file_hash)}));const o=n.find(".panel-breaking",".t3js-extensionScanner-hit-file-rest-container").length>0?"panel-danger":"panel-warning";n.addClass(o),n.removeClass("panel-default"),s.find(".t3js-extensionScanner-extension-body").removeClass("hide").append(n),s.removeClass("panel-default"),"panel-danger"===o&&(s.removeClass("panel-warning"),s.addClass(o)),"panel-warning"!==o||s.hasClass("panel-danger")||s.addClass(o)})),l.success){const e=parseInt(s.find(".t3js-extensionScanner-extension-body-loc").text(),10);if(s.find(".t3js-extensionScanner-extension-body-loc").empty().text(e+l.effectiveCodeLines),l.isFileIgnored){const e=parseInt(s.find(".t3js-extensionScanner-extension-body-ignored-files").text(),10);s.find(".t3js-extensionScanner-extension-body-ignored-files").empty().text(e+1)}const n=parseInt(s.find(".t3js-extensionScanner-extension-body-ignored-lines").text(),10);s.find(".t3js-extensionScanner-extension-body-ignored-lines").empty().text(n+l.ignoredLines)}o===n&&(a||(s.removeClass("panel-default"),s.addClass("panel-success")),s.addClass("t3js-extensionscan-finished"),s.removeClass("panel-has-progress"),this.setProgressForAll(),s.find(".t3js-extensionScanner-scan-single").text("Rescan").attr("disabled",null))},onrejected:t=>{o+=1,this.setStatusMessageForScan(e,o,n),this.setProgressForScan(e,o,n),s.removeClass("panel-has-progress"),this.setProgressForAll(),console.error(t)}})}))}else Notification.error("Oops, an error occurred","Please look at the browser console output for details"),console.error(i)}),(e=>{Router.handleAjaxError(e,t)}))}}export default new ExtensionScanner; \ No newline at end of file +import"bootstrap";import $ from"jquery";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxQueue from"@typo3/install/ajax/ajax-queue.js";import Router from"@typo3/install/router.js";class ExtensionScanner extends AbstractInteractableModule{constructor(){super(...arguments),this.listOfAffectedRestFileHashes=[],this.selectorExtensionContainer=".t3js-extensionScanner-extension",this.selectorNumberOfFiles=".t3js-extensionScanner-number-of-files",this.selectorScanSingleTrigger=".t3js-extensionScanner-scan-single",this.selectorExtensionScanButton=".t3js-extensionScanner-scan-all"}initialize(e){this.currentModal=e,this.getData(),e.on("show.bs.collapse",this.selectorExtensionContainer,(e=>{const n=$(e.currentTarget);if(void 0===n.data("scanned")){const e=n.data("extension");this.scanSingleExtension(e),n.data("scanned",!0)}})).on("typo3-modal-hide",(()=>{AjaxQueue.flush()})).on("click",this.selectorScanSingleTrigger,(e=>{e.preventDefault();const n=$(e.currentTarget).closest(this.selectorExtensionContainer).data("extension");this.scanSingleExtension(n)})).on("click",this.selectorExtensionScanButton,(n=>{n.preventDefault(),this.setModalButtonsState(!1);const t=e.find(this.selectorExtensionContainer);this.scanAll(t)}))}getData(){const e=this.getModalBody();new AjaxRequest(Router.getUrl("extensionScannerGetData")).get().then((async n=>{const t=await n.resolve();!0===t.success?(e.empty().append(t.html),Modal.setButtons(t.buttons)):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(n=>{Router.handleAjaxError(n,e)}))}getExtensionSelector(e){return this.selectorExtensionContainer+"-"+e}scanAll(e){this.findInModal(this.selectorExtensionContainer).removeClass("panel-danger panel-warning panel-success").find(".panel-progress-bar").css("width",0).attr("aria-valuenow",0).find("span").text("0%"),this.setProgressForAll(),e.each(((e,n)=>{const t=$(n),s=t.data("extension");this.scanSingleExtension(s),t.data("scanned",!0)}))}setStatusMessageForScan(e,n,t){this.findInModal(this.getExtensionSelector(e)).find(this.selectorNumberOfFiles).text("Checked "+n+" of "+t+" files")}setProgressForScan(e,n,t){const s=n/t*100;this.findInModal(this.getExtensionSelector(e)).find(".panel-progress-bar").css("width",s+"%").attr("aria-valuenow",s).find("span").text(s+"%")}setProgressForAll(){const e=this.findInModal(this.selectorExtensionContainer).length,n=this.findInModal(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-success").length+this.findInModal(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-warning").length+this.findInModal(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-danger").length,t=n/e*100,s=this.getModalBody();this.findInModal(".t3js-extensionScanner-progress-all-extension .progress-bar").css("width",t+"%").attr("aria-valuenow",t).find("span").text(n+" of "+e+" scanned"),n===e&&(this.findInModal(this.selectorExtensionScanButton).removeClass("disabled").prop("disabled",!1),Notification.success("Scan finished","All extensions have been scanned."),new AjaxRequest(Router.getUrl()).post({install:{action:"extensionScannerMarkFullyScannedRestFiles",token:this.getModuleContent().data("extension-scanner-mark-fully-scanned-rest-files-token"),hashes:Array.from(new Set(this.listOfAffectedRestFileHashes))}}).then((async e=>{const n=await e.resolve();!0===n.success&&Notification.success("Marked not affected files","Marked "+n.markedAsNotAffected+" ReST files as not affected.")}),(e=>{Router.handleAjaxError(e,s)})))}scanSingleExtension(e){const n=this.getModuleContent().data("extension-scanner-files-token"),t=this.getModalBody(),s=this.findInModal(this.getExtensionSelector(e));let a=!1;s.addClass("panel-default"),s.removeClass("panel-danger panel-warning panel-success t3js-extensionscan-finished"),s.data("hasRun","true"),s.find(".t3js-extensionScanner-scan-single").text("Scanning...").attr("disabled","disabled"),s.find(".t3js-extensionScanner-extension-body-loc").empty().text("0"),s.find(".t3js-extensionScanner-extension-body-ignored-files").empty().text("0"),s.find(".t3js-extensionScanner-extension-body-ignored-lines").empty().text("0"),this.setProgressForAll(),new AjaxRequest(Router.getUrl()).post({install:{action:"extensionScannerFiles",token:n,extension:e}}).then((async n=>{const i=await n.resolve();if(!0===i.success&&Array.isArray(i.files)){const n=i.files.length;if(n<=0)return void Notification.warning("No files found","The extension "+e+" contains no scannable files");this.setStatusMessageForScan(e,0,n),s.find(".t3js-extensionScanner-extension-body").text(""),s.addClass("panel-has-progress");let o=0;i.files.forEach((i=>{AjaxQueue.add({method:"POST",data:{install:{action:"extensionScannerScanFile",token:this.getModuleContent().data("extension-scanner-scan-file-token"),extension:e,file:i}},url:Router.getUrl(),onfulfilled:async r=>{const l=await r.resolve();if(o++,this.setStatusMessageForScan(e,o,n),this.setProgressForScan(e,o,n),l.success&&Array.isArray(l.matches)&&l.matches.forEach((e=>{a=!0;const n=t.find("#t3js-extensionScanner-file-hit-template").find(".panel").clone();n.find(".t3js-extensionScanner-hit-file-panel-head").attr("href","#collapse"+e.uniqueId),n.find(".t3js-extensionScanner-hit-file-panel-body").attr("id","collapse"+e.uniqueId),n.find(".t3js-extensionScanner-hit-filename").text(i),n.find(".t3js-extensionScanner-hit-message").text(e.message),"strong"===e.indicator?n.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge badge-danger" title="Reliable match, false positive unlikely">strong</span>'):n.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge badge-warning" title="Probable match, but can be a false positive">weak</span>'),!0===e.silenced&&n.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge badge-info" title="Match has been annotated by extension author as false positive match">silenced</span>'),n.find(".t3js-extensionScanner-hit-file-lineContent").empty().text(e.lineContent),n.find(".t3js-extensionScanner-hit-file-line").empty().text(e.line+": "),Array.isArray(e.restFiles)&&e.restFiles.forEach((e=>{const s=t.find("#t3js-extensionScanner-file-hit-rest-template").find(".panel").clone();s.find(".t3js-extensionScanner-hit-rest-panel-head").attr("href","#collapse"+e.uniqueId),s.find(".t3js-extensionScanner-hit-rest-panel-head .badge").empty().text(e.version),s.find(".t3js-extensionScanner-hit-rest-panel-body").attr("id","collapse"+e.uniqueId),s.find(".t3js-extensionScanner-hit-rest-headline").text(e.headline),s.find(".t3js-extensionScanner-hit-rest-body").text(e.content),s.addClass("panel-"+e.class),n.find(".t3js-extensionScanner-hit-file-rest-container").append(s),this.listOfAffectedRestFileHashes.push(e.file_hash)}));const o=n.find(".panel-breaking, .t3js-extensionScanner-hit-file-rest-container").length>0?"panel-danger":"panel-warning";n.addClass(o),n.removeClass("panel-default"),s.find(".t3js-extensionScanner-extension-body").removeClass("hide").append(n),s.removeClass("panel-default"),"panel-danger"===o&&(s.removeClass("panel-warning"),s.addClass(o)),"panel-warning"!==o||s.hasClass("panel-danger")||s.addClass(o)})),l.success){const e=parseInt(s.find(".t3js-extensionScanner-extension-body-loc").text(),10);if(s.find(".t3js-extensionScanner-extension-body-loc").empty().text(e+l.effectiveCodeLines),l.isFileIgnored){const e=parseInt(s.find(".t3js-extensionScanner-extension-body-ignored-files").text(),10);s.find(".t3js-extensionScanner-extension-body-ignored-files").empty().text(e+1)}const n=parseInt(s.find(".t3js-extensionScanner-extension-body-ignored-lines").text(),10);s.find(".t3js-extensionScanner-extension-body-ignored-lines").empty().text(n+l.ignoredLines)}o===n&&(a||(s.removeClass("panel-default"),s.addClass("panel-success")),s.addClass("t3js-extensionscan-finished"),s.removeClass("panel-has-progress"),this.setProgressForAll(),s.find(".t3js-extensionScanner-scan-single").text("Rescan").attr("disabled",null))},onrejected:t=>{o+=1,this.setStatusMessageForScan(e,o,n),this.setProgressForScan(e,o,n),s.removeClass("panel-has-progress"),this.setProgressForAll(),console.error(t)}})}))}else Notification.error("Oops, an error occurred","Please look at the browser console output for details"),console.error(i)}),(e=>{Router.handleAjaxError(e,t)}))}}export default new ExtensionScanner; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/tca-ext-tables-check.js b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/tca-ext-tables-check.js index afd7440a080c..a1def5de7ed5 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/tca-ext-tables-check.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/tca-ext-tables-check.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class TcaExtTablesCheck extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorCheckTrigger=".t3js-tcaExtTablesCheck-check",this.selectorOutputContainer=".t3js-tcaExtTablesCheck-output"}initialize(e){this.currentModal=e,this.check(),e.on("click",this.selectorCheckTrigger,(e=>{e.preventDefault(),this.check()}))}check(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=$(this.selectorOutputContainer),o=ProgressBar.render(Severity.loading,"Loading...","");t.empty().html(o),new AjaxRequest(Router.getUrl("tcaExtTablesCheck")).get({cache:"no-cache"}).then((async o=>{const r=await o.resolve();if(e.empty().append(r.html),Modal.setButtons(r.buttons),!0===r.success&&Array.isArray(r.status))if(r.status.length>0){const o=InfoBox.render(Severity.warning,"Following extensions change TCA in ext_tables.php","Check ext_tables.php files, look for ExtensionManagementUtility calls and $GLOBALS['TCA'] modifications");e.find(this.selectorOutputContainer).append(o),r.status.forEach((o=>{const r=InfoBox.render(o.severity,o.title,o.message);t.append(r),e.append(r)}))}else{const t=InfoBox.render(Severity.ok,"No TCA changes in ext_tables.php files. Good job!","");e.find(this.selectorOutputContainer).append(t)}else Notification.error("Something went wrong",'Please use the module "Check for broken extensions" to find a possible extension causing this issue.')}),(t=>{Router.handleAjaxError(t,e)}))}}export default new TcaExtTablesCheck; \ No newline at end of file +import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class TcaExtTablesCheck extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorCheckTrigger=".t3js-tcaExtTablesCheck-check",this.selectorOutputContainer=".t3js-tcaExtTablesCheck-output"}initialize(e){this.currentModal=e,this.check(),e.on("click",this.selectorCheckTrigger,(e=>{e.preventDefault(),this.check()}))}check(){this.setModalButtonsState(!1);const e=this.getModalBody(),t=$(this.selectorOutputContainer),o=ProgressBar.render(Severity.loading,"Loading...","");t.empty().append(o),new AjaxRequest(Router.getUrl("tcaExtTablesCheck")).get({cache:"no-cache"}).then((async o=>{const r=await o.resolve();if(e.empty().append(r.html),Modal.setButtons(r.buttons),!0===r.success&&Array.isArray(r.status))if(r.status.length>0){const o=InfoBox.render(Severity.warning,"Following extensions change TCA in ext_tables.php","Check ext_tables.php files, look for ExtensionManagementUtility calls and $GLOBALS['TCA'] modifications");e.find(this.selectorOutputContainer).append(o),r.status.forEach((o=>{const r=InfoBox.render(o.severity,o.title,o.message);t.append(r),e.append(r)}))}else{const t=InfoBox.render(Severity.ok,"No TCA changes in ext_tables.php files. Good job!","");e.find(this.selectorOutputContainer).append(t)}else Notification.error("Something went wrong",'Please use the module "Check for broken extensions" to find a possible extension causing this issue.')}),(t=>{Router.handleAjaxError(t,e)}))}}export default new TcaExtTablesCheck; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/tca-migrations-check.js b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/tca-migrations-check.js index 7a4689f4486c..63008b32e7d9 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/tca-migrations-check.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/tca-migrations-check.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import FlashMessage from"@typo3/install/renderable/flash-message.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class TcaMigrationsCheck extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorCheckTrigger=".t3js-tcaMigrationsCheck-check",this.selectorOutputContainer=".t3js-tcaMigrationsCheck-output"}initialize(e){this.currentModal=e,this.check(),e.on("click",this.selectorCheckTrigger,(e=>{e.preventDefault(),this.check()}))}check(){this.setModalButtonsState(!1);const e=$(this.selectorOutputContainer),t=this.getModalBody(),r=ProgressBar.render(Severity.loading,"Loading...","");e.empty().html(r),new AjaxRequest(Router.getUrl("tcaMigrationsCheck")).get({cache:"no-cache"}).then((async e=>{const r=await e.resolve();if(t.empty().append(r.html),Modal.setButtons(r.buttons),!0===r.success&&Array.isArray(r.status))if(r.status.length>0){const e=InfoBox.render(Severity.warning,"TCA migrations need to be applied","Check the following list and apply needed changes.");t.find(this.selectorOutputContainer).empty(),t.find(this.selectorOutputContainer).append(e),r.status.forEach((e=>{const r=InfoBox.render(e.severity,e.title,e.message);t.find(this.selectorOutputContainer).append(r)}))}else{const e=InfoBox.render(Severity.ok,"No TCA migrations need to be applied","Your TCA looks good.");t.find(this.selectorOutputContainer).append(e)}else{const e=FlashMessage.render(Severity.error,"Something went wrong",'Use "Check for broken extensions"');t.find(this.selectorOutputContainer).append(e)}this.setModalButtonsState(!0)}),(e=>{Router.handleAjaxError(e,t)}))}}export default new TcaMigrationsCheck; \ No newline at end of file +import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Modal from"@typo3/backend/modal.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import FlashMessage from"@typo3/install/renderable/flash-message.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class TcaMigrationsCheck extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorCheckTrigger=".t3js-tcaMigrationsCheck-check",this.selectorOutputContainer=".t3js-tcaMigrationsCheck-output"}initialize(e){this.currentModal=e,this.check(),e.on("click",this.selectorCheckTrigger,(e=>{e.preventDefault(),this.check()}))}check(){this.setModalButtonsState(!1);const e=$(this.selectorOutputContainer),t=this.getModalBody(),r=ProgressBar.render(Severity.loading,"Loading...","");e.empty().append(r),new AjaxRequest(Router.getUrl("tcaMigrationsCheck")).get({cache:"no-cache"}).then((async e=>{const r=await e.resolve();if(t.empty().append(r.html),Modal.setButtons(r.buttons),!0===r.success&&Array.isArray(r.status))if(r.status.length>0){const e=InfoBox.render(Severity.warning,"TCA migrations need to be applied","Check the following list and apply needed changes.");t.find(this.selectorOutputContainer).empty(),t.find(this.selectorOutputContainer).append(e),r.status.forEach((e=>{const r=InfoBox.render(e.severity,e.title,e.message);t.find(this.selectorOutputContainer).append(r)}))}else{const e=InfoBox.render(Severity.ok,"No TCA migrations need to be applied","Your TCA looks good.");t.find(this.selectorOutputContainer).append(e)}else{const e=FlashMessage.render(Severity.error,"Something went wrong",'Use "Check for broken extensions"');t.find(this.selectorOutputContainer).append(e)}this.setModalButtonsState(!0)}),(e=>{Router.handleAjaxError(e,t)}))}}export default new TcaMigrationsCheck; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/upgrade-docs.js b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/upgrade-docs.js index 3a7deb481ec1..cac8a3d13a6e 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/upgrade-docs.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/upgrade-docs.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import"bootstrap";import $ from"jquery";import"@typo3/install/renderable/clearable.js";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{topLevelModuleImport}from"@typo3/backend/utility/top-level-module-import.js";import Router from"@typo3/install/router.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import"@typo3/backend/element/icon-element.js";class UpgradeDocs extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorFulltextSearch=".t3js-upgradeDocs-fulltext-search",this.selectorChosenField=".t3js-upgradeDocs-chosen-select",this.selectorChangeLogsForVersionContainer=".t3js-version-changes",this.selectorChangeLogsForVersion=".t3js-changelog-list",this.selectorUpgradeDoc=".t3js-upgrade-doc"}initialize(e){this.currentModal=e;window.location!==window.parent.location?topLevelModuleImport("@typo3/install/chosen.jquery.min.js").then((()=>{this.getContent()})):import("@typo3/install/chosen.jquery.min.js").then((()=>{this.getContent()})),e.on("click",".t3js-upgradeDocs-markRead",(e=>{this.markRead(e.target)})),e.on("click",".t3js-upgradeDocs-unmarkRead",(e=>{this.unmarkRead(e.target)})),$.expr[":"].contains=$.expr.createPseudo((e=>t=>$(t).text().toUpperCase().includes(e.toUpperCase())))}getContent(){const e=this.getModalBody();new AjaxRequest(Router.getUrl("upgradeDocsGetContent")).get({cache:"no-cache"}).then((async t=>{const o=await t.resolve();!0===o.success&&"undefined"!==o.html&&o.html.length>0&&(e.empty().append(o.html),this.initializeFullTextSearch(),this.initializeChosenSelector(),this.loadChangelogs())}),(t=>{Router.handleAjaxError(t,e)}))}loadChangelogs(){const e=[],t=this.getModalBody();this.findInModal(this.selectorChangeLogsForVersionContainer).each(((o,s)=>{const a=new AjaxRequest(Router.getUrl("upgradeDocsGetChangelogForVersion")).withQueryArguments({install:{version:s.dataset.version}}).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();if(!0===t.success){const e=$(s),o=e.find(this.selectorChangeLogsForVersion);o.html(t.html),this.moveNotRelevantDocuments(o),e.find(".t3js-panel-loading").remove()}else Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)}));e.push(a)})),Promise.all(e).then((()=>{this.fulltextSearchField.prop("disabled",!1),this.appendItemsToChosenSelector()}))}initializeFullTextSearch(){this.fulltextSearchField=this.findInModal(this.selectorFulltextSearch);const e=this.fulltextSearchField.get(0);e.clearable({onClear:()=>{this.combinedFilterSearch()}}),e.focus(),new DebounceEvent("keyup",(()=>{this.combinedFilterSearch()})).bindTo(e)}initializeChosenSelector(){this.chosenField=this.getModalBody().find(this.selectorChosenField);const e={".chosen-select":{width:"100%",placeholder_text_multiple:"tags"},".chosen-select-deselect":{allow_single_deselect:!0},".chosen-select-no-single":{disable_search_threshold:10},".chosen-select-no-results":{no_results_text:"Oops, nothing found!"},".chosen-select-width":{width:"100%"}};for(const t in e)e.hasOwnProperty(t)&&this.findInModal(t).chosen(e[t]);this.chosenField.on("change",(()=>{this.combinedFilterSearch()}))}appendItemsToChosenSelector(){let e="";$(this.findInModal(this.selectorUpgradeDoc)).each(((t,o)=>{e+=$(o).data("item-tags")+","}));const t=[...new Set(e.slice(0,-1).split(",")).values()].reduce(((e,t)=>{const o=t.toLowerCase();return e.every((e=>e.toLowerCase()!==o))&&e.push(t),e}),[]).sort(((e,t)=>e.toLowerCase().localeCompare(t.toLowerCase())));this.chosenField.prop("disabled",!1);for(let e of t)this.chosenField.append($("<option>").text(e));this.chosenField.trigger("chosen:updated")}combinedFilterSearch(){const e=this.getModalBody(),t=e.find(this.selectorUpgradeDoc);if(this.chosenField.val().length<1&&this.fulltextSearchField.val().length<1){const e=this.currentModal.find(".panel-version .panel-collapse.show");return e.one("hidden.bs.collapse",(()=>{0===this.currentModal.find(".panel-version .panel-collapse.collapsing").length&&t.removeClass("searchhit filterhit")})),void e.collapse("hide")}if(t.removeClass("searchhit filterhit"),this.chosenField.val().length>0){t.addClass("hidden").removeClass("filterhit");const o=this.chosenField.val().map((e=>'[data-item-tags*="'+e+'"]')).join("");e.find(o).removeClass("hidden").addClass("searchhit filterhit")}else t.addClass("filterhit").removeClass("hidden");const o=this.fulltextSearchField.val();e.find(".filterhit").each(((e,t)=>{const s=$(t);$(":contains("+o+")",s).length>0||$('input[value*="'+o+'"]',s).length>0?s.removeClass("hidden").addClass("searchhit"):s.removeClass("searchhit").addClass("hidden")})),e.find(".searchhit").closest(".panel-collapse").each(((e,t)=>{window.setTimeout((()=>{$(t).collapse("show")}),20)})),e.find(".panel-version").each(((e,t)=>{const o=$(t);o.find(".searchhit",".filterhit").length<1&&o.find(" > .panel-collapse").collapse("hide")}))}moveNotRelevantDocuments(e){e.find('[data-item-state="read"]').appendTo(this.findInModal(".panel-body-read")),e.find('[data-item-state="notAffected"]').appendTo(this.findInModal(".panel-body-not-affected"))}markRead(e){const t=this.getModalBody(),o=this.getModuleContent().data("upgrade-docs-mark-read-token"),s=$(e).closest("button");s.toggleClass("t3js-upgradeDocs-unmarkRead t3js-upgradeDocs-markRead"),s.find("typo3-backend-icon,.t3js-icon").replaceWith('<typo3-backend-icon identifier="actions-ban" size="small"></typo3-backend-icon>'),s.closest(".panel").appendTo(this.findInModal(".panel-body-read")),new AjaxRequest(Router.getUrl()).post({install:{ignoreFile:s.data("filepath"),token:o,action:"upgradeDocsMarkRead"}}).catch((e=>{Router.handleAjaxError(e,t)}))}unmarkRead(e){const t=this.getModalBody(),o=this.getModuleContent().data("upgrade-docs-unmark-read-token"),s=$(e).closest("button"),a=s.closest(".panel").data("item-version");s.toggleClass("t3js-upgradeDocs-markRead t3js-upgradeDocs-unmarkRead"),s.find("typo3-backend-icon,.t3js-icon").replaceWith('<typo3-backend-icon identifier="actions-check" size="small"></typo3-backend-icon>'),s.closest(".panel").appendTo(this.findInModal('*[data-group-version="'+a+'"] .panel-body')),new AjaxRequest(Router.getUrl()).post({install:{ignoreFile:s.data("filepath"),token:o,action:"upgradeDocsUnmarkRead"}}).catch((e=>{Router.handleAjaxError(e,t)}))}}export default new UpgradeDocs; \ No newline at end of file +import"bootstrap";import $ from"jquery";import"@typo3/install/renderable/clearable.js";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{topLevelModuleImport}from"@typo3/backend/utility/top-level-module-import.js";import Router from"@typo3/install/router.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";import"@typo3/backend/element/icon-element.js";class UpgradeDocs extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorFulltextSearch=".t3js-upgradeDocs-fulltext-search",this.selectorChosenField=".t3js-upgradeDocs-chosen-select",this.selectorChangeLogsForVersionContainer=".t3js-version-changes",this.selectorChangeLogsForVersion=".t3js-changelog-list",this.selectorUpgradeDoc=".t3js-upgrade-doc"}initialize(e){this.currentModal=e;window.location!==window.parent.location?topLevelModuleImport("@typo3/install/chosen.jquery.min.js").then((()=>{this.getContent()})):import("@typo3/install/chosen.jquery.min.js").then((()=>{this.getContent()})),e.on("click",".t3js-upgradeDocs-markRead",(e=>{this.markRead(e.target)})),e.on("click",".t3js-upgradeDocs-unmarkRead",(e=>{this.unmarkRead(e.target)})),$.expr[":"].contains=$.expr.createPseudo((e=>t=>$(t).text().toUpperCase().includes(e.toUpperCase())))}getContent(){const e=this.getModalBody();new AjaxRequest(Router.getUrl("upgradeDocsGetContent")).get({cache:"no-cache"}).then((async t=>{const o=await t.resolve();!0===o.success&&"undefined"!==o.html&&o.html.length>0&&(e.empty().append(o.html),this.initializeFullTextSearch(),this.initializeChosenSelector(),this.loadChangelogs())}),(t=>{Router.handleAjaxError(t,e)}))}loadChangelogs(){const e=[],t=this.getModalBody();this.findInModal(this.selectorChangeLogsForVersionContainer).each(((o,s)=>{const a=new AjaxRequest(Router.getUrl("upgradeDocsGetChangelogForVersion")).withQueryArguments({install:{version:s.dataset.version}}).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();if(!0===t.success){const e=$(s),o=e.find(this.selectorChangeLogsForVersion);o.html(t.html),this.moveNotRelevantDocuments(o),e.find(".t3js-panel-loading").remove()}else Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)}));e.push(a)})),Promise.all(e).then((()=>{this.fulltextSearchField.prop("disabled",!1),this.appendItemsToChosenSelector()}))}initializeFullTextSearch(){this.fulltextSearchField=this.findInModal(this.selectorFulltextSearch);const e=this.fulltextSearchField.get(0);e.clearable({onClear:()=>{this.combinedFilterSearch()}}),e.focus(),new DebounceEvent("keyup",(()=>{this.combinedFilterSearch()})).bindTo(e)}initializeChosenSelector(){this.chosenField=this.getModalBody().find(this.selectorChosenField);const e={".chosen-select":{width:"100%",placeholder_text_multiple:"tags"},".chosen-select-deselect":{allow_single_deselect:!0},".chosen-select-no-single":{disable_search_threshold:10},".chosen-select-no-results":{no_results_text:"Oops, nothing found!"},".chosen-select-width":{width:"100%"}};for(const t in e)t in e&&this.findInModal(t).chosen(e[t]);this.chosenField.on("change",(()=>{this.combinedFilterSearch()}))}appendItemsToChosenSelector(){let e="";$(this.findInModal(this.selectorUpgradeDoc)).each(((t,o)=>{e+=$(o).data("item-tags")+","}));const t=[...new Set(e.slice(0,-1).split(",")).values()].reduce(((e,t)=>{const o=t.toLowerCase();return e.every((e=>e.toLowerCase()!==o))&&e.push(t),e}),[]).sort(((e,t)=>e.toLowerCase().localeCompare(t.toLowerCase())));this.chosenField.prop("disabled",!1);for(const e of t)this.chosenField.append($("<option>").text(e));this.chosenField.trigger("chosen:updated")}combinedFilterSearch(){const e=this.getModalBody(),t=e.find(this.selectorUpgradeDoc);if(this.chosenField.val().length<1&&this.fulltextSearchField.val().length<1){const e=this.currentModal.find(".panel-version .panel-collapse.show");return e.one("hidden.bs.collapse",(()=>{0===this.currentModal.find(".panel-version .panel-collapse.collapsing").length&&t.removeClass("searchhit filterhit")})),void e.collapse("hide")}if(t.removeClass("searchhit filterhit"),this.chosenField.val().length>0){t.addClass("hidden").removeClass("filterhit");const o=this.chosenField.val().map((e=>'[data-item-tags*="'+e+'"]')).join("");e.find(o).removeClass("hidden").addClass("searchhit filterhit")}else t.addClass("filterhit").removeClass("hidden");const o=this.fulltextSearchField.val();e.find(".filterhit").each(((e,t)=>{const s=$(t);$(":contains("+o+")",s).length>0||$('input[value*="'+o+'"]',s).length>0?s.removeClass("hidden").addClass("searchhit"):s.removeClass("searchhit").addClass("hidden")})),e.find(".searchhit").closest(".panel-collapse").each(((e,t)=>{window.setTimeout((()=>{$(t).collapse("show")}),20)})),e.find(".panel-version").each(((e,t)=>{const o=$(t);o.find(".searchhit, .filterhit").length<1&&o.find(" > .panel-collapse").collapse("hide")}))}moveNotRelevantDocuments(e){e.find('[data-item-state="read"]').appendTo(this.findInModal(".panel-body-read")),e.find('[data-item-state="notAffected"]').appendTo(this.findInModal(".panel-body-not-affected"))}markRead(e){const t=this.getModalBody(),o=this.getModuleContent().data("upgrade-docs-mark-read-token"),s=$(e).closest("button");s.toggleClass("t3js-upgradeDocs-unmarkRead t3js-upgradeDocs-markRead"),s.find("typo3-backend-icon,.t3js-icon").replaceWith('<typo3-backend-icon identifier="actions-ban" size="small"></typo3-backend-icon>'),s.closest(".panel").appendTo(this.findInModal(".panel-body-read")),new AjaxRequest(Router.getUrl()).post({install:{ignoreFile:s.data("filepath"),token:o,action:"upgradeDocsMarkRead"}}).catch((e=>{Router.handleAjaxError(e,t)}))}unmarkRead(e){const t=this.getModalBody(),o=this.getModuleContent().data("upgrade-docs-unmark-read-token"),s=$(e).closest("button"),a=s.closest(".panel").data("item-version");s.toggleClass("t3js-upgradeDocs-markRead t3js-upgradeDocs-unmarkRead"),s.find("typo3-backend-icon,.t3js-icon").replaceWith('<typo3-backend-icon identifier="actions-check" size="small"></typo3-backend-icon>'),s.closest(".panel").appendTo(this.findInModal('*[data-group-version="'+a+'"] .panel-body')),new AjaxRequest(Router.getUrl()).post({install:{ignoreFile:s.data("filepath"),token:o,action:"upgradeDocsUnmarkRead"}}).catch((e=>{Router.handleAjaxError(e,t)}))}}export default new UpgradeDocs; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/upgrade-wizards.js b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/upgrade-wizards.js index 3f0fa765bf51..cadbf159d81e 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/upgrade-wizards.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/module/upgrade/upgrade-wizards.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import SecurityUtility from"@typo3/core/security-utility.js";import FlashMessage from"@typo3/install/renderable/flash-message.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class UpgradeWizards extends AbstractInteractableModule{constructor(){super(),this.selectorOutputWizardsContainer=".t3js-upgradeWizards-wizards-output",this.selectorOutputDoneContainer=".t3js-upgradeWizards-done-output",this.selectorWizardsBlockingAddsTemplate=".t3js-upgradeWizards-blocking-adds-template",this.selectorWizardsBlockingAddsRows=".t3js-upgradeWizards-blocking-adds-rows",this.selectorWizardsBlockingAddsExecute=".t3js-upgradeWizards-blocking-adds-execute",this.selectorWizardsBlockingCharsetTemplate=".t3js-upgradeWizards-blocking-charset-template",this.selectorWizardsBlockingCharsetFix=".t3js-upgradeWizards-blocking-charset-fix",this.selectorWizardsDoneBodyTemplate=".t3js-upgradeWizards-done-body-template",this.selectorWizardsDoneRows=".t3js-upgradeWizards-done-rows",this.selectorWizardsDoneRowTemplate=".t3js-upgradeWizards-done-row-template table tr",this.selectorWizardsDoneRowMarkUndone=".t3js-upgradeWizards-done-markUndone",this.selectorWizardsDoneRowTitle=".t3js-upgradeWizards-done-title",this.selectorWizardsListTemplate=".t3js-upgradeWizards-list-template",this.selectorWizardsListRows=".t3js-upgradeWizards-list-rows",this.selectorWizardsListRowTemplate=".t3js-upgradeWizards-list-row-template",this.selectorWizardsListRowTitle=".t3js-upgradeWizards-list-row-title",this.selectorWizardsListRowExplanation=".t3js-upgradeWizards-list-row-explanation",this.selectorWizardsListRowExecute=".t3js-upgradeWizards-list-row-execute",this.selectorWizardsInputTemplate=".t3js-upgradeWizards-input",this.selectorWizardsInputTitle=".t3js-upgradeWizards-input-title",this.selectorWizardsInputDescription=".t3js-upgradeWizards-input-description",this.selectorWizardsInputHtml=".t3js-upgradeWizards-input-html",this.selectorWizardsInputPerform=".t3js-upgradeWizards-input-perform",this.selectorWizardsInputAbort=".t3js-upgradeWizards-input-abort",this.securityUtility=new SecurityUtility}static removeLoadingMessage(e){e.find(".alert-loading").remove()}static renderProgressBar(e){return ProgressBar.render(Severity.loading,e,"")}initialize(e){this.currentModal=e,this.getData().then((()=>{this.doneUpgrades()})),e.on("click",this.selectorWizardsDoneRowMarkUndone,(e=>{this.markUndone(e.target.dataset.identifier)})),e.on("click",this.selectorWizardsBlockingCharsetFix,(()=>{this.blockingUpgradesDatabaseCharsetFix()})),e.on("click",this.selectorWizardsBlockingAddsExecute,(()=>{this.blockingUpgradesDatabaseAddsExecute()})),e.on("click",this.selectorWizardsListRowExecute,(e=>{this.wizardInput(e.target.dataset.identifier,e.target.dataset.title)})),e.on("click",this.selectorWizardsInputPerform,(e=>{this.wizardExecute(e.target.dataset.identifier,e.target.dataset.title)})),e.on("click",this.selectorWizardsInputAbort,(e=>{this.findInModal(this.selectorOutputWizardsContainer).empty(),this.wizardsList()}))}getData(){const e=this.getModalBody(),t=this.findInModal(this.selectorOutputWizardsContainer);return new AjaxRequest(Router.getUrl("upgradeWizardsGetData")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();!0===s.success?(e.empty().append(s.html),this.blockingUpgradesDatabaseCharsetTest()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)}))}blockingUpgradesDatabaseCharsetTest(){const e=this.getModalBody(),t=this.findInModal(this.selectorOutputWizardsContainer);t.empty().html(UpgradeWizards.renderProgressBar("Checking database charset...")),new AjaxRequest(Router.getUrl("upgradeWizardsBlockingDatabaseCharsetTest")).get({cache:"no-cache"}).then((async s=>{const r=await s.resolve();UpgradeWizards.removeLoadingMessage(t),!0===r.success&&(!0===r.needsUpdate?e.find(this.selectorOutputWizardsContainer).append(e.find(this.selectorWizardsBlockingCharsetTemplate)).clone():this.blockingUpgradesDatabaseAdds())}),(e=>{Router.handleAjaxError(e,t)}))}blockingUpgradesDatabaseCharsetFix(){const e=$(this.selectorOutputWizardsContainer);e.empty().html(UpgradeWizards.renderProgressBar("Setting database charset to UTF-8...")),new AjaxRequest(Router.getUrl("upgradeWizardsBlockingDatabaseCharsetFix")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();if(UpgradeWizards.removeLoadingMessage(e),!0===s.success)Array.isArray(s.status)&&s.status.length>0&&s.status.forEach((t=>{const s=InfoBox.render(t.severity,t.title,t.message);e.append(s)}));else{const t=FlashMessage.render(Severity.error,"Something went wrong","");UpgradeWizards.removeLoadingMessage(e),e.append(t)}}),(t=>{Router.handleAjaxError(t,e)}))}blockingUpgradesDatabaseAdds(){const e=this.getModalBody(),t=this.findInModal(this.selectorOutputWizardsContainer);t.empty().html(UpgradeWizards.renderProgressBar("Check for missing mandatory database tables and fields...")),new AjaxRequest(Router.getUrl("upgradeWizardsBlockingDatabaseAdds")).get({cache:"no-cache"}).then((async s=>{const r=await s.resolve();if(UpgradeWizards.removeLoadingMessage(t),!0===r.success)if(!0===r.needsUpdate){const t=e.find(this.selectorWizardsBlockingAddsTemplate).clone();"object"==typeof r.adds.tables&&r.adds.tables.forEach((e=>{const s="Table: "+this.securityUtility.encodeHtml(e.table);t.find(this.selectorWizardsBlockingAddsRows).append(s,"<br>")})),"object"==typeof r.adds.columns&&r.adds.columns.forEach((e=>{const s="Table: "+this.securityUtility.encodeHtml(e.table)+", Field: "+this.securityUtility.encodeHtml(e.field);t.find(this.selectorWizardsBlockingAddsRows).append(s,"<br>")})),"object"==typeof r.adds.indexes&&r.adds.indexes.forEach((e=>{const s="Table: "+this.securityUtility.encodeHtml(e.table)+", Index: "+this.securityUtility.encodeHtml(e.index);t.find(this.selectorWizardsBlockingAddsRows).append(s,"<br>")})),e.find(this.selectorOutputWizardsContainer).append(t)}else this.wizardsList();else Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)}))}blockingUpgradesDatabaseAddsExecute(){const e=this.findInModal(this.selectorOutputWizardsContainer);e.empty().html(UpgradeWizards.renderProgressBar("Adding database tables and fields...")),new AjaxRequest(Router.getUrl("upgradeWizardsBlockingDatabaseExecute")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();if(UpgradeWizards.removeLoadingMessage(e),Array.isArray(s.status)&&s.status.length>0&&s.status.forEach((t=>{const s=InfoBox.render(t.severity,t.title,t.message);e.append(s)})),!0===s.success)this.wizardsList();else if(Array.isArray(s.status)&&0!==s.status.length){const t=$('<div class="btn-toolbar mt-3 mb-4"></div>'),s=$('<button class="btn btn-default">Retry database migration</button>'),r=$('<button class="btn btn-danger">Proceed despite of errors</button>');s.click((()=>{this.blockingUpgradesDatabaseAddsExecute()})),r.click((()=>{t.remove(),this.wizardsList()})),t.append(s),t.append(r),e.append(t)}else{const t=FlashMessage.render(Severity.error,"Something went wrong","");e.append(t)}}),(t=>{Router.handleAjaxError(t,e)}))}wizardsList(){const e=this.getModalBody(),t=this.findInModal(this.selectorOutputWizardsContainer);t.append(UpgradeWizards.renderProgressBar("Loading upgrade wizards...")),new AjaxRequest(Router.getUrl("upgradeWizardsList")).get({cache:"no-cache"}).then((async s=>{const r=await s.resolve();UpgradeWizards.removeLoadingMessage(t);const a=e.find(this.selectorWizardsListTemplate).clone();if(a.removeClass("t3js-upgradeWizards-list-template"),!0===r.success){let t=0,s=0;Array.isArray(r.wizards)&&r.wizards.length>0&&(s=r.wizards.length,r.wizards.forEach((s=>{if(!0===s.shouldRenderWizard){const r=e.find(this.selectorWizardsListRowTemplate).clone();t+=1,r.removeClass("t3js-upgradeWizards-list-row-template"),r.find(this.selectorWizardsListRowTitle).empty().text(s.title),r.find(this.selectorWizardsListRowExplanation).empty().text(s.explanation),r.find(this.selectorWizardsListRowExecute).attr("data-identifier",s.identifier).attr("data-title",s.title),a.find(this.selectorWizardsListRows).append(r)}})),a.find(this.selectorWizardsListRows+" hr:last").remove());let i=100;const o=a.find(".progress-bar");t>0?i=Math.round((s-t)/r.wizards.length*100):o.removeClass("progress-bar-info").addClass("progress-bar-success"),o.removeClass("progress-bar-striped").css("width",i+"%").attr("aria-valuenow",i).find("span").text(i+"%"),e.find(this.selectorOutputWizardsContainer).append(a),this.findInModal(this.selectorWizardsDoneRowMarkUndone).prop("disabled",!1)}else Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)}))}wizardInput(e,t){const s=this.getModuleContent().data("upgrade-wizards-input-token"),r=this.getModalBody(),a=this.findInModal(this.selectorOutputWizardsContainer);a.empty().html(UpgradeWizards.renderProgressBar('Loading "'+t+'"...')),r.animate({scrollTop:r.scrollTop()-Math.abs(r.find(".t3js-upgrade-status-section").position().top)},250),new AjaxRequest(Router.getUrl("upgradeWizardsInput")).post({install:{action:"upgradeWizardsInput",token:s,identifier:e}}).then((async e=>{const t=await e.resolve();a.empty();const s=r.find(this.selectorWizardsInputTemplate).clone();s.removeClass("t3js-upgradeWizards-input"),!0===t.success&&(Array.isArray(t.status)&&t.status.forEach((e=>{const t=FlashMessage.render(e.severity,e.title,e.message);a.append(t)})),t.userInput.wizardHtml.length>0&&s.find(this.selectorWizardsInputHtml).html(t.userInput.wizardHtml),s.find(this.selectorWizardsInputTitle).text(t.userInput.title),s.find(this.selectorWizardsInputDescription).html(this.securityUtility.stripHtml(t.userInput.description).replace(/\n/g,"<br>")),s.find(this.selectorWizardsInputPerform).attr("data-identifier",t.userInput.identifier).attr("data-title",t.userInput.title)),r.find(this.selectorOutputWizardsContainer).append(s)}),(e=>{Router.handleAjaxError(e,a)}))}wizardExecute(e,t){const s=this.getModuleContent().data("upgrade-wizards-execute-token"),r=this.getModalBody(),a={"install[action]":"upgradeWizardsExecute","install[token]":s,"install[identifier]":e};for(let e of this.findInModal(this.selectorOutputWizardsContainer+" form").serializeArray())a[e.name]=e.value;const i=this.findInModal(this.selectorOutputWizardsContainer);i.empty().html(UpgradeWizards.renderProgressBar('Executing "'+t+'"...')),this.findInModal(this.selectorWizardsDoneRowMarkUndone).prop("disabled",!0),new AjaxRequest(Router.getUrl()).post(a).then((async e=>{const t=await e.resolve();i.empty(),!0===t.success?(Array.isArray(t.status)&&t.status.forEach((e=>{const t=InfoBox.render(e.severity,e.title,e.message);i.append(t)})),this.wizardsList(),r.find(this.selectorOutputDoneContainer).empty(),this.doneUpgrades()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,i)}))}doneUpgrades(){const e=this.getModalBody(),t=e.find(this.selectorOutputDoneContainer);t.empty().html(UpgradeWizards.renderProgressBar("Loading executed upgrade wizards...")),new AjaxRequest(Router.getUrl("upgradeWizardsDoneUpgrades")).get({cache:"no-cache"}).then((async s=>{const r=await s.resolve();if(UpgradeWizards.removeLoadingMessage(t),!0===r.success){Array.isArray(r.status)&&r.status.length>0&&r.status.forEach((e=>{const s=InfoBox.render(e.severity,e.title,e.message);t.append(s)}));const s=e.find(this.selectorWizardsDoneBodyTemplate).clone(),a=s.find(this.selectorWizardsDoneRows);let i=!1;Array.isArray(r.wizardsDone)&&r.wizardsDone.length>0&&r.wizardsDone.forEach((t=>{i=!0;const s=e.find(this.selectorWizardsDoneRowTemplate).clone();s.find(this.selectorWizardsDoneRowMarkUndone).attr("data-identifier",t.identifier),s.find(this.selectorWizardsDoneRowTitle).text(t.title),a.append(s)})),Array.isArray(r.rowUpdatersDone)&&r.rowUpdatersDone.length>0&&r.rowUpdatersDone.forEach((t=>{i=!0;const s=e.find(this.selectorWizardsDoneRowTemplate).clone();s.find(this.selectorWizardsDoneRowMarkUndone).attr("data-identifier",t.identifier),s.find(this.selectorWizardsDoneRowTitle).text(t.title),a.append(s)})),i&&(e.find(this.selectorOutputDoneContainer).append(s),this.findInModal(this.selectorWizardsDoneRowMarkUndone).prop("disabled",!0))}else Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)}))}markUndone(e){const t=this.getModuleContent().data("upgrade-wizards-mark-undone-token"),s=this.getModalBody(),r=this.findInModal(this.selectorOutputDoneContainer);r.empty().html(UpgradeWizards.renderProgressBar("Marking upgrade wizard as undone...")),new AjaxRequest(Router.getUrl()).post({install:{action:"upgradeWizardsMarkUndone",token:t,identifier:e}}).then((async e=>{const t=await e.resolve();r.empty(),s.find(this.selectorOutputDoneContainer).empty(),!0===t.success&&Array.isArray(t.status)?t.status.forEach((e=>{Notification.success(e.title,e.message),this.doneUpgrades(),this.blockingUpgradesDatabaseCharsetTest()})):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,r)}))}}export default new UpgradeWizards; \ No newline at end of file +import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"@typo3/install/module/abstract-interactable-module.js";import Notification from"@typo3/backend/notification.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import SecurityUtility from"@typo3/core/security-utility.js";import FlashMessage from"@typo3/install/renderable/flash-message.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import Router from"@typo3/install/router.js";class UpgradeWizards extends AbstractInteractableModule{constructor(){super(),this.selectorOutputWizardsContainer=".t3js-upgradeWizards-wizards-output",this.selectorOutputDoneContainer=".t3js-upgradeWizards-done-output",this.selectorWizardsBlockingAddsTemplate=".t3js-upgradeWizards-blocking-adds-template",this.selectorWizardsBlockingAddsRows=".t3js-upgradeWizards-blocking-adds-rows",this.selectorWizardsBlockingAddsExecute=".t3js-upgradeWizards-blocking-adds-execute",this.selectorWizardsBlockingCharsetTemplate=".t3js-upgradeWizards-blocking-charset-template",this.selectorWizardsBlockingCharsetFix=".t3js-upgradeWizards-blocking-charset-fix",this.selectorWizardsDoneBodyTemplate=".t3js-upgradeWizards-done-body-template",this.selectorWizardsDoneRows=".t3js-upgradeWizards-done-rows",this.selectorWizardsDoneRowTemplate=".t3js-upgradeWizards-done-row-template table tr",this.selectorWizardsDoneRowMarkUndone=".t3js-upgradeWizards-done-markUndone",this.selectorWizardsDoneRowTitle=".t3js-upgradeWizards-done-title",this.selectorWizardsListTemplate=".t3js-upgradeWizards-list-template",this.selectorWizardsListRows=".t3js-upgradeWizards-list-rows",this.selectorWizardsListRowTemplate=".t3js-upgradeWizards-list-row-template",this.selectorWizardsListRowTitle=".t3js-upgradeWizards-list-row-title",this.selectorWizardsListRowExplanation=".t3js-upgradeWizards-list-row-explanation",this.selectorWizardsListRowExecute=".t3js-upgradeWizards-list-row-execute",this.selectorWizardsInputTemplate=".t3js-upgradeWizards-input",this.selectorWizardsInputTitle=".t3js-upgradeWizards-input-title",this.selectorWizardsInputDescription=".t3js-upgradeWizards-input-description",this.selectorWizardsInputHtml=".t3js-upgradeWizards-input-html",this.selectorWizardsInputPerform=".t3js-upgradeWizards-input-perform",this.selectorWizardsInputAbort=".t3js-upgradeWizards-input-abort",this.securityUtility=new SecurityUtility}static removeLoadingMessage(e){e.find(".alert-loading").remove()}static renderProgressBar(e){return ProgressBar.render(Severity.loading,e,"")}initialize(e){this.currentModal=e,this.getData().then((()=>{this.doneUpgrades()})),e.on("click",this.selectorWizardsDoneRowMarkUndone,(e=>{this.markUndone(e.target.dataset.identifier)})),e.on("click",this.selectorWizardsBlockingCharsetFix,(()=>{this.blockingUpgradesDatabaseCharsetFix()})),e.on("click",this.selectorWizardsBlockingAddsExecute,(()=>{this.blockingUpgradesDatabaseAddsExecute()})),e.on("click",this.selectorWizardsListRowExecute,(e=>{this.wizardInput(e.target.dataset.identifier,e.target.dataset.title)})),e.on("click",this.selectorWizardsInputPerform,(e=>{this.wizardExecute(e.target.dataset.identifier,e.target.dataset.title)})),e.on("click",this.selectorWizardsInputAbort,(()=>{this.findInModal(this.selectorOutputWizardsContainer).empty(),this.wizardsList()}))}getData(){const e=this.getModalBody(),t=this.findInModal(this.selectorOutputWizardsContainer);return new AjaxRequest(Router.getUrl("upgradeWizardsGetData")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();!0===s.success?(e.empty().append(s.html),this.blockingUpgradesDatabaseCharsetTest()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)}))}blockingUpgradesDatabaseCharsetTest(){const e=this.getModalBody(),t=this.findInModal(this.selectorOutputWizardsContainer);t.empty().append(UpgradeWizards.renderProgressBar("Checking database charset...")),new AjaxRequest(Router.getUrl("upgradeWizardsBlockingDatabaseCharsetTest")).get({cache:"no-cache"}).then((async s=>{const r=await s.resolve();UpgradeWizards.removeLoadingMessage(t),!0===r.success&&(!0===r.needsUpdate?e.find(this.selectorOutputWizardsContainer).append(e.find(this.selectorWizardsBlockingCharsetTemplate)).clone():this.blockingUpgradesDatabaseAdds())}),(e=>{Router.handleAjaxError(e,t)}))}blockingUpgradesDatabaseCharsetFix(){const e=$(this.selectorOutputWizardsContainer);e.empty().append(UpgradeWizards.renderProgressBar("Setting database charset to UTF-8...")),new AjaxRequest(Router.getUrl("upgradeWizardsBlockingDatabaseCharsetFix")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();if(UpgradeWizards.removeLoadingMessage(e),!0===s.success)Array.isArray(s.status)&&s.status.length>0&&s.status.forEach((t=>{const s=InfoBox.render(t.severity,t.title,t.message);e.append(s)}));else{const t=FlashMessage.render(Severity.error,"Something went wrong","");UpgradeWizards.removeLoadingMessage(e),e.append(t)}}),(t=>{Router.handleAjaxError(t,e)}))}blockingUpgradesDatabaseAdds(){const e=this.getModalBody(),t=this.findInModal(this.selectorOutputWizardsContainer);t.empty().append(UpgradeWizards.renderProgressBar("Check for missing mandatory database tables and fields...")),new AjaxRequest(Router.getUrl("upgradeWizardsBlockingDatabaseAdds")).get({cache:"no-cache"}).then((async s=>{const r=await s.resolve();if(UpgradeWizards.removeLoadingMessage(t),!0===r.success)if(!0===r.needsUpdate){const t=e.find(this.selectorWizardsBlockingAddsTemplate).clone();"object"==typeof r.adds.tables&&r.adds.tables.forEach((e=>{const s="Table: "+this.securityUtility.encodeHtml(e.table);t.find(this.selectorWizardsBlockingAddsRows).append(s,"<br>")})),"object"==typeof r.adds.columns&&r.adds.columns.forEach((e=>{const s="Table: "+this.securityUtility.encodeHtml(e.table)+", Field: "+this.securityUtility.encodeHtml(e.field);t.find(this.selectorWizardsBlockingAddsRows).append(s,"<br>")})),"object"==typeof r.adds.indexes&&r.adds.indexes.forEach((e=>{const s="Table: "+this.securityUtility.encodeHtml(e.table)+", Index: "+this.securityUtility.encodeHtml(e.index);t.find(this.selectorWizardsBlockingAddsRows).append(s,"<br>")})),e.find(this.selectorOutputWizardsContainer).append(t)}else this.wizardsList();else Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)}))}blockingUpgradesDatabaseAddsExecute(){const e=this.findInModal(this.selectorOutputWizardsContainer);e.empty().append(UpgradeWizards.renderProgressBar("Adding database tables and fields...")),new AjaxRequest(Router.getUrl("upgradeWizardsBlockingDatabaseExecute")).get({cache:"no-cache"}).then((async t=>{const s=await t.resolve();if(UpgradeWizards.removeLoadingMessage(e),Array.isArray(s.status)&&s.status.length>0&&s.status.forEach((t=>{const s=InfoBox.render(t.severity,t.title,t.message);e.append(s)})),!0===s.success)this.wizardsList();else if(Array.isArray(s.status)&&0!==s.status.length){const t=$('<div class="btn-toolbar mt-3 mb-4"></div>'),s=$('<button class="btn btn-default">Retry database migration</button>'),r=$('<button class="btn btn-danger">Proceed despite of errors</button>');s.click((()=>{this.blockingUpgradesDatabaseAddsExecute()})),r.click((()=>{t.remove(),this.wizardsList()})),t.append(s),t.append(r),e.append(t)}else{const t=FlashMessage.render(Severity.error,"Something went wrong","");e.append(t)}}),(t=>{Router.handleAjaxError(t,e)}))}wizardsList(){const e=this.getModalBody(),t=this.findInModal(this.selectorOutputWizardsContainer);t.append(UpgradeWizards.renderProgressBar("Loading upgrade wizards...")),new AjaxRequest(Router.getUrl("upgradeWizardsList")).get({cache:"no-cache"}).then((async s=>{const r=await s.resolve();UpgradeWizards.removeLoadingMessage(t);const a=e.find(this.selectorWizardsListTemplate).clone();if(a.removeClass("t3js-upgradeWizards-list-template"),!0===r.success){let t=0,s=0;Array.isArray(r.wizards)&&r.wizards.length>0&&(s=r.wizards.length,r.wizards.forEach((s=>{if(!0===s.shouldRenderWizard){const r=e.find(this.selectorWizardsListRowTemplate).clone();t+=1,r.removeClass("t3js-upgradeWizards-list-row-template"),r.find(this.selectorWizardsListRowTitle).empty().text(s.title),r.find(this.selectorWizardsListRowExplanation).empty().text(s.explanation),r.find(this.selectorWizardsListRowExecute).attr("data-identifier",s.identifier).attr("data-title",s.title),a.find(this.selectorWizardsListRows).append(r)}})),a.find(this.selectorWizardsListRows+" hr:last").remove());let i=100;const o=a.find(".progress-bar");t>0?i=Math.round((s-t)/r.wizards.length*100):o.removeClass("progress-bar-info").addClass("progress-bar-success"),o.removeClass("progress-bar-striped").css("width",i+"%").attr("aria-valuenow",i).find("span").text(i+"%"),e.find(this.selectorOutputWizardsContainer).append(a),this.findInModal(this.selectorWizardsDoneRowMarkUndone).prop("disabled",!1)}else Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)}))}wizardInput(e,t){const s=this.getModuleContent().data("upgrade-wizards-input-token"),r=this.getModalBody(),a=this.findInModal(this.selectorOutputWizardsContainer);a.empty().append(UpgradeWizards.renderProgressBar('Loading "'+t+'"...')),r.animate({scrollTop:r.scrollTop()-Math.abs(r.find(".t3js-upgrade-status-section").position().top)},250),new AjaxRequest(Router.getUrl("upgradeWizardsInput")).post({install:{action:"upgradeWizardsInput",token:s,identifier:e}}).then((async e=>{const t=await e.resolve();a.empty();const s=r.find(this.selectorWizardsInputTemplate).clone();s.removeClass("t3js-upgradeWizards-input"),!0===t.success&&(Array.isArray(t.status)&&t.status.forEach((e=>{const t=FlashMessage.render(e.severity,e.title,e.message);a.append(t)})),t.userInput.wizardHtml.length>0&&s.find(this.selectorWizardsInputHtml).html(t.userInput.wizardHtml),s.find(this.selectorWizardsInputTitle).text(t.userInput.title),s.find(this.selectorWizardsInputDescription).html(this.securityUtility.stripHtml(t.userInput.description).replace(/\n/g,"<br>")),s.find(this.selectorWizardsInputPerform).attr("data-identifier",t.userInput.identifier).attr("data-title",t.userInput.title)),r.find(this.selectorOutputWizardsContainer).append(s)}),(e=>{Router.handleAjaxError(e,a)}))}wizardExecute(e,t){const s=this.getModuleContent().data("upgrade-wizards-execute-token"),r=this.getModalBody(),a={"install[action]":"upgradeWizardsExecute","install[token]":s,"install[identifier]":e};for(const e of this.findInModal(this.selectorOutputWizardsContainer+" form").serializeArray())a[e.name]=e.value;const i=this.findInModal(this.selectorOutputWizardsContainer);i.empty().append(UpgradeWizards.renderProgressBar('Executing "'+t+'"...')),this.findInModal(this.selectorWizardsDoneRowMarkUndone).prop("disabled",!0),new AjaxRequest(Router.getUrl()).post(a).then((async e=>{const t=await e.resolve();i.empty(),!0===t.success?(Array.isArray(t.status)&&t.status.forEach((e=>{const t=InfoBox.render(e.severity,e.title,e.message);i.append(t)})),this.wizardsList(),r.find(this.selectorOutputDoneContainer).empty(),this.doneUpgrades()):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,i)}))}doneUpgrades(){const e=this.getModalBody(),t=e.find(this.selectorOutputDoneContainer);t.empty().append(UpgradeWizards.renderProgressBar("Loading executed upgrade wizards...")),new AjaxRequest(Router.getUrl("upgradeWizardsDoneUpgrades")).get({cache:"no-cache"}).then((async s=>{const r=await s.resolve();if(UpgradeWizards.removeLoadingMessage(t),!0===r.success){Array.isArray(r.status)&&r.status.length>0&&r.status.forEach((e=>{const s=InfoBox.render(e.severity,e.title,e.message);t.append(s)}));const s=e.find(this.selectorWizardsDoneBodyTemplate).clone(),a=s.find(this.selectorWizardsDoneRows);let i=!1;Array.isArray(r.wizardsDone)&&r.wizardsDone.length>0&&r.wizardsDone.forEach((t=>{i=!0;const s=e.find(this.selectorWizardsDoneRowTemplate).clone();s.find(this.selectorWizardsDoneRowMarkUndone).attr("data-identifier",t.identifier),s.find(this.selectorWizardsDoneRowTitle).text(t.title),a.append(s)})),Array.isArray(r.rowUpdatersDone)&&r.rowUpdatersDone.length>0&&r.rowUpdatersDone.forEach((t=>{i=!0;const s=e.find(this.selectorWizardsDoneRowTemplate).clone();s.find(this.selectorWizardsDoneRowMarkUndone).attr("data-identifier",t.identifier),s.find(this.selectorWizardsDoneRowTitle).text(t.title),a.append(s)})),i&&(e.find(this.selectorOutputDoneContainer).append(s),this.findInModal(this.selectorWizardsDoneRowMarkUndone).prop("disabled",!0))}else Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,t)}))}markUndone(e){const t=this.getModuleContent().data("upgrade-wizards-mark-undone-token"),s=this.getModalBody(),r=this.findInModal(this.selectorOutputDoneContainer);r.empty().append(UpgradeWizards.renderProgressBar("Marking upgrade wizard as undone...")),new AjaxRequest(Router.getUrl()).post({install:{action:"upgradeWizardsMarkUndone",token:t,identifier:e}}).then((async e=>{const t=await e.resolve();r.empty(),s.find(this.selectorOutputDoneContainer).empty(),!0===t.success&&Array.isArray(t.status)?t.status.forEach((e=>{Notification.success(e.title,e.message),this.doneUpgrades(),this.blockingUpgradesDatabaseCharsetTest()})):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")}),(e=>{Router.handleAjaxError(e,r)}))}}export default new UpgradeWizards; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/renderable/clearable.js b/typo3/sysext/install/Resources/Public/JavaScript/renderable/clearable.js index 13f1d7475cd0..b15486ad91bb 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/renderable/clearable.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/renderable/clearable.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -class Clearable{static createCloseButton(){const e=document.createElement("button");return e.type="button",e.tabIndex=-1,e.innerHTML='<span class="t3js-icon icon icon-size-small icon-state-default icon-actions-close" data-identifier="actions-close">\n <span class="icon-markup">\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">\n <path\n d="M11.9 5.5L9.4 8l2.5 2.5c.2.2.2.5 0\n .7l-.7.7c-.2.2-.5.2-.7 0L8 9.4l-2.5 2.5c-.2.2-.5.2-.7\n 0l-.7-.7c-.2-.2-.2-.5 0-.7L6.6 8 4.1 5.5c-.2-.2-.2-.5\n 0-.7l.7-.7c.2-.2.5-.2.7 0L8 6.6l2.5-2.5c.2-.2.5-.2.7\n 0l.7.7c.2.2.2.5 0 .7z"\n class="icon-color"/>\n </svg>\n </span>\n </span>',e.style.visibility="hidden",e.classList.add("close"),e}constructor(){"function"!=typeof HTMLInputElement.prototype.clearable&&this.registerClearable()}registerClearable(){HTMLInputElement.prototype.clearable=function(e={}){if(this.dataset.clearable)return;if("object"!=typeof e)throw new Error("Passed options must be an object, "+typeof e+" given");const t=document.createElement("div");t.classList.add("form-control-clearable","form-control"),this.parentNode.insertBefore(t,this),t.appendChild(this);const n=Clearable.createCloseButton(),s=()=>{n.style.visibility=0===this.value.length?"hidden":"visible"};n.addEventListener("click",(t=>{t.preventDefault(),this.value="","function"==typeof e.onClear&&e.onClear(this),this.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0})),s()})),t.appendChild(n),this.addEventListener("focus",s),this.addEventListener("keyup",s),s(),this.dataset.clearable="true"}}}export default new Clearable; \ No newline at end of file +class Clearable{constructor(){"function"!=typeof HTMLInputElement.prototype.clearable&&this.registerClearable()}static createCloseButton(){const e=document.createElement("button");return e.type="button",e.tabIndex=-1,e.innerHTML='<span class="t3js-icon icon icon-size-small icon-state-default icon-actions-close" data-identifier="actions-close">\n <span class="icon-markup">\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">\n <path\n d="M11.9 5.5L9.4 8l2.5 2.5c.2.2.2.5 0\n .7l-.7.7c-.2.2-.5.2-.7 0L8 9.4l-2.5 2.5c-.2.2-.5.2-.7\n 0l-.7-.7c-.2-.2-.2-.5 0-.7L6.6 8 4.1 5.5c-.2-.2-.2-.5\n 0-.7l.7-.7c.2-.2.5-.2.7 0L8 6.6l2.5-2.5c.2-.2.5-.2.7\n 0l.7.7c.2.2.2.5 0 .7z"\n class="icon-color"/>\n </svg>\n </span>\n </span>',e.style.visibility="hidden",e.classList.add("close"),e}registerClearable(){HTMLInputElement.prototype.clearable=function(e={}){if(this.dataset.clearable)return;if("object"!=typeof e)throw new Error("Passed options must be an object, "+typeof e+" given");const t=document.createElement("div");t.classList.add("form-control-clearable","form-control"),this.parentNode.insertBefore(t,this),t.appendChild(this);const n=Clearable.createCloseButton(),s=()=>{n.style.visibility=0===this.value.length?"hidden":"visible"};n.addEventListener("click",(t=>{t.preventDefault(),this.value="","function"==typeof e.onClear&&e.onClear(this),this.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0})),s()})),t.appendChild(n),this.addEventListener("focus",s),this.addEventListener("keyup",s),s(),this.dataset.clearable="true"}}}export default new Clearable; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/renderable/flash-message.js b/typo3/sysext/install/Resources/Public/JavaScript/renderable/flash-message.js index f472d90a42dc..eeb3130da392 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/renderable/flash-message.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/renderable/flash-message.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import Severity from"@typo3/install/renderable/severity.js";class FlashMessage{constructor(){this.template=$('<div class="t3js-message typo3-message alert"><h4></h4><p class="messageText"></p></div>')}render(e,s,t){let a=this.template.clone();return a.addClass("alert-"+Severity.getCssClass(e)),s&&a.find("h4").text(s),t?a.find(".messageText").text(t):a.find(".messageText").remove(),a}}export default new FlashMessage; \ No newline at end of file +import $ from"jquery";import Severity from"@typo3/install/renderable/severity.js";class FlashMessage{constructor(){this.template=$('<div class="t3js-message typo3-message alert"><h4></h4><p class="messageText"></p></div>')}render(e,s,t){const a=this.template.clone();return a.addClass("alert-"+Severity.getCssClass(e)),s&&a.find("h4").text(s),t?a.find(".messageText").text(t):a.find(".messageText").remove(),a}}export default new FlashMessage; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/renderable/info-box.js b/typo3/sysext/install/Resources/Public/JavaScript/renderable/info-box.js index a70bfaefb796..05767066fe20 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/renderable/info-box.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/renderable/info-box.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import Severity from"@typo3/install/renderable/severity.js";class InfoBox{constructor(){this.template=$('<div class="t3js-infobox callout callout-sm"><h4 class="callout-title"></h4><div class="callout-body"></div></div>')}render(t,l,e){let o=this.template.clone();return o.addClass("callout-"+Severity.getCssClass(t)),l&&o.find("h4").text(l),e?o.find(".callout-body").text(e):o.find(".callout-body").remove(),o}}export default new InfoBox; \ No newline at end of file +import $ from"jquery";import Severity from"@typo3/install/renderable/severity.js";class InfoBox{constructor(){this.template=$('<div class="t3js-infobox callout callout-sm"><h4 class="callout-title"></h4><div class="callout-body"></div></div>')}render(t,o,l){const e=this.template.clone();return e.addClass("callout-"+Severity.getCssClass(t)),o&&e.find("h4").text(o),l?e.find(".callout-body").text(l):e.find(".callout-body").remove(),e}}export default new InfoBox; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/renderable/progress-bar.js b/typo3/sysext/install/Resources/Public/JavaScript/renderable/progress-bar.js index a6039c584309..6ccfd895fd0a 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/renderable/progress-bar.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/renderable/progress-bar.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import Severity from"@typo3/install/renderable/severity.js";class ProgressBar{constructor(){this.template=$('<div class="progress"><div class="t3js-progressbar progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"> <span></span></div></div>')}render(r,s,e){let a=this.template.clone();return a.addClass("progress-bar-"+Severity.getCssClass(r)),e&&(a.css("width",e+"%"),a.attr("aria-valuenow",e)),s&&a.find("span").text(s),a}}export default new ProgressBar; \ No newline at end of file +import $ from"jquery";import Severity from"@typo3/install/renderable/severity.js";class ProgressBar{constructor(){this.template=$('<div class="progress"><div class="t3js-progressbar progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"> <span></span></div></div>')}render(r,s,e){const a=this.template.clone();return a.addClass("progress-bar-"+Severity.getCssClass(r)),e&&(a.css("width",e+"%"),a.attr("aria-valuenow",e)),s&&a.find("span").text(s),a}}export default new ProgressBar; \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Public/JavaScript/router.js b/typo3/sysext/install/Resources/Public/JavaScript/router.js index 138bff870ef7..906e9c328bc2 100644 --- a/typo3/sysext/install/Resources/Public/JavaScript/router.js +++ b/typo3/sysext/install/Resources/Public/JavaScript/router.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import{html}from"lit";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{default as Modal}from"@typo3/backend/modal.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import{topLevelModuleImport}from"@typo3/backend/utility/top-level-module-import.js";import"@typo3/backend/element/spinner-element.js";class Router{constructor(){this.rootSelector=".t3js-body",this.contentSelector=".t3js-module-body",this.scaffoldSelector=".t3js-scaffold",this.scaffoldContentOverlaySelector=".t3js-scaffold-content-overlay",this.scaffoldMenuToggleSelector=".t3js-topbar-button-modulemenu"}setContent(e){this.rootContainer.querySelector(this.contentSelector).innerHTML=e}initialize(){this.rootContainer=document.querySelector(this.rootSelector),this.context=this.rootContainer.dataset.context??"",this.controller=this.rootContainer.dataset.controller??"",this.registerInstallToolRoutes(),$(document).on("click",".t3js-login-lockInstallTool",(e=>{e.preventDefault(),this.logout()})),$(document).on("click",".t3js-login-login",(e=>{e.preventDefault(),this.login()})),$(document).on("keydown","#t3-install-form-password",(e=>{"Enter"===e.key&&(e.preventDefault(),$(".t3js-login-login").trigger("click"))})),$(document).on("click",".card .btn",(e=>{e.preventDefault();const t=$(e.currentTarget),o=t.data("import"),n=t.data("inline");if(void 0!==n&&1===parseInt(n,10))import(o).then((({default:e})=>{e.initialize(t)}));else{const e=t.closest(".card").find(".card-title").html(),n=t.data("modalSize")||Modal.sizes.large;Modal.advanced({type:Modal.types.default,title:e,size:n,content:html`<div class="modal-loading"><typo3-backend-spinner size="default"></typo3-backend-spinner></div>`,additionalCssClasses:["install-tool-modal"],staticBackdrop:!0,callback:e=>{import(o).then((({default:t})=>{window.location!==window.parent.location?topLevelModuleImport("jquery").then((({default:o})=>{t.initialize(o(e))})):t.initialize($(e))}))}})}})),"backend"===this.context?this.executeSilentConfigurationUpdate():this.preAccessCheck()}registerInstallToolRoutes(){void 0===TYPO3.settings&&(TYPO3.settings={ajaxUrls:{icons:window.location.origin+window.location.pathname+"?install[controller]=icon&install[action]=getIcon",icons_cache:window.location.origin+window.location.pathname+"?install[controller]=icon&install[action]=getCacheIdentifier"}})}getUrl(e,t,o){let n=location.href;return n=n.replace(location.search,""),void 0===t&&(t=this.controller),n=n+"?install[controller]="+t,n=n+"&install[context]="+this.context,void 0!==e&&(n=n+"&install[action]="+e),void 0!==o&&(n=n+"&"+o),n}executeSilentConfigurationUpdate(){this.updateLoadingInfo("Checking session and executing silent configuration update"),new AjaxRequest(this.getUrl("executeSilentConfigurationUpdate","layout")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.executeSilentTemplateFileUpdate():this.executeSilentConfigurationUpdate()}),(e=>{this.handleAjaxError(e)}))}executeSilentTemplateFileUpdate(){this.updateLoadingInfo("Checking session and executing silent template file update"),new AjaxRequest(this.getUrl("executeSilentTemplateFileUpdate","layout")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.executeSilentExtensionConfigurationSynchronization():this.executeSilentTemplateFileUpdate()}),(e=>{this.handleAjaxError(e)}))}executeSilentExtensionConfigurationSynchronization(){this.updateLoadingInfo("Executing silent extension configuration synchronization"),new AjaxRequest(this.getUrl("executeSilentExtensionConfigurationSynchronization","layout")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.loadMainLayout():this.setContent(InfoBox.render(Severity.error,"Something went wrong","").html())}),(e=>{this.handleAjaxError(e)}))}loadMainLayout(){this.updateLoadingInfo("Loading main layout"),new AjaxRequest(this.getUrl("mainLayout","layout","install[module]="+this.controller)).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();!0===t.success&&"undefined"!==t.html&&t.html.length>0?(this.rootContainer.innerHTML=t.html,"backend"!==this.context&&(this.rootContainer.querySelector('[data-installroute-controller="'+this.controller+'"]').classList.add("modulemenu-action-active"),this.registerScaffoldEvents()),this.loadCards()):this.rootContainer.innerHTML=InfoBox.render(Severity.error,"Something went wrong","").html()}),(e=>{this.handleAjaxError(e)}))}async handleAjaxError(e,t){if(403===e.response.status)"backend"===this.context?this.rootContainer.innerHTML=InfoBox.render(Severity.error,"The install tool session expired. Please reload the backend and try again.").html():this.checkEnableInstallToolFile();else{const o='<div class="t3js-infobox callout callout-sm callout-danger"><h4 class="callout-title">Something went wrong</h4><div class="callout-body"><p>Please use <b><a href="'+this.getUrl(void 0,"upgrade")+'">Check for broken extensions</a></b> to see if a loaded extension breaks this part of the install tool and unload it.</p><p>The box below may additionally reveal further details on what went wrong depending on your debug settings. It may help to temporarily switch to debug mode using <b>Settings > Configuration Presets > Debug settings.</b></p><p>If this error happens at an early state and no full exception back trace is shown, it may also help to manually increase debugging output in <strong>%config-dir%/system/settings.php</strong>:<code>[\'BE\'][\'debug\'] => true</code>, <code>[\'SYS\'][\'devIPmask\'] => \'*\'</code>, <code>[\'SYS\'][\'displayErrors\'] => 1</code>,<code>[\'SYS\'][\'exceptionalErrors\'] => 12290</code></p></div></div><div class="panel-group" role="tablist" aria-multiselectable="true"><div class="panel panel-default searchhit"><div class="panel-heading" role="tab" id="heading-error"><h3 class="panel-title"><a role="button" data-bs-toggle="collapse" data-bs-parent="#accordion" href="#collapse-error" aria-expanded="true" aria-controls="collapse-error" class="collapsed"><span class="caret"></span><strong>Ajax error</strong></a></h3></div><div id="collapse-error" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading-error"><div class="panel-body">'+await e.response.text()+"</div></div></div></div>";void 0!==t?$(t).empty().html(o):this.rootContainer.innerHTML=o}}checkEnableInstallToolFile(){new AjaxRequest(this.getUrl("checkEnableInstallToolFile")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkLogin():this.showEnableInstallTool()}),(e=>{this.handleAjaxError(e)}))}showEnableInstallTool(){new AjaxRequest(this.getUrl("showEnableInstallToolFile")).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();!0===t.success&&(this.rootContainer.innerHTML=t.html)}),(e=>{this.handleAjaxError(e)}))}checkLogin(){new AjaxRequest(this.getUrl("checkLogin")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.loadMainLayout():this.showLogin()}),(e=>{this.handleAjaxError(e)}))}showLogin(){new AjaxRequest(this.getUrl("showLogin")).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();!0===t.success&&(this.rootContainer.innerHTML=t.html)}),(e=>{this.handleAjaxError(e)}))}login(){const e=$(".t3js-login-output"),t=ProgressBar.render(Severity.loading,"Loading...","");e.empty().html(t),new AjaxRequest(this.getUrl()).post({install:{action:"login",token:$("[data-login-token]").data("login-token"),password:$(".t3-install-form-input-text").val()}}).then((async t=>{const o=await t.resolve();!0===o.success?this.executeSilentConfigurationUpdate():o.status.forEach((t=>{const o=InfoBox.render(t.severity,t.title,t.message);e.empty().html(o)}))}),(e=>{this.handleAjaxError(e)}))}logout(){new AjaxRequest(this.getUrl("logout")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success&&this.showEnableInstallTool()}),(e=>{this.handleAjaxError(e)}))}loadCards(){new AjaxRequest(this.getUrl("cards")).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();!0===t.success&&"undefined"!==t.html&&t.html.length>0?this.setContent(t.html):this.setContent(InfoBox.render(Severity.error,"Something went wrong","").html())}),(e=>{this.handleAjaxError(e)}))}registerScaffoldEvents(){localStorage.getItem("typo3-install-modulesCollapsed")||localStorage.setItem("typo3-install-modulesCollapsed","false"),this.toggleMenu("true"===localStorage.getItem("typo3-install-modulesCollapsed")),document.querySelector(this.scaffoldMenuToggleSelector).addEventListener("click",(e=>{e.preventDefault(),this.toggleMenu()})),document.querySelector(this.scaffoldContentOverlaySelector).addEventListener("click",(e=>{e.preventDefault(),this.toggleMenu(!0)})),document.querySelectorAll("[data-installroute-controller]").forEach((e=>{e.addEventListener("click",(e=>{window.innerWidth<768&&localStorage.setItem("typo3-install-modulesCollapsed","true")}))}))}toggleMenu(e){const t=document.querySelector(this.scaffoldSelector),o="scaffold-modulemenu-expanded";void 0===e&&(e=t.classList.contains(o)),t.classList.toggle(o,!e),localStorage.setItem("typo3-install-modulesCollapsed",e?"true":"false")}updateLoadingInfo(e){const t=this.rootContainer.querySelector("#t3js-ui-block-detail");void 0!==t&&t instanceof HTMLElement&&(t.innerText=e)}preAccessCheck(){this.updateLoadingInfo("Execute pre access check"),new AjaxRequest(this.getUrl("preAccessCheck","layout")).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();t.installToolLocked?this.checkEnableInstallToolFile():t.isAuthorized?this.executeSilentConfigurationUpdate():this.showLogin()}),(e=>{this.handleAjaxError(e)}))}}export default new Router; \ No newline at end of file +import $ from"jquery";import{html}from"lit";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import{default as Modal}from"@typo3/backend/modal.js";import InfoBox from"@typo3/install/renderable/info-box.js";import ProgressBar from"@typo3/install/renderable/progress-bar.js";import Severity from"@typo3/install/renderable/severity.js";import{topLevelModuleImport}from"@typo3/backend/utility/top-level-module-import.js";import"@typo3/backend/element/spinner-element.js";class Router{constructor(){this.rootSelector=".t3js-body",this.contentSelector=".t3js-module-body",this.scaffoldSelector=".t3js-scaffold",this.scaffoldContentOverlaySelector=".t3js-scaffold-content-overlay",this.scaffoldMenuToggleSelector=".t3js-topbar-button-modulemenu"}setContent(e){this.rootContainer.querySelector(this.contentSelector).innerHTML=e}initialize(){this.rootContainer=document.querySelector(this.rootSelector),this.context=this.rootContainer.dataset.context??"",this.controller=this.rootContainer.dataset.controller??"",this.registerInstallToolRoutes(),$(document).on("click",".t3js-login-lockInstallTool",(e=>{e.preventDefault(),this.logout()})),$(document).on("click",".t3js-login-login",(e=>{e.preventDefault(),this.login()})),$(document).on("keydown","#t3-install-form-password",(e=>{"Enter"===e.key&&(e.preventDefault(),$(".t3js-login-login").trigger("click"))})),$(document).on("click",".card .btn",(e=>{e.preventDefault();const t=$(e.currentTarget),o=t.data("import"),n=t.data("inline");if(void 0!==n&&1===parseInt(n,10))import(o).then((({default:e})=>{e.initialize(t)}));else{const e=t.closest(".card").find(".card-title").html(),n=t.data("modalSize")||Modal.sizes.large;Modal.advanced({type:Modal.types.default,title:e,size:n,content:html`<div class="modal-loading"><typo3-backend-spinner size="default"></typo3-backend-spinner></div>`,additionalCssClasses:["install-tool-modal"],staticBackdrop:!0,callback:e=>{import(o).then((({default:t})=>{window.location!==window.parent.location?topLevelModuleImport("jquery").then((({default:o})=>{t.initialize(o(e))})):t.initialize($(e))}))}})}})),"backend"===this.context?this.executeSilentConfigurationUpdate():this.preAccessCheck()}registerInstallToolRoutes(){void 0===TYPO3.settings&&(TYPO3.settings={ajaxUrls:{icons:window.location.origin+window.location.pathname+"?install[controller]=icon&install[action]=getIcon",icons_cache:window.location.origin+window.location.pathname+"?install[controller]=icon&install[action]=getCacheIdentifier"}})}getUrl(e,t,o){let n=location.href;return n=n.replace(location.search,""),void 0===t&&(t=this.controller),n=n+"?install[controller]="+t,n=n+"&install[context]="+this.context,void 0!==e&&(n=n+"&install[action]="+e),void 0!==o&&(n=n+"&"+o),n}executeSilentConfigurationUpdate(){this.updateLoadingInfo("Checking session and executing silent configuration update"),new AjaxRequest(this.getUrl("executeSilentConfigurationUpdate","layout")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.executeSilentTemplateFileUpdate():this.executeSilentConfigurationUpdate()}),(e=>{this.handleAjaxError(e)}))}executeSilentTemplateFileUpdate(){this.updateLoadingInfo("Checking session and executing silent template file update"),new AjaxRequest(this.getUrl("executeSilentTemplateFileUpdate","layout")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.executeSilentExtensionConfigurationSynchronization():this.executeSilentTemplateFileUpdate()}),(e=>{this.handleAjaxError(e)}))}executeSilentExtensionConfigurationSynchronization(){this.updateLoadingInfo("Executing silent extension configuration synchronization"),new AjaxRequest(this.getUrl("executeSilentExtensionConfigurationSynchronization","layout")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.loadMainLayout():this.setContent(InfoBox.render(Severity.error,"Something went wrong","").html())}),(e=>{this.handleAjaxError(e)}))}loadMainLayout(){this.updateLoadingInfo("Loading main layout"),new AjaxRequest(this.getUrl("mainLayout","layout","install[module]="+this.controller)).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();!0===t.success&&"undefined"!==t.html&&t.html.length>0?(this.rootContainer.innerHTML=t.html,"backend"!==this.context&&(this.rootContainer.querySelector('[data-installroute-controller="'+this.controller+'"]').classList.add("modulemenu-action-active"),this.registerScaffoldEvents()),this.loadCards()):this.rootContainer.innerHTML=InfoBox.render(Severity.error,"Something went wrong","").html()}),(e=>{this.handleAjaxError(e)}))}async handleAjaxError(e,t){if(403===e.response.status)"backend"===this.context?this.rootContainer.innerHTML=InfoBox.render(Severity.error,"The install tool session expired. Please reload the backend and try again.").html():this.checkEnableInstallToolFile();else{const o='<div class="t3js-infobox callout callout-sm callout-danger"><h4 class="callout-title">Something went wrong</h4><div class="callout-body"><p>Please use <b><a href="'+this.getUrl(void 0,"upgrade")+'">Check for broken extensions</a></b> to see if a loaded extension breaks this part of the install tool and unload it.</p><p>The box below may additionally reveal further details on what went wrong depending on your debug settings. It may help to temporarily switch to debug mode using <b>Settings > Configuration Presets > Debug settings.</b></p><p>If this error happens at an early state and no full exception back trace is shown, it may also help to manually increase debugging output in <strong>%config-dir%/system/settings.php</strong>:<code>[\'BE\'][\'debug\'] => true</code>, <code>[\'SYS\'][\'devIPmask\'] => \'*\'</code>, <code>[\'SYS\'][\'displayErrors\'] => 1</code>,<code>[\'SYS\'][\'exceptionalErrors\'] => 12290</code></p></div></div><div class="panel-group" role="tablist" aria-multiselectable="true"><div class="panel panel-default searchhit"><div class="panel-heading" role="tab" id="heading-error"><h3 class="panel-title"><a role="button" data-bs-toggle="collapse" data-bs-parent="#accordion" href="#collapse-error" aria-expanded="true" aria-controls="collapse-error" class="collapsed"><span class="caret"></span><strong>Ajax error</strong></a></h3></div><div id="collapse-error" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading-error"><div class="panel-body">'+await e.response.text()+"</div></div></div></div>";void 0!==t?$(t).empty().html(o):this.rootContainer.innerHTML=o}}checkEnableInstallToolFile(){new AjaxRequest(this.getUrl("checkEnableInstallToolFile")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.checkLogin():this.showEnableInstallTool()}),(e=>{this.handleAjaxError(e)}))}showEnableInstallTool(){new AjaxRequest(this.getUrl("showEnableInstallToolFile")).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();!0===t.success&&(this.rootContainer.innerHTML=t.html)}),(e=>{this.handleAjaxError(e)}))}checkLogin(){new AjaxRequest(this.getUrl("checkLogin")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success?this.loadMainLayout():this.showLogin()}),(e=>{this.handleAjaxError(e)}))}showLogin(){new AjaxRequest(this.getUrl("showLogin")).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();!0===t.success&&(this.rootContainer.innerHTML=t.html)}),(e=>{this.handleAjaxError(e)}))}login(){const e=$(".t3js-login-output"),t=ProgressBar.render(Severity.loading,"Loading...","");e.empty().append(t),new AjaxRequest(this.getUrl()).post({install:{action:"login",token:$("[data-login-token]").data("login-token"),password:$(".t3-install-form-input-text").val()}}).then((async t=>{const o=await t.resolve();!0===o.success?this.executeSilentConfigurationUpdate():o.status.forEach((t=>{const o=InfoBox.render(t.severity,t.title,t.message);e.empty().append(o)}))}),(e=>{this.handleAjaxError(e)}))}logout(){new AjaxRequest(this.getUrl("logout")).get({cache:"no-cache"}).then((async e=>{!0===(await e.resolve()).success&&this.showEnableInstallTool()}),(e=>{this.handleAjaxError(e)}))}loadCards(){new AjaxRequest(this.getUrl("cards")).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();!0===t.success&&"undefined"!==t.html&&t.html.length>0?this.setContent(t.html):this.setContent(InfoBox.render(Severity.error,"Something went wrong","").html())}),(e=>{this.handleAjaxError(e)}))}registerScaffoldEvents(){localStorage.getItem("typo3-install-modulesCollapsed")||localStorage.setItem("typo3-install-modulesCollapsed","false"),this.toggleMenu("true"===localStorage.getItem("typo3-install-modulesCollapsed")),document.querySelector(this.scaffoldMenuToggleSelector).addEventListener("click",(e=>{e.preventDefault(),this.toggleMenu()})),document.querySelector(this.scaffoldContentOverlaySelector).addEventListener("click",(e=>{e.preventDefault(),this.toggleMenu(!0)})),document.querySelectorAll("[data-installroute-controller]").forEach((e=>{e.addEventListener("click",(()=>{window.innerWidth<768&&localStorage.setItem("typo3-install-modulesCollapsed","true")}))}))}toggleMenu(e){const t=document.querySelector(this.scaffoldSelector),o="scaffold-modulemenu-expanded";void 0===e&&(e=t.classList.contains(o)),t.classList.toggle(o,!e),localStorage.setItem("typo3-install-modulesCollapsed",e?"true":"false")}updateLoadingInfo(e){const t=this.rootContainer.querySelector("#t3js-ui-block-detail");void 0!==t&&t instanceof HTMLElement&&(t.innerText=e)}preAccessCheck(){this.updateLoadingInfo("Execute pre access check"),new AjaxRequest(this.getUrl("preAccessCheck","layout")).get({cache:"no-cache"}).then((async e=>{const t=await e.resolve();t.installToolLocked?this.checkEnableInstallToolFile():t.isAuthorized?this.executeSilentConfigurationUpdate():this.showLogin()}),(e=>{this.handleAjaxError(e)}))}}export default new Router; \ No newline at end of file diff --git a/typo3/sysext/linkvalidator/Resources/Public/JavaScript/linkvalidator.js b/typo3/sysext/linkvalidator/Resources/Public/JavaScript/linkvalidator.js index 9700a880e686..9c666be2e900 100644 --- a/typo3/sysext/linkvalidator/Resources/Public/JavaScript/linkvalidator.js +++ b/typo3/sysext/linkvalidator/Resources/Public/JavaScript/linkvalidator.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import Notification from"@typo3/backend/notification.js";import RegularEvent from"@typo3/core/event/regular-event.js";var Selectors;!function(t){t.settingsContainerSelector=".t3js-linkvalidator-settings",t.actionButtonSelector=".t3js-linkvalidator-action-button"}(Selectors||(Selectors={}));class Linkvalidator{static toggleActionButtons(t){t.querySelector(Selectors.actionButtonSelector)?.toggleAttribute("disabled",!t.querySelectorAll('input[type="checkbox"]:checked').length)}constructor(){this.initializeEvents(),document.querySelectorAll(Selectors.settingsContainerSelector).forEach((t=>{Linkvalidator.toggleActionButtons(t)}))}initializeEvents(){new RegularEvent("change",((t,e)=>{Linkvalidator.toggleActionButtons(e.closest(Selectors.settingsContainerSelector))})).delegateTo(document,[Selectors.settingsContainerSelector,'input[type="checkbox"]'].join(" ")),new RegularEvent("click",((t,e)=>{Notification.success(e.dataset.notificationMessage||"Event triggered","",2)})).delegateTo(document,Selectors.actionButtonSelector)}}export default new Linkvalidator; \ No newline at end of file +import Notification from"@typo3/backend/notification.js";import RegularEvent from"@typo3/core/event/regular-event.js";var Selectors;!function(t){t.settingsContainerSelector=".t3js-linkvalidator-settings",t.actionButtonSelector=".t3js-linkvalidator-action-button"}(Selectors||(Selectors={}));class Linkvalidator{constructor(){this.initializeEvents(),document.querySelectorAll(Selectors.settingsContainerSelector).forEach((t=>{Linkvalidator.toggleActionButtons(t)}))}static toggleActionButtons(t){t.querySelector(Selectors.actionButtonSelector)?.toggleAttribute("disabled",!t.querySelectorAll('input[type="checkbox"]:checked').length)}initializeEvents(){new RegularEvent("change",((t,e)=>{Linkvalidator.toggleActionButtons(e.closest(Selectors.settingsContainerSelector))})).delegateTo(document,[Selectors.settingsContainerSelector,'input[type="checkbox"]'].join(" ")),new RegularEvent("click",((t,e)=>{Notification.success(e.dataset.notificationMessage||"Event triggered","",2)})).delegateTo(document,Selectors.actionButtonSelector)}}export default new Linkvalidator; \ No newline at end of file diff --git a/typo3/sysext/opendocs/Resources/Public/JavaScript/toolbar/opendocs-menu.js b/typo3/sysext/opendocs/Resources/Public/JavaScript/toolbar/opendocs-menu.js index 06d112c23588..f7a1a17fbaf1 100644 --- a/typo3/sysext/opendocs/Resources/Public/JavaScript/toolbar/opendocs-menu.js +++ b/typo3/sysext/opendocs/Resources/Public/JavaScript/toolbar/opendocs-menu.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Icons from"@typo3/backend/icons.js";import Viewport from"@typo3/backend/viewport.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";var Selectors;!function(e){e.containerSelector="#typo3-cms-opendocs-backend-toolbaritems-opendocstoolbaritem",e.closeSelector=".t3js-topbar-opendocs-close",e.menuContainerSelector=".dropdown-menu",e.toolbarIconSelector=".toolbar-item-icon .t3js-icon",e.openDocumentsItemsSelector=".t3js-topbar-opendocs-item",e.counterSelector="#tx-opendocs-counter",e.entrySelector=".t3js-open-doc"}(Selectors||(Selectors={}));class OpendocsMenu{constructor(){this.hashDataAttributeName="opendocsidentifier",this.toggleMenu=()=>{$(".scaffold").removeClass("scaffold-toolbar-expanded"),$(Selectors.containerSelector).toggleClass("open")},document.addEventListener("typo3:opendocs:updateRequested",(e=>this.updateMenu())),Viewport.Topbar.Toolbar.registerEvent((()=>{this.initializeEvents(),this.updateMenu()}))}static updateNumberOfDocs(){const e=$(Selectors.containerSelector).find(Selectors.openDocumentsItemsSelector).length;$(Selectors.counterSelector).text(e).toggle(e>0)}updateMenu(){let e=$(Selectors.toolbarIconSelector,Selectors.containerSelector),t=e.clone();Icons.getIcon("spinner-circle-light",Icons.sizes.small).then((t=>{e.replaceWith(t)})),new AjaxRequest(TYPO3.settings.ajaxUrls.opendocs_menu).get().then((async e=>{$(Selectors.containerSelector).find(Selectors.menuContainerSelector).html(await e.resolve()),OpendocsMenu.updateNumberOfDocs()})).finally((()=>{$(Selectors.toolbarIconSelector,Selectors.containerSelector).replaceWith(t)}))}initializeEvents(){$(Selectors.containerSelector).on("click",Selectors.closeSelector,(e=>{e.preventDefault();const t=$(e.currentTarget).data(this.hashDataAttributeName);this.closeDocument(t)})).on("click",Selectors.entrySelector,(e=>{e.preventDefault();const t=$(e.currentTarget);this.toggleMenu(),ModuleStateStorage.updateWithCurrentMount("web",t.data("pid"),!0);document.querySelector("typo3-backend-module-router").setAttribute("endpoint",t.attr("href"))}))}closeDocument(e){const t={};e&&(t.md5sum=e),new AjaxRequest(TYPO3.settings.ajaxUrls.opendocs_closedoc).post(t).then((async e=>{$(Selectors.menuContainerSelector,Selectors.containerSelector).html(await e.resolve()),OpendocsMenu.updateNumberOfDocs(),$(Selectors.containerSelector).toggleClass("open")}))}}let opendocsMenuObject;opendocsMenuObject=new OpendocsMenu,"undefined"!=typeof TYPO3&&(TYPO3.OpendocsMenu=opendocsMenuObject);export default opendocsMenuObject; \ No newline at end of file +import $ from"jquery";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Icons from"@typo3/backend/icons.js";import Viewport from"@typo3/backend/viewport.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";var Selectors;!function(e){e.containerSelector="#typo3-cms-opendocs-backend-toolbaritems-opendocstoolbaritem",e.closeSelector=".t3js-topbar-opendocs-close",e.menuContainerSelector=".dropdown-menu",e.toolbarIconSelector=".toolbar-item-icon .t3js-icon",e.openDocumentsItemsSelector=".t3js-topbar-opendocs-item",e.counterSelector="#tx-opendocs-counter",e.entrySelector=".t3js-open-doc"}(Selectors||(Selectors={}));class OpendocsMenu{constructor(){this.hashDataAttributeName="opendocsidentifier",this.toggleMenu=()=>{$(".scaffold").removeClass("scaffold-toolbar-expanded"),$(Selectors.containerSelector).toggleClass("open")},document.addEventListener("typo3:opendocs:updateRequested",(()=>this.updateMenu())),Viewport.Topbar.Toolbar.registerEvent((()=>{this.initializeEvents(),this.updateMenu()}))}static updateNumberOfDocs(){const e=$(Selectors.containerSelector).find(Selectors.openDocumentsItemsSelector).length;$(Selectors.counterSelector).text(e).toggle(e>0)}updateMenu(){const e=$(Selectors.toolbarIconSelector,Selectors.containerSelector),t=e.clone();Icons.getIcon("spinner-circle-light",Icons.sizes.small).then((t=>{e.replaceWith(t)})),new AjaxRequest(TYPO3.settings.ajaxUrls.opendocs_menu).get().then((async e=>{$(Selectors.containerSelector).find(Selectors.menuContainerSelector).html(await e.resolve()),OpendocsMenu.updateNumberOfDocs()})).finally((()=>{$(Selectors.toolbarIconSelector,Selectors.containerSelector).replaceWith(t)}))}initializeEvents(){$(Selectors.containerSelector).on("click",Selectors.closeSelector,(e=>{e.preventDefault();const t=$(e.currentTarget).data(this.hashDataAttributeName);this.closeDocument(t)})).on("click",Selectors.entrySelector,(e=>{e.preventDefault();const t=$(e.currentTarget);this.toggleMenu(),ModuleStateStorage.updateWithCurrentMount("web",t.data("pid"),!0);document.querySelector("typo3-backend-module-router").setAttribute("endpoint",t.attr("href"))}))}closeDocument(e){const t={};e&&(t.md5sum=e),new AjaxRequest(TYPO3.settings.ajaxUrls.opendocs_closedoc).post(t).then((async e=>{$(Selectors.menuContainerSelector,Selectors.containerSelector).html(await e.resolve()),OpendocsMenu.updateNumberOfDocs(),$(Selectors.containerSelector).toggleClass("open")}))}}const opendocsMenuObject=new OpendocsMenu;"undefined"!=typeof TYPO3&&(TYPO3.OpendocsMenu=opendocsMenuObject);export default opendocsMenuObject; \ No newline at end of file diff --git a/typo3/sysext/recycler/Resources/Public/JavaScript/recycler.js b/typo3/sysext/recycler/Resources/Public/JavaScript/recycler.js index def3e9a576a8..b9fb36dd225e 100644 --- a/typo3/sysext/recycler/Resources/Public/JavaScript/recycler.js +++ b/typo3/sysext/recycler/Resources/Public/JavaScript/recycler.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import NProgress from"nprogress";import"@typo3/backend/input/clearable.js";import"@typo3/backend/element/icon-element.js";import DeferredAction from"@typo3/backend/action-button/deferred-action.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import RegularEvent from"@typo3/core/event/regular-event.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";var RecyclerIdentifiers;!function(e){e.searchForm="#recycler-form",e.searchText="#recycler-form [name=search-text]",e.searchSubmitBtn="#recycler-form button[type=submit]",e.depthSelector="#recycler-form [name=depth]",e.tableSelector="#recycler-form [name=pages]",e.recyclerTable="#itemsInRecycler",e.paginator="#recycler-index nav",e.reloadAction="a[data-action=reload]",e.undo="a[data-action=undo]",e.delete="a[data-action=delete]",e.massUndo="button[data-multi-record-selection-action=massundo]",e.massDelete="button[data-multi-record-selection-action=massdelete]"}(RecyclerIdentifiers||(RecyclerIdentifiers={}));class Recycler{constructor(){this.elements={},this.paging={currentPage:1,totalPages:1,totalItems:0,itemsPerPage:TYPO3.settings.Recycler.pagingSize},this.markedRecordsForMassAction=[],this.handleCheckboxStateChanged=e=>{const t=$(e.target),a=t.parents("tr"),s=a.data("table")+":"+a.data("uid");if(t.prop("checked"))this.markedRecordsForMassAction.push(s);else{const e=this.markedRecordsForMassAction.indexOf(s);e>-1&&this.markedRecordsForMassAction.splice(e,1)}this.markedRecordsForMassAction.length>0?(this.elements.$massUndo.find("span.text").text(this.createMessage(TYPO3.lang["button.undoselected"],[this.markedRecordsForMassAction.length])),this.elements.$massDelete.find("span.text").text(this.createMessage(TYPO3.lang["button.deleteselected"],[this.markedRecordsForMassAction.length]))):this.resetMassActionButtons()},this.deleteRecord=e=>{if(TYPO3.settings.Recycler.deleteDisable)return;const t=$(e.target).parents("tr"),a="TBODY"!==t.parent().prop("tagName");let s,n;if(a)s=this.markedRecordsForMassAction,n=TYPO3.lang["modal.massdelete.text"];else{const e=t.data("uid"),a=t.data("table"),i=t.data("recordtitle");s=[a+":"+e],n="pages"===a?TYPO3.lang["modal.deletepage.text"]:TYPO3.lang["modal.deletecontent.text"],n=this.createMessage(n,[i,"["+s[0]+"]"])}Modal.advanced({title:TYPO3.lang["modal.delete.header"],content:n,severity:SeverityEnum.error,staticBackdrop:!0,buttons:[{text:TYPO3.lang["button.cancel"],btnClass:"btn-default",trigger:function(){Modal.dismiss()}},{text:TYPO3.lang["button.delete"],btnClass:"btn-danger",action:new DeferredAction((()=>{this.callAjaxAction("delete",s,a)}))}]})},this.undoRecord=e=>{const t=$(e.target).parents("tr"),a="TBODY"!==t.parent().prop("tagName");let s,n,i;if(a)s=this.markedRecordsForMassAction,n=TYPO3.lang["modal.massundo.text"],i=!0;else{const e=t.data("uid"),a=t.data("table"),r=t.data("recordtitle");s=[a+":"+e],i="pages"===a,n=i?TYPO3.lang["modal.undopage.text"]:TYPO3.lang["modal.undocontent.text"],n=this.createMessage(n,[r,"["+s[0]+"]"]),i&&t.data("parentDeleted")&&(n+=TYPO3.lang["modal.undo.parentpages"])}let r=null;r=i?$("<div />").append($("<p />").text(n),$("<div />",{class:"form-check"}).append($("<input />",{type:"checkbox",id:"undo-recursive",class:"form-check-input"}),$("<label />",{class:"form-check-label",for:"undo-recursive"}).text(TYPO3.lang["modal.undo.recursive"]))):$("<p />").text(n),Modal.advanced({title:TYPO3.lang["modal.undo.header"],content:r,severity:SeverityEnum.ok,staticBackdrop:!0,buttons:[{text:TYPO3.lang["button.cancel"],btnClass:"btn-default",trigger:function(){Modal.dismiss()}},{text:TYPO3.lang["button.undo"],btnClass:"btn-success",action:new DeferredAction((()=>{this.callAjaxAction("undo","object"==typeof s?s:[s],a,r.find("#undo-recursive").prop("checked"))}))}]})},DocumentService.ready().then((()=>{this.initialize()}))}static refreshPageTree(){top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))}getElements(){this.elements={$searchForm:$(RecyclerIdentifiers.searchForm),$searchTextField:$(RecyclerIdentifiers.searchText),$searchSubmitBtn:$(RecyclerIdentifiers.searchSubmitBtn),$depthSelector:$(RecyclerIdentifiers.depthSelector),$tableSelector:$(RecyclerIdentifiers.tableSelector),$recyclerTable:$(RecyclerIdentifiers.recyclerTable),$tableBody:$(RecyclerIdentifiers.recyclerTable).find("tbody"),$paginator:$(RecyclerIdentifiers.paginator),$reloadAction:$(RecyclerIdentifiers.reloadAction),$massUndo:$(RecyclerIdentifiers.massUndo),$massDelete:$(RecyclerIdentifiers.massDelete)}}registerEvents(){this.elements.$searchForm.on("submit",(e=>{e.preventDefault(),""!==this.elements.$searchTextField.val()&&this.loadDeletedElements()})),this.elements.$searchTextField.on("keyup",(e=>{""!==$(e.currentTarget).val()?this.elements.$searchSubmitBtn.removeClass("disabled"):(this.elements.$searchSubmitBtn.addClass("disabled"),this.loadDeletedElements())})),this.elements.$searchTextField.get(0).clearable({onClear:()=>{this.elements.$searchSubmitBtn.addClass("disabled"),this.loadDeletedElements()}}),this.elements.$depthSelector.on("change",(()=>{this.loadAvailableTables().then((()=>{this.loadDeletedElements()}))})),this.elements.$tableSelector.on("change",(()=>{this.paging.currentPage=1,this.loadDeletedElements()})),new RegularEvent("click",this.undoRecord).delegateTo(document,RecyclerIdentifiers.undo),new RegularEvent("click",this.deleteRecord).delegateTo(document,RecyclerIdentifiers.delete),this.elements.$reloadAction.on("click",(e=>{e.preventDefault(),this.loadAvailableTables().then((()=>{this.loadDeletedElements()}))})),this.elements.$paginator.on("click","[data-action]",(e=>{e.preventDefault();const t=$(e.currentTarget);let a=!1;switch(t.data("action")){case"previous":this.paging.currentPage>1&&(this.paging.currentPage--,a=!0);break;case"next":this.paging.currentPage<this.paging.totalPages&&(this.paging.currentPage++,a=!0);break;case"page":this.paging.currentPage=parseInt(t.find("span").text(),10),a=!0}a&&this.loadDeletedElements()})),TYPO3.settings.Recycler.deleteDisable?this.elements.$massDelete.remove():this.elements.$massDelete.show(),new RegularEvent("multiRecordSelection:checkbox:state:changed",this.handleCheckboxStateChanged).bindTo(document),new RegularEvent("multiRecordSelection:action:massundo",this.undoRecord).bindTo(document),new RegularEvent("multiRecordSelection:action:massdelete",this.deleteRecord).bindTo(document)}initialize(){NProgress.configure({parent:".module-loading-indicator",showSpinner:!1}),this.getElements(),this.registerEvents(),TYPO3.settings.Recycler.depthSelection>0?this.elements.$depthSelector.val(TYPO3.settings.Recycler.depthSelection).trigger("change"):this.loadAvailableTables().then((()=>{this.loadDeletedElements()}))}resetMassActionButtons(){this.markedRecordsForMassAction=[],this.elements.$massUndo.find("span.text").text(TYPO3.lang["button.undo"]),this.elements.$massDelete.find("span.text").text(TYPO3.lang["button.delete"]),document.dispatchEvent(new CustomEvent("multiRecordSelection:actions:hide"))}loadAvailableTables(){return NProgress.start(),this.elements.$tableSelector.val(""),this.paging.currentPage=1,new AjaxRequest(TYPO3.settings.ajaxUrls.recycler).withQueryArguments({action:"getTables",startUid:TYPO3.settings.Recycler.startUid,depth:this.elements.$depthSelector.find("option:selected").val()}).get().then((async e=>{const t=await e.resolve(),a=[];this.elements.$tableSelector.children().remove();for(let e of t){const t=e[0],s=e[1],n=(e[2]?e[2]:TYPO3.lang.label_allrecordtypes)+" ("+s+")";a.push($("<option />").val(t).text(n))}return a.length>0&&(this.elements.$tableSelector.append(a),""!==TYPO3.settings.Recycler.tableSelection&&this.elements.$tableSelector.val(TYPO3.settings.Recycler.tableSelection)),e})).finally((()=>NProgress.done()))}loadDeletedElements(){return NProgress.start(),this.resetMassActionButtons(),new AjaxRequest(TYPO3.settings.ajaxUrls.recycler).withQueryArguments({action:"getDeletedRecords",depth:this.elements.$depthSelector.find("option:selected").val(),startUid:TYPO3.settings.Recycler.startUid,table:this.elements.$tableSelector.find("option:selected").val(),filterTxt:this.elements.$searchTextField.val(),start:(this.paging.currentPage-1)*this.paging.itemsPerPage,limit:this.paging.itemsPerPage}).get().then((async e=>{const t=await e.resolve();return this.elements.$tableBody.html(t.rows),this.buildPaginator(t.totalItems),e})).finally((()=>NProgress.done()))}callAjaxAction(e,t,a,s=!1){let n={records:t,action:""},i=!1;if("undo"===e)n.action="undoRecords",n.recursive=s?1:0,i=!0;else{if("delete"!==e)return null;n.action="deleteRecords"}return NProgress.start(),new AjaxRequest(TYPO3.settings.ajaxUrls.recycler).post(n).then((async e=>{const t=await e.resolve();return t.success?Notification.success("",t.message):Notification.error("",t.message),this.paging.currentPage=1,this.loadAvailableTables().then((()=>{this.loadDeletedElements(),a&&this.resetMassActionButtons(),i&&Recycler.refreshPageTree()})),e}))}createMessage(e,t){return void 0===e?"":e.replace(/\{([0-9]+)\}/g,(function(e,a){return t[a]}))}buildPaginator(e){if(0===e)return void this.elements.$paginator.contents().remove();if(this.paging.totalItems=e,this.paging.totalPages=Math.ceil(e/this.paging.itemsPerPage),1===this.paging.totalPages)return void this.elements.$paginator.contents().remove();const t=$("<ul />",{class:"pagination"}),a=[],s=$("<li />",{class:"page-item"}).append($("<button />",{class:"page-link",type:"button","data-action":"previous"}).append($("<typo3-backend-icon />",{identifier:"actions-arrow-left-alt",size:"small"}))),n=$("<li />",{class:"page-item"}).append($("<button />",{class:"page-link",type:"button","data-action":"next"}).append($("<typo3-backend-icon />",{identifier:"actions-arrow-right-alt",size:"small"})));1===this.paging.currentPage&&s.disablePagingAction(),this.paging.currentPage===this.paging.totalPages&&n.disablePagingAction();for(let e=1;e<=this.paging.totalPages;e++){const t=$("<li />",{class:"page-item"+(this.paging.currentPage===e?" active":"")});t.append($("<button />",{class:"page-link",type:"button","data-action":"page"}).append($("<span />").text(e))),a.push(t)}t.append(s,a,n),this.elements.$paginator.html(t)}}$.fn.disablePagingAction=function(){$(this).addClass("disabled").find("button").prop("disabled",!0)};export default new Recycler; \ No newline at end of file +import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import NProgress from"nprogress";import"@typo3/backend/input/clearable.js";import"@typo3/backend/element/icon-element.js";import DeferredAction from"@typo3/backend/action-button/deferred-action.js";import Modal from"@typo3/backend/modal.js";import Notification from"@typo3/backend/notification.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import RegularEvent from"@typo3/core/event/regular-event.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";var RecyclerIdentifiers;!function(e){e.searchForm="#recycler-form",e.searchText="#recycler-form [name=search-text]",e.searchSubmitBtn="#recycler-form button[type=submit]",e.depthSelector="#recycler-form [name=depth]",e.tableSelector="#recycler-form [name=pages]",e.recyclerTable="#itemsInRecycler",e.paginator="#recycler-index nav",e.reloadAction="a[data-action=reload]",e.undo="a[data-action=undo]",e.delete="a[data-action=delete]",e.massUndo="button[data-multi-record-selection-action=massundo]",e.massDelete="button[data-multi-record-selection-action=massdelete]"}(RecyclerIdentifiers||(RecyclerIdentifiers={}));class Recycler{constructor(){this.elements={},this.paging={currentPage:1,totalPages:1,totalItems:0,itemsPerPage:parseInt(TYPO3.settings.Recycler.pagingSize,10)},this.markedRecordsForMassAction=[],this.handleCheckboxStateChanged=e=>{const t=$(e.target),a=t.parents("tr"),s=a.data("table")+":"+a.data("uid");if(t.prop("checked"))this.markedRecordsForMassAction.push(s);else{const e=this.markedRecordsForMassAction.indexOf(s);e>-1&&this.markedRecordsForMassAction.splice(e,1)}this.markedRecordsForMassAction.length>0?(this.elements.$massUndo.find("span.text").text(this.createMessage(TYPO3.lang["button.undoselected"],[this.markedRecordsForMassAction.length.toString(10)])),this.elements.$massDelete.find("span.text").text(this.createMessage(TYPO3.lang["button.deleteselected"],[this.markedRecordsForMassAction.length.toString(10)]))):this.resetMassActionButtons()},this.deleteRecord=e=>{if(TYPO3.settings.Recycler.deleteDisable)return;const t=$(e.target).parents("tr"),a="TBODY"!==t.parent().prop("tagName");let s,n;if(a)s=this.markedRecordsForMassAction,n=TYPO3.lang["modal.massdelete.text"];else{const e=t.data("uid"),a=t.data("table"),i=t.data("recordtitle");s=[a+":"+e],n="pages"===a?TYPO3.lang["modal.deletepage.text"]:TYPO3.lang["modal.deletecontent.text"],n=this.createMessage(n,[i,"["+s[0]+"]"])}Modal.advanced({title:TYPO3.lang["modal.delete.header"],content:n,severity:SeverityEnum.error,staticBackdrop:!0,buttons:[{text:TYPO3.lang["button.cancel"],btnClass:"btn-default",trigger:function(){Modal.dismiss()}},{text:TYPO3.lang["button.delete"],btnClass:"btn-danger",action:new DeferredAction((()=>{this.callAjaxAction("delete",s,a)}))}]})},this.undoRecord=e=>{const t=$(e.target).parents("tr"),a="TBODY"!==t.parent().prop("tagName");let s,n,i;if(a)s=this.markedRecordsForMassAction,n=TYPO3.lang["modal.massundo.text"],i=!0;else{const e=t.data("uid"),a=t.data("table"),r=t.data("recordtitle");s=[a+":"+e],i="pages"===a,n=i?TYPO3.lang["modal.undopage.text"]:TYPO3.lang["modal.undocontent.text"],n=this.createMessage(n,[r,"["+s[0]+"]"]),i&&t.data("parentDeleted")&&(n+=TYPO3.lang["modal.undo.parentpages"])}let r=null;r=i?$("<div />").append($("<p />").text(n),$("<div />",{class:"form-check"}).append($("<input />",{type:"checkbox",id:"undo-recursive",class:"form-check-input"}),$("<label />",{class:"form-check-label",for:"undo-recursive"}).text(TYPO3.lang["modal.undo.recursive"]))):$("<p />").text(n),Modal.advanced({title:TYPO3.lang["modal.undo.header"],content:r,severity:SeverityEnum.ok,staticBackdrop:!0,buttons:[{text:TYPO3.lang["button.cancel"],btnClass:"btn-default",trigger:function(){Modal.dismiss()}},{text:TYPO3.lang["button.undo"],btnClass:"btn-success",action:new DeferredAction((()=>{this.callAjaxAction("undo","object"==typeof s?s:[s],a,r.find("#undo-recursive").prop("checked"))}))}]})},DocumentService.ready().then((()=>{this.initialize()}))}static refreshPageTree(){top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))}getElements(){this.elements={$searchForm:$(RecyclerIdentifiers.searchForm),$searchTextField:$(RecyclerIdentifiers.searchText),$searchSubmitBtn:$(RecyclerIdentifiers.searchSubmitBtn),$depthSelector:$(RecyclerIdentifiers.depthSelector),$tableSelector:$(RecyclerIdentifiers.tableSelector),$recyclerTable:$(RecyclerIdentifiers.recyclerTable),$tableBody:$(RecyclerIdentifiers.recyclerTable).find("tbody"),$paginator:$(RecyclerIdentifiers.paginator),$reloadAction:$(RecyclerIdentifiers.reloadAction),$massUndo:$(RecyclerIdentifiers.massUndo),$massDelete:$(RecyclerIdentifiers.massDelete)}}registerEvents(){this.elements.$searchForm.on("submit",(e=>{e.preventDefault(),""!==this.elements.$searchTextField.val()&&this.loadDeletedElements()})),this.elements.$searchTextField.on("keyup",(e=>{""!==$(e.currentTarget).val()?this.elements.$searchSubmitBtn.removeClass("disabled"):(this.elements.$searchSubmitBtn.addClass("disabled"),this.loadDeletedElements())})),this.elements.$searchTextField.get(0).clearable({onClear:()=>{this.elements.$searchSubmitBtn.addClass("disabled"),this.loadDeletedElements()}}),this.elements.$depthSelector.on("change",(()=>{this.loadAvailableTables().then((()=>{this.loadDeletedElements()}))})),this.elements.$tableSelector.on("change",(()=>{this.paging.currentPage=1,this.loadDeletedElements()})),new RegularEvent("click",this.undoRecord).delegateTo(document,RecyclerIdentifiers.undo),new RegularEvent("click",this.deleteRecord).delegateTo(document,RecyclerIdentifiers.delete),this.elements.$reloadAction.on("click",(e=>{e.preventDefault(),this.loadAvailableTables().then((()=>{this.loadDeletedElements()}))})),this.elements.$paginator.on("click","[data-action]",(e=>{e.preventDefault();const t=$(e.currentTarget);let a=!1;switch(t.data("action")){case"previous":this.paging.currentPage>1&&(this.paging.currentPage--,a=!0);break;case"next":this.paging.currentPage<this.paging.totalPages&&(this.paging.currentPage++,a=!0);break;case"page":this.paging.currentPage=parseInt(t.find("span").text(),10),a=!0}a&&this.loadDeletedElements()})),TYPO3.settings.Recycler.deleteDisable?this.elements.$massDelete.remove():this.elements.$massDelete.show(),new RegularEvent("multiRecordSelection:checkbox:state:changed",this.handleCheckboxStateChanged).bindTo(document),new RegularEvent("multiRecordSelection:action:massundo",this.undoRecord).bindTo(document),new RegularEvent("multiRecordSelection:action:massdelete",this.deleteRecord).bindTo(document)}initialize(){NProgress.configure({parent:".module-loading-indicator",showSpinner:!1}),this.getElements(),this.registerEvents(),TYPO3.settings.Recycler.depthSelection>0?this.elements.$depthSelector.val(TYPO3.settings.Recycler.depthSelection).trigger("change"):this.loadAvailableTables().then((()=>{this.loadDeletedElements()}))}resetMassActionButtons(){this.markedRecordsForMassAction=[],this.elements.$massUndo.find("span.text").text(TYPO3.lang["button.undo"]),this.elements.$massDelete.find("span.text").text(TYPO3.lang["button.delete"]),document.dispatchEvent(new CustomEvent("multiRecordSelection:actions:hide"))}loadAvailableTables(){return NProgress.start(),this.elements.$tableSelector.val(""),this.paging.currentPage=1,new AjaxRequest(TYPO3.settings.ajaxUrls.recycler).withQueryArguments({action:"getTables",startUid:TYPO3.settings.Recycler.startUid,depth:this.elements.$depthSelector.find("option:selected").val()}).get().then((async e=>{const t=await e.resolve(),a=[];this.elements.$tableSelector.children().remove();for(const e of t){const t=e[0],s=e[1],n=(e[2]?e[2]:TYPO3.lang.label_allrecordtypes)+" ("+s+")";a.push($("<option />").val(t).text(n))}return a.length>0&&(this.elements.$tableSelector.append(a),""!==TYPO3.settings.Recycler.tableSelection&&this.elements.$tableSelector.val(TYPO3.settings.Recycler.tableSelection)),e})).finally((()=>NProgress.done()))}loadDeletedElements(){return NProgress.start(),this.resetMassActionButtons(),new AjaxRequest(TYPO3.settings.ajaxUrls.recycler).withQueryArguments({action:"getDeletedRecords",depth:this.elements.$depthSelector.find("option:selected").val(),startUid:TYPO3.settings.Recycler.startUid,table:this.elements.$tableSelector.find("option:selected").val(),filterTxt:this.elements.$searchTextField.val(),start:(this.paging.currentPage-1)*this.paging.itemsPerPage,limit:this.paging.itemsPerPage}).get().then((async e=>{const t=await e.resolve();return this.elements.$tableBody.html(t.rows),this.buildPaginator(t.totalItems),e})).finally((()=>NProgress.done()))}callAjaxAction(e,t,a,s=!1){const n={records:t,action:""};let i=!1;if("undo"===e)n.action="undoRecords",n.recursive=s?1:0,i=!0;else{if("delete"!==e)return null;n.action="deleteRecords"}return NProgress.start(),new AjaxRequest(TYPO3.settings.ajaxUrls.recycler).post(n).then((async e=>{const t=await e.resolve();return t.success?Notification.success("",t.message):Notification.error("",t.message),this.paging.currentPage=1,this.loadAvailableTables().then((()=>{this.loadDeletedElements(),a&&this.resetMassActionButtons(),i&&Recycler.refreshPageTree()})),e}))}createMessage(e,t){return void 0===e?"":e.replace(/\{([0-9]+)\}/g,(function(e,a){return t[a]}))}buildPaginator(e){if(0===e)return void this.elements.$paginator.contents().remove();if(this.paging.totalItems=e,this.paging.totalPages=Math.ceil(e/this.paging.itemsPerPage),1===this.paging.totalPages)return void this.elements.$paginator.contents().remove();const t=$("<ul />",{class:"pagination"}),a=[],s=$("<li />",{class:"page-item"}).append($("<button />",{class:"page-link",type:"button","data-action":"previous"}).append($("<typo3-backend-icon />",{identifier:"actions-arrow-left-alt",size:"small"}))),n=$("<li />",{class:"page-item"}).append($("<button />",{class:"page-link",type:"button","data-action":"next"}).append($("<typo3-backend-icon />",{identifier:"actions-arrow-right-alt",size:"small"})));1===this.paging.currentPage&&s.disablePagingAction(),this.paging.currentPage===this.paging.totalPages&&n.disablePagingAction();for(let e=1;e<=this.paging.totalPages;e++){const t=$("<li />",{class:"page-item"+(this.paging.currentPage===e?" active":"")});t.append($("<button />",{class:"page-link",type:"button","data-action":"page"}).append($("<span />").text(e))),a.push(t)}t.append(s,a,n),this.elements.$paginator.empty().append(t)}}$.fn.disablePagingAction=function(){$(this).addClass("disabled").find("button").prop("disabled",!0)};export default new Recycler; \ No newline at end of file diff --git a/typo3/sysext/redirects/Resources/Public/JavaScript/event-handler.js b/typo3/sysext/redirects/Resources/Public/JavaScript/event-handler.js index 7a812a109036..5b591e84e3c6 100644 --- a/typo3/sysext/redirects/Resources/Public/JavaScript/event-handler.js +++ b/typo3/sysext/redirects/Resources/Public/JavaScript/event-handler.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import NotificationService from"@typo3/backend/notification.js";import DeferredAction from"@typo3/backend/action-button/deferred-action.js";class EventHandler{constructor(){document.addEventListener("typo3:redirects:slugChanged",(e=>this.onSlugChanged(e.detail)))}dispatchCustomEvent(e,t=null){const r=new CustomEvent(e,{detail:t});document.dispatchEvent(r)}onSlugChanged(e){let t=[];const r=e.correlations;e.autoUpdateSlugs&&t.push({label:TYPO3.lang["notification.redirects.button.revert_update"],action:new DeferredAction((()=>this.revert([r.correlationIdSlugUpdate,r.correlationIdRedirectCreation])))}),e.autoCreateRedirects&&t.push({label:TYPO3.lang["notification.redirects.button.revert_redirect"],action:new DeferredAction((()=>this.revert([r.correlationIdRedirectCreation])))});let i=TYPO3.lang["notification.slug_only.title"],o=TYPO3.lang["notification.slug_only.message"];e.autoCreateRedirects&&(i=TYPO3.lang["notification.slug_and_redirects.title"],o=TYPO3.lang["notification.slug_and_redirects.message"]),NotificationService.info(i,o,0,t)}revert(e){const t=new AjaxRequest(TYPO3.settings.ajaxUrls.redirects_revert_correlation).withQueryArguments({correlation_ids:e}).get();return t.then((async e=>{const t=await e.resolve();"ok"===t.status&&NotificationService.success(t.title,t.message),"error"===t.status&&NotificationService.error(t.title,t.message)})).catch((()=>{NotificationService.error(TYPO3.lang.redirects_error_title,TYPO3.lang.redirects_error_message)})),t}}export default new EventHandler; \ No newline at end of file +import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import NotificationService from"@typo3/backend/notification.js";import DeferredAction from"@typo3/backend/action-button/deferred-action.js";class EventHandler{constructor(){document.addEventListener("typo3:redirects:slugChanged",(e=>this.onSlugChanged(e.detail)))}dispatchCustomEvent(e,t=null){const r=new CustomEvent(e,{detail:t});document.dispatchEvent(r)}onSlugChanged(e){const t=[],r=e.correlations;e.autoUpdateSlugs&&t.push({label:TYPO3.lang["notification.redirects.button.revert_update"],action:new DeferredAction((async()=>{await this.revert([r.correlationIdSlugUpdate,r.correlationIdRedirectCreation])}))}),e.autoCreateRedirects&&t.push({label:TYPO3.lang["notification.redirects.button.revert_redirect"],action:new DeferredAction((async()=>{await this.revert([r.correlationIdRedirectCreation])}))});let i=TYPO3.lang["notification.slug_only.title"],n=TYPO3.lang["notification.slug_only.message"];e.autoCreateRedirects&&(i=TYPO3.lang["notification.slug_and_redirects.title"],n=TYPO3.lang["notification.slug_and_redirects.message"]),NotificationService.info(i,n,0,t)}revert(e){const t=new AjaxRequest(TYPO3.settings.ajaxUrls.redirects_revert_correlation).withQueryArguments({correlation_ids:e}).get();return t.then((async e=>{const t=await e.resolve();"ok"===t.status&&NotificationService.success(t.title,t.message),"error"===t.status&&NotificationService.error(t.title,t.message)})).catch((()=>{NotificationService.error(TYPO3.lang.redirects_error_title,TYPO3.lang.redirects_error_message)})),t}}export default new EventHandler; \ No newline at end of file diff --git a/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/ckeditor5.js b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/ckeditor5.js index 984395b0631b..45b76141b58e 100644 --- a/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/ckeditor5.js +++ b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/ckeditor5.js @@ -18,4 +18,4 @@ var __decorate=function(t,e,o,i){var n,r=arguments.length,s=r<3?e:null===i?i=Obj rows="18" data-formengine-validation-rules="${this.formEngine.validationRules}" >${this.formEngine.value}</textarea> - `}firstUpdated(){if(!(this.target instanceof HTMLElement))throw new Error("No rich-text content target found.");const t=(this.options.importModules||[]).map((t=>import(t)));Promise.all(t).then((t=>{const e=t.filter((t=>t.default)).map((t=>t.default)),o=CKEditor5.builtinPlugins.concat(e),i=[].concat(...o.filter((t=>t.overrides?.length>0)).map((t=>t.overrides))),n=o.filter((t=>!i.includes(t)));let r={toolbar:this.options.toolbar,plugins:n,typo3link:this.options.typo3link||null,removePlugins:this.options.removePlugins||[]};this.options.language&&(r.language=this.options.language),this.options.style&&(r.style=this.options.style),this.options.wordCount&&(r.wordCount=this.options.wordCount),this.options.table&&(r.table=this.options.table),this.options.heading&&(r.heading=this.options.heading),this.options.alignment&&(r.alignment=this.options.alignment),CKEditor5.create(this.target,r).then((t=>{this.applyEditableElementStyles(t),this.handleWordCountPlugin(t),this.applyReadOnly(t)}))}))}applyEditableElementStyles(t){const e=t.editing.view,o={"min-height":this.options.height,"min-width":this.options.width};Object.keys(o).forEach((t=>{let i=o[t];i&&(isFinite(i)&&!Number.isNaN(parseFloat(i))&&(i+="px"),e.change((o=>{o.setStyle(t,i,e.document.getRoot())})))}))}handleWordCountPlugin(t){if(t.plugins.has("WordCount")&&(this.options?.wordCount?.displayWords||this.options?.wordCount?.displayCharacters)){const e=t.plugins.get("WordCount");this.renderRoot.appendChild(e.wordCountContainer)}}applyReadOnly(t){this.options.readOnly&&t.enableReadOnlyMode("typo3-lock")}};__decorate([property({type:Object})],CKEditor5Element.prototype,"options",void 0),__decorate([property({type:Object,attribute:"form-engine"})],CKEditor5Element.prototype,"formEngine",void 0),__decorate([query("textarea")],CKEditor5Element.prototype,"target",void 0),CKEditor5Element=__decorate([customElement("typo3-rte-ckeditor-ckeditor5")],CKEditor5Element);export{CKEditor5Element}; \ No newline at end of file + `}firstUpdated(){if(!(this.target instanceof HTMLElement))throw new Error("No rich-text content target found.");const t=(this.options.importModules||[]).map((t=>import(t)));Promise.all(t).then((t=>{const e=t.filter((t=>t.default)).map((t=>t.default)),o=CKEditor5.builtinPlugins.concat(e),i=[].concat(...o.filter((t=>t.overrides?.length>0)).map((t=>t.overrides))),n=o.filter((t=>!i.includes(t))),r={toolbar:this.options.toolbar,plugins:n,typo3link:this.options.typo3link||null,removePlugins:this.options.removePlugins||[]};this.options.language&&(r.language=this.options.language),this.options.style&&(r.style=this.options.style),this.options.wordCount&&(r.wordCount=this.options.wordCount),this.options.table&&(r.table=this.options.table),this.options.heading&&(r.heading=this.options.heading),this.options.alignment&&(r.alignment=this.options.alignment),CKEditor5.create(this.target,r).then((t=>{this.applyEditableElementStyles(t),this.handleWordCountPlugin(t),this.applyReadOnly(t)}))}))}applyEditableElementStyles(t){const e=t.editing.view,o={"min-height":this.options.height,"min-width":this.options.width};Object.keys(o).forEach((t=>{let i=o[t];i&&(isFinite(i)&&!Number.isNaN(parseFloat(i))&&(i+="px"),e.change((o=>{o.setStyle(t,i,e.document.getRoot())})))}))}handleWordCountPlugin(t){if(t.plugins.has("WordCount")&&(this.options?.wordCount?.displayWords||this.options?.wordCount?.displayCharacters)){const e=t.plugins.get("WordCount");this.renderRoot.appendChild(e.wordCountContainer)}}applyReadOnly(t){this.options.readOnly&&t.enableReadOnlyMode("typo3-lock")}};__decorate([property({type:Object})],CKEditor5Element.prototype,"options",void 0),__decorate([property({type:Object,attribute:"form-engine"})],CKEditor5Element.prototype,"formEngine",void 0),__decorate([query("textarea")],CKEditor5Element.prototype,"target",void 0),CKEditor5Element=__decorate([customElement("typo3-rte-ckeditor-ckeditor5")],CKEditor5Element);export{CKEditor5Element}; \ No newline at end of file diff --git a/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/rte-link-browser.js b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/rte-link-browser.js index 28b8236c025c..6ad448bdf7c6 100644 --- a/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/rte-link-browser.js +++ b/typo3/sysext/rte_ckeditor/Resources/Public/JavaScript/rte-link-browser.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import LinkBrowser from"@typo3/backend/link-browser.js";import Modal from"@typo3/backend/modal.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{LINK_ALLOWED_ATTRIBUTES,addLinkPrefix}from"@typo3/rte-ckeditor/plugin/typo3-link.js";class RteLinkBrowser{constructor(){this.editor=null,this.selectionStartPosition=null,this.selectionEndPosition=null}initialize(t){this.editor=Modal.currentModal.userData.editor,this.selectionStartPosition=Modal.currentModal.userData.selectionStartPosition,this.selectionEndPosition=Modal.currentModal.userData.selectionEndPosition;const e=document.querySelector(".t3js-removeCurrentLink");null!==e&&new RegularEvent("click",(t=>{t.preventDefault(),this.restoreSelection(),this.editor.execute("unlink"),Modal.dismiss()})).bindTo(e)}finalizeFunction(t){const e=LinkBrowser.getLinkAttributeValues(),i=e.params?e.params:"";delete e.params;const n=this.convertAttributes(e,"");this.restoreSelection(),this.editor.execute("link",this.sanitizeLink(t,i),n),Modal.dismiss()}restoreSelection(){this.editor.model.change((t=>{const e=[t.createRange(this.selectionStartPosition,this.selectionEndPosition)];t.setSelection(e)}))}convertAttributes(t,e){const i={attrs:{}};for(const[e,n]of Object.entries(t))LINK_ALLOWED_ATTRIBUTES.includes(e)&&(i.attrs[addLinkPrefix(e)]=n);return"string"==typeof e&&""!==e&&(i.linkText=e),i}sanitizeLink(t,e){const i=t.match(/^([a-z0-9]+:\/\/[^:\/?#]+(?:\/?[^?#]*)?)(\??[^#]*)(#?.*)$/);if(i&&i.length>0){t=i[1]+i[2];const n=i[2].length>0?"&":"?";e.length>0&&("&"===e[0]&&(e=e.substr(1)),e.length>0&&(t+=n+e)),t+=i[3]}return t}}let rteLinkBrowser=new RteLinkBrowser;export default rteLinkBrowser;LinkBrowser.finalizeFunction=t=>{rteLinkBrowser.finalizeFunction(t)}; \ No newline at end of file +import LinkBrowser from"@typo3/backend/link-browser.js";import Modal from"@typo3/backend/modal.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{LINK_ALLOWED_ATTRIBUTES,addLinkPrefix}from"@typo3/rte-ckeditor/plugin/typo3-link.js";class RteLinkBrowser{constructor(){this.editor=null,this.selectionStartPosition=null,this.selectionEndPosition=null}initialize(){this.editor=Modal.currentModal.userData.editor,this.selectionStartPosition=Modal.currentModal.userData.selectionStartPosition,this.selectionEndPosition=Modal.currentModal.userData.selectionEndPosition;const t=document.querySelector(".t3js-removeCurrentLink");null!==t&&new RegularEvent("click",(t=>{t.preventDefault(),this.restoreSelection(),this.editor.execute("unlink"),Modal.dismiss()})).bindTo(t)}finalizeFunction(t){const e=LinkBrowser.getLinkAttributeValues(),i=e.params?e.params:"";delete e.params;const n=this.convertAttributes(e,"");this.restoreSelection(),this.editor.execute("link",this.sanitizeLink(t,i),n),Modal.dismiss()}restoreSelection(){this.editor.model.change((t=>{const e=[t.createRange(this.selectionStartPosition,this.selectionEndPosition)];t.setSelection(e)}))}convertAttributes(t,e){const i={attrs:{}};for(const[e,n]of Object.entries(t))LINK_ALLOWED_ATTRIBUTES.includes(e)&&(i.attrs[addLinkPrefix(e)]=n);return"string"==typeof e&&""!==e&&(i.linkText=e),i}sanitizeLink(t,e){const i=t.match(/^([a-z0-9]+:\/\/[^:/?#]+(?:\/?[^?#]*)?)(\??[^#]*)(#?.*)$/);if(i&&i.length>0){t=i[1]+i[2];const n=i[2].length>0?"&":"?";e.length>0&&("&"===e[0]&&(e=e.substr(1)),e.length>0&&(t+=n+e)),t+=i[3]}return t}}const rteLinkBrowser=new RteLinkBrowser;export default rteLinkBrowser;LinkBrowser.finalizeFunction=t=>{rteLinkBrowser.finalizeFunction(t)}; \ No newline at end of file diff --git a/typo3/sysext/scheduler/Resources/Public/JavaScript/scheduler.js b/typo3/sysext/scheduler/Resources/Public/JavaScript/scheduler.js index 9449b08da2ee..e01b21767b64 100644 --- a/typo3/sysext/scheduler/Resources/Public/JavaScript/scheduler.js +++ b/typo3/sysext/scheduler/Resources/Public/JavaScript/scheduler.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import Tablesort from"tablesort";import DocumentSaveActions from"@typo3/backend/document-save-actions.js";import RegularEvent from"@typo3/core/event/regular-event.js";import Modal from"@typo3/backend/modal.js";import Icons from"@typo3/backend/icons.js";import{MessageUtility}from"@typo3/backend/utility/message-utility.js";import PersistentStorage from"@typo3/backend/storage/persistent.js";import DateTimePicker from"@typo3/backend/date-time-picker.js";import{MultiRecordSelectionSelectors}from"@typo3/backend/multi-record-selection.js";class Scheduler{static updateElementBrowserTriggers(){document.querySelectorAll(".t3js-element-browser").forEach((e=>{const t=document.getElementById(e.dataset.triggerFor);e.dataset.params=t.name+"|||pages"}))}static resolveDefaultNumberOfDays(){const e=document.getElementById("task_tableGarbageCollection_numberOfDays");return null===e||void 0===e.dataset.defaultNumberOfDays?null:JSON.parse(e.dataset.defaultNumberOfDays)}static storeCollapseState(e,t){let a={};PersistentStorage.isset("moduleData.scheduler_manage")&&(a=PersistentStorage.get("moduleData.scheduler_manage"));const l={};l[e]=t?1:0,$.extend(a,l),PersistentStorage.set("moduleData.scheduler_manage",a)}constructor(){this.initializeEvents(),this.initializeDefaultStates(),DocumentSaveActions.getInstance().addPreSubmitCallback((()=>{let e=$("#task_class").val();e=e.toLowerCase().replace(/\\/g,"-"),$(".extraFields").appendTo($("#extraFieldsHidden")),$(".extra_fields_"+e).appendTo($("#extraFieldsSection"))}))}actOnChangedTaskClass(e){let t=e.val();t=t.toLowerCase().replace(/\\/g,"-"),$(".extraFields").hide(),$(".extra_fields_"+t).show()}actOnChangedTaskType(e){this.toggleFieldsByTaskType($(e.currentTarget).val())}actOnChangeSchedulerTableGarbageCollectionAllTables(e){let t=$("#task_tableGarbageCollection_numberOfDays"),a=$("#task_tableGarbageCollection_table");if(e.prop("checked"))a.prop("disabled",!0),t.prop("disabled",!0);else{let e=parseInt(t.val(),10);if(e<1){let t=a.val();const l=Scheduler.resolveDefaultNumberOfDays();null!==l&&(e=l[t])}a.prop("disabled",!1),e>0&&t.prop("disabled",!1)}}actOnChangeSchedulerTableGarbageCollectionTable(e){let t=$("#task_tableGarbageCollection_numberOfDays");const a=Scheduler.resolveDefaultNumberOfDays();null!==a&&a[e.val()]>0?(t.prop("disabled",!1),t.val(a[e.val()])):(t.prop("disabled",!0),t.val(0))}toggleFieldsByTaskType(e){e=parseInt(e+"",10),$("#task_end_col").toggle(2===e),$("#task_frequency_row").toggle(2===e)}initializeEvents(){$("#task_class").on("change",(e=>{this.actOnChangedTaskClass($(e.currentTarget))})),$("#task_type").on("change",this.actOnChangedTaskType.bind(this)),$("#task_tableGarbageCollection_allTables").on("change",(e=>{this.actOnChangeSchedulerTableGarbageCollectionAllTables($(e.currentTarget))})),$("#task_tableGarbageCollection_table").on("change",(e=>{this.actOnChangeSchedulerTableGarbageCollectionTable($(e.currentTarget))})),$("[data-update-task-frequency]").on("change",(e=>{const t=$(e.currentTarget);$("#task_frequency").val(t.val()),t.val(t.attr("value")).trigger("blur")}));const e=document.querySelector("table.taskGroup-table");null!==e&&new Tablesort(e),document.querySelectorAll("#tx_scheduler_form .t3js-datetimepicker").forEach((e=>DateTimePicker.initialize(e))),$(document).on("click",".t3js-element-browser",(e=>{e.preventDefault();const t=e.currentTarget;Modal.advanced({type:Modal.types.iframe,content:t.href+"&mode="+t.dataset.mode+"&bparams="+t.dataset.params,size:Modal.sizes.large})})),new RegularEvent("show.bs.collapse",this.toggleCollapseIcon.bind(this)).bindTo(document),new RegularEvent("hide.bs.collapse",this.toggleCollapseIcon.bind(this)).bindTo(document),new RegularEvent("multiRecordSelection:action:go",this.executeTasks.bind(this)).bindTo(document),new RegularEvent("multiRecordSelection:action:go_cron",this.executeTasks.bind(this)).bindTo(document),window.addEventListener("message",this.listenOnElementBrowser.bind(this))}initializeDefaultStates(){let e=$("#task_type");e.length&&this.toggleFieldsByTaskType(e.val());let t=$("#task_class");t.length&&(this.actOnChangedTaskClass(t),Scheduler.updateElementBrowserTriggers())}listenOnElementBrowser(e){if(!MessageUtility.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;if("typo3:elementBrowser:elementAdded"===e.data.actionName){if(void 0===e.data.fieldName)throw"fieldName not defined in message";if(void 0===e.data.value)throw"value not defined in message";document.querySelector('input[name="'+e.data.fieldName+'"]').value=e.data.value.split("_").pop()}}toggleCollapseIcon(e){const t="hide.bs.collapse"===e.type,a=document.querySelector('.t3js-toggle-table[data-bs-target="#'+e.target.id+'"] .collapseIcon');null!==a&&Icons.getIcon(t?"actions-view-list-expand":"actions-view-list-collapse",Icons.sizes.small).then((e=>{a.innerHTML=e})),Scheduler.storeCollapseState($(e.target).data("table"),t)}executeTasks(e){const t=document.querySelector("#tx_scheduler_form");if(null===t)return;const a=[];if(e.detail.checkboxes.forEach((e=>{const t=e.closest(MultiRecordSelectionSelectors.elementSelector);null!==t&&t.dataset.taskId&&a.push(t.dataset.taskId)})),a.length){if("multiRecordSelection:action:go_cron"===e.type){const e=document.createElement("input");e.setAttribute("type","hidden"),e.setAttribute("name","scheduleCron"),e.setAttribute("value",a.join(",")),t.append(e)}else{const e=document.createElement("input");e.setAttribute("type","hidden"),e.setAttribute("name","execute"),e.setAttribute("value",a.join(",")),t.append(e)}t.submit()}}}export default new Scheduler; \ No newline at end of file +import $ from"jquery";import Tablesort from"tablesort";import DocumentSaveActions from"@typo3/backend/document-save-actions.js";import RegularEvent from"@typo3/core/event/regular-event.js";import Modal from"@typo3/backend/modal.js";import Icons from"@typo3/backend/icons.js";import{MessageUtility}from"@typo3/backend/utility/message-utility.js";import PersistentStorage from"@typo3/backend/storage/persistent.js";import DateTimePicker from"@typo3/backend/date-time-picker.js";import{MultiRecordSelectionSelectors}from"@typo3/backend/multi-record-selection.js";class Scheduler{constructor(){this.initializeEvents(),this.initializeDefaultStates(),DocumentSaveActions.getInstance().addPreSubmitCallback((()=>{let e=$("#task_class").val();e=e.toLowerCase().replace(/\\/g,"-"),$(".extraFields").appendTo($("#extraFieldsHidden")),$(".extra_fields_"+e).appendTo($("#extraFieldsSection"))}))}static updateElementBrowserTriggers(){document.querySelectorAll(".t3js-element-browser").forEach((e=>{const t=document.getElementById(e.dataset.triggerFor);e.dataset.params=t.name+"|||pages"}))}static resolveDefaultNumberOfDays(){const e=document.getElementById("task_tableGarbageCollection_numberOfDays");return null===e||void 0===e.dataset.defaultNumberOfDays?null:JSON.parse(e.dataset.defaultNumberOfDays)}static storeCollapseState(e,t){let a={};PersistentStorage.isset("moduleData.scheduler_manage")&&(a=PersistentStorage.get("moduleData.scheduler_manage"));const l={};l[e]=t?1:0,$.extend(a,l),PersistentStorage.set("moduleData.scheduler_manage",a)}actOnChangedTaskClass(e){let t=e.val();t=t.toLowerCase().replace(/\\/g,"-"),$(".extraFields").hide(),$(".extra_fields_"+t).show()}actOnChangedTaskType(e){this.toggleFieldsByTaskType($(e.currentTarget).val())}actOnChangeSchedulerTableGarbageCollectionAllTables(e){const t=$("#task_tableGarbageCollection_numberOfDays"),a=$("#task_tableGarbageCollection_table");if(e.prop("checked"))a.prop("disabled",!0),t.prop("disabled",!0);else{let e=parseInt(t.val(),10);if(e<1){const t=a.val(),l=Scheduler.resolveDefaultNumberOfDays();null!==l&&(e=l[t])}a.prop("disabled",!1),e>0&&t.prop("disabled",!1)}}actOnChangeSchedulerTableGarbageCollectionTable(e){const t=$("#task_tableGarbageCollection_numberOfDays"),a=Scheduler.resolveDefaultNumberOfDays();null!==a&&a[e.val()]>0?(t.prop("disabled",!1),t.val(a[e.val()])):(t.prop("disabled",!0),t.val(0))}toggleFieldsByTaskType(e){e=parseInt(e+"",10),$("#task_end_col").toggle(2===e),$("#task_frequency_row").toggle(2===e)}initializeEvents(){$("#task_class").on("change",(e=>{this.actOnChangedTaskClass($(e.currentTarget))})),$("#task_type").on("change",this.actOnChangedTaskType.bind(this)),$("#task_tableGarbageCollection_allTables").on("change",(e=>{this.actOnChangeSchedulerTableGarbageCollectionAllTables($(e.currentTarget))})),$("#task_tableGarbageCollection_table").on("change",(e=>{this.actOnChangeSchedulerTableGarbageCollectionTable($(e.currentTarget))})),$("[data-update-task-frequency]").on("change",(e=>{const t=$(e.currentTarget);$("#task_frequency").val(t.val()),t.val(t.attr("value")).trigger("blur")}));const e=document.querySelector("table.taskGroup-table");null!==e&&new Tablesort(e),document.querySelectorAll("#tx_scheduler_form .t3js-datetimepicker").forEach((e=>DateTimePicker.initialize(e))),$(document).on("click",".t3js-element-browser",(e=>{e.preventDefault();const t=e.currentTarget;Modal.advanced({type:Modal.types.iframe,content:t.href+"&mode="+t.dataset.mode+"&bparams="+t.dataset.params,size:Modal.sizes.large})})),new RegularEvent("show.bs.collapse",this.toggleCollapseIcon.bind(this)).bindTo(document),new RegularEvent("hide.bs.collapse",this.toggleCollapseIcon.bind(this)).bindTo(document),new RegularEvent("multiRecordSelection:action:go",this.executeTasks.bind(this)).bindTo(document),new RegularEvent("multiRecordSelection:action:go_cron",this.executeTasks.bind(this)).bindTo(document),window.addEventListener("message",this.listenOnElementBrowser.bind(this))}initializeDefaultStates(){const e=$("#task_type");e.length&&this.toggleFieldsByTaskType(e.val());const t=$("#task_class");t.length&&(this.actOnChangedTaskClass(t),Scheduler.updateElementBrowserTriggers())}listenOnElementBrowser(e){if(!MessageUtility.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;if("typo3:elementBrowser:elementAdded"===e.data.actionName){if(void 0===e.data.fieldName)throw"fieldName not defined in message";if(void 0===e.data.value)throw"value not defined in message";document.querySelector('input[name="'+e.data.fieldName+'"]').value=e.data.value.split("_").pop()}}toggleCollapseIcon(e){const t="hide.bs.collapse"===e.type,a=document.querySelector('.t3js-toggle-table[data-bs-target="#'+e.target.id+'"] .collapseIcon');null!==a&&Icons.getIcon(t?"actions-view-list-expand":"actions-view-list-collapse",Icons.sizes.small).then((e=>{a.innerHTML=e})),Scheduler.storeCollapseState($(e.target).data("table"),t)}executeTasks(e){const t=document.querySelector("#tx_scheduler_form");if(null===t)return;const a=[];if(e.detail.checkboxes.forEach((e=>{const t=e.closest(MultiRecordSelectionSelectors.elementSelector);null!==t&&t.dataset.taskId&&a.push(t.dataset.taskId)})),a.length){if("multiRecordSelection:action:go_cron"===e.type){const e=document.createElement("input");e.setAttribute("type","hidden"),e.setAttribute("name","scheduleCron"),e.setAttribute("value",a.join(",")),t.append(e)}else{const e=document.createElement("input");e.setAttribute("type","hidden"),e.setAttribute("name","execute"),e.setAttribute("value",a.join(",")),t.append(e)}t.submit()}}}export default new Scheduler; \ No newline at end of file diff --git a/typo3/sysext/t3editor/Resources/Public/JavaScript/element/code-mirror-element.js b/typo3/sysext/t3editor/Resources/Public/JavaScript/element/code-mirror-element.js index ed24d362c792..5f89773954a0 100644 --- a/typo3/sysext/t3editor/Resources/Public/JavaScript/element/code-mirror-element.js +++ b/typo3/sysext/t3editor/Resources/Public/JavaScript/element/code-mirror-element.js @@ -14,7 +14,7 @@ var __decorate=function(e,t,r,o){var i,l=arguments.length,n=l<3?t:null===o?o=Obj <div id="codemirror-parent" @keydown=${e=>this.onKeydown(e)}></div> ${this.label?html`<div class="panel panel-${this.panel}">${this.label}</div>`:""} ${null===this.editorView?html`<typo3-backend-spinner size="large" variant="dark"></typo3-backend-spinner>`:""} - `}firstUpdated(){if(this.nolazyload)return void this.initializeEditor(this.firstElementChild);const e={root:document.body};let t=new IntersectionObserver((e=>{e.forEach((e=>{e.intersectionRatio>0&&(t.unobserve(e.target),this.firstElementChild&&"textarea"===this.firstElementChild.nodeName.toLowerCase()&&this.initializeEditor(this.firstElementChild))}))}),e);t.observe(this)}onKeydown(e){e.ctrlKey&&e.altKey&&"f"===e.key&&(e.preventDefault(),this.fullscreen=!0),"Escape"===e.key&&this.fullscreen&&(e.preventDefault(),this.fullscreen=!1)}async initializeEditor(e){const t=EditorView.updateListener.of((t=>{t.docChanged&&(e.value=t.state.doc.toString(),e.dispatchEvent(new CustomEvent("change",{bubbles:!0})))}));this.lineDigits>0?this.style.setProperty("--rows",this.lineDigits.toString()):e.getAttribute("rows")&&this.style.setProperty("--rows",e.getAttribute("rows"));const r=[oneDark,t,lineNumbers(),highlightSpecialChars(),drawSelection(),EditorState.allowMultipleSelections.of(!0),syntaxHighlighting(defaultHighlightStyle,{fallback:!0})];if(this.readonly&&r.push(EditorState.readOnly.of(!0)),this.placeholder&&r.push(placeholder(this.placeholder)),this.mode){const e=await executeJavaScriptModuleInstruction(this.mode);r.push(...e)}this.addons.length>0&&r.push(...await Promise.all(this.addons.map((e=>executeJavaScriptModuleInstruction(e)))));const o=[...defaultKeymap,indentWithTab];if(this.keymaps.length>0){const e=await Promise.all(this.keymaps.map((e=>loadModule(e).then((t=>resolveSubjectRef(t,e))))));e.forEach((e=>o.push(...e)))}r.push(keymap.of(o)),this.editorView=new EditorView({state:EditorState.create({doc:e.value,extensions:r}),parent:this.renderRoot.querySelector("#codemirror-parent"),root:this.renderRoot})}};CodeMirrorElement.styles=css` + `}firstUpdated(){if(this.nolazyload)return void this.initializeEditor(this.firstElementChild);const e={root:document.body},t=new IntersectionObserver((e=>{e.forEach((e=>{e.intersectionRatio>0&&(t.unobserve(e.target),this.firstElementChild&&"textarea"===this.firstElementChild.nodeName.toLowerCase()&&this.initializeEditor(this.firstElementChild))}))}),e);t.observe(this)}onKeydown(e){e.ctrlKey&&e.altKey&&"f"===e.key&&(e.preventDefault(),this.fullscreen=!0),"Escape"===e.key&&this.fullscreen&&(e.preventDefault(),this.fullscreen=!1)}async initializeEditor(e){const t=EditorView.updateListener.of((t=>{t.docChanged&&(e.value=t.state.doc.toString(),e.dispatchEvent(new CustomEvent("change",{bubbles:!0})))}));this.lineDigits>0?this.style.setProperty("--rows",this.lineDigits.toString()):e.getAttribute("rows")&&this.style.setProperty("--rows",e.getAttribute("rows"));const r=[oneDark,t,lineNumbers(),highlightSpecialChars(),drawSelection(),EditorState.allowMultipleSelections.of(!0),syntaxHighlighting(defaultHighlightStyle,{fallback:!0})];if(this.readonly&&r.push(EditorState.readOnly.of(!0)),this.placeholder&&r.push(placeholder(this.placeholder)),this.mode){const e=await executeJavaScriptModuleInstruction(this.mode);r.push(...e)}this.addons.length>0&&r.push(...await Promise.all(this.addons.map((e=>executeJavaScriptModuleInstruction(e)))));const o=[...defaultKeymap,indentWithTab];if(this.keymaps.length>0){const e=await Promise.all(this.keymaps.map((e=>loadModule(e).then((t=>resolveSubjectRef(t,e))))));e.forEach((e=>o.push(...e)))}r.push(keymap.of(o)),this.editorView=new EditorView({state:EditorState.create({doc:e.value,extensions:r}),parent:this.renderRoot.querySelector("#codemirror-parent"),root:this.renderRoot})}};CodeMirrorElement.styles=css` :host { display: flex; flex-direction: column; diff --git a/typo3/sysext/t3editor/Resources/Public/JavaScript/language/typoscript.js b/typo3/sysext/t3editor/Resources/Public/JavaScript/language/typoscript.js index 71e50d37c604..a38b5e2f3511 100644 --- a/typo3/sysext/t3editor/Resources/Public/JavaScript/language/typoscript.js +++ b/typo3/sysext/t3editor/Resources/Public/JavaScript/language/typoscript.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import{StreamLanguage,LanguageSupport}from"@codemirror/language";import{typoScriptStreamParser}from"@typo3/t3editor/stream-parser/typoscript.js";import TsCodeCompletion from"@typo3/t3editor/autocomplete/ts-code-completion.js";import{syntaxTree}from"@codemirror/language";export function typoscript(){const t=StreamLanguage.define(typoScriptStreamParser),e=t.data.of({autocomplete:complete});return new LanguageSupport(t,[e])}export function complete(t){t.matchBefore(/\w*/);if(!t.explicit)return null;const e=parseCodeMirror5CompatibleCompletionState(t),o=t.pos-(e.completingAfterDot?1:0),r=syntaxTree(t.state).resolveInner(o,-1),n="Document"===r.name||e.completingAfterDot?"":t.state.sliceDoc(r.from,o),s="Document"===r.name||e.completingAfterDot?t.pos:r.from;let a={start:r.from,end:o,string:n,type:r.name};/^[\w$_]*$/.test(n)||(a={start:t.pos,end:t.pos,string:"",type:"."===n?"property":null}),e.token=a;const i=TsCodeCompletion.refreshCodeCompletion(e);if(("string"===r.name||"comment"===r.name)&&tokenIsSubStringOfKeywords(n,i))return null;return{from:s,options:getCompletions(n,i).map((t=>({label:t,type:"keyword"})))}}function parseCodeMirror5CompatibleCompletionState(t){const e=t.state.sliceDoc().split(t.state.lineBreak).length,o=t.state.sliceDoc(0,t.pos).split(t.state.lineBreak).length,r=t.state.sliceDoc().split(t.state.lineBreak)[o-1],n="."===t.state.sliceDoc(t.pos-1,t.pos);return{lineTokens:extractCodemirror5StyleLineTokens(e,t),currentLineNumber:o,currentLine:r,lineCount:e,completingAfterDot:n}}function extractCodemirror5StyleLineTokens(t,e){const o=Array(t).fill("").map((()=>[]));let r=0,n=1;return syntaxTree(e.state).cursor().iterate((s=>{const a=s.type.name||s.name;if("Document"===a)return;const i=s.from,l=s.to;r<i&&e.state.sliceDoc(r,i).split(e.state.lineBreak).forEach((e=>{e&&(o[Math.min(n-1,t-1)].push({type:null,string:e,start:r,end:r+e.length}),n++,r+=e.length)}));const p=e.state.sliceDoc(s.from,s.to);n=e.state.sliceDoc(0,s.from).split(e.state.lineBreak).length,o[n-1].push({type:a,string:p,start:i,end:l}),r=l})),r<e.state.doc.length&&o[n-1].push({type:null,string:e.state.sliceDoc(r),start:r,end:e.state.doc.length}),o}function tokenIsSubStringOfKeywords(t,e){const o=t.length;for(let r=0;r<e.length;++r)if(t===e[r].substr(o))return!0;return!1}function getCompletions(t,e){const o=new Set;for(let n=0,s=e.length;n<s;++n)0!==(r=e[n]).lastIndexOf(t,0)||o.has(r)||o.add(r);var r;const n=Array.from(o);return n.sort(),n} \ No newline at end of file +import{StreamLanguage,LanguageSupport}from"@codemirror/language";import{typoScriptStreamParser}from"@typo3/t3editor/stream-parser/typoscript.js";import TsCodeCompletion from"@typo3/t3editor/autocomplete/ts-code-completion.js";import{syntaxTree}from"@codemirror/language";export function typoscript(){const t=StreamLanguage.define(typoScriptStreamParser),e=t.data.of({autocomplete:complete});return new LanguageSupport(t,[e])}export function complete(t){if(!t.explicit)return null;const e=parseCodeMirror5CompatibleCompletionState(t),o=t.pos-(e.completingAfterDot?1:0),r=syntaxTree(t.state).resolveInner(o,-1),n="Document"===r.name||e.completingAfterDot?"":t.state.sliceDoc(r.from,o),s="Document"===r.name||e.completingAfterDot?t.pos:r.from;let a={start:r.from,end:o,string:n,type:r.name};/^[\w$_]*$/.test(n)||(a={start:t.pos,end:t.pos,string:"",type:"."===n?"property":null}),e.token=a;const i=TsCodeCompletion.refreshCodeCompletion(e);if(("string"===r.name||"comment"===r.name)&&tokenIsSubStringOfKeywords(n,i))return null;return{from:s,options:getCompletions(n,i).map((t=>({label:t,type:"keyword"})))}}function parseCodeMirror5CompatibleCompletionState(t){const e=t.state.sliceDoc().split(t.state.lineBreak).length,o=t.state.sliceDoc(0,t.pos).split(t.state.lineBreak).length,r=t.state.sliceDoc().split(t.state.lineBreak)[o-1],n="."===t.state.sliceDoc(t.pos-1,t.pos);return{lineTokens:extractCodemirror5StyleLineTokens(e,t),currentLineNumber:o,currentLine:r,lineCount:e,completingAfterDot:n}}function extractCodemirror5StyleLineTokens(t,e){const o=Array(t).fill("").map((()=>[]));let r=0,n=1;return syntaxTree(e.state).cursor().iterate((s=>{const a=s.type.name||s.name;if("Document"===a)return;const i=s.from,l=s.to;r<i&&e.state.sliceDoc(r,i).split(e.state.lineBreak).forEach((e=>{e&&(o[Math.min(n-1,t-1)].push({type:null,string:e,start:r,end:r+e.length}),n++,r+=e.length)}));const p=e.state.sliceDoc(s.from,s.to);n=e.state.sliceDoc(0,s.from).split(e.state.lineBreak).length,o[n-1].push({type:a,string:p,start:i,end:l}),r=l})),r<e.state.doc.length&&o[n-1].push({type:null,string:e.state.sliceDoc(r),start:r,end:e.state.doc.length}),o}function tokenIsSubStringOfKeywords(t,e){const o=t.length;for(let r=0;r<e.length;++r)if(t===e[r].substr(o))return!0;return!1}function getCompletions(t,e){const o=new Set;for(let n=0,s=e.length;n<s;++n)0!==(r=e[n]).lastIndexOf(t,0)||o.has(r)||o.add(r);var r;const n=Array.from(o);return n.sort(),n} \ No newline at end of file diff --git a/typo3/sysext/viewpage/Resources/Public/JavaScript/main.js b/typo3/sysext/viewpage/Resources/Public/JavaScript/main.js index 7d71b34e808a..053bc0e89266 100644 --- a/typo3/sysext/viewpage/Resources/Public/JavaScript/main.js +++ b/typo3/sysext/viewpage/Resources/Public/JavaScript/main.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import interact from"interactjs";import DocumentService from"@typo3/core/document-service.js";import PersistentStorage from"@typo3/backend/storage/persistent.js";import RegularEvent from"@typo3/core/event/regular-event.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";var Selectors;!function(e){e.resizableContainerIdentifier=".t3js-viewpage-resizeable",e.moduleBodySelector=".t3js-module-body",e.customSelector=".t3js-preset-custom",e.customWidthSelector=".t3js-preset-custom-width",e.customHeightSelector=".t3js-preset-custom-height",e.changeOrientationSelector=".t3js-change-orientation",e.changePresetSelector=".t3js-change-preset",e.inputWidthSelector=".t3js-viewpage-input-width",e.inputHeightSelector=".t3js-viewpage-input-height",e.currentLabelSelector=".t3js-viewpage-current-label",e.topbarContainerSelector=".t3js-viewpage-topbar",e.refreshSelector=".t3js-viewpage-refresh"}(Selectors||(Selectors={}));class ViewPage{constructor(){this.defaultLabel="",this.minimalHeight=300,this.minimalWidth=300,this.storagePrefix="moduleData.web_ViewpageView.States.",DocumentService.ready().then((()=>{const e=document.querySelector(".t3js-preset-custom-label");this.defaultLabel=e?.textContent.trim()??"",this.iframe=document.getElementById("tx_this_iframe"),this.inputCustomWidth=document.querySelector(Selectors.inputWidthSelector),this.inputCustomHeight=document.querySelector(Selectors.inputHeightSelector),this.customPresetItem=document.querySelector(Selectors.customSelector),this.customPresetItemWidth=document.querySelector(Selectors.customWidthSelector),this.customPresetItemHeight=document.querySelector(Selectors.customHeightSelector),this.currentLabelElement=document.querySelector(Selectors.currentLabelSelector),this.resizableContainer=document.querySelector(Selectors.resizableContainerIdentifier),this.initialize()}))}getCurrentWidth(){return this.inputCustomWidth.valueAsNumber}getCurrentHeight(){return this.inputCustomHeight.valueAsNumber}setLabel(e){this.currentLabelElement.textContent=e}getCurrentLabel(){return this.currentLabelElement.textContent}persistChanges(e,t){PersistentStorage.set(e,t)}setSize(e,t){isNaN(t)&&(t=this.calculateContainerMaxHeight()),t<this.minimalHeight&&(t=this.minimalHeight),isNaN(e)&&(e=this.calculateContainerMaxWidth()),e<this.minimalWidth&&(e=this.minimalWidth),this.inputCustomWidth.valueAsNumber=e,this.inputCustomHeight.valueAsNumber=t,this.resizableContainer.style.width=`${e}px`,this.resizableContainer.style.height=`${t}px`,this.resizableContainer.style.left="0"}persistCurrentPreset(){let e={width:this.getCurrentWidth(),height:this.getCurrentHeight(),label:this.getCurrentLabel()};this.persistChanges(this.storagePrefix+"current",e)}persistCustomPreset(){let e={width:this.getCurrentWidth(),height:this.getCurrentHeight()};this.customPresetItem.dataset.width=e.width.toString(10),this.customPresetItem.dataset.height=e.height.toString(10),this.customPresetItemWidth.textContent=e.width.toString(10),this.customPresetItemHeight.textContent=e.height.toString(10),this.persistChanges(this.storagePrefix+"current",e),this.persistChanges(this.storagePrefix+"custom",e)}persistCustomPresetAfterChange(){clearTimeout(this.queueDelayTimer),this.queueDelayTimer=window.setTimeout((()=>{this.persistCustomPreset()}),1e3)}initialize(){new RegularEvent("click",(()=>{this.setSize(this.getCurrentHeight(),this.getCurrentWidth()),this.persistCurrentPreset()})).bindTo(document.querySelector(Selectors.changeOrientationSelector)),[this.inputCustomWidth,this.inputCustomHeight].forEach((e=>{new DebounceEvent("change",(()=>{this.setSize(this.getCurrentWidth(),this.getCurrentHeight()),this.setLabel(this.defaultLabel),this.persistCustomPresetAfterChange()}),50).bindTo(e)})),new RegularEvent("click",((e,t)=>{this.setSize(parseInt(t.dataset.width,10),parseInt(t.dataset.height,10)),this.setLabel(t.dataset.label),this.persistCurrentPreset()})).delegateTo(document,Selectors.changePresetSelector),new RegularEvent("click",(()=>{this.iframe.contentWindow.location.reload()})).bindTo(document.querySelector(Selectors.refreshSelector)),interact(this.resizableContainer).on("resizestart",(e=>{const t=document.createElement("div");t.id="viewpage-iframe-cover",t.setAttribute("style","z-index:99;position:absolute;width:100%;top:0;left:0;height:100%;"),e.target.appendChild(t)})).on("resizeend",(()=>{document.getElementById("viewpage-iframe-cover").remove(),this.persistCustomPreset()})).resizable({origin:"self",edges:{top:!1,left:!0,bottom:!0,right:!0},listeners:{move:e=>{Object.assign(e.target.style,{width:`${e.rect.width}px`,height:`${e.rect.height}px`}),this.inputCustomWidth.valueAsNumber=e.rect.width,this.inputCustomHeight.valueAsNumber=e.rect.height,this.setLabel(this.defaultLabel)}},modifiers:[interact.modifiers.restrictSize({min:{width:this.minimalWidth,height:this.minimalHeight}})]})}calculateContainerMaxHeight(){this.resizableContainer.hidden=!0;const e=getComputedStyle(document.querySelector(Selectors.moduleBodySelector)),t=parseFloat(e.getPropertyValue("padding-top"))+parseFloat(e.getPropertyValue("padding-bottom")),i=document.body.getBoundingClientRect().height,r=document.querySelector(Selectors.topbarContainerSelector).getBoundingClientRect().height;return this.resizableContainer.hidden=!1,i-t-r-8}calculateContainerMaxWidth(){this.resizableContainer.hidden=!0;const e=getComputedStyle(document.querySelector(Selectors.moduleBodySelector)),t=parseFloat(e.getPropertyValue("padding-left"))+parseFloat(e.getPropertyValue("padding-right")),i=document.body.getBoundingClientRect().width;return this.resizableContainer.hidden=!1,i-t}}export default new ViewPage; \ No newline at end of file +import interact from"interactjs";import DocumentService from"@typo3/core/document-service.js";import PersistentStorage from"@typo3/backend/storage/persistent.js";import RegularEvent from"@typo3/core/event/regular-event.js";import DebounceEvent from"@typo3/core/event/debounce-event.js";var Selectors;!function(e){e.resizableContainerIdentifier=".t3js-viewpage-resizeable",e.moduleBodySelector=".t3js-module-body",e.customSelector=".t3js-preset-custom",e.customWidthSelector=".t3js-preset-custom-width",e.customHeightSelector=".t3js-preset-custom-height",e.changeOrientationSelector=".t3js-change-orientation",e.changePresetSelector=".t3js-change-preset",e.inputWidthSelector=".t3js-viewpage-input-width",e.inputHeightSelector=".t3js-viewpage-input-height",e.currentLabelSelector=".t3js-viewpage-current-label",e.topbarContainerSelector=".t3js-viewpage-topbar",e.refreshSelector=".t3js-viewpage-refresh"}(Selectors||(Selectors={}));class ViewPage{constructor(){this.defaultLabel="",this.minimalHeight=300,this.minimalWidth=300,this.storagePrefix="moduleData.web_ViewpageView.States.",DocumentService.ready().then((()=>{const e=document.querySelector(".t3js-preset-custom-label");this.defaultLabel=e?.textContent.trim()??"",this.iframe=document.getElementById("tx_this_iframe"),this.inputCustomWidth=document.querySelector(Selectors.inputWidthSelector),this.inputCustomHeight=document.querySelector(Selectors.inputHeightSelector),this.customPresetItem=document.querySelector(Selectors.customSelector),this.customPresetItemWidth=document.querySelector(Selectors.customWidthSelector),this.customPresetItemHeight=document.querySelector(Selectors.customHeightSelector),this.currentLabelElement=document.querySelector(Selectors.currentLabelSelector),this.resizableContainer=document.querySelector(Selectors.resizableContainerIdentifier),this.initialize()}))}getCurrentWidth(){return this.inputCustomWidth.valueAsNumber}getCurrentHeight(){return this.inputCustomHeight.valueAsNumber}setLabel(e){this.currentLabelElement.textContent=e}getCurrentLabel(){return this.currentLabelElement.textContent}persistChanges(e,t){PersistentStorage.set(e,t)}setSize(e,t){isNaN(t)&&(t=this.calculateContainerMaxHeight()),t<this.minimalHeight&&(t=this.minimalHeight),isNaN(e)&&(e=this.calculateContainerMaxWidth()),e<this.minimalWidth&&(e=this.minimalWidth),this.inputCustomWidth.valueAsNumber=e,this.inputCustomHeight.valueAsNumber=t,this.resizableContainer.style.width=`${e}px`,this.resizableContainer.style.height=`${t}px`,this.resizableContainer.style.left="0"}persistCurrentPreset(){const e={width:this.getCurrentWidth(),height:this.getCurrentHeight(),label:this.getCurrentLabel()};this.persistChanges(this.storagePrefix+"current",e)}persistCustomPreset(){const e={width:this.getCurrentWidth(),height:this.getCurrentHeight()};this.customPresetItem.dataset.width=e.width.toString(10),this.customPresetItem.dataset.height=e.height.toString(10),this.customPresetItemWidth.textContent=e.width.toString(10),this.customPresetItemHeight.textContent=e.height.toString(10),this.persistChanges(this.storagePrefix+"current",e),this.persistChanges(this.storagePrefix+"custom",e)}persistCustomPresetAfterChange(){clearTimeout(this.queueDelayTimer),this.queueDelayTimer=window.setTimeout((()=>{this.persistCustomPreset()}),1e3)}initialize(){new RegularEvent("click",(()=>{this.setSize(this.getCurrentHeight(),this.getCurrentWidth()),this.persistCurrentPreset()})).bindTo(document.querySelector(Selectors.changeOrientationSelector)),[this.inputCustomWidth,this.inputCustomHeight].forEach((e=>{new DebounceEvent("change",(()=>{this.setSize(this.getCurrentWidth(),this.getCurrentHeight()),this.setLabel(this.defaultLabel),this.persistCustomPresetAfterChange()}),50).bindTo(e)})),new RegularEvent("click",((e,t)=>{this.setSize(parseInt(t.dataset.width,10),parseInt(t.dataset.height,10)),this.setLabel(t.dataset.label),this.persistCurrentPreset()})).delegateTo(document,Selectors.changePresetSelector),new RegularEvent("click",(()=>{this.iframe.contentWindow.location.reload()})).bindTo(document.querySelector(Selectors.refreshSelector)),interact(this.resizableContainer).on("resizestart",(e=>{const t=document.createElement("div");t.id="viewpage-iframe-cover",t.setAttribute("style","z-index:99;position:absolute;width:100%;top:0;left:0;height:100%;"),e.target.appendChild(t)})).on("resizeend",(()=>{document.getElementById("viewpage-iframe-cover").remove(),this.persistCustomPreset()})).resizable({origin:"self",edges:{top:!1,left:!0,bottom:!0,right:!0},listeners:{move:e=>{Object.assign(e.target.style,{width:`${e.rect.width}px`,height:`${e.rect.height}px`}),this.inputCustomWidth.valueAsNumber=e.rect.width,this.inputCustomHeight.valueAsNumber=e.rect.height,this.setLabel(this.defaultLabel)}},modifiers:[interact.modifiers.restrictSize({min:{width:this.minimalWidth,height:this.minimalHeight}})]})}calculateContainerMaxHeight(){this.resizableContainer.hidden=!0;const e=getComputedStyle(document.querySelector(Selectors.moduleBodySelector)),t=parseFloat(e.getPropertyValue("padding-top"))+parseFloat(e.getPropertyValue("padding-bottom")),i=document.body.getBoundingClientRect().height,r=document.querySelector(Selectors.topbarContainerSelector).getBoundingClientRect().height;return this.resizableContainer.hidden=!1,i-t-r-8}calculateContainerMaxWidth(){this.resizableContainer.hidden=!0;const e=getComputedStyle(document.querySelector(Selectors.moduleBodySelector)),t=parseFloat(e.getPropertyValue("padding-left"))+parseFloat(e.getPropertyValue("padding-right")),i=document.body.getBoundingClientRect().width;return this.resizableContainer.hidden=!1,i-t}}export default new ViewPage; \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/backend.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/backend.js index 6a627e8e886b..0b3f48ec4796 100644 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/backend.js +++ b/typo3/sysext/workspaces/Resources/Public/JavaScript/backend.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import"@typo3/backend/element/icon-element.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import"@typo3/backend/input/clearable.js";import Workspaces from"@typo3/workspaces/workspaces.js";import{default as Modal}from"@typo3/backend/modal.js";import Persistent from"@typo3/backend/storage/persistent.js";import Utility from"@typo3/backend/utility.js";import Wizard from"@typo3/backend/wizard.js";import SecurityUtility from"@typo3/core/security-utility.js";import windowManager from"@typo3/backend/window-manager.js";import RegularEvent from"@typo3/core/event/regular-event.js";var Identifiers;!function(e){e.searchForm="#workspace-settings-form",e.searchTextField='#workspace-settings-form input[name="search-text"]',e.searchSubmitBtn='#workspace-settings-form button[type="submit"]',e.depthSelector='#workspace-settings-form [name="depth"]',e.languageSelector='#workspace-settings-form select[name="languages"]',e.stagesSelector='#workspace-settings-form select[name="stages"]',e.workspaceActions=".workspace-actions",e.chooseStageAction='.workspace-actions [name="stage-action"]',e.chooseSelectionAction='.workspace-actions [name="selection-action"]',e.chooseMassAction='.workspace-actions [name="mass-action"]',e.container="#workspace-panel",e.contentsContainer="#workspace-contents",e.noContentsContainer="#workspace-contents-empty",e.previewLinksButton=".t3js-preview-link",e.pagination="#workspace-pagination"}(Identifiers||(Identifiers={}));class Backend extends Workspaces{constructor(){super(),this.elements={},this.settings={dir:"ASC",id:TYPO3.settings.Workspaces.id,depth:1,language:"all",limit:30,query:"",sort:"label_Live",start:0,filterTxt:""},this.paging={currentPage:1,totalPages:1,totalItems:0},this.latestPath="",this.markedRecordsForMassAction=[],this.indentationPadding=26,this.handleCheckboxStateChanged=e=>{const t=$(e.target),a=t.parents("tr"),s=t.prop("checked"),n=a.data("table")+":"+a.data("uid")+":"+a.data("t3ver_oid");if(s)this.markedRecordsForMassAction.push(n);else{const e=this.markedRecordsForMassAction.indexOf(n);e>-1&&this.markedRecordsForMassAction.splice(e,1)}a.data("collectionCurrent")?Backend.changeCollectionChildrenState(a.data("collectionCurrent"),s):a.data("collection")&&(Backend.changeCollectionChildrenState(a.data("collection"),s),Backend.changeCollectionParentState(a.data("collection"),s)),this.elements.$chooseMassAction.prop("disabled",this.markedRecordsForMassAction.length>0)},this.viewChanges=e=>{e.preventDefault();const t=$(e.currentTarget).closest("tr");this.sendRemoteRequest(this.generateRemotePayload("getRowDetails",{stage:t.data("stage"),t3ver_oid:t.data("t3ver_oid"),table:t.data("table"),uid:t.data("uid"),filterFields:!0})).then((async e=>{const a=(await e.resolve())[0].result.data[0],s=$("<div />"),n=$("<ul />",{class:"nav nav-tabs",role:"tablist"}),i=$("<div />",{class:"tab-content"}),o=[];s.append($("<p />").html(TYPO3.lang.path.replace("{0}",a.path_Live)),$("<p />").html(TYPO3.lang.current_step.replace("{0}",a.label_Stage).replace("{1}",a.stage_position).replace("{2}",a.stage_count))),a.diff.length>0&&(n.append($("<li />",{role:"presentation",class:"nav-item"}).append($("<a />",{class:"nav-link",href:"#workspace-changes","aria-controls":"workspace-changes",role:"tab","data-bs-toggle":"tab"}).text(TYPO3.lang["window.recordChanges.tabs.changeSummary"]))),i.append($("<div />",{role:"tabpanel",class:"tab-pane",id:"workspace-changes"}).append($("<div />",{class:"form-section"}).append(Backend.generateDiffView(a.diff))))),a.comments.length>0&&(n.append($("<li />",{role:"presentation",class:"nav-item"}).append($("<a />",{class:"nav-link",href:"#workspace-comments","aria-controls":"workspace-comments",role:"tab","data-bs-toggle":"tab"}).html(TYPO3.lang["window.recordChanges.tabs.comments"]+" ").append($("<span />",{class:"badge"}).text(a.comments.length)))),i.append($("<div />",{role:"tabpanel",class:"tab-pane",id:"workspace-comments"}).append($("<div />",{class:"form-section"}).append(Backend.generateCommentView(a.comments))))),a.history.total>0&&(n.append($("<li />",{role:"presentation",class:"nav-item"}).append($("<a />",{class:"nav-link",href:"#workspace-history","aria-controls":"workspace-history",role:"tab","data-bs-toggle":"tab"}).text(TYPO3.lang["window.recordChanges.tabs.history"]))),i.append($("<div />",{role:"tabpanel",class:"tab-pane",id:"workspace-history"}).append($("<div />",{class:"form-section"}).append(Backend.generateHistoryView(a.history.data))))),n.find("li > a").first().addClass("active"),i.find(".tab-pane").first().addClass("active"),s.append($("<div />").append(n,i)),!1!==a.label_PrevStage&&t.data("stage")!==t.data("prevStage")&&o.push({text:a.label_PrevStage.title,active:!0,btnClass:"btn-default",name:"prevstage",trigger:(e,a)=>{a.hideModal(),this.sendToStage(t,"prev")}}),!1!==a.label_NextStage&&o.push({text:a.label_NextStage.title,active:!0,btnClass:"btn-default",name:"nextstage",trigger:(e,a)=>{a.hideModal(),this.sendToStage(t,"next")}}),o.push({text:TYPO3.lang.close,active:!0,btnClass:"btn-info",name:"cancel",trigger:(e,t)=>t.hideModal()}),Modal.advanced({type:Modal.types.default,title:TYPO3.lang["window.recordInformation"].replace("{0}",t.find(".t3js-title-live").text().trim()),content:s,severity:SeverityEnum.info,buttons:o,size:Modal.sizes.medium})}))},this.confirmDeleteRecordFromWorkspace=e=>{const t=$(e.target).closest("tr"),a=Modal.confirm(TYPO3.lang["window.discard.title"],TYPO3.lang["window.discard.message"],SeverityEnum.warning,[{text:TYPO3.lang.cancel,active:!0,btnClass:"btn-default",name:"cancel",trigger:()=>{a.hideModal()}},{text:TYPO3.lang.ok,btnClass:"btn-warning",name:"ok"}]);a.addEventListener("button.clicked",(e=>{"ok"===e.target.name&&this.sendRemoteRequest([this.generateRemoteActionsPayload("deleteSingleRecord",[t.data("table"),t.data("uid")])]).then((()=>{a.hideModal(),this.getWorkspaceInfos(),Backend.refreshPageTree()}))}))},this.runSelectionAction=e=>{const t=$(e.currentTarget).val(),a="discard"!==t;if(0===t.length)return;const s=[];for(let e=0;e<this.markedRecordsForMassAction.length;++e){const t=this.markedRecordsForMassAction[e].split(":");s.push({table:t[0],liveId:t[2],versionId:t[1]})}a?this.checkIntegrity({selection:s,type:"selection"}).then((async e=>{Wizard.setForceSelection(!1),"warning"===(await e.resolve())[0].result.result&&this.addIntegrityCheckWarningToWizard(),this.renderSelectionActionWizard(t,s)})):(Wizard.setForceSelection(!1),this.renderSelectionActionWizard(t,s))},this.addIntegrityCheckWarningToWizard=()=>{Wizard.addSlide("integrity-warning","Warning",TYPO3.lang["integrity.hasIssuesDescription"]+"<br>"+TYPO3.lang["integrity.hasIssuesQuestion"],SeverityEnum.warning)},this.runMassAction=e=>{const t=$(e.currentTarget).val(),a="discard"!==t;0!==t.length&&(a?this.checkIntegrity({language:this.settings.language,type:t}).then((async e=>{Wizard.setForceSelection(!1),"warning"===(await e.resolve())[0].result.result&&this.addIntegrityCheckWarningToWizard(),this.renderMassActionWizard(t)})):(Wizard.setForceSelection(!1),this.renderMassActionWizard(t)))},this.sendToSpecificStageAction=e=>{const t=[],a=$(e.currentTarget).val();for(let e=0;e<this.markedRecordsForMassAction.length;++e){const a=this.markedRecordsForMassAction[e].split(":");t.push({table:a[0],uid:a[1],t3ver_oid:a[2]})}this.sendRemoteRequest(this.generateRemoteActionsPayload("sendToSpecificStageWindow",[a,t])).then((async e=>{const s=this.renderSendToStageWindow(await e.resolve());s.addEventListener("button.clicked",(e=>{if("ok"===e.target.name){const e=Utility.convertFormToObject(s.querySelector("form"));e.affects={elements:t,nextStage:a},this.sendRemoteRequest([this.generateRemoteActionsPayload("sendToSpecificStageExecute",[e]),this.generateRemotePayload("getWorkspaceInfos",this.settings)]).then((async e=>{const t=await e.resolve();s.hideModal(),this.renderWorkspaceInfos(t[1].result),Backend.refreshPageTree()}))}})),s.addEventListener("typo3-modal-hide",(()=>{this.elements.$chooseStageAction.val("")}))}))},this.generatePreviewLinks=()=>{this.sendRemoteRequest(this.generateRemoteActionsPayload("generateWorkspacePreviewLinksForAllLanguages",[this.settings.id])).then((async e=>{const t=(await e.resolve())[0].result,a=$("<dl />");for(let[e,s]of Object.entries(t))a.append($("<dt />").text(e),$("<dd />").append($("<a />",{href:s,target:"_blank"}).text(s)));Modal.show(TYPO3.lang.previewLink,a,SeverityEnum.info,[{text:TYPO3.lang.ok,active:!0,btnClass:"btn-info",name:"ok",trigger:(e,t)=>t.hideModal()}],["modal-inner-scroll"])}))},DocumentService.ready().then((()=>{this.getElements(),this.registerEvents(),this.notifyWorkspaceSwitchAction(),this.settings.depth=this.elements.$depthSelector.val(),this.settings.language=this.elements.$languageSelector.val(),this.settings.stage=this.elements.$stagesSelector.val(),this.elements.$container.length&&this.getWorkspaceInfos()}))}static refreshPageTree(){top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))}static generateDiffView(e){const t=$("<div />",{class:"diff"});for(let a of e)t.append($("<div />",{class:"diff-item"}).append($("<div />",{class:"diff-item-title"}).text(a.label),$("<div />",{class:"diff-item-result diff-item-result-inline"}).html(a.content)));return t}static generateCommentView(e){const t=$("<div />");for(let a of e){const e=$("<div />",{class:"panel panel-default"});a.user_comment.length>0&&e.append($("<div />",{class:"panel-body"}).html(a.user_comment)),e.append($("<div />",{class:"panel-footer"}).append($("<span />",{class:"badge badge-success me-2"}).text(a.previous_stage_title+" > "+a.stage_title),$("<span />",{class:"badge badge-info"}).text(a.tstamp))),t.append($("<div />",{class:"media"}).append($("<div />",{class:"media-left text-center"}).text(a.user_username).prepend($("<div />").html(a.user_avatar)),$("<div />",{class:"media-body"}).append(e)))}return t}static generateHistoryView(e){const t=$("<div />");for(let a of e){const e=$("<div />",{class:"panel panel-default"});let s;if("object"==typeof a.differences){if(0===a.differences.length)continue;s=$("<div />",{class:"diff"});for(let e=0;e<a.differences.length;++e)s.append($("<div />",{class:"diff-item"}).append($("<div />",{class:"diff-item-title"}).text(a.differences[e].label),$("<div />",{class:"diff-item-result diff-item-result-inline"}).html(a.differences[e].html)));e.append($("<div />").append(s))}else e.append($("<div />",{class:"panel-body"}).text(a.differences));e.append($("<div />",{class:"panel-footer"}).append($("<span />",{class:"badge badge-info"}).text(a.datetime))),t.append($("<div />",{class:"media"}).append($("<div />",{class:"media-left text-center"}).text(a.user).prepend($("<div />").html(a.user_avatar)),$("<div />",{class:"media-body"}).append(e)))}return t}static changeCollectionParentState(e,t){const a=document.querySelector('tr[data-collection-current="'+e+'"] input[type=checkbox]');null!==a&&a.checked!==t&&(a.checked=t,a.dataset.manuallyChanged="true",a.dispatchEvent(new CustomEvent("multiRecordSelection:checkbox:state:changed",{bubbles:!0,cancelable:!1})))}static changeCollectionChildrenState(e,t){const a=document.querySelectorAll('tr[data-collection="'+e+'"] input[type=checkbox]');a.length&&a.forEach((e=>{e.checked!==t&&(e.checked=t,e.dataset.manuallyChanged="true",e.dispatchEvent(new CustomEvent("multiRecordSelection:checkbox:state:changed",{bubbles:!0,cancelable:!1})))}))}notifyWorkspaceSwitchAction(){const e=document.querySelector("main[data-workspace-switch-action]");if(e.dataset.workspaceSwitchAction){const t=JSON.parse(e.dataset.workspaceSwitchAction);top.TYPO3.WorkspacesMenu.performWorkspaceSwitch(t.id,t.title),top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh")),top.TYPO3.ModuleMenu.App.refreshMenu()}}checkIntegrity(e){return this.sendRemoteRequest(this.generateRemotePayload("checkIntegrity",e))}getElements(){this.elements.$searchForm=$(Identifiers.searchForm),this.elements.$searchTextField=$(Identifiers.searchTextField),this.elements.$searchSubmitBtn=$(Identifiers.searchSubmitBtn),this.elements.$depthSelector=$(Identifiers.depthSelector),this.elements.$languageSelector=$(Identifiers.languageSelector),this.elements.$stagesSelector=$(Identifiers.stagesSelector),this.elements.$container=$(Identifiers.container),this.elements.$contentsContainer=$(Identifiers.contentsContainer),this.elements.$noContentsContainer=$(Identifiers.noContentsContainer),this.elements.$tableBody=this.elements.$contentsContainer.find("tbody"),this.elements.$workspaceActions=$(Identifiers.workspaceActions),this.elements.$chooseStageAction=$(Identifiers.chooseStageAction),this.elements.$chooseSelectionAction=$(Identifiers.chooseSelectionAction),this.elements.$chooseMassAction=$(Identifiers.chooseMassAction),this.elements.$previewLinksButton=$(Identifiers.previewLinksButton),this.elements.$pagination=$(Identifiers.pagination)}registerEvents(){$(document).on("click",'[data-action="publish"]',(e=>{const t=e.target.closest("tr");this.checkIntegrity({selection:[{liveId:t.dataset.uid,versionId:t.dataset.t3ver_oid,table:t.dataset.table}],type:"selection"}).then((async e=>{"warning"===(await e.resolve())[0].result.result&&this.addIntegrityCheckWarningToWizard(),Wizard.setForceSelection(!1),Wizard.addSlide("publish-confirm","Publish",TYPO3.lang["window.publish.message"],SeverityEnum.info),Wizard.addFinalProcessingSlide((()=>{this.sendRemoteRequest(this.generateRemoteActionsPayload("publishSingleRecord",[t.dataset.table,t.dataset.t3ver_oid,t.dataset.uid])).then((()=>{Wizard.dismiss(),this.getWorkspaceInfos(),Backend.refreshPageTree()}))})).then((()=>{Wizard.show()}))}))})).on("click",'[data-action="prevstage"]',(e=>{this.sendToStage($(e.currentTarget).closest("tr"),"prev")})).on("click",'[data-action="nextstage"]',(e=>{this.sendToStage($(e.currentTarget).closest("tr"),"next")})).on("click",'[data-action="changes"]',this.viewChanges).on("click",'[data-action="preview"]',this.openPreview.bind(this)).on("click",'[data-action="open"]',(e=>{const t=e.currentTarget.closest("tr");let a=TYPO3.settings.FormEngine.moduleUrl+"&returnUrl="+encodeURIComponent(document.location.href)+"&id="+TYPO3.settings.Workspaces.id+"&edit["+t.dataset.table+"]["+t.dataset.uid+"]=edit";window.location.href=a})).on("click",'[data-action="version"]',(e=>{const t=e.currentTarget.closest("tr"),a="pages"===t.dataset.table?t.dataset.t3ver_oid:t.dataset.pid;window.location.href=TYPO3.settings.WebLayout.moduleUrl+"&id="+a})).on("click",'[data-action="remove"]',this.confirmDeleteRecordFromWorkspace).on("click",'[data-action="expand"]',(e=>{const t=$(e.currentTarget);let a;a="true"===t.first().attr("aria-expanded")?"apps-pagetree-expand":"apps-pagetree-collapse",t.empty().append(this.getIcon(a))})),$(window.top.document).on("click",".t3js-workspace-recipients-selectall",(()=>{$(".t3js-workspace-recipient",window.top.document).not(":disabled").prop("checked",!0)})).on("click",".t3js-workspace-recipients-deselectall",(()=>{$(".t3js-workspace-recipient",window.top.document).not(":disabled").prop("checked",!1)})),this.elements.$searchForm.on("submit",(e=>{e.preventDefault(),this.settings.filterTxt=this.elements.$searchTextField.val(),this.getWorkspaceInfos()})),this.elements.$searchTextField.on("keyup",(e=>{""!==e.target.value?this.elements.$searchSubmitBtn.removeClass("disabled"):(this.elements.$searchSubmitBtn.addClass("disabled"),this.getWorkspaceInfos())}));const e=this.elements.$searchTextField.get(0);void 0!==e&&e.clearable({onClear:()=>{this.elements.$searchSubmitBtn.addClass("disabled"),this.settings.filterTxt="",this.getWorkspaceInfos()}}),new RegularEvent("multiRecordSelection:checkbox:state:changed",this.handleCheckboxStateChanged).bindTo(document),this.elements.$depthSelector.on("change",(e=>{const t=e.target.value;Persistent.set("moduleData.workspaces_admin.depth",t),this.settings.depth=t,this.getWorkspaceInfos()})),this.elements.$previewLinksButton.on("click",this.generatePreviewLinks),this.elements.$languageSelector.on("change",(e=>{const t=$(e.target);Persistent.set("moduleData.workspaces_admin.language",t.val()),this.settings.language=t.val(),this.sendRemoteRequest(this.generateRemotePayload("getWorkspaceInfos",this.settings)).then((async e=>{const a=await e.resolve();this.elements.$languageSelector.prev().html(t.find(":selected").data("icon")),this.renderWorkspaceInfos(a[0].result)}))})),this.elements.$stagesSelector.on("change",(e=>{const t=e.target.value;Persistent.set("moduleData.workspaces_admin.stage",t),this.settings.stage=t,this.getWorkspaceInfos()})),this.elements.$chooseStageAction.on("change",this.sendToSpecificStageAction),this.elements.$chooseSelectionAction.on("change",this.runSelectionAction),this.elements.$chooseMassAction.on("change",this.runMassAction),this.elements.$pagination.on("click","[data-action]",(e=>{e.preventDefault();const t=$(e.currentTarget);let a=!1;switch(t.data("action")){case"previous":this.paging.currentPage>1&&(this.paging.currentPage--,a=!0);break;case"next":this.paging.currentPage<this.paging.totalPages&&(this.paging.currentPage++,a=!0);break;case"page":this.paging.currentPage=parseInt(t.data("page"),10),a=!0;break;default:throw'Unknown action "'+t.data("action")+'"'}a&&(this.settings.start=parseInt(this.settings.limit.toString(),10)*(this.paging.currentPage-1),this.getWorkspaceInfos())}))}sendToStage(e,t){let a,s,n;if("next"===t)a=e.data("nextStage"),s="sendToNextStageWindow",n="sendToNextStageExecute";else{if("prev"!==t)throw"Invalid direction given.";a=e.data("prevStage"),s="sendToPrevStageWindow",n="sendToPrevStageExecute"}this.sendRemoteRequest(this.generateRemoteActionsPayload(s,[e.data("uid"),e.data("table"),e.data("t3ver_oid")])).then((async t=>{const s=this.renderSendToStageWindow(await t.resolve());s.addEventListener("button.clicked",(t=>{if("ok"===t.target.name){const t=Utility.convertFormToObject(s.querySelector("form"));t.affects={table:e.data("table"),nextStage:a,t3ver_oid:e.data("t3ver_oid"),uid:e.data("uid"),elements:[]},this.sendRemoteRequest([this.generateRemoteActionsPayload(n,[t]),this.generateRemotePayload("getWorkspaceInfos",this.settings)]).then((async e=>{const t=await e.resolve();s.hideModal(),this.renderWorkspaceInfos(t[1].result),Backend.refreshPageTree()}))}}))}))}getWorkspaceInfos(){this.sendRemoteRequest(this.generateRemotePayload("getWorkspaceInfos",this.settings)).then((async e=>{this.renderWorkspaceInfos((await e.resolve())[0].result)}))}renderWorkspaceInfos(e){this.elements.$tableBody.children().remove(),this.resetMassActionState(e.data.length),this.buildPagination(e.total),0===e.total?(this.elements.$contentsContainer.hide(),this.elements.$noContentsContainer.show()):(this.elements.$contentsContainer.show(),this.elements.$noContentsContainer.hide());for(let t=0;t<e.data.length;++t){const a=e.data[t],s=$("<div />",{class:"btn-group"});let n,i=a.Workspaces_CollectionChildren>0&&""!==a.Workspaces_CollectionCurrent;s.append(this.getAction(i,"expand",a.expanded?"apps-pagetree-expand":"apps-pagetree-collapse").attr("title",TYPO3.lang["tooltip.expand"]).attr("data-bs-target",'[data-collection="'+a.Workspaces_CollectionCurrent+'"]').attr("aria-expanded",!i||a.expanded?"true":"false").attr("data-bs-toggle","collapse"),this.getAction(a.hasChanges,"changes","actions-document-info").attr("title",TYPO3.lang["tooltip.showChanges"]),this.getAction(a.allowedAction_publish&&""===a.Workspaces_CollectionParent,"publish","actions-version-swap-version").attr("title",TYPO3.lang["tooltip.publish"]),this.getAction(a.allowedAction_view,"preview","actions-version-workspace-preview").attr("title",TYPO3.lang["tooltip.viewElementAction"]),this.getAction(a.allowedAction_edit,"open","actions-open").attr("title",TYPO3.lang["tooltip.editElementAction"]),this.getAction(a.allowedAction_versionPageOpen,"version","actions-version-page-open").attr("title",TYPO3.lang["tooltip.openPage"]),this.getAction(a.allowedAction_delete,"remove","actions-version-document-remove").attr("title",TYPO3.lang["tooltip.discardVersion"])),""!==a.integrity.messages&&(n=$("<span>"+this.getIcon(a.integrity.status)+"</span>"),n.attr("title",a.integrity.messages)),this.latestPath!==a.path_Workspace&&(this.latestPath=a.path_Workspace,this.elements.$tableBody.append($("<tr />").append($("<th />"),$("<th />",{colspan:6}).html('<span title="'+a.path_Workspace+'">'+a.path_Workspace_crop+"</span>"))));const o=$("<span />",{class:"form-check form-toggle"}).append($("<input />",{type:"checkbox",class:"form-check-input t3js-multi-record-selection-check"})),r={"data-uid":a.uid,"data-pid":a.livepid,"data-t3ver_oid":a.t3ver_oid,"data-t3ver_wsid":a.t3ver_wsid,"data-table":a.table,"data-next-stage":a.value_nextStage,"data-prev-stage":a.value_prevStage,"data-stage":a.stage,"data-multi-record-selection-element":"true"};if(""!==a.Workspaces_CollectionParent){let t=e.data.find((e=>e.Workspaces_CollectionCurrent===a.Workspaces_CollectionParent));r["data-collection"]=a.Workspaces_CollectionParent,r.class="collapse"+(t.expanded?" show":"")}else""!==a.Workspaces_CollectionCurrent&&(r["data-collection-current"]=a.Workspaces_CollectionCurrent);this.elements.$tableBody.append($("<tr />",r).append($("<td />").empty().append(o),$("<td />",{class:"t3js-title-workspace",style:a.Workspaces_CollectionLevel>0?"padding-left: "+this.indentationPadding*a.Workspaces_CollectionLevel+"px":""}).html('<span class="icon icon-size-small">'+this.getIcon(a.icon_Workspace)+'</span> <a href="#" data-action="changes"><span class="workspace-state-'+a.state_Workspace+'" title="'+a.label_Workspace+'">'+a.label_Workspace_crop+"</span></a>"),$("<td />",{class:"t3js-title-live"}).html('<span class="icon icon-size-small">'+this.getIcon(a.icon_Live)+'</span> <span class"workspace-live-title title="'+a.label_Live+'">'+a.label_Live_crop+"</span>"),$("<td />").text(a.label_Stage),$("<td />").empty().append(n),$("<td />").html(this.getIcon(a.language.icon)),$("<td />",{class:"text-end nowrap"}).append(s)))}}buildPagination(e){if(0===e)return void this.elements.$pagination.contents().remove();if(this.paging.totalItems=e,this.paging.totalPages=Math.ceil(e/parseInt(this.settings.limit.toString(),10)),1===this.paging.totalPages)return void this.elements.$pagination.contents().remove();const t=$("<ul />",{class:"pagination"}),a=[],s=$("<li />",{class:"page-item"}).append($("<button />",{class:"page-link",type:"button","data-action":"previous"}).append($("<typo3-backend-icon />",{identifier:"actions-arrow-left-alt",size:"small"}))),n=$("<li />",{class:"page-item"}).append($("<button />",{class:"page-link",type:"button","data-action":"next"}).append($("<typo3-backend-icon />",{identifier:"actions-arrow-right-alt",size:"small"})));1===this.paging.currentPage&&s.disablePagingAction(),this.paging.currentPage===this.paging.totalPages&&n.disablePagingAction();for(let e=1;e<=this.paging.totalPages;e++){const t=$("<li />",{class:"page-item"+(this.paging.currentPage===e?" active":"")});t.append($("<button />",{class:"page-link",type:"button","data-action":"page","data-page":e}).append($("<span />").text(e))),a.push(t)}t.append(s,a,n),this.elements.$pagination.empty().append(t)}openPreview(e){const t=$(e.currentTarget).closest("tr");this.sendRemoteRequest(this.generateRemoteActionsPayload("viewSingleRecord",[t.data("table"),t.data("uid")])).then((async e=>{const t=(await e.resolve())[0].result;windowManager.localOpen(t)}))}renderSelectionActionWizard(e,t){Wizard.addSlide("mass-action-confirmation",TYPO3.lang["window.selectionAction.title"],"<p>"+(new SecurityUtility).encodeHtml(TYPO3.lang["tooltip."+e+"Selected"])+"</p>",SeverityEnum.warning),Wizard.addFinalProcessingSlide((()=>{this.sendRemoteRequest(this.generateRemoteActionsPayload("executeSelectionAction",{action:e,selection:t})).then((()=>{this.markedRecordsForMassAction=[],this.getWorkspaceInfos(),Wizard.dismiss(),Backend.refreshPageTree()}))})).then((()=>{Wizard.show(),Wizard.getComponent().on("wizard-dismissed",(()=>{this.elements.$chooseSelectionAction.val("")}))}))}renderMassActionWizard(e){let t;switch(e){case"publish":t="publishWorkspace";break;case"discard":t="flushWorkspace";break;default:throw"Invalid mass action "+e+" called."}const a=new SecurityUtility;Wizard.setForceSelection(!1),Wizard.addSlide("mass-action-confirmation",TYPO3.lang["window.massAction.title"],"<p>"+a.encodeHtml(TYPO3.lang["tooltip."+e+"All"])+"<br><br>"+a.encodeHtml(TYPO3.lang["tooltip.affectWholeWorkspace"])+"</p>",SeverityEnum.warning);const s=async e=>{const a=(await e.resolve())[0].result;a.processed<a.total?this.sendRemoteRequest(this.generateRemoteMassActionsPayload(t,a)).then(s):(this.getWorkspaceInfos(),Wizard.dismiss())};Wizard.addFinalProcessingSlide((()=>{this.sendRemoteRequest(this.generateRemoteMassActionsPayload(t,{init:!0,total:0,processed:0,language:this.settings.language})).then(s)})).then((()=>{Wizard.show(),Wizard.getComponent().on("wizard-dismissed",(()=>{this.elements.$chooseMassAction.val("")}))}))}getAction(e,t,a){return e?$("<button />",{class:"btn btn-default","data-action":t}).append(this.getIcon(a)):$("<span />",{class:"btn btn-default disabled"}).append(this.getIcon("empty-empty"))}getIcon(e){switch(e){case"language":e="flags-multiple";break;case"integrity":case"info":e="status-dialog-information";break;case"success":e="status-dialog-ok";break;case"warning":e="status-dialog-warning";break;case"error":e="status-dialog-error"}return'<typo3-backend-icon identifier="'+e+'" size="small"></typo3-backend-icon>'}resetMassActionState(e){this.markedRecordsForMassAction=[],e&&(this.elements.$workspaceActions.removeClass("hidden"),this.elements.$chooseMassAction.prop("disabled",!1)),document.dispatchEvent(new CustomEvent("multiRecordSelection:actions:hide"))}}$.fn.disablePagingAction=function(){$(this).addClass("disabled").find("button").prop("disabled",!0)};export default new Backend; \ No newline at end of file +import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import"@typo3/backend/element/icon-element.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import"@typo3/backend/input/clearable.js";import Workspaces from"@typo3/workspaces/workspaces.js";import{default as Modal}from"@typo3/backend/modal.js";import Persistent from"@typo3/backend/storage/persistent.js";import Utility from"@typo3/backend/utility.js";import Wizard from"@typo3/backend/wizard.js";import SecurityUtility from"@typo3/core/security-utility.js";import windowManager from"@typo3/backend/window-manager.js";import RegularEvent from"@typo3/core/event/regular-event.js";var Identifiers;!function(e){e.searchForm="#workspace-settings-form",e.searchTextField='#workspace-settings-form input[name="search-text"]',e.searchSubmitBtn='#workspace-settings-form button[type="submit"]',e.depthSelector='#workspace-settings-form [name="depth"]',e.languageSelector='#workspace-settings-form select[name="languages"]',e.stagesSelector='#workspace-settings-form select[name="stages"]',e.workspaceActions=".workspace-actions",e.chooseStageAction='.workspace-actions [name="stage-action"]',e.chooseSelectionAction='.workspace-actions [name="selection-action"]',e.chooseMassAction='.workspace-actions [name="mass-action"]',e.container="#workspace-panel",e.contentsContainer="#workspace-contents",e.noContentsContainer="#workspace-contents-empty",e.previewLinksButton=".t3js-preview-link",e.pagination="#workspace-pagination"}(Identifiers||(Identifiers={}));class Backend extends Workspaces{constructor(){super(),this.elements={},this.settings={dir:"ASC",id:TYPO3.settings.Workspaces.id,depth:1,language:"all",limit:30,query:"",sort:"label_Live",start:0,filterTxt:""},this.paging={currentPage:1,totalPages:1,totalItems:0},this.latestPath="",this.markedRecordsForMassAction=[],this.indentationPadding=26,this.handleCheckboxStateChanged=e=>{const t=$(e.target),a=t.parents("tr"),s=t.prop("checked"),n=a.data("table")+":"+a.data("uid")+":"+a.data("t3ver_oid");if(s)this.markedRecordsForMassAction.push(n);else{const e=this.markedRecordsForMassAction.indexOf(n);e>-1&&this.markedRecordsForMassAction.splice(e,1)}a.data("collectionCurrent")?Backend.changeCollectionChildrenState(a.data("collectionCurrent"),s):a.data("collection")&&(Backend.changeCollectionChildrenState(a.data("collection"),s),Backend.changeCollectionParentState(a.data("collection"),s)),this.elements.$chooseMassAction.prop("disabled",this.markedRecordsForMassAction.length>0)},this.viewChanges=e=>{e.preventDefault();const t=$(e.currentTarget).closest("tr");this.sendRemoteRequest(this.generateRemotePayload("getRowDetails",{stage:t.data("stage"),t3ver_oid:t.data("t3ver_oid"),table:t.data("table"),uid:t.data("uid"),filterFields:!0})).then((async e=>{const a=(await e.resolve())[0].result.data[0],s=$("<div />"),n=$("<ul />",{class:"nav nav-tabs",role:"tablist"}),i=$("<div />",{class:"tab-content"}),o=[];s.append($("<p />").html(TYPO3.lang.path.replace("{0}",a.path_Live)),$("<p />").html(TYPO3.lang.current_step.replace("{0}",a.label_Stage).replace("{1}",a.stage_position).replace("{2}",a.stage_count))),a.diff.length>0&&(n.append($("<li />",{role:"presentation",class:"nav-item"}).append($("<a />",{class:"nav-link",href:"#workspace-changes","aria-controls":"workspace-changes",role:"tab","data-bs-toggle":"tab"}).text(TYPO3.lang["window.recordChanges.tabs.changeSummary"]))),i.append($("<div />",{role:"tabpanel",class:"tab-pane",id:"workspace-changes"}).append($("<div />",{class:"form-section"}).append(Backend.generateDiffView(a.diff))))),a.comments.length>0&&(n.append($("<li />",{role:"presentation",class:"nav-item"}).append($("<a />",{class:"nav-link",href:"#workspace-comments","aria-controls":"workspace-comments",role:"tab","data-bs-toggle":"tab"}).html(TYPO3.lang["window.recordChanges.tabs.comments"]+" ").append($("<span />",{class:"badge"}).text(a.comments.length)))),i.append($("<div />",{role:"tabpanel",class:"tab-pane",id:"workspace-comments"}).append($("<div />",{class:"form-section"}).append(Backend.generateCommentView(a.comments))))),a.history.total>0&&(n.append($("<li />",{role:"presentation",class:"nav-item"}).append($("<a />",{class:"nav-link",href:"#workspace-history","aria-controls":"workspace-history",role:"tab","data-bs-toggle":"tab"}).text(TYPO3.lang["window.recordChanges.tabs.history"]))),i.append($("<div />",{role:"tabpanel",class:"tab-pane",id:"workspace-history"}).append($("<div />",{class:"form-section"}).append(Backend.generateHistoryView(a.history.data))))),n.find("li > a").first().addClass("active"),i.find(".tab-pane").first().addClass("active"),s.append($("<div />").append(n,i)),!1!==a.label_PrevStage&&t.data("stage")!==t.data("prevStage")&&o.push({text:a.label_PrevStage.title,active:!0,btnClass:"btn-default",name:"prevstage",trigger:(e,a)=>{a.hideModal(),this.sendToStage(t,"prev")}}),!1!==a.label_NextStage&&o.push({text:a.label_NextStage.title,active:!0,btnClass:"btn-default",name:"nextstage",trigger:(e,a)=>{a.hideModal(),this.sendToStage(t,"next")}}),o.push({text:TYPO3.lang.close,active:!0,btnClass:"btn-info",name:"cancel",trigger:(e,t)=>t.hideModal()}),Modal.advanced({type:Modal.types.default,title:TYPO3.lang["window.recordInformation"].replace("{0}",t.find(".t3js-title-live").text().trim()),content:s,severity:SeverityEnum.info,buttons:o,size:Modal.sizes.medium})}))},this.confirmDeleteRecordFromWorkspace=e=>{const t=$(e.target).closest("tr"),a=Modal.confirm(TYPO3.lang["window.discard.title"],TYPO3.lang["window.discard.message"],SeverityEnum.warning,[{text:TYPO3.lang.cancel,active:!0,btnClass:"btn-default",name:"cancel",trigger:()=>{a.hideModal()}},{text:TYPO3.lang.ok,btnClass:"btn-warning",name:"ok"}]);a.addEventListener("button.clicked",(e=>{"ok"===e.target.name&&this.sendRemoteRequest([this.generateRemoteActionsPayload("deleteSingleRecord",[t.data("table"),t.data("uid")])]).then((()=>{a.hideModal(),this.getWorkspaceInfos(),Backend.refreshPageTree()}))}))},this.runSelectionAction=e=>{const t=$(e.currentTarget).val(),a="discard"!==t;if(0===t.length)return;const s=[];for(let e=0;e<this.markedRecordsForMassAction.length;++e){const t=this.markedRecordsForMassAction[e].split(":");s.push({table:t[0],liveId:t[2],versionId:t[1]})}a?this.checkIntegrity({selection:s,type:"selection"}).then((async e=>{Wizard.setForceSelection(!1),"warning"===(await e.resolve())[0].result.result&&this.addIntegrityCheckWarningToWizard(),this.renderSelectionActionWizard(t,s)})):(Wizard.setForceSelection(!1),this.renderSelectionActionWizard(t,s))},this.addIntegrityCheckWarningToWizard=()=>{Wizard.addSlide("integrity-warning","Warning",TYPO3.lang["integrity.hasIssuesDescription"]+"<br>"+TYPO3.lang["integrity.hasIssuesQuestion"],SeverityEnum.warning)},this.runMassAction=e=>{const t=$(e.currentTarget).val(),a="discard"!==t;0!==t.length&&(a?this.checkIntegrity({language:this.settings.language,type:t}).then((async e=>{Wizard.setForceSelection(!1),"warning"===(await e.resolve())[0].result.result&&this.addIntegrityCheckWarningToWizard(),this.renderMassActionWizard(t)})):(Wizard.setForceSelection(!1),this.renderMassActionWizard(t)))},this.sendToSpecificStageAction=e=>{const t=[],a=$(e.currentTarget).val();for(let e=0;e<this.markedRecordsForMassAction.length;++e){const a=this.markedRecordsForMassAction[e].split(":");t.push({table:a[0],uid:a[1],t3ver_oid:a[2]})}this.sendRemoteRequest(this.generateRemoteActionsPayload("sendToSpecificStageWindow",[a,t])).then((async e=>{const s=this.renderSendToStageWindow(await e.resolve());s.addEventListener("button.clicked",(e=>{if("ok"===e.target.name){const e=Utility.convertFormToObject(s.querySelector("form"));e.affects={elements:t,nextStage:a},this.sendRemoteRequest([this.generateRemoteActionsPayload("sendToSpecificStageExecute",[e]),this.generateRemotePayload("getWorkspaceInfos",this.settings)]).then((async e=>{const t=await e.resolve();s.hideModal(),this.renderWorkspaceInfos(t[1].result),Backend.refreshPageTree()}))}})),s.addEventListener("typo3-modal-hide",(()=>{this.elements.$chooseStageAction.val("")}))}))},this.generatePreviewLinks=()=>{this.sendRemoteRequest(this.generateRemoteActionsPayload("generateWorkspacePreviewLinksForAllLanguages",[this.settings.id])).then((async e=>{const t=(await e.resolve())[0].result,a=$("<dl />");for(const[e,s]of Object.entries(t))a.append($("<dt />").text(e),$("<dd />").append($("<a />",{href:s,target:"_blank"}).text(s)));Modal.show(TYPO3.lang.previewLink,a,SeverityEnum.info,[{text:TYPO3.lang.ok,active:!0,btnClass:"btn-info",name:"ok",trigger:(e,t)=>t.hideModal()}],["modal-inner-scroll"])}))},DocumentService.ready().then((()=>{this.getElements(),this.registerEvents(),this.notifyWorkspaceSwitchAction(),this.settings.depth=this.elements.$depthSelector.val(),this.settings.language=this.elements.$languageSelector.val(),this.settings.stage=this.elements.$stagesSelector.val(),this.elements.$container.length&&this.getWorkspaceInfos()}))}static refreshPageTree(){top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))}static generateDiffView(e){const t=$("<div />",{class:"diff"});for(const a of e)t.append($("<div />",{class:"diff-item"}).append($("<div />",{class:"diff-item-title"}).text(a.label),$("<div />",{class:"diff-item-result diff-item-result-inline"}).html(a.content)));return t}static generateCommentView(e){const t=$("<div />");for(const a of e){const e=$("<div />",{class:"panel panel-default"});a.user_comment.length>0&&e.append($("<div />",{class:"panel-body"}).html(a.user_comment)),e.append($("<div />",{class:"panel-footer"}).append($("<span />",{class:"badge badge-success me-2"}).text(a.previous_stage_title+" > "+a.stage_title),$("<span />",{class:"badge badge-info"}).text(a.tstamp))),t.append($("<div />",{class:"media"}).append($("<div />",{class:"media-left text-center"}).text(a.user_username).prepend($("<div />").html(a.user_avatar)),$("<div />",{class:"media-body"}).append(e)))}return t}static generateHistoryView(e){const t=$("<div />");for(const a of e){const e=$("<div />",{class:"panel panel-default"});let s;if("object"==typeof a.differences){if(0===a.differences.length)continue;s=$("<div />",{class:"diff"});for(let e=0;e<a.differences.length;++e)s.append($("<div />",{class:"diff-item"}).append($("<div />",{class:"diff-item-title"}).text(a.differences[e].label),$("<div />",{class:"diff-item-result diff-item-result-inline"}).html(a.differences[e].html)));e.append($("<div />").append(s))}else e.append($("<div />",{class:"panel-body"}).text(a.differences));e.append($("<div />",{class:"panel-footer"}).append($("<span />",{class:"badge badge-info"}).text(a.datetime))),t.append($("<div />",{class:"media"}).append($("<div />",{class:"media-left text-center"}).text(a.user).prepend($("<div />").html(a.user_avatar)),$("<div />",{class:"media-body"}).append(e)))}return t}static changeCollectionParentState(e,t){const a=document.querySelector('tr[data-collection-current="'+e+'"] input[type=checkbox]');null!==a&&a.checked!==t&&(a.checked=t,a.dataset.manuallyChanged="true",a.dispatchEvent(new CustomEvent("multiRecordSelection:checkbox:state:changed",{bubbles:!0,cancelable:!1})))}static changeCollectionChildrenState(e,t){const a=document.querySelectorAll('tr[data-collection="'+e+'"] input[type=checkbox]');a.length&&a.forEach((e=>{e.checked!==t&&(e.checked=t,e.dataset.manuallyChanged="true",e.dispatchEvent(new CustomEvent("multiRecordSelection:checkbox:state:changed",{bubbles:!0,cancelable:!1})))}))}notifyWorkspaceSwitchAction(){const e=document.querySelector("main[data-workspace-switch-action]");if(e.dataset.workspaceSwitchAction){const t=JSON.parse(e.dataset.workspaceSwitchAction);top.TYPO3.WorkspacesMenu.performWorkspaceSwitch(t.id,t.title),top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh")),top.TYPO3.ModuleMenu.App.refreshMenu()}}checkIntegrity(e){return this.sendRemoteRequest(this.generateRemotePayload("checkIntegrity",e))}getElements(){this.elements.$searchForm=$(Identifiers.searchForm),this.elements.$searchTextField=$(Identifiers.searchTextField),this.elements.$searchSubmitBtn=$(Identifiers.searchSubmitBtn),this.elements.$depthSelector=$(Identifiers.depthSelector),this.elements.$languageSelector=$(Identifiers.languageSelector),this.elements.$stagesSelector=$(Identifiers.stagesSelector),this.elements.$container=$(Identifiers.container),this.elements.$contentsContainer=$(Identifiers.contentsContainer),this.elements.$noContentsContainer=$(Identifiers.noContentsContainer),this.elements.$tableBody=this.elements.$contentsContainer.find("tbody"),this.elements.$workspaceActions=$(Identifiers.workspaceActions),this.elements.$chooseStageAction=$(Identifiers.chooseStageAction),this.elements.$chooseSelectionAction=$(Identifiers.chooseSelectionAction),this.elements.$chooseMassAction=$(Identifiers.chooseMassAction),this.elements.$previewLinksButton=$(Identifiers.previewLinksButton),this.elements.$pagination=$(Identifiers.pagination)}registerEvents(){$(document).on("click",'[data-action="publish"]',(e=>{const t=e.target.closest("tr");this.checkIntegrity({selection:[{liveId:t.dataset.uid,versionId:t.dataset.t3ver_oid,table:t.dataset.table}],type:"selection"}).then((async e=>{"warning"===(await e.resolve())[0].result.result&&this.addIntegrityCheckWarningToWizard(),Wizard.setForceSelection(!1),Wizard.addSlide("publish-confirm","Publish",TYPO3.lang["window.publish.message"],SeverityEnum.info),Wizard.addFinalProcessingSlide((()=>{this.sendRemoteRequest(this.generateRemoteActionsPayload("publishSingleRecord",[t.dataset.table,t.dataset.t3ver_oid,t.dataset.uid])).then((()=>{Wizard.dismiss(),this.getWorkspaceInfos(),Backend.refreshPageTree()}))})).then((()=>{Wizard.show()}))}))})).on("click",'[data-action="prevstage"]',(e=>{this.sendToStage($(e.currentTarget).closest("tr"),"prev")})).on("click",'[data-action="nextstage"]',(e=>{this.sendToStage($(e.currentTarget).closest("tr"),"next")})).on("click",'[data-action="changes"]',this.viewChanges).on("click",'[data-action="preview"]',this.openPreview.bind(this)).on("click",'[data-action="open"]',(e=>{const t=e.currentTarget.closest("tr"),a=TYPO3.settings.FormEngine.moduleUrl+"&returnUrl="+encodeURIComponent(document.location.href)+"&id="+TYPO3.settings.Workspaces.id+"&edit["+t.dataset.table+"]["+t.dataset.uid+"]=edit";window.location.href=a})).on("click",'[data-action="version"]',(e=>{const t=e.currentTarget.closest("tr"),a="pages"===t.dataset.table?t.dataset.t3ver_oid:t.dataset.pid;window.location.href=TYPO3.settings.WebLayout.moduleUrl+"&id="+a})).on("click",'[data-action="remove"]',this.confirmDeleteRecordFromWorkspace).on("click",'[data-action="expand"]',(e=>{const t=$(e.currentTarget);let a;a="true"===t.first().attr("aria-expanded")?"apps-pagetree-expand":"apps-pagetree-collapse",t.empty().append(this.getIcon(a))})),$(window.top.document).on("click",".t3js-workspace-recipients-selectall",(()=>{$(".t3js-workspace-recipient",window.top.document).not(":disabled").prop("checked",!0)})).on("click",".t3js-workspace-recipients-deselectall",(()=>{$(".t3js-workspace-recipient",window.top.document).not(":disabled").prop("checked",!1)})),this.elements.$searchForm.on("submit",(e=>{e.preventDefault(),this.settings.filterTxt=this.elements.$searchTextField.val(),this.getWorkspaceInfos()})),this.elements.$searchTextField.on("keyup",(e=>{""!==e.target.value?this.elements.$searchSubmitBtn.removeClass("disabled"):(this.elements.$searchSubmitBtn.addClass("disabled"),this.getWorkspaceInfos())}));const e=this.elements.$searchTextField.get(0);void 0!==e&&e.clearable({onClear:()=>{this.elements.$searchSubmitBtn.addClass("disabled"),this.settings.filterTxt="",this.getWorkspaceInfos()}}),new RegularEvent("multiRecordSelection:checkbox:state:changed",this.handleCheckboxStateChanged).bindTo(document),this.elements.$depthSelector.on("change",(e=>{const t=e.target.value;Persistent.set("moduleData.workspaces_admin.depth",t),this.settings.depth=t,this.getWorkspaceInfos()})),this.elements.$previewLinksButton.on("click",this.generatePreviewLinks),this.elements.$languageSelector.on("change",(e=>{const t=$(e.target);Persistent.set("moduleData.workspaces_admin.language",t.val()),this.settings.language=t.val(),this.sendRemoteRequest(this.generateRemotePayload("getWorkspaceInfos",this.settings)).then((async e=>{const a=await e.resolve();this.elements.$languageSelector.prev().html(t.find(":selected").data("icon")),this.renderWorkspaceInfos(a[0].result)}))})),this.elements.$stagesSelector.on("change",(e=>{const t=e.target.value;Persistent.set("moduleData.workspaces_admin.stage",t),this.settings.stage=t,this.getWorkspaceInfos()})),this.elements.$chooseStageAction.on("change",this.sendToSpecificStageAction),this.elements.$chooseSelectionAction.on("change",this.runSelectionAction),this.elements.$chooseMassAction.on("change",this.runMassAction),this.elements.$pagination.on("click","[data-action]",(e=>{e.preventDefault();const t=$(e.currentTarget);let a=!1;switch(t.data("action")){case"previous":this.paging.currentPage>1&&(this.paging.currentPage--,a=!0);break;case"next":this.paging.currentPage<this.paging.totalPages&&(this.paging.currentPage++,a=!0);break;case"page":this.paging.currentPage=parseInt(t.data("page"),10),a=!0;break;default:throw'Unknown action "'+t.data("action")+'"'}a&&(this.settings.start=parseInt(this.settings.limit.toString(),10)*(this.paging.currentPage-1),this.getWorkspaceInfos())}))}sendToStage(e,t){let a,s,n;if("next"===t)a=e.data("nextStage"),s="sendToNextStageWindow",n="sendToNextStageExecute";else{if("prev"!==t)throw"Invalid direction given.";a=e.data("prevStage"),s="sendToPrevStageWindow",n="sendToPrevStageExecute"}this.sendRemoteRequest(this.generateRemoteActionsPayload(s,[e.data("uid"),e.data("table"),e.data("t3ver_oid")])).then((async t=>{const s=this.renderSendToStageWindow(await t.resolve());s.addEventListener("button.clicked",(t=>{if("ok"===t.target.name){const t=Utility.convertFormToObject(s.querySelector("form"));t.affects={table:e.data("table"),nextStage:a,t3ver_oid:e.data("t3ver_oid"),uid:e.data("uid"),elements:[]},this.sendRemoteRequest([this.generateRemoteActionsPayload(n,[t]),this.generateRemotePayload("getWorkspaceInfos",this.settings)]).then((async e=>{const t=await e.resolve();s.hideModal(),this.renderWorkspaceInfos(t[1].result),Backend.refreshPageTree()}))}}))}))}getWorkspaceInfos(){this.sendRemoteRequest(this.generateRemotePayload("getWorkspaceInfos",this.settings)).then((async e=>{this.renderWorkspaceInfos((await e.resolve())[0].result)}))}renderWorkspaceInfos(e){this.elements.$tableBody.children().remove(),this.resetMassActionState(e.data.length),this.buildPagination(e.total),0===e.total?(this.elements.$contentsContainer.hide(),this.elements.$noContentsContainer.show()):(this.elements.$contentsContainer.show(),this.elements.$noContentsContainer.hide());for(let t=0;t<e.data.length;++t){const a=e.data[t],s=$("<div />",{class:"btn-group"});let n;const i=a.Workspaces_CollectionChildren>0&&""!==a.Workspaces_CollectionCurrent;s.append(this.getAction(i,"expand",a.expanded?"apps-pagetree-expand":"apps-pagetree-collapse").attr("title",TYPO3.lang["tooltip.expand"]).attr("data-bs-target",'[data-collection="'+a.Workspaces_CollectionCurrent+'"]').attr("aria-expanded",!i||a.expanded?"true":"false").attr("data-bs-toggle","collapse"),this.getAction(a.hasChanges,"changes","actions-document-info").attr("title",TYPO3.lang["tooltip.showChanges"]),this.getAction(a.allowedAction_publish&&""===a.Workspaces_CollectionParent,"publish","actions-version-swap-version").attr("title",TYPO3.lang["tooltip.publish"]),this.getAction(a.allowedAction_view,"preview","actions-version-workspace-preview").attr("title",TYPO3.lang["tooltip.viewElementAction"]),this.getAction(a.allowedAction_edit,"open","actions-open").attr("title",TYPO3.lang["tooltip.editElementAction"]),this.getAction(a.allowedAction_versionPageOpen,"version","actions-version-page-open").attr("title",TYPO3.lang["tooltip.openPage"]),this.getAction(a.allowedAction_delete,"remove","actions-version-document-remove").attr("title",TYPO3.lang["tooltip.discardVersion"])),""!==a.integrity.messages&&(n=$("<span>"+this.getIcon(a.integrity.status)+"</span>"),n.attr("title",a.integrity.messages)),this.latestPath!==a.path_Workspace&&(this.latestPath=a.path_Workspace,this.elements.$tableBody.append($("<tr />").append($("<th />"),$("<th />",{colspan:6}).html('<span title="'+a.path_Workspace+'">'+a.path_Workspace_crop+"</span>"))));const o=$("<span />",{class:"form-check form-toggle"}).append($("<input />",{type:"checkbox",class:"form-check-input t3js-multi-record-selection-check"})),r={"data-uid":a.uid,"data-pid":a.livepid,"data-t3ver_oid":a.t3ver_oid,"data-t3ver_wsid":a.t3ver_wsid,"data-table":a.table,"data-next-stage":a.value_nextStage,"data-prev-stage":a.value_prevStage,"data-stage":a.stage,"data-multi-record-selection-element":"true"};if(""!==a.Workspaces_CollectionParent){const t=e.data.find((e=>e.Workspaces_CollectionCurrent===a.Workspaces_CollectionParent));r["data-collection"]=a.Workspaces_CollectionParent,r.class="collapse"+(t.expanded?" show":"")}else""!==a.Workspaces_CollectionCurrent&&(r["data-collection-current"]=a.Workspaces_CollectionCurrent);this.elements.$tableBody.append($("<tr />",r).append($("<td />").empty().append(o),$("<td />",{class:"t3js-title-workspace",style:a.Workspaces_CollectionLevel>0?"padding-left: "+this.indentationPadding*a.Workspaces_CollectionLevel+"px":""}).html('<span class="icon icon-size-small">'+this.getIcon(a.icon_Workspace)+'</span> <a href="#" data-action="changes"><span class="workspace-state-'+a.state_Workspace+'" title="'+a.label_Workspace+'">'+a.label_Workspace_crop+"</span></a>"),$("<td />",{class:"t3js-title-live"}).html('<span class="icon icon-size-small">'+this.getIcon(a.icon_Live)+'</span> <span class"workspace-live-title title="'+a.label_Live+'">'+a.label_Live_crop+"</span>"),$("<td />").text(a.label_Stage),$("<td />").empty().append(n),$("<td />").html(this.getIcon(a.language.icon)),$("<td />",{class:"text-end nowrap"}).append(s)))}}buildPagination(e){if(0===e)return void this.elements.$pagination.contents().remove();if(this.paging.totalItems=e,this.paging.totalPages=Math.ceil(e/parseInt(this.settings.limit.toString(),10)),1===this.paging.totalPages)return void this.elements.$pagination.contents().remove();const t=$("<ul />",{class:"pagination"}),a=[],s=$("<li />",{class:"page-item"}).append($("<button />",{class:"page-link",type:"button","data-action":"previous"}).append($("<typo3-backend-icon />",{identifier:"actions-arrow-left-alt",size:"small"}))),n=$("<li />",{class:"page-item"}).append($("<button />",{class:"page-link",type:"button","data-action":"next"}).append($("<typo3-backend-icon />",{identifier:"actions-arrow-right-alt",size:"small"})));1===this.paging.currentPage&&s.disablePagingAction(),this.paging.currentPage===this.paging.totalPages&&n.disablePagingAction();for(let e=1;e<=this.paging.totalPages;e++){const t=$("<li />",{class:"page-item"+(this.paging.currentPage===e?" active":"")});t.append($("<button />",{class:"page-link",type:"button","data-action":"page","data-page":e}).append($("<span />").text(e))),a.push(t)}t.append(s,a,n),this.elements.$pagination.empty().append(t)}openPreview(e){const t=$(e.currentTarget).closest("tr");this.sendRemoteRequest(this.generateRemoteActionsPayload("viewSingleRecord",[t.data("table"),t.data("uid")])).then((async e=>{const t=(await e.resolve())[0].result;windowManager.localOpen(t)}))}renderSelectionActionWizard(e,t){Wizard.addSlide("mass-action-confirmation",TYPO3.lang["window.selectionAction.title"],"<p>"+(new SecurityUtility).encodeHtml(TYPO3.lang["tooltip."+e+"Selected"])+"</p>",SeverityEnum.warning),Wizard.addFinalProcessingSlide((()=>{this.sendRemoteRequest(this.generateRemoteActionsPayload("executeSelectionAction",{action:e,selection:t})).then((()=>{this.markedRecordsForMassAction=[],this.getWorkspaceInfos(),Wizard.dismiss(),Backend.refreshPageTree()}))})).then((()=>{Wizard.show(),Wizard.getComponent().on("wizard-dismissed",(()=>{this.elements.$chooseSelectionAction.val("")}))}))}renderMassActionWizard(e){let t;switch(e){case"publish":t="publishWorkspace";break;case"discard":t="flushWorkspace";break;default:throw"Invalid mass action "+e+" called."}const a=new SecurityUtility;Wizard.setForceSelection(!1),Wizard.addSlide("mass-action-confirmation",TYPO3.lang["window.massAction.title"],"<p>"+a.encodeHtml(TYPO3.lang["tooltip."+e+"All"])+"<br><br>"+a.encodeHtml(TYPO3.lang["tooltip.affectWholeWorkspace"])+"</p>",SeverityEnum.warning);const s=async e=>{const a=(await e.resolve())[0].result;a.processed<a.total?this.sendRemoteRequest(this.generateRemoteMassActionsPayload(t,a)).then(s):(this.getWorkspaceInfos(),Wizard.dismiss())};Wizard.addFinalProcessingSlide((()=>{this.sendRemoteRequest(this.generateRemoteMassActionsPayload(t,{init:!0,total:0,processed:0,language:this.settings.language})).then(s)})).then((()=>{Wizard.show(),Wizard.getComponent().on("wizard-dismissed",(()=>{this.elements.$chooseMassAction.val("")}))}))}getAction(e,t,a){return e?$("<button />",{class:"btn btn-default","data-action":t}).append(this.getIcon(a)):$("<span />",{class:"btn btn-default disabled"}).append(this.getIcon("empty-empty"))}getIcon(e){switch(e){case"language":e="flags-multiple";break;case"integrity":case"info":e="status-dialog-information";break;case"success":e="status-dialog-ok";break;case"warning":e="status-dialog-warning";break;case"error":e="status-dialog-error"}return'<typo3-backend-icon identifier="'+e+'" size="small"></typo3-backend-icon>'}resetMassActionState(e){this.markedRecordsForMassAction=[],e&&(this.elements.$workspaceActions.removeClass("hidden"),this.elements.$chooseMassAction.prop("disabled",!1)),document.dispatchEvent(new CustomEvent("multiRecordSelection:actions:hide"))}}$.fn.disablePagingAction=function(){$(this).addClass("disabled").find("button").prop("disabled",!0)};export default new Backend; \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/toolbar/workspaces-menu.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/toolbar/workspaces-menu.js index ca39b067cae5..f5f21f124933 100644 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/toolbar/workspaces-menu.js +++ b/typo3/sysext/workspaces/Resources/Public/JavaScript/toolbar/workspaces-menu.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import ModuleMenu from"@typo3/backend/module-menu.js";import Viewport from"@typo3/backend/viewport.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";var Identifiers,Classes;!function(e){e.topbarHeaderSelector=".t3js-topbar-header",e.containerSelector="#typo3-cms-workspaces-backend-toolbaritems-workspaceselectortoolbaritem",e.activeMenuItemLinkSelector=".t3js-workspaces-switchlink.active",e.menuItemLinkSelector=".t3js-workspaces-switchlink",e.toolbarItemSelector=".dropdown-toggle",e.workspaceModuleLinkSelector=".t3js-workspaces-modulelink"}(Identifiers||(Identifiers={})),function(e){e.workspaceBodyClass="typo3-in-workspace",e.workspacesTitleInToolbarClass="toolbar-item-name"}(Classes||(Classes={}));class WorkspacesMenu{static refreshPageTree(){document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))}static getWorkspaceState(){const e=document.querySelector([Identifiers.containerSelector,Identifiers.activeMenuItemLinkSelector].join(" "));if(null===e)return null;const t=parseInt(e.dataset.workspaceid||"0",10);return{id:t,title:e.innerText.trim(),inWorkspace:0!==t}}static updateTopBar(e){const t=document.querySelector(Identifiers.containerSelector);if(t.querySelector(Identifiers.containerSelector+" ."+Classes.workspacesTitleInToolbarClass)?.remove(),e.inWorkspace&&e.title){const o=document.createElement("span");o.classList.add(Classes.workspacesTitleInToolbarClass),o.textContent=e.title,t.querySelector(Identifiers.toolbarItemSelector).append(o)}}static updateBackendContext(e=null){if(e??(e=WorkspacesMenu.getWorkspaceState()),null===e)return;document.querySelector(Identifiers.topbarHeaderSelector).classList.toggle(Classes.workspaceBodyClass,e.inWorkspace),e.inWorkspace&&!e.title&&(e.title=TYPO3.lang["Workspaces.workspaceTitle"]),WorkspacesMenu.updateTopBar(e)}constructor(){Viewport.Topbar.Toolbar.registerEvent((()=>{this.initializeEvents(),WorkspacesMenu.updateBackendContext()})),new RegularEvent("typo3:datahandler:process",(e=>{const t=e.detail.payload;"sys_workspace"===t.table&&"delete"===t.action&&!1===t.hasErrors&&Viewport.Topbar.refresh()})).bindTo(document)}performWorkspaceSwitch(e,t){const o=document.querySelector(Identifiers.containerSelector);o.querySelector(Identifiers.activeMenuItemLinkSelector).classList.remove("active"),o.querySelector(Identifiers.menuItemLinkSelector+'[data-workspaceid="'+e+'"]')?.classList.add("active"),WorkspacesMenu.updateBackendContext({id:e,title:t,inWorkspace:0!==e})}initializeEvents(){const e=document.querySelector(Identifiers.containerSelector);new RegularEvent("click",(e=>{e.preventDefault(),ModuleMenu.App.showModule(e.target.dataset.module)})).delegateTo(e,Identifiers.workspaceModuleLinkSelector),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.switchWorkspace(parseInt(t.dataset.workspaceid,10))})).delegateTo(e,Identifiers.menuItemLinkSelector)}switchWorkspace(e){new AjaxRequest(TYPO3.settings.ajaxUrls.workspace_switch).post({workspaceId:e,pageId:ModuleStateStorage.current("web").identifier}).then((async t=>{const o=await t.resolve();o.workspaceId||(o.workspaceId=0),this.performWorkspaceSwitch(o.workspaceId,o.title||"");const r=ModuleMenu.App.getCurrentModule();if(o.pageId){let e=TYPO3.Backend.ContentContainer.getUrl();e+=(e.includes("?")?"&":"?")+"id="+o.pageId,Viewport.ContentContainer.setUrl(e)}else"workspaces_admin"===r?ModuleMenu.App.showModule(r,"workspace="+e):r.startsWith("web_")?ModuleMenu.App.reloadFrames():o.pageModule&&ModuleMenu.App.showModule(o.pageModule);WorkspacesMenu.refreshPageTree(),ModuleMenu.App.refreshMenu()}))}}const workspacesMenu=new WorkspacesMenu;TYPO3.WorkspacesMenu=workspacesMenu;export default workspacesMenu; \ No newline at end of file +import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import ModuleMenu from"@typo3/backend/module-menu.js";import Viewport from"@typo3/backend/viewport.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{ModuleStateStorage}from"@typo3/backend/storage/module-state-storage.js";var Identifiers,Classes;!function(e){e.topbarHeaderSelector=".t3js-topbar-header",e.containerSelector="#typo3-cms-workspaces-backend-toolbaritems-workspaceselectortoolbaritem",e.activeMenuItemLinkSelector=".t3js-workspaces-switchlink.active",e.menuItemLinkSelector=".t3js-workspaces-switchlink",e.toolbarItemSelector=".dropdown-toggle",e.workspaceModuleLinkSelector=".t3js-workspaces-modulelink"}(Identifiers||(Identifiers={})),function(e){e.workspaceBodyClass="typo3-in-workspace",e.workspacesTitleInToolbarClass="toolbar-item-name"}(Classes||(Classes={}));class WorkspacesMenu{constructor(){Viewport.Topbar.Toolbar.registerEvent((()=>{this.initializeEvents(),WorkspacesMenu.updateBackendContext()})),new RegularEvent("typo3:datahandler:process",(e=>{const t=e.detail.payload;"sys_workspace"===t.table&&"delete"===t.action&&!1===t.hasErrors&&Viewport.Topbar.refresh()})).bindTo(document)}static refreshPageTree(){document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))}static getWorkspaceState(){const e=document.querySelector([Identifiers.containerSelector,Identifiers.activeMenuItemLinkSelector].join(" "));if(null===e)return null;const t=parseInt(e.dataset.workspaceid||"0",10);return{id:t,title:e.innerText.trim(),inWorkspace:0!==t}}static updateTopBar(e){const t=document.querySelector(Identifiers.containerSelector);if(t.querySelector(Identifiers.containerSelector+" ."+Classes.workspacesTitleInToolbarClass)?.remove(),e.inWorkspace&&e.title){const o=document.createElement("span");o.classList.add(Classes.workspacesTitleInToolbarClass),o.textContent=e.title,t.querySelector(Identifiers.toolbarItemSelector).append(o)}}static updateBackendContext(e=null){if(e??(e=WorkspacesMenu.getWorkspaceState()),null===e)return;document.querySelector(Identifiers.topbarHeaderSelector).classList.toggle(Classes.workspaceBodyClass,e.inWorkspace),e.inWorkspace&&!e.title&&(e.title=TYPO3.lang["Workspaces.workspaceTitle"]),WorkspacesMenu.updateTopBar(e)}performWorkspaceSwitch(e,t){const o=document.querySelector(Identifiers.containerSelector);o.querySelector(Identifiers.activeMenuItemLinkSelector).classList.remove("active"),o.querySelector(Identifiers.menuItemLinkSelector+'[data-workspaceid="'+e+'"]')?.classList.add("active"),WorkspacesMenu.updateBackendContext({id:e,title:t,inWorkspace:0!==e})}initializeEvents(){const e=document.querySelector(Identifiers.containerSelector);new RegularEvent("click",(e=>{e.preventDefault(),ModuleMenu.App.showModule(e.target.dataset.module)})).delegateTo(e,Identifiers.workspaceModuleLinkSelector),new RegularEvent("click",((e,t)=>{e.preventDefault(),this.switchWorkspace(parseInt(t.dataset.workspaceid,10))})).delegateTo(e,Identifiers.menuItemLinkSelector)}switchWorkspace(e){new AjaxRequest(TYPO3.settings.ajaxUrls.workspace_switch).post({workspaceId:e,pageId:ModuleStateStorage.current("web").identifier}).then((async t=>{const o=await t.resolve();o.workspaceId||(o.workspaceId=0),this.performWorkspaceSwitch(o.workspaceId,o.title||"");const r=ModuleMenu.App.getCurrentModule();if(o.pageId){let e=TYPO3.Backend.ContentContainer.getUrl();e+=(e.includes("?")?"&":"?")+"id="+o.pageId,Viewport.ContentContainer.setUrl(e)}else"workspaces_admin"===r?ModuleMenu.App.showModule(r,"workspace="+e):r.startsWith("web_")?ModuleMenu.App.reloadFrames():o.pageModule&&ModuleMenu.App.showModule(o.pageModule);WorkspacesMenu.refreshPageTree(),ModuleMenu.App.refreshMenu()}))}}const workspacesMenu=new WorkspacesMenu;TYPO3.WorkspacesMenu=workspacesMenu;export default workspacesMenu; \ No newline at end of file -- GitLab