"use strict";

if (typeof window.chat !== "function") {
  // Not initialized

  // Initialize of the class in global namespace
  window.chat = class chat {
    /**
     * Заблокировать функцию закрытия всплывающего окна?
     */
    static freeze = false;

    /**
     * Тело всплывающего окна (массив)
     */
    static body = {};

    /**
     * Инициализирован класс?
     */
    static initialized = false;

    /**
     * Управление кнопками (escape, enter...)
     *
     * Содержит функцию инициализирующую обработчики событий keydown для document
     * функция деинициализируется с document при закрытии окна
     */
    static buttons;

    /**
     * Инстанции активных чатов
     *
     * ['market', 'worker']
     */
    static instances = [];

    /**
     * Ожидание зависимости: ядро
     *
     * @param {function} execute Функция, которая будет встроена в демпфер
     * @param {mixed} args Аргументы встраиваемой функции
     *
     * @return {void}
     */
    static core(execute, ...args) {
      if (typeof execute === "function") {
        // Получена функция

        // Инициализация интервала для ожидания загрузки зависимостей
        const interval = setInterval(() => {
          if (typeof core === "function") {
            // Инициализировано ядро

            // Деинициализация интервала для ожидания загрузки записимостей
            clearInterval(interval);

            // Запуск выполнения
            return execute(...args);
          }
        }, 100);

        // Инициализация деинициализации интервала для ожидания загрузки зависимостей спустя большой срок времени
        setTimeout(() => clearInterval(interval), 3000);
      }
    }

    /**
     * Ожидание зависимости: демпфер
     *
     * @param {function} execute Функция, которая будет встроена в демпфер
     * @param {number} timer Количество миллисекунд для демпфера
     * @param {mixed} args Аргументы встраиваемой функции
     *
     * @return {void}
     */
    static damper(execute, timer = 3000, ...args) {
      if (typeof execute === "function") {
        // Получена функция

        // Инициализация интервала для ожидания загрузки зависимостей
        const interval = setInterval(() => {
          if (typeof damper === "function") {
            // Инициализирован демпфер

            // Деинициализация интервала для ожидания загрузки записимостей
            clearInterval(interval);

            // Запуск выполнения
            return damper(() => execute(...args), timer);
          }
        }, 100);

        // Инициализация деинициализации интервала для ожидания загрузки зависимостей спустя большой срок времени
        setTimeout(() => clearInterval(interval), 3000);
      }
    }

    /**
     * Авторизация
     *
     * @param {function} execute Функция, которая будет встроена в демпфер
     * @param {mixed} args Аргументы встраиваемой функции
     *
     * @return {void}
     */
    static authorization(execute, ...args) {
      if (typeof execute === "function") {
        // Получена функция

        if (core.page === this.page) {
          // Пройдена проверка на страницу

          // Запуск выполнения
          return execute(...args);
        }
      }
    }

    /**
     * Сгенерировать окно с чатами
     *
     * @param {HTMLElement} row Строка
     *
     * @return {void}
     */
    static popup = damper((row) => {
      if (row instanceof HTMLElement) {
        // Получена строка

        // Инициализация идентификатора задачи
        const id = row.getAttribute("id");

        // Инициализация буфера выбранных строк
        const buffer = [
          ...document.querySelectorAll('div[data-selected="true"]'),
        ];

        // Удаление статуса активной строки у остальных строк
        for (const element of buffer) {
          element.removeAttribute("data-selected");
        }

        // Инициализация статуса активной строки (обрабатываемой в данный момент)
        row.setAttribute("data-selected", "true");

        // Инициализация оболочки всплывающего окна
        this.body.wrap = document.createElement("div");
        this.body.wrap.setAttribute("id", "popup");

        // Инициализация оболочки всплывающего окна
        const popup = document.createElement("section");
        popup.classList.add("list");

        // Инициализация заголовка всплывающего окна
        const title = document.createElement("h3");
        title.innerText = id;

        // Инициализация оболочки с основной информацией
        const main = document.createElement("section");
        main.classList.add("main");

        function generate_title(chat) {
          if (typeof core === "function") {
            // Найдено ядро

            if (chat === "worker") {
              // Чат: СОТРУДНИК <-> ОПЕРАТОР

              if (core.interface === "worker") {
                // Сотрудник

                return "Оператор";
              } else if (
                core.interface === "operator" ||
                core.interface === "administrator"
              ) {
                // Оператор или администратор

                return "Сотрудник";
              } else return "Чат";
            }

            if (chat === "market") {
              // Чат: МАГАЗИН <-> ОПЕРАТОР

              if (core.interface === "market") {
                // Магазин

                return "Оператор";
              } else if (
                core.interface === "operator" ||
                core.interface === "administrator"
              ) {
                // Оператор или администратор

                return "Магазин";
              } else return "Чат";
            }
          } else return "Чат";
        }

        // Инициализация колонки чата: МАГАЗИН <-> ОПЕРАТОР
        const market = document.createElement("div");
        market.classList.add("column");
        market.setAttribute("data-chat", "market");

        // Инициализация чата
        const market_chat = document.createElement("div");

        // Инициализация заголовка всплывающего окна
        const market_title = document.createElement("h4");
        market_title.innerText = generate_title("market");

        // Инициализация строки
        const market_message = document.createElement("section");
        market_message.classList.add("row", "merged", "message");
        market_message.setAttribute("data-chat-element", "input");

        // Инициализация поля воода сообщения
        const market_textarea = document.createElement("textarea");
        market_textarea.setAttribute("maxlength", "600");
        market_textarea.setAttribute(
          "placeholder",
          "Текст сообщения",
        );
        market_textarea.setAttribute(
          "onkeydown",
          `if (this.value.length > 60) this.style.setProperty("resize", "vertical"); else this.style.removeProperty("resize");`,
        );

        // Инициализация кнопки отправки сообщения
        const market_send = document.createElement("button");
        market_send.classList.add("sea");
        market_send.setAttribute(
          "onclick",
          `chat.send(this, this.previousElementSibling, document.getElementById('${id}'), 'market', 'message')`,
        );

        const market_icon = document.createElement("i");
        market_icon.classList.add("icon", "enter");

        market.prepend(market_title);

        market.append(market_chat);

        market_message.append(market_textarea);
        market_send.append(market_icon);
        market_message.append(market_send);

        market.appendChild(market_message);

        // Обновлять позицию окна с ошибками при изменении размера textarea (там position: absolute)
        new MutationObserver(() => top(this.body.errors)).observe(
          market_textarea,
          {
            attributes: true,
            attributeFilter: ["style"],
          },
        );

        // Инициализация функции игнорирования блокировки для выбранных кнопок
        market_textarea.addEventListener("keydown", (e) => {
          if (e.keyCode === 27) {
            // Нажата кнопка: "escape"

            // Вызов глобальной функции управления кнопками
            this.buttons(e, true);
          } else if (e.ctrlKey && e.keyCode === 13) {
            // Нажаты кнопки: "control", "enter"

            // Инициализация буфера с текущим статусом блокировки закрытия окна
            const freeze = this.freeze;

            // Блокировка закрытия окна (чтобы не вызвался click() через событие onclick)
            this.freeze = true;

            // Активация виртуальной кнопки "отправить сообщение"
            market_send.click();

            // Возвращение статуса блокировки закрытия окна
            this.freeze = freeze;
          }
        });

        // Добавление функции блокировки удаления окна и клавиш по событиям
        market_textarea.addEventListener("focus", () => this.freeze = true);
        market_textarea.addEventListener(
          "focusout",
          () => this.freeze = false,
        );

        // Инициализация колонки чата: СОТРУДНИК <-> ОПЕРАТОР
        const worker = document.createElement("div");
        worker.classList.add("column");
        worker.setAttribute("data-chat", "worker");

        // Инициализация чата
        const worker_chat = document.createElement("div");

        // Инициализация заголовка всплывающего окна
        const worker_title = document.createElement("h4");
        worker_title.innerText = generate_title("worker");

        // Инициализация строки
        const worker_message = document.createElement("section");
        worker_message.classList.add("row", "merged", "message");
        worker_message.setAttribute("data-chat-element", "input");

        // Инициализация поля воода сообщения
        const worker_textarea = document.createElement("textarea");
        worker_textarea.setAttribute("maxlength", "600");
        worker_textarea.setAttribute(
          "placeholder",
          "Текст сообщения",
        );
        worker_textarea.setAttribute(
          "onkeydown",
          `if (this.value.length > 60) this.style.setProperty("resize", "vertical"); else this.style.removeProperty("resize");`,
        );

        // Инициализация кнопки отправки сообщения
        const worker_send = document.createElement("button");
        worker_send.classList.add("sea");
        worker_send.setAttribute(
          "onclick",
          `chat.send(this, this.previousElementSibling, document.getElementById('${id}'), 'worker', 'message')`,
        );

        const worker_icon = document.createElement("i");
        worker_icon.classList.add("icon", "enter");

        worker.prepend(worker_title);

        worker.append(worker_chat);

        worker_message.append(worker_textarea);
        worker_send.append(worker_icon);
        worker_message.append(worker_send);

        worker.appendChild(worker_message);

        // Обновлять позицию окна с ошибками при изменении размера textarea (там position: absolute)
        new MutationObserver(() => top(this.body.errors)).observe(
          worker_textarea,
          {
            attributes: true,
            attributeFilter: ["style"],
          },
        );

        // Инициализация функции игнорирования блокировки для выбранных кнопок
        worker_textarea.addEventListener("keydown", (e) => {
          if (e.keyCode === 27) {
            // Нажата кнопка: "escape"

            // Вызов глобальной функции управления кнопками
            this.buttons(e, true);
          } else if (e.ctrlKey && e.keyCode === 13) {
            // Нажаты кнопки: "control", "enter"

            // Инициализация буфера с текущим статусом блокировки закрытия окна
            const freeze = this.freeze;

            // Блокировка закрытия окна (чтобы не вызвался click() через событие onclick)
            this.freeze = true;

            // Активация виртуальной кнопки "отправить сообщение"
            worker_send.click();

            // Возвращение статуса блокировки закрытия окна
            this.freeze = freeze;
          }
        });

        // Добавление функции блокировки удаления окна и клавиш по событиям
        worker_textarea.addEventListener("focus", () => this.freeze = true);
        worker_textarea.addEventListener(
          "focusout",
          () => this.freeze = false,
        );

        // Инициализация окна с ошибками
        this.body.errors = document.createElement("section");
        this.body.errors.classList.add(
          "errors",
          "window",
          "list",
          "calculated",
          "hidden",
        );
        this.body.errors.setAttribute("data-errors", true);

        // Инициализация элемента-оболочки с ошибками для всплывающего окна
        const errors = document.createElement("section");
        errors.classList.add("body");

        // Инициализация элемента-списка ошибок
        const dl = document.createElement("dl");

        // Инициализация активного всплывающего окна
        const old = document.getElementById("popup");

        if (old instanceof HTMLElement) {
          // Найдено активное окно

          // Деинициализация быстрых действий по кнопкам
          document.removeEventListener("keydown", this.buttons);

          // Сброс блокировки
          this.freeze = false;

          // Удаление активного окна
          old.remove();
        }

        // Запись в документ
        popup.appendChild(title);

        if (typeof core === "function") {
          // Найдено ядро

          if (core.interface === "worker") {
            // Сотрудник

            main.appendChild(worker);

            // Генерация чата
            this.generate(row, worker_chat, "worker", true, false, true);
          } else if (core.interface === "market") {
            // Магазин

            main.appendChild(market);

            // Генерация чата
            this.generate(row, market_chat, "market", true, false, true);
          } else if (
            core.interface === "operator" || core.interface === "administrator"
          ) {
            // Оператор или администратор

            main.appendChild(market);
            main.appendChild(worker);

            // Генерация чатов
            this.generate(row, market_chat, "market", true, false, true);
            this.generate(row, worker_chat, "worker", true, false, true);
          }
        }

        popup.appendChild(main);
        this.body.wrap.appendChild(popup);

        errors.appendChild(dl);
        this.body.errors.appendChild(errors);
        this.body.wrap.appendChild(this.body.errors);

        document.body.appendChild(this.body.wrap);

        if (typeof core === "function") {
          // Найдено ядро

          if (core.interface === "worker") {
            // Сотрудник

            // Фокусировка
            worker_textarea.focus();
          } else if (core.interface === "market") {
            // Магазин

            // Фокусировка
            market_textarea.focus();
          } else if (
            core.interface === "operator" || core.interface === "administrator"
          ) {
            // Оператор или администратор

            // Фокусировка
            market_textarea.focus();
          }
        }

        // Инициализация переменных для окна с ошибками (12 - это значение gap из div#popup)
        this.body.errors.calculate = (errors) => {
          errors.style.setProperty(
            "transition",
            "0s",
          );
          errors.style.setProperty(
            "--top",
            popup.offsetTop + popup.offsetHeight + 12 + "px",
          );
          setTimeout(
            () => errors.style.removeProperty("transition"),
            100,
          );

          // Инициализация ширины окна с ошибками
          this.body.errors.style.setProperty(
            "--calculated-width",
            popup.offsetWidth + "px",
          );
        };
        this.body.errors.calculate(this.body.errors);
        const resize = new ResizeObserver(() =>
          this.body.errors.calculate(this.body.errors)
        );
        resize.observe(this.body.wrap);

        // Инициализация функции закрытия всплывающего окна
        const click = () => {
          // Блокировка
          if (this.freeze) return;

          // Удаление всплывающего окна
          this.body.wrap.remove();

          // Инициализация буфера выбранных строк
          const buffer = [
            ...document.querySelectorAll('div[data-selected="true"]'),
          ];

          // Удаление статуса активной строки у остальных строк
          for (const element of buffer) {
            element.removeAttribute("data-selected");
          }

          // Деинициализация быстрых действий по кнопкам
          document.removeEventListener("keydown", this.buttons);

          // Деинициализация автообновления чата
          for (const index in this.instances) {
            clearInterval(this.instances[index]);
          }

          // Сброс блокировки
          this.freeze = false;
        };

        // Инициализация функции добавления функции закрытия всплывающего окна
        const enable = () => this.body.wrap.addEventListener("click", click);

        // Инициализация функции удаления функции закрытия всплывающего окна
        const disable = () =>
          this.body.wrap.removeEventListener("click", click);

        // Первичная активация функции удаления всплывающего окна
        enable();

        // Инициализация функции управления кнопками
        this.buttons = (e, force = false) => {
          // Блокировка
          if (!force && this.freeze) return;

          if (e.keyCode === 27) {
            // Нажата кнопка: "escape"

            // Удаление окна
            click();
          }
        };

        // Инициализация быстрых действий по кнопкам
        document.addEventListener("keydown", this.buttons);

        // Добавление функции удаления всплывающего окна по событиям
        popup.addEventListener("mouseenter", disable);
        popup.addEventListener("mouseleave", enable);
      }
    }, 300);

    /**
     * Сгенерировать чат (вызов демпфера)
     *
     * @param {HTMLElement} row Строка <div>
     * @param {HTMLElement} wrap Оболочка с классом ".column" <div>
     * @param {string} chat Тип чата (market, worker, both)
     * @param {bool} scroll Прокрутить до последнего сообщения?
     * @param {bool} sound Проигрывать звук уведомления о новом сообщении?
     * @param {bool} force Принудительное выполнение (используется в damper())
     *
     * @return {void}
     */
    static generate(
      row,
      wrap,
      chat = "worker",
      scroll,
      sound = true,
      force = false,
    ) {
      // Запуск выполнения
      this._generate(row, wrap, chat, scroll, sound, force);
    }

    /**
     * Сгенерировать чат (демпфер)
     *
     * @param {HTMLElement} row Строка <div>
     * @param {HTMLElement} wrap Оболочка с классом ".column" <div>
     * @param {string} chat Тип чата (market, worker, both)
     * @param {bool} scroll Прокрутить до последнего сообщения?
     * @param {bool} sound Проигрывать звук уведомления о новом сообщении?
     * @param {bool} force Принудительное выполнение (используется в damper())
     *
     * @return {void}
     */
    static _generate = damper(
      async (
        row,
        wrap,
        chat = "worker",
        scroll,
        sound = true,
        force = false,
      ) => {
        // Инициализация идентификатора
        const id = row.getAttribute("id");

        // Запуск отсрочки разблокировки на случай, если сервер не отвечает
        const timeout = setTimeout(() => {
          this.errors(["Сервер не отвечает"]);
        }, 5000);

        // Запрос к серверу
        return await fetch(`/task/${id}/chat`, {
          method: "POST",
          headers: {
            "Content-Type": "application/x-www-form-urlencoded",
          },
          body: `chat=${chat}`,
        })
          .then((response) => response.json())
          .then((data) => {
            // Удаление отсрочки разблокировки
            clearTimeout(timeout);

            if (this.errors(data.errors, false, false)) {
              // Сгенерированы ошибки
            } else {
              // Не сгенерированы ошибки (подразумевается их отсутствие)

              if (
                typeof data.chat === "string" && data.chat.length > 0 &&
                wrap instanceof HTMLElement && document.body.contains(wrap)
              ) {
                // Сгенерирован чат

                // Инициализация родителя для реинициализации оболочки
                const parent = wrap.parentElement;

                // Инициализация значения прокрутки
                const scrollTop = wrap.scrollTop;

                // Подсчёт количества сообщений
                const messages = wrap.children.length;

                // Запись в оболочку
                wrap.outerHTML = data.chat;

                // Реинициаизация оболочки
                wrap = parent.querySelector(
                  'section[data-chat-element="messages"]',
                );

                // Если сообщений стало больше, то проиграть звук уведомления
                if (sound && wrap.children.length > messages) {
                  window.notification.play();
                }

                // Перерасчёт положения окна ошибок
                this.body.errors.calculate(this.body.errors);

                // Прокрутка до актуального значения прокрутки
                wrap.scrollTop = scrollTop;

                if (
                  (data.unreaded || scroll) &&
                  wrap.lastElementChild instanceof HTMLElement
                ) {
                  // Получено новое сообщение, либо запрошена прокрутка, и найдено последнее сообщение в чате

                  // Прокрутка до последнего сообщения
                  wrap.lastElementChild.scrollIntoView();
                }
              }

              if (typeof data.row === "string" && data.row.length > 0) {
                // Получена строка

                // Инициализация буфера списка классов
                // const buffer = [...row.classList];

                // Инициализация статуса активной строки
                const selected = row.getAttribute("data-selected");

                // Реинициализация строки
                row.outerHTML = data.row;

                // Реинициализация строки (выражение странное, но правильное)
                row = document.getElementById(row.getAttribute("id"));

                // Копирование классов из буфера классов удалённой строки
                // row.classList.add(...buffer);

                // Копирование статуса активной строки
                if (
                  typeof selected === "string" && selected === "true" &&
                  document.body.contains(document.getElementById("popup")) &&
                  !document.body.contains(
                    document.querySelector('[data-selected="true"]'),
                  )
                ) row.setAttribute("data-selected", "true");
              }

              // Деинициализация устаревшего интервала обновления чата
              if (typeof this.instances[chat] !== "undefined") {
                clearTimeout(this.instances[chat]);
              }

              // Инициализация интервала обновления чата
              this.instances[chat] = setTimeout(
                () => this.generate(row, wrap, chat, false, true, force),
                10000,
              );
            }
          });
      },
      500,
      5,
    );

    /**
     * Отправить сообщение
     *
     * @param {HTMLElement} button Кнопка <button>
     * @param {HTMLElement} textarea Поле ввода сообщения <textarea>
     * @param {HTMLElement} row Строка <div>
     * @param {string} chat Тип чата (market, worker, both)
     * @param {string} type Тип сообщения (message, error)
     * @param {bool} open Открыть чат после отправки
     * @param {string} text Тест сообщения, вместо текста из второго аргумента "textarea"
     *
     * @return {void}
     */
    static send = damper(
      async (
        button,
        textarea,
        row,
        chat = "worker",
        type = "message",
        open = false,
        text,
      ) => {
        if (
          button instanceof HTMLElement &&
          (textarea instanceof HTMLElement ||
            (typeof text === "string" && text.length > 0)) &&
          row instanceof HTMLElement
        ) {
          // Получена кнопка, поле ввода сообщения и строка

          // Инициализация идентификатора строки
          const id = row.getAttribute("id");

          if (typeof id === "string") {
            // Инициализирован идентификатор

            // Запрос к серверу
            return await fetch(`/task/${id}/chat/send`, {
              method: "POST",
              headers: {
                "Content-Type": "application/x-www-form-urlencoded",
              },
              body: `chat=${chat}&type=${type}&text=${
                typeof text === "string" && text.length > 0
                  ? text
                  : textarea.value
              }`,
            })
              .then((response) => response.json())
              .then((data) => {
                if (this.errors(data.errors)) {
                  // Сгенерированы ошибки
                } else {
                  // Не сгенерированы ошибки (подразумевается их отсутствие)

                  if (open) {
                    // Запрошено открытие окна чата

                    // Деинициализация быстрых действий по кнопкам
                    document.removeEventListener("keydown", this.buttons);

                    // Сброс блокировки
                    this.freeze = false;

                    // Реинициализация активного окна (чат)
                    this.popup(row);
                  }

                  if (data.sended) {
                    // Отправлено сообщение

                    if (
                      textarea instanceof HTMLElement &&
                      document.body.contains(textarea)
                    ) {
                      // Найдено поле для ввода сообщения

                      // Сброс поля для ввода сообщения
                      textarea.value = "";
                      textarea.style.removeProperty("height");
                      textarea.style.removeProperty("resize");

                      // Регенерация окна чата
                      this.generate(
                        row,
                        textarea.parentElement.previousElementSibling,
                        chat,
                        true,
                        false,
                        true,
                      );
                    }
                  }

                  if (typeof data.row === "string" && data.row.length > 0) {
                    // Получена строка

                    // Инициализация буфера списка классов
                    /* const buffer = [...row.classList].filter(function (index) {
                      return type !== "solution" ||
                        (type === "solution" && index !== "problematic");
                    }); */

                    // Инициализация статуса активной строки
                    const selected = row.getAttribute("data-selected");

                    // Реинициализация строки
                    row.outerHTML = data.row;

                    // Реинициализация строки (выражение странное, но правильное)
                    row = document.getElementById(row.getAttribute("id"));

                    // Копирование классов из буфера классов удалённой строки
                    // row.classList.add(...buffer);

                    // Копирование статуса активной строки
                    if (
                      typeof selected === "string" && selected === "true" &&
                      document.body.contains(
                        document.getElementById("popup"),
                      ) &&
                      !document.body.contains(
                        document.querySelector('[data-selected="true"]'),
                      )
                    ) row.setAttribute("data-selected", "true");
                  }
                }
              });
          }
        }
      },
      300,
    );

    /**
     * Сгенерировать HTML-элемент со списком ошибок
     *
     * @param {object} registry Реестр ошибок
     * @param {bool} render Отобразить в окне с ошибками?
     * @param {bool} clean Очистить окно с ошибками перед добавлением?
     *
     * @return {bool} Сгенерированы ошибки?
     */
    static errors(registry, render = true, clean = true) {
      // Инициализация ссылки на HTML-элемент с ошибками
      const wrap = document.body.contains(this.body.errors)
        ? this.body.errors
        : document.querySelector('[data-errors="true"]');

      if (wrap instanceof HTMLElement && document.body.contains(wrap)) {
        // Найден HTML-элемент с ошибками

        // Перерасчёт высоты элемента
        function height() {
          wrap.classList.remove("hidden");
          wrap.classList.remove("animation");
          // Реинициализация переменной с данными о высоте HTML-элемента (16 - это padding-top + padding-bottom у div#popup > section.errors)
          wrap.style.setProperty(
            "--height",
            wrap.offsetHeight - 16 + "px",
          );
          wrap.classList.add("animation");
          wrap.classList.add("hidden");
        }

        // Инициализация элемента-списка ошибок
        const list = wrap.getElementsByTagName("dl")[0];

        // Удаление ошибок из прошлой генерации
        if (clean) list.innerHTML = null;

        for (const error in registry) {
          // Генерация HTML-элементов с текстами ошибок

          // Инициализация HTML-элемента текста ошибки
          const samp = document.createElement("samp");

          if (typeof registry[error] === "object") {
            // Категория ошибок

            // Проверка наличия ошибок
            if (registry[error].length === 0) continue;

            // Инициализация HTML-элемента-оболочки
            const wrap = document.createElement("dt");

            // Запись текста категории
            samp.innerText = error;

            // Запись HTML-элементов в список
            wrap.appendChild(samp);
            list.appendChild(wrap);

            // Реинициализация высоты
            height();

            // Обработка вложенных ошибок (вход в рекурсию)
            this.errors(registry[error], false);
          } else {
            // Текст ошибки (подразумевается)

            // Инициализация HTML-элемента
            const wrap = document.createElement("dd");

            // Запись текста ошибки
            samp.innerText = registry[error];

            // Запись HTML-элемента в список
            wrap.appendChild(samp);
            list.appendChild(wrap);

            // Реинициализация высоты
            height();
          }
        }

        if (render) {
          // Запрошена отрисовка

          if (
            list.childElementCount !== 0
          ) {
            // Найдены ошибки

            // Сброс анимации
            // УЛОВКА: таким образом не запускается анимация до взаимодействия с элементом (исправлял это в CSS, но не помню как)
            wrap.classList.add("animation");

            // Отображение
            wrap.classList.remove("hidden");
          } else {
            // Не найдены ошибки

            // Скрытие
            wrap.classList.add("hidden");
          }
        }

        return list.childElementCount === 0 ? false : true;
      }

      return false;
    }
  };
}

// Вызов события: "инициализировано"
document.dispatchEvent(
  new CustomEvent("chat.initialized", {
    detail: { chat: window.chat },
  }),
);
