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,1230 @@
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 { MessageUtility } from '../../utility/message-utility';
15
+ import { AjaxDispatcher } from './../inline-relation/ajax-dispatcher';
16
+ import DocumentService from '@typo3/core/document-service';
17
+ import NProgress from 'nprogress';
18
+ import Sortable from 'sortablejs';
19
+ import FormEngine from '@typo3/backend/form-engine';
20
+ import FormEngineValidation from '@typo3/backend/form-engine-validation';
21
+ import Icons from '../../icons';
22
+ import InfoWindow from '../../info-window';
23
+ import Modal from '../../modal';
24
+ import Notification from '../../notification';
25
+ import RegularEvent from '@typo3/core/event/regular-event';
26
+ import Severity from '../../severity';
27
+ import Utility from '../../utility';
28
+ import { selector } from '@typo3/core/literals';
29
+ import type AjaxRequest from '@typo3/core/ajax/ajax-request';
30
+ import type { InlineResponseInterface } from './../inline-relation/inline-response-interface';
31
+
32
+ enum Selectors {
33
+ toggleSelector = '[data-bs-toggle="formengine-inline"]',
34
+ controlSectionSelector = '.t3js-formengine-irre-control',
35
+ createNewRecordButtonSelector = '.t3js-create-new-button',
36
+ createNewRecordBySelectorSelector = '.t3js-create-new-selector',
37
+ deleteRecordButtonSelector = '.t3js-editform-delete-inline-record',
38
+ enableDisableRecordButtonSelector = '.t3js-toggle-visibility-button',
39
+ infoWindowButton = '[data-action="infowindow"]',
40
+ synchronizeLocalizeRecordButtonSelector = '.t3js-synchronizelocalize-button',
41
+ uniqueValueSelectors = 'select.t3js-inline-unique',
42
+ revertUniqueness = '.t3js-revert-unique',
43
+ controlContainer = '.t3js-inline-controls',
44
+ }
45
+
46
+ enum States {
47
+ new = 'inlineIsNewRecord',
48
+ visible = 'panel-visible',
49
+ collapsed = 'panel-collapsed',
50
+ notLoaded = 't3js-not-loaded',
51
+ }
52
+
53
+ enum Separators {
54
+ structureSeparator = '-',
55
+ }
56
+
57
+ enum SortDirections {
58
+ DOWN = 'down',
59
+ UP = 'up',
60
+ }
61
+
62
+ interface RequestQueue {
63
+ [key: string]: AjaxRequest;
64
+ }
65
+
66
+ interface ProgressQueue {
67
+ [key: string]: any;
68
+ }
69
+
70
+ interface Appearance {
71
+ expandSingle?: boolean;
72
+ useSortable?: boolean;
73
+ }
74
+
75
+ export interface UniqueDefinition {
76
+ elTable: string;
77
+ field: string;
78
+ max: number;
79
+ possible: Record<string, string>;
80
+ selector: string;
81
+ table: string;
82
+ type: string;
83
+ used: UniqueDefinitionCollection;
84
+ }
85
+
86
+ interface UniqueDefinitionCollection {
87
+ [key: string]: UniqueDefinitionUsed;
88
+ }
89
+
90
+ interface UniqueDefinitionUsed {
91
+ table: string;
92
+ uid: string | number;
93
+ }
94
+
95
+ class InlineControlContainer {
96
+ private container: HTMLElement = null;
97
+ private ajaxDispatcher: AjaxDispatcher = null;
98
+ private appearance: Appearance = null;
99
+ private requestQueue: RequestQueue = {};
100
+ private progressQueue: ProgressQueue = {};
101
+ private readonly noTitleString: string = (TYPO3.lang ? TYPO3.lang['FormEngine.noRecordTitle'] : '[No title]');
102
+
103
+ /**
104
+ * @param {string} elementId
105
+ */
106
+ constructor(elementId: string) {
107
+ DocumentService.ready().then((document: Document): void => {
108
+ this.container = <HTMLElement>document.getElementById(elementId);
109
+ this.ajaxDispatcher = new AjaxDispatcher(this.container.dataset.objectGroup);
110
+
111
+ this.registerEvents();
112
+ });
113
+ }
114
+
115
+ /**
116
+ * @param {string} objectId
117
+ * @return HTMLDivElement
118
+ */
119
+ private static getInlineRecordContainer(objectId: string): HTMLDivElement {
120
+ return <HTMLDivElement>document.querySelector(selector`[data-object-id="${objectId}"]`);
121
+ }
122
+
123
+ /**
124
+ * @param {string} objectId
125
+ * @return HTMLButtonElement
126
+ */
127
+ private static getCollapseButton(objectId: string): HTMLButtonElement {
128
+ return <HTMLButtonElement>document.querySelector(selector`[aria-controls="${objectId}_fields"]`);
129
+ }
130
+
131
+ /**
132
+ * @param {string} objectId
133
+ */
134
+ private static toggleElement(objectId: string): void {
135
+ const recordContainer = InlineControlContainer.getInlineRecordContainer(objectId);
136
+ if (recordContainer.classList.contains(States.collapsed)) {
137
+ InlineControlContainer.expandElement(recordContainer, objectId);
138
+ } else {
139
+ InlineControlContainer.collapseElement(recordContainer, objectId);
140
+ }
141
+ }
142
+
143
+ /**
144
+ * @param {HTMLDivElement} recordContainer
145
+ * @param {string} objectId
146
+ */
147
+ private static collapseElement(recordContainer: HTMLDivElement, objectId: string): void {
148
+ const collapseButton = InlineControlContainer.getCollapseButton(objectId);
149
+ recordContainer.classList.remove(States.visible);
150
+ recordContainer.classList.add(States.collapsed);
151
+ collapseButton.setAttribute('aria-expanded', 'false');
152
+ }
153
+
154
+ /**
155
+ * @param {HTMLDivElement} recordContainer
156
+ * @param {string} objectId
157
+ */
158
+ private static expandElement(recordContainer: HTMLDivElement, objectId: string): void {
159
+ const collapseButton = InlineControlContainer.getCollapseButton(objectId);
160
+ recordContainer.classList.remove(States.collapsed);
161
+ recordContainer.classList.add(States.visible);
162
+ collapseButton.setAttribute('aria-expanded', 'true');
163
+ }
164
+
165
+ /**
166
+ * @param {string} objectId
167
+ * @return boolean
168
+ */
169
+ private static isNewRecord(objectId: string): boolean {
170
+ const recordContainer = InlineControlContainer.getInlineRecordContainer(objectId);
171
+ return recordContainer.classList.contains(States.new);
172
+ }
173
+
174
+ /**
175
+ * @param {string} objectId
176
+ * @param {boolean} value
177
+ */
178
+ private static updateExpandedCollapsedStateLocally(objectId: string, value: boolean): void {
179
+ const recordContainer = InlineControlContainer.getInlineRecordContainer(objectId);
180
+
181
+ const ucName = 'uc[inlineView]'
182
+ + '[' + recordContainer.dataset.topmostParentTable + ']'
183
+ + '[' + recordContainer.dataset.topmostParentUid + ']'
184
+ + recordContainer.dataset.fieldName;
185
+ const ucFormObj = document.getElementsByName(ucName);
186
+
187
+ if (ucFormObj.length) {
188
+ (<HTMLInputElement>ucFormObj[0]).value = value ? '1' : '0';
189
+ }
190
+ }
191
+
192
+ /**
193
+ * @param {UniqueDefinitionCollection} hashmap
194
+ */
195
+ private static getValuesFromHashMap(hashmap: UniqueDefinitionCollection): Array<any> {
196
+ return Object.keys(hashmap).map((key: string) => hashmap[key]);
197
+ }
198
+
199
+ private static selectOptionValueExists(selectElement: HTMLSelectElement, value: string): boolean {
200
+ return selectElement.querySelector(selector`option[value="${value}"]`) !== null;
201
+ }
202
+
203
+ /**
204
+ * @param {HTMLSelectElement} selectElement
205
+ * @param {string} value
206
+ */
207
+ private static removeSelectOptionByValue(selectElement: HTMLSelectElement, value: string): void {
208
+ const option = selectElement.querySelector(selector`option[value="${value}"]`);
209
+ if (option !== null) {
210
+ option.remove();
211
+ }
212
+ }
213
+
214
+ /**
215
+ * @param {HTMLSelectElement} selectElement
216
+ * @param {string} value
217
+ * @param {UniqueDefinition} unique
218
+ */
219
+ private static reAddSelectOption(selectElement: HTMLSelectElement, value: string, unique: UniqueDefinition): void {
220
+ if (InlineControlContainer.selectOptionValueExists(selectElement, value)) {
221
+ return;
222
+ }
223
+
224
+ const options: NodeListOf<HTMLOptionElement> = selectElement.querySelectorAll('option');
225
+ let index: number = -1;
226
+
227
+ for (const possibleValue of Object.keys(unique.possible)) {
228
+ if (possibleValue === value) {
229
+ break;
230
+ }
231
+
232
+ for (let k = 0; k < options.length; ++k) {
233
+ const option = options[k];
234
+ if (option.value === possibleValue) {
235
+ index = k;
236
+ break;
237
+ }
238
+ }
239
+ }
240
+
241
+ if (index === -1) {
242
+ index = 0;
243
+ } else if (index < options.length) {
244
+ index++;
245
+ }
246
+ // recreate the <option> tag
247
+ const readdOption = document.createElement('option');
248
+ readdOption.text = unique.possible[value];
249
+ readdOption.value = value;
250
+ // add the <option> at the right position
251
+ selectElement.insertBefore(readdOption, selectElement.options[index]);
252
+ }
253
+
254
+ private registerEvents(): void {
255
+ this.registerInfoButton();
256
+ this.registerSort();
257
+ this.registerCreateRecordButton();
258
+ this.registerEnableDisableButton();
259
+ this.registerDeleteButton();
260
+ this.registerSynchronizeLocalize();
261
+ this.registerRevertUniquenessAction();
262
+ this.registerToggle();
263
+
264
+ this.registerCreateRecordBySelector();
265
+ this.registerUniqueSelectFieldChanged();
266
+
267
+ new RegularEvent('message', this.handlePostMessage).bindTo(window);
268
+
269
+ if (this.getAppearance().useSortable) {
270
+ const recordListContainer = <HTMLDivElement>document.getElementById(this.container.getAttribute('id') + '_records');
271
+ // tslint:disable-next-line:no-unused-expression
272
+ new Sortable(recordListContainer, {
273
+ group: recordListContainer.getAttribute('id'),
274
+ handle: '.sortableHandle',
275
+ onSort: (): void => {
276
+ this.updateSorting();
277
+ },
278
+ });
279
+ }
280
+ }
281
+
282
+ private registerToggle(): void {
283
+ new RegularEvent('click', (e: Event, targetElement: HTMLElement): void => {
284
+ e.preventDefault();
285
+ e.stopImmediatePropagation();
286
+
287
+ this.loadRecordDetails(targetElement.closest(Selectors.toggleSelector).parentElement.dataset.objectId);
288
+ }).delegateTo(this.container, `${Selectors.toggleSelector} .form-irre-header-cell:not(${Selectors.controlSectionSelector}`);
289
+ }
290
+
291
+ private registerSort(): void {
292
+ new RegularEvent('click', (e: Event, targetElement: HTMLElement): void => {
293
+ e.preventDefault();
294
+ e.stopImmediatePropagation();
295
+
296
+ this.changeSortingByButton(
297
+ (<HTMLDivElement>targetElement.closest('[data-object-id]')).dataset.objectId,
298
+ <SortDirections>targetElement.dataset.direction,
299
+ );
300
+ }).delegateTo(this.container, Selectors.controlSectionSelector + ' [data-action="sort"]');
301
+ }
302
+
303
+ private registerCreateRecordButton(): void {
304
+ new RegularEvent('click', (e: Event, targetElement: HTMLElement): void => {
305
+ e.preventDefault();
306
+ e.stopImmediatePropagation();
307
+
308
+ if (this.isBelowMax()) {
309
+ let objectId = this.container.dataset.objectGroup;
310
+ if (typeof targetElement.dataset.recordUid !== 'undefined') {
311
+ objectId += Separators.structureSeparator + targetElement.dataset.recordUid;
312
+ }
313
+
314
+ this.importRecord([objectId, (this.container.querySelector(Selectors.createNewRecordBySelectorSelector) as HTMLInputElement)?.value], targetElement.dataset.recordUid ?? null);
315
+ }
316
+ }).delegateTo(this.container, Selectors.createNewRecordButtonSelector);
317
+ }
318
+
319
+ private registerCreateRecordBySelector(): void {
320
+ new RegularEvent('change', (e: Event, targetElement: HTMLElement): void => {
321
+ e.preventDefault();
322
+ e.stopImmediatePropagation();
323
+
324
+ const selectTarget = <HTMLSelectElement>targetElement;
325
+ const recordUid = selectTarget.options[selectTarget.selectedIndex].getAttribute('value');
326
+
327
+ this.importRecord([this.container.dataset.objectGroup, recordUid]);
328
+ }).delegateTo(this.container, Selectors.createNewRecordBySelectorSelector);
329
+ }
330
+
331
+ /**
332
+ * @param {MessageEvent} e
333
+ */
334
+ private readonly handlePostMessage = (e: MessageEvent): void => {
335
+ if (!MessageUtility.verifyOrigin(e.origin)) {
336
+ throw 'Denied message sent by ' + e.origin;
337
+ }
338
+
339
+ if (e.data.actionName === 'typo3:foreignRelation:insert') {
340
+ if (typeof e.data.objectGroup === 'undefined') {
341
+ throw 'No object group defined for message';
342
+ }
343
+
344
+ if (e.data.objectGroup !== this.container.dataset.objectGroup) {
345
+ // Received message isn't provisioned for current InlineControlContainer instance
346
+ return;
347
+ }
348
+
349
+ if (this.isUniqueElementUsed(parseInt(e.data.uid, 10), e.data.table)) {
350
+ Notification.error('There is already a relation to the selected element');
351
+ return;
352
+ }
353
+
354
+ this.importRecord([e.data.objectGroup, e.data.uid]).then((): void => {
355
+ if (e.source) {
356
+ const message = {
357
+ actionName: 'typo3:foreignRelation:inserted',
358
+ objectGroup: e.data.objectId,
359
+ table: e.data.table,
360
+ uid: e.data.uid,
361
+ };
362
+ MessageUtility.send(message, e.source as Window);
363
+ }
364
+ });
365
+ }
366
+
367
+ if (e.data.actionName === 'typo3:foreignRelation:delete') {
368
+ if (e.data.objectGroup !== this.container.dataset.objectGroup) {
369
+ // Received message isn't provisioned for current FilesContainer instance
370
+ return;
371
+ }
372
+
373
+ const forceDirectRemoval = e.data.directRemoval || false;
374
+ const objectId = [e.data.objectGroup, e.data.uid].join('-');
375
+ this.deleteRecord(objectId, forceDirectRemoval);
376
+ }
377
+ };
378
+
379
+ /**
380
+ * @param {string} uid
381
+ * @param {string} markup
382
+ * @param {string} afterUid
383
+ * @param {string} selectedValue
384
+ */
385
+ private createRecord(uid: string, markup: string, afterUid: string = null, selectedValue: string = null): void {
386
+ let objectId = this.container.dataset.objectGroup;
387
+ if (afterUid !== null) {
388
+ objectId += Separators.structureSeparator + afterUid;
389
+ }
390
+
391
+ if (afterUid !== null) {
392
+ InlineControlContainer.getInlineRecordContainer(objectId).insertAdjacentHTML('afterend', markup);
393
+ this.memorizeAddRecord(uid, afterUid, selectedValue);
394
+ } else {
395
+ document.getElementById(this.container.getAttribute('id') + '_records').insertAdjacentHTML('beforeend', markup);
396
+ this.memorizeAddRecord(uid, null, selectedValue);
397
+ }
398
+ }
399
+
400
+ /**
401
+ * @param {Array} params
402
+ * @param {string} afterUid
403
+ */
404
+ private async importRecord(params: Array<any>, afterUid?: string): Promise<void> {
405
+ return this.ajaxDispatcher.send(
406
+ this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint('record_inline_create')),
407
+ params,
408
+ ).then(async (response: InlineResponseInterface): Promise<void> => {
409
+ if (this.isBelowMax()) {
410
+ this.createRecord(
411
+ response.compilerInput.uid,
412
+ response.data,
413
+ typeof afterUid !== 'undefined' ? afterUid : null,
414
+ typeof response.compilerInput.childChildUid !== 'undefined' ? response.compilerInput.childChildUid : null,
415
+ );
416
+ }
417
+ });
418
+ }
419
+
420
+ private registerEnableDisableButton(): void {
421
+ new RegularEvent('click', (e: Event, target: HTMLElement): void => {
422
+ e.preventDefault();
423
+ e.stopImmediatePropagation();
424
+
425
+ const objectId = (<HTMLDivElement>target.closest('[data-object-id]')).dataset.objectId;
426
+ const recordContainer = InlineControlContainer.getInlineRecordContainer(objectId);
427
+ const hiddenFieldName = selector`data${recordContainer.dataset.fieldName}[${target.dataset.hiddenField}]`;
428
+ const hiddenValueCheckBox = <HTMLInputElement>document.querySelector('[data-formengine-input-name="' + hiddenFieldName + '"');
429
+ const hiddenValueInput = <HTMLInputElement>document.querySelector('[name="' + hiddenFieldName + '"');
430
+
431
+ if (hiddenValueCheckBox !== null && hiddenValueInput !== null) {
432
+ hiddenValueCheckBox.checked = !hiddenValueCheckBox.checked;
433
+ hiddenValueInput.value = hiddenValueCheckBox.checked ? '1' : '0';
434
+ FormEngineValidation.markFieldAsChanged(hiddenValueCheckBox);
435
+ }
436
+
437
+ const hiddenClass = 'panel-hidden';
438
+ const isHidden = recordContainer.classList.contains(hiddenClass);
439
+ let toggleIcon: string;
440
+
441
+ if (isHidden) {
442
+ toggleIcon = 'actions-edit-hide';
443
+ recordContainer.classList.remove(hiddenClass);
444
+ } else {
445
+ toggleIcon = 'actions-edit-unhide';
446
+ recordContainer.classList.add(hiddenClass);
447
+ }
448
+
449
+ Icons.getIcon(toggleIcon, Icons.sizes.small).then((markup: string): void => {
450
+ target.replaceChild(document.createRange().createContextualFragment(markup), target.querySelector('.t3js-icon'));
451
+ });
452
+ }).delegateTo(this.container, Selectors.enableDisableRecordButtonSelector);
453
+ }
454
+
455
+ private registerInfoButton(): void {
456
+ new RegularEvent('click', function(this: HTMLElement, e: Event): void {
457
+ e.preventDefault();
458
+ e.stopImmediatePropagation();
459
+
460
+ InfoWindow.showItem(this.dataset.infoTable, this.dataset.infoUid);
461
+ }).delegateTo(this.container, Selectors.infoWindowButton);
462
+ }
463
+
464
+ private registerDeleteButton(): void {
465
+ new RegularEvent('click', (e: Event, targetElement: HTMLElement): void => {
466
+ e.preventDefault();
467
+ e.stopImmediatePropagation();
468
+
469
+ const title = TYPO3.lang['label.confirm.delete_record.title'] || 'Delete this record?';
470
+ const content = (TYPO3.lang['label.confirm.delete_record.content'] || 'Are you sure you want to delete the record \'%s\'?').replace('%s', targetElement.dataset.recordInfo);
471
+ const modal = Modal.confirm(title, content, Severity.warning, [
472
+ {
473
+ text: TYPO3.lang['buttons.confirm.delete_record.no'] || 'Cancel',
474
+ active: true,
475
+ btnClass: 'btn-default',
476
+ name: 'no',
477
+ },
478
+ {
479
+ text: TYPO3.lang['buttons.confirm.delete_record.yes'] || 'Yes, delete this record',
480
+ btnClass: 'btn-warning',
481
+ name: 'yes',
482
+ },
483
+ ]);
484
+ modal.addEventListener('button.clicked', (modalEvent: Event): void => {
485
+ if ((<HTMLAnchorElement>modalEvent.target).name === 'yes') {
486
+ const objectId = (<HTMLDivElement>targetElement.closest('[data-object-id]')).dataset.objectId;
487
+ this.deleteRecord(objectId);
488
+ }
489
+
490
+ modal.hideModal();
491
+ });
492
+ }).delegateTo(this.container, Selectors.deleteRecordButtonSelector);
493
+ }
494
+
495
+ /**
496
+ * @param {Event} e
497
+ */
498
+ private registerSynchronizeLocalize(): void {
499
+ new RegularEvent('click', (e: Event, targetElement: HTMLElement): void => {
500
+ e.preventDefault();
501
+ e.stopImmediatePropagation();
502
+
503
+ this.ajaxDispatcher.send(
504
+ this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint('record_inline_synchronizelocalize')),
505
+ [this.container.dataset.objectGroup, targetElement.dataset.type],
506
+ ).then(async (response: InlineResponseInterface): Promise<void> => {
507
+ document.getElementById(this.container.getAttribute('id') + '_records').insertAdjacentHTML('beforeend', response.data);
508
+
509
+ const objectIdPrefix = this.container.dataset.objectGroup + Separators.structureSeparator;
510
+ for (const itemUid of response.compilerInput.delete) {
511
+ this.deleteRecord(objectIdPrefix + itemUid, true);
512
+ }
513
+
514
+ for (const item of Object.values(response.compilerInput.localize)) {
515
+ if (typeof item.remove !== 'undefined') {
516
+ const removableRecordContainer = InlineControlContainer.getInlineRecordContainer(objectIdPrefix + item.remove);
517
+ removableRecordContainer.parentElement.removeChild(removableRecordContainer);
518
+ }
519
+
520
+ this.memorizeAddRecord(item.uid, null, item.selectedValue);
521
+ }
522
+ });
523
+ }).delegateTo(this.container, Selectors.synchronizeLocalizeRecordButtonSelector);
524
+ }
525
+
526
+ private registerUniqueSelectFieldChanged(): void {
527
+ new RegularEvent('change', (e: Event, targetElement: HTMLElement): void => {
528
+ e.preventDefault();
529
+ e.stopImmediatePropagation();
530
+
531
+ const recordContainer = (<HTMLDivElement>targetElement.closest('[data-object-id]'));
532
+ if (recordContainer !== null) {
533
+ const objectId = recordContainer.dataset.objectId;
534
+ const objectUid = recordContainer.dataset.objectUid;
535
+ this.handleChangedField(<HTMLSelectElement>targetElement, objectId);
536
+
537
+ const formField = this.getFormFieldForElements();
538
+ if (formField === null) {
539
+ return;
540
+ }
541
+ this.updateUnique(<HTMLSelectElement>targetElement, formField, objectUid);
542
+ }
543
+ }).delegateTo(this.container, Selectors.uniqueValueSelectors);
544
+ }
545
+
546
+ private registerRevertUniquenessAction(): void {
547
+ new RegularEvent('click', (e: Event, targetElement: HTMLElement): void => {
548
+ e.preventDefault();
549
+ e.stopImmediatePropagation();
550
+
551
+ this.revertUnique(targetElement.dataset.uid);
552
+ }).delegateTo(this.container, Selectors.revertUniqueness);
553
+ }
554
+
555
+ /**
556
+ * @param {string} objectId
557
+ */
558
+ private loadRecordDetails(objectId: string): void {
559
+ const recordFieldsContainer = document.getElementById(objectId + '_fields');
560
+ const recordContainer = InlineControlContainer.getInlineRecordContainer(objectId);
561
+ const isLoading = typeof this.requestQueue[objectId] !== 'undefined';
562
+ const isLoaded = recordFieldsContainer !== null && !recordContainer.classList.contains(States.notLoaded);
563
+
564
+ if (!isLoaded) {
565
+ const progress = this.getProgress(objectId, recordContainer.dataset.objectIdHash);
566
+
567
+ if (!isLoading) {
568
+ const ajaxRequest = this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint('record_inline_details'));
569
+ const request = this.ajaxDispatcher.send(ajaxRequest, [objectId]);
570
+
571
+ request.then(async (response: InlineResponseInterface): Promise<void> => {
572
+ delete this.requestQueue[objectId];
573
+ delete this.progressQueue[objectId];
574
+
575
+ recordContainer.classList.remove(States.notLoaded);
576
+ recordFieldsContainer.innerHTML = response.data;
577
+ this.collapseExpandRecord(objectId);
578
+
579
+ progress.done();
580
+
581
+ FormEngine.reinitialize();
582
+ FormEngineValidation.initializeInputFields();
583
+ FormEngineValidation.validate(this.container);
584
+
585
+ if (this.hasObjectGroupDefinedUniqueConstraints()) {
586
+ const recordContainer = InlineControlContainer.getInlineRecordContainer(objectId);
587
+ this.removeUsed(recordContainer);
588
+ }
589
+ });
590
+
591
+ this.requestQueue[objectId] = ajaxRequest;
592
+ progress.start();
593
+ } else {
594
+ // Abort loading if collapsed again
595
+ this.requestQueue[objectId].abort();
596
+ delete this.requestQueue[objectId];
597
+ delete this.progressQueue[objectId];
598
+ progress.done();
599
+ }
600
+
601
+ return;
602
+ }
603
+
604
+ this.collapseExpandRecord(objectId);
605
+ }
606
+
607
+ /**
608
+ * Collapses or expands a record and stores the state either in a form field or directly in backend user's UC, depending
609
+ * on whether the record is new or already existing.
610
+ *
611
+ * @param {String} objectId
612
+ */
613
+ private collapseExpandRecord(objectId: string): void {
614
+ const recordElement = InlineControlContainer.getInlineRecordContainer(objectId);
615
+ const expandSingle = this.getAppearance().expandSingle === true;
616
+ const isCollapsed: boolean = recordElement.classList.contains(States.collapsed);
617
+ let collapse: Array<string> = [];
618
+ const expand: Array<string> = [];
619
+
620
+ if (expandSingle && isCollapsed) {
621
+ collapse = this.collapseAllRecords(recordElement.dataset.objectUid);
622
+ }
623
+
624
+ InlineControlContainer.toggleElement(objectId);
625
+
626
+ if (InlineControlContainer.isNewRecord(objectId)) {
627
+ InlineControlContainer.updateExpandedCollapsedStateLocally(objectId, isCollapsed);
628
+ } else if (isCollapsed) {
629
+ expand.push(recordElement.dataset.objectUid);
630
+ } else if (!isCollapsed) {
631
+ collapse.push(recordElement.dataset.objectUid);
632
+ }
633
+
634
+ this.ajaxDispatcher.send(
635
+ this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint('record_inline_expandcollapse')),
636
+ [objectId, expand.join(','), collapse.join(',')]
637
+ );
638
+ }
639
+
640
+ /**
641
+ * @param {string} newUid
642
+ * @param {string} afterUid
643
+ * @param {string} selectedValue
644
+ */
645
+ private memorizeAddRecord(newUid: string, afterUid: string = null, selectedValue: string = null): void {
646
+ const formField = this.getFormFieldForElements();
647
+ if (formField === null) {
648
+ return;
649
+ }
650
+
651
+ let records = Utility.trimExplode(',', (<HTMLInputElement>formField).value);
652
+ if (afterUid) {
653
+ const newRecords = [];
654
+ for (let i = 0; i < records.length; i++) {
655
+ if (records[i].length) {
656
+ newRecords.push(records[i]);
657
+ }
658
+ if (afterUid === records[i]) {
659
+ newRecords.push(newUid);
660
+ }
661
+ }
662
+ records = newRecords;
663
+ } else {
664
+ records.push(newUid);
665
+ }
666
+
667
+ (<HTMLInputElement>formField).value = records.join(',');
668
+ FormEngineValidation.markFieldAsChanged(formField);
669
+ document.dispatchEvent(new Event('change'));
670
+
671
+ this.redrawSortingButtons(this.container.dataset.objectGroup, records);
672
+ this.setUnique(newUid, selectedValue);
673
+
674
+ if (!this.isBelowMax()) {
675
+ this.toggleContainerControls(false);
676
+ }
677
+
678
+ FormEngine.reinitialize();
679
+ FormEngineValidation.initializeInputFields();
680
+ FormEngineValidation.validate(this.container);
681
+ }
682
+
683
+ /**
684
+ * @param {String} objectUid
685
+ * @return Array<string>
686
+ */
687
+ private memorizeRemoveRecord(objectUid: string): Array<string> {
688
+ const formField = this.getFormFieldForElements();
689
+ if (formField === null) {
690
+ return [];
691
+ }
692
+
693
+ const records = Utility.trimExplode(',', (<HTMLInputElement>formField).value);
694
+ const indexOfRemoveUid = records.indexOf(objectUid);
695
+ if (indexOfRemoveUid > -1) {
696
+ records.splice(indexOfRemoveUid, 1);
697
+
698
+ (<HTMLInputElement>formField).value = records.join(',');
699
+ FormEngineValidation.markFieldAsChanged(formField);
700
+ document.dispatchEvent(new Event('change'));
701
+
702
+ this.redrawSortingButtons(this.container.dataset.objectGroup, records);
703
+ }
704
+
705
+ return records;
706
+ }
707
+
708
+ /**
709
+ * @param {string} objectId
710
+ * @param {SortDirections} direction
711
+ */
712
+ private changeSortingByButton(objectId: string, direction: SortDirections): void {
713
+ const currentRecordContainer = InlineControlContainer.getInlineRecordContainer(objectId);
714
+ const recordUid = currentRecordContainer.dataset.objectUid;
715
+ const recordListContainer = <HTMLDivElement>document.getElementById(this.container.getAttribute('id') + '_records');
716
+ const records = Array.from(recordListContainer.children).map((child: HTMLElement) => child.dataset.objectUid);
717
+ const position = records.indexOf(recordUid);
718
+ let isChanged = false;
719
+
720
+ if (direction === SortDirections.UP && position > 0) {
721
+ records[position] = records[position - 1];
722
+ records[position - 1] = recordUid;
723
+ isChanged = true;
724
+ } else if (direction === SortDirections.DOWN && position < records.length - 1) {
725
+ records[position] = records[position + 1];
726
+ records[position + 1] = recordUid;
727
+ isChanged = true;
728
+ }
729
+
730
+ if (isChanged) {
731
+ const objectIdPrefix = this.container.dataset.objectGroup + Separators.structureSeparator;
732
+ const adjustment = direction === SortDirections.UP ? 1 : 0;
733
+ currentRecordContainer.parentElement.insertBefore(
734
+ InlineControlContainer.getInlineRecordContainer(objectIdPrefix + records[position - adjustment]),
735
+ InlineControlContainer.getInlineRecordContainer(objectIdPrefix + records[position + 1 - adjustment]),
736
+ );
737
+
738
+ this.updateSorting();
739
+ }
740
+ }
741
+
742
+ private updateSorting(): void {
743
+ const formField = this.getFormFieldForElements();
744
+ if (formField === null) {
745
+ return;
746
+ }
747
+
748
+ const recordListContainer = <HTMLDivElement>document.getElementById(this.container.getAttribute('id') + '_records');
749
+ const records = Array.from(recordListContainer.querySelectorAll(selector`[data-object-parent-group="${this.container.dataset.objectGroup}"][data-placeholder-record="0"]`))
750
+ .map((child: HTMLElement) => child.dataset.objectUid);
751
+
752
+ (<HTMLInputElement>formField).value = records.join(',');
753
+ FormEngineValidation.markFieldAsChanged(formField);
754
+ document.dispatchEvent(new Event('inline:sorting-changed'));
755
+ document.dispatchEvent(new Event('change'));
756
+
757
+ this.redrawSortingButtons(this.container.dataset.objectGroup, records);
758
+ }
759
+
760
+ /**
761
+ * @param {String} objectId
762
+ * @param {Boolean} forceDirectRemoval
763
+ */
764
+ private deleteRecord(objectId: string, forceDirectRemoval: boolean = false): void {
765
+ const recordContainer = InlineControlContainer.getInlineRecordContainer(objectId);
766
+ const objectUid = recordContainer.dataset.objectUid;
767
+
768
+ recordContainer.classList.add('t3js-inline-record-deleted');
769
+
770
+ if (!InlineControlContainer.isNewRecord(objectId) && !forceDirectRemoval) {
771
+ const deleteCommandInput = this.container.querySelector(selector`[name="cmd${recordContainer.dataset.fieldName}[delete]"]`);
772
+ deleteCommandInput.removeAttribute('disabled');
773
+
774
+ // Move input field to inline container so we can remove the record container
775
+ recordContainer.parentElement.insertAdjacentElement('afterbegin', deleteCommandInput);
776
+ }
777
+
778
+ new RegularEvent('transitionend', (): void => {
779
+ recordContainer.remove();
780
+ FormEngineValidation.validate(this.container);
781
+ }).bindTo(recordContainer);
782
+
783
+ this.revertUnique(objectUid);
784
+ this.memorizeRemoveRecord(objectUid);
785
+ recordContainer.classList.add('form-irre-object--deleted');
786
+
787
+ if (this.isBelowMax()) {
788
+ this.toggleContainerControls(true);
789
+ }
790
+ }
791
+
792
+ /**
793
+ * @param {boolean} visible
794
+ */
795
+ private toggleContainerControls(visible: boolean): void {
796
+ const controlContainer = this.container.querySelectorAll(
797
+ ':scope > ' + Selectors.controlContainer
798
+ );
799
+ controlContainer.forEach((container: HTMLElement): void => {
800
+ const controlContainerButtons = container.querySelectorAll('button, a');
801
+ controlContainerButtons.forEach((button: HTMLElement): void => {
802
+ button.style.display = visible ? null : 'none';
803
+ });
804
+ });
805
+ }
806
+
807
+ /**
808
+ * @param {string} objectId
809
+ * @param {string} objectIdHash
810
+ */
811
+ private getProgress(objectId: string, objectIdHash: string): any {
812
+ const headerIdentifier = '#' + objectIdHash + '_header';
813
+ let progress: any;
814
+
815
+ if (typeof this.progressQueue[objectId] !== 'undefined') {
816
+ progress = this.progressQueue[objectId];
817
+ } else {
818
+ progress = NProgress;
819
+ progress.configure({ parent: headerIdentifier, showSpinner: false });
820
+ this.progressQueue[objectId] = progress;
821
+ }
822
+
823
+ return progress;
824
+ }
825
+
826
+ /**
827
+ * @param {string} excludeUid
828
+ */
829
+ private collapseAllRecords(excludeUid: string): Array<string> {
830
+ const formField = this.getFormFieldForElements();
831
+ const collapse: Array<string> = [];
832
+
833
+ if (formField !== null) {
834
+ const records = Utility.trimExplode(',', (<HTMLInputElement>formField).value);
835
+ for (const recordUid of records) {
836
+ if (recordUid === excludeUid) {
837
+ continue;
838
+ }
839
+
840
+ const recordObjectId = this.container.dataset.objectGroup + Separators.structureSeparator + recordUid;
841
+ const recordContainer = InlineControlContainer.getInlineRecordContainer(recordObjectId);
842
+ if (recordContainer.classList.contains(States.visible)) {
843
+ InlineControlContainer.collapseElement(recordContainer, recordObjectId);
844
+
845
+ if (InlineControlContainer.isNewRecord(recordObjectId)) {
846
+ InlineControlContainer.updateExpandedCollapsedStateLocally(recordObjectId, false);
847
+ } else {
848
+ collapse.push(recordUid);
849
+ }
850
+ }
851
+ }
852
+ }
853
+
854
+ return collapse;
855
+ }
856
+
857
+ /**
858
+ * @return HTMLInputElement | void
859
+ */
860
+ private getFormFieldForElements(): HTMLInputElement | null {
861
+ const formFields = document.getElementsByName(this.container.dataset.formField);
862
+ if (formFields.length > 0) {
863
+ return <HTMLInputElement>formFields[0];
864
+ }
865
+
866
+ return null;
867
+ }
868
+
869
+ /**
870
+ * Redraws rhe sorting buttons of each record
871
+ *
872
+ * @param {string} objectId
873
+ * @param {Array<string>} records
874
+ */
875
+ private redrawSortingButtons(objectId: string, records: Array<string> = []): void {
876
+ if (records.length === 0) {
877
+ const formField = this.getFormFieldForElements();
878
+ if (formField !== null) {
879
+ records = Utility.trimExplode(',', (<HTMLInputElement>formField).value);
880
+ }
881
+ }
882
+
883
+ if (records.length === 0) {
884
+ return;
885
+ }
886
+
887
+ records.forEach((recordUid: string, index: number): void => {
888
+ const recordContainer = InlineControlContainer.getInlineRecordContainer(objectId + Separators.structureSeparator + recordUid);
889
+ const headerIdentifier = recordContainer.dataset.objectIdHash + '_header';
890
+ const headerElement = document.getElementById(headerIdentifier);
891
+ const sortUp = headerElement.querySelector(selector`[data-action="sort"][data-direction="${SortDirections.UP}"]`);
892
+
893
+ if (sortUp !== null) {
894
+ let iconIdentifier = 'actions-move-up';
895
+ if (index === 0) {
896
+ sortUp.classList.add('disabled');
897
+ iconIdentifier = 'empty-empty';
898
+ } else {
899
+ sortUp.classList.remove('disabled');
900
+ }
901
+ Icons.getIcon(iconIdentifier, Icons.sizes.small).then((markup: string): void => {
902
+ sortUp.replaceChild(document.createRange().createContextualFragment(markup), sortUp.querySelector('.t3js-icon'));
903
+ });
904
+ }
905
+
906
+ const sortDown = headerElement.querySelector(selector`[data-action="sort"][data-direction="${SortDirections.DOWN}"]`);
907
+ if (sortDown !== null) {
908
+ let iconIdentifier = 'actions-move-down';
909
+ if (index === records.length - 1) {
910
+ sortDown.classList.add('disabled');
911
+ iconIdentifier = 'empty-empty';
912
+ } else {
913
+ sortDown.classList.remove('disabled');
914
+ }
915
+ Icons.getIcon(iconIdentifier, Icons.sizes.small).then((markup: string): void => {
916
+ sortDown.replaceChild(document.createRange().createContextualFragment(markup), sortDown.querySelector('.t3js-icon'));
917
+ });
918
+ }
919
+ });
920
+ }
921
+
922
+ /**
923
+ * @return {boolean}
924
+ */
925
+ private isBelowMax(): boolean {
926
+ const formField = this.getFormFieldForElements();
927
+ if (formField === null) {
928
+ return true;
929
+ }
930
+
931
+ if (typeof TYPO3.settings.FormEngineInline.config[this.container.dataset.objectGroup] !== 'undefined') {
932
+ const records = Utility.trimExplode(',', (<HTMLInputElement>formField).value);
933
+ if (records.length >= TYPO3.settings.FormEngineInline.config[this.container.dataset.objectGroup].max) {
934
+ return false;
935
+ }
936
+
937
+ if (this.hasObjectGroupDefinedUniqueConstraints()) {
938
+ const unique = TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup];
939
+ if (unique.used.length >= unique.max && unique.max >= 0) {
940
+ return false;
941
+ }
942
+ }
943
+ }
944
+
945
+ return true;
946
+ }
947
+
948
+ /**
949
+ * @param {number} uid
950
+ * @param {string} table
951
+ */
952
+ private isUniqueElementUsed(uid: number, table: string): boolean {
953
+ if (!this.hasObjectGroupDefinedUniqueConstraints()) {
954
+ return false;
955
+ }
956
+
957
+ const unique: UniqueDefinition = TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup];
958
+ const values = InlineControlContainer.getValuesFromHashMap(unique.used);
959
+
960
+ if (unique.type === 'select' && values.indexOf(uid) !== -1) {
961
+ return true;
962
+ }
963
+
964
+ if (unique.type === 'groupdb') {
965
+ for (let i = values.length - 1; i >= 0; i--) {
966
+ // if the pair table:uid is already used:
967
+ if (values[i].table === table && values[i].uid === uid) {
968
+ return true;
969
+ }
970
+ }
971
+ }
972
+
973
+ return false;
974
+ }
975
+
976
+ /**
977
+ * @param {HTMLDivElement} recordContainer
978
+ */
979
+ private removeUsed(recordContainer: HTMLDivElement): void {
980
+ if (!this.hasObjectGroupDefinedUniqueConstraints()) {
981
+ return;
982
+ }
983
+
984
+ const unique: UniqueDefinition = TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup];
985
+ if (unique.type !== 'select') {
986
+ return;
987
+ }
988
+
989
+ const uniqueValueField = <HTMLSelectElement>recordContainer.querySelector(
990
+ '[name="data[' + unique.table + '][' + recordContainer.dataset.objectUid + '][' + unique.field + ']"]',
991
+ );
992
+ const values = InlineControlContainer.getValuesFromHashMap(unique.used);
993
+
994
+ if (uniqueValueField !== null) {
995
+ const selectedValue = uniqueValueField.options[uniqueValueField.selectedIndex].value;
996
+ for (const value of values) {
997
+ if (value !== selectedValue) {
998
+ InlineControlContainer.removeSelectOptionByValue(uniqueValueField, value);
999
+ }
1000
+ }
1001
+ }
1002
+ }
1003
+
1004
+ /**
1005
+ * @param {string} recordUid
1006
+ * @param {string} selectedValue
1007
+ */
1008
+ private setUnique(recordUid: string, selectedValue: string): void {
1009
+ if (!this.hasObjectGroupDefinedUniqueConstraints()) {
1010
+ return;
1011
+ }
1012
+ const selectorElement: HTMLSelectElement = <HTMLSelectElement>document.getElementById(
1013
+ this.container.dataset.objectGroup + '_selector',
1014
+ );
1015
+ const unique: UniqueDefinition = TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup];
1016
+ if (unique.type === 'select') {
1017
+ if (!(unique.selector && unique.max === -1)) {
1018
+ const formField = this.getFormFieldForElements();
1019
+ const recordObjectId = this.container.dataset.objectGroup + Separators.structureSeparator + recordUid;
1020
+ const recordContainer = InlineControlContainer.getInlineRecordContainer(recordObjectId);
1021
+ let uniqueValueField = <HTMLSelectElement>recordContainer.querySelector(
1022
+ '[name="data[' + unique.table + '][' + recordUid + '][' + unique.field + ']"]',
1023
+ );
1024
+ const values = InlineControlContainer.getValuesFromHashMap(unique.used);
1025
+ if (selectorElement !== null) {
1026
+ // remove all items from the new select-item which are already used in other children
1027
+ if (uniqueValueField !== null) {
1028
+ for (const value of values) {
1029
+ InlineControlContainer.removeSelectOptionByValue(uniqueValueField, value);
1030
+ }
1031
+ // set the selected item automatically to the first of the remaining items if no selector is used
1032
+ if (!unique.selector) {
1033
+ selectedValue = uniqueValueField.options[0].value;
1034
+ uniqueValueField.options[0].selected = true;
1035
+ this.updateUnique(uniqueValueField, formField, recordUid);
1036
+ this.handleChangedField(uniqueValueField, this.container.dataset.objectGroup + '[' + recordUid + ']');
1037
+ }
1038
+ }
1039
+ for (const value of values) {
1040
+ InlineControlContainer.removeSelectOptionByValue(uniqueValueField, value);
1041
+ }
1042
+ if (typeof unique.used.length !== 'undefined') {
1043
+ unique.used = {};
1044
+ }
1045
+ unique.used[recordUid] = {
1046
+ table: unique.elTable,
1047
+ uid: selectedValue,
1048
+ };
1049
+ }
1050
+ // remove the newly used item from each select-field of the child records
1051
+ if (formField !== null && InlineControlContainer.selectOptionValueExists(selectorElement, selectedValue)) {
1052
+ const records = Utility.trimExplode(',', (<HTMLInputElement>formField).value);
1053
+ for (const record of records) {
1054
+ uniqueValueField = <HTMLSelectElement>document.querySelector(
1055
+ '[name="data[' + unique.table + '][' + record + '][' + unique.field + ']"]',
1056
+ );
1057
+ if (uniqueValueField !== null && record !== recordUid) {
1058
+ InlineControlContainer.removeSelectOptionByValue(uniqueValueField, selectedValue);
1059
+ }
1060
+ }
1061
+ }
1062
+ }
1063
+ } else if (unique.type === 'groupdb') {
1064
+ // add the new record to the used items:
1065
+ unique.used[recordUid] = {
1066
+ table: unique.elTable,
1067
+ uid: selectedValue,
1068
+ };
1069
+ }
1070
+
1071
+ // remove used items from a selector-box
1072
+ if (unique.selector === 'select' && InlineControlContainer.selectOptionValueExists(selectorElement, selectedValue)) {
1073
+ InlineControlContainer.removeSelectOptionByValue(selectorElement, selectedValue);
1074
+ unique.used[recordUid] = {
1075
+ table: unique.elTable,
1076
+ uid: selectedValue,
1077
+ };
1078
+ }
1079
+ }
1080
+
1081
+ /**
1082
+ * @param {HTMLSelectElement} srcElement
1083
+ * @param {HTMLInputElement} formField
1084
+ * @param {string} recordUid
1085
+ */
1086
+ private updateUnique(srcElement: HTMLSelectElement, formField: HTMLInputElement, recordUid: string): void {
1087
+ if (!this.hasObjectGroupDefinedUniqueConstraints()) {
1088
+ return;
1089
+ }
1090
+ const unique = TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup];
1091
+ const oldValue = unique.used[recordUid];
1092
+
1093
+ if (unique.selector === 'select') {
1094
+ const selectorElement: HTMLSelectElement = <HTMLSelectElement>document.getElementById(
1095
+ this.container.dataset.objectGroup + '_selector',
1096
+ );
1097
+ InlineControlContainer.removeSelectOptionByValue(selectorElement, srcElement.value);
1098
+ if (typeof oldValue !== 'undefined') {
1099
+ InlineControlContainer.reAddSelectOption(selectorElement, oldValue, unique);
1100
+ }
1101
+ }
1102
+
1103
+ if (unique.selector && unique.max === -1) {
1104
+ return;
1105
+ }
1106
+
1107
+ if (!unique || formField === null) {
1108
+ return;
1109
+ }
1110
+
1111
+ const records = Utility.trimExplode(',', formField.value);
1112
+ let uniqueValueField;
1113
+ for (const record of records) {
1114
+ uniqueValueField = <HTMLSelectElement>document.querySelector(
1115
+ '[name="data[' + unique.table + '][' + record + '][' + unique.field + ']"]',
1116
+ );
1117
+ if (uniqueValueField !== null && uniqueValueField !== srcElement) {
1118
+ InlineControlContainer.removeSelectOptionByValue(uniqueValueField, srcElement.value);
1119
+ if (typeof oldValue !== 'undefined') {
1120
+ InlineControlContainer.reAddSelectOption(uniqueValueField, oldValue, unique);
1121
+ }
1122
+ }
1123
+ }
1124
+ unique.used[recordUid] = srcElement.value;
1125
+ }
1126
+
1127
+ /**
1128
+ * @param {string} recordUid
1129
+ */
1130
+ private revertUnique(recordUid: string): void {
1131
+ if (!this.hasObjectGroupDefinedUniqueConstraints()) {
1132
+ return;
1133
+ }
1134
+
1135
+ const unique = TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup];
1136
+ const recordObjectId = this.container.dataset.objectGroup + Separators.structureSeparator + recordUid;
1137
+ const recordContainer = InlineControlContainer.getInlineRecordContainer(recordObjectId);
1138
+
1139
+ const uniqueValueField = <HTMLSelectElement>recordContainer.querySelector(
1140
+ '[name="data[' + unique.table + '][' + recordContainer.dataset.objectUid + '][' + unique.field + ']"]',
1141
+ );
1142
+ if (unique.type === 'select') {
1143
+ let uniqueValue;
1144
+ if (uniqueValueField !== null) {
1145
+ uniqueValue = uniqueValueField.value;
1146
+ } else if (recordContainer.dataset.tableUniqueOriginalValue !== '') {
1147
+ uniqueValue = recordContainer.dataset.tableUniqueOriginalValue;
1148
+ } else {
1149
+ return;
1150
+ }
1151
+
1152
+ if (unique.selector === 'select') {
1153
+ if (!isNaN(parseInt(uniqueValue, 10))) {
1154
+ const selectorElement: HTMLSelectElement = <HTMLSelectElement>document.getElementById(
1155
+ this.container.dataset.objectGroup + '_selector',
1156
+ );
1157
+ InlineControlContainer.reAddSelectOption(selectorElement, uniqueValue, unique);
1158
+ }
1159
+ }
1160
+
1161
+ if (unique.selector && unique.max === -1) {
1162
+ return;
1163
+ }
1164
+
1165
+ const formField = this.getFormFieldForElements();
1166
+ if (formField === null) {
1167
+ return;
1168
+ }
1169
+
1170
+ const records = Utility.trimExplode(',', formField.value);
1171
+ let recordObj;
1172
+ // walk through all inline records on that level and get the select field
1173
+ for (let i = 0; i < records.length; i++) {
1174
+ recordObj = <HTMLSelectElement>document.querySelector(
1175
+ '[name="data[' + unique.table + '][' + records[i] + '][' + unique.field + ']"]',
1176
+ );
1177
+ if (recordObj !== null) {
1178
+ InlineControlContainer.reAddSelectOption(recordObj, uniqueValue, unique);
1179
+ }
1180
+ }
1181
+
1182
+ delete unique.used[recordUid];
1183
+ } else if (unique.type === 'groupdb') {
1184
+ delete unique.used[recordUid];
1185
+ }
1186
+ }
1187
+
1188
+ /**
1189
+ * @return {boolean}
1190
+ */
1191
+ private hasObjectGroupDefinedUniqueConstraints(): boolean {
1192
+ return typeof TYPO3.settings.FormEngineInline.unique !== 'undefined'
1193
+ && typeof TYPO3.settings.FormEngineInline.unique[this.container.dataset.objectGroup] !== 'undefined';
1194
+ }
1195
+
1196
+ /**
1197
+ * @param {HTMLInputElement | HTMLSelectElement} formField
1198
+ * @param {string} objectId
1199
+ */
1200
+ private handleChangedField(formField: HTMLInputElement | HTMLSelectElement, objectId: string): void {
1201
+ let value;
1202
+ if (formField instanceof HTMLSelectElement) {
1203
+ value = formField.options[formField.selectedIndex].text;
1204
+ } else {
1205
+ value = formField.value;
1206
+ }
1207
+ document.getElementById(objectId + '_label').textContent = value.length ? value : this.noTitleString;
1208
+ }
1209
+
1210
+ /**
1211
+ * @return {Object}
1212
+ */
1213
+ private getAppearance(): Appearance {
1214
+ if (this.appearance === null) {
1215
+ this.appearance = {};
1216
+
1217
+ if (typeof this.container.dataset.appearance === 'string') {
1218
+ try {
1219
+ this.appearance = JSON.parse(this.container.dataset.appearance);
1220
+ } catch (e) {
1221
+ console.error(e);
1222
+ }
1223
+ }
1224
+ }
1225
+
1226
+ return this.appearance;
1227
+ }
1228
+ }
1229
+
1230
+ export default InlineControlContainer;