Commit 2f99b84b authored by Andreas Fernandez's avatar Andreas Fernandez Committed by Frank Nägler
Browse files

[TASK] Split FormEngine.js

This patch splits the FormEngine.js into smaller, logically separated
parts. This increases readability and maintainability and has a positive
effect on the client's performance as only the required code is loaded.

Not every aspect of the FormEngine JavaScript is handled here yet, since
other areas, e.g. IRRE, need refactoring as well.

Resolves: #87324
Releases: master
Change-Id: I6704445254a524f8ed3152ab6b0b7105fb97d65a
Reviewed-on: https://review.typo3.org/c/58608

Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: André Buchmann's avatarAndré Schließer <andy.schliesser@gmail.com>
Tested-by: Susanne Moog's avatarSusanne Moog <susanne.moog@typo3.org>
Tested-by: Frank Nägler's avatarFrank Naegler <frank.naegler@typo3.org>
Reviewed-by: André Buchmann's avatarAndré Schließer <andy.schliesser@gmail.com>
Reviewed-by: Susanne Moog's avatarSusanne Moog <susanne.moog@typo3.org>
Reviewed-by: Frank Nägler's avatarFrank Naegler <frank.naegler@typo3.org>
parent bc798359
......@@ -36,6 +36,9 @@ declare namespace TYPO3 {
export class FormEngine {
public readonly Validation: FormEngineValidation;
public legacyFieldChangedCb(): void;
public getFieldElement(fieldName: string, appendix?: string, noFallback?: boolean): JQuery;
public updateHiddenFieldValueFromSelect(selectFieldEl: HTMLElement, originalFieldEl: HTMLElement): void;
public preventFollowLinkIfNotSaved(href: string): boolean;
public setSelectOptionFromExternalSource(
fieldName: string,
......
......@@ -255,10 +255,13 @@ class GroupElement extends AbstractFormElement
$deleteControlOnClick = 'inline.revertUnique(' . GeneralUtility::quoteJSvalue($objectPrefix) . ',null,' . GeneralUtility::quoteJSvalue($row['uid']) . ');';
}
$fieldId = StringUtility::getUniqueId('tceforms-multiselect-');
$selectorAttributes = [
'id' => StringUtility::getUniqueId('tceforms-multiselect-'),
'id' => $fieldId,
'data-formengine-input-name' => htmlspecialchars($elementName),
'data-formengine-validation-rules' => $this->getValidationDataAsJsonString($config),
'data-maxitems' => $maxItems,
'size' => $size,
];
$selectorClasses = [
......@@ -321,7 +324,7 @@ class GroupElement extends AbstractFormElement
$html[] = '<div class="btn-group-vertical">';
if ($maxItems > 1 && $size >=5 && $showMoveIcons) {
$html[] = '<a href="#"';
$html[] = ' class="btn btn-default t3js-btn-moveoption-top"';
$html[] = ' class="btn btn-default t3js-btn-option t3js-btn-moveoption-top"';
$html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
$html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move_to_top')) . '"';
$html[] = '>';
......@@ -330,14 +333,14 @@ class GroupElement extends AbstractFormElement
}
if ($maxItems > 1 && $size > 1 && $showMoveIcons) {
$html[] = '<a href="#"';
$html[] = ' class="btn btn-default t3js-btn-moveoption-up"';
$html[] = ' class="btn btn-default t3js-btn-option t3js-btn-moveoption-up"';
$html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
$html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move_up')) . '"';
$html[] = '>';
$html[] = $this->iconFactory->getIcon('actions-move-up', Icon::SIZE_SMALL)->render();
$html[] = '</a>';
$html[] = '<a href="#"';
$html[] = ' class="btn btn-default t3js-btn-moveoption-down"';
$html[] = ' class="btn btn-default t3js-btn-option t3js-btn-moveoption-down"';
$html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
$html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move_down')) . '"';
$html[] = '>';
......@@ -346,7 +349,7 @@ class GroupElement extends AbstractFormElement
}
if ($maxItems > 1 && $size >= 5 && $showMoveIcons) {
$html[] = '<a href="#"';
$html[] = ' class="btn btn-default t3js-btn-moveoption-bottom"';
$html[] = ' class="btn btn-default t3js-btn-option t3js-btn-moveoption-bottom"';
$html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
$html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move_to_bottom')) . '"';
$html[] = '>';
......@@ -355,7 +358,7 @@ class GroupElement extends AbstractFormElement
}
if ($showDeleteControl) {
$html[] = '<a href="#"';
$html[] = ' class="btn btn-default t3js-btn-removeoption"';
$html[] = ' class="btn btn-default t3js-btn-option t3js-btn-removeoption"';
$html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
$html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.remove_selected')) . '"';
$html[] = ' onClick="' . $deleteControlOnClick . '"';
......@@ -379,6 +382,12 @@ class GroupElement extends AbstractFormElement
$html[] = '<input type="hidden" name="' . htmlspecialchars($elementName) . '" value="' . htmlspecialchars(implode(',', $listOfSelectedValues)) . '" />';
$html[] = '</div>';
$resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/Element/GroupElement' => '
function(GroupElement) {
new GroupElement(' . GeneralUtility::quoteJSvalue($fieldId) . ');
}'
];
$resultArray['html'] = implode(LF, $html);
return $resultArray;
}
......
......@@ -270,6 +270,7 @@ class InputDateTimeElement extends AbstractFormElement
$fullElement = implode(LF, $fullElement);
}
$resultArray['requireJsModules'][] = 'TYPO3/CMS/Backend/FormEngine/Element/InputDateTimeElement';
$resultArray['html'] = '<div class="formengine-field-item t3js-formengine-field-item">' . $fieldInformationHtml . $fullElement . '</div>';
return $resultArray;
}
......
......@@ -147,9 +147,11 @@ class InputLinkElement extends AbstractFormElement
}
}
$fieldId = StringUtility::getUniqueId('formengine-input-');
$attributes = [
'value' => '',
'id' => StringUtility::getUniqueId('formengine-input-'),
'id' => $fieldId,
'class' => implode(' ', [
'form-control',
't3js-clearable',
......@@ -302,6 +304,11 @@ class InputLinkElement extends AbstractFormElement
$fullElement = implode(LF, $fullElement);
}
$resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/Element/InputLinkElement' => '
function(InputLinkElement) {
new InputLinkElement(' . GeneralUtility::quoteJSvalue($fieldId) . ');
}'
];
$resultArray['html'] = '<div class="formengine-field-item t3js-formengine-field-item">' . $fieldInformationHtml . $fullElement . '</div>';
return $resultArray;
}
......
......@@ -237,6 +237,12 @@ class SelectCheckBoxElement extends AbstractFormElement
if (is_array($group['header'])) {
$html[] = '</div>';
}
$resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/Element/SelectCheckBoxElement' => '
function(SelectCheckBoxElement) {
new SelectCheckBoxElement(' . GeneralUtility::quoteJSvalue($checkboxId) . ');
}'
];
}
$html[] = '</div>';
}
......
......@@ -217,6 +217,9 @@ class SelectMultipleSideBySideElement extends AbstractFormElement
$fieldWizardHtml = $fieldWizardResult['html'];
$resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
$selectedOptionsFieldId = StringUtility::getUniqueId('tceforms-multiselect-');
$availableOptionsFieldId = StringUtility::getUniqueId('tceforms-multiselect-');
$html = [];
$html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
$html[] = $fieldInformationHtml;
......@@ -231,7 +234,7 @@ class SelectMultipleSideBySideElement extends AbstractFormElement
$html[] = '<div class="form-wizards-wrap form-wizards-aside">';
$html[] = '<div class="form-wizards-element">';
$html[] = '<select';
$html[] = ' id="' . StringUtility::getUniqueId('tceforms-multiselect-') . '"';
$html[] = ' id="' . $selectedOptionsFieldId . '"';
$html[] = ' size="' . $size . '"';
$html[] = ' class="' . implode(' ', $classes) . '"';
$html[] = $multipleAttribute;
......@@ -244,7 +247,7 @@ class SelectMultipleSideBySideElement extends AbstractFormElement
$html[] = '<div class="btn-group-vertical">';
if ($maxItems > 1 && $size >= 5) {
$html[] = '<a href="#"';
$html[] = ' class="btn btn-default t3js-btn-moveoption-top"';
$html[] = ' class="btn btn-default t3js-btn-option t3js-btn-moveoption-top"';
$html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
$html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move_to_top')) . '"';
$html[] = '>';
......@@ -253,14 +256,14 @@ class SelectMultipleSideBySideElement extends AbstractFormElement
}
if ($maxItems > 1) {
$html[] = '<a href="#"';
$html[] = ' class="btn btn-default t3js-btn-moveoption-up"';
$html[] = ' class="btn btn-default t3js-btn-option t3js-btn-moveoption-up"';
$html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
$html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move_up')) . '"';
$html[] = '>';
$html[] = $this->iconFactory->getIcon('actions-move-up', Icon::SIZE_SMALL)->render();
$html[] = '</a>';
$html[] = '<a href="#"';
$html[] = ' class="btn btn-default t3js-btn-moveoption-down"';
$html[] = ' class="btn btn-default t3js-btn-option t3js-btn-moveoption-down"';
$html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
$html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move_down')) . '"';
$html[] = '>';
......@@ -269,7 +272,7 @@ class SelectMultipleSideBySideElement extends AbstractFormElement
}
if ($maxItems > 1 && $size >= 5) {
$html[] = '<a href="#"';
$html[] = ' class="btn btn-default t3js-btn-moveoption-bottom"';
$html[] = ' class="btn btn-default t3js-btn-option t3js-btn-moveoption-bottom"';
$html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
$html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.move_to_bottom')) . '"';
$html[] = '>';
......@@ -277,7 +280,7 @@ class SelectMultipleSideBySideElement extends AbstractFormElement
$html[] = '</a>';
}
$html[] = '<a href="#"';
$html[] = ' class="btn btn-default t3js-btn-removeoption"';
$html[] = ' class="btn btn-default t3js-btn-option t3js-btn-removeoption"';
$html[] = ' data-fieldname="' . htmlspecialchars($elementName) . '"';
$html[] = ' title="' . htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.remove_selected')) . '"';
$html[] = '>';
......@@ -297,7 +300,7 @@ class SelectMultipleSideBySideElement extends AbstractFormElement
$html[] = '<select';
$html[] = ' data-relatedfieldname="' . htmlspecialchars($elementName) . '"';
$html[] = ' data-exclusivevalues="' . htmlspecialchars($config['exclusiveKeys']) . '"';
$html[] = ' id="' . StringUtility::getUniqueId('tceforms-multiselect-') . '"';
$html[] = ' id="' . $availableOptionsFieldId . '"';
$html[] = ' data-formengine-input-name="' . htmlspecialchars($elementName) . '"';
$html[] = ' class="form-control t3js-formengine-select-itemstoselect"';
$html[] = ' size="' . $size . '"';
......@@ -327,6 +330,12 @@ class SelectMultipleSideBySideElement extends AbstractFormElement
$html[] = '</div>';
$html[] = '</div>';
$resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/Element/SelectMultipleSideBySideElement' => '
function(SelectMultipleSideBySideElement) {
new SelectMultipleSideBySideElement(' . GeneralUtility::quoteJSvalue($selectedOptionsFieldId) . ', ' . GeneralUtility::quoteJSvalue($availableOptionsFieldId) . ');
}'
];
$resultArray['html'] = implode(LF, $html);
return $resultArray;
}
......
......@@ -146,8 +146,10 @@ class TextElement extends AbstractFormElement
}
}
$fieldId = StringUtility::getUniqueId('formengine-textarea-');
$attributes = [
'id' => StringUtility::getUniqueId('formengine-textarea-'),
'id' => $fieldId,
'name' => htmlspecialchars($parameterArray['itemFormElName']),
'data-formengine-validation-rules' => $this->getValidationDataAsJsonString($config),
'data-formengine-input-name' => htmlspecialchars($parameterArray['itemFormElName']),
......@@ -301,6 +303,11 @@ class TextElement extends AbstractFormElement
$fullElement = implode(LF, $fullElement);
}
$resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/Element/TextElement' => '
function(TextElement) {
new TextElement(' . GeneralUtility::quoteJSvalue($fieldId) . ');
}'
];
$resultArray['html'] = '<div class="formengine-field-item t3js-formengine-field-item">' . $fieldInformationHtml . $fullElement . '</div>';
return $resultArray;
}
......
......@@ -151,8 +151,10 @@ class TextTableElement extends AbstractFormElement
}
}
$fieldId = StringUtility::getUniqueId('formengine-textarea-');
$attributes = [
'id' => StringUtility::getUniqueId('formengine-textarea-'),
'id' => $fieldId,
'name' => htmlspecialchars($parameterArray['itemFormElName']),
'data-formengine-validation-rules' => $this->getValidationDataAsJsonString($config),
'data-formengine-input-name' => htmlspecialchars($parameterArray['itemFormElName']),
......@@ -216,6 +218,11 @@ class TextTableElement extends AbstractFormElement
$html[] = '</div>';
$html[] = '</div>';
$resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/Element/TextTableElement' => '
function(TextTableElement) {
new TextTableElement(' . GeneralUtility::quoteJSvalue($fieldId) . ');
}'
];
$resultArray['html'] = implode(LF, $html);
return $resultArray;
}
......
......@@ -18,6 +18,7 @@ namespace TYPO3\CMS\Backend\Form\FieldWizard;
use TYPO3\CMS\Backend\Form\AbstractNode;
use TYPO3\CMS\Core\DataHandling\Localization\State;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Allows to define the localization state per field.
......@@ -127,6 +128,11 @@ class LocalizationStateSelector extends AbstractNode
}
$html[] = '</div>';
$result['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/FieldWizard/LocalizationStateSelector' => '
function(LocalizationStateSelector) {
new LocalizationStateSelector(' . GeneralUtility::quoteJSvalue($fieldElementName) . ');
}'
];
$result['html'] = implode(LF, $html);
return $result;
}
......
/*
* 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!
*/
import * as $ from 'jquery';
import FormEngine = require('TYPO3/CMS/Backend/FormEngine');
import FormEngineValidation = require('TYPO3/CMS/Backend/FormEngineValidation');
export abstract class AbstractSortableSelectItems {
/**
* Moves currently selected options from a select field to the very top,
* can be multiple entries as well
*
* @param {HTMLSelectElement} fieldElement
*/
private static moveOptionToTop(fieldElement: HTMLSelectElement): void {
Array.from(fieldElement.querySelectorAll(':checked')).reverse().forEach((optionEl: HTMLOptionElement): void => {
fieldElement.insertBefore(optionEl, fieldElement.firstElementChild);
});
}
/**
* Moves currently selected options from a select field as the very last entries
*
* @param {HTMLSelectElement} fieldElement
*/
private static moveOptionToBottom(fieldElement: HTMLSelectElement): void {
Array.from(fieldElement.querySelectorAll(':checked')).forEach((optionEl: HTMLOptionElement): void => {
fieldElement.insertBefore(optionEl, null);
});
}
/**
* Moves currently selected options from a select field up by one position,
* can be multiple entries as well
*
* @param {HTMLSelectElement} fieldElement
*/
private static moveOptionUp(fieldElement: HTMLSelectElement): void {
const allChildren = Array.from(fieldElement.children);
const selectedOptions = Array.from(fieldElement.querySelectorAll(':checked'));
for (let optionEl of selectedOptions) {
if (allChildren.indexOf(optionEl) === 0 && optionEl.previousElementSibling === null) {
break;
}
fieldElement.insertBefore(optionEl, optionEl.previousElementSibling);
}
}
/**
* Moves currently selected options from a select field up by one position,
* can be multiple entries as well
*
* @param {HTMLSelectElement} fieldElement
*/
private static moveOptionDown(fieldElement: HTMLSelectElement): void {
const allChildren = Array.from(fieldElement.children).reverse();
const selectedOptions = Array.from(fieldElement.querySelectorAll(':checked')).reverse();
for (let optionEl of selectedOptions) {
if (allChildren.indexOf(optionEl) === 0 && optionEl.nextElementSibling === null) {
break;
}
fieldElement.insertBefore(optionEl, optionEl.nextElementSibling.nextElementSibling);
}
}
/**
* Removes currently selected options from a select field
*
* @param {HTMLSelectElement} fieldElement
* @param {HTMLSelectElement} availableFieldElement
*/
private static removeOption(fieldElement: HTMLSelectElement, availableFieldElement: HTMLSelectElement): void {
Array.from(fieldElement.querySelectorAll(':checked')).forEach((option: HTMLOptionElement): void => {
const originalOption = <HTMLOptionElement>availableFieldElement.querySelector('option[value="' + option.value + '"]');
originalOption.classList.remove('hidden');
originalOption.disabled = false;
fieldElement.removeChild(option);
});
}
/**
* @param {HTMLSelectElement} fieldElement
*/
protected registerSortableEventHandler = (fieldElement: HTMLSelectElement): void => {
const aside = fieldElement.closest('.form-wizards-wrap').querySelector('.form-wizards-items-aside');
if (aside === null) {
return;
}
aside.addEventListener('click', (e): void => {
let target: HTMLAnchorElement;
if ((target = <HTMLAnchorElement>(<Element>e.target).closest('.t3js-btn-option')) === null) {
if ((<Element>e.target).matches('.t3js-btn-option')) {
target = <HTMLAnchorElement>e.target;
}
return;
}
e.preventDefault();
const relatedFieldName = target.dataset.fieldname;
if (target.classList.contains('t3js-btn-moveoption-top')) {
AbstractSortableSelectItems.moveOptionToTop(fieldElement);
} else if (target.classList.contains('t3js-btn-moveoption-up')) {
AbstractSortableSelectItems.moveOptionUp(fieldElement);
} else if (target.classList.contains('t3js-btn-moveoption-down')) {
AbstractSortableSelectItems.moveOptionDown(fieldElement);
} else if (target.classList.contains('t3js-btn-moveoption-bottom')) {
AbstractSortableSelectItems.moveOptionToBottom(fieldElement);
} else if (target.classList.contains('t3js-btn-removeoption')) {
AbstractSortableSelectItems.removeOption(
fieldElement,
<HTMLSelectElement>FormEngine.getFieldElement(relatedFieldName, '_avail').get(0)
);
}
FormEngine.updateHiddenFieldValueFromSelect(fieldElement, FormEngine.getFieldElement(relatedFieldName).get(0));
FormEngine.legacyFieldChangedCb();
FormEngineValidation.markFieldAsChanged($(fieldElement));
FormEngineValidation.validate();
});
}
}
/*
* 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!
*/
import * as $ from 'jquery';
enum Selectors {
fieldContainerSelector = '.t3js-formengine-field-group',
filterTextFieldSelector = '.t3js-formengine-multiselect-filter-textfield',
filterSelectFieldSelector = '.t3js-formengine-multiselect-filter-dropdown'
}
/**
* Select field filter functions, see TCA option "enableMultiSelectFilterTextfield"
* and "multiSelectFilterItems"
*/
class SelectBoxFilter {
private selectElement: HTMLSelectElement = null;
private filterText: string = '';
private $availableOptions: JQuery = null;
constructor(selectElement: HTMLSelectElement) {
this.selectElement = selectElement;
this.initializeEvents();
}
private initializeEvents(): void {
const wizardsElement = this.selectElement.closest('.form-wizards-element');
if (wizardsElement === null) {
return;
}
wizardsElement.addEventListener('keyup', (e: Event): void => {
if ((<HTMLElement>e.target).matches(Selectors.filterTextFieldSelector)) {
this.filter((<HTMLInputElement>e.target).value);
}
});
wizardsElement.addEventListener('change', (e: Event): void => {
if ((<HTMLElement>e.target).matches(Selectors.filterSelectFieldSelector)) {
this.filter((<HTMLInputElement>e.target).value);
}
});
}
/**
* Filter the actual items
*
* @param {string} filterText
*/
private filter(filterText: string): void {
this.filterText = filterText;
if (!this.$availableOptions) {
this.$availableOptions = $(this.selectElement).find('option').clone();
}
this.selectElement.innerHTML = '';
const matchFilter = new RegExp(filterText, 'i');
this.$availableOptions.each((i, el): void => {
if (filterText.length === 0 || el.textContent.match(matchFilter)) {
this.selectElement.appendChild(el);
}
});
}
}
export = SelectBoxFilter;
/*
* 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!
*/
import {AbstractSortableSelectItems} from './AbstractSortableSelectItems';
import * as $ from 'jquery';
import FormEngineSuggest = require('../../FormEngineSuggest');
class GroupElement extends AbstractSortableSelectItems {
private element: HTMLSelectElement = null;
constructor(elementId: string) {
super();
$((): void => {
this.element = <HTMLSelectElement>document.querySelector('#' + elementId);
this.registerEventHandler();
this.registerSuggest();
});
}
private registerEventHandler(): void {
this.registerSortableEventHandler(this.element);
}
private registerSuggest(): void {
let suggestContainer;
if ((suggestContainer = <HTMLElement>this.element.closest('.t3js-formengine-field-item').querySelector('.t3-form-suggest')) !== null) {
// tslint:disable-next-line:no-unused-expression
new FormEngineSuggest(suggestContainer);
}
}
}
export = GroupElement;
/*
* 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!
*/
import * as $ from 'jquery';
import FormEngine = require('TYPO3/CMS/Backend/FormEngine');