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.
Files changed (415) hide show
  1. package/.github/workflows/sync-and-publish.yml +265 -0
  2. package/.last-synced-tag +1 -0
  3. package/LICENSE.txt +339 -0
  4. package/README-TYPO3.md +166 -0
  5. package/README.md +28 -0
  6. package/adminpanel/admin-panel.ts +375 -0
  7. package/adminpanel/modules/cache.ts +27 -0
  8. package/adminpanel/modules/preview.ts +75 -0
  9. package/backend/action-button/abstract-action.ts +22 -0
  10. package/backend/action-button/deferred-action.ts +41 -0
  11. package/backend/action-button/immediate-action.ts +31 -0
  12. package/backend/action-dispatcher.ts +104 -0
  13. package/backend/ajax-data-handler/message-interface.ts +18 -0
  14. package/backend/ajax-data-handler/response-interface.ts +20 -0
  15. package/backend/ajax-data-handler.ts +214 -0
  16. package/backend/backend-exception.ts +22 -0
  17. package/backend/broadcast-message.ts +50 -0
  18. package/backend/broadcast-service.ts +52 -0
  19. package/backend/browse-database.ts +38 -0
  20. package/backend/clear-cache.ts +88 -0
  21. package/backend/clipboard-panel.ts +240 -0
  22. package/backend/close-current-window.ts +2 -0
  23. package/backend/code-editor/autocomplete/completion-result.ts +105 -0
  24. package/backend/code-editor/autocomplete/ts-code-completion.ts +142 -0
  25. package/backend/code-editor/autocomplete/ts-parser.ts +485 -0
  26. package/backend/code-editor/autocomplete/ts-ref.ts +164 -0
  27. package/backend/code-editor/element/code-mirror-element.ts +282 -0
  28. package/backend/code-editor/language/typoscript.ts +181 -0
  29. package/backend/code-editor/stream-parser/typoscript.ts +1600 -0
  30. package/backend/color-picker.ts +158 -0
  31. package/backend/color-scheme-switch.ts +179 -0
  32. package/backend/column-selector-button.ts +302 -0
  33. package/backend/context-help.ts +68 -0
  34. package/backend/context-menu-actions.ts +322 -0
  35. package/backend/context-menu.ts +751 -0
  36. package/backend/copy-to-clipboard.ts +79 -0
  37. package/backend/date-time-picker.ts +270 -0
  38. package/backend/document-save-actions.ts +159 -0
  39. package/backend/drag-tooltip.ts +285 -0
  40. package/backend/drag-uploader.ts +914 -0
  41. package/backend/element/alert-element.ts +116 -0
  42. package/backend/element/dispatch-modal-button.ts +50 -0
  43. package/backend/element/draggable-resizable-element.ts +249 -0
  44. package/backend/element/editable-page-title.ts +306 -0
  45. package/backend/element/icon-element.ts +84 -0
  46. package/backend/element/immediate-action-element.ts +89 -0
  47. package/backend/element/pagination.ts +59 -0
  48. package/backend/element/progress-bar-element.ts +172 -0
  49. package/backend/element/progress-tracker-element.ts +119 -0
  50. package/backend/element/pseudo-button.ts +58 -0
  51. package/backend/element/spinner-element.ts +51 -0
  52. package/backend/element/thumbnail-element.ts +80 -0
  53. package/backend/element-browser.ts +182 -0
  54. package/backend/enum/data-transfer-types.ts +21 -0
  55. package/backend/enum/icon-types.ts +31 -0
  56. package/backend/enum/key-types.ts +27 -0
  57. package/backend/enum/severity.ts +20 -0
  58. package/backend/enum/viewport/scaffold-identifier.ts +24 -0
  59. package/backend/event/client-request.ts +25 -0
  60. package/backend/event/consumable.ts +18 -0
  61. package/backend/event/consumer-scope.ts +54 -0
  62. package/backend/event/event-dispatcher.ts +26 -0
  63. package/backend/event/interaction-request-assignment.ts +24 -0
  64. package/backend/event/interaction-request-map.ts +75 -0
  65. package/backend/event/interaction-request.ts +48 -0
  66. package/backend/event/trigger-request.ts +56 -0
  67. package/backend/form/submit-interceptor.ts +66 -0
  68. package/backend/form-engine/container/files-control-container.ts +716 -0
  69. package/backend/form-engine/container/flex-form-container-container.ts +166 -0
  70. package/backend/form-engine/container/flex-form-section-container.ts +210 -0
  71. package/backend/form-engine/container/inline-control-container.ts +1230 -0
  72. package/backend/form-engine/container/site-language-container.ts +616 -0
  73. package/backend/form-engine/element/abstract-sortable-select-items.ts +186 -0
  74. package/backend/form-engine/element/category-element.ts +160 -0
  75. package/backend/form-engine/element/color-element.ts +81 -0
  76. package/backend/form-engine/element/datetime-element.ts +66 -0
  77. package/backend/form-engine/element/extra/char-counter.ts +112 -0
  78. package/backend/form-engine/element/extra/select-box-filter.ts +85 -0
  79. package/backend/form-engine/element/folder-element.ts +66 -0
  80. package/backend/form-engine/element/group-element.ts +47 -0
  81. package/backend/form-engine/element/json-element.ts +52 -0
  82. package/backend/form-engine/element/link-element.ts +121 -0
  83. package/backend/form-engine/element/mfa-info-element.ts +169 -0
  84. package/backend/form-engine/element/modifier/resizable.ts +27 -0
  85. package/backend/form-engine/element/modifier/tabbable.ts +29 -0
  86. package/backend/form-engine/element/online-media-form-element.ts +65 -0
  87. package/backend/form-engine/element/password-element.ts +71 -0
  88. package/backend/form-engine/element/select-multiple-side-by-side-element.ts +83 -0
  89. package/backend/form-engine/element/select-single-element.ts +82 -0
  90. package/backend/form-engine/element/select-tree-element.ts +148 -0
  91. package/backend/form-engine/element/select-tree-toolbar.ts +118 -0
  92. package/backend/form-engine/element/select-tree.ts +210 -0
  93. package/backend/form-engine/element/slug-element.ts +243 -0
  94. package/backend/form-engine/element/suggest/result-container.ts +118 -0
  95. package/backend/form-engine/element/suggest/result-item.ts +90 -0
  96. package/backend/form-engine/element/table-permission-element.ts +101 -0
  97. package/backend/form-engine/element/table-wizard-element.ts +427 -0
  98. package/backend/form-engine/element/text-element.ts +52 -0
  99. package/backend/form-engine/element/text-table-element.ts +31 -0
  100. package/backend/form-engine/field-control/add-record.ts +40 -0
  101. package/backend/form-engine/field-control/edit-popup.ts +64 -0
  102. package/backend/form-engine/field-control/insert-clipboard.ts +50 -0
  103. package/backend/form-engine/field-control/link-popup.ts +53 -0
  104. package/backend/form-engine/field-control/list-module.ts +40 -0
  105. package/backend/form-engine/field-control/password-generator.ts +102 -0
  106. package/backend/form-engine/field-control/reset-selection.ts +48 -0
  107. package/backend/form-engine/field-wizard/localization-state-selector.ts +67 -0
  108. package/backend/form-engine/field-wizard/value-picker.ts +123 -0
  109. package/backend/form-engine/field-wizard/value-slider.ts +86 -0
  110. package/backend/form-engine/inline-relation/ajax-dispatcher.ts +124 -0
  111. package/backend/form-engine/inline-relation/inline-response-interface.ts +41 -0
  112. package/backend/form-engine-link-browser-adapter.ts +129 -0
  113. package/backend/form-engine-review.ts +155 -0
  114. package/backend/form-engine-suggest.ts +133 -0
  115. package/backend/form-engine-validation.ts +686 -0
  116. package/backend/form-engine.ts +1391 -0
  117. package/backend/global-event-handler.ts +252 -0
  118. package/backend/grid-editor.ts +1077 -0
  119. package/backend/hashing/md5.ts +252 -0
  120. package/backend/helper.ts +24 -0
  121. package/backend/hotkeys/hotkey-storage.ts +63 -0
  122. package/backend/hotkeys.ts +216 -0
  123. package/backend/icons.ts +236 -0
  124. package/backend/image-manipulation.ts +885 -0
  125. package/backend/info-window.ts +46 -0
  126. package/backend/input/clearable.ts +128 -0
  127. package/backend/input/index.d.ts +8 -0
  128. package/backend/java-script-module-import-event-handler.ts +19 -0
  129. package/backend/key-bindings.ts +42 -0
  130. package/backend/layout-module/drag-drop.ts +359 -0
  131. package/backend/layout-module/paste.ts +165 -0
  132. package/backend/link-browser.ts +89 -0
  133. package/backend/live-search/element/backend-search.ts +34 -0
  134. package/backend/live-search/element/hint.ts +33 -0
  135. package/backend/live-search/element/provider/default-result-item.ts +52 -0
  136. package/backend/live-search/element/provider/page-provider-result-item.ts +52 -0
  137. package/backend/live-search/element/result/item/action/action-container.ts +135 -0
  138. package/backend/live-search/element/result/item/action/action.ts +56 -0
  139. package/backend/live-search/element/result/item/item-container.ts +224 -0
  140. package/backend/live-search/element/result/item/item.ts +74 -0
  141. package/backend/live-search/element/result/result-container.ts +110 -0
  142. package/backend/live-search/element/result/result-detail-container.ts +50 -0
  143. package/backend/live-search/element/result/result-pagination.ts +122 -0
  144. package/backend/live-search/element/search-option-item.ts +70 -0
  145. package/backend/live-search/live-search-configurator.ts +54 -0
  146. package/backend/live-search/live-search-shortcut.ts +29 -0
  147. package/backend/live-search/result-types/backend-module-result-type.ts +8 -0
  148. package/backend/live-search/result-types/default-result-type.ts +21 -0
  149. package/backend/live-search/result-types/page-result-type.ts +23 -0
  150. package/backend/localization.ts +390 -0
  151. package/backend/login-refresh.ts +525 -0
  152. package/backend/login.ts +179 -0
  153. package/backend/mail-link-handler.ts +45 -0
  154. package/backend/modal-interface.ts +19 -0
  155. package/backend/modal.ts +685 -0
  156. package/backend/module/iframe.ts +125 -0
  157. package/backend/module/router.ts +275 -0
  158. package/backend/module-menu.ts +550 -0
  159. package/backend/module.ts +133 -0
  160. package/backend/multi-record-selection-action.ts +44 -0
  161. package/backend/multi-record-selection-delete-action.ts +108 -0
  162. package/backend/multi-record-selection-edit-action.ts +56 -0
  163. package/backend/multi-record-selection.ts +468 -0
  164. package/backend/multi-step-wizard.ts +568 -0
  165. package/backend/new-content-element-wizard-button.ts +51 -0
  166. package/backend/new-multiple-pages.ts +108 -0
  167. package/backend/new-record-wizard.ts +680 -0
  168. package/backend/notification.ts +366 -0
  169. package/backend/offset.ts +40 -0
  170. package/backend/online-media.ts +110 -0
  171. package/backend/page-actions.ts +91 -0
  172. package/backend/page-link-handler.ts +51 -0
  173. package/backend/pagetsconfig/pagetsconfig-includes.ts +77 -0
  174. package/backend/popover.ts +165 -0
  175. package/backend/record-download-button.ts +104 -0
  176. package/backend/record-link-handler.ts +31 -0
  177. package/backend/record-search.ts +44 -0
  178. package/backend/recordlist.ts +230 -0
  179. package/backend/resource/resource.ts +35 -0
  180. package/backend/security/element/csp-reports.ts +406 -0
  181. package/backend/security/element/sudo-mode.ts +249 -0
  182. package/backend/security/sudo-mode-interceptor.ts +19 -0
  183. package/backend/settings/editor/editable-setting.ts +231 -0
  184. package/backend/settings/editor.ts +451 -0
  185. package/backend/settings/enum/settings-mode.enum.ts +24 -0
  186. package/backend/settings/type/base.ts +200 -0
  187. package/backend/settings/type/bool.ts +56 -0
  188. package/backend/settings/type/color.ts +64 -0
  189. package/backend/settings/type/int.ts +85 -0
  190. package/backend/settings/type/number.ts +61 -0
  191. package/backend/settings/type/page.ts +100 -0
  192. package/backend/settings/type/string.ts +77 -0
  193. package/backend/settings/type/stringlist.ts +87 -0
  194. package/backend/severity.ts +88 -0
  195. package/backend/site-inline-actions.ts +32 -0
  196. package/backend/sortable-table.ts +208 -0
  197. package/backend/storage/abstract-client-storage.ts +76 -0
  198. package/backend/storage/browser-session.ts +28 -0
  199. package/backend/storage/client.ts +28 -0
  200. package/backend/storage/module-state-storage.ts +118 -0
  201. package/backend/storage/persistent.ts +204 -0
  202. package/backend/switch-user.ts +82 -0
  203. package/backend/tabs.ts +72 -0
  204. package/backend/telephone-link-handler.ts +40 -0
  205. package/backend/tests/backend/storage/browser-storage-test.ts +87 -0
  206. package/backend/tests/backend/storage/client-test.ts +87 -0
  207. package/backend/tests/backend/utility/dom-helper-test.ts +51 -0
  208. package/backend/tests/backend-exception-test.ts +28 -0
  209. package/backend/tests/element/immediate-action-element-test.ts +81 -0
  210. package/backend/tests/form-engine-validation-test.ts +304 -0
  211. package/backend/tests/grid-editor-test.ts +29 -0
  212. package/backend/tests/hashing/md5Test.ts +28 -0
  213. package/backend/tests/icons-test.ts +23 -0
  214. package/backend/tests/notification-test.ts +161 -0
  215. package/backend/tests/popover-test.ts +83 -0
  216. package/backend/toolbar/clear-cache-menu.ts +88 -0
  217. package/backend/toolbar/live-search.ts +284 -0
  218. package/backend/toolbar/shortcut-menu.ts +254 -0
  219. package/backend/toolbar/system-information-menu.ts +161 -0
  220. package/backend/toolbar.ts +41 -0
  221. package/backend/tree/file-storage-browser.ts +194 -0
  222. package/backend/tree/file-storage-tree-container.ts +465 -0
  223. package/backend/tree/file-storage-tree.ts +42 -0
  224. package/backend/tree/page-browser.ts +272 -0
  225. package/backend/tree/page-tree-element.ts +602 -0
  226. package/backend/tree/page-tree.ts +152 -0
  227. package/backend/tree/tree-module-state.ts +159 -0
  228. package/backend/tree/tree-node-toggle.ts +31 -0
  229. package/backend/tree/tree-node.ts +97 -0
  230. package/backend/tree/tree-toolbar.ts +117 -0
  231. package/backend/tree/tree.ts +1595 -0
  232. package/backend/url-link-handler.ts +36 -0
  233. package/backend/user-pass-login.ts +255 -0
  234. package/backend/user-settings-manager.ts +189 -0
  235. package/backend/utility/collapse-state-persister.ts +205 -0
  236. package/backend/utility/collapse-state-search.ts +183 -0
  237. package/backend/utility/dom-helper.ts +91 -0
  238. package/backend/utility/drag-drop-utility.ts +47 -0
  239. package/backend/utility/message-utility.ts +45 -0
  240. package/backend/utility/top-level-module-import.ts +32 -0
  241. package/backend/utility.ts +194 -0
  242. package/backend/viewport/abstract-container.ts +22 -0
  243. package/backend/viewport/content-container.ts +98 -0
  244. package/backend/viewport/loader.ts +28 -0
  245. package/backend/viewport/navigation-container.ts +119 -0
  246. package/backend/viewport/resizable-navigation.ts +176 -0
  247. package/backend/viewport/toolbar.ts +27 -0
  248. package/backend/viewport/topbar.ts +39 -0
  249. package/backend/viewport.ts +46 -0
  250. package/backend/window-manager.ts +84 -0
  251. package/backend/wizard/move-content-element.ts +107 -0
  252. package/backend/wizard/move-page.ts +74 -0
  253. package/backend/wizard.ts +331 -0
  254. package/belog/backend-log.ts +88 -0
  255. package/beuser/backend-user-listing.ts +40 -0
  256. package/beuser/permissions.ts +291 -0
  257. package/core/ajax/ajax-request-types.ts +15 -0
  258. package/core/ajax/ajax-request.ts +202 -0
  259. package/core/ajax/ajax-response.ts +57 -0
  260. package/core/ajax/input-transformer.ts +92 -0
  261. package/core/ajax/simple-response-interface.ts +5 -0
  262. package/core/authentication/mfa-provider/totp.ts +54 -0
  263. package/core/cache/dedupe-async-task.ts +95 -0
  264. package/core/directive/markdown.ts +82 -0
  265. package/core/directive/nl2br.ts +16 -0
  266. package/core/document-service.ts +35 -0
  267. package/core/event/debounce-event.ts +40 -0
  268. package/core/event/event-interface.ts +24 -0
  269. package/core/event/regular-event.ts +58 -0
  270. package/core/event/request-animation-frame-event.ts +42 -0
  271. package/core/event/throttle-event.ts +54 -0
  272. package/core/java-script-item-handler.ts +29 -0
  273. package/core/java-script-item-processor.ts +171 -0
  274. package/core/lit-helper.ts +126 -0
  275. package/core/literals.ts +17 -0
  276. package/core/referrer-refresh.ts +2 -0
  277. package/core/security-utility.ts +89 -0
  278. package/core/tests/ajax/ajax-request-test.ts +290 -0
  279. package/core/tests/ajax/input-transformer-test.ts +64 -0
  280. package/core/tests/cache/dedupe-async-task-test.ts +152 -0
  281. package/core/tests/event/throttle-event-test.ts +47 -0
  282. package/core/tests/literals-test.ts +24 -0
  283. package/core/tests/security-utility-test.ts +50 -0
  284. package/core/utility/types.ts +9 -0
  285. package/dashboard/chart-initializer.ts +136 -0
  286. package/dashboard/dashboard.ts +1487 -0
  287. package/extensionmanager/distribution-image.ts +55 -0
  288. package/extensionmanager/main.ts +291 -0
  289. package/extensionmanager/repository.ts +195 -0
  290. package/extensionmanager/update.ts +118 -0
  291. package/extensionmanager/upload-form.ts +42 -0
  292. package/filelist/browse-files.ts +148 -0
  293. package/filelist/browse-folders.ts +94 -0
  294. package/filelist/context-menu-actions.ts +335 -0
  295. package/filelist/create-folder.ts +69 -0
  296. package/filelist/file-delete.ts +69 -0
  297. package/filelist/file-list-actions.ts +132 -0
  298. package/filelist/file-list-dragdrop.ts +139 -0
  299. package/filelist/file-list-rename-handler.ts +128 -0
  300. package/filelist/file-list-transfer-handler.ts +138 -0
  301. package/filelist/file-list.ts +396 -0
  302. package/filelist/file-replace.ts +37 -0
  303. package/filelist/linkbrowser-file-handler.ts +94 -0
  304. package/filelist/linkbrowser-folder-handler.ts +74 -0
  305. package/filelist/rename-file.ts +109 -0
  306. package/form/backend/form-editor/component/property-grid-editor.ts +340 -0
  307. package/form/backend/form-editor/core.ts +2104 -0
  308. package/form/backend/form-editor/helper.ts +226 -0
  309. package/form/backend/form-editor/inspector-component.ts +2323 -0
  310. package/form/backend/form-editor/mediator.ts +941 -0
  311. package/form/backend/form-editor/modals-component.ts +433 -0
  312. package/form/backend/form-editor/stage-component.ts +908 -0
  313. package/form/backend/form-editor/tree-component.ts +548 -0
  314. package/form/backend/form-editor/view-model.ts +1343 -0
  315. package/form/backend/form-editor.ts +846 -0
  316. package/form/backend/form-manager/main.ts +39 -0
  317. package/form/backend/form-manager/view-model.ts +789 -0
  318. package/form/backend/form-manager.ts +180 -0
  319. package/form/backend/helper.ts +60 -0
  320. package/form/frontend/date-picker.ts +38 -0
  321. package/frontend/default_frontend.ts +82 -0
  322. package/impexp/context-menu-actions.ts +50 -0
  323. package/impexp/import-export.ts +62 -0
  324. package/indexed-search/pagination.ts +26 -0
  325. package/indexed-search/statistics.ts +50 -0
  326. package/info/translation-status.ts +48 -0
  327. package/install/ajax/ajax-queue.ts +83 -0
  328. package/install/init-install.ts +2 -0
  329. package/install/init-installer.ts +2 -0
  330. package/install/install.ts +25 -0
  331. package/install/installer.ts +458 -0
  332. package/install/message-interface.ts +18 -0
  333. package/install/module/abstract-inline-module.ts +27 -0
  334. package/install/module/abstract-interactable-module.ts +107 -0
  335. package/install/module/environment/environment-check.ts +92 -0
  336. package/install/module/environment/folder-structure.ts +145 -0
  337. package/install/module/environment/image-processing.ts +141 -0
  338. package/install/module/environment/mail-test.ts +127 -0
  339. package/install/module/environment/php-info.ts +50 -0
  340. package/install/module/environment/system-information.ts +50 -0
  341. package/install/module/maintenance/cache.ts +59 -0
  342. package/install/module/maintenance/clear-tables.ts +134 -0
  343. package/install/module/maintenance/clear-typo3temp-files.ts +137 -0
  344. package/install/module/maintenance/create-admin.ts +126 -0
  345. package/install/module/maintenance/database-analyzer.ts +266 -0
  346. package/install/module/maintenance/dump-autoload.ts +58 -0
  347. package/install/module/maintenance/language-packs.ts +338 -0
  348. package/install/module/maintenance/reset-backend-user-uc.ts +58 -0
  349. package/install/module/password-strength.ts +56 -0
  350. package/install/module/settings/change-install-tool-password.ts +101 -0
  351. package/install/module/settings/extension-configuration.ts +224 -0
  352. package/install/module/settings/features.ts +102 -0
  353. package/install/module/settings/local-configuration.ts +316 -0
  354. package/install/module/settings/presets.ts +147 -0
  355. package/install/module/settings/system-maintainer.ts +111 -0
  356. package/install/module/upgrade/core-update.ts +255 -0
  357. package/install/module/upgrade/extension-compat-tester.ts +195 -0
  358. package/install/module/upgrade/extension-scanner.ts +381 -0
  359. package/install/module/upgrade/tca-ext-tables-check.ts +93 -0
  360. package/install/module/upgrade/tca-migrations-check.ts +101 -0
  361. package/install/module/upgrade/upgrade-docs.ts +310 -0
  362. package/install/module/upgrade/upgrade-wizards.ts +612 -0
  363. package/install/renderable/clearable.ts +113 -0
  364. package/install/renderable/flash-message.ts +64 -0
  365. package/install/renderable/info-box.ts +66 -0
  366. package/install/renderable/language-packs.ts +304 -0
  367. package/install/renderable/offset-group.ts +38 -0
  368. package/install/renderable/severity.ts +49 -0
  369. package/install/renderable/wrap-group.ts +36 -0
  370. package/install/router.ts +468 -0
  371. package/linkvalidator/linkvalidator-modal.ts +45 -0
  372. package/linkvalidator/linkvalidator.ts +101 -0
  373. package/lowlevel/query-generator.ts +132 -0
  374. package/lowlevel/reference-index.ts +44 -0
  375. package/opendocs/toolbar/opendocs-menu.ts +117 -0
  376. package/package.json +34 -0
  377. package/recycler/recycler.ts +539 -0
  378. package/redirects/event-handler.ts +108 -0
  379. package/redirects/form-engine-evaluation.ts +34 -0
  380. package/redirects/redirects-module.ts +58 -0
  381. package/rte-ckeditor/ckeditor5.ts +402 -0
  382. package/rte-ckeditor/css-prefixer.ts +94 -0
  383. package/rte-ckeditor/legacy/ckeditor5-bundle.ts +17 -0
  384. package/rte-ckeditor/legacy/ckeditor5-inspector.ts +2 -0
  385. package/rte-ckeditor/plugin/typo3-link.ts +810 -0
  386. package/rte-ckeditor/plugin/whitespace.ts +114 -0
  387. package/rte-ckeditor/rte-link-browser.ts +112 -0
  388. package/rte-ckeditor/tests/css-prefixer-test.ts +314 -0
  389. package/scheduler/scheduler-add-group.ts +119 -0
  390. package/scheduler/scheduler-editable-group-name.ts +307 -0
  391. package/scheduler/scheduler-sortable-groups.ts +59 -0
  392. package/scheduler/scheduler.ts +466 -0
  393. package/setup/setup-module.ts +122 -0
  394. package/styleguide/checkbox-demo.ts +5 -0
  395. package/styleguide/element/theme-switcher-element.ts +101 -0
  396. package/styleguide/filter.ts +22 -0
  397. package/styleguide/find-icons.ts +78 -0
  398. package/styleguide/processing-indicator.ts +78 -0
  399. package/styleguide/render-notifications.ts +64 -0
  400. package/sys-note/element/delete-button.ts +78 -0
  401. package/tstemplate/constant-editor.ts +64 -0
  402. package/tstemplate/information-module.ts +31 -0
  403. package/tstemplate/template-analyzer.ts +77 -0
  404. package/viewpage/main.ts +253 -0
  405. package/workspaces/backend.ts +979 -0
  406. package/workspaces/preview.ts +232 -0
  407. package/workspaces/renderable/comment-view.ts +79 -0
  408. package/workspaces/renderable/diff-view.ts +58 -0
  409. package/workspaces/renderable/history-view.ts +97 -0
  410. package/workspaces/renderable/record-information.ts +118 -0
  411. package/workspaces/renderable/record-table.ts +355 -0
  412. package/workspaces/renderable/send-to-stage-form.ts +108 -0
  413. package/workspaces/toolbar/workspaces-menu.ts +190 -0
  414. package/workspaces/utility/icon-helper.ts +57 -0
  415. package/workspaces/workspaces.ts +136 -0
@@ -0,0 +1,224 @@
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
+ import 'bootstrap';
15
+ import '../../renderable/clearable';
16
+ import '../../renderable/wrap-group';
17
+ import '../../renderable/offset-group';
18
+ import { AbstractInteractableModule, type ModuleLoadedResponse } from '../abstract-interactable-module';
19
+ import ModuleMenu from '@typo3/backend/module-menu';
20
+ import Notification from '@typo3/backend/notification';
21
+ import AjaxRequest from '@typo3/core/ajax/ajax-request';
22
+ import Router from '../../router';
23
+ import { topLevelModuleImport } from '@typo3/backend/utility/top-level-module-import';
24
+ import type MessageInterface from '@typo3/install/message-interface';
25
+ import RegularEvent from '@typo3/core/event/regular-event';
26
+ import { Collapse } from 'bootstrap';
27
+ import DebounceEvent from '@typo3/core/event/debounce-event';
28
+ import { KeyTypesEnum } from '@typo3/backend/enum/key-types';
29
+ import type { AjaxResponse } from '@typo3/core/ajax/ajax-response';
30
+ import type { ModalElement } from '@typo3/backend/modal';
31
+
32
+ enum Identifiers {
33
+ formListener = '.t3js-extensionConfiguration-form',
34
+ searchInput = '.t3js-extensionConfiguration-search'
35
+ }
36
+
37
+ type ExtensionConfigurationWrittenResponse = {
38
+ status: MessageInterface[],
39
+ success: boolean,
40
+ };
41
+
42
+ /**
43
+ * Module: @typo3/install/module/extension-configuration
44
+ */
45
+ class ExtensionConfiguration extends AbstractInteractableModule {
46
+ public override initialize(currentModal: ModalElement): void {
47
+ super.initialize(currentModal);
48
+ this.getContent();
49
+
50
+ // Focus search field on certain user interactions
51
+ new RegularEvent('keydown', (event: KeyboardEvent) => {
52
+ const searchInput = currentModal.querySelector<HTMLInputElement>(Identifiers.searchInput);
53
+ if (event.ctrlKey || event.metaKey) {
54
+ // Focus search field on ctrl-f
55
+ if (event.key === 'f' || event.key === 'F') {
56
+ event.preventDefault();
57
+ searchInput.focus();
58
+ }
59
+ } else if (event.key === KeyTypesEnum.ESCAPE) {
60
+ if (searchInput.value.trim() !== '') {
61
+ // Clear search on ESC key otherwise close the modal
62
+ event.preventDefault();
63
+ searchInput.value = '';
64
+ searchInput.focus();
65
+ }
66
+ }
67
+ }).bindTo(currentModal);
68
+
69
+ // Perform expand collapse on search matches
70
+ new DebounceEvent('input', (event: Event, target: HTMLInputElement): void => {
71
+ const typedQuery = target.value;
72
+ this.search(typedQuery);
73
+ }, 100).delegateTo(currentModal, Identifiers.searchInput);
74
+
75
+ new RegularEvent('change', (event: Event, target: HTMLInputElement): void => {
76
+ const typedQuery = target.value;
77
+ this.search(typedQuery);
78
+ }).delegateTo(currentModal, Identifiers.searchInput);
79
+
80
+ new RegularEvent('submit', (event: Event, target: HTMLFormElement): void => {
81
+ event.preventDefault();
82
+ this.write(target);
83
+ }).delegateTo(currentModal, Identifiers.formListener);
84
+ }
85
+
86
+ private search(typedQuery: string): void {
87
+ this.currentModal.querySelectorAll('.search-item').forEach((element: Element): void => {
88
+ if (typedQuery === '' || element.textContent.toLowerCase().trim().includes(typedQuery.toLowerCase())) {
89
+ element.classList.add('searchhit');
90
+ element.classList.remove('hidden');
91
+ } else {
92
+ element.classList.remove('searchhit');
93
+ element.classList.add('hidden');
94
+ }
95
+ });
96
+ this.currentModal.querySelectorAll('.searchhit').forEach((resultElement: HTMLElement) => {
97
+ Collapse.getOrCreateInstance(resultElement).show();
98
+ });
99
+ }
100
+
101
+ private getContent(): void {
102
+ const modalContent = this.getModalBody();
103
+ (new AjaxRequest(Router.getUrl('extensionConfigurationGetContent')))
104
+ .get({ cache: 'no-cache' })
105
+ .then(
106
+ async (response: AjaxResponse): Promise<void> => {
107
+ const data: ModuleLoadedResponse = await response.resolve();
108
+ if (data.success === true) {
109
+ modalContent.innerHTML = data.html;
110
+ (modalContent.querySelector(Identifiers.searchInput) as HTMLInputElement).clearable();
111
+ this.initializeWrap();
112
+ this.initializeColorPicker();
113
+ }
114
+ },
115
+ (error: AjaxResponse): void => {
116
+ Router.handleAjaxError(error, modalContent);
117
+ }
118
+ );
119
+ }
120
+
121
+ private initializeColorPicker(): void {
122
+ const isInIframe = window.location !== window.parent.location;
123
+ if (isInIframe) {
124
+ topLevelModuleImport('@typo3/backend/color-picker.js');
125
+ } else {
126
+ import('@typo3/backend/color-picker');
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Submit the form and show the result message
132
+ */
133
+ private write(form: HTMLFormElement): void {
134
+ const modalContent = this.getModalBody();
135
+ const executeToken = this.getModuleContent().dataset.extensionConfigurationWriteToken;
136
+ const extensionConfiguration: Record<string, string> = {};
137
+ for (const [name, value] of new FormData(form)) {
138
+ extensionConfiguration[name] = value.toString();
139
+ }
140
+
141
+ (new AjaxRequest(Router.getUrl()))
142
+ .post({
143
+ install: {
144
+ token: executeToken,
145
+ action: 'extensionConfigurationWrite',
146
+ extensionKey: form.dataset.extensionKey,
147
+ extensionConfiguration: extensionConfiguration,
148
+ },
149
+ })
150
+ .then(
151
+ async (response: AjaxResponse): Promise<void> => {
152
+ const data: ExtensionConfigurationWrittenResponse = await response.resolve();
153
+ if (data.success === true && Array.isArray(data.status)) {
154
+ data.status.forEach((element: MessageInterface): void => {
155
+ Notification.showMessage(element.title, element.message, element.severity);
156
+ });
157
+ if (document.body.dataset.context === 'backend') {
158
+ ModuleMenu.App.refreshMenu();
159
+ }
160
+ } else {
161
+ Notification.error('Something went wrong', 'The request was not processed successfully. Please check the browser\'s console and TYPO3\'s log.');
162
+ }
163
+ },
164
+ (error: AjaxResponse): void => {
165
+ Router.handleAjaxError(error, modalContent);
166
+ }
167
+ );
168
+ }
169
+
170
+ /**
171
+ * configuration properties
172
+ */
173
+ private initializeWrap(): void {
174
+ const isInIframe = window.location !== window.parent.location;
175
+ if (isInIframe) {
176
+ topLevelModuleImport('@typo3/install/renderable/wrap-group.js');
177
+ topLevelModuleImport('@typo3/install/renderable/offset-group.js');
178
+ }
179
+
180
+ this.currentModal.querySelectorAll('.t3js-emconf-offset').forEach((element: HTMLInputElement): void => {
181
+ const parent = element.parentElement;
182
+
183
+ element.setAttribute('data-offsetfield-x', '#' + element.id + '_offset_x');
184
+ element.setAttribute('data-offsetfield-y', '#' + element.id + '_offset_y');
185
+ element.classList.add('hidden');
186
+
187
+ const offsetGroup = parent.ownerDocument.createElement('typo3-install-offset-group');
188
+ offsetGroup.offsetId = element.id;
189
+ offsetGroup.values = element.value.split(',');
190
+
191
+ parent.appendChild(offsetGroup);
192
+
193
+ parent.querySelectorAll('.t3js-emconf-offsetfield').forEach((offsetField: HTMLInputElement) => {
194
+ new RegularEvent('keyup', (event: KeyboardEvent) => {
195
+ const target = parent.querySelector<HTMLInputElement>((event.currentTarget as HTMLElement).dataset.target);
196
+ target.value = parent.querySelector<HTMLInputElement>(target.dataset.offsetfieldX).value + ',' + parent.querySelector<HTMLInputElement>(target.dataset.offsetfieldY).value;
197
+ }).bindTo(offsetField);
198
+ });
199
+ });
200
+
201
+ this.currentModal.querySelectorAll('.t3js-emconf-wrap').forEach((element: HTMLInputElement): void => {
202
+ const parent = element.parentElement;
203
+
204
+ element.setAttribute('data-wrapfield-start', '#' + element.id + '_wrap_start');
205
+ element.setAttribute('data-wrapfield-end', '#' + element.id + '_wrap_end');
206
+ element.classList.add('hidden');
207
+
208
+ const offsetGroup = parent.ownerDocument.createElement('typo3-install-wrap-group');
209
+ offsetGroup.wrapId = element.id;
210
+ offsetGroup.values = element.value.split('|');
211
+
212
+ parent.appendChild(offsetGroup);
213
+
214
+ parent.querySelectorAll('.t3js-emconf-wrapfield').forEach((wrapField: HTMLInputElement) => {
215
+ new RegularEvent('keyup', (event: KeyboardEvent) => {
216
+ const target = parent.querySelector<HTMLInputElement>((event.currentTarget as HTMLElement).dataset.target);
217
+ target.value = parent.querySelector<HTMLInputElement>(target.dataset.wrapfieldStart).value + '|' + parent.querySelector<HTMLInputElement>(target.dataset.wrapfieldEnd).value;
218
+ }).bindTo(wrapField);
219
+ });
220
+ });
221
+ }
222
+ }
223
+
224
+ export default new ExtensionConfiguration();
@@ -0,0 +1,102 @@
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
+ import { AbstractInteractableModule, type ModuleLoadedResponseWithButtons } from '../abstract-interactable-module';
15
+ import Modal from '@typo3/backend/modal';
16
+ import Notification from '@typo3/backend/notification';
17
+ import AjaxRequest from '@typo3/core/ajax/ajax-request';
18
+ import Router from '../../router';
19
+ import RegularEvent from '@typo3/core/event/regular-event';
20
+ import type { AjaxResponse } from '@typo3/core/ajax/ajax-response';
21
+ import type { ModalElement } from '@typo3/backend/modal';
22
+ import type MessageInterface from '@typo3/install/message-interface';
23
+
24
+ enum Identifiers {
25
+ saveTrigger = '.t3js-features-save'
26
+ }
27
+
28
+ type FeaturesWrittenResponse = {
29
+ status: MessageInterface[],
30
+ success: boolean,
31
+ };
32
+
33
+ /**
34
+ * Module: @typo3/install/module/features
35
+ */
36
+ class Features extends AbstractInteractableModule {
37
+ public override initialize(currentModal: ModalElement): void {
38
+ super.initialize(currentModal);
39
+ this.getContent();
40
+
41
+ new RegularEvent('click', (event: Event): void => {
42
+ event.preventDefault();
43
+ this.save();
44
+ }).delegateTo(currentModal, Identifiers.saveTrigger);
45
+ }
46
+
47
+ private getContent(): void {
48
+ const modalContent = this.getModalBody();
49
+ (new AjaxRequest(Router.getUrl('featuresGetContent')))
50
+ .get({ cache: 'no-cache' })
51
+ .then(
52
+ async (response: AjaxResponse): Promise<void> => {
53
+ const data: ModuleLoadedResponseWithButtons = await response.resolve();
54
+ if (data.success === true && data.html !== 'undefined' && data.html.length > 0) {
55
+ modalContent.innerHTML = data.html;
56
+ Modal.setButtons(data.buttons);
57
+ } else {
58
+ Notification.error('Something went wrong', 'The request was not processed successfully. Please check the browser\'s console and TYPO3\'s log.');
59
+ }
60
+ },
61
+ (error: AjaxResponse): void => {
62
+ Router.handleAjaxError(error, modalContent);
63
+ }
64
+ );
65
+ }
66
+
67
+ private save(): void {
68
+ this.setModalButtonsState(false);
69
+
70
+ const modalContent = this.getModalBody();
71
+ const executeToken = this.getModuleContent().dataset.featuresSaveToken;
72
+ const postData: Record<string, string> = {};
73
+ const formData = new FormData(this.findInModal('form'));
74
+ for (const [name, value] of formData) {
75
+ postData[name] = value.toString();
76
+ }
77
+ postData['install[action]'] = 'featuresSave';
78
+ postData['install[token]'] = executeToken;
79
+ (new AjaxRequest(Router.getUrl()))
80
+ .post(postData)
81
+ .then(
82
+ async (response: AjaxResponse): Promise<void> => {
83
+ const data: FeaturesWrittenResponse = await response.resolve();
84
+ if (data.success === true && Array.isArray(data.status)) {
85
+ data.status.forEach((element: MessageInterface): void => {
86
+ Notification.showMessage(element.title, element.message, element.severity);
87
+ });
88
+ this.getContent();
89
+ } else {
90
+ Notification.error('Something went wrong', 'The request was not processed successfully. Please check the browser\'s console and TYPO3\'s log.');
91
+ }
92
+ },
93
+ (error: AjaxResponse): void => {
94
+ Router.handleAjaxError(error, modalContent);
95
+ }
96
+ ).finally((): void => {
97
+ this.setModalButtonsState(true);
98
+ });
99
+ }
100
+ }
101
+
102
+ export default new Features();
@@ -0,0 +1,316 @@
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
+ import 'bootstrap';
15
+ import { Collapse } from 'bootstrap';
16
+ import '../../renderable/clearable';
17
+ import { AbstractInteractableModule, type ModuleLoadedResponseWithButtons } from '../abstract-interactable-module';
18
+ import Modal from '@typo3/backend/modal';
19
+ import Notification from '@typo3/backend/notification';
20
+ import AjaxRequest from '@typo3/core/ajax/ajax-request';
21
+ import Router from '../../router';
22
+ import RegularEvent from '@typo3/core/event/regular-event';
23
+ import { KeyTypesEnum } from '@typo3/backend/enum/key-types';
24
+ import type { AjaxResponse } from '@typo3/core/ajax/ajax-response';
25
+ import type { ModalElement } from '@typo3/backend/modal';
26
+ import type MessageInterface from '@typo3/install/message-interface';
27
+
28
+ enum Identifiers {
29
+ item = '.t3js-localConfiguration-item',
30
+ toggleAllTrigger = '.t3js-localConfiguration-toggleAll',
31
+ writeTrigger = '.t3js-localConfiguration-write',
32
+ searchTrigger = '.t3js-localConfiguration-search',
33
+ createRowTrigger = '.t3js-localConfiguration-createRow',
34
+ removeRowTrigger = '.t3js-localConfiguration-removeRow',
35
+ configurationItem = '.t3js-localConfiguration-configuration-item',
36
+ }
37
+
38
+ type LocalConfigurationWrittenResponse = {
39
+ status: MessageInterface[],
40
+ success: boolean,
41
+ };
42
+
43
+ /**
44
+ * Module: @typo3/install/module/local-configuration
45
+ */
46
+ class LocalConfiguration extends AbstractInteractableModule {
47
+ private searchInput: HTMLInputElement;
48
+ private toggleAll: boolean = false;
49
+
50
+ public override initialize(currentModal: ModalElement): void {
51
+ super.initialize(currentModal);
52
+ this.getContent();
53
+
54
+ // Write out new settings
55
+ new RegularEvent('click', (event: Event): void => {
56
+ event.preventDefault();
57
+ this.write();
58
+ }).delegateTo(currentModal, Identifiers.writeTrigger);
59
+
60
+ // Expand / collapse "Toggle all" button
61
+ new RegularEvent('click', (): void => {
62
+ const modalContent = this.getModalBody();
63
+ const panels = modalContent.querySelectorAll<HTMLElement>('.panel-collapse');
64
+ this.toggleAll = !this.toggleAll;
65
+ const action = this.toggleAll ? 'show' : 'hide';
66
+ panels.forEach((panel: HTMLElement) => {
67
+ const toggleButton: HTMLElement = modalContent.querySelector(`[data-bs-target="#${panel.id}"]`);
68
+ if (toggleButton) {
69
+ toggleButton.classList.toggle('collapsed', !this.toggleAll);
70
+ }
71
+ Collapse.getOrCreateInstance(panel)[action]();
72
+ });
73
+ }).delegateTo(currentModal, Identifiers.toggleAllTrigger);
74
+
75
+ // Focus search field on certain user interactions
76
+ new RegularEvent('keydown', (event: KeyboardEvent) => {
77
+ const searchInput = currentModal.querySelector<HTMLInputElement>(Identifiers.searchTrigger);
78
+ if (event.ctrlKey || event.metaKey) {
79
+ // Focus search field on ctrl-f
80
+ if (event.key === 'f' || event.key === 'F') {
81
+ event.preventDefault();
82
+ searchInput.focus();
83
+ }
84
+ } else if (event.key === KeyTypesEnum.ESCAPE) {
85
+ if (searchInput.value.trim() !== '') {
86
+ // Clear search on ESC key otherwise close the modal
87
+ event.preventDefault();
88
+ searchInput.value = '';
89
+ searchInput.focus();
90
+ }
91
+ }
92
+ }).bindTo(currentModal);
93
+
94
+ // Perform expand collapse on search matches
95
+ new RegularEvent('input', (event: Event, target: HTMLInputElement): void => {
96
+ const typedQuery = target.value;
97
+ this.search(typedQuery);
98
+ }).delegateTo(currentModal, Identifiers.searchTrigger);
99
+
100
+ new RegularEvent('change', (event: Event, target: HTMLInputElement): void => {
101
+ const typedQuery = target.value;
102
+ this.search(typedQuery);
103
+ }).delegateTo(currentModal, Identifiers.searchTrigger);
104
+
105
+ // Remove a cloned row
106
+ new RegularEvent('click', (event: Event, target: HTMLInputElement): void => {
107
+ event.preventDefault();
108
+ const row = target.closest(Identifiers.configurationItem);
109
+ row?.remove();
110
+ }).delegateTo(currentModal, Identifiers.removeRowTrigger);
111
+
112
+ // Add a fresh clone row
113
+ new RegularEvent('click', (event: Event, target: HTMLInputElement): void => {
114
+ event.preventDefault();
115
+
116
+ const container = target.closest('.t3js-configuration-list') as HTMLElement|null;
117
+ if (container === null) {
118
+ return;
119
+ }
120
+
121
+ const querySelectorLast = (selector: string): HTMLElement|null => {
122
+ const elements = container.querySelectorAll(selector);
123
+ return elements.item(elements.length - 1) as HTMLElement|null;
124
+ };
125
+
126
+ // Iterate all rows to find empty inputs per row
127
+ const rows = container.querySelectorAll(Identifiers.configurationItem);
128
+ for (const row of rows) {
129
+ const inputs = row.querySelectorAll('input[type="text"]') as NodeListOf<HTMLInputElement>;
130
+ let arrayKey;
131
+ let arrayValue;
132
+ if (container.dataset.type === 'map') {
133
+ arrayKey = inputs.item(0).value.trim();
134
+ arrayValue = inputs.item(1).value.trim();
135
+ } else if (container.dataset.type === 'element-list') {
136
+ arrayKey = undefined;
137
+ arrayValue = inputs[0].value.trim();
138
+ } else {
139
+ return;
140
+ }
141
+
142
+ // Skip if map is lacking key/value, or element-list is lacking value
143
+ // @todo: this doesn't allow empty values when using `map` – on purpose?
144
+ if (arrayKey === '' || arrayValue === '') {
145
+ for (const input of inputs) {
146
+ if (input.value === '') {
147
+ // Found an empty input, focus it
148
+ input.focus();
149
+ return;
150
+ }
151
+ }
152
+ }
153
+ }
154
+
155
+ const rowTemplate = (container.querySelector('#' + container.dataset.namespace) as HTMLTemplateElement).content.cloneNode(true) as DocumentFragment;
156
+ container.querySelector('.configuration-map-item-collection').append(rowTemplate);
157
+
158
+ const lastRow = querySelectorLast(Identifiers.configurationItem);
159
+ // Focus first input field in inserted item
160
+ (lastRow.querySelector('input[type="text"]') as HTMLInputElement|null)?.focus();
161
+ }).delegateTo(currentModal, Identifiers.createRowTrigger);
162
+ }
163
+
164
+ private search(typedQuery: string): void {
165
+ this.currentModal.querySelectorAll(Identifiers.item).forEach((element: HTMLElement): void => {
166
+ if (element.textContent.toLowerCase().trim().includes(typedQuery.toLowerCase())) {
167
+ element.classList.remove('hidden');
168
+ element.classList.add('searchhit');
169
+ } else {
170
+ element.classList.remove('searchhit');
171
+ element.classList.add('hidden');
172
+ }
173
+ });
174
+
175
+ this.currentModal.querySelectorAll('.searchhit').forEach((resultElement: HTMLElement) => {
176
+ const collapseElement = resultElement.closest('.panel-collapse');
177
+ Collapse.getOrCreateInstance(collapseElement).show();
178
+ });
179
+ }
180
+
181
+ private getContent(): void {
182
+ const modalContent = this.getModalBody();
183
+ (new AjaxRequest(Router.getUrl('localConfigurationGetContent')))
184
+ .get({ cache: 'no-cache' })
185
+ .then(
186
+ async (response: AjaxResponse): Promise<void> => {
187
+ const data: ModuleLoadedResponseWithButtons = await response.resolve();
188
+ if (data.success === true) {
189
+ modalContent.innerHTML = data.html;
190
+ Modal.setButtons(data.buttons);
191
+ this.searchInput = modalContent.querySelector<HTMLInputElement>((Identifiers.searchTrigger));
192
+ this.searchInput.clearable();
193
+ }
194
+ },
195
+ (error: AjaxResponse): void => {
196
+ Router.handleAjaxError(error, modalContent);
197
+ }
198
+ );
199
+ }
200
+
201
+ private write(): void {
202
+ this.setModalButtonsState(false);
203
+
204
+ const modalContent = this.getModalBody();
205
+ const executeToken = this.getModuleContent().dataset.localConfigurationWriteToken;
206
+ const configurationValues: Record<string, string | Record<string, string>> = {};
207
+ const collectedArrayKeys: Record<string, string[]> = {};
208
+ const collectedArrayValues: Record<string, string[]> = {};
209
+ this.currentModal.querySelectorAll('.t3js-localConfiguration-pathValue').forEach((element: HTMLInputElement): void => {
210
+ if (element.type === 'checkbox') {
211
+ if (element.checked) {
212
+ configurationValues[element.dataset.path] = '1';
213
+ } else {
214
+ configurationValues[element.dataset.path] = '0';
215
+ }
216
+ } else {
217
+ if (element.dataset.valuetype === 'map' || element.dataset.valuetype === 'element-list') {
218
+ // Special type "data-valuetype='map|element-list'" found.
219
+ // "map": has 'speaking' key and a value
220
+ // "element-list": only the value counts, the key is just a running numerical index
221
+ // Note that "array" is a regular input string value that is later exploded and NOT part
222
+ // of this code fork. The type "list" is a string-only listing NOT getting exploded.
223
+ // We want to convert these pairs:
224
+ // <input type="text" name="/GFX/someKey/key[]" value="myKey">
225
+ // <input type="text" name="/GFX/someKey/value[]" value="myValue">
226
+ // <input type="text" name="/GFX/someKey/key[]" value="anotherKey">
227
+ // <input type="text" name="/GFX/someKey/value[]" value="anotherValue">
228
+ // into:
229
+ // configurationValues[GFX/someKey][myKey] = myValue
230
+ // configurationValues[GFX/someKey][anotherKey] = anotherValue
231
+ // This is done with a temporary helper structure of collectedArrayKeys+collectedArrayValues,
232
+ // indexed by their main key ("GFX/someKey") so they can later be easily piped into it as JavaScript array.
233
+
234
+ // The "value[]" portion is always required (for map+list), but "key[]" can be optional (only needed for map)
235
+ if (element.dataset.path!.includes('/key[]') && element.value !== '') {
236
+ const itemArrayPath = element.dataset.path!.replace('/key[]', '');
237
+ if (collectedArrayKeys[itemArrayPath] === undefined) {
238
+ collectedArrayKeys[itemArrayPath] = [];
239
+ }
240
+ collectedArrayKeys[itemArrayPath].push(element.value);
241
+ } else if (element.dataset.path!.includes('/value[]') && element.value !== '') {
242
+ const itemArrayPath = element.dataset.path!.replace('/value[]', '');
243
+ if (collectedArrayValues[itemArrayPath] === undefined) {
244
+ collectedArrayValues[itemArrayPath] = [];
245
+ }
246
+ collectedArrayValues[itemArrayPath].push(element.value);
247
+ }
248
+ } else {
249
+ // Regular input string values.
250
+ configurationValues[element.dataset.path] = element.value;
251
+ }
252
+ }
253
+ });
254
+
255
+ // Now iterate the collectedArrayValues and collectedArrayKeys (map needs key+value, list only values).
256
+ for (const itemArrayPath in collectedArrayValues) {
257
+ if (Object.prototype.hasOwnProperty.call(collectedArrayValues, itemArrayPath)) {
258
+ // Create a properly typed object for the collection
259
+ if (configurationValues[itemArrayPath] === undefined) {
260
+ configurationValues[itemArrayPath] = {};
261
+ }
262
+
263
+ // Ensure configurationValues[itemArrayPath] is treated as a Record.
264
+ // Record keys are sorted, and configuration values are also persisted with sorting.
265
+ const configObject = configurationValues[itemArrayPath] as Record<string, string>;
266
+
267
+ // Iterate by index, not by keys array values
268
+ collectedArrayValues[itemArrayPath].forEach((arrayValue, index) => {
269
+ // Ensure we really have that paired collectedArrayValues[] value corresponding to the same key for maps.
270
+ // Empty key+value pairs are skipped (removed from config).
271
+ // Also, element-list is evaluated for the cases collectedArrayKeys is not set.
272
+ if (arrayValue === '') {
273
+ return;
274
+ }
275
+
276
+ if (collectedArrayKeys[itemArrayPath]
277
+ && collectedArrayKeys[itemArrayPath][index] !== undefined
278
+ && collectedArrayKeys[itemArrayPath][index] !== ''
279
+ ) {
280
+ // Case "map"
281
+ // Now populate configurationValues[itemArrayPath][myKey] = myValue
282
+ const arrayKey = collectedArrayKeys[itemArrayPath][index];
283
+ configObject[arrayKey] = arrayValue;
284
+ } else {
285
+ // Case "list"
286
+ // Now populate configurationValues[itemArrayPath][numericalKey] = myValue
287
+ configObject[index] = arrayValue;
288
+ }
289
+ });
290
+ }
291
+ }
292
+
293
+ (new AjaxRequest(Router.getUrl())).post({
294
+ install: {
295
+ action: 'localConfigurationWrite',
296
+ token: executeToken,
297
+ configurationValues: configurationValues,
298
+ },
299
+ }).then(async (response: AjaxResponse): Promise<void> => {
300
+ const data: LocalConfigurationWrittenResponse = await response.resolve();
301
+ if (data.success === true && Array.isArray(data.status)) {
302
+ data.status.forEach((element: MessageInterface): void => {
303
+ Notification.showMessage(element.title, element.message, element.severity);
304
+ });
305
+ } else {
306
+ Notification.error('Something went wrong', 'The request was not processed successfully. Please check the browser\'s console and TYPO3\'s log.');
307
+ }
308
+ }, (error: AjaxResponse): void => {
309
+ Router.handleAjaxError(error, modalContent);
310
+ }).finally((): void => {
311
+ this.setModalButtonsState(true);
312
+ });
313
+ }
314
+ }
315
+
316
+ export default new LocalConfiguration();