typo3-typescript-sources 14.3.9 → 14.3.10

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 (92) hide show
  1. package/.last-synced-tag +1 -1
  2. package/README.md +1 -1
  3. package/backend/bookmark/bookmark-manager.ts +2 -1
  4. package/backend/bookmark/bookmark-store.ts +8 -12
  5. package/backend/clipboard-panel.ts +9 -3
  6. package/backend/context-menu-actions.ts +14 -12
  7. package/backend/context-menu.ts +2 -1
  8. package/backend/contextual-record-edit.ts +2 -1
  9. package/backend/date-time-picker.ts +2 -1
  10. package/backend/drag-uploader.ts +2 -1
  11. package/backend/element/contextual-record-edit-trigger.ts +7 -6
  12. package/backend/element/datetime-element.ts +2 -1
  13. package/backend/element/editable-page-title.ts +2 -1
  14. package/backend/element/pseudo-button.ts +29 -5
  15. package/backend/element/qrcode-modal-button.ts +2 -2
  16. package/backend/element/sidebar-toggle-element.ts +1 -15
  17. package/backend/element-browser.ts +4 -3
  18. package/backend/event/event-dispatcher.ts +4 -2
  19. package/backend/form-engine/element/extra/char-counter.ts +1 -1
  20. package/backend/form-engine.ts +4 -3
  21. package/backend/hotkeys.ts +2 -1
  22. package/backend/image-manipulation.ts +2 -1
  23. package/backend/info-window.ts +4 -3
  24. package/backend/layout-module/drag-drop.ts +2 -1
  25. package/backend/live-search/live-search-configurator.ts +4 -3
  26. package/backend/live-search/live-search-shortcut.ts +2 -1
  27. package/backend/live-search/live-search.ts +4 -3
  28. package/backend/live-search/result-types/default-result-type.ts +2 -1
  29. package/backend/localization/label-provider.ts +2 -1
  30. package/backend/login-refresh.ts +4 -24
  31. package/backend/login.ts +3 -2
  32. package/backend/modal.ts +9 -15
  33. package/backend/module-menu.ts +4 -3
  34. package/backend/multi-record-selection-edit-action.ts +3 -2
  35. package/backend/new-record-wizard.ts +2 -1
  36. package/backend/notification.ts +3 -19
  37. package/backend/recordlist.ts +6 -4
  38. package/backend/security/element/sudo-mode.ts +6 -7
  39. package/backend/settings/type/page.ts +3 -2
  40. package/backend/severity.ts +2 -22
  41. package/backend/storage/module-state-storage.ts +3 -1
  42. package/backend/storage/persistent.ts +15 -3
  43. package/backend/switch-user.ts +3 -2
  44. package/backend/tests/element/pseudo-button-test.ts +95 -0
  45. package/backend/tests/notification-test.ts +1 -1
  46. package/backend/tree/file-storage-browser.ts +5 -3
  47. package/backend/tree/file-storage-tree-container.ts +8 -6
  48. package/backend/tree/page-browser.ts +2 -1
  49. package/backend/tree/page-position-select.ts +2 -1
  50. package/backend/tree/page-tree-element.ts +5 -4
  51. package/backend/tree/tree.ts +2 -1
  52. package/backend/user-settings-manager.ts +2 -1
  53. package/backend/utility/drag-drop-utility.ts +2 -1
  54. package/backend/utility/top-level-module-import.ts +3 -1
  55. package/backend/viewport/content-container.ts +2 -1
  56. package/backend/viewport/content-navigation-toggle.ts +3 -10
  57. package/backend/viewport.ts +5 -4
  58. package/backend/window-manager.ts +5 -4
  59. package/backend/wizard/finisher/noop-finisher.ts +3 -2
  60. package/backend/wizard/finisher/redirect-finisher.ts +4 -3
  61. package/backend/wizard/finisher/reload-finisher.ts +3 -2
  62. package/backend/wizard/move-page.ts +2 -1
  63. package/core/java-script-item-processor.ts +3 -1
  64. package/core/lit-helper.ts +5 -2
  65. package/core/tests/utility/top-window-test.ts +121 -0
  66. package/core/utility/top-window.ts +99 -0
  67. package/dashboard/dashboard.ts +2 -1
  68. package/extensionmanager/dependency-check.ts +2 -1
  69. package/extensionmanager/extension-toggle-button.ts +2 -1
  70. package/extensionmanager/repository.ts +2 -1
  71. package/filelist/context-menu-actions.ts +20 -19
  72. package/filelist/file-delete.ts +4 -3
  73. package/filelist/file-list-dragdrop.ts +3 -2
  74. package/filelist/file-list-rename-handler.ts +2 -1
  75. package/filelist/file-list-replace-handler.ts +2 -1
  76. package/filelist/file-list-transfer-handler.ts +5 -4
  77. package/filelist/file-list.ts +7 -6
  78. package/impexp/context-menu-actions.ts +5 -3
  79. package/install/installer.ts +2 -1
  80. package/install/module/abstract-interactable-module.ts +2 -2
  81. package/install/module/maintenance/language-packs.ts +2 -1
  82. package/install/module/settings/extension-configuration.ts +3 -4
  83. package/install/renderable/flash-message.ts +2 -2
  84. package/install/renderable/info-box.ts +2 -2
  85. package/opendocs/open-document-store.ts +5 -4
  86. package/package.json +2 -2
  87. package/recycler/recycler.ts +2 -1
  88. package/redirects/event-handler.ts +2 -1
  89. package/scheduler/scheduler-add-group.ts +2 -1
  90. package/workspaces/backend.ts +8 -6
  91. package/workspaces/preview.ts +3 -2
  92. package/workspaces/workspace-state.ts +5 -4
package/.last-synced-tag CHANGED
@@ -1 +1 @@
1
- v14.3.9
1
+ v14.3.10
package/README.md CHANGED
@@ -23,7 +23,7 @@ npm install github:maikschneider/typo3-typescript-sources#main
23
23
 
24
24
  - **Original Repository:** [TYPO3/typo3](https://github.com/TYPO3/typo3)
25
25
  - **Source Path:** `Build/Sources/TypeScript/`
26
- - **Current TYPO3 Version:** `v14.3.9`
26
+ - **Current TYPO3 Version:** `v14.3.10`
27
27
 
28
28
  ## 🌿 Branches
29
29
 
@@ -21,6 +21,7 @@ import Modal from '../modal';
21
21
  import Notification from '../notification';
22
22
  import { SeverityEnum } from '../enum/severity';
23
23
  import { PseudoButtonLitElement } from '@typo3/backend/element/pseudo-button';
24
+ import { topDocument } from '@typo3/core/utility/top-window';
24
25
  import '@typo3/backend/element/spinner-element';
25
26
  import '@typo3/backend/element/icon-element';
26
27
  import labels from '~labels/core.bookmarks';
@@ -1208,7 +1209,7 @@ export class BookmarkManagerButtonElement extends PseudoButtonLitElement {
1208
1209
 
1209
1210
  protected override buttonActivated(): void {
1210
1211
  // Create element in top frame to avoid adoptedStyleSheets cross-document issues
1211
- const targetDocument = top?.document ?? document;
1212
+ const targetDocument = topDocument;
1212
1213
  const managerElement = targetDocument.createElement('typo3-backend-bookmark-manager-content') as BookmarkManagerContentElement;
1213
1214
  if (this.editId !== undefined) {
1214
1215
  managerElement.editId = this.editId;
@@ -20,6 +20,7 @@ import Notification from '../notification';
20
20
  import { SeverityEnum } from '../enum/severity';
21
21
  import { BroadcastMessage, type BroadcastEvent } from '@typo3/backend/broadcast-message';
22
22
  import BroadcastService from '@typo3/backend/broadcast-service';
23
+ import { topWindow } from '@typo3/core/utility/top-window';
23
24
 
24
25
  export const BookmarkStoreChangedEvent = 'typo3:bookmark-store:changed';
25
26
 
@@ -683,19 +684,14 @@ class BookmarkStore {
683
684
  * Returns singleton instance from top frame, creating it if needed.
684
685
  */
685
686
  function getOrCreateInstance(): BookmarkStore {
686
- try {
687
- if (top?.TYPO3?.BookmarkStore) {
688
- return top.TYPO3.BookmarkStore;
689
- }
690
- const instance = new BookmarkStore();
691
- if (top?.TYPO3) {
692
- top.TYPO3.BookmarkStore = instance;
693
- }
694
- return instance;
695
- } catch {
696
- // Cross-origin access denied, create local instance
697
- return new BookmarkStore();
687
+ if (topWindow.TYPO3?.BookmarkStore) {
688
+ return topWindow.TYPO3.BookmarkStore;
689
+ }
690
+ const instance = new BookmarkStore();
691
+ if (topWindow.TYPO3) {
692
+ topWindow.TYPO3.BookmarkStore = instance;
698
693
  }
694
+ return instance;
699
695
  }
700
696
 
701
697
  export default getOrCreateInstance();
@@ -21,6 +21,7 @@ import type { AjaxResponse } from '@typo3/core/ajax/ajax-response';
21
21
  import Notification from '@typo3/backend/notification';
22
22
  import '@typo3/backend/element/spinner-element';
23
23
  import '@typo3/backend/element/icon-element';
24
+ import { topWindow } from '@typo3/core/utility/top-window';
24
25
 
25
26
  enum CopyMode {
26
27
  cut = 'cut',
@@ -60,11 +61,12 @@ interface DispatchArgs {
60
61
  * Module: @typo3/backend/clipboard-panel
61
62
  *
62
63
  * @example
63
- * <typo3-backend-clipboard-panel return-url="/typo3/module" table="_FILE"></typo3-backend-clipboard-panel>
64
+ * <typo3-backend-clipboard-panel return-url="/typo3/module" module-url="/typo3/clipboard/process" table="_FILE"></typo3-backend-clipboard-panel>
64
65
  */
65
66
  @customElement('typo3-backend-clipboard-panel')
66
67
  export class ClipboardPanel extends LitElement {
67
68
  @property({ type: String, attribute: 'return-url' }) returnUrl: string = '';
69
+ @property({ type: String, attribute: 'module-url' }) moduleUrl: string = '';
68
70
  @property({ type: String }) table: string = '';
69
71
 
70
72
  private static renderLoader(): TemplateResult {
@@ -89,8 +91,12 @@ export class ClipboardPanel extends LitElement {
89
91
  `;
90
92
  }
91
93
 
94
+ private getModuleUrl(): string {
95
+ return this.moduleUrl || topWindow.TYPO3.settings.Clipboard.moduleUrl;
96
+ }
97
+
92
98
  private renderPanel(): Promise<TemplateResult> {
93
- return (new AjaxRequest(top.TYPO3.settings.Clipboard.moduleUrl))
99
+ return (new AjaxRequest(this.getModuleUrl()))
94
100
  .withQueryArguments({ action: 'getClipboardData' })
95
101
  .post({ table: this.table })
96
102
  .then(async (response: AjaxResponse): Promise<TemplateResult> => {
@@ -202,7 +208,7 @@ export class ClipboardPanel extends LitElement {
202
208
  private updateClipboard(event: PointerEvent, payload: object): void {
203
209
  event.preventDefault();
204
210
  const target: HTMLElement = event.currentTarget as HTMLElement;
205
- (new AjaxRequest(top.TYPO3.settings.Clipboard.moduleUrl))
211
+ (new AjaxRequest(this.getModuleUrl()))
206
212
  .post(payload)
207
213
  .then(async (response: AjaxResponse): Promise<void> => {
208
214
  const resolvedBody = await response.resolve();
@@ -23,6 +23,7 @@ import Viewport from './viewport';
23
23
  import '@typo3/backend/new-record-wizard';
24
24
  import Utility from '@typo3/backend/utility';
25
25
  import { topLevelModuleImport } from '@typo3/backend/utility/top-level-module-import';
26
+ import { topWindow, topDocument } from '@typo3/core/utility/top-window';
26
27
  import { html } from 'lit';
27
28
  import coreCommonLabels from '~labels/core.common';
28
29
  import cacheLabels from '~labels/core.cache';
@@ -38,7 +39,8 @@ class ContextMenuActions {
38
39
  * @returns {string}
39
40
  */
40
41
  public static getReturnUrl(): string {
41
- return encodeURIComponent(top.list_frame.document.location.pathname + top.list_frame.document.location.search);
42
+ const location = topWindow.list_frame.document.location;
43
+ return encodeURIComponent(location.pathname + location.search);
42
44
  }
43
45
 
44
46
  public static editRecord(table: string, uid: number, dataset: DOMStringMap): void {
@@ -51,10 +53,10 @@ class ContextMenuActions {
51
53
  }
52
54
 
53
55
  Viewport.ContentContainer.setUrl(
54
- top.TYPO3.settings.FormEngine.moduleUrl
56
+ topWindow.TYPO3.settings.FormEngine.moduleUrl
55
57
  + '&edit[' + table + '][' + uid + ']=edit'
56
58
  + overrideVals
57
- + '&module=' + encodeURIComponent(top.TYPO3.ModuleMenu.App.getCurrentModule())
59
+ + '&module=' + encodeURIComponent(topWindow.TYPO3.ModuleMenu.App.getCurrentModule())
58
60
  + '&returnUrl=' + ContextMenuActions.getReturnUrl(),
59
61
  );
60
62
  }
@@ -112,7 +114,7 @@ class ContextMenuActions {
112
114
  pageId: uid
113
115
  },
114
116
  });
115
- top.document.dispatchEvent(event);
117
+ topDocument.dispatchEvent(event);
116
118
  }
117
119
  }
118
120
 
@@ -152,13 +154,13 @@ class ContextMenuActions {
152
154
  }
153
155
 
154
156
  Viewport.ContentContainer.setUrl(
155
- top.TYPO3.settings.FormEngine.moduleUrl + '&edit[' + table + '][' + (table !== 'pages' ? '-' : '') + uid + ']=new&returnUrl=' + ContextMenuActions.getReturnUrl(),
157
+ topWindow.TYPO3.settings.FormEngine.moduleUrl + '&edit[' + table + '][' + (table !== 'pages' ? '-' : '') + uid + ']=new&returnUrl=' + ContextMenuActions.getReturnUrl(),
156
158
  );
157
159
  }
158
160
 
159
161
  public static openHistoryPopUp(table: string, uid: number): void {
160
162
  Viewport.ContentContainer.setUrl(
161
- top.TYPO3.settings.RecordHistory.moduleUrl + '&element=' + table + ':' + uid + '&returnUrl=' + ContextMenuActions.getReturnUrl(),
163
+ topWindow.TYPO3.settings.RecordHistory.moduleUrl + '&element=' + table + ':' + uid + '&returnUrl=' + ContextMenuActions.getReturnUrl(),
162
164
  );
163
165
  }
164
166
 
@@ -198,7 +200,7 @@ class ContextMenuActions {
198
200
  public static disableRecord(table: string, uid: number, dataset: DOMStringMap): void {
199
201
  const disableFieldName = dataset.disableField || 'hidden';
200
202
  Viewport.ContentContainer.setUrl(
201
- top.TYPO3.settings.RecordCommit.moduleUrl
203
+ topWindow.TYPO3.settings.RecordCommit.moduleUrl
202
204
  + '&data[' + table + '][' + uid + '][' + disableFieldName + ']=1'
203
205
  + '&redirect=' + ContextMenuActions.getReturnUrl(),
204
206
  );
@@ -207,7 +209,7 @@ class ContextMenuActions {
207
209
  public static enableRecord(table: string, uid: number, dataset: DOMStringMap): void {
208
210
  const disableFieldName = dataset.disableField || 'hidden';
209
211
  Viewport.ContentContainer.setUrl(
210
- top.TYPO3.settings.RecordCommit.moduleUrl
212
+ topWindow.TYPO3.settings.RecordCommit.moduleUrl
211
213
  + '&data[' + table + '][' + uid + '][' + disableFieldName + ']=0'
212
214
  + '&redirect=' + ContextMenuActions.getReturnUrl(),
213
215
  );
@@ -215,7 +217,7 @@ class ContextMenuActions {
215
217
 
216
218
  public static showInMenus(table: string, uid: number): void {
217
219
  Viewport.ContentContainer.setUrl(
218
- top.TYPO3.settings.RecordCommit.moduleUrl
220
+ topWindow.TYPO3.settings.RecordCommit.moduleUrl
219
221
  + '&data[' + table + '][' + uid + '][nav_hide]=0'
220
222
  + '&redirect=' + ContextMenuActions.getReturnUrl(),
221
223
  );
@@ -223,7 +225,7 @@ class ContextMenuActions {
223
225
 
224
226
  public static hideInMenus(table: string, uid: number): void {
225
227
  Viewport.ContentContainer.setUrl(
226
- top.TYPO3.settings.RecordCommit.moduleUrl
228
+ topWindow.TYPO3.settings.RecordCommit.moduleUrl
227
229
  + '&data[' + table + '][' + uid + '][nav_hide]=1'
228
230
  + '&redirect=' + ContextMenuActions.getReturnUrl(),
229
231
  );
@@ -350,7 +352,7 @@ class ContextMenuActions {
350
352
  + '&redirect=' + ContextMenuActions.getReturnUrl();
351
353
 
352
354
  Viewport.ContentContainer.setUrl(
353
- top.TYPO3.settings.RecordCommit.moduleUrl + url,
355
+ topWindow.TYPO3.settings.RecordCommit.moduleUrl + url,
354
356
  );
355
357
  };
356
358
  if (!dataset.title) {
@@ -384,7 +386,7 @@ class ContextMenuActions {
384
386
  }
385
387
 
386
388
  private static refreshPageTree(): void {
387
- top.document.dispatchEvent(new CustomEvent('typo3:pagetree:refresh'));
389
+ topDocument.dispatchEvent(new CustomEvent('typo3:pagetree:refresh'));
388
390
  }
389
391
  }
390
392
 
@@ -18,6 +18,7 @@ import '@typo3/backend/element/spinner-element';
18
18
  import { customElement, queryAll, state } from 'lit/decorators.js';
19
19
  import { html, LitElement, nothing, type TemplateResult } from 'lit';
20
20
  import { delay } from '@typo3/core/lit-helper';
21
+ import { topDocument } from '@typo3/core/utility/top-window';
21
22
  import { styleMap } from 'lit/directives/style-map.js';
22
23
  import { unsafeHTML } from 'lit/directives/unsafe-html.js';
23
24
  import { Task, initialState } from '@lit/task';
@@ -99,7 +100,7 @@ class ContextMenu {
99
100
  bubbles: true,
100
101
  composed: true
101
102
  });
102
- top.document.dispatchEvent(contextMenuEvent);
103
+ topDocument.dispatchEvent(contextMenuEvent);
103
104
  }
104
105
 
105
106
  private handleTriggerEvent(event: PointerEvent): void {
@@ -15,6 +15,7 @@ import FormEngine from '@typo3/backend/form-engine';
15
15
  import Modal from '@typo3/backend/modal';
16
16
  import Severity from '@typo3/backend/severity';
17
17
  import DocumentService from '@typo3/core/document-service';
18
+ import { topWindow } from '@typo3/core/utility/top-window';
18
19
  import '@typo3/backend/element/icon-element';
19
20
  import labels from '~labels/backend.alt_doc';
20
21
 
@@ -78,7 +79,7 @@ class ContextualRecordEdit {
78
79
  document.querySelectorAll('.has-change').forEach((el) => el.classList.remove('has-change'));
79
80
  // Navigate the content frame directly (same-origin access) — using
80
81
  // postMessage for this failed silently in Firefox.
81
- top?.TYPO3?.Backend?.ContentContainer?.setUrl(this.fullscreenButton.href);
82
+ topWindow.TYPO3?.Backend?.ContentContainer?.setUrl(this.fullscreenButton.href);
82
83
  // Tell the parent to close the modal
83
84
  window.parent.postMessage(
84
85
  { actionName: 'typo3:editform:navigate' },
@@ -21,6 +21,7 @@ import type { PostValidationEvent } from '@typo3/backend/form-engine-validation'
21
21
  import type { DateConfiguration } from '@typo3/backend/type/date-configuration';
22
22
  import '@typo3/backend/input/clearable';
23
23
  import coreLabels from '~labels/core.core';
24
+ import { openerTopWindow, topWindow } from '@typo3/core/utility/top-window';
24
25
 
25
26
  const ISO8601_LOCALTIME = 'ISO8601_LOCALTIME';
26
27
 
@@ -33,7 +34,7 @@ interface FlatpickrInputElement extends HTMLInputElement {
33
34
  * contains all logic for the date time picker used in FormEngine, EXT:belog and EXT:scheduler
34
35
  */
35
36
  class DateTimePicker {
36
- private readonly format: DateConfiguration = (typeof opener?.top?.TYPO3 !== 'undefined' ? opener.top : top).TYPO3.settings.DateConfiguration;
37
+ private readonly format: DateConfiguration = (openerTopWindow() ?? topWindow).TYPO3.settings.DateConfiguration;
37
38
 
38
39
  /**
39
40
  * initialize date fields to add a datepicker to each field
@@ -27,6 +27,7 @@ import { KeyTypesEnum } from '@typo3/backend/enum/key-types';
27
27
  import '@typo3/backend/element/progress-bar-element';
28
28
  import type { ProgressBarElement } from '@typo3/backend/element/progress-bar-element';
29
29
  import { topLevelModuleImport } from '@typo3/backend/utility/top-level-module-import';
30
+ import { topWindow } from '@typo3/core/utility/top-window';
30
31
  import { FormatUtility } from '@typo3/backend/utility/format-utility';
31
32
  import coreLabels from '~labels/core.core';
32
33
  import commonLabels from '~labels/core.common';
@@ -490,7 +491,7 @@ export default class DragUploader {
490
491
  {
491
492
  label: coreLabels.get('file_upload.reload.filelist.actions.reload'),
492
493
  action: new ImmediateAction((): void => {
493
- top.list_frame.document.location.href = this.reloadUrl;
494
+ topWindow.list_frame.document.location.href = this.reloadUrl;
494
495
  })
495
496
  }
496
497
  ]
@@ -17,6 +17,7 @@ import Modal, { Types, Sizes, Positions, type ModalElement } from '@typo3/backen
17
17
  import Notification from '@typo3/backend/notification';
18
18
  import Persistent from '@typo3/backend/storage/persistent';
19
19
  import labels from '~labels/backend.alt_doc';
20
+ import { topWindow, topDocument } from '@typo3/core/utility/top-window';
20
21
 
21
22
  /**
22
23
  * Module: @typo3/backend/element/contextual-record-edit-trigger
@@ -49,8 +50,8 @@ export class ContextualRecordEditTriggerElement extends PseudoButtonLitElement {
49
50
 
50
51
  protected override async buttonActivated(): Promise<void> {
51
52
  if (Persistent.isset('contextualRecordEdit') && Persistent.get('contextualRecordEdit') == 0) {
52
- if (top?.TYPO3?.Backend?.ContentContainer) {
53
- top.TYPO3.Backend.ContentContainer.setUrl(this.editUrl);
53
+ if (topWindow.TYPO3?.Backend?.ContentContainer) {
54
+ topWindow.TYPO3.Backend.ContentContainer.setUrl(this.editUrl);
54
55
  }
55
56
  return;
56
57
  }
@@ -67,7 +68,7 @@ export class ContextualRecordEditTriggerElement extends PseudoButtonLitElement {
67
68
  }
68
69
 
69
70
  private setupMessageHandling(modal: ModalElement): void {
70
- const messageTarget = top;
71
+ const messageTarget = topWindow;
71
72
  let savedRecordTitle = '';
72
73
  let hasSaved = false;
73
74
  let closeConfirmed = false;
@@ -105,9 +106,9 @@ export class ContextualRecordEditTriggerElement extends PseudoButtonLitElement {
105
106
  modal.addEventListener('typo3-modal-hidden', () => {
106
107
  messageTarget.removeEventListener('message', messageHandler);
107
108
  if (hasSaved) {
108
- top.document.dispatchEvent(new CustomEvent('typo3:pagetree:refresh'));
109
- if (top.TYPO3?.Backend?.ContentContainer) {
110
- top.TYPO3.Backend.ContentContainer.refresh();
109
+ topDocument.dispatchEvent(new CustomEvent('typo3:pagetree:refresh'));
110
+ if (topWindow.TYPO3?.Backend?.ContentContainer) {
111
+ topWindow.TYPO3.Backend.ContentContainer.refresh();
111
112
  }
112
113
  Notification.success(
113
114
  labels.get('notification.record_updated.title'),
@@ -15,6 +15,7 @@ import { html, LitElement, nothing, type TemplateResult, type PropertyValues } f
15
15
  import { customElement, property, state } from 'lit/decorators.js';
16
16
  import { DateTime, type DurationUnit, type DurationUnits } from 'luxon';
17
17
  import type { DateConfiguration } from '@typo3/backend/type/date-configuration';
18
+ import { openerTopWindow, topWindow } from '@typo3/core/utility/top-window';
18
19
 
19
20
  /**
20
21
  * Display mode for the time element
@@ -267,7 +268,7 @@ export class DateTimeElement extends LitElement {
267
268
 
268
269
  private getConfiguredDateFormats(): DateConfiguration | null {
269
270
  try {
270
- const root = (typeof opener?.top?.TYPO3 !== 'undefined' ? opener.top : top);
271
+ const root = openerTopWindow() ?? topWindow;
271
272
  return root.TYPO3.settings.DateConfiguration;
272
273
  } catch {
273
274
  return null;
@@ -17,6 +17,7 @@ import '@typo3/backend/element/icon-element';
17
17
  import AjaxDataHandler from '../ajax-data-handler';
18
18
  import commonLabels from '~labels/core.common';
19
19
  import layoutLabels from '~labels/backend.layout';
20
+ import { topDocument } from '@typo3/core/utility/top-window';
20
21
 
21
22
  @customElement('typo3-backend-editable-page-title')
22
23
  export class EditablePageTitle extends LitElement {
@@ -251,7 +252,7 @@ export class EditablePageTitle extends LitElement {
251
252
 
252
253
  AjaxDataHandler.process(parameters).then((): void => {
253
254
  this.pageTitle = newPageTitle;
254
- top.document.dispatchEvent(new CustomEvent('typo3:pagetree:refresh'));
255
+ topDocument.dispatchEvent(new CustomEvent('typo3:pagetree:refresh'));
255
256
  }).finally((): void => {
256
257
  this.endEditing();
257
258
  this._isSubmitting = false;
@@ -12,7 +12,7 @@
12
12
  */
13
13
 
14
14
  import { property } from 'lit/decorators.js';
15
- import { html, css, LitElement, type TemplateResult } from 'lit';
15
+ import { html, css, LitElement, type PropertyValues, type TemplateResult } from 'lit';
16
16
  import { KeyTypesEnum } from '@typo3/backend/enum/key-types';
17
17
 
18
18
  /**
@@ -20,18 +20,24 @@ import { KeyTypesEnum } from '@typo3/backend/enum/key-types';
20
20
  */
21
21
  export abstract class PseudoButtonLitElement extends LitElement {
22
22
  static override styles = [
23
- css`:host { cursor: pointer; appearance: button; }`
23
+ css`:host { cursor: pointer; appearance: button; }`,
24
+ css`:host([disabled]) { cursor: default; }`
24
25
  ];
25
26
 
26
27
  // eslint-disable-next-line lit/no-native-attributes
27
28
  @property({ type: String, reflect: true }) override role: string = 'button';
28
29
  @property({ type: String, reflect: true }) override tabIndex: number = 0;
30
+ /** As a disabled <button>: not activated, not in the tab order, announced as disabled */
31
+ @property({ type: Boolean, reflect: true }) disabled: boolean = false;
32
+
33
+ /** Of the markup or the element, back once the button is enabled again */
34
+ private enabledTabIndex: number = 0;
29
35
 
30
36
  public constructor() {
31
37
  super();
32
38
  this.addEventListener('click', (e: Event): void => {
33
39
  e.preventDefault();
34
- this.buttonActivated(e);
40
+ this.activate(e);
35
41
  });
36
42
  this.addEventListener('keydown', (e: KeyboardEvent): void => {
37
43
  if (e.key === KeyTypesEnum.SPACE) {
@@ -39,20 +45,38 @@ export abstract class PseudoButtonLitElement extends LitElement {
39
45
  }
40
46
  if (e.key === KeyTypesEnum.ENTER) {
41
47
  e.preventDefault();
42
- this.buttonActivated(e);
48
+ this.activate(e);
43
49
  }
44
50
  });
45
51
  this.addEventListener('keyup', (e: KeyboardEvent): void => {
46
52
  if (e.key === KeyTypesEnum.SPACE) {
47
53
  e.preventDefault();
48
- this.buttonActivated(e);
54
+ this.activate(e);
49
55
  }
50
56
  });
51
57
  }
52
58
 
59
+ protected override willUpdate(changed: PropertyValues<this>): void {
60
+ super.willUpdate(changed);
61
+ // Only a change of the state: a button enabled from the start keeps its tab index
62
+ if (changed.has('disabled') && this.disabled !== Boolean(changed.get('disabled'))) {
63
+ if (this.disabled) {
64
+ this.enabledTabIndex = this.tabIndex;
65
+ }
66
+ this.ariaDisabled = this.disabled ? 'true' : null;
67
+ this.tabIndex = this.disabled ? -1 : this.enabledTabIndex;
68
+ }
69
+ }
70
+
53
71
  protected override render(): TemplateResult {
54
72
  return html`<slot></slot>`;
55
73
  }
56
74
 
75
+ private activate(e: Event): void {
76
+ if (!this.disabled) {
77
+ this.buttonActivated(e);
78
+ }
79
+ }
80
+
57
81
  protected abstract buttonActivated(e: Event): void | Promise<void>;
58
82
  }
@@ -16,6 +16,7 @@ import { PseudoButtonLitElement } from '@typo3/backend/element/pseudo-button';
16
16
  import Modal from '@typo3/backend/modal';
17
17
  import { html } from 'lit';
18
18
  import { topLevelModuleImport } from '@typo3/backend/utility/top-level-module-import';
19
+ import { topWindow } from '@typo3/core/utility/top-window';
19
20
  import listLabels from '~labels/core.mod_web_list';
20
21
 
21
22
  /**
@@ -36,8 +37,7 @@ export class QrCodeModalButton extends PseudoButtonLitElement {
36
37
  }
37
38
 
38
39
  protected async loadModuleFrameAgnostic(module: string): Promise<any> {
39
- const isInIframe = window.location !== window.parent.location;
40
- if (isInIframe) {
40
+ if (topWindow !== window) {
41
41
  await topLevelModuleImport(module);
42
42
  } else {
43
43
  await import(module);
@@ -49,7 +49,6 @@ export class SidebarToggleElement extends PseudoButtonLitElement {
49
49
 
50
50
  @property({ type: String, attribute: 'label-collapse' }) labelCollapse: string = 'Collapse sidebar';
51
51
  @property({ type: String, attribute: 'label-expand' }) labelExpand: string = 'Expand sidebar';
52
- @property({ type: Boolean, reflect: true }) disabled: boolean = false;
53
52
 
54
53
  @state() private expanded: boolean = false;
55
54
 
@@ -67,7 +66,6 @@ export class SidebarToggleElement extends PseudoButtonLitElement {
67
66
 
68
67
  protected override updated(): void {
69
68
  this.updateLabels();
70
- this.updateDisabledState();
71
69
  }
72
70
 
73
71
  protected override render(): TemplateResult {
@@ -77,9 +75,7 @@ export class SidebarToggleElement extends PseudoButtonLitElement {
77
75
  }
78
76
 
79
77
  protected override buttonActivated(): void {
80
- if (!this.disabled) {
81
- ScaffoldState.toggleSidebar();
82
- }
78
+ ScaffoldState.toggleSidebar();
83
79
  }
84
80
 
85
81
  private readonly handleSidebarToggle = (event: ScaffoldSidebarToggleEvent): void => {
@@ -91,16 +87,6 @@ export class SidebarToggleElement extends PseudoButtonLitElement {
91
87
  this.title = label;
92
88
  this.setAttribute('aria-label', label);
93
89
  }
94
-
95
- private updateDisabledState(): void {
96
- if (this.disabled) {
97
- this.setAttribute('aria-disabled', 'true');
98
- this.tabIndex = -1;
99
- } else {
100
- this.removeAttribute('aria-disabled');
101
- this.tabIndex = 0;
102
- }
103
- }
104
90
  }
105
91
 
106
92
  declare global {
@@ -14,6 +14,7 @@
14
14
  import { MessageUtility } from '@typo3/backend/utility/message-utility';
15
15
  import DocumentService from '@typo3/core/document-service';
16
16
  import Modal from '@typo3/backend/modal';
17
+ import { topWindow } from '@typo3/core/utility/top-window';
17
18
 
18
19
  interface RTESettings {
19
20
  parameters: string;
@@ -79,7 +80,7 @@ class ElementBrowser {
79
80
  const isInModalFrame = typeof window.frames !== 'undefined'
80
81
  && typeof window.frames.frameElement !== 'undefined'
81
82
  && window.frames.frameElement.classList.contains('t3js-modal-iframe');
82
- const otherModalFrames = Array.from(top.frames || []).filter((item) => {
83
+ const otherModalFrames = Array.from(topWindow.frames || []).filter((item) => {
83
84
  try {
84
85
  return typeof item.frameElement !== 'undefined'
85
86
  && item.frameElement.classList.contains('t3js-modal-iframe')
@@ -185,9 +186,9 @@ class ElementBrowser {
185
186
 
186
187
  private findEditFormWindow(): Window | null {
187
188
  try {
188
- for (let i = 0; i < top.frames.length; i++) {
189
+ for (let i = 0; i < topWindow.frames.length; i++) {
189
190
  try {
190
- const frame = top.frames[i];
191
+ const frame = topWindow.frames[i];
191
192
  if (frame !== window && this.windowHasEditForm(frame)) {
192
193
  return frame;
193
194
  }
@@ -11,6 +11,8 @@
11
11
  * The TYPO3 project - inspiring people to share!
12
12
  */
13
13
 
14
+ import { topDocument } from '@typo3/core/utility/top-window';
15
+
14
16
  /**
15
17
  * Module: @typo3/backend/event/event-dispatcher
16
18
  */
@@ -19,8 +21,8 @@ export class EventDispatcher {
19
21
  const event = new CustomEvent(name, { detail: detail });
20
22
  if (!useTop) {
21
23
  document.dispatchEvent(event);
22
- } else if (typeof top !== 'undefined') {
23
- top.document.dispatchEvent(event);
24
+ } else {
25
+ topDocument.dispatchEvent(event);
24
26
  }
25
27
  }
26
28
  }
@@ -49,7 +49,7 @@ export class CharCounter extends LitElement {
49
49
 
50
50
  protected override render(): TemplateResult {
51
51
  return html`
52
- <span class="form-hint form-hint--${this.determineCounterClass()}">
52
+ <span class="form-hint form-hint--${this.determineCounterClass()}" role="status" aria-live="polite" aria-atomic="true">
53
53
  ${labels.get('labels.remainingCharacters', { '0': this.remainingCharacters.toString(10) })}
54
54
  </span>
55
55
  `;
@@ -39,6 +39,7 @@ import RegularEvent from '@typo3/core/event/regular-event';
39
39
  import backendAltDocLabels from '~labels/backend.alt_doc';
40
40
  import coreCoreLabels from '~labels/core.core';
41
41
  import type { ElementBrowserMessage, ElementBrowserElementAddedMessage } from '@typo3/backend/element-browser';
42
+ import { topWindow } from '@typo3/core/utility/top-window';
42
43
 
43
44
  export interface OnFieldChangeItem {
44
45
  name: string;
@@ -531,12 +532,12 @@ export default (function() {
531
532
  * Initialize events for all form engine relevant tasks.
532
533
  */
533
534
  FormEngine.initializeEvents = function() {
534
- if (top.TYPO3 && typeof top.TYPO3.Backend !== 'undefined') {
535
+ if (topWindow.TYPO3 && typeof topWindow.TYPO3.Backend !== 'undefined') {
535
536
  // @todo: The consumer scope attachment is actually not needed
536
537
  // in page wizard and context panel (which currently uses it own handling).
537
538
  // This needs to be streamlined.
538
- top.TYPO3.Backend.consumerScope.attach(FormEngine);
539
- window.addEventListener('pagehide', () => top.TYPO3.Backend.consumerScope.detach(FormEngine), { once: true });
539
+ topWindow.TYPO3.Backend.consumerScope.attach(FormEngine);
540
+ window.addEventListener('pagehide', () => topWindow.TYPO3.Backend.consumerScope.detach(FormEngine), { once: true });
540
541
  }
541
542
 
542
543
  FormEngine.formElement.addEventListener('submit', function(e: SubmitEvent) {
@@ -15,6 +15,7 @@ import HotkeyStorage, { type ScopedHotkeyMap, type Options, type HotkeySetup } f
15
15
  import RegularEvent from '@typo3/core/event/regular-event';
16
16
  import { HotkeyStruct } from '@typo3/backend/hotkeys/hotkey-struct';
17
17
  import { HotkeyDispatchedEvent, HotkeyRequestedEvent } from '@typo3/backend/hotkeys/events';
18
+ import { topDocument } from '@typo3/core/utility/top-window';
18
19
 
19
20
  export enum ModifierKeys {
20
21
  META = 'meta',
@@ -101,7 +102,7 @@ class Hotkeys {
101
102
  new RegularEvent('keydown', (e: KeyboardEvent): void => {
102
103
  const hotkeyStruct = HotkeyStruct.fromEvent(e);
103
104
  if (hotkeyStruct.hasAnyModifier()) {
104
- top.document.dispatchEvent(new HotkeyRequestedEvent(e));
105
+ topDocument.dispatchEvent(new HotkeyRequestedEvent(e));
105
106
  }
106
107
  }).bindTo(document);
107
108
 
@@ -23,6 +23,7 @@ import { default as Modal, type ModalElement } from './modal';
23
23
  import '@typo3/backend/element/spinner-element';
24
24
  import { renderNodes } from '@typo3/core/lit-helper';
25
25
  import { topLevelModuleImport } from '@typo3/backend/utility/top-level-module-import';
26
+ import { topDocument } from '@typo3/core/utility/top-window';
26
27
  import { Offset } from '@typo3/backend/offset';
27
28
  import type { DraggableResizableEvent, PointerEventNames, DraggableResizableElement } from '@typo3/backend/element/draggable-resizable-element';
28
29
  import type { EventInterface } from '@typo3/core/event/event-interface';
@@ -608,7 +609,7 @@ class ImageManipulation {
608
609
  private initFocusArea(container: HTMLElement): DraggableResizableElement {
609
610
  topLevelModuleImport('@typo3/backend/element/draggable-resizable-element.js');
610
611
 
611
- const focusAreaEl = top.document.createElement('typo3-backend-draggable-resizable');
612
+ const focusAreaEl = topDocument.createElement('typo3-backend-draggable-resizable');
612
613
  // assign area declaration (as persisted in the database)
613
614
  focusAreaEl.setAttribute('offset', JSON.stringify(this.convertAreaToOffset(this.currentCropVariant.focusArea, container)));
614
615
  // use the same events as cropper.js does
@@ -13,6 +13,7 @@
13
13
 
14
14
  import { SeverityEnum } from './enum/severity';
15
15
  import Modal from './modal';
16
+ import { topWindow } from '@typo3/core/utility/top-window';
16
17
 
17
18
  /**
18
19
  * Module: @typo3/backend/info-window
@@ -29,7 +30,7 @@ class InfoWindow {
29
30
  Modal.advanced({
30
31
  type: Modal.types.iframe,
31
32
  size: Modal.sizes.large,
32
- content: top.TYPO3.settings.ShowItem.moduleUrl
33
+ content: topWindow.TYPO3.settings.ShowItem.moduleUrl
33
34
  + '&table=' + encodeURIComponent(table)
34
35
  + '&uid=' + (typeof uid === 'number' ? uid : encodeURIComponent(uid)),
35
36
  severity: SeverityEnum.notice,
@@ -37,8 +38,8 @@ class InfoWindow {
37
38
  }
38
39
  }
39
40
 
40
- if (!top.TYPO3.InfoWindow) {
41
- top.TYPO3.InfoWindow = InfoWindow;
41
+ if (!topWindow.TYPO3.InfoWindow) {
42
+ topWindow.TYPO3.InfoWindow = InfoWindow;
42
43
  }
43
44
 
44
45
  // expose as global object