/* * 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 AjaxRequest = require('TYPO3/CMS/Core/Ajax/AjaxRequest'); import {MessageUtility} from '../../Utility/MessageUtility'; import {AjaxDispatcher} from './../InlineRelation/AjaxDispatcher'; import {InlineResponseInterface} from './../InlineRelation/InlineResponseInterface'; import DocumentService = require('TYPO3/CMS/Core/DocumentService'); import NProgress = require('nprogress'); import Sortable = require('Sortable'); import FormEngine = require('TYPO3/CMS/Backend/FormEngine'); import FormEngineValidation = require('TYPO3/CMS/Backend/FormEngineValidation'); import Icons = require('../../Icons'); import InfoWindow = require('../../InfoWindow'); import Modal = require('../../Modal'); import Notification = require('../../Notification'); import RegularEvent = require('TYPO3/CMS/Core/Event/RegularEvent'); import Severity = require('../../Severity'); import Utility = require('../../Utility'); enum Selectors { toggleSelector = '[data-bs-toggle="formengine-inline"]', controlSectionSelector = '.t3js-formengine-irre-control', createNewRecordButtonSelector = '.t3js-create-new-button', createNewRecordBySelectorSelector = '.t3js-create-new-selector', deleteRecordButtonSelector = '.t3js-editform-delete-inline-record', enableDisableRecordButtonSelector = '.t3js-toggle-visibility-button', infoWindowButton = '[data-action="infowindow"]', synchronizeLocalizeRecordButtonSelector = '.t3js-synchronizelocalize-button', uniqueValueSelectors = 'select.t3js-inline-unique', revertUniqueness = '.t3js-revert-unique', controlContainer = '.t3js-inline-controls', } enum States { new = 'inlineIsNewRecord', visible = 'panel-visible', collapsed = 'panel-collapsed', notLoaded = 't3js-not-loaded', } enum Separators { structureSeparator = '-', } enum SortDirections { DOWN = 'down', UP = 'up', } interface RequestQueue { [key: string]: AjaxRequest; } interface ProgressQueue { [key: string]: any; } interface Appearance { expandSingle?: boolean; useSortable?: boolean; } interface UniqueDefinition { elTable: string; field: string; max: number; possible: { [key: string]: string }; selector: string; table: string; type: string; used: UniqueDefinitionCollection; } interface UniqueDefinitionCollection { [key: string]: UniqueDefinitionUsed; } interface UniqueDefinitionUsed { table: string; uid: string | number; } class InlineControlContainer { private container: HTMLElement = null; private ajaxDispatcher: AjaxDispatcher = null; private appearance: Appearance = null; private requestQueue: RequestQueue = {}; private progessQueue: ProgressQueue = {}; private noTitleString: string = (TYPO3.lang ? TYPO3.lang['FormEngine.noRecordTitle'] : '[No title]'); /** * @param {string} objectId * @return HTMLDivElement */ private static getInlineRecordContainer(objectId: string): HTMLDivElement { return document.querySelector('[data-object-id="' + objectId + '"]'); } /** * @param {string} objectId * @return HTMLButtonElement */ private static getCollapseButton(objectId: string): HTMLButtonElement { return document.querySelector('[aria-controls="' + objectId + '_fields"]'); } /** * @param {string} objectId */ private static toggleElement(objectId: string): void { const recordContainer = InlineControlContainer.getInlineRecordContainer(objectId); if (recordContainer.classList.contains(States.collapsed)) { InlineControlContainer.expandElement(recordContainer, objectId); } else { InlineControlContainer.collapseElement(recordContainer, objectId); } } /** * @param {HTMLDivElement} recordContainer * @param {string} objectId */ private static collapseElement(recordContainer: HTMLDivElement, objectId: string): void { const collapseButton = InlineControlContainer.getCollapseButton(objectId); recordContainer.classList.remove(States.visible); recordContainer.classList.add(States.collapsed); collapseButton.setAttribute('aria-expanded', 'false'); } /** * @param {HTMLDivElement} recordContainer * @param {string} objectId */ private static expandElement(recordContainer: HTMLDivElement, objectId: string): void { const collapseButton = InlineControlContainer.getCollapseButton(objectId); recordContainer.classList.remove(States.collapsed); recordContainer.classList.add(States.visible); collapseButton.setAttribute('aria-expanded', 'true'); } /** * @param {string} objectId * @return boolean */ private static isNewRecord(objectId: string): boolean { const recordContainer = InlineControlContainer.getInlineRecordContainer(objectId); return recordContainer.classList.contains(States.new); } /** * @param {string} objectId * @param {boolean} value */ private static updateExpandedCollapsedStateLocally(objectId: string, value: boolean): void { const recordContainer = InlineControlContainer.getInlineRecordContainer(objectId); const ucName = 'uc[inlineView]' + '[' + recordContainer.dataset.topmostParentTable + ']' + '[' + recordContainer.dataset.topmostParentUid + ']' + recordContainer.dataset.fieldName; const ucFormObj = document.getElementsByName(ucName); if (ucFormObj.length) { (ucFormObj[0]).value = value ? '1' : '0'; } } /** * @param {UniqueDefinitionCollection} hashmap */ private static getValuesFromHashMap(hashmap: UniqueDefinitionCollection): Array { return Object.keys(hashmap).map((key: string) => hashmap[key]); } private static selectOptionValueExists(selectElement: HTMLSelectElement, value: string): boolean { return selectElement.querySelector('option[value="' + value + '"]') !== null; } /** * @param {HTMLSelectElement} selectElement * @param {string} value */ private static removeSelectOptionByValue(selectElement: HTMLSelectElement, value: string): void { const option = selectElement.querySelector('option[value="' + value + '"]'); if (option !== null) { option.remove(); } } /** * @param {HTMLSelectElement} selectElement * @param {string} value * @param {UniqueDefinition} unique */ private static reAddSelectOption(selectElement: HTMLSelectElement, value: string, unique: UniqueDefinition): void { if (InlineControlContainer.selectOptionValueExists(selectElement, value)) { return; } const options: NodeListOf = selectElement.querySelectorAll('option'); let index: number = -1; for (let possibleValue of Object.keys(unique.possible)) { if (possibleValue === value) { break; } for (let k = 0; k < options.length; ++k) { const option = options[k]; if (option.value === possibleValue) { index = k; break; } } } if (index === -1) { index = 0; } else if (index < options.length) { index++; } // recreate the