From 29c95a7e650d797f0e29252d26772c2d0f90f56e Mon Sep 17 00:00:00 2001
From: Benjamin Franzke <ben@bnf.dev>
Date: Wed, 9 Aug 2023 13:39:16 +0200
Subject: [PATCH] [TASK] Migrate @typo3/form/backend/form-editor to TypeScript

Resolves: #101713
Related: #82577
Releases: main, 12.4
Change-Id: Ic7b41b79552a4c14d7395609e0ac3dda9c47b20f
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/80721
Tested-by: core-ci <typo3@b13.com>
Tested-by: Benjamin Franzke <ben@bnf.dev>
Reviewed-by: Benjamin Franzke <ben@bnf.dev>
---
 .../TypeScript/form/backend/form-editor.ts    |  855 ++++++++++++
 .../Sources/TypeScript/form/backend/helper.ts |   15 +-
 .../Public/JavaScript/backend/form-editor.js  | 1157 +----------------
 3 files changed, 861 insertions(+), 1166 deletions(-)
 create mode 100644 Build/Sources/TypeScript/form/backend/form-editor.ts

diff --git a/Build/Sources/TypeScript/form/backend/form-editor.ts b/Build/Sources/TypeScript/form/backend/form-editor.ts
new file mode 100644
index 000000000000..ec331a9b090a
--- /dev/null
+++ b/Build/Sources/TypeScript/form/backend/form-editor.ts
@@ -0,0 +1,855 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Module: @typo3/form/backend/form-editor
+ */
+import $ from 'jquery';
+import Notification from '@typo3/backend/notification';
+import * as Core from '@typo3/form/backend/form-editor/core';
+import type { JavaScriptItemPayload } from '@typo3/core/java-script-item-processor';
+import type {
+  ApplicationStateStack,
+  AjaxRequests,
+  Endpoints,
+  CollectionElementConfiguration,
+  CollectionEntry,
+  DataBackend,
+  Factory,
+  FormEditorDefinitions,
+  FormElement,
+  FormElementDefinition,
+  FormElementPropertyValidatorDefinition,
+  PublisherSubscriber,
+  PropertyCollectionElement,
+  PropertyValidationService,
+  Repository,
+  RootFormElement,
+  Utility,
+  ValidationResults,
+  ValidationResultsWithPath,
+  ValidationResultsRecursive,
+  Validator
+} from '@typo3/form/backend/form-editor/core';
+
+const assert = Core.assert;
+
+type AdditionalViewModelModules = JavaScriptItemPayload[];
+
+export interface Mediator {
+  bootstrap(formEditorInstance: FormEditor, viewModel: ViewModel): void;
+}
+
+export interface ViewModel {
+  bootstrap(formEditorInstance: FormEditor, additionalViewModelModules: AdditionalViewModelModules): void;
+  enableButton(button: JQuery): void;
+  disableButton(button: JQuery): void;
+  hideComponent(component: JQuery): void;
+  showRemoveFormElementModal(formElement?: FormElement): void;
+  showRemoveCollectionElementModal(collectionElementIdentifier: string, collectionName: string, formElement?: FormElement): void;
+  setElementValidationErrorClass(element: JQuery, classIdentifier?: string): void;
+  removeElementValidationErrorClass(element: JQuery, classIdentifier?: string): void;
+}
+
+export type FormEditorConfiguration = {
+  prototypeName: string,
+  endpoints: Endpoints,
+  formEditorDefinitions: FormEditorDefinitions,
+  formDefinition: FormElementDefinition,
+  formPersistenceIdentifier: string,
+  additionalViewModelModules: AdditionalViewModelModules,
+  maximumUndoSteps: number
+};
+
+export class FormEditor {
+  private isRunning: boolean = false;
+  private unsavedContent: boolean = false;
+  private readonly configuration: FormEditorConfiguration;
+  private readonly mediator: Mediator;
+  private readonly viewModel: ViewModel;
+
+  public constructor(
+    configuration: FormEditorConfiguration,
+    mediator: Mediator,
+    viewModel: ViewModel
+  ) {
+    this.configuration = configuration || {} as FormEditorConfiguration;
+    this.mediator = mediator;
+    this.viewModel = viewModel;
+  }
+
+  public getPublisherSubscriber(): PublisherSubscriber {
+    return Core.getPublisherSubscriber();
+  }
+
+  public undoApplicationState(): void {
+    this.getApplicationStateStack().incrementCurrentStackPointer();
+  }
+
+  public redoApplicationState(): void {
+    this.getApplicationStateStack().decrementCurrentStackPointer();
+  }
+
+  public getMaximalApplicationStates(): number {
+    return this.getApplicationStateStack().getMaximalStackSize();
+  }
+
+  public getCurrentApplicationStates(): number {
+    return this.getApplicationStateStack().getCurrentStackSize();
+  }
+
+  public getCurrentApplicationStatePosition(): number {
+    return this.getApplicationStateStack().getCurrentStackPointer();
+  }
+
+  /**
+   * @internal
+   * @throws 1519855175
+   */
+  public setFormDefinition(formDefinition: FormElementDefinition): void {
+    assert('object' === $.type(formDefinition), 'Invalid parameter "formDefinition"', 1519855175);
+    this.getApplicationStateStack().setCurrentState('formDefinition', this.getFactory().createFormElement(formDefinition, undefined, undefined, true));
+  }
+
+  /**
+   * @throws 1475378543
+   */
+  public getRunningAjaxRequest<T extends keyof AjaxRequests>(
+    type: keyof AjaxRequests
+  ): AjaxRequests[T] | null {
+    assert(this.getUtility().isNonEmptyString(type), 'Invalid parameter "type"', 1475378543);
+    return Core.getRunningAjaxRequest(type);
+  }
+
+  public getUtility(): Utility {
+    return Core.getUtility();
+  }
+
+  public assert(test: boolean|(() => boolean), message: string, messageCode: number): void {
+    this.getUtility().assert(test, message, messageCode);
+  }
+
+  public buildPropertyPath(
+    propertyPath: string,
+    collectionElementIdentifier?: string,
+    collectionName?: keyof FormEditorDefinitions,
+    _formElement?: string | FormElement,
+    allowEmptyReturnValue?: boolean
+  ): string {
+    if (this.getUtility().isUndefinedOrNull(_formElement)) {
+      _formElement = this.getCurrentlySelectedFormElement();
+    }
+    const formElement = this.getRepository().findFormElement(_formElement);
+    return this.getUtility().buildPropertyPath(
+      propertyPath,
+      collectionElementIdentifier,
+      collectionName,
+      formElement,
+      allowEmptyReturnValue
+    );
+  }
+
+  public addPropertyValidationValidator(validatorIdentifier: string, func: Validator): void {
+    this.getPropertyValidationService().addValidator(validatorIdentifier, func);
+  }
+
+  public validateCurrentlySelectedFormElementProperty(
+    propertyPath: string
+  ): ValidationResults {
+    return this.validateFormElementProperty(
+      this.getCurrentlySelectedFormElement(),
+      propertyPath
+    );
+  }
+
+  public validateFormElementProperty(
+    _formElement: FormElement | string,
+    propertyPath: string
+  ): ValidationResults {
+    const formElement = this.getRepository().findFormElement(_formElement);
+    return this.getPropertyValidationService().validateFormElementProperty(formElement, propertyPath);
+  }
+
+  public validateFormElement(
+    _formElement: FormElement | string
+  ): ValidationResultsWithPath {
+    const formElement = this.getRepository().findFormElement(_formElement);
+    return this.getPropertyValidationService().validateFormElement(formElement);
+  }
+
+  public validationResultsHasErrors(
+    validationResults: ValidationResultsRecursive
+  ): boolean {
+    return this.getPropertyValidationService().validationResultsHasErrors(validationResults);
+  }
+
+  public validateFormElementRecursive(
+    _formElement: FormElement | string,
+    returnAfterFirstMatch?: boolean
+  ): ValidationResultsRecursive {
+    const formElement = this.getRepository().findFormElement(_formElement);
+    return this.getPropertyValidationService().validateFormElementRecursive(formElement, returnAfterFirstMatch);
+  }
+
+  /**
+   * @throws 1475378544
+   */
+  public setUnsavedContent(unsavedContent: boolean): void {
+    assert('boolean' === $.type(unsavedContent), 'Invalid parameter "unsavedContent"', 1475378544);
+    this.unsavedContent = unsavedContent;
+  }
+
+  public getUnsavedContent(): boolean {
+    return this.unsavedContent;
+  }
+
+  public getRootFormElement(): RootFormElement {
+    return this.getRepository().getRootFormElement();
+  }
+
+  public getCurrentlySelectedFormElement(): FormElement {
+    return this.getRepository().findFormElementByIdentifierPath(this.getApplicationStateStack().getCurrentState('currentlySelectedFormElementIdentifierPath'));
+  }
+
+  /**
+   * @publish core/currentlySelectedFormElementChanged
+   */
+  public setCurrentlySelectedFormElement(
+    _formElement: FormElement | string,
+    doNotRefreshCurrentlySelectedPageIndex?: boolean
+  ): void {
+    doNotRefreshCurrentlySelectedPageIndex = !!doNotRefreshCurrentlySelectedPageIndex;
+
+    const formElement = this.getRepository().findFormElement(_formElement);
+    this.getApplicationStateStack().setCurrentState('currentlySelectedFormElementIdentifierPath', formElement.get('__identifierPath'));
+
+    if (!doNotRefreshCurrentlySelectedPageIndex) {
+      this.refreshCurrentlySelectedPageIndex();
+    }
+    this.getPublisherSubscriber().publish('core/currentlySelectedFormElementChanged', [formElement]);
+  }
+
+  /**
+   * @throws 1475378545
+   */
+  public getFormElementByIdentifierPath(identifierPath: string): FormElement {
+    assert(this.getUtility().isNonEmptyString(identifierPath), 'Invalid parameter "identifierPath"', 1475378545);
+    return this.getRepository().findFormElementByIdentifierPath(identifierPath);
+  }
+
+  public isFormElementIdentifierUsed(formElementIdentifier: string): boolean {
+    return this.getRepository().isFormElementIdentifierUsed(formElementIdentifier);
+  }
+
+  public createAndAddFormElement(
+    formElementType: string,
+    referenceFormElement?: FormElement | string,
+    disablePublishersOnSet?: boolean
+  ): FormElement {
+    const formElement = this.addFormElement(
+      this.createFormElement(formElementType, disablePublishersOnSet),
+      referenceFormElement,
+      disablePublishersOnSet
+    );
+    formElement.set('renderables', formElement.get('renderables'));
+    return formElement;
+  }
+
+  /**
+   * @throws 1475434337
+   */
+  public addFormElement(
+    formElement: FormElement,
+    _referenceFormElement?: FormElement | string,
+    disablePublishersOnSet?: boolean
+  ): FormElement {
+    this.saveApplicationState();
+
+    if (this.getUtility().isUndefinedOrNull(_referenceFormElement)) {
+      _referenceFormElement = this.getCurrentlySelectedFormElement();
+    }
+    const referenceFormElement = this.getRepository().findFormElement(_referenceFormElement);
+    assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475434337);
+    return this.getRepository().addFormElement(formElement, referenceFormElement, true, disablePublishersOnSet);
+  }
+
+  /**
+   * @throws 1475434336
+   * @throws 1475435857
+   */
+  public createFormElement(
+    formElementType: string,
+    disablePublishersOnSet: boolean
+  ): FormElement {
+    assert(this.getUtility().isNonEmptyString(formElementType), 'Invalid parameter "formElementType"', 1475434336);
+
+    const identifier = this.getRepository().getNextFreeFormElementIdentifier(formElementType);
+    const formElementDefinition = this.getFormElementDefinitionByType(formElementType, undefined);
+    return this.getFactory().createFormElement({
+      type: formElementType,
+      identifier: identifier,
+      label: formElementDefinition.label || formElementType
+    }, undefined, undefined, undefined, disablePublishersOnSet);
+  }
+
+  public removeFormElement(
+    _formElementToRemove: FormElement | string,
+    disablePublishersOnSet?: boolean
+  ): FormElement {
+    this.saveApplicationState();
+
+    const formElementToRemove = this.getRepository().findFormElement(_formElementToRemove);
+    const parentFormElement = formElementToRemove.get('__parentRenderable');
+    this.getRepository().removeFormElement(formElementToRemove, true, disablePublishersOnSet);
+    return parentFormElement;
+  }
+
+  /**
+   * @throws 1475378551
+   */
+  public moveFormElement(
+    _formElementToMove: FormElement | string,
+    position: string,
+    _referenceFormElement: FormElement | string,
+    disablePublishersOnSet?: boolean
+  ): FormElement {
+    this.saveApplicationState();
+
+    let formElementToMove = this.getRepository().findFormElement(_formElementToMove);
+    const referenceFormElement = this.getRepository().findFormElement(_referenceFormElement);
+
+    assert('after' === position || 'before' === position || 'inside' === position, 'Invalid position "' + position + '"', 1475378551);
+
+    formElementToMove = this.getRepository().moveFormElement(formElementToMove, position, referenceFormElement, true);
+    disablePublishersOnSet = !!disablePublishersOnSet;
+    if (!disablePublishersOnSet) {
+      formElementToMove.get('__parentRenderable').set('renderables', formElementToMove.get('__parentRenderable').get('renderables'));
+    }
+    return formElementToMove;
+  }
+
+  /**
+   * @throws 1475378555
+   * @throws 1475378556
+   * @throws 1475446108
+   */
+  public getPropertyCollectionElementConfiguration(
+    collectionElementIdentifier: string,
+    collectionName: keyof FormEditorDefinitions,
+    _formElement?: string | FormElement
+  ): CollectionEntry {
+    let collection, collectionElement;
+    if (this.getUtility().isUndefinedOrNull(_formElement)) {
+      _formElement = this.getCurrentlySelectedFormElement();
+    }
+    const formElement = this.getRepository().findFormElement(_formElement);
+
+    assert(this.getUtility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475378555);
+    assert(this.getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475378556);
+
+    const formElementDefinition = this.getFormElementDefinitionByType(formElement.get('type'), undefined);
+    if (!this.getUtility().isUndefinedOrNull(formElementDefinition.propertyCollections)) {
+      collection = formElementDefinition.propertyCollections[collectionName];
+      assert(!this.getUtility().isUndefinedOrNull(collection), 'Invalid collection name "' + collectionName + '"', 1475446108);
+      collectionElement = this.getRepository().findCollectionElementByIdentifierPath(collectionElementIdentifier, collection);
+      // Return a dereferenced object
+      return $.extend(true, {}, collectionElement);
+    } else {
+      return {} as CollectionEntry;
+    }
+  }
+
+  /**
+   * @throws 1475378557
+   * @throws 1475378558
+   */
+  public getIndexFromPropertyCollectionElement(
+    collectionElementIdentifier: string,
+    collectionName: keyof FormEditorDefinitions,
+    _formElement?: string | FormElement
+  ): number {
+    if (this.getUtility().isUndefinedOrNull(_formElement)) {
+      _formElement = this.getCurrentlySelectedFormElement();
+    }
+    const formElement = this.getRepository().findFormElement(_formElement);
+
+    assert(this.getUtility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475378557);
+    assert(this.getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475378558);
+
+    const indexFromPropertyCollectionElement = this.getRepository().getIndexFromPropertyCollectionElementByIdentifier(
+      collectionElementIdentifier,
+      collectionName,
+      formElement
+    );
+
+    return indexFromPropertyCollectionElement;
+  }
+
+  public createAndAddPropertyCollectionElement(
+    collectionElementIdentifier: string,
+    collectionName: keyof FormEditorDefinitions,
+    formElement: FormElement,
+    collectionElementConfiguration: CollectionElementConfiguration,
+    referenceCollectionElementIdentifier: string
+  ): FormElement {
+    return this.addPropertyCollectionElement(
+      this.createPropertyCollectionElement(
+        collectionElementIdentifier,
+        collectionName,
+        collectionElementConfiguration
+      ),
+      collectionName,
+      formElement,
+      referenceCollectionElementIdentifier
+    );
+  }
+
+  /**
+   * @throws 1475443300
+   * @throws 1475443301
+   */
+  public addPropertyCollectionElement(
+    collectionElement: CollectionEntry,
+    collectionName: keyof FormEditorDefinitions,
+    _formElement?: FormElement | string,
+    referenceCollectionElementIdentifier?: string
+  ): FormElement {
+    let collection;
+    this.saveApplicationState();
+
+    if (this.getUtility().isUndefinedOrNull(_formElement)) {
+      _formElement = this.getCurrentlySelectedFormElement();
+    }
+    const formElement = this.getRepository().findFormElement(_formElement);
+
+    assert('object' === $.type(collectionElement), 'Invalid parameter "collectionElement"', 1475443301);
+    assert(this.getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475443300);
+
+    if (this.getUtility().isUndefinedOrNull(referenceCollectionElementIdentifier)) {
+      collection = formElement.get(collectionName);
+      if ('array' === $.type(collection) && collection.length > 0) {
+        referenceCollectionElementIdentifier = collection[collection.length - 1].identifier;
+      }
+    }
+
+    return this.getRepository().addPropertyCollectionElement(
+      collectionElement,
+      collectionName,
+      formElement,
+      referenceCollectionElementIdentifier,
+      false
+    );
+  }
+
+  /**
+   * @throws 1475378559
+   * @throws 1475378560
+   */
+  public createPropertyCollectionElement(
+    collectionElementIdentifier: string,
+    collectionName: keyof FormEditorDefinitions,
+    collectionElementConfiguration: CollectionElementConfiguration
+  ): PropertyCollectionElement {
+    assert(this.getUtility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475378559);
+    assert(this.getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475378560);
+    if ('object' !== $.type(collectionElementConfiguration)) {
+      collectionElementConfiguration = {} as CollectionElementConfiguration;
+    }
+
+    return this.getFactory().createPropertyCollectionElement(
+      collectionElementIdentifier,
+      collectionElementConfiguration,
+      collectionName
+    );
+  }
+
+  /**
+   * @throws 1475378561
+   * @throws 1475378562
+   */
+  public removePropertyCollectionElement(
+    collectionElementIdentifier: string,
+    collectionName: keyof FormEditorDefinitions,
+    _formElement?: FormElement | string,
+    disablePublishersOnSet?: boolean
+  ): void {
+    this.saveApplicationState();
+
+    if (this.getUtility().isUndefinedOrNull(_formElement)) {
+      _formElement = this.getCurrentlySelectedFormElement();
+    }
+    const formElement = this.getRepository().findFormElement(_formElement);
+
+    assert(this.getUtility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475378561);
+    assert(this.getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475378562);
+
+    this.getRepository().removePropertyCollectionElementByIdentifier(
+      formElement,
+      collectionElementIdentifier,
+      collectionName,
+      true
+    );
+
+    disablePublishersOnSet = !!disablePublishersOnSet;
+    if (!disablePublishersOnSet) {
+      this.getPublisherSubscriber().publish('core/formElement/somePropertyChanged', ['__fakeProperty']);
+    }
+  }
+
+  /**
+   * @throws 1477404352
+   * @throws 1477404353
+   * @throws 1477404354
+   * @throws 1477404355
+   */
+  public movePropertyCollectionElement(
+    collectionElementToMove: string,
+    position: string,
+    referenceCollectionElement: string,
+    collectionName: keyof FormEditorDefinitions,
+    formElement: FormElement,
+    disablePublishersOnSet?: boolean
+  ): void {
+    this.saveApplicationState();
+
+    formElement = this.getRepository().findFormElement(formElement);
+
+    assert('string' === $.type(collectionElementToMove), 'Invalid parameter "collectionElementToMove"', 1477404352);
+    assert('string' === $.type(referenceCollectionElement), 'Invalid parameter "referenceCollectionElement"', 1477404353);
+    assert('after' === position || 'before' === position, 'Invalid position "' + position + '"', 1477404354);
+    assert(this.getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1477404355);
+
+    this.getRepository().movePropertyCollectionElement(collectionElementToMove, position, referenceCollectionElement, collectionName, formElement, disablePublishersOnSet);
+  }
+
+  /**
+   * @throws 1475378563
+   */
+  public getFormElementDefinitionByType<T extends undefined | keyof FormElementDefinition, R = T extends keyof FormElementDefinition ? FormElementDefinition[T] : FormElementDefinition>(
+    elementType: string,
+    formElementDefinitionKey?: T
+  ): R {
+    assert(this.getUtility().isNonEmptyString(elementType), 'Invalid parameter "elementType"', 1475378563);
+
+    const formElementDefinition = this.getRepository().getFormEditorDefinition('formElements', elementType);
+
+    if (formElementDefinitionKey !== undefined/* && formElementDefinitionKey !== null*/) {
+      const formElementDefinitionEntry = formElementDefinition[formElementDefinitionKey];
+      if (formElementDefinitionEntry !== null && (typeof formElementDefinitionEntry === 'object')) {
+        return $.extend(true, {}, formElementDefinitionEntry);
+      } else {
+        return formElementDefinitionEntry as R;
+      }
+    }
+
+    if (formElementDefinition !== null && (typeof formElementDefinition === 'object')) {
+      return $.extend(true, {}, formElementDefinition);
+    } else {
+      return formElementDefinition as R;
+    }
+  }
+
+  public getFormElementDefinition<T extends keyof FormElementDefinition>(
+    formElement: FormElement | string,
+    formElementDefinitionKey?: T
+  ): T extends keyof FormElementDefinition ? FormElementDefinition[T] : FormElementDefinition {
+    formElement = this.getRepository().findFormElement(formElement);
+    return this.getFormElementDefinitionByType(formElement.get('type'), formElementDefinitionKey);
+  }
+
+  public getFormEditorDefinition<D extends keyof FormEditorDefinitions, S extends keyof FormEditorDefinitions[D]>(
+    definitionName: D,
+    subject: S
+  ): FormEditorDefinitions[D][S] {
+    return this.getRepository().getFormEditorDefinition(definitionName, subject);
+  }
+
+  /**
+   * @throws 1475672362
+   */
+  public getFormElementPropertyValidatorDefinition(validatorIdentifier: string): FormElementPropertyValidatorDefinition {
+    assert(this.getUtility().isNonEmptyString(validatorIdentifier), 'Invalid parameter "validatorIdentifier"', 1475672362);
+
+    const validatorDefinition = this.getRepository().getFormEditorDefinition('formElementPropertyValidators', validatorIdentifier);
+    // Return a dereferenced object
+    return $.extend(true, {}, validatorDefinition);
+  }
+
+  public getCurrentlySelectedPageIndex(): number {
+    return this.getApplicationStateStack().getCurrentState('currentlySelectedPageIndex');
+  }
+
+  public refreshCurrentlySelectedPageIndex(): void {
+    this.getApplicationStateStack().setCurrentState(
+      'currentlySelectedPageIndex',
+      this.getPageIndexFromFormElement(this.getCurrentlySelectedFormElement())
+    );
+  }
+
+  /**
+   * @throws 1477786068
+   */
+  public getCurrentlySelectedPage(): FormElement {
+    const currentPage = this.getRepository().getRootFormElement().get('renderables')[this.getCurrentlySelectedPageIndex()];
+    assert('object' === $.type(currentPage), 'No page found', 1477786068);
+    return currentPage;
+  }
+
+  public getLastTopLevelElementOnCurrentPage(): FormElement {
+
+    const renderables = this.getCurrentlySelectedPage().get('renderables');
+    if (this.getUtility().isUndefinedOrNull(renderables)) {
+      return undefined;
+    }
+    return renderables[renderables.length - 1];
+  }
+
+  public getLastFormElementWithinParentFormElement(formElement: FormElement): FormElement {
+    formElement = this.getRepository().findFormElement(formElement);
+    if (formElement.get('__identifierPath') === this.getRootFormElement().get('__identifierPath')) {
+      return formElement;
+    }
+    return formElement.get('__parentRenderable').get('renderables')[formElement.get('__parentRenderable').get('renderables').length - 1];
+  }
+
+  public getPageIndexFromFormElement(formElement: FormElement | string): number {
+    formElement = this.getRepository().findFormElement(formElement);
+
+    return this.getRepository().getIndexForEnclosingCompositeFormElementWhichIsOnTopLevelForFormElement(
+      formElement
+    );
+  }
+
+  public renderCurrentFormPage(): void {
+    this.renderFormPage(this.getCurrentlySelectedPageIndex());
+  }
+
+  /**
+   * @throws 1475446442
+   */
+  public renderFormPage(pageIndex: number): void {
+    assert('number' === $.type(pageIndex), 'Invalid parameter "pageIndex"', 1475446442);
+    this.getDataBackend().renderFormDefinitionPage(pageIndex);
+  }
+
+  public findEnclosingCompositeFormElementWhichIsNotOnTopLevel(
+    formElement: FormElement | string
+  ): FormElement | null {
+    return this.getRepository().findEnclosingCompositeFormElementWhichIsNotOnTopLevel(
+      this.getRepository().findFormElement(formElement)
+    );
+  }
+
+  /**
+   * @todo deprecate, method is unused
+   */
+  public findEnclosingGridRowFormElement(
+    formElement: FormElement | string
+  ): FormElement | null {
+    return this.getRepository().findEnclosingGridRowFormElement(
+      this.getRepository().findFormElement(formElement)
+    );
+  }
+
+  public getNonCompositeNonToplevelFormElements(): FormElement[] {
+    return this.getRepository().getNonCompositeNonToplevelFormElements();
+  }
+
+  public isRootFormElementSelected(): boolean {
+    return (this.getCurrentlySelectedFormElement().get('__identifierPath') === this.getRootFormElement().get('__identifierPath'));
+  }
+
+  public getViewModel(): ViewModel {
+    return this.viewModel;
+  }
+
+  public saveFormDefinition(): void {
+    this.getDataBackend().saveFormDefinition();
+  }
+
+  /**
+   * @throws 1473200696
+   */
+  public run(): FormEditor {
+    if (this.isRunning) {
+      throw 'You can not run the app twice (1473200696)';
+    }
+
+    try {
+      this.bootstrap();
+      this.isRunning = true;
+    } catch(error) {
+      Notification.error(
+        TYPO3.lang['formEditor.error.headline'],
+        TYPO3.lang['formEditor.error.message']
+        + '\r\n'
+        + '\r\n'
+        + TYPO3.lang['formEditor.error.technicalReason']
+        + '\r\n'
+        + error.message);
+    }
+    return this;
+  }
+
+  private saveApplicationState(): void {
+    this.getApplicationStateStack().addAndReset({
+      formDefinition: this.getApplicationStateStack().getCurrentState('formDefinition').clone(),
+      currentlySelectedPageIndex: this.getApplicationStateStack().getCurrentState('currentlySelectedPageIndex'),
+      currentlySelectedFormElementIdentifierPath: this.getApplicationStateStack().getCurrentState('currentlySelectedFormElementIdentifierPath')
+    });
+  }
+
+  private getDataBackend(): DataBackend {
+    return Core.getDataBackend();
+  }
+
+  private getFactory(): Factory {
+    return Core.getFactory();
+  }
+
+  private getRepository(): Repository {
+    return Core.getRepository();
+  }
+
+  private getPropertyValidationService(): PropertyValidationService {
+    return Core.getPropertyValidationService();
+  }
+
+  private getApplicationStateStack(): ApplicationStateStack {
+    return Core.getApplicationStateStack();
+  }
+
+  /**
+   * @publish ajax/beforeSend
+   * @publish ajax/complete
+   */
+  private ajaxSetup(): void {
+    $.ajaxSetup({
+      beforeSend: () => {
+        this.getPublisherSubscriber().publish('ajax/beforeSend');
+      },
+      complete: () => {
+        this.getPublisherSubscriber().publish('ajax/complete');
+      }
+    });
+  }
+
+  /**
+   * @throws 1475379748
+   * @throws 1475379749
+   * @throws 1475927876
+   */
+  private dataBackendSetup(endpoints: Endpoints, prototypeName: string, formPersistenceIdentifier: string): void {
+    assert('object' === $.type(endpoints), 'Invalid parameter "endpoints"', 1475379748);
+    assert(this.getUtility().isNonEmptyString(prototypeName), 'Invalid parameter "prototypeName"', 1475927876);
+    assert(this.getUtility().isNonEmptyString(formPersistenceIdentifier), 'Invalid parameter "formPersistenceIdentifier"', 1475379749);
+
+    Core.getDataBackend().setEndpoints(endpoints);
+    Core.getDataBackend().setPrototypeName(prototypeName);
+    Core.getDataBackend().setPersistenceIdentifier(formPersistenceIdentifier);
+  }
+
+  /**
+   * @throws 1475379750
+   */
+  private repositorySetup(formEditorDefinitions: FormEditorDefinitions): void {
+    assert('object' === $.type(formEditorDefinitions), 'Invalid parameter "formEditorDefinitions"', 1475379750);
+
+    this.getRepository().setFormEditorDefinitions(formEditorDefinitions);
+  }
+
+  /**
+   * @throws 1475492374
+   */
+  private viewSetup(additionalViewModelModules: AdditionalViewModelModules): void {
+    assert('function' === $.type(this.viewModel.bootstrap), 'The view model does not implement the method "bootstrap"', 1475492374);
+
+    if (this.getUtility().isUndefinedOrNull(additionalViewModelModules)) {
+      additionalViewModelModules = [];
+    }
+    this.viewModel.bootstrap(formEditorInstance, additionalViewModelModules);
+  }
+
+  /**
+   * @throws 1475492032
+   */
+  private mediatorSetup(): void {
+    assert('function' === $.type(this.mediator.bootstrap), 'The mediator does not implement the method "bootstrap"', 1475492032);
+    this.mediator.bootstrap(formEditorInstance, this.viewModel);
+  }
+
+  /**
+   * @throws 1475379751
+   */
+  private applicationStateStackSetup(
+    rootFormElement: FormElementDefinition,
+    maximumUndoSteps: number
+  ): void {
+    assert('object' === $.type(rootFormElement), 'Invalid parameter "rootFormElement"', 1475379751);
+
+    if ('number' !== $.type(maximumUndoSteps)) {
+      maximumUndoSteps = 10;
+    }
+    this.getApplicationStateStack().setMaximalStackSize(maximumUndoSteps);
+
+    this.getApplicationStateStack().addAndReset({
+      currentlySelectedPageIndex: 0,
+      currentlySelectedFormElementIdentifierPath: rootFormElement.identifier
+    }, true);
+
+    this.getApplicationStateStack().setCurrentState('formDefinition', this.getFactory().createFormElement(rootFormElement, undefined, undefined, true));
+  }
+
+  private bootstrap(): void {
+    this.mediatorSetup();
+    this.ajaxSetup();
+    this.dataBackendSetup(this.configuration.endpoints, this.configuration.prototypeName, this.configuration.formPersistenceIdentifier);
+    this.repositorySetup(this.configuration.formEditorDefinitions);
+    this.applicationStateStackSetup(this.configuration.formDefinition, this.configuration.maximumUndoSteps);
+    this.setCurrentlySelectedFormElement(this.getRepository().getRootFormElement());
+
+    this.viewSetup(this.configuration.additionalViewModelModules);
+  }
+}
+
+let formEditorInstance: FormEditor = null;
+
+/**
+ * @public
+ * @static
+ *
+ * Implement the "Singleton Pattern".
+ *
+ * Return a singleton instance of a
+ * "FormEditor" object.
+ */
+export function getInstance(
+  configuration: FormEditorConfiguration,
+  mediator: Mediator,
+  viewModel: ViewModel
+): FormEditor {
+  if (formEditorInstance === null) {
+    formEditorInstance = new FormEditor(configuration, mediator, viewModel);
+  }
+  return formEditorInstance;
+}
+
+declare global {
+  interface PublisherSubscriberTopicArgumentsMap {
+    'core/currentlySelectedFormElementChanged': readonly [
+      formElement: FormElement
+    ];
+    'ajax/beforeSend': undefined;
+    'ajax/complete': undefined;
+  }
+}
diff --git a/Build/Sources/TypeScript/form/backend/helper.ts b/Build/Sources/TypeScript/form/backend/helper.ts
index d5ea289a463b..d755679d839b 100644
--- a/Build/Sources/TypeScript/form/backend/helper.ts
+++ b/Build/Sources/TypeScript/form/backend/helper.ts
@@ -20,15 +20,6 @@ interface ModuleRequirements {
   mediator?: JavaScriptItemPayload;
 }
 
-interface FormEditorLike {
-  getInstance(options: any, mediator: MediatorLike, viewModel: typeof import('@typo3/form/backend/form-manager/view-model')): FormEditorLike;
-  run(): FormEditorLike;
-}
-
-// eslint-disable-next-line @typescript-eslint/no-empty-interface
-interface MediatorLike {
-}
-
 /**
  * @exports @typo3/form/backend/helper
  */
@@ -40,7 +31,11 @@ export class Helper {
         loadModule(requirements.mediator),
         loadModule(requirements.viewModel),
       ]).then((modules: [any, any, any]) =>
-        ((app: FormEditorLike, mediator: MediatorLike, viewModel: typeof import('@typo3/form/backend/form-manager/view-model')) => {
+        ((
+          app: typeof import('@typo3/form/backend/form-editor'),
+          mediator: import('@typo3/form/backend/form-editor').Mediator,
+          viewModel: import('@typo3/form/backend/form-editor').ViewModel
+        ) => {
           window.TYPO3.FORMEDITOR_APP = app.getInstance(options, mediator, viewModel).run();
         })(...modules)
       );
diff --git a/typo3/sysext/form/Resources/Public/JavaScript/backend/form-editor.js b/typo3/sysext/form/Resources/Public/JavaScript/backend/form-editor.js
index b58dfd21f75f..9f1fc2d1a8a2 100644
--- a/typo3/sysext/form/Resources/Public/JavaScript/backend/form-editor.js
+++ b/typo3/sysext/form/Resources/Public/JavaScript/backend/form-editor.js
@@ -10,1159 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-
-/**
- * Module: @typo3/form/backend/form-editor
- */
-import $ from 'jquery';
-import * as Core from '@typo3/form/backend/form-editor/core.js';
-import Notification from '@typo3/backend/notification.js';
-
-const {
-  getInstance
-} = factory($, Core, Notification);
-
-export {
-  getInstance
-};
-
-function factory($, core, Notification) {
-  /**
-   * Return a static method named "getInstance".
-   * Use this method to create the formeditor app.
-   */
-  return (function(_core, Notification) {
-
-    /**
-     * @private
-     *
-     * Hold the instance (Singleton Pattern)
-     */
-    var _formEditorInstance = null;
-
-    /**
-     * @public
-     *
-     * @param object _configuration
-     * @param object _mediator
-     * @param object _viewModel
-     * @return object
-     */
-    function FormEditor(_configuration, _mediator, _viewModel) {
-
-      /**
-       * @private
-       *
-       * @var bool
-       */
-      var _isRunning = false;
-
-      /**
-       * @private
-       *
-       * @var bool
-       */
-      var _unsavedContent = false;
-
-      /**
-       * @private
-       *
-       * @var bool
-       */
-      var _previewMode = false;
-
-      /**
-       * @public
-       *
-       * @return object
-       */
-      function getPublisherSubscriber() {
-        return _core.getPublisherSubscriber();
-      };
-
-      /**
-       * @public
-       *
-       * @return void
-       */
-      function _saveApplicationState() {
-
-        _getApplicationStateStack().addAndReset({
-          formDefinition: _getApplicationStateStack().getCurrentState('formDefinition').clone(),
-          currentlySelectedPageIndex: _getApplicationStateStack().getCurrentState('currentlySelectedPageIndex'),
-          currentlySelectedFormElementIdentifierPath: _getApplicationStateStack().getCurrentState('currentlySelectedFormElementIdentifierPath')
-        });
-      };
-
-      /**
-       * @public
-       *
-       * @return void
-       */
-      function undoApplicationState() {
-        _getApplicationStateStack().incrementCurrentStackPointer();
-      };
-
-      /**
-       * @public
-       *
-       * @return void
-       */
-      function redoApplicationState() {
-        _getApplicationStateStack().decrementCurrentStackPointer();
-      };
-
-      /**
-       * @public
-       *
-       * @return int
-       */
-      function getMaximalApplicationStates() {
-        return _getApplicationStateStack().getMaximalStackSize();
-      };
-
-      /**
-       * @public
-       *
-       * @return int
-       */
-      function getCurrentApplicationStates() {
-        return _getApplicationStateStack().getCurrentStackSize();
-      };
-
-      /**
-       * @public
-       *
-       * @return int
-       */
-      function getCurrentApplicationStatePosition() {
-        return _getApplicationStateStack().getCurrentStackPointer();
-      };
-
-      /**
-       * @internal
-       *
-       * @return void
-       * @throws 1519855175
-       */
-      function setFormDefinition(formDefinition) {
-        assert('object' === $.type(formDefinition), 'Invalid parameter "formDefinition"', 1519855175);
-        _getApplicationStateStack().setCurrentState('formDefinition', _getFactory().createFormElement(formDefinition, undefined, undefined, true));
-      };
-
-      /**
-       * @public
-       *
-       * @param string type
-       * @return object
-       * @throws 1475378543
-       */
-      function getRunningAjaxRequest(type) {
-        assert(getUtility().isNonEmptyString(type), 'Invalid parameter "type"', 1475378543);
-        return _core.getRunningAjaxRequest(type);
-      };
-
-      /**
-       * @public
-       *
-       * @return object
-       */
-      function getUtility() {
-        return _core.getUtility();
-      };
-
-      /**
-       * @public
-       *
-       * @param mixed test
-       * @param string message
-       * @param int messageCode
-       * @return void
-       */
-      function assert(test, message, messageCode) {
-        getUtility().assert(test, message, messageCode);
-      };
-
-      /**
-       * @public
-       *
-       * @param string propertyPath
-       * @param string collectionElementIdentifier
-       * @param string collectionName
-       * @param object formElement
-       * @param boolean allowEmptyReturnValue
-       * @return string
-       */
-      function buildPropertyPath(propertyPath, collectionElementIdentifier, collectionName, formElement, allowEmptyReturnValue) {
-        if (getUtility().isUndefinedOrNull(formElement)) {
-          formElement = getCurrentlySelectedFormElement();
-        }
-        formElement = _getRepository().findFormElement(formElement);
-        return getUtility().buildPropertyPath(propertyPath, collectionElementIdentifier, collectionName, formElement, allowEmptyReturnValue);
-      };
-
-      /**
-       * @public
-       *
-       * @param string validatorIdentifier
-       * @param function func
-       * @return void
-       */
-      function addPropertyValidationValidator(validatorIdentifier, func) {
-        _getPropertyValidationService().addValidator(validatorIdentifier, func);
-      };
-
-      /**
-       * @public
-       *
-       * @param string propertyPath
-       * @return object
-       */
-      function validateCurrentlySelectedFormElementProperty(propertyPath) {
-        return validateFormElementProperty(
-          getCurrentlySelectedFormElement(),
-          propertyPath
-        );
-      };
-
-      /**
-       * @public
-       *
-       * @param object formElement
-       * @param string propertyPath
-       * @return object
-       */
-      function validateFormElementProperty(formElement, propertyPath) {
-        formElement = _getRepository().findFormElement(formElement);
-        return _getPropertyValidationService().validateFormElementProperty(formElement, propertyPath);
-      };
-
-      /**
-       * @public
-       *
-       * @param object formElement
-       * @return object
-       */
-      function validateFormElement(formElement) {
-        formElement = _getRepository().findFormElement(formElement);
-        return _getPropertyValidationService().validateFormElement(formElement);
-      };
-
-      /**
-       * @public
-       *
-       * @param object validationResults
-       * @return boolean
-       */
-      function validationResultsHasErrors(validationResults) {
-        return _getPropertyValidationService().validationResultsHasErrors(validationResults);
-      };
-
-      /**
-       * @public
-       *
-       * @param object formElement
-       * @param boolean returnAfterFirstMatch
-       * @return object
-       */
-      function validateFormElementRecursive(formElement, returnAfterFirstMatch) {
-        formElement = _getRepository().findFormElement(formElement);
-        return _getPropertyValidationService().validateFormElementRecursive(formElement, returnAfterFirstMatch);
-      };
-
-      /**
-       * @public
-       *
-       * @param bool unsavedContent
-       * @return void
-       * @throws 1475378544
-       */
-      function setUnsavedContent(unsavedContent) {
-        assert('boolean' === $.type(unsavedContent), 'Invalid parameter "unsavedContent"', 1475378544);
-        _unsavedContent = unsavedContent;
-      };
-
-      /**
-       * @public
-       *
-       * @return boolean
-       */
-      function getUnsavedContent() {
-        return _unsavedContent;
-      };
-
-      /**
-       * @public
-       *
-       * @return object
-       */
-      function getRootFormElement() {
-        return _getRepository().getRootFormElement();
-      };
-
-      /**
-       * @public
-       *
-       * @return string
-       */
-      function getCurrentlySelectedFormElement() {
-        return _getRepository().findFormElementByIdentifierPath(_getApplicationStateStack().getCurrentState('currentlySelectedFormElementIdentifierPath'));
-      };
-
-      /**
-       * @public
-       *
-       * @param string|object formElement
-       * @param boolean doNotRefreshCurrentlySelectedPageIndex
-       * @return void
-       * @publish core/currentlySelectedFormElementChanged
-       */
-      function setCurrentlySelectedFormElement(formElement, doNotRefreshCurrentlySelectedPageIndex) {
-        doNotRefreshCurrentlySelectedPageIndex = !!doNotRefreshCurrentlySelectedPageIndex;
-
-        formElement = _getRepository().findFormElement(formElement);
-        _getApplicationStateStack().setCurrentState('currentlySelectedFormElementIdentifierPath', formElement.get('__identifierPath'));
-
-        if (!doNotRefreshCurrentlySelectedPageIndex) {
-          refreshCurrentlySelectedPageIndex();
-        }
-        getPublisherSubscriber().publish('core/currentlySelectedFormElementChanged', [formElement]);
-      };
-
-      /**
-       * @public
-       *
-       * @param string identifierPath
-       * @return object
-       * @throws 1475378545
-       */
-      function getFormElementByIdentifierPath(identifierPath) {
-        assert(getUtility().isNonEmptyString(identifierPath), 'Invalid parameter "identifierPath"', 1475378545);
-        return _getRepository().findFormElementByIdentifierPath(identifierPath);
-      };
-
-      /**
-       * @public
-       *
-       * @param string identifierPath
-       * @return bool
-       */
-      function isFormElementIdentifierUsed(formElementIdentifier) {
-        return _getRepository().isFormElementIdentifierUsed(formElementIdentifier);
-      }
-
-      /**
-       * @public
-       *
-       * @param string formElementType
-       * @param string|object referenceFormElement
-       * @param boolean disablePublishersOnSet
-       * @return object
-       */
-      function createAndAddFormElement(formElementType, referenceFormElement, disablePublishersOnSet) {
-        var formElement;
-        formElement = addFormElement(createFormElement(formElementType, disablePublishersOnSet), referenceFormElement, disablePublishersOnSet);
-        formElement.set('renderables', formElement.get('renderables'));
-        return formElement;
-      };
-
-      /**
-       * @public
-       *
-       * @param object formElement
-       * @param string|object referenceFormElement
-       * @param boolean disablePublishersOnSet
-       * @return object
-       * @throws 1475434337
-       */
-      function addFormElement(formElement, referenceFormElement, disablePublishersOnSet) {
-        _saveApplicationState();
-
-        if (getUtility().isUndefinedOrNull(referenceFormElement)) {
-          referenceFormElement = getCurrentlySelectedFormElement();
-        }
-        referenceFormElement = _getRepository().findFormElement(referenceFormElement);
-        assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475434337);
-        return _getRepository().addFormElement(formElement, referenceFormElement, true, disablePublishersOnSet);
-      };
-
-      /**
-       * @public
-       *
-       * @param string formElementType
-       * @param boolean disablePublishersOnSet
-       * @return object
-       * @throws 1475434336
-       * @throws 1475435857
-       */
-      function createFormElement(formElementType, disablePublishersOnSet) {
-        var formElementDefinition, identifier;
-        assert(getUtility().isNonEmptyString(formElementType), 'Invalid parameter "formElementType"', 1475434336);
-
-        identifier = _getRepository().getNextFreeFormElementIdentifier(formElementType);
-        formElementDefinition = getFormElementDefinitionByType(formElementType);
-        return _getFactory().createFormElement({
-          type: formElementType,
-          identifier: identifier,
-          label: formElementDefinition['label'] || formElementType
-        }, undefined, undefined, undefined, disablePublishersOnSet);
-      };
-
-      /**
-       * @public
-       *
-       * @param string|object formElementToRemove
-       * @param boolean disablePublishersOnSet
-       * @return object
-       */
-      function removeFormElement(formElementToRemove, disablePublishersOnSet) {
-        var parentFormElement;
-        _saveApplicationState();
-
-        formElementToRemove = _getRepository().findFormElement(formElementToRemove);
-        parentFormElement = formElementToRemove.get('__parentRenderable');
-        _getRepository().removeFormElement(formElementToRemove, true, disablePublishersOnSet);
-        return parentFormElement;
-      };
-
-      /**
-       * @public
-       *
-       * @param string|object formElementToMove
-       * @param string position
-       * @param string|object referenceFormElement
-       * @param boolean disablePublishersOnSet
-       * @return string
-       * @throws 1475378551
-       */
-      function moveFormElement(formElementToMove, position, referenceFormElement, disablePublishersOnSet) {
-        _saveApplicationState();
-
-        formElementToMove = _getRepository().findFormElement(formElementToMove);
-        referenceFormElement = _getRepository().findFormElement(referenceFormElement);
-
-        assert('after' === position || 'before' === position || 'inside' === position, 'Invalid position "' + position + '"', 1475378551);
-
-        formElementToMove = _getRepository().moveFormElement(formElementToMove, position, referenceFormElement, true);
-        disablePublishersOnSet = !!disablePublishersOnSet;
-        if (!disablePublishersOnSet) {
-          formElementToMove.get('__parentRenderable').set('renderables', formElementToMove.get('__parentRenderable').get('renderables'));
-        }
-        return formElementToMove;
-      };
-
-      /**
-       * @public
-       *
-       * @param string collectionElementIdentifier
-       * @param string collectionName
-       * @param string formElement
-       * @return object (dereferenced)
-       * @throws 1475378555
-       * @throws 1475378556
-       * @throws 1475446108
-       */
-      function getPropertyCollectionElementConfiguration(collectionElementIdentifier, collectionName, formElement) {
-        var collection, collectionElement, formElementDefinition;
-        if (getUtility().isUndefinedOrNull(formElement)) {
-          formElement = getCurrentlySelectedFormElement();
-        }
-        formElement = _getRepository().findFormElement(formElement);
-
-        assert(getUtility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475378555);
-        assert(getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475378556);
-
-        formElementDefinition = getFormElementDefinitionByType(formElement.get('type'));
-        if (!getUtility().isUndefinedOrNull(formElementDefinition['propertyCollections'])) {
-          collection = formElementDefinition['propertyCollections'][collectionName];
-          assert(!getUtility().isUndefinedOrNull(collection), 'Invalid collection name "' + collectionName + '"', 1475446108);
-          collectionElement = _getRepository().findCollectionElementByIdentifierPath(collectionElementIdentifier, collection);
-          return $.extend(true, {}, collectionElement);
-        } else {
-          return {};
-        }
-      };
-
-      /**
-       * @public
-       *
-       * @param string collectionElementIdentifier
-       * @param string collectionName
-       * @param string formElement
-       * @return int
-       * @throws 1475378557
-       * @throws 1475378558
-       */
-      function getIndexFromPropertyCollectionElement(collectionElementIdentifier, collectionName, formElement) {
-        var indexFromPropertyCollectionElement;
-        if (getUtility().isUndefinedOrNull(formElement)) {
-          formElement = getCurrentlySelectedFormElement();
-        }
-        formElement = _getRepository().findFormElement(formElement);
-
-        assert(getUtility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475378557);
-        assert(getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475378558);
-
-        indexFromPropertyCollectionElement = _getRepository().getIndexFromPropertyCollectionElementByIdentifier(
-          collectionElementIdentifier,
-          collectionName,
-          formElement
-        );
-
-        return indexFromPropertyCollectionElement;
-      };
-
-      /**
-       * @public
-       *
-       * @param string collectionElementIdentifier
-       * @param string collectionName
-       * @param object formElement
-       * @param object collectionElementConfiguration
-       * @param string referenceCollectionElementIdentifier
-       * @return object
-       */
-      function createAndAddPropertyCollectionElement(collectionElementIdentifier, collectionName, formElement, collectionElementConfiguration, referenceCollectionElementIdentifier) {
-        return addPropertyCollectionElement(createPropertyCollectionElement(collectionElementIdentifier, collectionName, collectionElementConfiguration), collectionName, formElement, referenceCollectionElementIdentifier);
-      };
-
-      /**
-       * @public
-       *
-       * @param object collectionElement
-       * @param string collectionName
-       * @param string|object formElement
-       * @param string referenceCollectionElementIdentifier
-       * @return object
-       * @throws 1475443300
-       * @throws 1475443301
-       */
-      function addPropertyCollectionElement(collectionElement, collectionName, formElement, referenceCollectionElementIdentifier) {
-        var collection;
-        _saveApplicationState();
-
-        if (getUtility().isUndefinedOrNull(formElement)) {
-          formElement = getCurrentlySelectedFormElement();
-        }
-        formElement = _getRepository().findFormElement(formElement);
-
-        assert('object' === $.type(collectionElement), 'Invalid parameter "collectionElement"', 1475443301);
-        assert(getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475443300);
-
-        if (getUtility().isUndefinedOrNull(referenceCollectionElementIdentifier)) {
-          collection = formElement.get(collectionName);
-          if ('array' === $.type(collection) && collection.length > 0) {
-            referenceCollectionElementIdentifier = collection[collection.length - 1]['identifier'];
-          }
-        }
-
-        return _getRepository().addPropertyCollectionElement(
-          collectionElement,
-          collectionName,
-          formElement,
-          referenceCollectionElementIdentifier,
-          false
-        );
-      };
-
-      /**
-       * @public
-       *
-       * @param string collectionElementIdentifier
-       * @param string collectionName
-       * @param object collectionElementConfiguration
-       * @return void
-       * @throws 1475378559
-       * @throws 1475378560
-       */
-      function createPropertyCollectionElement(collectionElementIdentifier, collectionName, collectionElementConfiguration) {
-        assert(getUtility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475378559);
-        assert(getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475378560);
-        if ('object' !== $.type(collectionElementConfiguration)) {
-          collectionElementConfiguration = {};
-        }
-
-        return _getFactory().createPropertyCollectionElement(collectionElementIdentifier, collectionElementConfiguration, collectionName);
-      };
-
-      /**
-       * @public
-       *
-       * @param string collectionElementIdentifier
-       * @param string collectionName
-       * @param string formElement
-       * @param bool disablePublishersOnSet
-       * @return void
-       * @throws 1475378561
-       * @throws 1475378562
-       */
-      function removePropertyCollectionElement(collectionElementIdentifier, collectionName, formElement, disablePublishersOnSet) {
-        _saveApplicationState();
-
-        if (getUtility().isUndefinedOrNull(formElement)) {
-          formElement = getCurrentlySelectedFormElement();
-        }
-        formElement = _getRepository().findFormElement(formElement);
-
-        assert(getUtility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475378561);
-        assert(getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475378562);
-
-        _getRepository().removePropertyCollectionElementByIdentifier(
-          formElement,
-          collectionElementIdentifier,
-          collectionName,
-          true
-        );
-
-        disablePublishersOnSet = !!disablePublishersOnSet;
-        if (!disablePublishersOnSet) {
-          getPublisherSubscriber().publish('core/formElement/somePropertyChanged', ['__fakeProperty']);
-        }
-      };
-
-      /**
-       * @public
-       *
-       * @param string collectionElementToMove
-       * @param string position
-       * @param string referenceCollectionElement
-       * @param string collectionName
-       * @param object formElement
-       * @param boolean disablePublishersOnSet
-       * @return string
-       * @throws 1477404352
-       * @throws 1477404353
-       * @throws 1477404354
-       * @throws 1477404355
-       */
-      function movePropertyCollectionElement(collectionElementToMove, position, referenceCollectionElement, collectionName, formElement, disablePublishersOnSet) {
-        _saveApplicationState();
-
-        formElement = _getRepository().findFormElement(formElement);
-
-        assert('string' === $.type(collectionElementToMove), 'Invalid parameter "collectionElementToMove"', 1477404352);
-        assert('string' === $.type(referenceCollectionElement), 'Invalid parameter "referenceCollectionElement"', 1477404353);
-        assert('after' === position || 'before' === position, 'Invalid position "' + position + '"', 1477404354);
-        assert(getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1477404355);
-
-        return _getRepository().movePropertyCollectionElement(collectionElementToMove, position, referenceCollectionElement, collectionName, formElement, disablePublishersOnSet);
-      };
-
-      /**
-       * @public
-       *
-       * @param string elementType
-       * @param string formElementDefinitionKey
-       * @returnmixed
-       * @throws 1475378563
-       */
-      function getFormElementDefinitionByType(elementType, formElementDefinitionKey) {
-        var formElementDefinition;
-        assert(getUtility().isNonEmptyString(elementType), 'Invalid parameter "elementType"', 1475378563);
-
-        formElementDefinition = _getRepository().getFormEditorDefinition('formElements', elementType);
-
-        if (!getUtility().isUndefinedOrNull(formElementDefinitionKey)) {
-          formElementDefinition = formElementDefinition[formElementDefinitionKey];
-        }
-
-        if ('object' === $.type(formElementDefinition) || 'array' === $.type(formElementDefinition)) {
-          return $.extend(true, {}, formElementDefinition);
-        } else {
-          return formElementDefinition;
-        }
-      };
-
-      /**
-       * @public
-       *
-       * @param object formElement
-       * @param string formElementDefinitionKey
-       * @return mixed
-       */
-      function getFormElementDefinition(formElement, formElementDefinitionKey) {
-        formElement = _getRepository().findFormElement(formElement);
-        return getFormElementDefinitionByType(formElement.get('type'), formElementDefinitionKey);
-      };
-
-      /**
-       * @public
-       *
-       * @param string collectionName
-       * @param string collectionElementIdentifier
-       * @return mixed
-       */
-      function getFormEditorDefinition(definitionName, subject) {
-        return _getRepository().getFormEditorDefinition(definitionName, subject);
-      };
-
-      /**
-       * @public
-       *
-       * @param string validatorIdentifier
-       * @return object (dereferenced)
-       * @throws 1475672362
-       */
-      function getFormElementPropertyValidatorDefinition(validatorIdentifier) {
-        var validatorDefinition;
-        assert(getUtility().isNonEmptyString(validatorIdentifier), 'Invalid parameter "validatorIdentifier"', 1475672362);
-
-        validatorDefinition = _getRepository().getFormEditorDefinition('formElementPropertyValidators', validatorIdentifier);
-        return $.extend(true, {}, validatorDefinition);
-      };
-
-      /**
-       * @public
-       *
-       * @return int
-       */
-      function getCurrentlySelectedPageIndex() {
-        return _getApplicationStateStack().getCurrentState('currentlySelectedPageIndex');
-      };
-
-      /**
-       * @public
-       *
-       * @return void
-       */
-      function refreshCurrentlySelectedPageIndex() {
-        _getApplicationStateStack().setCurrentState('currentlySelectedPageIndex', getPageIndexFromFormElement(getCurrentlySelectedFormElement()));
-      };
-
-      /**
-       * @public
-       *
-       * @return object
-       * @throws 1477786068
-       */
-      function getCurrentlySelectedPage() {
-        var currentPage;
-
-        currentPage = _getRepository().getRootFormElement().get('renderables')[getCurrentlySelectedPageIndex()];
-        assert('object' === $.type(currentPage), 'No page found', 1477786068);
-        return currentPage;
-      };
-
-      /**
-       * @public
-       *
-       * @return object
-       */
-      function getLastTopLevelElementOnCurrentPage() {
-        var lastRenderable, renderables;
-
-        renderables = getCurrentlySelectedPage().get('renderables');
-        if (getUtility().isUndefinedOrNull(renderables)) {
-          return undefined;
-        }
-        lastRenderable = renderables[renderables.length - 1];
-        return lastRenderable;
-      };
-
-      /**
-       * @public
-       *
-       * @param object
-       * @return object
-       */
-      function getLastFormElementWithinParentFormElement(formElement) {
-        var lastElement;
-
-        formElement = _getRepository().findFormElement(formElement);
-        if (formElement.get('__identifierPath') === getRootFormElement().get('__identifierPath')) {
-          return formElement;
-        }
-        return formElement.get('__parentRenderable').get('renderables')[formElement.get('__parentRenderable').get('renderables').length - 1];
-      };
-
-      /**
-       * @public
-       *
-       * @param object
-       * @return int
-       */
-      function getPageIndexFromFormElement(formElement) {
-        formElement = _getRepository().findFormElement(formElement);
-
-        return _getRepository().getIndexForEnclosingCompositeFormElementWhichIsOnTopLevelForFormElement(
-          formElement
-        );
-      };
-
-      /**
-       * @public
-       *
-       * @return void
-       */
-      function renderCurrentFormPage() {
-        renderFormPage(getCurrentlySelectedPageIndex());
-      };
-
-      /**
-       * @public
-       *
-       * @param int pageIndex
-       * @return void
-       * @throws 1475446442
-       */
-      function renderFormPage(pageIndex) {
-        assert('number' === $.type(pageIndex), 'Invalid parameter "pageIndex"', 1475446442);
-        _getDataBackend().renderFormDefinitionPage(pageIndex);
-      };
-
-      /**
-       * @public
-       *
-       * @param object formElement
-       * @return object|null
-       */
-      function findEnclosingCompositeFormElementWhichIsNotOnTopLevel(formElement) {
-        return _getRepository().findEnclosingCompositeFormElementWhichIsNotOnTopLevel(
-          _getRepository().findFormElement(formElement)
-        );
-      };
-
-      /**
-       * @public
-       *
-       * @param object formElement
-       * @return object|null
-       */
-      function findEnclosingGridRowFormElement(formElement) {
-        return _getRepository().findEnclosingGridRowFormElement(
-          _getRepository().findFormElement(formElement)
-        );
-      };
-
-      /**
-       * @public
-       *
-       * @return object
-       */
-      function getNonCompositeNonToplevelFormElements() {
-        return _getRepository().getNonCompositeNonToplevelFormElements();
-      };
-
-      /**
-       * @public
-       *
-       * @return boolean
-       */
-      function isRootFormElementSelected() {
-        return (getCurrentlySelectedFormElement().get('__identifierPath') === getRootFormElement().get('__identifierPath'));
-      };
-
-      /**
-       * @public
-       *
-       * @return object
-       */
-      function getViewModel() {
-        return _viewModel;
-      };
-
-      /**
-       * @public
-       *
-       * @return void
-       */
-      function saveFormDefinition() {
-        _getDataBackend().saveFormDefinition();
-      };
-
-      /**
-       * @private
-       *
-       * @return object
-       */
-      function _getDataBackend() {
-        return _core.getDataBackend();
-      };
-
-      /**
-       * @private
-       *
-       * @return object
-       */
-      function _getFactory() {
-        return _core.getFactory();
-      };
-
-      /**
-       * @private
-       *
-       * @return object
-       */
-      function _getRepository() {
-        return _core.getRepository();
-      };
-
-      /**
-       * @private
-       *
-       * @return object
-       */
-      function _getPropertyValidationService() {
-        return _core.getPropertyValidationService();
-      };
-
-      /**
-       * @public
-       *
-       * @return object
-       */
-      function _getApplicationStateStack() {
-        return _core.getApplicationStateStack();
-      };
-
-      /**
-       * @private
-       *
-       * @return void
-       * @publish ajax/beforeSend
-       * @publish ajax/complete
-       */
-      function _ajaxSetup() {
-        $.ajaxSetup({
-          beforeSend: function() {
-            getPublisherSubscriber().publish('ajax/beforeSend');
-          },
-          complete: function() {
-            getPublisherSubscriber().publish('ajax/complete');
-          }
-        });
-      };
-
-      /**
-       * @private
-       *
-       * @param object endpoints
-       * @param string prototypeName
-       * @param string formPersistenceIdentifier
-       * @return void
-       * @throws 1475379748
-       * @throws 1475379749
-       * @throws 1475927876
-       */
-      function _dataBackendSetup(endpoints, prototypeName, formPersistenceIdentifier) {
-        assert('object' === $.type(endpoints), 'Invalid parameter "endpoints"', 1475379748);
-        assert(getUtility().isNonEmptyString(prototypeName), 'Invalid parameter "prototypeName"', 1475927876);
-        assert(getUtility().isNonEmptyString(formPersistenceIdentifier), 'Invalid parameter "formPersistenceIdentifier"', 1475379749);
-
-        _core.getDataBackend().setEndpoints(endpoints);
-        _core.getDataBackend().setPrototypeName(prototypeName);
-        _core.getDataBackend().setPersistenceIdentifier(formPersistenceIdentifier);
-      };
-
-      /**
-       * @private
-       *
-       * @param object formEditorDefinitions
-       * @return void
-       * @throws 1475379750
-       */
-      function _repositorySetup(formEditorDefinitions) {
-        assert('object' === $.type(formEditorDefinitions), 'Invalid parameter "formEditorDefinitions"', 1475379750);
-
-        _getRepository().setFormEditorDefinitions(formEditorDefinitions);
-      }
-
-      /**
-       * @private
-       *
-       * @param object additionalViewModelModules
-       * @return void
-       * @throws 1475492374
-       */
-      function _viewSetup(additionalViewModelModules) {
-        assert('function' === $.type(_viewModel.bootstrap), 'The view model does not implement the method "bootstrap"', 1475492374);
-
-        if (getUtility().isUndefinedOrNull(additionalViewModelModules)) {
-          additionalViewModelModules = [];
-        }
-        _viewModel.bootstrap(_formEditorInstance, additionalViewModelModules);
-      };
-
-      /**
-       * @private
-       *
-       * @return void
-       * @throws 1475492032
-       */
-      function _mediatorSetup() {
-        assert('function' === $.type(_mediator.bootstrap), 'The mediator does not implement the method "bootstrap"', 1475492032);
-        _mediator.bootstrap(_formEditorInstance, _viewModel);
-      };
-
-      /**
-       * @private
-       *
-       * @param object rootFormElement
-       * @param int maximumUndoSteps
-       * @return void
-       * @throws 1475379751
-       */
-      function _applicationStateStackSetup(rootFormElement, maximumUndoSteps) {
-        assert('object' === $.type(rootFormElement), 'Invalid parameter "rootFormElement"', 1475379751);
-
-        if ('number' !== $.type(maximumUndoSteps)) {
-          maximumUndoSteps = 10;
-        }
-        _getApplicationStateStack().setMaximalStackSize(maximumUndoSteps);
-
-        _getApplicationStateStack().addAndReset({
-          currentlySelectedPageIndex: 0,
-          currentlySelectedFormElementIdentifierPath: rootFormElement['identifier']
-        }, true);
-
-        _getApplicationStateStack().setCurrentState('formDefinition', _getFactory().createFormElement(rootFormElement, undefined, undefined, true));
-      };
-
-      /**
-       * @private
-       *
-       * @return void
-       */
-      function _bootstrap() {
-        _configuration = _configuration || {};
-
-        _mediatorSetup();
-        _ajaxSetup();
-        _dataBackendSetup(_configuration['endpoints'], _configuration['prototypeName'], _configuration['formPersistenceIdentifier']);
-        _repositorySetup(_configuration['formEditorDefinitions']);
-        _applicationStateStackSetup(_configuration['formDefinition'], _configuration['maximumUndoSteps']);
-        setCurrentlySelectedFormElement(_getRepository().getRootFormElement());
-
-        _viewSetup(_configuration['additionalViewModelModules']);
-      };
-
-      /**
-       * @public
-       *
-       * @return TYPO3/CMS/Form/Backend/FormEditor
-       * @throws 1473200696
-       */
-      function run() {
-        if (_isRunning) {
-          throw 'You can not run the app twice (1473200696)';
-        }
-
-        try {
-          _bootstrap();
-          _isRunning = true;
-        } catch(error) {
-          Notification.error(
-            TYPO3.lang['formEditor.error.headline'],
-            TYPO3.lang['formEditor.error.message']
-            + "\r\n"
-            + "\r\n"
-            + TYPO3.lang['formEditor.error.technicalReason']
-            + "\r\n"
-            + error.message);
-        }
-        return this;
-      };
-
-      /**
-       * Publish the public methods.
-       * Implements the "Revealing Module Pattern".
-       */
-      return {
-        getRootFormElement: getRootFormElement,
-
-        createAndAddFormElement: createAndAddFormElement,
-        createFormElement: createFormElement,
-        addFormElement: addFormElement,
-        moveFormElement: moveFormElement,
-        removeFormElement: removeFormElement,
-
-        getCurrentlySelectedFormElement: getCurrentlySelectedFormElement,
-        setCurrentlySelectedFormElement: setCurrentlySelectedFormElement,
-
-        getFormElementByIdentifierPath: getFormElementByIdentifierPath,
-        isFormElementIdentifierUsed: isFormElementIdentifierUsed,
-
-        createAndAddPropertyCollectionElement: createAndAddPropertyCollectionElement,
-        createPropertyCollectionElement: createPropertyCollectionElement,
-        addPropertyCollectionElement: addPropertyCollectionElement,
-        removePropertyCollectionElement: removePropertyCollectionElement,
-        movePropertyCollectionElement: movePropertyCollectionElement,
-        getIndexFromPropertyCollectionElement: getIndexFromPropertyCollectionElement,
-        getPropertyCollectionElementConfiguration: getPropertyCollectionElementConfiguration,
-
-        saveFormDefinition: saveFormDefinition,
-        renderCurrentFormPage: renderCurrentFormPage,
-        renderFormPage: renderFormPage,
-
-        getCurrentlySelectedPageIndex: getCurrentlySelectedPageIndex,
-        refreshCurrentlySelectedPageIndex: refreshCurrentlySelectedPageIndex,
-        getPageIndexFromFormElement: getPageIndexFromFormElement,
-        getCurrentlySelectedPage: getCurrentlySelectedPage,
-        getLastTopLevelElementOnCurrentPage: getLastTopLevelElementOnCurrentPage,
-        findEnclosingCompositeFormElementWhichIsNotOnTopLevel: findEnclosingCompositeFormElementWhichIsNotOnTopLevel,
-        findEnclosingGridRowFormElement: findEnclosingGridRowFormElement,
-        isRootFormElementSelected: isRootFormElementSelected,
-        getLastFormElementWithinParentFormElement: getLastFormElementWithinParentFormElement,
-        getNonCompositeNonToplevelFormElements: getNonCompositeNonToplevelFormElements,
-
-        getFormElementDefinitionByType: getFormElementDefinitionByType,
-        getFormElementDefinition: getFormElementDefinition,
-        getFormElementPropertyValidatorDefinition: getFormElementPropertyValidatorDefinition,
-        getFormEditorDefinition: getFormEditorDefinition,
-
-        getPublisherSubscriber: getPublisherSubscriber,
-        getRunningAjaxRequest: getRunningAjaxRequest,
-
-        setUnsavedContent: setUnsavedContent,
-        getUnsavedContent: getUnsavedContent,
-
-        addPropertyValidationValidator: addPropertyValidationValidator,
-        validateFormElementProperty: validateFormElementProperty,
-        validateCurrentlySelectedFormElementProperty: validateCurrentlySelectedFormElementProperty,
-        validateFormElement: validateFormElement,
-        validateFormElementRecursive: validateFormElementRecursive,
-        validationResultsHasErrors: validationResultsHasErrors,
-
-        getUtility: getUtility,
-        assert: assert,
-        buildPropertyPath: buildPropertyPath,
-
-        getViewModel: getViewModel,
-        undoApplicationState: undoApplicationState,
-        redoApplicationState: redoApplicationState,
-        getMaximalApplicationStates: getMaximalApplicationStates,
-        getCurrentApplicationStates: getCurrentApplicationStates,
-        getCurrentApplicationStatePosition: getCurrentApplicationStatePosition,
-        setFormDefinition: setFormDefinition,
-
-        run: run
-      };
-    };
-
-    /**
-     * Emulation of static methods
-     */
-    return {
-      /**
-       * @public
-       * @static
-       *
-       * Implement the "Singleton Pattern".
-       *
-       * Return a singleton instance of a
-       * "FormEditor" object.
-       *
-       * @param object configuration
-       * @param object mediator
-       * @param object viewModel
-       * @return object
-       */
-      getInstance: function(configuration, mediator, viewModel) {
-        if (_formEditorInstance === null) {
-          _formEditorInstance = new FormEditor(configuration, mediator, viewModel);
-        }
-        return _formEditorInstance;
-      }
-    };
-  })(core, Notification);
-}
+import $ from"jquery";import Notification from"@typo3/backend/notification.js";import*as Core from"@typo3/form/backend/form-editor/core.js";const assert=Core.assert;export class FormEditor{constructor(e,t,i){this.isRunning=!1,this.unsavedContent=!1,this.configuration=e||{},this.mediator=t,this.viewModel=i}getPublisherSubscriber(){return Core.getPublisherSubscriber()}undoApplicationState(){this.getApplicationStateStack().incrementCurrentStackPointer()}redoApplicationState(){this.getApplicationStateStack().decrementCurrentStackPointer()}getMaximalApplicationStates(){return this.getApplicationStateStack().getMaximalStackSize()}getCurrentApplicationStates(){return this.getApplicationStateStack().getCurrentStackSize()}getCurrentApplicationStatePosition(){return this.getApplicationStateStack().getCurrentStackPointer()}setFormDefinition(e){assert("object"===$.type(e),'Invalid parameter "formDefinition"',1519855175),this.getApplicationStateStack().setCurrentState("formDefinition",this.getFactory().createFormElement(e,void 0,void 0,!0))}getRunningAjaxRequest(e){return assert(this.getUtility().isNonEmptyString(e),'Invalid parameter "type"',1475378543),Core.getRunningAjaxRequest(e)}getUtility(){return Core.getUtility()}assert(e,t,i){this.getUtility().assert(e,t,i)}buildPropertyPath(e,t,i,r,n){this.getUtility().isUndefinedOrNull(r)&&(r=this.getCurrentlySelectedFormElement());const o=this.getRepository().findFormElement(r);return this.getUtility().buildPropertyPath(e,t,i,o,n)}addPropertyValidationValidator(e,t){this.getPropertyValidationService().addValidator(e,t)}validateCurrentlySelectedFormElementProperty(e){return this.validateFormElementProperty(this.getCurrentlySelectedFormElement(),e)}validateFormElementProperty(e,t){const i=this.getRepository().findFormElement(e);return this.getPropertyValidationService().validateFormElementProperty(i,t)}validateFormElement(e){const t=this.getRepository().findFormElement(e);return this.getPropertyValidationService().validateFormElement(t)}validationResultsHasErrors(e){return this.getPropertyValidationService().validationResultsHasErrors(e)}validateFormElementRecursive(e,t){const i=this.getRepository().findFormElement(e);return this.getPropertyValidationService().validateFormElementRecursive(i,t)}setUnsavedContent(e){assert("boolean"===$.type(e),'Invalid parameter "unsavedContent"',1475378544),this.unsavedContent=e}getUnsavedContent(){return this.unsavedContent}getRootFormElement(){return this.getRepository().getRootFormElement()}getCurrentlySelectedFormElement(){return this.getRepository().findFormElementByIdentifierPath(this.getApplicationStateStack().getCurrentState("currentlySelectedFormElementIdentifierPath"))}setCurrentlySelectedFormElement(e,t){t=!!t;const i=this.getRepository().findFormElement(e);this.getApplicationStateStack().setCurrentState("currentlySelectedFormElementIdentifierPath",i.get("__identifierPath")),t||this.refreshCurrentlySelectedPageIndex(),this.getPublisherSubscriber().publish("core/currentlySelectedFormElementChanged",[i])}getFormElementByIdentifierPath(e){return assert(this.getUtility().isNonEmptyString(e),'Invalid parameter "identifierPath"',1475378545),this.getRepository().findFormElementByIdentifierPath(e)}isFormElementIdentifierUsed(e){return this.getRepository().isFormElementIdentifierUsed(e)}createAndAddFormElement(e,t,i){const r=this.addFormElement(this.createFormElement(e,i),t,i);return r.set("renderables",r.get("renderables")),r}addFormElement(e,t,i){this.saveApplicationState(),this.getUtility().isUndefinedOrNull(t)&&(t=this.getCurrentlySelectedFormElement());const r=this.getRepository().findFormElement(t);return assert("object"===$.type(e),'Invalid parameter "formElement"',1475434337),this.getRepository().addFormElement(e,r,!0,i)}createFormElement(e,t){assert(this.getUtility().isNonEmptyString(e),'Invalid parameter "formElementType"',1475434336);const i=this.getRepository().getNextFreeFormElementIdentifier(e),r=this.getFormElementDefinitionByType(e,void 0);return this.getFactory().createFormElement({type:e,identifier:i,label:r.label||e},void 0,void 0,void 0,t)}removeFormElement(e,t){this.saveApplicationState();const i=this.getRepository().findFormElement(e),r=i.get("__parentRenderable");return this.getRepository().removeFormElement(i,!0,t),r}moveFormElement(e,t,i,r){this.saveApplicationState();let n=this.getRepository().findFormElement(e);const o=this.getRepository().findFormElement(i);return assert("after"===t||"before"===t||"inside"===t,'Invalid position "'+t+'"',1475378551),n=this.getRepository().moveFormElement(n,t,o,!0),(r=!!r)||n.get("__parentRenderable").set("renderables",n.get("__parentRenderable").get("renderables")),n}getPropertyCollectionElementConfiguration(e,t,i){let r,n;this.getUtility().isUndefinedOrNull(i)&&(i=this.getCurrentlySelectedFormElement());const o=this.getRepository().findFormElement(i);assert(this.getUtility().isNonEmptyString(e),'Invalid parameter "collectionElementIdentifier"',1475378555),assert(this.getUtility().isNonEmptyString(t),'Invalid parameter "collectionName"',1475378556);const a=this.getFormElementDefinitionByType(o.get("type"),void 0);return this.getUtility().isUndefinedOrNull(a.propertyCollections)?{}:(r=a.propertyCollections[t],assert(!this.getUtility().isUndefinedOrNull(r),'Invalid collection name "'+t+'"',1475446108),n=this.getRepository().findCollectionElementByIdentifierPath(e,r),$.extend(!0,{},n))}getIndexFromPropertyCollectionElement(e,t,i){this.getUtility().isUndefinedOrNull(i)&&(i=this.getCurrentlySelectedFormElement());const r=this.getRepository().findFormElement(i);assert(this.getUtility().isNonEmptyString(e),'Invalid parameter "collectionElementIdentifier"',1475378557),assert(this.getUtility().isNonEmptyString(t),'Invalid parameter "collectionName"',1475378558);return this.getRepository().getIndexFromPropertyCollectionElementByIdentifier(e,t,r)}createAndAddPropertyCollectionElement(e,t,i,r,n){return this.addPropertyCollectionElement(this.createPropertyCollectionElement(e,t,r),t,i,n)}addPropertyCollectionElement(e,t,i,r){let n;this.saveApplicationState(),this.getUtility().isUndefinedOrNull(i)&&(i=this.getCurrentlySelectedFormElement());const o=this.getRepository().findFormElement(i);return assert("object"===$.type(e),'Invalid parameter "collectionElement"',1475443301),assert(this.getUtility().isNonEmptyString(t),'Invalid parameter "collectionName"',1475443300),this.getUtility().isUndefinedOrNull(r)&&(n=o.get(t),"array"===$.type(n)&&n.length>0&&(r=n[n.length-1].identifier)),this.getRepository().addPropertyCollectionElement(e,t,o,r,!1)}createPropertyCollectionElement(e,t,i){return assert(this.getUtility().isNonEmptyString(e),'Invalid parameter "collectionElementIdentifier"',1475378559),assert(this.getUtility().isNonEmptyString(t),'Invalid parameter "collectionName"',1475378560),"object"!==$.type(i)&&(i={}),this.getFactory().createPropertyCollectionElement(e,i,t)}removePropertyCollectionElement(e,t,i,r){this.saveApplicationState(),this.getUtility().isUndefinedOrNull(i)&&(i=this.getCurrentlySelectedFormElement());const n=this.getRepository().findFormElement(i);assert(this.getUtility().isNonEmptyString(e),'Invalid parameter "collectionElementIdentifier"',1475378561),assert(this.getUtility().isNonEmptyString(t),'Invalid parameter "collectionName"',1475378562),this.getRepository().removePropertyCollectionElementByIdentifier(n,e,t,!0),(r=!!r)||this.getPublisherSubscriber().publish("core/formElement/somePropertyChanged",["__fakeProperty"])}movePropertyCollectionElement(e,t,i,r,n,o){this.saveApplicationState(),n=this.getRepository().findFormElement(n),assert("string"===$.type(e),'Invalid parameter "collectionElementToMove"',1477404352),assert("string"===$.type(i),'Invalid parameter "referenceCollectionElement"',1477404353),assert("after"===t||"before"===t,'Invalid position "'+t+'"',1477404354),assert(this.getUtility().isNonEmptyString(r),'Invalid parameter "collectionName"',1477404355),this.getRepository().movePropertyCollectionElement(e,t,i,r,n,o)}getFormElementDefinitionByType(e,t){assert(this.getUtility().isNonEmptyString(e),'Invalid parameter "elementType"',1475378563);const i=this.getRepository().getFormEditorDefinition("formElements",e);if(void 0!==t){const e=i[t];return null!==e&&"object"==typeof e?$.extend(!0,{},e):e}return null!==i&&"object"==typeof i?$.extend(!0,{},i):i}getFormElementDefinition(e,t){return e=this.getRepository().findFormElement(e),this.getFormElementDefinitionByType(e.get("type"),t)}getFormEditorDefinition(e,t){return this.getRepository().getFormEditorDefinition(e,t)}getFormElementPropertyValidatorDefinition(e){assert(this.getUtility().isNonEmptyString(e),'Invalid parameter "validatorIdentifier"',1475672362);const t=this.getRepository().getFormEditorDefinition("formElementPropertyValidators",e);return $.extend(!0,{},t)}getCurrentlySelectedPageIndex(){return this.getApplicationStateStack().getCurrentState("currentlySelectedPageIndex")}refreshCurrentlySelectedPageIndex(){this.getApplicationStateStack().setCurrentState("currentlySelectedPageIndex",this.getPageIndexFromFormElement(this.getCurrentlySelectedFormElement()))}getCurrentlySelectedPage(){const e=this.getRepository().getRootFormElement().get("renderables")[this.getCurrentlySelectedPageIndex()];return assert("object"===$.type(e),"No page found",1477786068),e}getLastTopLevelElementOnCurrentPage(){const e=this.getCurrentlySelectedPage().get("renderables");if(!this.getUtility().isUndefinedOrNull(e))return e[e.length-1]}getLastFormElementWithinParentFormElement(e){return(e=this.getRepository().findFormElement(e)).get("__identifierPath")===this.getRootFormElement().get("__identifierPath")?e:e.get("__parentRenderable").get("renderables")[e.get("__parentRenderable").get("renderables").length-1]}getPageIndexFromFormElement(e){return e=this.getRepository().findFormElement(e),this.getRepository().getIndexForEnclosingCompositeFormElementWhichIsOnTopLevelForFormElement(e)}renderCurrentFormPage(){this.renderFormPage(this.getCurrentlySelectedPageIndex())}renderFormPage(e){assert("number"===$.type(e),'Invalid parameter "pageIndex"',1475446442),this.getDataBackend().renderFormDefinitionPage(e)}findEnclosingCompositeFormElementWhichIsNotOnTopLevel(e){return this.getRepository().findEnclosingCompositeFormElementWhichIsNotOnTopLevel(this.getRepository().findFormElement(e))}findEnclosingGridRowFormElement(e){return this.getRepository().findEnclosingGridRowFormElement(this.getRepository().findFormElement(e))}getNonCompositeNonToplevelFormElements(){return this.getRepository().getNonCompositeNonToplevelFormElements()}isRootFormElementSelected(){return this.getCurrentlySelectedFormElement().get("__identifierPath")===this.getRootFormElement().get("__identifierPath")}getViewModel(){return this.viewModel}saveFormDefinition(){this.getDataBackend().saveFormDefinition()}run(){if(this.isRunning)throw"You can not run the app twice (1473200696)";try{this.bootstrap(),this.isRunning=!0}catch(e){Notification.error(TYPO3.lang["formEditor.error.headline"],TYPO3.lang["formEditor.error.message"]+"\r\n\r\n"+TYPO3.lang["formEditor.error.technicalReason"]+"\r\n"+e.message)}return this}saveApplicationState(){this.getApplicationStateStack().addAndReset({formDefinition:this.getApplicationStateStack().getCurrentState("formDefinition").clone(),currentlySelectedPageIndex:this.getApplicationStateStack().getCurrentState("currentlySelectedPageIndex"),currentlySelectedFormElementIdentifierPath:this.getApplicationStateStack().getCurrentState("currentlySelectedFormElementIdentifierPath")})}getDataBackend(){return Core.getDataBackend()}getFactory(){return Core.getFactory()}getRepository(){return Core.getRepository()}getPropertyValidationService(){return Core.getPropertyValidationService()}getApplicationStateStack(){return Core.getApplicationStateStack()}ajaxSetup(){$.ajaxSetup({beforeSend:()=>{this.getPublisherSubscriber().publish("ajax/beforeSend")},complete:()=>{this.getPublisherSubscriber().publish("ajax/complete")}})}dataBackendSetup(e,t,i){assert("object"===$.type(e),'Invalid parameter "endpoints"',1475379748),assert(this.getUtility().isNonEmptyString(t),'Invalid parameter "prototypeName"',1475927876),assert(this.getUtility().isNonEmptyString(i),'Invalid parameter "formPersistenceIdentifier"',1475379749),Core.getDataBackend().setEndpoints(e),Core.getDataBackend().setPrototypeName(t),Core.getDataBackend().setPersistenceIdentifier(i)}repositorySetup(e){assert("object"===$.type(e),'Invalid parameter "formEditorDefinitions"',1475379750),this.getRepository().setFormEditorDefinitions(e)}viewSetup(e){assert("function"===$.type(this.viewModel.bootstrap),'The view model does not implement the method "bootstrap"',1475492374),this.getUtility().isUndefinedOrNull(e)&&(e=[]),this.viewModel.bootstrap(formEditorInstance,e)}mediatorSetup(){assert("function"===$.type(this.mediator.bootstrap),'The mediator does not implement the method "bootstrap"',1475492032),this.mediator.bootstrap(formEditorInstance,this.viewModel)}applicationStateStackSetup(e,t){assert("object"===$.type(e),'Invalid parameter "rootFormElement"',1475379751),"number"!==$.type(t)&&(t=10),this.getApplicationStateStack().setMaximalStackSize(t),this.getApplicationStateStack().addAndReset({currentlySelectedPageIndex:0,currentlySelectedFormElementIdentifierPath:e.identifier},!0),this.getApplicationStateStack().setCurrentState("formDefinition",this.getFactory().createFormElement(e,void 0,void 0,!0))}bootstrap(){this.mediatorSetup(),this.ajaxSetup(),this.dataBackendSetup(this.configuration.endpoints,this.configuration.prototypeName,this.configuration.formPersistenceIdentifier),this.repositorySetup(this.configuration.formEditorDefinitions),this.applicationStateStackSetup(this.configuration.formDefinition,this.configuration.maximumUndoSteps),this.setCurrentlySelectedFormElement(this.getRepository().getRootFormElement()),this.viewSetup(this.configuration.additionalViewModelModules)}}let formEditorInstance=null;export function getInstance(e,t,i){return null===formEditorInstance&&(formEditorInstance=new FormEditor(e,t,i)),formEditorInstance}
\ No newline at end of file
-- 
GitLab