typo3-typescript-sources 13.4.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/sync-and-publish.yml +265 -0
- package/.last-synced-tag +1 -0
- package/LICENSE.txt +339 -0
- package/README-TYPO3.md +166 -0
- package/README.md +28 -0
- package/adminpanel/admin-panel.ts +375 -0
- package/adminpanel/modules/cache.ts +27 -0
- package/adminpanel/modules/preview.ts +75 -0
- package/backend/action-button/abstract-action.ts +22 -0
- package/backend/action-button/deferred-action.ts +41 -0
- package/backend/action-button/immediate-action.ts +31 -0
- package/backend/action-dispatcher.ts +104 -0
- package/backend/ajax-data-handler/message-interface.ts +18 -0
- package/backend/ajax-data-handler/response-interface.ts +20 -0
- package/backend/ajax-data-handler.ts +214 -0
- package/backend/backend-exception.ts +22 -0
- package/backend/broadcast-message.ts +50 -0
- package/backend/broadcast-service.ts +52 -0
- package/backend/browse-database.ts +38 -0
- package/backend/clear-cache.ts +88 -0
- package/backend/clipboard-panel.ts +240 -0
- package/backend/close-current-window.ts +2 -0
- package/backend/code-editor/autocomplete/completion-result.ts +105 -0
- package/backend/code-editor/autocomplete/ts-code-completion.ts +142 -0
- package/backend/code-editor/autocomplete/ts-parser.ts +485 -0
- package/backend/code-editor/autocomplete/ts-ref.ts +164 -0
- package/backend/code-editor/element/code-mirror-element.ts +282 -0
- package/backend/code-editor/language/typoscript.ts +181 -0
- package/backend/code-editor/stream-parser/typoscript.ts +1600 -0
- package/backend/color-picker.ts +158 -0
- package/backend/color-scheme-switch.ts +179 -0
- package/backend/column-selector-button.ts +302 -0
- package/backend/context-help.ts +68 -0
- package/backend/context-menu-actions.ts +322 -0
- package/backend/context-menu.ts +751 -0
- package/backend/copy-to-clipboard.ts +79 -0
- package/backend/date-time-picker.ts +270 -0
- package/backend/document-save-actions.ts +159 -0
- package/backend/drag-tooltip.ts +285 -0
- package/backend/drag-uploader.ts +914 -0
- package/backend/element/alert-element.ts +116 -0
- package/backend/element/dispatch-modal-button.ts +50 -0
- package/backend/element/draggable-resizable-element.ts +249 -0
- package/backend/element/editable-page-title.ts +306 -0
- package/backend/element/icon-element.ts +84 -0
- package/backend/element/immediate-action-element.ts +89 -0
- package/backend/element/pagination.ts +59 -0
- package/backend/element/progress-bar-element.ts +172 -0
- package/backend/element/progress-tracker-element.ts +119 -0
- package/backend/element/pseudo-button.ts +58 -0
- package/backend/element/spinner-element.ts +51 -0
- package/backend/element/thumbnail-element.ts +80 -0
- package/backend/element-browser.ts +182 -0
- package/backend/enum/data-transfer-types.ts +21 -0
- package/backend/enum/icon-types.ts +31 -0
- package/backend/enum/key-types.ts +27 -0
- package/backend/enum/severity.ts +20 -0
- package/backend/enum/viewport/scaffold-identifier.ts +24 -0
- package/backend/event/client-request.ts +25 -0
- package/backend/event/consumable.ts +18 -0
- package/backend/event/consumer-scope.ts +54 -0
- package/backend/event/event-dispatcher.ts +26 -0
- package/backend/event/interaction-request-assignment.ts +24 -0
- package/backend/event/interaction-request-map.ts +75 -0
- package/backend/event/interaction-request.ts +48 -0
- package/backend/event/trigger-request.ts +56 -0
- package/backend/form/submit-interceptor.ts +66 -0
- package/backend/form-engine/container/files-control-container.ts +716 -0
- package/backend/form-engine/container/flex-form-container-container.ts +166 -0
- package/backend/form-engine/container/flex-form-section-container.ts +210 -0
- package/backend/form-engine/container/inline-control-container.ts +1230 -0
- package/backend/form-engine/container/site-language-container.ts +616 -0
- package/backend/form-engine/element/abstract-sortable-select-items.ts +186 -0
- package/backend/form-engine/element/category-element.ts +160 -0
- package/backend/form-engine/element/color-element.ts +81 -0
- package/backend/form-engine/element/datetime-element.ts +66 -0
- package/backend/form-engine/element/extra/char-counter.ts +112 -0
- package/backend/form-engine/element/extra/select-box-filter.ts +85 -0
- package/backend/form-engine/element/folder-element.ts +66 -0
- package/backend/form-engine/element/group-element.ts +47 -0
- package/backend/form-engine/element/json-element.ts +52 -0
- package/backend/form-engine/element/link-element.ts +121 -0
- package/backend/form-engine/element/mfa-info-element.ts +169 -0
- package/backend/form-engine/element/modifier/resizable.ts +27 -0
- package/backend/form-engine/element/modifier/tabbable.ts +29 -0
- package/backend/form-engine/element/online-media-form-element.ts +65 -0
- package/backend/form-engine/element/password-element.ts +71 -0
- package/backend/form-engine/element/select-multiple-side-by-side-element.ts +83 -0
- package/backend/form-engine/element/select-single-element.ts +82 -0
- package/backend/form-engine/element/select-tree-element.ts +148 -0
- package/backend/form-engine/element/select-tree-toolbar.ts +118 -0
- package/backend/form-engine/element/select-tree.ts +210 -0
- package/backend/form-engine/element/slug-element.ts +243 -0
- package/backend/form-engine/element/suggest/result-container.ts +118 -0
- package/backend/form-engine/element/suggest/result-item.ts +90 -0
- package/backend/form-engine/element/table-permission-element.ts +101 -0
- package/backend/form-engine/element/table-wizard-element.ts +427 -0
- package/backend/form-engine/element/text-element.ts +52 -0
- package/backend/form-engine/element/text-table-element.ts +31 -0
- package/backend/form-engine/field-control/add-record.ts +40 -0
- package/backend/form-engine/field-control/edit-popup.ts +64 -0
- package/backend/form-engine/field-control/insert-clipboard.ts +50 -0
- package/backend/form-engine/field-control/link-popup.ts +53 -0
- package/backend/form-engine/field-control/list-module.ts +40 -0
- package/backend/form-engine/field-control/password-generator.ts +102 -0
- package/backend/form-engine/field-control/reset-selection.ts +48 -0
- package/backend/form-engine/field-wizard/localization-state-selector.ts +67 -0
- package/backend/form-engine/field-wizard/value-picker.ts +123 -0
- package/backend/form-engine/field-wizard/value-slider.ts +86 -0
- package/backend/form-engine/inline-relation/ajax-dispatcher.ts +124 -0
- package/backend/form-engine/inline-relation/inline-response-interface.ts +41 -0
- package/backend/form-engine-link-browser-adapter.ts +129 -0
- package/backend/form-engine-review.ts +155 -0
- package/backend/form-engine-suggest.ts +133 -0
- package/backend/form-engine-validation.ts +686 -0
- package/backend/form-engine.ts +1391 -0
- package/backend/global-event-handler.ts +252 -0
- package/backend/grid-editor.ts +1077 -0
- package/backend/hashing/md5.ts +252 -0
- package/backend/helper.ts +24 -0
- package/backend/hotkeys/hotkey-storage.ts +63 -0
- package/backend/hotkeys.ts +216 -0
- package/backend/icons.ts +236 -0
- package/backend/image-manipulation.ts +885 -0
- package/backend/info-window.ts +46 -0
- package/backend/input/clearable.ts +128 -0
- package/backend/input/index.d.ts +8 -0
- package/backend/java-script-module-import-event-handler.ts +19 -0
- package/backend/key-bindings.ts +42 -0
- package/backend/layout-module/drag-drop.ts +359 -0
- package/backend/layout-module/paste.ts +165 -0
- package/backend/link-browser.ts +89 -0
- package/backend/live-search/element/backend-search.ts +34 -0
- package/backend/live-search/element/hint.ts +33 -0
- package/backend/live-search/element/provider/default-result-item.ts +52 -0
- package/backend/live-search/element/provider/page-provider-result-item.ts +52 -0
- package/backend/live-search/element/result/item/action/action-container.ts +135 -0
- package/backend/live-search/element/result/item/action/action.ts +56 -0
- package/backend/live-search/element/result/item/item-container.ts +224 -0
- package/backend/live-search/element/result/item/item.ts +74 -0
- package/backend/live-search/element/result/result-container.ts +110 -0
- package/backend/live-search/element/result/result-detail-container.ts +50 -0
- package/backend/live-search/element/result/result-pagination.ts +122 -0
- package/backend/live-search/element/search-option-item.ts +70 -0
- package/backend/live-search/live-search-configurator.ts +54 -0
- package/backend/live-search/live-search-shortcut.ts +29 -0
- package/backend/live-search/result-types/backend-module-result-type.ts +8 -0
- package/backend/live-search/result-types/default-result-type.ts +21 -0
- package/backend/live-search/result-types/page-result-type.ts +23 -0
- package/backend/localization.ts +390 -0
- package/backend/login-refresh.ts +525 -0
- package/backend/login.ts +179 -0
- package/backend/mail-link-handler.ts +45 -0
- package/backend/modal-interface.ts +19 -0
- package/backend/modal.ts +685 -0
- package/backend/module/iframe.ts +125 -0
- package/backend/module/router.ts +275 -0
- package/backend/module-menu.ts +550 -0
- package/backend/module.ts +133 -0
- package/backend/multi-record-selection-action.ts +44 -0
- package/backend/multi-record-selection-delete-action.ts +108 -0
- package/backend/multi-record-selection-edit-action.ts +56 -0
- package/backend/multi-record-selection.ts +468 -0
- package/backend/multi-step-wizard.ts +568 -0
- package/backend/new-content-element-wizard-button.ts +51 -0
- package/backend/new-multiple-pages.ts +108 -0
- package/backend/new-record-wizard.ts +680 -0
- package/backend/notification.ts +366 -0
- package/backend/offset.ts +40 -0
- package/backend/online-media.ts +110 -0
- package/backend/page-actions.ts +91 -0
- package/backend/page-link-handler.ts +51 -0
- package/backend/pagetsconfig/pagetsconfig-includes.ts +77 -0
- package/backend/popover.ts +165 -0
- package/backend/record-download-button.ts +104 -0
- package/backend/record-link-handler.ts +31 -0
- package/backend/record-search.ts +44 -0
- package/backend/recordlist.ts +230 -0
- package/backend/resource/resource.ts +35 -0
- package/backend/security/element/csp-reports.ts +406 -0
- package/backend/security/element/sudo-mode.ts +249 -0
- package/backend/security/sudo-mode-interceptor.ts +19 -0
- package/backend/settings/editor/editable-setting.ts +231 -0
- package/backend/settings/editor.ts +451 -0
- package/backend/settings/enum/settings-mode.enum.ts +24 -0
- package/backend/settings/type/base.ts +200 -0
- package/backend/settings/type/bool.ts +56 -0
- package/backend/settings/type/color.ts +64 -0
- package/backend/settings/type/int.ts +85 -0
- package/backend/settings/type/number.ts +61 -0
- package/backend/settings/type/page.ts +100 -0
- package/backend/settings/type/string.ts +77 -0
- package/backend/settings/type/stringlist.ts +87 -0
- package/backend/severity.ts +88 -0
- package/backend/site-inline-actions.ts +32 -0
- package/backend/sortable-table.ts +208 -0
- package/backend/storage/abstract-client-storage.ts +76 -0
- package/backend/storage/browser-session.ts +28 -0
- package/backend/storage/client.ts +28 -0
- package/backend/storage/module-state-storage.ts +118 -0
- package/backend/storage/persistent.ts +204 -0
- package/backend/switch-user.ts +82 -0
- package/backend/tabs.ts +72 -0
- package/backend/telephone-link-handler.ts +40 -0
- package/backend/tests/backend/storage/browser-storage-test.ts +87 -0
- package/backend/tests/backend/storage/client-test.ts +87 -0
- package/backend/tests/backend/utility/dom-helper-test.ts +51 -0
- package/backend/tests/backend-exception-test.ts +28 -0
- package/backend/tests/element/immediate-action-element-test.ts +81 -0
- package/backend/tests/form-engine-validation-test.ts +304 -0
- package/backend/tests/grid-editor-test.ts +29 -0
- package/backend/tests/hashing/md5Test.ts +28 -0
- package/backend/tests/icons-test.ts +23 -0
- package/backend/tests/notification-test.ts +161 -0
- package/backend/tests/popover-test.ts +83 -0
- package/backend/toolbar/clear-cache-menu.ts +88 -0
- package/backend/toolbar/live-search.ts +284 -0
- package/backend/toolbar/shortcut-menu.ts +254 -0
- package/backend/toolbar/system-information-menu.ts +161 -0
- package/backend/toolbar.ts +41 -0
- package/backend/tree/file-storage-browser.ts +194 -0
- package/backend/tree/file-storage-tree-container.ts +465 -0
- package/backend/tree/file-storage-tree.ts +42 -0
- package/backend/tree/page-browser.ts +272 -0
- package/backend/tree/page-tree-element.ts +602 -0
- package/backend/tree/page-tree.ts +152 -0
- package/backend/tree/tree-module-state.ts +159 -0
- package/backend/tree/tree-node-toggle.ts +31 -0
- package/backend/tree/tree-node.ts +97 -0
- package/backend/tree/tree-toolbar.ts +117 -0
- package/backend/tree/tree.ts +1595 -0
- package/backend/url-link-handler.ts +36 -0
- package/backend/user-pass-login.ts +255 -0
- package/backend/user-settings-manager.ts +189 -0
- package/backend/utility/collapse-state-persister.ts +205 -0
- package/backend/utility/collapse-state-search.ts +183 -0
- package/backend/utility/dom-helper.ts +91 -0
- package/backend/utility/drag-drop-utility.ts +47 -0
- package/backend/utility/message-utility.ts +45 -0
- package/backend/utility/top-level-module-import.ts +32 -0
- package/backend/utility.ts +194 -0
- package/backend/viewport/abstract-container.ts +22 -0
- package/backend/viewport/content-container.ts +98 -0
- package/backend/viewport/loader.ts +28 -0
- package/backend/viewport/navigation-container.ts +119 -0
- package/backend/viewport/resizable-navigation.ts +176 -0
- package/backend/viewport/toolbar.ts +27 -0
- package/backend/viewport/topbar.ts +39 -0
- package/backend/viewport.ts +46 -0
- package/backend/window-manager.ts +84 -0
- package/backend/wizard/move-content-element.ts +107 -0
- package/backend/wizard/move-page.ts +74 -0
- package/backend/wizard.ts +331 -0
- package/belog/backend-log.ts +88 -0
- package/beuser/backend-user-listing.ts +40 -0
- package/beuser/permissions.ts +291 -0
- package/core/ajax/ajax-request-types.ts +15 -0
- package/core/ajax/ajax-request.ts +202 -0
- package/core/ajax/ajax-response.ts +57 -0
- package/core/ajax/input-transformer.ts +92 -0
- package/core/ajax/simple-response-interface.ts +5 -0
- package/core/authentication/mfa-provider/totp.ts +54 -0
- package/core/cache/dedupe-async-task.ts +95 -0
- package/core/directive/markdown.ts +82 -0
- package/core/directive/nl2br.ts +16 -0
- package/core/document-service.ts +35 -0
- package/core/event/debounce-event.ts +40 -0
- package/core/event/event-interface.ts +24 -0
- package/core/event/regular-event.ts +58 -0
- package/core/event/request-animation-frame-event.ts +42 -0
- package/core/event/throttle-event.ts +54 -0
- package/core/java-script-item-handler.ts +29 -0
- package/core/java-script-item-processor.ts +171 -0
- package/core/lit-helper.ts +126 -0
- package/core/literals.ts +17 -0
- package/core/referrer-refresh.ts +2 -0
- package/core/security-utility.ts +89 -0
- package/core/tests/ajax/ajax-request-test.ts +290 -0
- package/core/tests/ajax/input-transformer-test.ts +64 -0
- package/core/tests/cache/dedupe-async-task-test.ts +152 -0
- package/core/tests/event/throttle-event-test.ts +47 -0
- package/core/tests/literals-test.ts +24 -0
- package/core/tests/security-utility-test.ts +50 -0
- package/core/utility/types.ts +9 -0
- package/dashboard/chart-initializer.ts +136 -0
- package/dashboard/dashboard.ts +1487 -0
- package/extensionmanager/distribution-image.ts +55 -0
- package/extensionmanager/main.ts +291 -0
- package/extensionmanager/repository.ts +195 -0
- package/extensionmanager/update.ts +118 -0
- package/extensionmanager/upload-form.ts +42 -0
- package/filelist/browse-files.ts +148 -0
- package/filelist/browse-folders.ts +94 -0
- package/filelist/context-menu-actions.ts +335 -0
- package/filelist/create-folder.ts +69 -0
- package/filelist/file-delete.ts +69 -0
- package/filelist/file-list-actions.ts +132 -0
- package/filelist/file-list-dragdrop.ts +139 -0
- package/filelist/file-list-rename-handler.ts +128 -0
- package/filelist/file-list-transfer-handler.ts +138 -0
- package/filelist/file-list.ts +396 -0
- package/filelist/file-replace.ts +37 -0
- package/filelist/linkbrowser-file-handler.ts +94 -0
- package/filelist/linkbrowser-folder-handler.ts +74 -0
- package/filelist/rename-file.ts +109 -0
- package/form/backend/form-editor/component/property-grid-editor.ts +340 -0
- package/form/backend/form-editor/core.ts +2104 -0
- package/form/backend/form-editor/helper.ts +226 -0
- package/form/backend/form-editor/inspector-component.ts +2323 -0
- package/form/backend/form-editor/mediator.ts +941 -0
- package/form/backend/form-editor/modals-component.ts +433 -0
- package/form/backend/form-editor/stage-component.ts +908 -0
- package/form/backend/form-editor/tree-component.ts +548 -0
- package/form/backend/form-editor/view-model.ts +1343 -0
- package/form/backend/form-editor.ts +846 -0
- package/form/backend/form-manager/main.ts +39 -0
- package/form/backend/form-manager/view-model.ts +789 -0
- package/form/backend/form-manager.ts +180 -0
- package/form/backend/helper.ts +60 -0
- package/form/frontend/date-picker.ts +38 -0
- package/frontend/default_frontend.ts +82 -0
- package/impexp/context-menu-actions.ts +50 -0
- package/impexp/import-export.ts +62 -0
- package/indexed-search/pagination.ts +26 -0
- package/indexed-search/statistics.ts +50 -0
- package/info/translation-status.ts +48 -0
- package/install/ajax/ajax-queue.ts +83 -0
- package/install/init-install.ts +2 -0
- package/install/init-installer.ts +2 -0
- package/install/install.ts +25 -0
- package/install/installer.ts +458 -0
- package/install/message-interface.ts +18 -0
- package/install/module/abstract-inline-module.ts +27 -0
- package/install/module/abstract-interactable-module.ts +107 -0
- package/install/module/environment/environment-check.ts +92 -0
- package/install/module/environment/folder-structure.ts +145 -0
- package/install/module/environment/image-processing.ts +141 -0
- package/install/module/environment/mail-test.ts +127 -0
- package/install/module/environment/php-info.ts +50 -0
- package/install/module/environment/system-information.ts +50 -0
- package/install/module/maintenance/cache.ts +59 -0
- package/install/module/maintenance/clear-tables.ts +134 -0
- package/install/module/maintenance/clear-typo3temp-files.ts +137 -0
- package/install/module/maintenance/create-admin.ts +126 -0
- package/install/module/maintenance/database-analyzer.ts +266 -0
- package/install/module/maintenance/dump-autoload.ts +58 -0
- package/install/module/maintenance/language-packs.ts +338 -0
- package/install/module/maintenance/reset-backend-user-uc.ts +58 -0
- package/install/module/password-strength.ts +56 -0
- package/install/module/settings/change-install-tool-password.ts +101 -0
- package/install/module/settings/extension-configuration.ts +224 -0
- package/install/module/settings/features.ts +102 -0
- package/install/module/settings/local-configuration.ts +316 -0
- package/install/module/settings/presets.ts +147 -0
- package/install/module/settings/system-maintainer.ts +111 -0
- package/install/module/upgrade/core-update.ts +255 -0
- package/install/module/upgrade/extension-compat-tester.ts +195 -0
- package/install/module/upgrade/extension-scanner.ts +381 -0
- package/install/module/upgrade/tca-ext-tables-check.ts +93 -0
- package/install/module/upgrade/tca-migrations-check.ts +101 -0
- package/install/module/upgrade/upgrade-docs.ts +310 -0
- package/install/module/upgrade/upgrade-wizards.ts +612 -0
- package/install/renderable/clearable.ts +113 -0
- package/install/renderable/flash-message.ts +64 -0
- package/install/renderable/info-box.ts +66 -0
- package/install/renderable/language-packs.ts +304 -0
- package/install/renderable/offset-group.ts +38 -0
- package/install/renderable/severity.ts +49 -0
- package/install/renderable/wrap-group.ts +36 -0
- package/install/router.ts +468 -0
- package/linkvalidator/linkvalidator-modal.ts +45 -0
- package/linkvalidator/linkvalidator.ts +101 -0
- package/lowlevel/query-generator.ts +132 -0
- package/lowlevel/reference-index.ts +44 -0
- package/opendocs/toolbar/opendocs-menu.ts +117 -0
- package/package.json +34 -0
- package/recycler/recycler.ts +539 -0
- package/redirects/event-handler.ts +108 -0
- package/redirects/form-engine-evaluation.ts +34 -0
- package/redirects/redirects-module.ts +58 -0
- package/rte-ckeditor/ckeditor5.ts +402 -0
- package/rte-ckeditor/css-prefixer.ts +94 -0
- package/rte-ckeditor/legacy/ckeditor5-bundle.ts +17 -0
- package/rte-ckeditor/legacy/ckeditor5-inspector.ts +2 -0
- package/rte-ckeditor/plugin/typo3-link.ts +810 -0
- package/rte-ckeditor/plugin/whitespace.ts +114 -0
- package/rte-ckeditor/rte-link-browser.ts +112 -0
- package/rte-ckeditor/tests/css-prefixer-test.ts +314 -0
- package/scheduler/scheduler-add-group.ts +119 -0
- package/scheduler/scheduler-editable-group-name.ts +307 -0
- package/scheduler/scheduler-sortable-groups.ts +59 -0
- package/scheduler/scheduler.ts +466 -0
- package/setup/setup-module.ts +122 -0
- package/styleguide/checkbox-demo.ts +5 -0
- package/styleguide/element/theme-switcher-element.ts +101 -0
- package/styleguide/filter.ts +22 -0
- package/styleguide/find-icons.ts +78 -0
- package/styleguide/processing-indicator.ts +78 -0
- package/styleguide/render-notifications.ts +64 -0
- package/sys-note/element/delete-button.ts +78 -0
- package/tstemplate/constant-editor.ts +64 -0
- package/tstemplate/information-module.ts +31 -0
- package/tstemplate/template-analyzer.ts +77 -0
- package/viewpage/main.ts +253 -0
- package/workspaces/backend.ts +979 -0
- package/workspaces/preview.ts +232 -0
- package/workspaces/renderable/comment-view.ts +79 -0
- package/workspaces/renderable/diff-view.ts +58 -0
- package/workspaces/renderable/history-view.ts +97 -0
- package/workspaces/renderable/record-information.ts +118 -0
- package/workspaces/renderable/record-table.ts +355 -0
- package/workspaces/renderable/send-to-stage-form.ts +108 -0
- package/workspaces/toolbar/workspaces-menu.ts +190 -0
- package/workspaces/utility/icon-helper.ts +57 -0
- package/workspaces/workspaces.ts +136 -0
|
@@ -0,0 +1,686 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the TYPO3 CMS project.
|
|
3
|
+
*
|
|
4
|
+
* It is free software; you can redistribute it and/or modify it under
|
|
5
|
+
* the terms of the GNU General Public License, either version 2
|
|
6
|
+
* of the License, or any later version.
|
|
7
|
+
*
|
|
8
|
+
* For the full copyright and license information, please read the
|
|
9
|
+
* LICENSE.txt file that was distributed with this source code.
|
|
10
|
+
*
|
|
11
|
+
* The TYPO3 project - inspiring people to share!
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Module: @typo3/backend/form-engine-validation
|
|
16
|
+
* Contains all JS functions related to TYPO3 TCEforms/FormEngineValidation
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
import $ from 'jquery';
|
|
20
|
+
import { DateTime } from 'luxon';
|
|
21
|
+
import Md5 from '@typo3/backend/hashing/md5';
|
|
22
|
+
import Modal from '@typo3/backend/modal';
|
|
23
|
+
import Severity from '@typo3/backend/severity';
|
|
24
|
+
import Utility from './utility';
|
|
25
|
+
import RegularEvent from '@typo3/core/event/regular-event';
|
|
26
|
+
import DomHelper from '@typo3/backend/utility/dom-helper';
|
|
27
|
+
import { selector } from '@typo3/core/literals';
|
|
28
|
+
import SubmitInterceptor from '@typo3/backend/form/submit-interceptor';
|
|
29
|
+
import { FormEngineReview } from '@typo3/backend/form-engine-review';
|
|
30
|
+
import type FormEngine from '@typo3/backend/form-engine';
|
|
31
|
+
|
|
32
|
+
type FormEngineFieldElement = HTMLInputElement|HTMLTextAreaElement|HTMLSelectElement;
|
|
33
|
+
type CustomEvaluationCallback = (value: string) => string;
|
|
34
|
+
type FormEngineInputParams = { field: string, evalList?: string, is_in?: string };
|
|
35
|
+
|
|
36
|
+
export interface PostValidationEvent {
|
|
37
|
+
field: FormEngineFieldElement,
|
|
38
|
+
isValid: boolean,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let formEngineInstance: typeof FormEngine;
|
|
42
|
+
let validationSuspended = false;
|
|
43
|
+
|
|
44
|
+
const customEvaluations: Map<string, CustomEvaluationCallback> = new Map();
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The main FormEngineValidation object
|
|
48
|
+
*
|
|
49
|
+
* @exports @typo3/backend/form-engine-validation
|
|
50
|
+
*/
|
|
51
|
+
export default class FormEngineValidation {
|
|
52
|
+
|
|
53
|
+
public static rulesSelector: string = '[data-formengine-validation-rules]';
|
|
54
|
+
public static inputSelector: string = '[data-formengine-input-params]';
|
|
55
|
+
public static markerSelector: string = '.t3js-formengine-validation-marker';
|
|
56
|
+
public static labelSelector: string = '.t3js-formengine-label';
|
|
57
|
+
public static errorClass: string = 'has-error';
|
|
58
|
+
public static validationErrorClass: string = 'has-validation-error';
|
|
59
|
+
public static passwordDummy: string = '********';
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Initialize validation for the first time
|
|
63
|
+
*/
|
|
64
|
+
public static initialize(formEngine: typeof FormEngine): void {
|
|
65
|
+
formEngineInstance = formEngine;
|
|
66
|
+
formEngineInstance.formElement.querySelectorAll('.' + FormEngineValidation.errorClass).forEach((e: HTMLElement) => e.classList.remove(FormEngineValidation.errorClass));
|
|
67
|
+
|
|
68
|
+
// Initialize input fields
|
|
69
|
+
FormEngineValidation.initializeInputFields();
|
|
70
|
+
|
|
71
|
+
new FormEngineReview(formEngineInstance.formElement);
|
|
72
|
+
|
|
73
|
+
// Bind to field changes
|
|
74
|
+
new RegularEvent('change', (e: Event, target: FormEngineFieldElement): void => {
|
|
75
|
+
FormEngineValidation.validateField(target);
|
|
76
|
+
FormEngineValidation.markFieldAsChanged(target);
|
|
77
|
+
}).delegateTo(formEngineInstance.formElement, FormEngineValidation.rulesSelector);
|
|
78
|
+
|
|
79
|
+
FormEngineValidation.registerSubmitCallback();
|
|
80
|
+
|
|
81
|
+
FormEngineValidation.validate();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Initialize all input fields
|
|
86
|
+
*/
|
|
87
|
+
public static initializeInputFields(): void {
|
|
88
|
+
formEngineInstance.formElement.querySelectorAll(FormEngineValidation.inputSelector).forEach((visibleField: FormEngineFieldElement): void => {
|
|
89
|
+
// ignore fields which already have been initialized
|
|
90
|
+
if ('formengineInputInitialized' in visibleField.dataset) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const config = JSON.parse(visibleField.dataset.formengineInputParams);
|
|
95
|
+
const fieldName = config.field;
|
|
96
|
+
const actualValueField = formEngineInstance.formElement.querySelector(selector`[name="${fieldName}"]`) as HTMLInputElement;
|
|
97
|
+
|
|
98
|
+
actualValueField.dataset.config = visibleField.dataset.formengineInputParams;
|
|
99
|
+
FormEngineValidation.initializeInputField(fieldName);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Initialize field by name
|
|
105
|
+
*/
|
|
106
|
+
public static initializeInputField(fieldName: string): void {
|
|
107
|
+
const field = formEngineInstance.formElement.querySelector(selector`[name="${fieldName}"]`) as HTMLInputElement;
|
|
108
|
+
const humanReadableField = formEngineInstance.formElement.querySelector(selector`[data-formengine-input-name="${fieldName}"]`) as FormEngineFieldElement;
|
|
109
|
+
|
|
110
|
+
if (field.dataset.config !== undefined) {
|
|
111
|
+
const config = JSON.parse(field.dataset.config);
|
|
112
|
+
const value = FormEngineValidation.formatByEvals(config, field.value);
|
|
113
|
+
if (value.length) {
|
|
114
|
+
humanReadableField.value = value;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
new RegularEvent('change', (): void => {
|
|
119
|
+
FormEngineValidation.updateInputField(humanReadableField.dataset.formengineInputName);
|
|
120
|
+
}).bindTo(humanReadableField);
|
|
121
|
+
|
|
122
|
+
// add the attribute so that acceptance tests can know when the field initialization has completed
|
|
123
|
+
humanReadableField.dataset.formengineInputInitialized = 'true';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public static registerCustomEvaluation(name: string, handler: CustomEvaluationCallback): void {
|
|
127
|
+
if (!customEvaluations.has(name)) {
|
|
128
|
+
customEvaluations.set(name, handler);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public static formatByEvals(config: FormEngineInputParams, value: string): string {
|
|
133
|
+
if (config.evalList !== undefined) {
|
|
134
|
+
const evalList = Utility.trimExplode(',', config.evalList);
|
|
135
|
+
for (const evalInstruction of evalList) {
|
|
136
|
+
value = FormEngineValidation.formatValue(evalInstruction, value);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return value;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Format field value
|
|
144
|
+
*/
|
|
145
|
+
public static formatValue(type: string, value: string|number): string {
|
|
146
|
+
switch (type) {
|
|
147
|
+
case 'date':
|
|
148
|
+
case 'datetime':
|
|
149
|
+
case 'time':
|
|
150
|
+
case 'timesec':
|
|
151
|
+
if (value === '') {
|
|
152
|
+
return '';
|
|
153
|
+
}
|
|
154
|
+
const isoDt = DateTime.fromISO(String(value), { zone: 'utc' });
|
|
155
|
+
if (!isoDt.isValid) {
|
|
156
|
+
throw new Error('Invalid ISO8601 DateTime string: ' + value);
|
|
157
|
+
}
|
|
158
|
+
return isoDt.toISO({ suppressMilliseconds: true });
|
|
159
|
+
case 'password':
|
|
160
|
+
return (value) ? FormEngineValidation.passwordDummy : '';
|
|
161
|
+
default:
|
|
162
|
+
return value.toString();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Update input field after change
|
|
168
|
+
*/
|
|
169
|
+
public static updateInputField(fieldName: string): void {
|
|
170
|
+
const field = formEngineInstance.formElement.querySelector(selector`[name="${fieldName}"]`) as HTMLInputElement;
|
|
171
|
+
const humanReadableField = formEngineInstance.formElement.querySelector(selector`[data-formengine-input-name="${fieldName}"]`) as FormEngineFieldElement;
|
|
172
|
+
|
|
173
|
+
if (field.dataset.config !== undefined) {
|
|
174
|
+
const config = JSON.parse(field.dataset.config);
|
|
175
|
+
const newValue = FormEngineValidation.processByEvals(config, humanReadableField.value);
|
|
176
|
+
const formattedValue = FormEngineValidation.formatByEvals(config, newValue);
|
|
177
|
+
|
|
178
|
+
// Only update value field if value actually changed
|
|
179
|
+
if (field.value !== newValue) {
|
|
180
|
+
if (field.disabled && field.dataset.enableOnModification) {
|
|
181
|
+
field.disabled = false;
|
|
182
|
+
}
|
|
183
|
+
field.value = newValue;
|
|
184
|
+
// After updating the value of the main field, dispatch a "change" event to inform e.g. the "RequestUpdate"
|
|
185
|
+
// component, which always listens to the main field instead of the "human readable field", about it.
|
|
186
|
+
field.dispatchEvent(new Event('change'));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Synchronize the "human-readable field" as the data normalization may have cleared invalid characters
|
|
190
|
+
if (humanReadableField.value !== formattedValue) {
|
|
191
|
+
humanReadableField.value = formattedValue;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Run validation for field
|
|
198
|
+
*/
|
|
199
|
+
public static validateField(field: FormEngineFieldElement|JQuery, value?: string): string {
|
|
200
|
+
if (field instanceof $) {
|
|
201
|
+
// @deprecated
|
|
202
|
+
console.warn('Passing a jQuery element to FormEngineValidation.validateField() is deprecated and will be removed in TYPO3 v14.');
|
|
203
|
+
console.trace();
|
|
204
|
+
field = <FormEngineFieldElement>(field as JQuery).get(0);
|
|
205
|
+
}
|
|
206
|
+
if (!(field instanceof HTMLElement)) {
|
|
207
|
+
// Can be removed altogether with jQuery support in TYPO3 v14
|
|
208
|
+
return value;
|
|
209
|
+
}
|
|
210
|
+
value = value || field.value || '';
|
|
211
|
+
|
|
212
|
+
if (typeof field.dataset.formengineValidationRules === 'undefined') {
|
|
213
|
+
return value;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const rules: any = JSON.parse(field.dataset.formengineValidationRules);
|
|
217
|
+
let markParent = false;
|
|
218
|
+
let selected = 0;
|
|
219
|
+
// keep the original value, validateField should not alter it
|
|
220
|
+
const returnValue: string = value;
|
|
221
|
+
let relatedField: FormEngineFieldElement;
|
|
222
|
+
let minItems: number;
|
|
223
|
+
let maxItems: number;
|
|
224
|
+
|
|
225
|
+
if (!Array.isArray(value)) {
|
|
226
|
+
value = value.trimStart();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
for (const rule of rules) {
|
|
230
|
+
if (markParent) {
|
|
231
|
+
// abort any further validation as validating the field already failed
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
switch (rule.type) {
|
|
235
|
+
case 'required':
|
|
236
|
+
if (value === '') {
|
|
237
|
+
markParent = true;
|
|
238
|
+
field.classList.add(FormEngineValidation.errorClass);
|
|
239
|
+
field.closest(FormEngineValidation.markerSelector)?.querySelector(FormEngineValidation.labelSelector)?.classList.add(FormEngineValidation.errorClass);
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
242
|
+
case 'range':
|
|
243
|
+
if (value !== '') {
|
|
244
|
+
if (rule.minItems || rule.maxItems) {
|
|
245
|
+
relatedField = formEngineInstance.formElement.querySelector(selector`[name="${field.dataset.relatedfieldname}"]`) as FormEngineFieldElement;
|
|
246
|
+
if (relatedField !== null) {
|
|
247
|
+
selected = Utility.trimExplode(',', relatedField.value).length;
|
|
248
|
+
} else {
|
|
249
|
+
selected = parseInt(field.value, 10);
|
|
250
|
+
}
|
|
251
|
+
if (rule.minItems !== undefined) {
|
|
252
|
+
minItems = rule.minItems * 1;
|
|
253
|
+
if (!isNaN(minItems) && selected < minItems) {
|
|
254
|
+
markParent = true;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (rule.maxItems !== undefined) {
|
|
258
|
+
maxItems = rule.maxItems * 1;
|
|
259
|
+
if (!isNaN(maxItems) && selected > maxItems) {
|
|
260
|
+
markParent = true;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (rule.lower !== undefined) {
|
|
265
|
+
if (field.dataset.inputType === 'datetimepicker') {
|
|
266
|
+
// HEADS up: value and range.lower are both fake UTC-0 ISO8601 strings (they are not UTC-0, but actually server localtime!)
|
|
267
|
+
// But it's fine to compare them in UTC for this comparison and not map to localtime as in date-time-picker parseDate.
|
|
268
|
+
const dt = DateTime.fromISO(value, { zone: 'utc' });
|
|
269
|
+
const lower = DateTime.fromISO(rule.lower, { zone: 'utc' });
|
|
270
|
+
if (!dt.isValid || dt < lower.minus(lower.second * 1000)) {
|
|
271
|
+
markParent = true;
|
|
272
|
+
}
|
|
273
|
+
} else {
|
|
274
|
+
const minValue = rule.lower * 1;
|
|
275
|
+
if (!isNaN(minValue) && parseInt(value, 10) < minValue) {
|
|
276
|
+
markParent = true;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (rule.upper !== undefined) {
|
|
281
|
+
if (field.dataset.inputType === 'datetimepicker') {
|
|
282
|
+
// HEADS up: value and range.upper are both fake UTC-0 ISO8601 strings (they are not UTC-0, but actually server localtime!)
|
|
283
|
+
// But it's fine to compare them in UTC-0 for this comparison and not map to localtime as in date-time-picker parseDate.
|
|
284
|
+
const dt = DateTime.fromISO(value, { zone: 'utc' });
|
|
285
|
+
const upper = DateTime.fromISO(rule.upper, { zone: 'utc' });
|
|
286
|
+
if (!dt.isValid || dt > upper.plus((59 - upper.second) * 1000)) {
|
|
287
|
+
markParent = true;
|
|
288
|
+
}
|
|
289
|
+
} else {
|
|
290
|
+
const maxValue = rule.upper * 1;
|
|
291
|
+
if (!isNaN(maxValue) && parseInt(value, 10) > maxValue) {
|
|
292
|
+
markParent = true;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
break;
|
|
298
|
+
case 'select':
|
|
299
|
+
case 'category':
|
|
300
|
+
if (rule.minItems || rule.maxItems) {
|
|
301
|
+
relatedField = formEngineInstance.formElement.querySelector(selector`[name="${field.dataset.relatedfieldname}"]`) as FormEngineFieldElement;
|
|
302
|
+
if (relatedField !== null) {
|
|
303
|
+
selected = Utility.trimExplode(',', relatedField.value).length;
|
|
304
|
+
} else if (field instanceof HTMLSelectElement) {
|
|
305
|
+
selected = field.querySelectorAll('option:checked').length;
|
|
306
|
+
} else {
|
|
307
|
+
selected = field.querySelectorAll('input[value]:checked').length;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (rule.minItems !== undefined) {
|
|
311
|
+
minItems = rule.minItems * 1;
|
|
312
|
+
if (!isNaN(minItems) && selected < minItems) {
|
|
313
|
+
markParent = true;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (rule.maxItems !== undefined) {
|
|
317
|
+
maxItems = rule.maxItems * 1;
|
|
318
|
+
if (!isNaN(maxItems) && selected > maxItems) {
|
|
319
|
+
markParent = true;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
break;
|
|
324
|
+
case 'group':
|
|
325
|
+
case 'folder':
|
|
326
|
+
if (rule.minItems || rule.maxItems) {
|
|
327
|
+
selected = Utility.trimExplode(',', field.value).length;
|
|
328
|
+
if (rule.minItems !== undefined) {
|
|
329
|
+
minItems = rule.minItems * 1;
|
|
330
|
+
if (!isNaN(minItems) && selected < minItems) {
|
|
331
|
+
markParent = true;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (rule.maxItems !== undefined) {
|
|
335
|
+
maxItems = rule.maxItems * 1;
|
|
336
|
+
if (!isNaN(maxItems) && selected > maxItems) {
|
|
337
|
+
markParent = true;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
break;
|
|
342
|
+
case 'inline':
|
|
343
|
+
if (rule.minItems || rule.maxItems) {
|
|
344
|
+
selected = Utility.trimExplode(',', field.value).length;
|
|
345
|
+
if (rule.minItems !== undefined) {
|
|
346
|
+
minItems = rule.minItems * 1;
|
|
347
|
+
if (!isNaN(minItems) && selected < minItems) {
|
|
348
|
+
markParent = true;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (rule.maxItems !== undefined) {
|
|
352
|
+
maxItems = rule.maxItems * 1;
|
|
353
|
+
if (!isNaN(maxItems) && selected > maxItems) {
|
|
354
|
+
markParent = true;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
break;
|
|
359
|
+
case 'min':
|
|
360
|
+
if (field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement) {
|
|
361
|
+
if (field.value.length > 0 && field.value.length < field.minLength) {
|
|
362
|
+
markParent = true;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
break;
|
|
366
|
+
case 'null':
|
|
367
|
+
// unknown type null, we ignore it
|
|
368
|
+
break;
|
|
369
|
+
default:
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const isValid = !markParent;
|
|
375
|
+
field.classList.toggle(FormEngineValidation.errorClass, !isValid);
|
|
376
|
+
field.setAttribute('aria-invalid', markParent.toString());
|
|
377
|
+
field.closest(FormEngineValidation.markerSelector)?.querySelector(FormEngineValidation.labelSelector)?.classList.toggle(FormEngineValidation.errorClass, !isValid);
|
|
378
|
+
|
|
379
|
+
FormEngineValidation.markParentTab(field, isValid);
|
|
380
|
+
formEngineInstance.formElement.dispatchEvent(new CustomEvent<PostValidationEvent>('t3-formengine-postfieldvalidation', { detail: { field: field, isValid: isValid }, cancelable: false, bubbles: true }));
|
|
381
|
+
|
|
382
|
+
return returnValue;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
public static processByEvals(config: FormEngineInputParams, value: string): string {
|
|
386
|
+
if (config.evalList !== undefined) {
|
|
387
|
+
const evalList = Utility.trimExplode(',', config.evalList);
|
|
388
|
+
for (const evalInstruction of evalList) {
|
|
389
|
+
value = FormEngineValidation.processValue(evalInstruction, value, config);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return value;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Process a value by given command and config
|
|
397
|
+
*/
|
|
398
|
+
public static processValue(command: string, value: string, config: FormEngineInputParams): string {
|
|
399
|
+
let newString = '';
|
|
400
|
+
let theValue = '';
|
|
401
|
+
let a = 0;
|
|
402
|
+
let returnValue = value;
|
|
403
|
+
switch (command) {
|
|
404
|
+
case 'alpha':
|
|
405
|
+
case 'num':
|
|
406
|
+
case 'alphanum':
|
|
407
|
+
case 'alphanum_x':
|
|
408
|
+
newString = '';
|
|
409
|
+
for (a = 0; a < value.length; a++) {
|
|
410
|
+
const theChar = value.substr(a, 1);
|
|
411
|
+
let special = (theChar === '_' || theChar === '-');
|
|
412
|
+
let alpha = (theChar >= 'a' && theChar <= 'z') || (theChar >= 'A' && theChar <= 'Z');
|
|
413
|
+
let num = (theChar >= '0' && theChar <= '9');
|
|
414
|
+
switch (command) {
|
|
415
|
+
case 'alphanum':
|
|
416
|
+
special = false;
|
|
417
|
+
break;
|
|
418
|
+
case 'alpha':
|
|
419
|
+
num = false;
|
|
420
|
+
special = false;
|
|
421
|
+
break;
|
|
422
|
+
case 'num':
|
|
423
|
+
alpha = false;
|
|
424
|
+
special = false;
|
|
425
|
+
break;
|
|
426
|
+
default:
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
if (alpha || num || special) {
|
|
430
|
+
newString += theChar;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (newString !== value) {
|
|
434
|
+
returnValue = newString;
|
|
435
|
+
}
|
|
436
|
+
break;
|
|
437
|
+
case 'is_in':
|
|
438
|
+
if (config.is_in) {
|
|
439
|
+
theValue = '' + value;
|
|
440
|
+
// Escape special characters, see https://stackoverflow.com/a/6969486/4828813
|
|
441
|
+
config.is_in = config.is_in.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
|
442
|
+
const re = new RegExp('[^' + config.is_in + ']+', 'g');
|
|
443
|
+
newString = theValue.replace(re, '');
|
|
444
|
+
} else {
|
|
445
|
+
newString = theValue;
|
|
446
|
+
}
|
|
447
|
+
returnValue = newString;
|
|
448
|
+
break;
|
|
449
|
+
case 'nospace':
|
|
450
|
+
returnValue = ('' + value).replace(/ /g, '');
|
|
451
|
+
break;
|
|
452
|
+
case 'md5':
|
|
453
|
+
if (value !== '') {
|
|
454
|
+
returnValue = Md5.hash(value);
|
|
455
|
+
}
|
|
456
|
+
break;
|
|
457
|
+
case 'upper':
|
|
458
|
+
returnValue = value.toUpperCase();
|
|
459
|
+
break;
|
|
460
|
+
case 'lower':
|
|
461
|
+
returnValue = value.toLowerCase();
|
|
462
|
+
break;
|
|
463
|
+
case 'integer':
|
|
464
|
+
if (value !== '') {
|
|
465
|
+
returnValue = FormEngineValidation.parseInt(value).toString();
|
|
466
|
+
}
|
|
467
|
+
break;
|
|
468
|
+
case 'decimal':
|
|
469
|
+
if (value !== '') {
|
|
470
|
+
returnValue = FormEngineValidation.parseDouble(value);
|
|
471
|
+
}
|
|
472
|
+
break;
|
|
473
|
+
case 'trim':
|
|
474
|
+
returnValue = String(value).trim();
|
|
475
|
+
break;
|
|
476
|
+
case 'time':
|
|
477
|
+
case 'timesec':
|
|
478
|
+
if (value !== '') {
|
|
479
|
+
const dt = DateTime.fromISO(value, { zone: 'utc' }).set({
|
|
480
|
+
year: 1970,
|
|
481
|
+
month: 1,
|
|
482
|
+
day: 1
|
|
483
|
+
});
|
|
484
|
+
returnValue = dt.toISO({ suppressMilliseconds: true });
|
|
485
|
+
}
|
|
486
|
+
break;
|
|
487
|
+
case 'year':
|
|
488
|
+
if (value !== '') {
|
|
489
|
+
let year = parseInt(value, 10);
|
|
490
|
+
if (isNaN(year)) {
|
|
491
|
+
year = new Date().getUTCFullYear();
|
|
492
|
+
}
|
|
493
|
+
returnValue = year.toString(10);
|
|
494
|
+
}
|
|
495
|
+
break;
|
|
496
|
+
case 'null':
|
|
497
|
+
// unknown type null, we ignore it
|
|
498
|
+
break;
|
|
499
|
+
case 'password':
|
|
500
|
+
// password is only a display evaluation, we ignore it
|
|
501
|
+
break;
|
|
502
|
+
default:
|
|
503
|
+
if (customEvaluations.has(command)) {
|
|
504
|
+
returnValue = customEvaluations.get(command).call(null, value);
|
|
505
|
+
} else if (typeof TBE_EDITOR === 'object' && TBE_EDITOR.customEvalFunctions !== undefined && typeof TBE_EDITOR.customEvalFunctions[command] === 'function') {
|
|
506
|
+
returnValue = TBE_EDITOR.customEvalFunctions[command](value);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return returnValue;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Validate the complete form
|
|
514
|
+
*/
|
|
515
|
+
public static validate(section?: Element): void {
|
|
516
|
+
if (typeof section === 'undefined' || section instanceof Document) {
|
|
517
|
+
formEngineInstance.formElement.querySelectorAll(FormEngineValidation.markerSelector + ', .t3js-tabmenu-item').forEach((tabMenuItem: HTMLElement): void => {
|
|
518
|
+
tabMenuItem.classList.remove(FormEngineValidation.validationErrorClass);
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const sectionElement = section || document;
|
|
523
|
+
for (const field of sectionElement.querySelectorAll<FormEngineFieldElement>(FormEngineValidation.rulesSelector)) {
|
|
524
|
+
if (field.closest('.t3js-flex-section-deleted, .t3js-inline-record-deleted, .t3js-file-reference-deleted') === null) {
|
|
525
|
+
let modified = false;
|
|
526
|
+
const currentValue = field.value;
|
|
527
|
+
const newValue = FormEngineValidation.validateField(field, currentValue);
|
|
528
|
+
if (Array.isArray(newValue) && Array.isArray(currentValue)) {
|
|
529
|
+
// handling for multi-selects
|
|
530
|
+
if (newValue.length !== currentValue.length) {
|
|
531
|
+
modified = true;
|
|
532
|
+
} else {
|
|
533
|
+
for (let i = 0; i < newValue.length; i++) {
|
|
534
|
+
if (newValue[i] !== currentValue[i]) {
|
|
535
|
+
modified = true;
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
} else if (newValue.length && currentValue !== newValue) {
|
|
541
|
+
modified = true;
|
|
542
|
+
}
|
|
543
|
+
if (modified) {
|
|
544
|
+
if (field.disabled && field.dataset.enableOnModification) {
|
|
545
|
+
field.disabled = false;
|
|
546
|
+
}
|
|
547
|
+
field.value = newValue;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Helper function to mark a field as changed.
|
|
555
|
+
*/
|
|
556
|
+
public static markFieldAsChanged(field: FormEngineFieldElement|JQuery): void {
|
|
557
|
+
if (field instanceof $) {
|
|
558
|
+
// @deprecated
|
|
559
|
+
console.warn('Passing a jQuery element to FormEngineValidation.markFieldAsChanged() is deprecated and will be removed in TYPO3 v14.');
|
|
560
|
+
console.trace();
|
|
561
|
+
field = <FormEngineFieldElement>(field as JQuery).get(0);
|
|
562
|
+
}
|
|
563
|
+
if (!(field instanceof HTMLElement)) {
|
|
564
|
+
// Can be removed altogether with jQuery support in TYPO3 v14
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
field.classList.add('has-change');
|
|
568
|
+
const fieldLabel = field.closest('.t3js-formengine-palette-field')?.querySelector('.t3js-formengine-label');
|
|
569
|
+
if (fieldLabel !== null) {
|
|
570
|
+
fieldLabel.classList.add('has-change');
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Parse value to integer
|
|
576
|
+
*/
|
|
577
|
+
public static parseInt(value: number|string|boolean): number {
|
|
578
|
+
const theVal = '' + value;
|
|
579
|
+
|
|
580
|
+
if (!value) {
|
|
581
|
+
return 0;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const returnValue = parseInt(theVal, 10);
|
|
585
|
+
if (isNaN(returnValue)) {
|
|
586
|
+
return 0;
|
|
587
|
+
}
|
|
588
|
+
return returnValue;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Parse value to double
|
|
593
|
+
*/
|
|
594
|
+
public static parseDouble(value: number|string|boolean, precision: number = 2): string {
|
|
595
|
+
let theVal = '' + value;
|
|
596
|
+
theVal = theVal.replace(/[^0-9,.-]/g, '');
|
|
597
|
+
const negative = theVal.startsWith('-');
|
|
598
|
+
theVal = theVal.replace(/-/g, '');
|
|
599
|
+
theVal = theVal.replace(/,/g, '.');
|
|
600
|
+
if (theVal.indexOf('.') === -1) {
|
|
601
|
+
theVal += '.0';
|
|
602
|
+
}
|
|
603
|
+
const parts = theVal.split('.');
|
|
604
|
+
const dec = parts.pop();
|
|
605
|
+
let theNumberVal = Number(parts.join('') + '.' + dec);
|
|
606
|
+
if (negative) {
|
|
607
|
+
theNumberVal *= -1;
|
|
608
|
+
}
|
|
609
|
+
theVal = theNumberVal.toFixed(precision);
|
|
610
|
+
|
|
611
|
+
return theVal;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
public static pol(foreign: string, value: string): object {
|
|
615
|
+
// @todo deprecate
|
|
616
|
+
// eslint-disable-next-line no-eval
|
|
617
|
+
return eval(((foreign == '-') ? '-' : '') + value);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Find tab by field and mark it as has-validation-error
|
|
622
|
+
*/
|
|
623
|
+
public static markParentTab(element: FormEngineFieldElement, isValid: boolean): void {
|
|
624
|
+
const panes = DomHelper.parents(element, '.tab-pane');
|
|
625
|
+
panes.forEach((pane: HTMLElement): void => {
|
|
626
|
+
if (isValid) {
|
|
627
|
+
// If incoming element is valid, check for errors in the same sheet
|
|
628
|
+
isValid = pane.querySelector('.has-error') === null;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const id = pane.id;
|
|
632
|
+
formEngineInstance.formElement
|
|
633
|
+
.querySelector('[data-bs-target="#' + id + '"]')
|
|
634
|
+
.closest('.t3js-tabmenu-item')
|
|
635
|
+
.classList.toggle(FormEngineValidation.validationErrorClass, !isValid);
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* @internal
|
|
641
|
+
*/
|
|
642
|
+
public static suspend() {
|
|
643
|
+
validationSuspended = true;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* @internal
|
|
648
|
+
*/
|
|
649
|
+
public static resume() {
|
|
650
|
+
validationSuspended = false;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
public static isValid(): boolean {
|
|
654
|
+
return document.querySelector('.' + FormEngineValidation.errorClass) === null;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
public static showErrorModal(): void {
|
|
658
|
+
const modal = Modal.confirm(
|
|
659
|
+
TYPO3.lang.alert || 'Alert',
|
|
660
|
+
TYPO3.lang['FormEngine.fieldsMissing'],
|
|
661
|
+
Severity.error,
|
|
662
|
+
[
|
|
663
|
+
{
|
|
664
|
+
text: TYPO3.lang['button.ok'] || 'OK',
|
|
665
|
+
active: true,
|
|
666
|
+
btnClass: 'btn-default',
|
|
667
|
+
name: 'ok',
|
|
668
|
+
},
|
|
669
|
+
]
|
|
670
|
+
);
|
|
671
|
+
modal.addEventListener('button.clicked', () => modal.hideModal());
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
public static registerSubmitCallback() {
|
|
675
|
+
const submitInterceptor = new SubmitInterceptor(formEngineInstance.formElement);
|
|
676
|
+
submitInterceptor.addPreSubmitCallback((): boolean => {
|
|
677
|
+
if (validationSuspended || FormEngineValidation.isValid()) {
|
|
678
|
+
return true;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
FormEngineValidation.showErrorModal();
|
|
682
|
+
|
|
683
|
+
return false;
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
}
|