<td colspan="2" align="center">
    <h2>{$params_tpl['title']}{$params_tpl['help']}</h2>
    <div style="display: flex; flex-direction: column;">{$tpl_types['result']}</div>
    <div style="display: flex; flex-direction: column;">{$tpl_types['field1']}</div>
    <div style="display: flex; flex-direction: column;">{$tpl_types['func_row']}</div>
    <div style="display: none; flex-direction: column;">{$tpl_types['flex_data']}</div>
</td>
<script>
  (function() {
    const tplNum = {$tpl_num};
    {literal}
      const selectedInputElement = document.querySelector(`textarea[name="calc_params[${tplNum}][func_row]"]`);
      const $selectedInputElement = $(`textarea[name="calc_params[${tplNum}][func_row]"]`)
      const selectElement = document.querySelector(`select[name="calc_params[${tplNum}][field1]"]`);
      const $selectElement = $(`select[name="calc_params[${tplNum}][field1]"]`);
      
      const emptyOption = document.createElement('option');
      selectElement.prepend(emptyOption);
      const optionsMap = [ ...selectElement.options ].reduce((acc, option) => {
        acc[option.text] = option.value || '';
        return acc;
      }, {});

      /**
       * Вставляет текст в поле ввода (`input`) в позицию текущего курсора.
       *
       * @param {HTMLInputElement|HTMLTextAreaElement} input - Элемент ввода (`<input>` или `<textarea>`).
       * @param {string} text - Текст, который необходимо вставить в текущую позицию курсора.
      *
      * @description
      * Эта функция вставляет переданный текст в поле ввода в месте, где находится текущий курсор.
      * Она обновляет значение элемента ввода и корректно устанавливает позицию курсора после вставки текста.
      *
      * @example
      * const textarea = document.querySelector('textarea');
      * textarea.addEventListener('focus', () => {
      *   textarea.addEventListener('input', () => {
      *     insertTextAtCursor(textarea, '{newValue}');
      *   });
      * });
      */
      const insertTextAtCursor = (input, text) => {
        const { value, selectionStart, selectionEnd } = input;

        // Формируем обновленный текст
        const updatedText =
          value.slice(0, selectionStart) +
          text +
          value.slice(selectionEnd);

        // Обновляем значение и позицию курсора
        input.value = updatedText;
        input.selectionStart = selectionStart + text.length;
        input.selectionEnd = selectionStart + text.length;

      };

      /**
       * Обработчик события изменения элемента `<select>`.
       *
       * @param {Event} event - Объект события, переданный браузером.
       * @param {HTMLSelectElement} event.target - Элемент `<select>`, в котором произошло событие.
      *
      * @description
      * При изменении выбора в элементе `<select>`:
      * 1. Получает выбранный элемент и его текстовое содержимое.
      * 2. Формирует строку в формате `{selectedOption.textContent}`.
      * 3. Вставляет эту строку в поле `selectedInputElement` в текущую позицию курсора.
      * 4. Триггерит событие `change` для элемента `selectedInputElement`.
      * 5. Сбрасывает выбранное значение в элементе `<select>` обратно в начало.
      *
      * Используется для динамического обновления интерфейса и синхронизации данных.
      *
      * @example
      * const selectedInputElement = document.querySelector('textarea[name="calc_params"]');
      * const selectElement = document.querySelector('select[name="calc_params[field1]"]');
      *
      * selectElement.addEventListener('change', handleSelectChange);
      *
      * // При выборе элемента в `<select>` текст вставляется в `selectedInputElement`.
      * 
      * @see selectedInputElement - Элемент `<textarea>` для вставки текста.
      */
      const handleSelectChange = ({ target }) => {
        const { value, options, selectedIndex } = target;

        if (!value) return; // Если значение пустое, ничего не делаем

        const selectedOption = options[selectedIndex];
        const optionText = `{${selectedOption.textContent}}`;

        // Вставка текста в поле
        insertTextAtCursor(selectedInputElement, optionText);

        // Триггерим событие "change" для `selectedInputElement`
        selectedInputElement.dispatchEvent(new Event('change'));

        // Сбрасываем выбранное значение
        target.selectedIndex = 0;
      };
      /**
       * Извлекает имена опций из строки, используя переданное регулярное выражение.
       *
       * @param {string} value - Исходная строка, из которой необходимо извлечь имена опций.
      * @param {RegExp} regex - Регулярное выражение для поиска текста в строке.
      * @returns {string[]} - Массив строк, содержащий имена опций.
      *
      * @description
      * Функция разделяет строку `value` по регулярному выражению `regex` и возвращает массив элементов.
      * Метод `filter(Boolean)` удаляет пустые элементы из массива.
      *
      * @example
      * const value = " {Цена} - {Связь.Кредит}";
      * const regex = /{([^}]+)}/g;
      * const options = getOptionsNamesFromText(value, regex);
      * console.log(options); 
      * // результат: ['Цена', '-', 'Связь.Кредит']
      */
      const getOptionsNamesFromText = (value, regex) => value.split(regex).filter(Boolean);
      /**
       * Получает идентификаторы полей из массива опций из Поля-источник в Поле записи функции.
       *
       * @param {string[]} optionsNames - Массив строк, содержащих имена опций(Поле-источник).
      * @returns {string[]} - Массив идентификаторов полей, полученных из `optionsMap`.
      *
      * @description
      * Для каждого имени в массиве `optionsNames` проверяется его наличие в объекте `optionsMap`(Поле-источник : id).
      * Если имя присутствует, то из значения `optionsMap` получается идентификатор поля.
      * Значение разбивается по символу `'.'`, а идентификатор очищается от символа `'f'`.
      * Возвращаемый массив содержит уникальные идентификаторы для всех найденных опций.
      *
      * @example
      * const optionsMap = {
      *   Цена: "f1013",
      *   "Связь.Кредит": "f1014.f121"
      * };
      * const optionsNames = ["Цена", "-"" ,"Связь.Кредит"];
      * const fields = getFieldsIdFromOptions(optionsNames);
      * console.log(fields); 
      * // результат: ['1013', '1014']
      * 
      * @see optionsMap - Объект соответствия опций id (Поле-источник : id).
      */
      const getFieldsIdFromOptions = (optionsNames) => {
        const fieldsId = optionsNames
          .filter(optionName => Object.prototype.hasOwnProperty.call(optionsMap, optionName))
          .map(optionName => {
            const fieldIdWithF = optionsMap[optionName];
            return fieldIdWithF.split('.')[0].replace('f', '');
          })
        return fieldsId || [];
      }
      
      /**
       * Получаем строку вычисления из массива опций из Поля-источник в Поле записи функции.
       *
       * @param {string[]} optionsNames - Массив строк, содержащих названия опций(Поле-источник).
      * @returns {string} - Строка, сформированная путем замены имен опций их значениями из объекта `optionsMap`.
      *
      * @description
      * Для каждой опции из массива `optionsNames` проверяется её наличие в объекте `optionsMap`(Поле-источник : id).
      * Если опция присутствует в `optionsMap`, она заменяется на строку вида `$line['<replacement>']`.
      * Если опция отсутствует, она остаётся без изменений в массиве.
      * Возвращаемая строка представляет собой конкатенацию всех заменённых и неизменённых элементов.
      *
      * @example
      * const optionsMap = {
      *   Цена: "f1013",
      *   "Связь.Кредит": "f1014.f121"
      * };
      * const optionsNames = ["Цена", "-"" ,"Связь.Кредит"];
      * const result = getMathStringFromOptions(optionsNames);
      * console.log(result); 
      * // результат: "$line['f1013'] - $line['f1014.f121']"
      * 
      * @see optionsMap - Объект соответствия опций id (Поле-источник : id).
      */
      const getMathStringFromOptions = (optionsNames) => {
        const optionsValues = optionsNames.map(optionName => {
          const isOptionMatched = Object.prototype.hasOwnProperty.call(optionsMap, optionName)
          if (!isOptionMatched) return optionName;
          const selectValue = optionsMap[optionName];
          
          const replacement = `$line['${selectValue.replace(/\./g, "']['")}']`;
          return replacement;
        });
        return optionsValues.join('');
      }
      
      /**
       * Обработчик события изменения содержимого textarea.
       * Удаляет символы перевода строки и возврата каретки из текста,
       * ищет текст в фигурных скобках, получает идентификаторы опций и обновляет их в инпуты.
       *
       * @param {Event} event - Объект события, переданный браузером.
      * @property {HTMLTextAreaElement} event.target - Текстовое поле, в котором произошло событие.
      * 
      * @description
      * 1. Удаляет символы перевода строки (\n) и возврата каретки (\r) из текста в textarea.
      * 2. Использует регулярное выражение `{([^}]+)}` для поиска текста в фигурных скобках.
      * 3. Извлекает названия опций из текста и идентификаторы из них.
      * 4. Обновляет идентификаторы в полях формы, связанных с расчетами.
      * 5. Записывает вычисленную формулу в соответствующий инпут в DOM.
      *
      * @example
      * Пример вызова при изменении содержимого textarea:
      *
      * <textarea name="calc_params[1][func_row]" onchange="handleTextareaChange(event)"></textarea>
      *
      * @see getOptionsNamesFromText - функция для извлечения имен из текста по регулярному выражению.
      * @see getFieldsIdFromOptions - функция для получения id полей в вычислении.
      * @see getMathStringFromOptions - функция получения строки вычисления.
      * @see setStartCalcFields - обновляет состояние действий для запуска вычисления.
      */
      const handleTextareaChange = (event) => {
        // Удаление всех символов перевода строки (\n) и возврата каретки (\r) из значения selectedInputElement
        event.target.value = event.target.value.replace(/\n/g, '').replace(/\r/g, '');
        const newValue = event.target.value;
        // Регулярное выражение для поиска текста в фигурных скобках
        const regex =/{([^}]+)}/g;

        const optionsNames = getOptionsNamesFromText(newValue, regex);
        const fieldsId = getFieldsIdFromOptions(optionsNames);
        const mathString = getMathStringFromOptions(optionsNames);
        
        setStartCalcFields(fieldsId || []);
        
        //запись полученной формулы в инпут
        document.getElementsByName(`calc_params[${tplNum}][flex_data]`)[0].value = mathString;
      };

      // Привязываем обработчики событий
      $selectElement.change(handleSelectChange);
      $selectedInputElement.change(handleTextareaChange);
    {/literal}
  })();
</script>
