diff --git a/Build/Sources/TypeScript/form/backend/form-editor/tree-component.ts b/Build/Sources/TypeScript/form/backend/form-editor/tree-component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..93477fc42b4a0a64e9449e2030010e7975573525
--- /dev/null
+++ b/Build/Sources/TypeScript/form/backend/form-editor/tree-component.ts
@@ -0,0 +1,560 @@
+/*
+ * 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/tree-component
+ */
+import $ from 'jquery';
+import * as Helper from '@typo3/form/backend/form-editor/helper';
+import Icons from '@typo3/backend/icons';
+import Sortable from 'sortablejs';
+
+import type {
+  FormEditor,
+} from '@typo3/form/backend/form-editor';
+import type {
+  Utility,
+  FormElement,
+  FormElementDefinition,
+  PublisherSubscriber,
+} from '@typo3/form/backend/form-editor/core';
+import type {
+  Configuration as HelperConfiguration,
+} from '@typo3/form/backend/form-editor/helper';
+
+interface Configuration extends Partial<HelperConfiguration> {
+  isSortable: boolean,
+  svgLink: {
+    height: number,
+    width: number
+    paths: {
+      angle: string,
+      vertical: string,
+      hidden: string,
+    },
+  }
+}
+
+const defaultConfiguration: Configuration = {
+  domElementClassNames: {
+    collapsed: 'mjs-nestedSortable-collapsed',
+    expanded: 'mjs-nestedSortable-expanded',
+    hasChildren: 't3-form-element-has-children',
+    sortable: 'sortable',
+    svgLinkWrapper: 'svg-wrapper',
+    noNesting: 'mjs-nestedSortable-no-nesting'
+  },
+  domElementDataAttributeNames: {
+    abstractType: 'data-element-abstract-type'
+  },
+  domElementDataAttributeValues: {
+    collapse: 'actions-chevron-right',
+    expander: 'treeExpander',
+    title: 'treeTitle'
+  },
+  isSortable: true,
+  svgLink: {
+    height: 15,
+    paths: {
+      angle: 'M0 0 V20 H15',
+      vertical: 'M0 0 V20 H0',
+      hidden: 'M0 0 V0 H0'
+    },
+    width: 20
+  }
+};
+
+let configuration: Configuration = null;
+
+let formEditorApp: FormEditor = null;
+
+let treeDomElement: JQuery = null;
+
+const expanderStates: Record<string, boolean> = {};
+
+function getFormEditorApp(): FormEditor {
+  return formEditorApp;
+}
+
+function getHelper(_configuration?: HelperConfiguration): typeof Helper {
+  if (getUtility().isUndefinedOrNull(_configuration)) {
+    return Helper.setConfiguration(configuration);
+  }
+  return Helper.setConfiguration(_configuration);
+}
+
+function getUtility(): Utility {
+  return getFormEditorApp().getUtility();
+}
+
+function assert(test: boolean|(() => boolean), message: string, messageCode: number): void {
+  return getFormEditorApp().assert(test, message, messageCode);
+}
+
+function getRootFormElement(): FormElement {
+  return getFormEditorApp().getRootFormElement();
+}
+
+function getCurrentlySelectedFormElement(): FormElement {
+  return getFormEditorApp().getCurrentlySelectedFormElement();
+}
+
+function getPublisherSubscriber(): PublisherSubscriber {
+  return getFormEditorApp().getPublisherSubscriber();
+}
+
+function getFormElementDefinition<T extends keyof FormElementDefinition>(
+  formElement: FormElement,
+  formElementDefinitionKey?: T
+): T extends keyof FormElementDefinition ? FormElementDefinition[T] : FormElementDefinition {
+  return getFormEditorApp().getFormElementDefinition(formElement, formElementDefinitionKey);
+}
+
+function getLinkSvg(type: keyof Configuration['svgLink']['paths']): JQuery {
+  return $('<span class="' + getHelper().getDomElementClassName('svgLinkWrapper') + '">'
+    + '<svg version="1.1" width="' + configuration.svgLink.width + '" height="' + configuration.svgLink.height + '">'
+    + '<path class="link" d="' + configuration.svgLink.paths[type] + '">'
+    + '</svg>'
+    + '</span>');
+}
+
+/**
+ * @publish view/tree/render/listItemAdded
+ * @throws 1478715704
+ */
+function renderNestedSortableListItem(formElement: FormElement): JQuery {
+  assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1478715704);
+
+  const listItem = $('<li></li>');
+  if (!getFormElementDefinition(formElement, '_isCompositeFormElement')) {
+    listItem.addClass(getHelper().getDomElementClassName('noNesting'));
+  }
+
+  const listItemContent = $('<div></div>')
+    .attr(getHelper().getDomElementDataAttribute('elementIdentifier'), formElement.get('__identifierPath'))
+    .append(
+      $('<span></span>')
+        .attr(getHelper().getDomElementDataAttribute('identifier'), getHelper().getDomElementDataAttributeValue('title'))
+        .append(buildTitleByFormElement(formElement))
+    );
+
+  if (getFormElementDefinition(formElement, '_isCompositeFormElement')) {
+    listItemContent.attr(getHelper().getDomElementDataAttribute('abstractType'), 'isCompositeFormElement');
+  }
+  if (getFormElementDefinition(formElement, '_isTopLevelFormElement')) {
+    listItemContent.attr(getHelper().getDomElementDataAttribute('abstractType'), 'isTopLevelFormElement');
+  }
+
+  const expanderItem = $('<span></span>').attr('data-identifier', getHelper().getDomElementDataAttributeValue('expander'));
+  listItemContent.prepend(expanderItem);
+
+  Icons.getIcon(getFormElementDefinition(formElement, 'iconIdentifier'), Icons.sizes.small, null, Icons.states.default).then(function(icon) {
+    expanderItem.after(
+      $(icon).addClass(getHelper().getDomElementClassName('icon'))
+        .attr('title', 'id = ' + formElement.get('identifier'))
+    );
+
+    if (getFormElementDefinition(formElement, '_isCompositeFormElement')) {
+      if (formElement.get('renderables') && formElement.get('renderables').length > 0) {
+        Icons.getIcon(getHelper().getDomElementDataAttributeValue('collapse'), Icons.sizes.small).then(function(icon) {
+          expanderItem.before(getLinkSvg('angle')).html(icon);
+          listItem.addClass(getHelper().getDomElementClassName('hasChildren'));
+        });
+      } else {
+        expanderItem.before(getLinkSvg('angle')).remove();
+      }
+    } else {
+      listItemContent.prepend(getLinkSvg('angle'));
+      expanderItem.remove();
+    }
+
+    let searchElement = formElement.get('__parentRenderable');
+    while (searchElement) {
+      if (searchElement.get('__identifierPath') === getRootFormElement().get('__identifierPath')) {
+        break;
+      }
+
+      if (searchElement.get('__identifierPath') === getFormEditorApp().getLastFormElementWithinParentFormElement(searchElement).get('__identifierPath')) {
+        listItemContent.prepend(getLinkSvg('hidden'));
+      } else {
+        listItemContent.prepend(getLinkSvg('vertical'));
+      }
+      searchElement = searchElement.get('__parentRenderable');
+    }
+  });
+  listItem.append(listItemContent);
+
+  getPublisherSubscriber().publish('view/tree/render/listItemAdded', [listItem, formElement]);
+  const childFormElements = formElement.get('renderables');
+  let childList = null;
+  if ('array' === $.type(childFormElements)) {
+    childList = $('<ol></ol>');
+    for (let i = 0, len = childFormElements.length; i < len; ++i) {
+      childList.append(renderNestedSortableListItem(childFormElements[i]));
+    }
+  }
+
+  if (childList) {
+    listItem.append(childList);
+  }
+  return listItem;
+}
+
+/**
+ * @publish view/tree/dnd/stop
+ * @publish view/tree/dnd/change
+ * @publish view/tree/dnd/update
+ */
+function addSortableEvents(): void {
+  const defaultConfiguration: Sortable.Options = {
+    handle: 'div' + getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'),
+    draggable: 'li',
+    animation: 200,
+    fallbackTolerance: 200,
+    fallbackOnBody: true,
+    swapThreshold: 0.6,
+    dragClass: 'form-sortable-drag',
+    ghostClass: 'form-sortable-ghost',
+    onChange: function (e) {
+      let enclosingCompositeFormElement;
+      const parentFormElementIdentifierPath = getParentTreeNodeIdentifierPathWithinDomElement($(e.item));
+
+      if (parentFormElementIdentifierPath) {
+        enclosingCompositeFormElement = getFormEditorApp().findEnclosingCompositeFormElementWhichIsNotOnTopLevel(parentFormElementIdentifierPath);
+      }
+      getPublisherSubscriber().publish('view/tree/dnd/change', [$(e.item), parentFormElementIdentifierPath, enclosingCompositeFormElement]);
+    },
+    onEnd: function (e) {
+      const movedFormElementIdentifierPath = getTreeNodeIdentifierPathWithinDomElement($(e.item));
+      const previousFormElementIdentifierPath = getSiblingTreeNodeIdentifierPathWithinDomElement($(e.item), 'prev');
+      const nextFormElementIdentifierPath = getSiblingTreeNodeIdentifierPathWithinDomElement($(e.item), 'next');
+
+      getPublisherSubscriber().publish('view/tree/dnd/update', [$(e.item), movedFormElementIdentifierPath, previousFormElementIdentifierPath, nextFormElementIdentifierPath]);
+      getPublisherSubscriber().publish('view/tree/dnd/stop', [getTreeNodeIdentifierPathWithinDomElement($(e.item))]);
+    },
+  };
+
+  const sortableRoot: HTMLElement = treeDomElement.get(0).querySelector('ol.' + getHelper().getDomElementClassName('sortable'));
+  new Sortable(sortableRoot, {
+    ...defaultConfiguration,
+    ...{
+      group: 'tree-step-nodes',
+      put: ['tree-step-nodes'],
+    }
+  });
+
+  sortableRoot.querySelectorAll('ol').forEach(function (sortableList) {
+    new Sortable(sortableList, {
+      ...defaultConfiguration,
+      ...{
+        group: 'tree-leaves-nodes',
+        pull: ['tree-leaves-nodes'],
+      }
+    });
+  });
+}
+
+function saveExpanderStates(): void {
+  const addStates = function(formElement: FormElement) {
+    if (getFormElementDefinition(formElement, '_isCompositeFormElement')) {
+      const treeNode = getTreeNode(formElement);
+      if (treeNode.length) {
+        if (treeNode.closest('li').hasClass(getHelper().getDomElementClassName('expanded'))) {
+          expanderStates[formElement.get('__identifierPath')] = true;
+        } else {
+          expanderStates[formElement.get('__identifierPath')] = false;
+        }
+      }
+
+      if (getUtility().isUndefinedOrNull(expanderStates[formElement.get('__identifierPath')])) {
+        expanderStates[formElement.get('__identifierPath')] = true;
+      }
+    }
+
+    const childFormElements = formElement.get('renderables');
+    if ('array' === $.type(childFormElements)) {
+      for (let i = 0, len = childFormElements.length; i < len; ++i) {
+        addStates(childFormElements[i]);
+      }
+    }
+  };
+  addStates(getRootFormElement());
+
+  for (const identifierPath of Object.keys(expanderStates)) {
+    try {
+      getFormEditorApp().getFormElementByIdentifierPath(identifierPath);
+    } catch (error) {
+      delete expanderStates[identifierPath];
+    }
+  }
+}
+
+function loadExpanderStates(): void {
+  for (const identifierPath of Object.keys(expanderStates)) {
+    const treeNode = getTreeNode(identifierPath);
+    if (treeNode.length) {
+      if (expanderStates[identifierPath]) {
+        treeNode.closest('li')
+          .removeClass(getHelper().getDomElementClassName('collapsed'))
+          .addClass(getHelper().getDomElementClassName('expanded'));
+      } else {
+        treeNode.closest('li')
+          .addClass(getHelper().getDomElementClassName('collapsed'))
+          .removeClass(getHelper().getDomElementClassName('expanded'));
+      }
+    }
+  }
+}
+
+/**
+ * @throws 1478721208
+ */
+export function renderCompositeFormElementChildsAsSortableList(formElement: FormElement): JQuery {
+  assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1478721208);
+
+  const elementList = $('<ol></ol>').addClass(getHelper().getDomElementClassName('sortable'));
+  if ('array' === $.type(formElement.get('renderables'))) {
+    for (let i = 0, len = formElement.get('renderables').length; i < len; ++i) {
+      elementList.append(renderNestedSortableListItem(formElement.get('renderables')[i]));
+    }
+  }
+  return elementList;
+}
+
+/**
+ * @publish view/tree/node/clicked
+ */
+export function renew(formElement?: FormElement): void {
+  if (getFormEditorApp().getUtility().isUndefinedOrNull(formElement)) {
+    formElement = getRootFormElement();
+  }
+  saveExpanderStates();
+  treeDomElement.off().empty().append(renderCompositeFormElementChildsAsSortableList(formElement));
+
+  // We make use of the same strategy for db click detection as the current core pagetree implementation.
+  // @see https://github.com/typo3/typo3/blob/260226e93c651356545e91a7c55ee63e186766d5/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTree.js#L350
+  let clicks = 0;
+  treeDomElement.on('click', function(e) {
+    const formElementIdentifierPath = $(e.target)
+      .closest(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'))
+      .attr(getHelper().getDomElementDataAttribute('elementIdentifier'));
+    if (getUtility().isUndefinedOrNull(formElementIdentifierPath) || !getUtility().isNonEmptyString(formElementIdentifierPath)) {
+      return;
+    }
+
+    clicks++;
+
+    if (clicks === 1) {
+      setTimeout(function() {
+        if (clicks === 1) {
+          getPublisherSubscriber().publish('view/tree/node/clicked', [formElementIdentifierPath]);
+        } else {
+          editTreeNodeLabel(formElementIdentifierPath);
+        }
+        clicks = 0;
+      }, 300);
+    }
+  });
+
+  $(getHelper().getDomElementDataIdentifierSelector('expander'), treeDomElement).on('click', function(this: HTMLElement) {
+    $(this).closest('li').toggleClass(getHelper().getDomElementClassName('collapsed')).toggleClass(getHelper().getDomElementClassName('expanded'));
+  });
+
+  if (configuration.isSortable) {
+    addSortableEvents();
+  }
+  loadExpanderStates();
+}
+
+export function getAllTreeNodes(): JQuery {
+  return $(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'), treeDomElement);
+}
+
+export function getTreeNodeWithinDomElement(element: HTMLElement | JQuery): JQuery {
+  return $(element).find(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey')).first();
+}
+
+export function getTreeNodeIdentifierPathWithinDomElement(element: HTMLElement | JQuery): string {
+  return getTreeNodeWithinDomElement($(element)).attr(getHelper().getDomElementDataAttribute('elementIdentifier'));
+}
+
+export function getParentTreeNodeWithinDomElement(element: HTMLElement | JQuery): JQuery {
+  return $(element).parent().closest('li').find(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey')).first();
+}
+
+export function getParentTreeNodeIdentifierPathWithinDomElement(
+  element: HTMLElement | JQuery
+): string {
+  return getParentTreeNodeWithinDomElement(element).attr(getHelper().getDomElementDataAttribute('elementIdentifier'));
+}
+
+export function getSiblingTreeNodeIdentifierPathWithinDomElement(
+  element: HTMLElement | JQuery,
+  position: string
+): string {
+  if (getUtility().isUndefinedOrNull(position)) {
+    position = 'prev';
+  }
+  const formElementIdentifierPath = getTreeNodeIdentifierPathWithinDomElement(element);
+  element = (position === 'prev') ? $(element).prev('li') : $(element).next('li');
+  return element.find(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'))
+    .not(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKeyValue', [formElementIdentifierPath]))
+    .first()
+    .attr(getHelper().getDomElementDataAttribute('elementIdentifier'));
+}
+
+export function setTreeNodeTitle(title?: string, formElement?: FormElement): void {
+  let titleContent: HTMLElement;
+  if (getUtility().isUndefinedOrNull(title)) {
+    titleContent = buildTitleByFormElement(formElement);
+  } else {
+    titleContent = document.createElement('span');
+    titleContent.textContent = title;
+  }
+
+  $(getHelper().getDomElementDataIdentifierSelector('title'), getTreeNode(formElement)).get(0).replaceChildren(titleContent);
+}
+
+export function getTreeNode(formElement?: FormElement | string): JQuery {
+  let formElementIdentifierPath: string;
+
+  if (typeof formElement === 'string') {
+    formElementIdentifierPath = formElement;
+  } else {
+    if (getUtility().isUndefinedOrNull(formElement)) {
+      formElementIdentifierPath = getCurrentlySelectedFormElement().get('__identifierPath');
+    } else {
+      formElementIdentifierPath = formElement.get('__identifierPath');
+    }
+  }
+  return $(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKeyValue', [formElementIdentifierPath]), treeDomElement);
+}
+
+/**
+ * @throws 1478719287
+ */
+export function buildTitleByFormElement(formElement: FormElement): HTMLElement {
+  if (getUtility().isUndefinedOrNull(formElement)) {
+    formElement = getCurrentlySelectedFormElement();
+  }
+  assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1478719287);
+
+  const span = document.createElement('span');
+  span.textContent = formElement.get('label') ? formElement.get('label') : formElement.get('identifier');
+  const small = document.createElement('small');
+  small.textContent = '(' + getFormElementDefinition(formElement, 'label') + ')';
+  span.appendChild(small);
+  return span;
+}
+
+export function getTreeDomElement(): JQuery {
+  return treeDomElement;
+}
+
+function editTreeNodeLabel(formElementIdentifierPath: string): void {
+  const treeNode = getTreeNode(formElementIdentifierPath);
+  const titleNode = $(getHelper().getDomElementDataIdentifierSelector('title'), treeNode);
+  const currentTitle = titleNode.children()[0].childNodes[0].nodeValue.trim();
+  const treeRootWidth = getTreeDomElement().width();
+  let nodeIsEdit = true;
+
+  const input = $('<input>')
+    .attr('class', 'node-edit')
+    .css('top', function() {
+      const top = titleNode.position().top;
+      return top + 'px';
+    })
+    .css('left', titleNode.position().left + 'px')
+    .css('width', treeRootWidth - titleNode.position().left + 'px')
+    .attr('type', 'text')
+    .attr('value', currentTitle)
+    .on('click', (e: Event) => {
+      e.stopPropagation();
+    })
+    .on('keyup', function(this: HTMLInputElement, e) {
+      if (e.keyCode === 13 || e.keyCode === 9) { //enter || tab
+        const newTitle = this.value.trim();
+
+        if (getUtility().isNonEmptyString(newTitle) && (newTitle !== currentTitle)) {
+          nodeIsEdit = false;
+          input.remove();
+          getPublisherSubscriber().publish('view/tree/node/changed', [formElementIdentifierPath, newTitle]);
+        } else {
+          nodeIsEdit = false;
+          input.remove();
+        }
+      } else if (e.keyCode === 27) { //esc
+        nodeIsEdit = false;
+        input.remove();
+      }
+    })
+    .on('blur', function(this: HTMLInputElement) {
+      if(nodeIsEdit) {
+        const newTitle = this.value.trim();
+        input.remove();
+        if(getUtility().isNonEmptyString(newTitle) && newTitle !== currentTitle) {
+          getPublisherSubscriber().publish('view/tree/node/changed', [formElementIdentifierPath, newTitle]);
+        }
+      }
+    });
+
+  treeNode.append(input);
+  input.focus();
+}
+
+/**
+ * @throws 1478714814
+ */
+export function bootstrap(
+  this: typeof import('./tree-component'),
+  _formEditorApp: FormEditor,
+  appendToDomElement: JQuery,
+  customConfiguration?: typeof defaultConfiguration
+): typeof import('./tree-component') {
+  formEditorApp = _formEditorApp;
+  assert('object' === $.type(appendToDomElement), 'Invalid parameter "appendToDomElement"', 1478714814);
+  treeDomElement = $(appendToDomElement);
+  configuration = $.extend(true, defaultConfiguration, customConfiguration || {});
+  Helper.bootstrap(formEditorApp);
+  return this;
+}
+
+declare global {
+  interface PublisherSubscriberTopicArgumentsMap {
+    'view/tree/node/changed': readonly [
+      formElementIdentifierPath: string,
+      newLabel: string,
+    ];
+    'view/tree/node/clicked': readonly [
+      formElementIdentifierPath: string
+    ];
+    'view/tree/render/listItemAdded': readonly [
+      listItem: JQuery,
+      formElement: FormElement
+    ];
+    'view/tree/dnd/update': readonly [
+      dndItem: JQuery,
+      movedFormElementIdentifierPath: string,
+      previousFormElementIdentifierPath: string,
+      nextFormElementIdentifierPath: string,
+    ];
+    'view/tree/dnd/change': readonly [
+      dndItem: JQuery,
+      parentFormElementIdentifierPath: string,
+      enclosingCompositeFormElement: FormElement,
+    ];
+    'view/tree/dnd/stop': readonly [
+      treeNodeIdentifierPathWithinDomElement: string,
+    ];
+  }
+}
diff --git a/typo3/sysext/form/Resources/Public/JavaScript/backend/form-editor/tree-component.js b/typo3/sysext/form/Resources/Public/JavaScript/backend/form-editor/tree-component.js
index 5261c548c9a0be972259c3f632f991c4743ac60f..aad22b4d0fd895bf9e8b97a9b3b4eb93cff48934 100644
--- a/typo3/sysext/form/Resources/Public/JavaScript/backend/form-editor/tree-component.js
+++ b/typo3/sysext/form/Resources/Public/JavaScript/backend/form-editor/tree-component.js
@@ -10,751 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-
-/**
- * Module: @typo3/form/backend/form-editor/tree-component
- */
-import $ from 'jquery';
-import * as Helper from '@typo3/form/backend/form-editor/helper.js';
-import Icons from '@typo3/backend/icons.js';
-import Sortable from 'sortablejs';
-
-const {
-  bootstrap,
-  buildTitleByFormElement,
-  getAllTreeNodes,
-  getParentTreeNodeWithinDomElement,
-  getParentTreeNodeIdentifierPathWithinDomElement,
-  getSiblingTreeNodeIdentifierPathWithinDomElement,
-  getTreeDomElement,
-  getTreeNode,
-  getTreeNodeWithinDomElement,
-  getTreeNodeIdentifierPathWithinDomElement,
-  renderCompositeFormElementChildsAsSortableList,
-  renew,
-  setTreeNodeTitle
-} = factory($, Helper, Icons);
-
-export {
-  bootstrap,
-  buildTitleByFormElement,
-  getAllTreeNodes,
-  getParentTreeNodeWithinDomElement,
-  getParentTreeNodeIdentifierPathWithinDomElement,
-  getSiblingTreeNodeIdentifierPathWithinDomElement,
-  getTreeDomElement,
-  getTreeNode,
-  getTreeNodeWithinDomElement,
-  getTreeNodeIdentifierPathWithinDomElement,
-  renderCompositeFormElementChildsAsSortableList,
-  renew,
-  setTreeNodeTitle
-};
-
-function factory($, Helper, Icons) {
-
-  return (function($, Helper, Icons) {
-
-    /**
-     * @private
-     *
-     * @var object
-     */
-    var _configuration = null;
-
-    /**
-     * @private
-     *
-     * @var object
-     */
-    var _expanderStates = {};
-
-    /**
-     * @private
-     *
-     * @var object
-     */
-    var _defaultConfiguration = {
-      domElementClassNames: {
-        collapsed: 'mjs-nestedSortable-collapsed',
-        expanded: 'mjs-nestedSortable-expanded',
-        hasChildren: 't3-form-element-has-children',
-        sortable: 'sortable',
-        svgLinkWrapper: 'svg-wrapper',
-        noNesting: 'mjs-nestedSortable-no-nesting'
-      },
-      domElementDataAttributeNames: {
-        abstractType: 'data-element-abstract-type'
-      },
-      domElementDataAttributeValues: {
-        collapse: 'actions-chevron-right',
-        expander: 'treeExpander',
-        title: 'treeTitle'
-      },
-      isSortable: true,
-      svgLink: {
-        height: 15,
-        paths: {
-          angle: 'M0 0 V20 H15',
-          vertical: 'M0 0 V20 H0',
-          hidden: 'M0 0 V0 H0'
-        },
-        width: 20
-      }
-    };
-
-    /**
-     * @private
-     *
-     * @var object
-     */
-    var _formEditorApp = null;
-
-    /**
-     * @private
-     *
-     * @var object
-     */
-    var _treeDomElement = null;
-
-    /* *************************************************************
-     * Private Methods
-     * ************************************************************/
-
-    /**
-     * @private
-     *
-     * @return void
-     * @throws 1478268638
-     */
-    function _helperSetup() {
-      assert('function' === $.type(Helper.bootstrap),
-        'The view model helper does not implement the method "bootstrap"',
-        1478268638
-      );
-      Helper.bootstrap(getFormEditorApp());
-    };
-
-    /**
-     * @private
-     *
-     * @return object
-     */
-    function getFormEditorApp() {
-      return _formEditorApp;
-    };
-
-    /**
-     * @public
-     *
-     * @param object
-     * @return object
-     */
-    function getHelper(configuration) {
-      if (getUtility().isUndefinedOrNull(configuration)) {
-        return Helper.setConfiguration(_configuration);
-      }
-      return Helper.setConfiguration(configuration);
-    };
-
-    /**
-     * @private
-     *
-     * @return object
-     */
-    function getUtility() {
-      return getFormEditorApp().getUtility();
-    };
-
-    /**
-     * @private
-     *
-     * @param mixed test
-     * @param string message
-     * @param int messageCode
-     * @return void
-     */
-    function assert(test, message, messageCode) {
-      return getFormEditorApp().assert(test, message, messageCode);
-    };
-
-    /**
-     * @private
-     *
-     * @return object
-     */
-    function getRootFormElement() {
-      return getFormEditorApp().getRootFormElement();
-    };
-
-    /**
-     * @private
-     *
-     * @return object
-     */
-    function getCurrentlySelectedFormElement() {
-      return getFormEditorApp().getCurrentlySelectedFormElement();
-    };
-
-    /**
-     * @private
-     *
-     * @return object
-     */
-    function getPublisherSubscriber() {
-      return getFormEditorApp().getPublisherSubscriber();
-    };
-
-    /**
-     * @private
-     *
-     * @param object
-     * @param string
-     * @return mixed
-     */
-    function getFormElementDefinition(formElement, formElementDefinitionKey) {
-      return getFormEditorApp().getFormElementDefinition(formElement, formElementDefinitionKey);
-    };
-
-    /**
-     * @private
-     *
-     * @return object
-     */
-    function _getLinkSvg(type) {
-      return $('<span class="' + getHelper().getDomElementClassName('svgLinkWrapper') + '">'
-        + '<svg version="1.1" width="' + _configuration['svgLink']['width'] + '" height="' + _configuration['svgLink']['height'] + '">'
-        + '<path class="link" d="' + _configuration['svgLink']['paths'][type] + '">'
-        + '</svg>'
-        + '</span>');
-    };
-
-    /**
-     * @private
-     *
-     * @param object
-     * @return object
-     * @publish view/tree/render/listItemAdded
-     * @throws 1478715704
-     */
-    function _renderNestedSortableListItem(formElement) {
-      var childFormElements, childList, expanderItem, isLastFormElementWithinParentFormElement,
-        listItem, listItemContent, searchElement;
-      assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1478715704);
-
-      isLastFormElementWithinParentFormElement = false;
-      if (formElement.get('__identifierPath') === getFormEditorApp().getLastFormElementWithinParentFormElement(formElement).get('__identifierPath')) {
-        isLastFormElementWithinParentFormElement = true;
-      }
-
-      listItem = $('<li></li>');
-      if (!getFormElementDefinition(formElement, '_isCompositeFormElement')) {
-        listItem.addClass(getHelper().getDomElementClassName('noNesting'));
-      }
-
-      listItemContent = $('<div></div>')
-        .attr(getHelper().getDomElementDataAttribute('elementIdentifier'), formElement.get('__identifierPath'))
-        .append(
-          $('<span></span>')
-            .attr(getHelper().getDomElementDataAttribute('identifier'), getHelper().getDomElementDataAttributeValue('title'))
-            .html(buildTitleByFormElement(formElement))
-        );
-
-      if (getFormElementDefinition(formElement, '_isCompositeFormElement')) {
-        listItemContent.attr(getHelper().getDomElementDataAttribute('abstractType'), 'isCompositeFormElement');
-      }
-      if (getFormElementDefinition(formElement, '_isTopLevelFormElement')) {
-        listItemContent.attr(getHelper().getDomElementDataAttribute('abstractType'), 'isTopLevelFormElement');
-      }
-
-      expanderItem = $('<span></span>').attr('data-identifier', getHelper().getDomElementDataAttributeValue('expander'));
-      listItemContent.prepend(expanderItem);
-
-      Icons.getIcon(getFormElementDefinition(formElement, 'iconIdentifier'), Icons.sizes.small, null, Icons.states.default).then(function(icon) {
-        expanderItem.after(
-          $(icon).addClass(getHelper().getDomElementClassName('icon'))
-            .attr('title', 'id = ' + formElement.get('identifier'))
-        );
-
-        if (getFormElementDefinition(formElement, '_isCompositeFormElement')) {
-          if (formElement.get('renderables') && formElement.get('renderables').length > 0) {
-            Icons.getIcon(getHelper().getDomElementDataAttributeValue('collapse'), Icons.sizes.small).then(function(icon) {
-              expanderItem.before(_getLinkSvg('angle')).html($(icon));
-              listItem.addClass(getHelper().getDomElementClassName('hasChildren'));
-            });
-          } else {
-            expanderItem.before(_getLinkSvg('angle')).remove();
-          }
-        } else {
-          listItemContent.prepend(_getLinkSvg('angle'));
-          expanderItem.remove();
-        }
-
-        searchElement = formElement.get('__parentRenderable');
-        while (searchElement) {
-          if (searchElement.get('__identifierPath') === getRootFormElement().get('__identifierPath')) {
-            break;
-          }
-
-          if (searchElement.get('__identifierPath') === getFormEditorApp().getLastFormElementWithinParentFormElement(searchElement).get('__identifierPath')) {
-            listItemContent.prepend(_getLinkSvg('hidden'));
-          } else {
-            listItemContent.prepend(_getLinkSvg('vertical'));
-          }
-          searchElement = searchElement.get('__parentRenderable');
-        }
-      });
-      listItem.append(listItemContent);
-
-      getPublisherSubscriber().publish('view/tree/render/listItemAdded', [listItem, formElement]);
-      childFormElements = formElement.get('renderables');
-      childList = null;
-      if ('array' === $.type(childFormElements)) {
-        childList = $('<ol></ol>');
-        for (var i = 0, len = childFormElements.length; i < len; ++i) {
-          childList.append(_renderNestedSortableListItem(childFormElements[i]));
-        }
-      }
-
-      if (childList) {
-        listItem.append(childList);
-      }
-      return listItem;
-    };
-
-    /**
-     * @private
-     *
-     * @return void
-     * @publish view/tree/dnd/stop
-     * @publish view/tree/dnd/change
-     * @publish view/tree/dnd/update
-     */
-    function _addSortableEvents() {
-      const defaultConfiguration = {
-        handle: 'div' + getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'),
-        draggable: 'li',
-        animation: 200,
-        fallbackTolerance: 200,
-        fallbackOnBody: true,
-        swapThreshold: 0.6,
-        dragClass: 'form-sortable-drag',
-        ghostClass: 'form-sortable-ghost',
-        onChange: function (e) {
-          let enclosingCompositeFormElement;
-          const parentFormElementIdentifierPath = getParentTreeNodeIdentifierPathWithinDomElement($(e.item));
-
-          if (parentFormElementIdentifierPath) {
-            enclosingCompositeFormElement = getFormEditorApp().findEnclosingCompositeFormElementWhichIsNotOnTopLevel(parentFormElementIdentifierPath);
-          }
-          getPublisherSubscriber().publish('view/tree/dnd/change', [$(e.item), parentFormElementIdentifierPath, enclosingCompositeFormElement]);
-        },
-        onEnd: function (e) {
-          const movedFormElementIdentifierPath = getTreeNodeIdentifierPathWithinDomElement($(e.item));
-          const previousFormElementIdentifierPath = getSiblingTreeNodeIdentifierPathWithinDomElement($(e.item), 'prev');
-          const nextFormElementIdentifierPath = getSiblingTreeNodeIdentifierPathWithinDomElement($(e.item), 'next');
-
-          getPublisherSubscriber().publish('view/tree/dnd/update', [$(e.item), movedFormElementIdentifierPath, previousFormElementIdentifierPath, nextFormElementIdentifierPath]);
-          getPublisherSubscriber().publish('view/tree/dnd/stop', [getTreeNodeIdentifierPathWithinDomElement($(e.item))]);
-        },
-      };
-
-      const sortableRoot = _treeDomElement.get(0).querySelector('ol.' + getHelper().getDomElementClassName('sortable'));
-      new Sortable(sortableRoot, {
-        ...defaultConfiguration,
-        ...{
-          group: 'tree-step-nodes',
-          put: ['tree-step-nodes'],
-        }
-      });
-
-      sortableRoot.querySelectorAll('ol').forEach(function (sortableList) {
-        new Sortable(sortableList, {
-          ...defaultConfiguration,
-          ...{
-            group: 'tree-leaves-nodes',
-            pull: ['tree-leaves-nodes'],
-          }
-        });
-      });
-    }
-
-    /**
-     * @private
-     *
-     * @return void
-     */
-    function _saveExpanderStates() {
-      var addStates;
-
-      addStates = function(formElement) {
-        var childFormElements, treeNode;
-
-        if (getFormElementDefinition(formElement, '_isCompositeFormElement')) {
-          treeNode = getTreeNode(formElement);
-          if (treeNode.length) {
-            if (treeNode.closest('li').hasClass(getHelper().getDomElementClassName('expanded'))) {
-              _expanderStates[formElement.get('__identifierPath')] = true;
-            } else {
-              _expanderStates[formElement.get('__identifierPath')] = false;
-            }
-          }
-
-          if (getUtility().isUndefinedOrNull(_expanderStates[formElement.get('__identifierPath')])) {
-            _expanderStates[formElement.get('__identifierPath')] = true;
-          }
-        }
-
-        childFormElements = formElement.get('renderables');
-        if ('array' === $.type(childFormElements)) {
-          for (var i = 0, len = childFormElements.length; i < len; ++i) {
-            addStates(childFormElements[i]);
-          }
-        }
-      };
-      addStates(getRootFormElement());
-
-      for (var identifierPath in _expanderStates) {
-        if (!_expanderStates.hasOwnProperty(identifierPath)) {
-          continue;
-        }
-        try {
-          getFormEditorApp().getFormElementByIdentifierPath(identifierPath);
-        } catch (error) {
-          delete _expanderStates[identifierPath];
-        }
-      }
-    };
-
-    /**
-     * @private
-     *
-     * @return void
-     */
-    function _loadExpanderStates() {
-      for (var identifierPath in _expanderStates) {
-        var treeNode;
-
-        if (!_expanderStates.hasOwnProperty(identifierPath)) {
-          continue;
-        }
-        treeNode = getTreeNode(identifierPath);
-        if (treeNode.length) {
-          if (_expanderStates[identifierPath]) {
-            treeNode.closest('li')
-              .removeClass(getHelper().getDomElementClassName('collapsed'))
-              .addClass(getHelper().getDomElementClassName('expanded'));
-          } else {
-            treeNode.closest('li')
-              .addClass(getHelper().getDomElementClassName('collapsed'))
-              .removeClass(getHelper().getDomElementClassName('expanded'));
-          }
-        }
-      }
-    };
-
-    /* *************************************************************
-     * Public Methods
-     * ************************************************************/
-
-    /**
-     * @public
-     *
-     * @param object
-     * @return object
-     * @throws 1478721208
-     */
-    function renderCompositeFormElementChildsAsSortableList(formElement) {
-      var elementList;
-      assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1478721208);
-
-      elementList = $('<ol></ol>').addClass(getHelper().getDomElementClassName('sortable'));
-      if ('array' === $.type(formElement.get('renderables'))) {
-        for (var i = 0, len = formElement.get('renderables').length; i < len; ++i) {
-          elementList.append(_renderNestedSortableListItem(formElement.get('renderables')[i]));
-        }
-      }
-      return elementList;
-    };
-
-    /**
-     * @public
-     *
-     * @return void
-     * @param object
-     * @publish view/tree/node/clicked
-     */
-    function renew(formElement) {
-      if (getFormEditorApp().getUtility().isUndefinedOrNull(formElement)) {
-        formElement = getRootFormElement();
-      }
-      _saveExpanderStates();
-      _treeDomElement.off().empty().append(renderCompositeFormElementChildsAsSortableList(formElement));
-
-      // We make use of the same strategy for db click detection as the current core pagetree implementation.
-      // @see https://github.com/typo3/typo3/blob/260226e93c651356545e91a7c55ee63e186766d5/typo3/sysext/backend/Resources/Public/JavaScript/PageTree/PageTree.js#L350
-      var clicks = 0;
-      _treeDomElement.on("click", function(e) {
-        var formElementIdentifierPath;
-
-        formElementIdentifierPath = $(e.target)
-          .closest(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'))
-          .attr(getHelper().getDomElementDataAttribute('elementIdentifier'));
-        if (getUtility().isUndefinedOrNull(formElementIdentifierPath) || !getUtility().isNonEmptyString(formElementIdentifierPath)) {
-          return;
-        }
-
-        clicks++;
-
-        if (clicks === 1) {
-          setTimeout(function() {
-            if (clicks === 1) {
-              getPublisherSubscriber().publish('view/tree/node/clicked', [formElementIdentifierPath]);
-            } else {
-              _editTreeNodeLabel(formElementIdentifierPath);
-            }
-            clicks = 0;
-          }, 300);
-        }
-      });
-
-      $(getHelper().getDomElementDataIdentifierSelector('expander'), _treeDomElement).on('click', function() {
-        $(this).closest('li').toggleClass(getHelper().getDomElementClassName('collapsed')).toggleClass(getHelper().getDomElementClassName('expanded'));
-      });
-
-      if (_configuration['isSortable']) {
-        _addSortableEvents();
-      }
-      _loadExpanderStates();
-    };
-
-    /**
-     * @public
-     *
-     * @param object
-     * @return string
-     */
-    function getAllTreeNodes() {
-      return $(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'), _treeDomElement);
-    };
-
-    /**
-     * @public
-     *
-     * @param object
-     * @return string
-     */
-    function getTreeNodeWithinDomElement(element) {
-      return $(element).find(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey')).first();
-    };
-
-    /**
-     * @public
-     *
-     * @param object
-     * @return string
-     */
-    function getTreeNodeIdentifierPathWithinDomElement(element) {
-      return getTreeNodeWithinDomElement($(element)).attr(getHelper().getDomElementDataAttribute('elementIdentifier'));
-    };
-
-    /**
-     * @public
-     *
-     * @param object
-     * @return string
-     */
-    function getParentTreeNodeWithinDomElement(element) {
-      return $(element).parent().closest('li').find(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey')).first();
-    };
-
-    /**
-     * @public
-     *
-     * @param object
-     * @return string
-     */
-    function getParentTreeNodeIdentifierPathWithinDomElement(element) {
-      return getParentTreeNodeWithinDomElement(element).attr(getHelper().getDomElementDataAttribute('elementIdentifier'));
-    };
-
-    /**
-     * @private
-     *
-     * @param object
-     * @param string
-     * @return string
-     */
-    function getSiblingTreeNodeIdentifierPathWithinDomElement(element, position) {
-      var formElementIdentifierPath;
-
-      if (getUtility().isUndefinedOrNull(position)) {
-        position = 'prev';
-      }
-      formElementIdentifierPath = getTreeNodeIdentifierPathWithinDomElement(element);
-      element = (position === 'prev') ? $(element).prev('li') : $(element).next('li');
-      return element.find(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'))
-        .not(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKeyValue', [formElementIdentifierPath]))
-        .first()
-        .attr(getHelper().getDomElementDataAttribute('elementIdentifier'));
-    };
-
-    /**
-     * @public
-     *
-     * @param string
-     * @param object
-     * @return void
-     */
-    function setTreeNodeTitle(title, formElement) {
-      if (getUtility().isUndefinedOrNull(title)) {
-        title = buildTitleByFormElement(formElement);
-      }
-
-      $(getHelper().getDomElementDataIdentifierSelector('title'), getTreeNode(formElement)).html(title);
-    };
-
-    /**
-     * @public
-     *
-     * @param string|object
-     * @return object
-     */
-    function getTreeNode(formElement) {
-      var formElementIdentifierPath;
-
-      if ('string' === $.type(formElement)) {
-        formElementIdentifierPath = formElement;
-      } else {
-        if (getUtility().isUndefinedOrNull(formElement)) {
-          formElementIdentifierPath = getCurrentlySelectedFormElement().get('__identifierPath');
-        } else {
-          formElementIdentifierPath = formElement.get('__identifierPath');
-        }
-      }
-      return $(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKeyValue', [formElementIdentifierPath]), _treeDomElement);
-    };
-
-    /**
-     * @public
-     *
-     * @param object
-     * @return object
-     * @throws 1478719287
-     */
-    function buildTitleByFormElement(formElement) {
-      if (getUtility().isUndefinedOrNull(formElement)) {
-        formElement = getCurrentlySelectedFormElement();
-      }
-      assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1478719287);
-
-      return $('<span></span>')
-        .text((formElement.get('label') ? formElement.get('label') : formElement.get('identifier')))
-        .append($('<small></small>').text("(" + getFormElementDefinition(formElement, 'label') + ")"));
-    };
-
-    /**
-     * @public
-     *
-     * @return object
-     */
-    function getTreeDomElement() {
-      return _treeDomElement;
-    };
-
-    /**
-     * @private
-     *
-     * @param string
-     */
-    function _editTreeNodeLabel(formElementIdentifierPath) {
-      var treeNode = getTreeNode(formElementIdentifierPath);
-      var titleNode = $(getHelper().getDomElementDataIdentifierSelector('title'), treeNode);
-      var currentTitle = titleNode.children()[0].childNodes[0].nodeValue.trim();
-      var treeRootWidth = getTreeDomElement().width();
-      var nodeIsEdit = true;
-
-      var input = $('<input>')
-        .attr('class', 'node-edit')
-        .css('top', function() {
-          var top = titleNode.position().top;
-          return top + 'px';
-        })
-        .css('left', titleNode.position().left + 'px')
-        .css('width', treeRootWidth - titleNode.position().left + 'px')
-        .attr('type', 'text')
-        .attr('value', currentTitle)
-        .on('click', function(e) {
-          e.stopPropagation();
-        })
-        .on('keyup', function(e) {
-          if (e.keyCode === 13 || e.keyCode === 9) { //enter || tab
-            var newTitle = this.value.trim();
-
-            if (getUtility().isNonEmptyString(newTitle) && (newTitle !== currentTitle)) {
-              nodeIsEdit = false;
-              input.remove();
-              getPublisherSubscriber().publish('view/tree/node/changed', [formElementIdentifierPath, newTitle]);
-            } else {
-              nodeIsEdit = false;
-              input.remove();
-            }
-          } else if (e.keyCode === 27) { //esc
-            nodeIsEdit = false;
-            input.remove();
-          }
-        })
-        .on('blur', function() {
-          if(nodeIsEdit) {
-            var newTitle = this.value.trim();
-            input.remove();
-            if(getUtility().isNonEmptyString(newTitle) && newTitle !== currentTitle) {
-              getPublisherSubscriber().publish('view/tree/node/changed', [formElementIdentifierPath, newTitle]);
-            }
-          }
-        });
-
-      treeNode.append(input);
-      input.focus();
-    };
-
-    /**
-     * @public
-     *
-     * @param object
-     * @param object
-     * @param object
-     * @return this
-     * @throws 1478714814
-     */
-    function bootstrap(formEditorApp, appendToDomElement, configuration) {
-      _formEditorApp = formEditorApp;
-      assert('object' === $.type(appendToDomElement), 'Invalid parameter "appendToDomElement"', 1478714814);
-
-      _treeDomElement = $(appendToDomElement);
-      _configuration = $.extend(true, _defaultConfiguration, configuration || {});
-      _helperSetup();
-      return this;
-    };
-
-    /**
-     * Publish the public methods.
-     * Implements the "Revealing Module Pattern".
-     */
-    return {
-      bootstrap: bootstrap,
-      buildTitleByFormElement: buildTitleByFormElement,
-      getAllTreeNodes: getAllTreeNodes,
-      getParentTreeNodeWithinDomElement: getParentTreeNodeWithinDomElement,
-      getParentTreeNodeIdentifierPathWithinDomElement: getParentTreeNodeIdentifierPathWithinDomElement,
-      getSiblingTreeNodeIdentifierPathWithinDomElement: getSiblingTreeNodeIdentifierPathWithinDomElement,
-      getTreeDomElement: getTreeDomElement,
-      getTreeNode: getTreeNode,
-      getTreeNodeWithinDomElement: getTreeNodeWithinDomElement,
-      getTreeNodeIdentifierPathWithinDomElement: getTreeNodeIdentifierPathWithinDomElement,
-      renderCompositeFormElementChildsAsSortableList: renderCompositeFormElementChildsAsSortableList,
-      renew: renew,
-      setTreeNodeTitle: setTreeNodeTitle
-    };
-  })($, Helper, Icons);
-}
+import $ from"jquery";import*as Helper from"@typo3/form/backend/form-editor/helper.js";import Icons from"@typo3/backend/icons.js";import Sortable from"sortablejs";const defaultConfiguration={domElementClassNames:{collapsed:"mjs-nestedSortable-collapsed",expanded:"mjs-nestedSortable-expanded",hasChildren:"t3-form-element-has-children",sortable:"sortable",svgLinkWrapper:"svg-wrapper",noNesting:"mjs-nestedSortable-no-nesting"},domElementDataAttributeNames:{abstractType:"data-element-abstract-type"},domElementDataAttributeValues:{collapse:"actions-chevron-right",expander:"treeExpander",title:"treeTitle"},isSortable:!0,svgLink:{height:15,paths:{angle:"M0 0 V20 H15",vertical:"M0 0 V20 H0",hidden:"M0 0 V0 H0"},width:20}};let configuration=null,formEditorApp=null,treeDomElement=null;const expanderStates={};function getFormEditorApp(){return formEditorApp}function getHelper(e){return getUtility().isUndefinedOrNull(e)?Helper.setConfiguration(configuration):Helper.setConfiguration(e)}function getUtility(){return getFormEditorApp().getUtility()}function assert(e,t,n){return getFormEditorApp().assert(e,t,n)}function getRootFormElement(){return getFormEditorApp().getRootFormElement()}function getCurrentlySelectedFormElement(){return getFormEditorApp().getCurrentlySelectedFormElement()}function getPublisherSubscriber(){return getFormEditorApp().getPublisherSubscriber()}function getFormElementDefinition(e,t){return getFormEditorApp().getFormElementDefinition(e,t)}function getLinkSvg(e){return $('<span class="'+getHelper().getDomElementClassName("svgLinkWrapper")+'"><svg version="1.1" width="'+configuration.svgLink.width+'" height="'+configuration.svgLink.height+'"><path class="link" d="'+configuration.svgLink.paths[e]+'"></svg></span>')}function renderNestedSortableListItem(e){assert("object"===$.type(e),'Invalid parameter "formElement"',1478715704);const t=$("<li></li>");getFormElementDefinition(e,"_isCompositeFormElement")||t.addClass(getHelper().getDomElementClassName("noNesting"));const n=$("<div></div>").attr(getHelper().getDomElementDataAttribute("elementIdentifier"),e.get("__identifierPath")).append($("<span></span>").attr(getHelper().getDomElementDataAttribute("identifier"),getHelper().getDomElementDataAttributeValue("title")).append(buildTitleByFormElement(e)));getFormElementDefinition(e,"_isCompositeFormElement")&&n.attr(getHelper().getDomElementDataAttribute("abstractType"),"isCompositeFormElement"),getFormElementDefinition(e,"_isTopLevelFormElement")&&n.attr(getHelper().getDomElementDataAttribute("abstractType"),"isTopLevelFormElement");const r=$("<span></span>").attr("data-identifier",getHelper().getDomElementDataAttributeValue("expander"));n.prepend(r),Icons.getIcon(getFormElementDefinition(e,"iconIdentifier"),Icons.sizes.small,null,Icons.states.default).then((function(i){r.after($(i).addClass(getHelper().getDomElementClassName("icon")).attr("title","id = "+e.get("identifier"))),getFormElementDefinition(e,"_isCompositeFormElement")?e.get("renderables")&&e.get("renderables").length>0?Icons.getIcon(getHelper().getDomElementDataAttributeValue("collapse"),Icons.sizes.small).then((function(e){r.before(getLinkSvg("angle")).html(e),t.addClass(getHelper().getDomElementClassName("hasChildren"))})):r.before(getLinkSvg("angle")).remove():(n.prepend(getLinkSvg("angle")),r.remove());let o=e.get("__parentRenderable");for(;o&&o.get("__identifierPath")!==getRootFormElement().get("__identifierPath");)o.get("__identifierPath")===getFormEditorApp().getLastFormElementWithinParentFormElement(o).get("__identifierPath")?n.prepend(getLinkSvg("hidden")):n.prepend(getLinkSvg("vertical")),o=o.get("__parentRenderable")})),t.append(n),getPublisherSubscriber().publish("view/tree/render/listItemAdded",[t,e]);const i=e.get("renderables");let o=null;if("array"===$.type(i)){o=$("<ol></ol>");for(let e=0,t=i.length;e<t;++e)o.append(renderNestedSortableListItem(i[e]))}return o&&t.append(o),t}function addSortableEvents(){const e={handle:"div"+getHelper().getDomElementDataAttribute("elementIdentifier","bracesWithKey"),draggable:"li",animation:200,fallbackTolerance:200,fallbackOnBody:!0,swapThreshold:.6,dragClass:"form-sortable-drag",ghostClass:"form-sortable-ghost",onChange:function(e){let t;const n=getParentTreeNodeIdentifierPathWithinDomElement($(e.item));n&&(t=getFormEditorApp().findEnclosingCompositeFormElementWhichIsNotOnTopLevel(n)),getPublisherSubscriber().publish("view/tree/dnd/change",[$(e.item),n,t])},onEnd:function(e){const t=getTreeNodeIdentifierPathWithinDomElement($(e.item)),n=getSiblingTreeNodeIdentifierPathWithinDomElement($(e.item),"prev"),r=getSiblingTreeNodeIdentifierPathWithinDomElement($(e.item),"next");getPublisherSubscriber().publish("view/tree/dnd/update",[$(e.item),t,n,r]),getPublisherSubscriber().publish("view/tree/dnd/stop",[getTreeNodeIdentifierPathWithinDomElement($(e.item))])}},t=treeDomElement.get(0).querySelector("ol."+getHelper().getDomElementClassName("sortable"));new Sortable(t,{...e,group:"tree-step-nodes",put:["tree-step-nodes"]}),t.querySelectorAll("ol").forEach((function(t){new Sortable(t,{...e,group:"tree-leaves-nodes",pull:["tree-leaves-nodes"]})}))}function saveExpanderStates(){const e=function(t){if(getFormElementDefinition(t,"_isCompositeFormElement")){const e=getTreeNode(t);e.length&&(e.closest("li").hasClass(getHelper().getDomElementClassName("expanded"))?expanderStates[t.get("__identifierPath")]=!0:expanderStates[t.get("__identifierPath")]=!1),getUtility().isUndefinedOrNull(expanderStates[t.get("__identifierPath")])&&(expanderStates[t.get("__identifierPath")]=!0)}const n=t.get("renderables");if("array"===$.type(n))for(let t=0,r=n.length;t<r;++t)e(n[t])};e(getRootFormElement());for(const e of Object.keys(expanderStates))try{getFormEditorApp().getFormElementByIdentifierPath(e)}catch(t){delete expanderStates[e]}}function loadExpanderStates(){for(const e of Object.keys(expanderStates)){const t=getTreeNode(e);t.length&&(expanderStates[e]?t.closest("li").removeClass(getHelper().getDomElementClassName("collapsed")).addClass(getHelper().getDomElementClassName("expanded")):t.closest("li").addClass(getHelper().getDomElementClassName("collapsed")).removeClass(getHelper().getDomElementClassName("expanded")))}}export function renderCompositeFormElementChildsAsSortableList(e){assert("object"===$.type(e),'Invalid parameter "formElement"',1478721208);const t=$("<ol></ol>").addClass(getHelper().getDomElementClassName("sortable"));if("array"===$.type(e.get("renderables")))for(let n=0,r=e.get("renderables").length;n<r;++n)t.append(renderNestedSortableListItem(e.get("renderables")[n]));return t}export function renew(e){getFormEditorApp().getUtility().isUndefinedOrNull(e)&&(e=getRootFormElement()),saveExpanderStates(),treeDomElement.off().empty().append(renderCompositeFormElementChildsAsSortableList(e));let t=0;treeDomElement.on("click",(function(e){const n=$(e.target).closest(getHelper().getDomElementDataAttribute("elementIdentifier","bracesWithKey")).attr(getHelper().getDomElementDataAttribute("elementIdentifier"));!getUtility().isUndefinedOrNull(n)&&getUtility().isNonEmptyString(n)&&(t++,1===t&&setTimeout((function(){1===t?getPublisherSubscriber().publish("view/tree/node/clicked",[n]):editTreeNodeLabel(n),t=0}),300))})),$(getHelper().getDomElementDataIdentifierSelector("expander"),treeDomElement).on("click",(function(){$(this).closest("li").toggleClass(getHelper().getDomElementClassName("collapsed")).toggleClass(getHelper().getDomElementClassName("expanded"))})),configuration.isSortable&&addSortableEvents(),loadExpanderStates()}export function getAllTreeNodes(){return $(getHelper().getDomElementDataAttribute("elementIdentifier","bracesWithKey"),treeDomElement)}export function getTreeNodeWithinDomElement(e){return $(e).find(getHelper().getDomElementDataAttribute("elementIdentifier","bracesWithKey")).first()}export function getTreeNodeIdentifierPathWithinDomElement(e){return getTreeNodeWithinDomElement($(e)).attr(getHelper().getDomElementDataAttribute("elementIdentifier"))}export function getParentTreeNodeWithinDomElement(e){return $(e).parent().closest("li").find(getHelper().getDomElementDataAttribute("elementIdentifier","bracesWithKey")).first()}export function getParentTreeNodeIdentifierPathWithinDomElement(e){return getParentTreeNodeWithinDomElement(e).attr(getHelper().getDomElementDataAttribute("elementIdentifier"))}export function getSiblingTreeNodeIdentifierPathWithinDomElement(e,t){getUtility().isUndefinedOrNull(t)&&(t="prev");const n=getTreeNodeIdentifierPathWithinDomElement(e);return(e="prev"===t?$(e).prev("li"):$(e).next("li")).find(getHelper().getDomElementDataAttribute("elementIdentifier","bracesWithKey")).not(getHelper().getDomElementDataAttribute("elementIdentifier","bracesWithKeyValue",[n])).first().attr(getHelper().getDomElementDataAttribute("elementIdentifier"))}export function setTreeNodeTitle(e,t){let n;getUtility().isUndefinedOrNull(e)?n=buildTitleByFormElement(t):(n=document.createElement("span"),n.textContent=e),$(getHelper().getDomElementDataIdentifierSelector("title"),getTreeNode(t)).get(0).replaceChildren(n)}export function getTreeNode(e){let t;return t="string"==typeof e?e:getUtility().isUndefinedOrNull(e)?getCurrentlySelectedFormElement().get("__identifierPath"):e.get("__identifierPath"),$(getHelper().getDomElementDataAttribute("elementIdentifier","bracesWithKeyValue",[t]),treeDomElement)}export function buildTitleByFormElement(e){getUtility().isUndefinedOrNull(e)&&(e=getCurrentlySelectedFormElement()),assert("object"===$.type(e),'Invalid parameter "formElement"',1478719287);const t=document.createElement("span");t.textContent=e.get("label")?e.get("label"):e.get("identifier");const n=document.createElement("small");return n.textContent="("+getFormElementDefinition(e,"label")+")",t.appendChild(n),t}export function getTreeDomElement(){return treeDomElement}function editTreeNodeLabel(e){const t=getTreeNode(e),n=$(getHelper().getDomElementDataIdentifierSelector("title"),t),r=n.children()[0].childNodes[0].nodeValue.trim(),i=getTreeDomElement().width();let o=!0;const l=$("<input>").attr("class","node-edit").css("top",(function(){return n.position().top+"px"})).css("left",n.position().left+"px").css("width",i-n.position().left+"px").attr("type","text").attr("value",r).on("click",(e=>{e.stopPropagation()})).on("keyup",(function(t){if(13===t.keyCode||9===t.keyCode){const t=this.value.trim();getUtility().isNonEmptyString(t)&&t!==r?(o=!1,l.remove(),getPublisherSubscriber().publish("view/tree/node/changed",[e,t])):(o=!1,l.remove())}else 27===t.keyCode&&(o=!1,l.remove())})).on("blur",(function(){if(o){const t=this.value.trim();l.remove(),getUtility().isNonEmptyString(t)&&t!==r&&getPublisherSubscriber().publish("view/tree/node/changed",[e,t])}}));t.append(l),l.focus()}export function bootstrap(e,t,n){return formEditorApp=e,assert("object"===$.type(t),'Invalid parameter "appendToDomElement"',1478714814),treeDomElement=$(t),configuration=$.extend(!0,defaultConfiguration,n||{}),Helper.bootstrap(formEditorApp),this}
\ No newline at end of file