var openedAllRec = false;
var recordView = false;
var BLOCK_HEIGHT = 20.5; // Фиксированный размер блока (30 мин)
var prev_val = '';
var isCalendarTarget = 0;
var globalCalendarEventId = 0;
var addLinkFieldsBlockPoints = {
    top: '',
    left: ''
};

/**
 Состояние, аналогичное React, для отслеживания и обновления значения.
  * 
  * @param any initialValue - Начальное значение состояния.
* @returns Array Массив, содержащий:
*  - Функция для получения текущего значения состояния.
*  - Функция для обновления состояния.
*  - Функция для подписки на изменение состояния.
*/
  const useState = (initialValue) => {
  let value = initialValue; // Храним текущее состояние
  const listeners = new Set(); // Множество слушателей изменений состояния

  // Функция для обновления состояния
  const setState = (newValue) => {
    // Если передано как функция, вызываем её с текущим значением
    if (typeof newValue === 'function') {
      value = newValue(value);
    } 
    // Если передан объект, создаем новый объект для предотвращения мутации
    else if (typeof newValue === 'object' && newValue !== null) {
      value = JSON.parse(JSON.stringify(newValue)); // Создаем новый объект (глубокое копирование)
    } else {
      // Для примитивных значений просто присваиваем
      value = newValue;
    }
    
    // Уведомляем всех подписчиков о новом значении
    listeners.forEach((listener) => listener(value));
  };

  // Функция для подписки на изменения состояния
  const onStateChange = (listener) => {
    listeners.add(listener);
    return () => listeners.delete(listener); // Возвращаем функцию для отмены подписки
  };

  // Возвращаем массив с функцией для получения значения, функцией для его обновления и функцией подписки
  return [() => value, setState, onStateChange]; 
};


/**
 * Инициализирует события быстрого редактирования в календаре
 */
function init_calendar_fast_edit() {
    var elems = $('[class^="fast_edit_span_"].not-init, input.sub_edit_link_input.not-init, [id^="fast_edit_span_"].type_4_mult');
    if (elems.length > 0) {
        elems.each(function(i, elem) {
            if ($(elem).hasClass('fast_edit_text')) {
                addHandler_text(elem);
                autosize(elem);
                $(elem).css('resize', 'vertical');
                $(elem).css('width', '290');
                if ($(elem).prop("tagName") == 'TEXTAREA') {
                    $(elem).css('width', '287');
                    $(elem).css('min-height', '30px');
                    $(elem).css('box-sizing', 'border-box');
                    $(elem).css('padding', '5px 0 0 5px');
                    $(elem).css('max-height', '200');
                }
                if ($(elem).prop("tagName") == 'INPUT') {

                    $(elem).css('border', '1px dotted rgb(160, 160, 160)');
                }

            } else if ($(elem).hasClass('fast_edit_span--multi-select')) {
                addHandler_mult_select(elem);
            } else if ($(elem).hasClass('sub_fast_edit_select--select')) {
                addHandler_select(elem);
            } else if ($(elem).hasClass('type_4_mult')) {
                $(elem).on('change', function(){
                    var a = $(elem).parent().parent().parent().parent().parent().parent().parent().parent().find('.cal_event_footer'); // футер корневого элемента
                    a.find('.go_event_link').css('display', 'none');
                    a.find('.save_event_button').css('display', 'block');
                })
            } else if ($(elem).hasClass('combobox') ||
                $(elem).hasClass('sub_edit_link')) {
                calendar_combobox($(elem), true);
                if (!$(elem).parent().parent().hasClass( "event_options_block" )) {
                    $(elem).next().children().first().on('click', function(){
                        isCalendarTarget = 1;
                        globalActiveFieldId = find_select_of_autocomplete($(this)).attr('field_id');
                        globalCalendarEventId = $(this).parent().next('.hidden_event_id').text();
                        // Вычисляем положение popup формы добавления полей
                        var offsetPos = $(this).parent('.autocomplete').position();
                        addLinkFieldsBlockPoints.left = offsetPos.left;
                        addLinkFieldsBlockPoints.top =  offsetPos.top + 20;
                        prev_val = $(elem).val();
                    });
                    $(elem).next().children().first().on('blur', function(){
                        if (prev_val !== $(elem).val()) { // выводим кнопку сохранить
                            var a = $(elem).parent().parent().parent().parent().parent().parent().parent().find('.cal_event_footer'); // футер корневого элемента
                            a.find('.go_event_link').css('display', 'none');
                            a.find('.save_event_button').css('display', 'block');
                            var field_id = $(elem).attr('field_id');
                            var line_id = $(elem).attr('line_id');

                            var data = $(elem).next().children().first().val();
                            for(var i = 0; i < $(elem).children().length;i++) {
                                if ($($(elem).children()[i]).text() === data) {
                                    $(elem).attr('ac_link_val', $($(elem).children()[i]).val());
                                }
                            }
                            if (line_id !== 'new') {
                                $(elem).next().children().first().css('background-color','#fff6ad');
                                $(elem).next().children().first().next().css('background-color','#fff6ad');
                                var ac_link_val = $(elem).attr('ac_link_val');
                                save_value(field_id, line_id , ac_link_val);
                                $(elem).next().children().first().animate({
                                    backgroundColor: '#fff'
                                }, 500);
                                $(elem).next().children().first().next().animate({
                                    backgroundColor: '#fff'
                                }, 500);
                            }
                        }
                        prev_val = $(elem).val();
                    })

                    // Устанавливаю значение по умолчанию
                    const valueToSet = $(elem).attr('ac_link_val');
                    if ($(elem).find(`option[value="${valueToSet}"]`).length === 0) {
                      $(elem).append(`<option value="${valueToSet}">${valueToSet}</option>`);
                    }
                    $(elem).val(valueToSet).trigger('change');
                    
                }
            } else if ($(elem).hasClass('fast_edit_file')) {
                addHandler_file(elem);
            } else if ($(elem).hasClass('fast_edit_datepicker')) {
                addHandler_date(elem);
                $(elem).datetimepicker({
                    showOn:"button",
                    dateFormat: lang.date_js_format,
                    buttonImage: "images/calbtn.png",
                    buttonImageOnly: true,
                    buttonText: lang.Calendar,
                    showAnim: (('\v'=='v')?"":"show")
                }).attr('placeholder', lang.datetime_placeholder);
            }
        });
    }

    $('.not-init').each(function() {
        $(this).removeClass('not-init');
    });
}

// Показать/скрыть календарь сбоку
function viewMiniCalendar() {
    if (document.getElementById("mini_calendar_layout").style.display == "none") { // Календарь скрыт - показываем
        $("#mini_calendar_layout").animate({width: "show", opacity: "show", marginLeft: "30px"}, 200);
        $.cookie("mini_calendar_hide" + calendar.id, null);
        document.getElementById("view_calendar_arrow").innerHTML = "◀";
        $("#calendar_scrollable-content").removeClass('mini_calendar_layout__hide');
        $("#calendar_scrollable-content").addClass('mini_calendar_layout__show');
    }
    else { // Скрываем календарь
        $("#mini_calendar_layout").animate({width: "hide", opacity: "hide", marginLeft: "0px"}, 200);
        $.cookie("mini_calendar_hide" + calendar.id, 1);
        document.getElementById("view_calendar_arrow").innerHTML = "▶";
        $("#calendar_scrollable-content").addClass('mini_calendar_layout__hide');
        $("#calendar_scrollable-content").removeClass('mini_calendar_layout__show');
    }
    setTimeout("fixCalendarLayout()", 300);
}

// Листание календаря сбоку, dir - направление, вперёд-назад от текущей даты
function getMiniCalendar(dir) {
    $("#mini_calendar_header").animate({color: '#c0c0c0'}, 200);
    $.ajax({
        type: "POST",
        url: "calendar.php",
        data: {
            csrf: csrf,
            id: calendar.id,
            get: 'mini_calendar',
            data: {
              month: calendar.month, 
              year: calendar.year, 
              direction: dir
          }
        },
        success: function (msg) {
            obj_data = JSON.parse(msg);
            if (!obj_data) {
                jalert("Incorrect response server!");
                return;
            }

            var mini_calendar = obj_data.mini_calendar;
            document.getElementById("mini_calendar_header").innerHTML = obj_data.monthname + " " + obj_data.year;
            $("#mini_calendar_header").animate({color: '#000000'}, 200);

            calendar.month = obj_data.month;
            calendar.year = obj_data.year;

            // Построение календаря
            var cal_content = "";
            for (str_num in mini_calendar) {
                cal_content += "<tr";
                if (mini_calendar[str_num]['cur_week']) cal_content += " class='m_cur_week'"; // Текущая неделя
                cal_content += ">";
                for (day_num in mini_calendar[str_num]) {
                    if (day_num == "cur_week") continue;
                    cal_content += "<td onclick='jumpToDay(" + mini_calendar[str_num][day_num]['day'] + ", " + mini_calendar[str_num][day_num]['month'] + ", " + mini_calendar[str_num][day_num]['year'] + ")'";
                    if (mini_calendar[str_num][day_num]['cur'] != "1" || mini_calendar[str_num][day_num]['today'] == "1") {
                        cal_content += " style='";
                        if (mini_calendar[str_num][day_num]['cur'] != "1") // День текущего отображаемого месяца
                            cal_content += "color:#989898;";
                        if (mini_calendar[str_num][day_num]['today'] == "1")
                            cal_content += "border: 1px dotted #989898; color: #cd6b69"; // Сегодняшний день
                        cal_content += "'";
                    }
                    cal_content += ">"
                    if (mini_calendar[str_num][day_num]['event'] == "1")
                        cal_content += "<b>" + mini_calendar[str_num][day_num]['day'] + "</b>";
                    else
                        cal_content += mini_calendar[str_num][day_num]['day'];
                    cal_content + "</td>";
                }
                cal_content += "</tr>";
            }
            $("#mini_calendar_content").animate({opacity: 'hide'}, 200, function () {
                $("#mini_calendar_content").html(cal_content);
                $("#mini_calendar_content").animate({opacity: 'show'}, 400);
            });
        }
    });
}

// Переходы по типам и страницам календаря
/**
 * @param {string} type - режим календаря(день, неделя, месяц)
 */
function calendar_transition(link, type) {
    if (type) {
        $.cookie('default_calendar_view', type)
    }
    if ($(".event_options_block:visible").length > 0) {
        $("#fast_search_submit_form").attr("action", link);
        fastSearchSubmit();
    }
    else
        location.href = link;
}

// Переход ко дню
function jumpToDay(day, month, year) {
    calendar_transition("calendar.php?id=" + calendar.id + "&day=" + day + "&month=" + month + "&year=" + year);
}

// Показать все записи дня (временно не используется)
function viewAllRecords(day, month, year) {
    if (openedAllRec) {
        hideAllRecords();
        setTimeout("viewAllRecords('" + day + "', " + month + ", " + year + ")", 100);
        return;
    }
    var qWidth;
    var qHeight;

    document.getElementById("q_short_header_" + day + "_" + month + "_" + year).style.display = "none";
    document.getElementById("q_full_header_" + day + "_" + month + "_" + year).style.display = "block";

    qWidth = '300';


    document.getElementById("quadr_" + day + "_" + month + "_" + year).style.width = "300px";
    document.getElementById("quadr_" + day + "_" + month + "_" + year).style.position = "absolute";
    document.getElementById("quadr_" + day + "_" + month + "_" + year).style.zIndex = 400;
    document.getElementById("quadr_" + day + "_" + month + "_" + year).style.border = "1px solid black";
    document.getElementById("quadr_" + day + "_" + month + "_" + year).style.background = "#ffffff";

    $("#hidden_items" + day + "_" + month + "_" + year).animate({height: "show"}, 200);
    $("#more_link" + day + "_" + month + "_" + year).animate({height: "hide", opacity: "hide"}, 200);

    openedAllRec = day + "_" + month + "_" + year;
}

// Скрыть все записи дня (временно не используется)
function hideAllRecords() {
    if (!openedAllRec) return;
    $("#more_link" + openedAllRec).animate({height: "show", opacity: "show"}, 100);
    $("#hidden_items" + openedAllRec).animate({height: "hide"}, 100);

    sourceWidth = $("#cell_" + openedAllRec).width() + "px";
    $("#quadr_" + openedAllRec).animate({width: sourceWidth, marginLeft: "0px"}, 100, function () {
        document.getElementById("quadr_" + openedAllRec).style.position = "static";
        document.getElementById("quadr_" + openedAllRec).style.zIndex = 0;
        document.getElementById("quadr_" + openedAllRec).style.border = "none";
        document.getElementById("quadr_" + openedAllRec).style.background = "transparent";
        document.getElementById("q_short_header_" + openedAllRec).style.display = "block";
        document.getElementById("q_full_header_" + openedAllRec).style.display = "none";

        openedAllRec = false;
    });
}
var my_event_id = calendar.default_event;
// Смена события по умолчанию
function changeDefautEvent(event_id) {
    my_event_id = event_id;
    if (event_id == calendar.default_event) return; // Клик по текущему событию
    calendar.default_event = event_id;
    $.cookie("calendar_default_event" + calendar.id, event_id);
    $(".calendar_legend_event").removeClass("calendar_legend_default");
    $("#calendar_legend_event" + event_id).addClass("calendar_legend_default");
    if (calendar.group_field != all_calendar_events[event_id]['group_field'] && calendar.day) // Дневной вид, поле группировки текущего события не равно полю группировки нового события - перестраиваем календарь
        window.location.reload();
}

// Скрытые события
/**
 * @param {string} event_id
 * @param {string} type [0 - месяц, 1 - неделя, 2 - день]
 * @param {string} hce - hidden calendar event
 * @param {boolean}  - first init flag
 */
var hiddenEvents = new Object;
function viewHideEvents(event_id, type, flag = false) {
    if ($.cookie("hce_" + event_id + "_" + type) && !flag) { // Отображаем события
        $(".calendar_event_id" + event_id).fadeIn(200);
        $("#one_event_legend" + event_id).animate({opacity: 1}, 200);
        $.removeCookie("hce_" + event_id + "_" + type);
    }
    else { // скрываем события
        $(".calendar_event_id" + event_id).fadeOut(200);
        $("#one_event_legend" + event_id).animate({opacity: 0.3}, 200);
        viewSystemHelp(event_id, 0)
        $.cookie("hce_" + event_id + "_" + type, true);
    }
}
/**
 * @param {boolean} работаем с фоновыми событиями(true) или с обычными(false)
 */
function resizeRow(background)
{
    var i,j,z,item,checkVal,element_counter,rows,event_condition;
    var cache_array = new Array();
    if ($('#big_calendar table tbody')) {
        rows = $('#big_calendar table tbody')[0].childElementCount; // количество рядов календаря
    } else {
        console.log('календарь не загружен');
        return;
    }

    for (i = 0; i < Object.keys(all_calendar_events).length; i++) { // формируем название для условия
        event_condition += Object.keys(all_calendar_events)[i];
    }
    cache_array[event_condition] = new Array();

    var number_of_groups = $('#big_calendar table tbody')[0].children[0].childElementCount - 1;

    var calendar_type = $.cookie('default_calendar_view');
    switch (calendar_type) {
        case 'day':
            calendar_type = 2;
            break;
        case 'week':
            calendar_type = 1;
            break;
        default:
            calendar_type = 0;
    }

    for (z = 0; z < number_of_groups; z++) {
        cache_array[event_condition][z] = new Array();
        for (var rows_counter = 0; rows_counter < rows; rows_counter++) {
            cache_array[event_condition][z]['row'+rows_counter] = new Array();
        }
    }

    var hidden_events = new Array();
    for (item in $.cookie()) {
        if (item.includes('hce')) {
            var a = item.split("_");
            if (a[2] == calendar_type)  {
                hidden_events.push(a[1]);
            }
        }
    }
    var min_z_index = 0;
    for (z = 0; z < number_of_groups; z++) {
        i = 0;
        switch (calendar_type) {
            case 2:
                i = 1;
                break;
            case 1:
                i = 3;
                break;
            default:
                i = 0;
        }
        for (i; i < rows; i++) {
            var el = $('#big_calendar table tbody');
            if (i % 2) {
                el = el[0].children[i].children[(z + 1)];
            } else {
                el = el[0].children[i].children[z];
            }
            if (el) {
                el = el.cloneNode([true]);

                for (var min_z_index_counter = 0; min_z_index_counter < el.children.length; min_z_index_counter++) {
                    if ((el.children[min_z_index_counter].style.zIndex > min_z_index) && (el.children[min_z_index_counter].getAttribute('is_background') !== '0')) {
                        min_z_index = parseInt(el.children[min_z_index_counter].style.zIndex);
                    }
                }

                if (background) {
                    for (j = 0; j < Object.keys(all_calendar_events).length; j++) {
                        if (all_calendar_events[Object.keys(all_calendar_events)[j]].background === '0') {
                            hidden_events.push(Object.keys(all_calendar_events)[j]);
                        }
                    }

                    hidden_events = unique(hidden_events);

                    for (var hidden_events_counter = 0; hidden_events_counter < hidden_events.length; hidden_events_counter++) {
                        for (element_counter = 0; element_counter < el.children.length; element_counter++) {
                            checkVal = el.children[element_counter].id.split("_");
                            checkVal = checkVal[checkVal.length - 1];
                            if (checkVal == hidden_events[hidden_events_counter]) {
                                el.removeChild(el.children[element_counter]);
                                element_counter--;
                            }
                        }
                    }
                } else {
                    for (var all_calendar_events_counter = 0; all_calendar_events_counter < Object.keys(all_calendar_events).length; all_calendar_events_counter++) {
                        if (all_calendar_events[Object.keys(all_calendar_events)[all_calendar_events_counter]].background === '1') {
                            hidden_events.push(Object.keys(all_calendar_events)[all_calendar_events_counter]);
                        }
                    }

                    hidden_events = unique(hidden_events);

                    for (var hidden_events_counter = 0; hidden_events_counter < hidden_events.length; hidden_events_counter++) {
                        for (element_counter = 0; element_counter < el.children.length; element_counter++) {
                            checkVal = el.children[element_counter].id.split("_");
                            checkVal = checkVal[checkVal.length - 1];
                            if (checkVal == hidden_events[hidden_events_counter]) {
                                el.removeChild(el.children[element_counter]);
                                element_counter--;
                            }
                        }
                    }
                }

                for (element_counter = 0; element_counter < el.children.length; element_counter++) {
                    var inside_el = el.children[element_counter];
                    var ev_height = parseInt(inside_el.getAttribute('event_length'));
                    if ((inside_el.id !== 'cur_time_line') && (inside_el.id !== 'cur_time_line_arrow')) {
                        cache_array[event_condition][z]['row' + i].push({
                            id: inside_el.id,
                            width: '0',
                            clone: false,
                            order: element_counter + 1,
                            checked: false
                        });
                        if (ev_height > 1) {
                            if ((i + ev_height) <= rows) {
                                for (var k = 1; k < ev_height; k++) {
                                    cache_array[event_condition][z]['row' + (i + k)].push({
                                        id: inside_el.id,
                                        width: '0',
                                        clone: true,
                                        order: null,
                                        checked: false
                                    });
                                }
                            } else {
                                $('#' + inside_el.id).attr('event_length', rows - i);
                            }
                        }
                    }
                }
            }
        }
    }
    // сортируем массив по колизиям
    for (z = 0; z < number_of_groups; z++) {
        var cache = Object.keys(cache_array[event_condition][z]);
        var cache_row = [];
        for (i = 1; i < cache.length; i++) {
            var prev_row, this_row;
            prev_row = JSON.parse(JSON.stringify(cache_array[event_condition][z][cache[i-1]]));
            this_row = JSON.parse(JSON.stringify(cache_array[event_condition][z][cache[i]]));
            cache_row = JSON.parse(JSON.stringify(cache_array[event_condition][z][cache[i]]));

            if ((prev_row.length > 0) && (this_row.length > 0)) {
                for (j = 0; j < this_row.length; j++) {
                    for (k = 0; k < prev_row.length; k++) {
                        if(this_row[j].id === prev_row[k].id) {
                            prev_row[k].checked = true;
                            prev_row[k].clone = true;
                            cache_row[j].checked = true;
                        }
                    }
                }

                var s = cache_row.length;
                while (s--) {
                    if (cache_row[s].checked) {
                        cache_row.splice(s, 1);
                    }
                }
                if (cache_row.length > 0) {
                    for (j = 0; j < prev_row.length; j++) {
                        if (!prev_row[j].checked && (cache_row.length !== 0)) {
                            prev_row[j] = cache_row[0];
                            prev_row[j].checked = true;
                            cache_row.shift();
                        }
                    }

                    var l = prev_row.length;
                    while (l--) {
                        if (!prev_row[l].checked) {
                            prev_row.splice(l, 1);
                        }
                    }

                    while (cache_row.length > 0) {
                        cache_row[0].checked = true;
                        prev_row.push(cache_row[0]);
                        cache_row.shift();
                    }

                    for (j = 0; j < prev_row.length; j++) {
                        if (prev_row[j].checked) {
                            prev_row[j].checked = false;
                            prev_row[j].order = j + 1;
                        }
                    }
                }

                cache_array[event_condition][z]['row' + i] = prev_row;
            }
        }
    }

    var row_width = $('#big_calendar table tbody')[0].children[1].children[1];
    row_width = $(row_width).width();
    for (z = 0; z < number_of_groups; z++) {
        cache = Object.keys(cache_array[event_condition][z]);
        var max_elements_in_row = 0;
        for (i=0; i< cache.length; i++) {
            if (cache_array[event_condition][z][cache[i]].length > max_elements_in_row) {
                max_elements_in_row = cache_array[event_condition][z][cache[i]].length;
            }
        }
        var elements_width = row_width/max_elements_in_row;
        if (!background){
            elements_width = elements_width - 2;
        } else {
            elements_width = elements_width - 1.5;
        }

        for (i = 0; i < cache.length; i++) {
            var counter = 0;
            for (j = 0; j < cache_array[event_condition][z][cache[i]].length; j++) {
              // изменения по заданию 90180 отменено, так как появился баг. задание 90774
              // let temp = elements_width*(cache_array[event_condition][z][cache[i]][j].order - 1) + counter;
              //   cache_array[event_condition][z][cache[i]][j].width = temp == 0 ? '99.6%' : `calc(98.6% - ${elements_width}px)`;
              cache_array[event_condition][z][cache[i]][j].width = elements_width + 'px';
              cache_array[event_condition][z][cache[i]][j].margin = elements_width*(cache_array[event_condition][z][cache[i]][j].order - 1) + counter+ 'px';
              counter+= 2;
            }

        }
    }

    var cache_array_keys = Object.keys(cache_array);
    for (i = 0; i < cache_array_keys.length; i++) {
        for (j = 0; j < cache_array[cache_array_keys[i]].length; j++) { // это количество групп
            var group_column = cache_array[cache_array_keys[i]][j]; // один из столбцов группы
            keys = Object.keys(group_column); // массив ключей(рядов) столбца
            for (z = 0; z < keys.length; z++) { //топаем по рядам
                var finall_row = group_column[keys[z]]; //нашли наконец ряд
                for (var k = 0; k < finall_row.length; k++) {
                    var element = document.getElementById(finall_row[k].id);
                    if (!finall_row[k].clone && element) {
                        if (element.getAttribute('event_length') == '1') {
                            element.setAttribute('c_default-width', finall_row[k].width);
                        } else {
                            element.setAttribute('data-default-width', finall_row[k].width);
                        }
                        element.style.width = finall_row[k].width;
                        element.style.minWidth = finall_row[k].width;
                        element.style.marginLeft = finall_row[k].margin;
                    }
                }
            }
        }
    }
    return min_z_index;
}

// вытаскиваем уникальные ключи в одномерном массиве
function unique(arr) {
    var obj = {};
    for (var i = 0; i < arr.length; i++) {
        var str = arr[i];
        obj[str] = true;
    }
    return Object.keys(obj);
}
// Показать быстрый поиск события
function toogleOptions(event_id) {
    if (document.getElementById("event_opitons" + event_id).style.display == "none") { // Показываем быстрый поиск
        $("#event_opitons" + event_id).animate({height: 'show', opacity: 'show'}, 200);
        document.getElementById("toogle_event_option" + event_id).innerHTML = "▼";
        if (document.getElementById("event_fast_search_submit").style.display == "none") // Показываем кнопку "применить", если она не выведена
            $("#event_fast_search_submit").fadeIn(400);
    }
    else { // Скрываем быстрый поиск
        $("#event_opitons" + event_id).animate({height: 'hide', opacity: 'hide'}, 200, function () {
            document.getElementById("event_opitons" + event_id).style.display = "none";
            if ($("[id^=event_opitons]:visible").length == 0) // Скрываем кнопку "применить", если больше нет отображаемых опций
                $("#event_fast_search_submit").fadeOut(200);
        });
        document.getElementById("toogle_event_option" + event_id).innerHTML = "▶";
    }
}

// Быстрый поиск
function fastSearchSubmit() {
    // Строим форму по полям быстрого поиска
    $("#calendar_event_list [id^=fast_edit_span_]").each(function () {
        var fieldId = $(this).attr("field_id");
        var eventId = str_replace("fast_edit_span_" + fieldId + "_e", "", this.id);
        if ($(this).attr("f_value") != undefined) var value = $(this).attr("f_value");
        else var value = $(this).val();
        document.getElementById("fast_search_submit_form").innerHTML += "<input type='hidden' name='event_fast_search_input[" + eventId + "][" + fieldId + "]' value='" + value + "' />";
    });
    // Отправляем форму
    document.getElementById("fast_search_submit_form").submit();
}
/**
 * @function createPopover
 * @description Инициализирует и возвращает функцию `showPopover` для создания и управления поповерами.
 * @returns {Function} - Функция `showPopover`.
 */
const createPopover = () => {
  const [getId, setId] = useState(null);
  /**
 * @function createWrapper
 * @description Создает обертку с определенными стилями и поведением для обработки прокрутки.
 * @param {string} selector - CSS-селектор для элемента-обертки.
 * @returns {Object} - Объект, содержащий элемент-обертку и методы для ее отображения/скрытия.
 */
  const createWrapper = (selector) => {
    const $wrapper = $(selector);
    $wrapper.css('height', $(document).height() + 'px');
    $wrapper.css('top', '-' + $(document).scrollTop() + 'px' )
    $(document).on('scroll', () => {
      $wrapper.css('top', '-' + $(document).scrollTop() + 'px' )
    });
    const showWrapper = () => $wrapper.css('display', '');
    const hideWrapper = () => $wrapper.css('display', 'none');
    return { $wrapper, showWrapper, hideWrapper };
  };
  /**
 * @function createLoader
 * @description Создает элемент лоадер с заданными стилями и методами для его отображения/скрытия.
 * @param {Object} params - Параметры для загрузчика.
 * @param {string} params.url - URL фона для загрузчика.
 * @param {number} params.size - Размер загрузчика в пикселях.
 * @returns {Object} - Объект, содержащий элемент загрузчика и методы для его отображения/скрытия.
 */
  const createLoader = ({ url, size }) => {
    const loader = document.createElement('article');
    loader.id = 'event_view_add_loader';
    loader.style.background = `${url} no-repeat center`;
    loader.style.width = `${size}px`;
    loader.style.height = `${size}px`;
    loader.style.margin = '0 auto';
    const showLoader = () => loader.style.display = ''
    const hideLoader = () => loader.style.display = 'none';
    return { loader, showLoader, hideLoader };
  }
  /**
 * @function createOverlay
 * @description Создает элемент оверлея с обработчиком клика.
 * @param {Object} params - Параметры для оверлея.
 * @param {Function} params.onClick - Функция-обработчик, выполняемая при клике.
 * @returns {HTMLElement} - Элемент оверлея.
 */
  const createOverlay = ({ onClick }) => {
    const overlay = document.createElement('article');
    overlay.id = 'one_event__overlay';
    overlay.classList.add('one_event__overlay'); 
    const handleClick = (...args) => { 
      onClick(...args); 
      removeOverlay() 
    }
    const removeOverlay = () => {
      overlay.removeEventListener('click', handleClick);
      overlay.remove();
    }
    overlay.addEventListener('click', handleClick);
    return overlay;
  }
  /**
   * @function selectPopover
   * @description Выбирает элементы поповера, связанные с текущим ID события.
   * @returns {Object} - Объект, содержащий элементы поповера и обертки.
   */
  const selectPopover = () => {
    const eventId = getId();
    if(!eventId) return console.warn('no event id');

    const popover = document.getElementById("record_card_add" + eventId)
    const topArrow = document.getElementById("event_top_arrow" + eventId)
    const bottomArrow = document.getElementById("event_bottom_arrow" + eventId)
    const contentWrapper = document.getElementById("event_view_edit_add" + eventId);
    const popoverCloseButton = popover.querySelector(".cal_event_header img");
    
    const hideAllPopoversBlocks = () => $('.one_event_block').css('opacity', 0);
    const showPopoverBlock = () => {
      $(popover).animate({opacity: 1}, 300);
    };
    const hidePopoverBlock = () => 
      $(popover).animate({opacity: 0}, 200, () => {
        $(contentWrapper).html("");
        $(popover).css('marginTop', '');
      });

    return { 
      popover, 
      topArrow, 
      bottomArrow, 
      contentWrapper,
      hidePopoverBlock,
      showPopoverBlock,
      hideAllPopoversBlocks,
      popoverCloseButton,
    };
  };

const movePopoverToTarget = (target) => {
  const { popover, topArrow, bottomArrow } = selectPopover();

  const targetRect = target.getBoundingClientRect();
    
  const targetTopDiffWrapperTop = targetRect.top + $(document).scrollTop();
  const viewportHeight = window.innerHeight;
  const middleOfScreen = viewportHeight / 2;
  const viewportEdge = window.innerWidth;

  const isBottom = targetRect.top > middleOfScreen
  const isRight = targetRect.left + popover.offsetWidth/2 > viewportEdge
  const isLeft = targetRect.left - popover.offsetWidth/2 < 0

  let newPopoverLeft;
  if(isRight) {
    newPopoverLeft = viewportEdge - popover.offsetWidth - 20;
  } else if(isLeft) {
    newPopoverLeft = 20;
    popover.style.left = `${newPopoverLeft}px`;
  } else {
    newPopoverLeft = targetRect.left + targetRect.width/2 - popover.offsetWidth/2;
    popover.style.left = `${newPopoverLeft}px`;
  }
  popover.style.left = newPopoverLeft ? `${newPopoverLeft}px` : '';

  topArrow.children.forEach((arrow) => {
    const popoverMiddleLeft = newPopoverLeft + popover.offsetWidth/2;
    const targetMiddleLeft = targetRect.left + targetRect.width/2;
    arrow.style.left = targetMiddleLeft - popoverMiddleLeft + 'px'
  });
  bottomArrow.children.forEach((arrow) => {
    const popoverMiddleLeft = newPopoverLeft + popover.offsetWidth/2;
    const targetMiddleLeft = targetRect.left + targetRect.width/2;
    arrow.style.left = targetMiddleLeft - popoverMiddleLeft + 'px'
  });
  
  if (isBottom) {
    popover.style.top = `${targetTopDiffWrapperTop - popover.offsetHeight}px`;
    topArrow.style.opacity = 0;
    bottomArrow.style.opacity = 1;
  } else {
    popover.style.top = `${targetTopDiffWrapperTop + targetRect.height}px`;
    topArrow.style.opacity = 1;
    bottomArrow.style.opacity = 0;
  }

  return { isBottom, isRight, isLeft };
}

/**
 * @function createPopoverBlock
 * @description Располагает поповер относительно целевого элемента.
 * @param {Object} params - Параметры для позиционирования поповера.
 * @param {number} [params.loaderSize=70] - Размер лоадер в пикселях.
 * @param {HTMLElement} params.target - Целевой элемент для позиционирования поповера.
 * @returns {Object} - Объект, содержащий методы для изменения размеров и удаления поповера.
 */
  const createPopoverBlock = ({ loaderSize = 70, onClose }) => {
    const [getIsBottom, setIsBottom] = useState(false);
    const { 
      popover, 
      contentWrapper, 
      hidePopoverBlock, 
      showPopoverBlock, 
      hideAllPopoversBlocks,
      popoverCloseButton
    } = selectPopover();
    const { loader: contentLoader, showLoader, hideLoader } = createLoader({
      url: 'url("images/process.gif")',
      size: loaderSize
    });

    const moveAndShowPopover = (target) => {
      hideAllPopoversBlocks();
      showLoader();
      contentWrapper.appendChild(contentLoader);
      $(contentWrapper).css( 'height', loaderSize + 'px' );
      const { isBottom } = movePopoverToTarget(target);
      setIsBottom(isBottom);
      showPopoverBlock();
      popoverCloseButton.addEventListener('click', onClose)
      makePopoverDraggable(); // Добавляем возможность перетаскивания
    }

    const resizePopover = () => {
      const isBottom = getIsBottom();
      const eventId = getId();
      if(!eventId) return console.warn('no event id');
      hideLoader();
      const content = document.getElementById("event_view_hidden" + eventId);
  
      const contentRect = content.getBoundingClientRect();
      const popoverRect = popover.getBoundingClientRect();
      const contentDifference = contentRect.height + 20 - loaderSize;
      
      if (isBottom) {
        const nextTop = popoverRect.top - contentDifference  ;
        const oversize = Math.min(nextTop, 0);
        const topResize = contentDifference - oversize;

        $(contentWrapper).css( 'height', loaderSize + 'px' );
        $(contentWrapper).animate({ height: contentRect.height + 20 }, 300, function() {
          $(this).css('height', ''); // Убирает инлайн-значение height
          $(this).css('max-height', contentRect.height + 20 + 'px'); 
          $(this).css('overflow', 'hidden auto'); 
        });
        
        
        $(popover).animate({ marginTop: - topResize }, 300);
      } else {
        const nextBottom = window.innerHeight - popoverRect.bottom - contentDifference;
        const oversize = Math.min(nextBottom, 0);
        $(contentWrapper).css( 'height', loaderSize + 'px' );
        $(contentWrapper).animate({ height: contentRect.height + 20 }, 300, function() {
          $(this).css('height', ''); // Убирает инлайн-значение height
          $(this).css('max-height', contentRect.height + 20 + 'px'); // Убирает инлайн-значение height
          $(this).css('overflow', 'hidden auto'); 
        });
        
        $(popover).animate({ marginTop: oversize }, 300);
      }
    }
    const removePopover = () => {
      hidePopoverBlock();
      // непонятная легаси логика
      $(".c_event_layout").css("background", "");
      if (addedCell)
          if (document.getElementById(addedCell))
              document.getElementById(addedCell).style.background = "";
      if (nextCell)
          if (document.getElementById(nextCell))
              document.getElementById(nextCell).style.background = "";
      addedCell = false;
      nextCell = false;
      recordView = false;
      if (pageReload) window.location.reload();
    };

    // Функция для перетаскивания popover
    const makePopoverDraggable = () => {
      let isDragging = false;
      let currentX;
      let currentY;
      let initialX;
      let initialY;
      
      let popoverHeader = popover.querySelector('.cal_event_header')
      popoverHeader.onmousedown = (e) => {
          // Проверяем, что клик не по кнопке закрытия или другим интерактивным элементам
          if (e.target === popoverCloseButton || e.target.closest('.no-drag')) return;

          isDragging = true;
          initialX = e.clientX - currentX;
          initialY = e.clientY - currentY;

          popoverHeader.style.cursor = 'grabbing';
          e.preventDefault(); // Предотвращаем выделение текста
      };

      document.onmousemove = (e) => {
          if (!isDragging) return;

          e.preventDefault();
          currentX = e.clientX - initialX;
          currentY = e.clientY - initialY;

          // Устанавливаем позицию через top и left, игнорируя marginTop
          popover.style.position = 'absolute';
          popover.style.left = currentX + 'px';
          popover.style.top = currentY + 'px';
          popover.style.marginTop = '0'; // Сбрасываем marginTop, чтобы не конфликтовать с resizePopover
      };

      document.onmouseup = () => {
          if (isDragging) {
              isDragging = false;
              popoverHeader.style.cursor = 'grab';
          }
      };

      // Инициализируем текущие координаты из начальной позиции
      const rect = popover.getBoundingClientRect();
      currentX = rect.left;
      currentY = rect.top;

      // Добавляем курсор для удобства
      popoverHeader.style.cursor = 'grab';
    };

    return { moveAndShowPopover, resizePopover, removePopover } ;
  }
  /**
   * @function showPopover
   * @description Отображает поповер для указанного ID события, добавляет оверлей и обертку.
   * @param {string} eventId - ID события, связанного с поповером.
   * @param {Event} event - Объект события, вызвавшего отображение поповера.
   * @returns {Object} - Объект, содержащий методы для изменения размеров и удаления поповера.
   */
  const showPopover = (eventId, event, onClose) => {
    setId(eventId);
    const { $wrapper, showWrapper, hideWrapper } = createWrapper('.one_event__wrapper');
    const overlay = createOverlay({ onClick: () => { 
      if(removePopover) removePopover();
      if(hideWrapper) hideWrapper();
      if(onClose) onClose();
    } });
    $wrapper.append(overlay);
    
    showWrapper();
    const { moveAndShowPopover, resizePopover, removePopover } = createPopoverBlock({ 
      loaderSize: 70,
      onClose: () => { 
        if(removePopover) removePopover();
        if(hideWrapper) hideWrapper();
        if(onClose) onClose();
      }
    });
    moveAndShowPopover(event.target);

    return resizePopover;
  }

  return showPopover;
}
const showPopover = createPopover();

/**
 * Совершенно безумная логика позиционирования поповера
 * @todo Переписать реализацию
 */
function offsetEventBlock(eventId) {
  const popoverElement = document.getElementById("record_card_add" + eventId);
  const hiddenContentElement = document.getElementById("event_view_hidden" + eventId);
  const contentElement = document.getElementById("event_view_edit_add" + eventId);
  const topArrow = document.getElementById("event_top_arrow" + eventId);
  const bottomArrow = document.getElementById("event_bottom_arrow" + eventId);

  const contentRect = contentElement.getBoundingClientRect();
  const hiddenContentRect = hiddenContentElement.getBoundingClientRect();

  const contentDifference = hiddenContentRect.height - loader.offsetHeight 

  const popoverRect = popoverElement.getBoundingClientRect();
  
  const isOpenTop = bottomArrow.style.opacity !== '0';
  const isOpenBottom = topArrow.style.opacity !== '0';
  if(isOpenTop) {
    const futureTop = popoverRect.top - contentDifference;
    const diffViewport = Math.min(futureTop, 0);
    
    const marginTop = contentDifference + diffViewport;

    $(contentRect).css( 'height', loader.offsetHeight + 'px' );
    $(contentRect).animate({ height: hiddenContentRect.height }, 300);
    $(popoverElement).animate({ marginTop: - marginTop }, 300);
  }
  
  if(isOpenBottom) {
    const futureBottom = window.innerHeight - popoverRect.bottom - contentDifference 
    const diffViewport = Math.min(futureBottom, 0);

    $(contentRect).css( 'height', loader.offsetHeight + 'px' );
    $(contentRect).animate({ height: hiddenContentRect.height }, 300);
    $(popoverElement).animate({ marginTop: diffViewport }, 300);
  }
}


// Показать в карточке события форму для добавления записей в связанные поля
function add_link_block_show_calendar(field_id, event_id) {
    var curMargin = parseInt(document.getElementById("record_card_add" + event_id).style.marginTop);
    var fullHeight = 0;
    if (document.getElementById("add_link_block" + field_id).style.display == "none") {
        l_field_id = all_fields[field_id]['s_field_id'];
        var v_o = document.getElementById("fast_edit_span_" + l_field_id + "_" + field_id + "_0");

        var overlay = $('<div>');
        overlay.addClass('overlay');
        $('body').append(overlay);
        $(`#record_card_add${event_id}`).append(overlay);
        $(document.getElementById("add_link_block" + field_id)).slideDown(200);
        var overlay_obj = new Overlay();
        overlay_obj.close(document.getElementById("add_link_block" + field_id), [], 'slide', 200);

        var firstValue = $("[id^=fast_edit_span_" + field_id + "]").val();
        if (!v_o)
            v_o = document.getElementById("fast_edit_span_" + l_field_id + "_new_0");
        if (v_o) {
            if (typeof(v_o.value) === 'undefined')
                v_o.innerHTML = '';
            else
                v_o.value = '';
        }
        $("#add_link_block" + field_id).show();
        fullHeight += parseInt($("#add_link_block" + field_id).height());
        var newMarginTop = curMargin - fullHeight;
        document.getElementById("record_card_add" + event_id).style.marginTop = newMarginTop + "px";

    }
    else {
        fullHeight += parseInt($("#add_link_block" + field_id).height());
        var newMarginTop = curMargin + fullHeight;
        document.getElementById("record_card_add" + event_id).style.marginTop = curMargin + "px";
        $("#add_link_block" + field_id).hide();
    }
}
function type_4_mult_val_check_all(el){
    el.parentElement.parentElement.firstElementChild.value="";
    let input = el.parentElement.parentElement.firstElementChild.value,
        field_type=el.parentElement.parentElement.firstElementChild.attributes['field_type'].value;
    if(el.checked==true){
        for(let i=0;i<$(el).parent().parent().find('.type_4_mult_val').length;i++){
            $(el).parent().parent().find('.type_4_mult_val')[i].firstElementChild.checked=true;
            if(i==0){
                input=$(el).parent().parent().find('.type_4_mult_val')[i].firstElementChild.value;
            }else{
                if(field_type==4){
                    input+=`\r\n${$(el).parent().parent().find('.type_4_mult_val')[i].firstElementChild.value}`;
                }else{
                    input+=`-${$(el).parent().parent().find('.type_4_mult_val')[i].firstElementChild.value}`;
                }
            }
        }
    }else{
        for(let i=0;i<$(el).parent().parent().find('.type_4_mult_val').length;i++){
            $(el).parent().parent().find('.type_4_mult_val')[i].firstElementChild.checked=false;
        }
        input="";
    }
    el.parentElement.parentElement.firstElementChild.value=input;
    $(element).parent().parent().parent().children().first().trigger('change');
}
function save_type_4_mult_val(element){
    let input = element.parentElement.parentElement.parentElement.firstElementChild.value,
        field_type=element.parentElement.parentElement.parentElement.firstElementChild.attributes['field_type'].value,
        value = [];
    if(element.checked==false){
        if(field_type==4){
            if (input.indexOf(',')>0) {
                input = input.split(',').join('\n');
            }
            input=input.split("\n");
            for(let i=0;i<input.length;i++){
                input[i] = input[i].replace('\r','')
                if(input[i]==element.value){
                    input.splice(i,1);
                }
            }
            input=input.join('\r\n');
            $(element).parent().parent().parent().children().first().val(input);
        }
        else{
            input=input.split("-")
            input.forEach(function(el) {
                if(el != '') {
                    value.push(el);
                }
            });
            for(let i=0;i<value.length;i++){
                value[i] = value[i].replace('\r','')
                if(value[i]==element.value){
                    value.splice(i,1);
                }
            }
            value = value.join('-');
            $(element).parent().parent().parent().children().first().val(value);
        }
        $(element).parent().parent().parent().children().first().trigger('change');
    }
    else{
        if(input==""){
            input+=element.value;
        }
        else{
            if(field_type==4){
                if (input.indexOf(',')>0) {
                    input = input.split(',').join('\r\n');
                }
                input+=`\r\n${element.value}`;
            }else{
                input+=`-${element.value}`;
            }
        }
        $(element).parent().parent().parent().children().first().val(input);
        $(element).parent().parent().parent().children().first().trigger('change');
    }
}
var addFieldsList = new Object;
// Сохранить новое значение в поле связи
function add_link_block_save_calendar(field_id, event_id) {
    var insertObj = new Object,
        type_4_mult_val = '';
    for (addFieldId in addFieldsList) { // Формируем значения
        one_field = addFieldsList[addFieldId];
        type_field = one_field.type_field;
        v_o = document.getElementById("fast_edit_span_" + addFieldId + "_" + field_id + "_0");
        if (!v_o) v_o = document.getElementById("fast_edit_span_" + addFieldId + "_new_0");
        if (!v_o) continue;
        if(type_field == 4&&one_field.mult_value == "1"){
            insertObj[addFieldId] = v_o.value;
            $("#sub_cell_"+ field_id +"_new .autocomplete__input").val(v_o.value);
        }
        if ((type_field == 1) ||
            (type_field == 2) ||
            (type_field == 12) ||
            (type_field == 3) ||
            (type_field == 10) ||
            (type_field == 7) ||
            (type_field == 14)) {
            if (typeof(v_o.value) === 'undefined')
                insertObj[addFieldId] = v_o.innerHTML;
            else {
                var sourceValue = v_o.value;

                if ((type_field == 4 || type_field == 7 || type_field == 14) && one_field.mult_value != "1" && sourceValue == "") {
                    var reserveValue = $(v_o).next("span").html();
                    if (reserveValue != "") {
                        $(v_o).find("option").each(function () {
                            if ($(this).html() == reserveValue) {
                                $(this).attr("selected", true);
                                sourceValue = $(this).attr("value")
                                $(v_o).val(sourceValue);
                            }
                        });
                    }
                } else if (type_field == 7) {
                    if (one_field.mult_value == "1"){
                        sourceValue = v_o.value.split('-');
                    }
                }

                insertObj[addFieldId] = sourceValue;
            }
        }
        else if (type_field == 5) {
            insertObj[addFieldId] = v_o.getAttribute('f_value') || v_o.getAttribute('ac_link_val');
        }

        if (all_fields[field_id]['s_field_id'] == addFieldId) {
            var sFieldVal = insertObj[addFieldId];
            if (type_field == 14)
                sFieldVal = $(v_o).next("span").html();
            if (type_field == 5) {
                sFieldVal = $(v_o).find("option[value='"+ insertObj[addFieldId] +"']").text();
            }
        }
    }

    for (addFieldId in insertObj) {
        one_field = addFieldsList[addFieldId];
        type_field = one_field.type_field;
        if ((insertObj[addFieldId] == "" ||
            (
                (type_field == 1 || type_field == 7 || type_field == 14) &&
                insertObj[addFieldId] == "0"
            ))&&one_field.main=='1'
        ) { // Не заполнены обязательные поля
            jalert(lang.Qst_required_alert + " '" + addFieldsList[addFieldId]['name_field'] + "'!");
            return;
        }
    }

    $.ajax({
        type: "POST",
        url: "calendar.php",
        data: {
            csrf: csrf,
            id: calendar.id,
            put: 'new_link_line',
            table_id: all_fields[field_id]['s_table_id'],
            data: insertObj
        },
        success: function (input) {
            var data = JSON.parse(input);
            if (!data) {
                jalert("JSON parse error!");
                return;
            }
            if (data.error) {
                jalert(data.error);
                return;
            }

            if (type_field == 7) { // Поле типа Пользователь
                // sFieldVal = sFieldVal.join('\r\n');
            }

            // sFieldVal = str_replace("&amp;", "&", sFieldVal);
            // sFieldVal = str_replace("&quot;", "\"", sFieldVal);
            // sFieldVal = str_replace("&apos;", "'", sFieldVal);
            // sFieldVal = str_replace("&lt;", "<", sFieldVal);
            // sFieldVal = str_replace("&gt;", ">", sFieldVal);

            let curValue = "";
            // Формируем текстовую строку, содержащую выбранные значения
            if ($("#add_link_block" + field_id)) {
                $("#add_link_block" + field_id).find("input[type=checkbox][id^=link_field]").each(function (i, item) {
                    if ($(item).prop('checked')) {
                        curValue = curValue + $(item).next().text() + ", ";
                    }
                });
            }

            $("[id^=fast_edit_span_" + field_id + "]").first().append('<option value="' + data.new_line_id + '">'+ sFieldVal +'</option>');
            $("[id^=fast_edit_span_" + field_id + "]").first().val(data.new_line_id);
            $("[id^=fast_edit_span_" + field_id + "]").val(data.new_line_id);

            let textValue = $(`[field_id="${field_id}"]`).find(`option[value=${data.new_line_id}]`).text()
            $(`[field_id="${field_id}"]`).siblings('.autocomplete').find('input.autocomplete__input').val(textValue);

            if(curValue!=''){
                $("[id^=fast_edit_span_" + field_id + "]").next().first().children().first().val(curValue.slice(0, -2)); // Удаляем последние 2 символа (запятая, пробел)
            }
            line_id = $("[id^=fast_edit_span_" + field_id + "]").first().attr('line_id');
            save_value(field_id, line_id , data.new_line_id); // здесь же сохраняем
            $('#go_event_link' + event_id).css('display', 'none'); // выводим бесполезную кнопку сохранить
            $('#save_event_button' + event_id).css('display', 'block');
            add_link_block_show_calendar(field_id, event_id);

            var overlay = new Overlay();

            /**
             * Открытый блок для добавления ссылки
             * @type Element
             */
            var block = $('#add_link_block' + field_id);

            if (typeof block !== 'indefined') {
                overlay.hide(block, 'slide', 200);
            }
        }
    });
}

function show_special_text_edit(el) {
    var target = $(el);
    var field_id = target.attr('field_id');
    var view = $('#fast_cell_view_' + field_id);
    var edit = $('#fast_cell_edit_' + field_id);
    let autocomplete_input = edit.find('.autocomplete__input');
    var close_btn = $('#close-edit-button' + field_id);

    if (view.length > 0 && edit.length > 0) {
        var html = (edit.hasClass('hidden-input--html')) ? view.html() : '';
        target.hide();
        view.hide();
        if(autocomplete_input.length > 0){
            let tag_start = autocomplete_input.val().indexOf('<');
            if(tag_start != -1){
                autocomplete_input.val(autocomplete_input.val().replace(/<\/?[^>]+(>|$)/g, ""));//.substr(0, tag_start));
            }
            if(view.find('.show-field-slave__item--inline').length > 0){
                autocomplete_input.val(view.text().replace(view.find('.show-field-slave__item--inline').text(), ''));
            }
        }
        edit.show();
        edit.css({width: '262px', display: 'inline-block'});
        if (edit.hasClass('hidden-input--html')) {
            autosize(edit);
            edit.val(html);
            $('#html_resize_' + field_id).css('opacity', '0');
            showHtmlEditor(field_id, 'fast_edit');
        }
        close_btn.show();
        close_btn.css('opacity', 1);
    }
}

function hoverEvent(event_id, line_id, e, date_field, str = '', cell = '') {
    let block,
        month = (str == '' || cell == '') ? false : true;
    if (month || isGrouped && calendar.week) {
        block = `#calendar_event_line_${date_field}_${line_id}_${event_id}_${str}_${cell}`;
    } else {
        block = `#calendar_event_line_${date_field}_${line_id}_${event_id}`;
    }
    $(`${block}`).hover(function(){
        $(`${block} .hoverEventBlock`).css('display','block');
        $(`${block}`).css('z-index','999');
    }, function(){
        $(`${block} .hoverEventBlock`).css('display','none');
        $(`${block}`).css('z-index','');
    });
    $.ajax({
        type: "POST",
        url: "calendar.php",
        data: {csrf: csrf, id: calendar.id, get: 'show', data: {event_id: event_id, line_id: line_id}},
        success: function (msg) {
            let resp = JSON.parse(msg),
                arr_fields = '';
            $(`${block} .hoverEventBlock .cal_event_header b`).text(resp.event_name);
            $(`${block} .hoverEventBlock .cal_event_footer a`).attr('href',`view_line2.php?table=${resp.table_id}&line=${resp.line_id}`);
            if (resp.date_field) {
                $(`${block} .hoverEventBlock .event_datefields`).text(`${resp.date_field.name} : ${resp.date_field.value}`);
            }
            if (resp.period_field) {
                $(`${block} .hoverEventBlock .event_datefields`).text(`${lang.since_from}: ${resp.date_field.value} ${lang.since_to}: ${resp.period_field.value}`);
            }
            for (let key in resp.fields) {
                arr_fields += `<div style="word-break: break-all;hyphens: auto;"><b>${resp.fields[key].name}</b> : ${resp.fields[key].value.replace(/\r\n/g,', ')}</div>`
            }
            $(`${block} .hoverEventBlock .event_fields`).html(arr_fields);
        }
    });
}
// Просмотр события
function viewOneEvent(event_id, line_id, e, str, cell) {
    // Выводим блок в позицию и отображаем
    const resizePopover = showPopover(event_id, e);

    var eventTableId = all_calendar_events[event_id]['table_id'];
    var eventNameTable = all_calendar_events[event_id]['name_table'];
    // Ссылка на строку, скрываем кнопку "сохранить"
    document.getElementById("go_event_link" + event_id).innerHTML = "<a href='view_line2.php?table=" + eventTableId + "&line=" + line_id + "&back_url=" + back_url + "'>" + lang.Jump_to_record + " &raquo;</a>";
    document.getElementById("go_event_link" + event_id).style.display = "block";
    document.getElementById("save_event_button" + event_id).style.display = "none";

    highlightEvent(true, event_id, line_id); // Подсвечиваем событие, как при наведении

    const controller = new AbortController();
    const { signal } = controller;
    
    // Отмена предыдущего запроса при необходимости
    if (window.currentRequest) {
        window.currentRequest.abort();
    }
    window.currentRequest = controller;
    
    fetch("calendar.php", {
      method: "POST",
      body: new URLSearchParams({
        csrf: csrf,
        id: calendar.id,
        get: 'event',
        'data[event_id]': event_id,
        'data[line_id]': line_id
      }).toString(),
      headers: {
        "Content-Type": "application/x-www-form-urlencoded" // Важно для имитации поведения AJAX-запроса
      },
      signal
    }).then(response => {
      if (!response.ok) {
          throw new Error(response.statusText);
      }
      return response.json();
    }).then(resp => {
      var date_fields = "";
      var nameDateField = "";
      addFieldsList = new Object;

      // Выводим ответ от сервера в блок события
      document.getElementById("event_view_edit_add" + event_id).style.backgroundImage = "none";
      if (resp.status == "error")
          eventText = "<div style='text-align: center; font-style: oblique; margin: 15px 0px -15px;'>" + resp.value + "</div>";
      else if (resp.status == "ok") {
          eventText = "<table cellpadding='0' cellspacing='0' border='0' class='textpad' style='margin: 10px 0px; position: relative;'>";
          fields = resp.value;
          var evalStr = "";
          for (orderField in fields) {
              let one_field_text = '';
              one_field = fields[orderField];
              fieldId = one_field.field_id;
              if ((one_field.type_field == 1 || (one_field.type_field == 3 && one_field.view_html != "1")) && one_field.value.allow_write == "1") fullValue = one_field.value.fast_edit_div + one_field.value.fast_edit_div_close;
              else fullValue = one_field.value.fast_edit_div + one_field.value.value + one_field.value.fast_edit_div_close;

              if (fullValue == "null") {
                fullValue = "";
              }

              if (one_field.date_field == "1") { // Дата события
                  if (one_field.type_value == 1) {
                      fullValue = fullValue.replace(" datepicker ", " datetimepicker fast_edit_datetime ");
                  }
                  date_fields = fullValue + date_fields;
                  if (nameDateField == "") nameDateField = one_field.name + ": ";
              }
              else if (one_field.period_field == "1") { // Период события
                  if (one_field.type_value == 1) {
                      fullValue = fullValue.replace(" datepicker ", " datetimepicker fast_edit_datetime ");
                  }
                  date_fields += " " + lang.since_to + ": " + fullValue
                  nameDateField = lang.since_from + ": ";
              }
              else { // Обычное поле
                  var fullAddValue;
                  eventText += "<tr><td style='vertical-align: top; max-width: 130px; overflow: hidden; padding-top: 5px !important;' class='textpad_field_name'><b>" + one_field.name + ": </b></td>";
                  eventText += "<td class='textpad_field_value' style='width: 100%;";

                  if (one_field.add_link_fields != "0")
                      eventText += " white-space: nowrap;";
                  else if (!one_field.fast_edit_div)
                      eventText += " white-space: normal;";
                  if (one_field.type_value == 1) {
                      fullValue = fullValue.replace(" datepicker ", " datetimepicker fast_edit_datetime ");
                  }
                  if (one_field.type_field == '5') {
                      let one_field_style = (one_field.view_html) ? "white-space: pre-wrap;": "",
                          one_field_display = "<a href='" + one_field.value.ac_link + "' target='__blank'" + (one_field.value.disable_link == 1? " class='link-disable-link-a'" : "") + ">" + (
                              one_field.value?.display || one_field.value?.value || ""
                          ) + "</a>";
                      one_field_text = "<span id='fast_cell_view_" + fieldId + "' field-id='" + fieldId + "'" +
                                                "style='word-break: break-word;margin-left: 6px;margin-top: 6px;" + one_field_style + "'>" + one_field_display + "</span>" +
                                          "<button class='user-data__edit-btn' id='edit-button" + fieldId + "'" +
                                                  "field_id='" + fieldId + "' type='button' onclick='show_special_text_edit(this)'></button>" +
                                          "<div class='user-data__link-block' id='fast_cell_edit_" + fieldId + "'>" +
                                              "<div class='user-data__autocomplete-wrap user-data__autocomplete-wrap--hidden'>" + fullValue + "<div hidden='hidden' class='hidden_event_id'>" + event_id + "</div></div>" +
                                          "</div>";
                  } else {
                      one_field_text = fullValue + "";
                  }
                  eventText += "' id='sub_cell_" + fieldId + "_" + line_id + "'>" + one_field_text;
                  if (one_field.add_link_fields != "0") {
                      eventText += "<div hidden='hidden' class='hidden_event_id'>" + event_id + "</div>";
                      eventText += "<img src=\"images/green_plus.png\" style=\"margin-top: 5px;\" class=\"add_link_img\" onclick=\"add_link_block_show_calendar(" + fieldId + ", " + event_id + ")\" onmouseover=\"this.src='images/green_plus_active.png'\" onmouseout=\"this.src='images/green_plus.png'\" />";
                      eventText += "</td></tr>";
                      eventText += "<tbody id='add_link_block" + fieldId + "' class=\"add_link_block user-data__row-wrap\" style='display: none;'><tr><td colspan='2' style='padding: 5px 0px'></td></tr>";

                      for (addFieldNum in one_field.add_link_fields) {
                          var oneAddField = one_field['add_link_fields'][addFieldNum];
                          addFieldId = oneAddField.id;
                          addFieldsList[addFieldId] = oneAddField;
                          if (oneAddField.type_field == 1 || oneAddField.type_field == 3) fullAddValue = oneAddField.fast_edit_div + oneAddField.fast_edit_div_close;
                          else fullAddValue = oneAddField.fast_edit_div + oneAddField.def_value + oneAddField.fast_edit_div_close;
                          eventText += "<tr><td style='vertical-align: top; max-width: 130px; overflow: hidden; padding-top: 5px !important;' class='textpad_field_name'><b>" + oneAddField.name_field + ": </b></td>";
                          eventText += "<td style='width: 100%;' class='textpad_field_value' id='sub_cell_" + addFieldId + "_" + fieldId + "_0'>" + fullAddValue + "</td></tr>";
                          while (fullAddValue.indexOf("<script>") >= 0) { // JS, который надо выполнить
                              firstPos = fullAddValue.indexOf("<script>");
                              secondPos = fullAddValue.indexOf("<\/script>");
                              evalStr += fullAddValue.substring(firstPos + 8, secondPos) + "\r\n";
                              fullAddValue = fullAddValue.substring(secondPos + 9);
                          }
                      }

                      eventText += "<tr><td class='add_link_block_bottom'></td>";
                      eventText += "<td class='add_link_block_bottom'><button onclick='add_link_block_save_calendar(" + fieldId + ", " + event_id + ")' class='add_link_block_save user-data__save-adding green-btn simple-btn'>" + lang.Save + "</button></td>";
                      eventText += "</tr><tr><td colspan='2' style='padding: 5px 0px'></td></tr></tbody>"
                  }
                  else
                      eventText += "</td></tr>";
              }
              while (fullValue.indexOf("<script>") >= 0) { // JS, который надо выполнить
                  firstPos = fullValue.indexOf("<script>");
                  secondPos = fullValue.indexOf("<\/script>");
                  evalStr += fullValue.substring(firstPos + 8, secondPos) + "\r\n";
                  fullValue = fullValue.substring(secondPos + 9);
              }
          }
          eventText += "</table>";
      }
      // Добавляем содержание в блок
      date_fields = "<div class='event_datefields' id='event_datefields" + event_id + "'>" + nameDateField + date_fields + "</div>";
      document.getElementById("event_view_edit_add" + event_id).innerHTML = "<div id='event_view_hidden" + event_id + "'>" + date_fields + eventText + "</div>";

      $("#event_view_hidden" + event_id + " [part=add_link_field]").attr("part", "");
      $("#event_view_hidden" + event_id + " .add_link_block [part='']").attr("part", "add_link_field");


      if (evalStr) { // Выполняем JS, выставляем обработчики
          eval(evalStr);
          $(".datepicker").datepicker({
              showOn: "button",
              dateFormat: lang.date_js_format,
              buttonImage: "images/calbtn.png",
              buttonImageOnly: true,
              buttonText: lang.Calendar,
              showAnim: (('\v' == 'v') ? "" : "show"),  // в ie не включаем анимацию, тормозит
          }).attr('placeholder', lang.date_placeholder);
          $(".datetimepicker").datetimepicker({
              showOn: "button",
              dateFormat: lang.date_js_format,
              timeFormat: "HH:mm",
              buttonImage: "images/calbtn.png",
              buttonImageOnly: true,
              buttonText: lang.Calendar,
              showAnim: (('\v' == 'v') ? "" : "show"),  // в ie не включаем анимацию, тормозит
          }).attr('placeholder', lang.datetime_placeholder);
      }

      if (resp.can_del == "1") // Если есть права на удаление - выводим ссылку
          document.getElementById("go_event_link" + event_id).innerHTML = "<a style='float: left;' href='#' onclick=\"jconfirm('" + lang.del_cal_event_confim + " &quot;" + eventNameTable + "&quot;. " + lang.Continue + "?', function() { sub_drop_line(" + eventTableId + ", " + line_id + ") }, function () { }); return false\">" + lang.Delete + "</a>" + document.getElementById("go_event_link" + event_id).innerHTML;

      resizePopover(event_id) // Сдвигаем блок, т.к. высота поменялась
      // Фиксирум поля по ширине
      var maxInputWidthSelect = 380 - parseInt($(".textpad_field_name").width());
      var maxInputWidth = 350 - parseInt($(".textpad_field_name").width());
      $(".event_view_edit .textpad div, .event_view_edit .textpad input").each(function () {
          if (parseInt($(this).width()) > maxInputWidth) $(this).width(maxInputWidth);
      });
      $(".event_view_edit .textpad select").each(function () {
          if (parseInt($(this).width()) > maxInputWidthSelect) {
              $(this).width(maxInputWidthSelect);
              $(this).parent("span").width(maxInputWidthSelect)
              $(this).next("span").width(maxInputWidthSelect - 5)
          }
          var optTextVal = "";
          $(this).find("option").each(function () {
              if ($(this).attr("selected")) optTextVal = $(this).html();
          })
          $(this).next("span").html(optTextVal);
          // substr_select_span($(this).attr("id"));
      });
      init_calendar_fast_edit();
    })

    recordView = line_id;
}
/**
 * Вычисляет новое время, добавляя или вычитая заданное количество минут.
 * 
 * @param {string} time - Время в формате "HH:mm".
 * @param {number} minuteDiff - Разница в минутах (может быть отрицательной).
 * @returns {string} Новое время в формате "HH:mm".
 * 
 * @example
 * getNextTime("14:30", 90); // "16:00"
 * getNextTime("23:45", 30); // "00:15"
 * getNextTime("00:15", -20); // "23:55"
 */
const getNextTime = (time, minuteDiff) => {
  // Разделяем строку времени на часы и минуты
  const [hour, minute] = time.split(':').map(Number);

  // Вычисляем общее количество минут
  const totalMinutes = hour * 60 + minute + minuteDiff;

  // Рассчитываем новое время
  const newHour = Math.floor((totalMinutes / 60) % 24); // Учитываем переход через 23 -> 0
  const newMinute = totalMinutes % 60;

  // Форматируем часы и минуты с ведущими нулями
  const formattedHour = String(newHour).padStart(2, '0');
  const formattedMinute = String(newMinute).padStart(2, '0');

  return `${formattedHour}:${formattedMinute}`;
};
/**
 * Находит элемент в DOM, соответствующий указанному времени и ключу группы.
 * 
 * @param {string} time - Время в формате "ЧЧ:мм" (например, "14:30").
 * @param {string} groupKey - Значение атрибута `data-group-key`, которое нужно найти.
 * @returns {HTMLElement | null} - Найденный элемент или `null`, если совпадений нет.
 * 
 * @example
 * // Предположим, в DOM существует элемент с такими атрибутами:
 * // <div data-hour="14" data-minute="30" data-group-key="group1"></div>
 * const element = getElementByTimeAndGroupKey("14:30", "group1");
 * console.log(element); // Вернет найденный элемент
 */
const getElementByTimeAndGroupKey = (time, groupKey) => {
  if (!time) {
    console.warn("Invalid input: 'time' are required");
    return null;
  }
    
  const [hour, minute] = time.split(':');
  if (!hour || !minute) {
    console.warn("Invalid time format. Expected 'HH:mm'");
    return null;
  }

  const cells = document.querySelectorAll('[data-hour]');

  const result = [ ...cells ].find(cell => {
    if(cell.getAttribute('data-hour') === hour)
    return cell.getAttribute('data-hour') === hour &&
    cell.getAttribute('data-minute') === minute &&  
    cell.getAttribute('data-group-key') === groupKey
  });
  return result;
}
/**
 * Добавляет событие в календарь, отображает форму редактирования и отправляет запрос для получения полей события.
 * 
 * @param {MouseEvent} event - Событие клика, связанное с добавлением.
 * @param {Object} options - Опции для добавления события.
 * @param {string} options.date - Время события в формате "HH:mm".
 * @param {string} options.groupKey - Ключ группы, связанный с событием.
 * @param {string} [options.groupValue] - Значение группы, связанное с событием (если доступно).
 * @param {string} [options.eventId=calendar.default_event] - Идентификатор события (по умолчанию — `calendar.default_event`).
 * 
 * @throws {Error} Если нет доступа для добавления строк по умолчанию.
 * 
 * @returns {void}
 */
const addDayEvent = (
  event,  
  {
    date: time,
    groupKey,
    groupValue,
    eventId = calendar.default_event,
  } 
) => {
  if (all_calendar_events[eventId]["add_acc"] == "0")
    throw new Error("Нет доступа на добавление строк по умолчанию");

  document.getElementById("go_event_link" + eventId).style.display = "none";
  document.getElementById("save_event_button" + eventId).style.display = "none";
  /**@fixme Необходимо отферакторить */
  // Нужны для работы saveEvent
  saveEventId = eventId;
  eventFields = new Object;

  let [ hour, minute ] = time.split(":");
  const nextTime = getNextTime(time, 30);
  const currentCell = event.target;
  /**@fixme Необходимо отферакторить */
  // Нужна для работы saveEvent
  addedCell = currentCell;
  const nextCell = getElementByTimeAndGroupKey(nextTime, groupKey);
  
  const resizePopover = showPopover(eventId, event, onClose = () => {
    if(currentCell) currentCell.style.background = "none";
    if(nextCell) nextCell.style.background = "none";
  }); // Позиционируем блок и выводим
  
  const isCond = calendar.group_field == all_calendar_events[eventId]['group_field'] && all_calendar_events[eventId]['group_field'] != "0";
  
  const defValues = isCond && (js_group_values[groupKey] || groupValue)
    ? {
      field_id: calendar.group_field, 
      value: js_group_values[groupKey] || groupValue
    } 
    : {};
  

  if(currentCell) currentCell.style.background = "#efefef";
  if(nextCell) nextCell.style.background = "#efefef";
  const date = `${calendar.day}.${calendar.month}.${calendar.year} ${hour}:${minute}:00`;
  
  /**@fixme Необходимо отферакторить */
  // Получаем список полей
  $.ajax({
    type: "POST",
    url: "calendar.php",
    data: {
      csrf: csrf,
      id: calendar.id,
      get: 'add_fields',
      data: {
        event_id: eventId, 
        date, 
        def_field: defValues
      }
    },
    timeout: 12000,
    error: function(xhr, textStatus) {
        document.getElementById("event_view_edit_add" + eventId).style.backgroundImage = "none";
        document.getElementById("event_view_edit_add" + eventId).innerHTML = textStatus;
    },
    success: function (msg) {
        resp = JSON.parse(msg);
        if (!resp) {
            jalert("JSON parse error!");
            return;
        }

        var date_fields = "";
        var nameDateField = "";
        addFieldsList = new Object;

        document.getElementById("event_view_edit_add" + eventId).style.backgroundImage = "none";
        // Строим содержание
        if (resp.status == "error")
            eventText = resp.value;
        else if (resp.status == "ok") {
            document.getElementById("go_event_link" + eventId).style.display = "none";
            document.getElementById("save_event_button" + eventId).style.display = "block";
            eventText = "<table cellpadding='0' cellspacing='0' border='0' class='textpad' style='margin: 10px 0px; position: relative;'>";
            fields = resp.value;
            var evalStr = "";
            for (orderField in fields) {
                one_field = fields[orderField];
                fieldId = one_field.id;
                eventFields[fieldId] = new Object;
                eventFields[fieldId] = one_field;
                if (one_field.type_field == 1 || one_field.type_field == 3) fullValue = one_field.fast_edit_div + one_field.fast_edit_div_close;
                else fullValue = one_field.fast_edit_div + one_field.def_value + one_field.fast_edit_div_close;
                if (one_field.date_field == "1") { // Поле даты события
                    if (one_field.type_value == 1) {
                        fullValue = fullValue.replace(" datepicker ", " datetimepicker fast_edit_datetime ");
                    }
                    date_fields = fullValue + date_fields;
                    if (nameDateField == "") nameDateField = one_field.name_field + ": ";
                }
                else if (one_field.period_field == "1") { // Поле периода события
                    if (one_field.type_value == 1) {
                        fullValue = fullValue.replace(" datepicker ", " datetimepicker fast_edit_datetime ");
                    }
                    date_fields += " " + lang.since_to + ": " + fullValue
                    nameDateField = lang.since_from + ": ";
                }
                else { // Обычное поле
                    eventText += "<tr><td style='vertical-align: top; max-width: 130px; overflow: hidden; padding-top: 5px !important;' class='textpad_field_name'>";
                    if (eventFields[fieldId]['main'] == "1") eventText += "<div style='display: flex'><span style='color: red'>* </span>";
                    eventText += "<b>" + one_field.name_field + ": </b></div></td>";
                    if (one_field.type_value == 1) {
                        fullValue = fullValue.replace(" datepicker ", " datetimepicker fast_edit_datetime ");
                    }
                    eventText += "<td style='width: 100%;' class='textpad_field_value' id='sub_cell_" + fieldId + "_new'>" + fullValue;
                    if (one_field.add_link_fields != "0") {
                        eventText += "<div hidden='hidden' class='hidden_event_id'>" + eventId + "</div>";
                        eventText += "<img src=\"images/green_plus.png\" style=\"margin-top: 5px;\" class=\"add_link_img\" onclick=\"add_link_block_show_calendar(" + fieldId + ", " + eventId + ")\" onmouseover=\"this.src='images/green_plus_active.png'\" onmouseout=\"this.src='images/green_plus.png'\" />";
                        eventText += "</td></tr>";
                        eventText += "<tbody id='add_link_block" + fieldId + "' class=\"add_link_block user-data__row-wrap\" style='display: none;'><tr><td colspan='2' style='padding: 5px 0px'></td></tr>";

                        for (addFieldNum in one_field.add_link_fields) {
                            var oneAddField = one_field['add_link_fields'][addFieldNum];
                            addFieldId = oneAddField.id
                            addFieldsList[addFieldId] = oneAddField;
                            if (oneAddField.type_field == 1 || oneAddField.type_field == 3) { fullAddValue = oneAddField.fast_edit_div + oneAddField.fast_edit_div_close; }
                            else {
                                fullAddValue = oneAddField.fast_edit_div + oneAddField.def_value + oneAddField.fast_edit_div_close
                            }
                            eventText += "<tr><td style='vertical-align: top; max-width: 130px; overflow: hidden; padding-top: 5px !important;' class='textpad_field_name'><b>" + oneAddField.name_field + ": </b></td>";
                            eventText += "<td style='width: 100%;' class='textpad_field_value' id='sub_cell_" + addFieldId + "_" + fieldId + "_0'>" + fullAddValue + "</td></tr>";
                            while (fullAddValue.indexOf("<script>") >= 0) { // JS, который надо выполнить
                                firstPos = fullAddValue.indexOf("<script>");
                                secondPos = fullAddValue.indexOf("<\/script>");
                                evalStr += fullAddValue.substring(firstPos + 8, secondPos) + "\r\n";
                                fullAddValue = fullAddValue.substring(secondPos + 9);
                            }
                        }

                        eventText += "<tr><td class='add_link_block_bottom'></td>";
                        eventText += "<td class='add_link_block_bottom'><button onclick='add_link_block_save_calendar(" + fieldId + ", " + eventId + ")' class='add_link_block_save user-data__save-adding green-btn simple-btn'>" + lang.Save + "</button></td>";
                        eventText += "</tr><tr><td colspan='2' style='padding: 5px 0px'></td></tr></tbody>"
                    }
                    else
                        eventText += "</td></tr>";
                }

                while (fullValue.indexOf("<script>") >= 0) { // JS, который надо выполнить
                    firstPos = fullValue.indexOf("<script>");
                    secondPos = fullValue.indexOf("<\/script>");
                    evalStr += fullValue.substring(firstPos + 8, secondPos) + "\r\n";
                    fullValue = fullValue.substring(secondPos + 9);
                }
            }
            eventText += "</table>";
        }

        // Вставляем содержание
        date_fields = "<div class='event_datefields' id='event_datefields" + eventId + "'>" + nameDateField + date_fields + "</div>";
        document.getElementById("event_view_edit_add" + eventId).innerHTML = "<div id='event_view_hidden" + eventId + "'>" + date_fields + eventText + "</div>";

        if (evalStr) { // Выполняем JS, ставим обработчики на поля
            eval(evalStr);
            $(".datepicker").datepicker({
                showOn: "button",
                dateFormat: lang.date_js_format,
                buttonImage: "images/calbtn.png",
                buttonImageOnly: true,
                buttonText: lang.Calendar,
                showAnim: (('\v' == 'v') ? "" : "show"),  // в ie не включаем анимацию, тормозит
            }).attr('placeholder', lang.date_placeholder);
            //теперь уберем баг с непредвиденным автозакрытием календаря
            var span_obj = $(".datetimepicker").parent("span.datepicker_span");
            for (var i = 0; i < span_obj.length; i++) {
                $(span_obj.get(i)).replaceWith(function (e) {
                    return span_obj.get(i).innerHTML;
                });
            }
            $(".datetimepicker").datetimepicker({
                showOn: "button",
                dateFormat: lang.date_js_format,
                timeFormat: "HH:mm",
                buttonImage: "images/calbtn.png",
                buttonImageOnly: true,
                buttonText: lang.Calendar,
                showAnim: (('\v' == 'v') ? "" : "show"),  // в ie не включаем анимацию, тормозит
            }).attr('placeholder', lang.datetime_placeholder);
        }

        resizePopover(eventId); // Сдвигаем блок под изменение высоты
        // Фиксирум поля по ширине
        var maxInputWidthSelect = 380 - parseInt($(".textpad_field_name").width());
        var maxInputWidth = 350 - parseInt($(".textpad_field_name").width());
        $(".event_view_edit .textpad div, .event_view_edit .textpad input").each(function () {
            if (parseInt($(this).width()) > maxInputWidth) $(this).width(maxInputWidth);
        });
        $(".event_view_edit .textpad select").each(function () {
            if (parseInt($(this).width()) > maxInputWidthSelect) {
                $(this).width(maxInputWidthSelect);
                $(this).parent("span").width(maxInputWidthSelect)
                $(this).next("span").width(maxInputWidthSelect - 5)
            }
        });
        $("#record_card_add" + eventId).find($('textarea')).each(function (i, item) {
            autosize(item);
        });
        init_calendar_fast_edit();
    }
  });
};

var addedCell = false;
var nextCell = false;
/**
 * Обработчик события добавления элемента, присваивается к каждой пустой строке в calendar.tpl
 * @fixme Какое то странное решение, задается размером с час, остальное нужно править руками.
 * Не вижу удобства для пользователя, по возможности сделать функционал который  позволяет 
 * выделять дипазоны вплоть до минуты
 * @param {*} date - данные о строке.
 * @param {*} e 
 * @param {*} event_id 
 * @returns 
 */
function addEvent(date, e, event_id) {
    if (event_id == undefined) // Если событие не выбрано, выбираем событие по умолчанию
        event_id = calendar.default_event;

    if (all_calendar_events[event_id]["add_acc"] == "0")
        return; // Нет доступа на добавление строк по умолчанию

    // Скрываем ссылку, отображаем кнопку "сохранить"
    document.getElementById("go_event_link" + event_id).style.display = "none";
    document.getElementById("save_event_button" + event_id).style.display = "none";

    saveEventId = event_id;
    eventFields = new Object;

    const resizePopover = showPopover(event_id, e); // Позиционируем блок и выводим

    var defValues = new Object;
    if (calendar.day) { // Дата добавления в дневном виде
        gr_date = "";
        if (date.indexOf("|") >= 0) {
            parts_val = date.split("|");
            date = parts_val[0];
            gr_date = parts_val[1];

            if (calendar.group_field == all_calendar_events[event_id]['group_field'] && all_calendar_events[event_id]['group_field'] != "0") {
                var def_gr_value = js_group_values[gr_date];
                if (def_gr_value)
                    defValues = {
                      field_id: calendar.group_field, 
                      value: def_gr_value
                    };
            }
        }

        addedCell = CSS.escape("cell_" + str_replace(":", "_", date) + "_" + gr_date);
    }
    else if (!isGrouped && calendar.week) // В недельном
        addedCell = "cell_" + str_replace(" ", "_", str_replace(":", "_", str_replace(".", "_", date)));
    else if(isGrouped) { // В недельном и месячном с группировкой
        addedCell = "cell_" + date;
        const gr_date_match = date.match(/(\d+)$/);
        let gr_date = gr_date_match ? gr_date_match[0] : null;
        if (!!gr_date && calendar.group_field == all_calendar_events[event_id]['group_field'] && all_calendar_events[event_id]['group_field'] != "0") {
          var def_gr_value = js_group_values[gr_date];
          if (def_gr_value)
              defValues = {
                field_id: calendar.group_field, 
                value: def_gr_value
              };
      }
    }
    else // В месячном
        addedCell = "cell_" + str_replace(".", "_", date);
    if (document.getElementById(addedCell)) {
        if (calendar.day || !isGrouped && calendar.week) {
            var cellOffset = $("#" + addedCell).offset();
            if (e.pageY > cellOffset.top + 15 && calendar.day) {
                if (calendar.week) {
                    var dateValues = date.split(" ");
                    var datePartsVal = dateValues[1].split(":");
                }
                else
                    var datePartsVal = date.split(":");
                var cell_height = $('.day_top').outerHeight() - 1;
                var diffCell = (Math.ceil((e.pageY - cellOffset.top) / cell_height) - 1);
                var halfTime = diffCell % 2;
                diffCell = Math.floor(diffCell / 2);
                if (datePartsVal[0] == "00") {
                    var timeDate = parseInt(diffCell);
                } else {
                    var timeDate = parseInt(ltrim(datePartsVal[0], "0")) + parseInt(diffCell);
                }

                if (timeDate < 10) timeDate = "0" + timeDate;
                timeDate = timeDate + ":" + halfTime;

                if (calendar.week) {
                    addedCell = "cell_" + str_replace(".", "_", dateValues[0]) + "_" + str_replace(":", "_", timeDate);
                    date = dateValues[0] + " " + timeDate;
                }
                else {
                    addedCell = "cell_" + str_replace(":", "_", timeDate) + "_" + gr_date;
                    date = timeDate;
                }
            }

            if (calendar.week) {
                var dateValues = date.split(" ");
                var datePartsVal = dateValues[1].split(":");
            }
            else
                var datePartsVal = date.split(":");

            if (datePartsVal[1] == "1") {
                var nextHour = parseInt(ltrim(datePartsVal[0], "0")) + 1;
                if (nextHour < 10) nextHour = "0" + nextHour;
                if (calendar.week)
                    nextCell = "cell_" + str_replace(".", "_", dateValues[0]) + "_" + nextHour + "_0";
                else
                    nextCell = "cell_" + nextHour + "_0_" + gr_date;
            }
            else {
                if (calendar.week)
                    nextCell = "cell_" + str_replace(".", "_", dateValues[0]) + "_" + datePartsVal[0] + "_1";
                else
                    nextCell = "cell_" + datePartsVal[0] + "_1_" + gr_date;
            }
        }

        if (document.getElementById(addedCell)) // Подсвечиваем ячейку, в которую будет добавлена запись
            document.getElementById(addedCell).style.background = "#efefef";

        if (nextCell)
            if (document.getElementById(nextCell))
                document.getElementById(nextCell).style.background = "#efefef";
    }

    if (calendar.day && date.indexOf(":") > 0) {
        date = str_replace(":0", ":00:00", date);
        date = str_replace(":1", ":30:00", date);
        date = calendar.day + "." + calendar.month + "." + calendar.year + " " + date;
    }
    else if (!isGrouped && calendar.week && date.indexOf(":") > 0) {
        date = str_replace(":0", ":00:00", date);
        date = str_replace(":1", ":30:00", date);
    }
    else
        date = date.replace(/_\d+$/, '').replace(/_/g, '.') + " {current}";

    // Получаем список полей
    $.ajax({
        type: "POST",
        url: "calendar.php",
        data: {
            csrf: csrf,
            id: calendar.id,
            get: 'add_fields',
            data: {
              event_id: event_id, 
              date: date, 
              def_field: defValues
            }
        },
        timeout: 12000,
        error: function(xhr, textStatus) {
            document.getElementById("event_view_edit_add" + event_id).style.backgroundImage = "none";
            document.getElementById("event_view_edit_add" + event_id).innerHTML = textStatus;
        },
        success: function (msg) {
            resp = JSON.parse(msg);
            if (!resp) {
                jalert("JSON parse error!");
                return;
            }

            var date_fields = "";
            var nameDateField = "";
            addFieldsList = new Object;

            document.getElementById("event_view_edit_add" + event_id).style.backgroundImage = "none";
            // Строим содержание
            if (resp.status == "error")
                eventText = resp.value;
            else if (resp.status == "ok") {
                document.getElementById("go_event_link" + event_id).style.display = "none";
                document.getElementById("save_event_button" + event_id).style.display = "block";
                eventText = "<table cellpadding='0' cellspacing='0' border='0' class='textpad' style='margin: 10px 0px; position: relative;'>";
                fields = resp.value;
                var evalStr = "";
                for (orderField in fields) {
                    one_field = fields[orderField];
                    fieldId = one_field.id;
                    eventFields[fieldId] = new Object;
                    eventFields[fieldId] = one_field;
                    if (one_field.type_field == 1 || one_field.type_field == 3) fullValue = one_field.fast_edit_div + one_field.fast_edit_div_close;
                    else fullValue = one_field.fast_edit_div + one_field.def_value + one_field.fast_edit_div_close;
                    if (one_field.date_field == "1") { // Поле даты события
                        if (one_field.type_value == 1) {
                            fullValue = fullValue.replace(" datepicker ", " datetimepicker fast_edit_datetime ");
                        }
                        date_fields = fullValue + date_fields;
                        if (nameDateField == "") nameDateField = one_field.name_field + ": ";
                    }
                    else if (one_field.period_field == "1") { // Поле периода события
                        if (one_field.type_value == 1) {
                            fullValue = fullValue.replace(" datepicker ", " datetimepicker fast_edit_datetime ");
                        }
                        date_fields += " " + lang.since_to + ": " + fullValue
                        nameDateField = lang.since_from + ": ";
                    }
                    else { // Обычное поле
                        eventText += "<tr><td style='vertical-align: top; max-width: 130px; overflow: hidden; padding-top: 5px !important;' class='textpad_field_name'>";
                        if (eventFields[fieldId]['main'] == "1") eventText += "<div style='display: flex'><span style='color: red'>* </span>";
                        eventText += "<b>" + one_field.name_field + ": </b></div></td>";
                        if (one_field.type_value == 1) {
                            fullValue = fullValue.replace(" datepicker ", " datetimepicker fast_edit_datetime ");
                        }
                        eventText += "<td style='width: 100%;' class='textpad_field_value' id='sub_cell_" + fieldId + "_new'>" + fullValue;
                        if (one_field.add_link_fields != "0") {
                            eventText += "<div hidden='hidden' class='hidden_event_id'>" + event_id + "</div>";
                            eventText += "<img src=\"images/green_plus.png\" style=\"margin-top: 5px;\" class=\"add_link_img\" onclick=\"add_link_block_show_calendar(" + fieldId + ", " + event_id + ")\" onmouseover=\"this.src='images/green_plus_active.png'\" onmouseout=\"this.src='images/green_plus.png'\" />";
                            eventText += "</td></tr>";
                            eventText += "<tbody id='add_link_block" + fieldId + "' class=\"add_link_block user-data__row-wrap\" style='display: none;'><tr><td colspan='2' style='padding: 5px 0px'></td></tr>";

                            for (addFieldNum in one_field.add_link_fields) {
                                var oneAddField = one_field['add_link_fields'][addFieldNum];
                                addFieldId = oneAddField.id
                                addFieldsList[addFieldId] = oneAddField;
                                if (oneAddField.type_field == 1 || oneAddField.type_field == 3) { fullAddValue = oneAddField.fast_edit_div + oneAddField.fast_edit_div_close; }
                                else {
                                    fullAddValue = oneAddField.fast_edit_div + oneAddField.def_value + oneAddField.fast_edit_div_close
                                }
                                eventText += "<tr><td style='vertical-align: top; max-width: 130px; overflow: hidden; padding-top: 5px !important;' class='textpad_field_name'><b>" + oneAddField.name_field + ": </b></td>";
                                eventText += "<td style='width: 100%;' class='textpad_field_value' id='sub_cell_" + addFieldId + "_" + fieldId + "_0'>" + fullAddValue + "</td></tr>";
                                while (fullAddValue.indexOf("<script>") >= 0) { // JS, который надо выполнить
                                    firstPos = fullAddValue.indexOf("<script>");
                                    secondPos = fullAddValue.indexOf("<\/script>");
                                    evalStr += fullAddValue.substring(firstPos + 8, secondPos) + "\r\n";
                                    fullAddValue = fullAddValue.substring(secondPos + 9);
                                }
                            }

                            eventText += "<tr><td class='add_link_block_bottom'></td>";
                            eventText += "<td class='add_link_block_bottom'><button onclick='add_link_block_save_calendar(" + fieldId + ", " + event_id + ")' class='add_link_block_save user-data__save-adding green-btn simple-btn'>" + lang.Save + "</button></td>";
                            eventText += "</tr><tr><td colspan='2' style='padding: 5px 0px'></td></tr></tbody>"
                        }
                        else
                            eventText += "</td></tr>";
                    }

                    while (fullValue.indexOf("<script>") >= 0) { // JS, который надо выполнить
                        firstPos = fullValue.indexOf("<script>");
                        secondPos = fullValue.indexOf("<\/script>");
                        evalStr += fullValue.substring(firstPos + 8, secondPos) + "\r\n";
                        fullValue = fullValue.substring(secondPos + 9);
                    }
                }
                eventText += "</table>";
            }

            // Вставляем содержание
            date_fields = "<div class='event_datefields' id='event_datefields" + event_id + "'>" + nameDateField + date_fields + "</div>";
            document.getElementById("event_view_edit_add" + event_id).innerHTML = "<div id='event_view_hidden" + event_id + "'>" + date_fields + eventText + "</div>";

            if (evalStr) { // Выполняем JS, ставим обработчики на поля
                eval(evalStr);
                $(".datepicker").datepicker({
                    showOn: "button",
                    dateFormat: lang.date_js_format,
                    buttonImage: "images/calbtn.png",
                    buttonImageOnly: true,
                    buttonText: lang.Calendar,
                    showAnim: (('\v' == 'v') ? "" : "show"),  // в ie не включаем анимацию, тормозит
                }).attr('placeholder', lang.date_placeholder);
                //теперь уберем баг с непредвиденным автозакрытием календаря
                var span_obj = $(".datetimepicker").parent("span.datepicker_span");
                for (var i = 0; i < span_obj.length; i++) {
                    $(span_obj.get(i)).replaceWith(function (e) {
                        return span_obj.get(i).innerHTML;
                    });
                }
                $(".datetimepicker").datetimepicker({
                    showOn: "button",
                    dateFormat: lang.date_js_format,
                    timeFormat: "HH:mm",
                    buttonImage: "images/calbtn.png",
                    buttonImageOnly: true,
                    buttonText: lang.Calendar,
                    showAnim: (('\v' == 'v') ? "" : "show"),  // в ie не включаем анимацию, тормозит
                }).attr('placeholder', lang.datetime_placeholder);
            }

            resizePopover(event_id); // Сдвигаем блок под изменение высоты
            // Фиксирум поля по ширине
            var maxInputWidthSelect = 380 - parseInt($(".textpad_field_name").width());
            var maxInputWidth = 350 - parseInt($(".textpad_field_name").width());
            $(".event_view_edit .textpad div, .event_view_edit .textpad input").each(function () {
                if (parseInt($(this).width()) > maxInputWidth) $(this).width(maxInputWidth);
            });
            $(".event_view_edit .textpad select").each(function () {
                if (parseInt($(this).width()) > maxInputWidthSelect) {
                    $(this).width(maxInputWidthSelect);
                    $(this).parent("span").width(maxInputWidthSelect)
                    $(this).next("span").width(maxInputWidthSelect - 5)
                }
            });
            $("#record_card_add" + event_id).find($('textarea')).each(function (i, item) {
                autosize(item);
            });
            init_calendar_fast_edit();

            // $('.datepicker').mask('99.99.9999');
            // $('.datetimepicker').mask('99.99.9999 99:99');
        }
    });
}

/* Множественный выбор при добавлении новой записи для поля типа Связь */
function user_multi_select(el) {
    var trigger_for_all = true;
    var values_to_post = [];
    var hidden_input_for_vals = $(el).parent().parent().find('input[data-js=\'checked_vals\']');
    var string_to_insert_in_hidden_input_for_vals = '';
    var els_for_add_check = $(el).parent().parent().find('input[data-js=\'value\']');
    /* кнопка 'все'*/
    var all_checkbox = $(el).parent().parent().find('input[data-js=\'all_vals\']');
    /* вычисления для кнопки "все" */
    if($(el).attr('data-js') === 'all_vals' && $(el).prop( "checked" )) {
        els_for_add_check.each(function( index ) {
           $( this ).prop("checked", true);
            values_to_post.push($(this).val());
        })
    } else if($(el).attr('data-js') === 'all_vals' && !$(el).prop( "checked" )) {
        els_for_add_check.each(function( index ) {
            $( this ).prop("checked", false);
        });
        values_to_post = [];
    } else { /* вычисления для простого чекбокса
            пройти по всем чекбоксам, если все чекнутые, включить кнопку все
            если хоть один не чекнутый, выключить кнопку */
        els_for_add_check.each(function( index ) {
            if($( this ).prop("checked") === false) {
                trigger_for_all = false;
            } else {
                values_to_post.push($(this).val());
            };
        });
        if (trigger_for_all) {
            all_checkbox.prop("checked", true);
        } else {
            all_checkbox.prop("checked", false);
        }
    }
    /* формируем строку для записи */
    for (var value in values_to_post) {
        if(string_to_insert_in_hidden_input_for_vals === '') {
            string_to_insert_in_hidden_input_for_vals += values_to_post[value];
        } else {
            string_to_insert_in_hidden_input_for_vals += '-' + values_to_post[value];
        }
    }
    /* записываем данные в скрытый инпут */
    hidden_input_for_vals.val(string_to_insert_in_hidden_input_for_vals);
}

// Добавление новой строки/сохранение текущей
function saveEvent() {
    // Проверка коректности дат
    let isCorrect = true;
    $('.one_event_block .datetimepicker, .one_event_block .datepicker').each( (i,item) => {
        var $this = $(item);
        if (!isDateValid($this.val()) && $this.val() != '') {
            incorrectDateFieldFormat($this);
            isCorrect = false;
        };
    });
    let field_id,
        line_id,
        ac_link_val,
        elems = $('[id^="fast_edit_span_"].type_4_mult');;

    for(let i = 0; i < elems.length; i++) {
        field_id = elems[i].attributes.field_id.value.replace('_','');
        line_id = elems[i].attributes.line_id.value;
        if (line_id !== 'new') {
            ac_link_val = elems[i].value;
            save_value_multi(field_id, line_id , ac_link_val);
        }
    }


    if (!isCorrect) {
        return;
    }

    //некорректные даты
    if (calendar_error_status == 1)
        jalert('Время начала события больше времени окончания!');

    if (!addedCell) { // Сохранение текущей записи, скрываем кнопку сохранения, показываем ссылки
        $(".save_event_button").fadeOut(200, function () {
            setTimeout("$('.go_event_link').fadeIn(200)", 200);
        })
        if (pageReload) window.location.reload(); // Даты изменились, перестраиваем календарь
        return;
    }

    $(".save_event_button").fadeOut(100);
    var insertObj = new Object;
    var calendar_type;
    for (fieldId in eventFields) { // Формируем значения
        one_field = eventFields[fieldId];
        type_field = one_field.type_field;
        v_o = document.getElementById("fast_edit_span_" + fieldId + "_new_0");
        if (!v_o) continue;
        if(type_field == 4 && one_field.mult_value == "1"){
            insertObj[fieldId] = v_o.value;
        }
        if ((type_field == 1) || (type_field == 2) || (type_field == 4) || (type_field == 12) || (type_field == 3) || (type_field == 10) || (type_field == 7) || (type_field == 14)) {
            if (typeof(v_o.value) === 'undefined')
                insertObj[fieldId] = v_o.innerHTML;
            else
                insertObj[fieldId] = v_o.value;
        }
        else if (type_field == 5) {
            if (v_o.value == "Добавить новую запись") { // Если в поле связь ввели не существующее значение
                insertObj[fieldId] = '';
            } else {
                insertObj[fieldId] = v_o.value;
            }
        }

    }

    var emptyFields = "";
    $(".textpad tr").css("background", "transparent");
    $(".textpad td.textpad_field_name b").css("color", "black");

    for (fieldId in insertObj) {
        if (eventFields[fieldId]['main'] == "1" && (insertObj[fieldId] === '' || insertObj[fieldId] === '0' || insertObj[fieldId] === 'null' || insertObj[fieldId] === null || insertObj[fieldId] === undefined || insertObj[fieldId] === lang.No_data ) ) { // Не заполнены обязательные поля
            if (emptyFields != "") emptyFields += ", ";
            emptyFields += "'" + eventFields[fieldId]['name_field'] + "'";
            $("#sub_cell_" + fieldId + "_new").parent("tr").css("background", "var(--color-misty-rose)");
            $("#sub_cell_" + fieldId + "_new").parent("tr").find("td.textpad_field_name b").css("color", "#FF0000");
        }
    }
    if (emptyFields != "") {
        jalert(lang.not_all_fields);
        $(".save_event_button").fadeIn(200);
        return;
    }

    if (calendar.day)
        calendar_type = "day";
    else if (calendar.week)
        calendar_type = "week";
    else
        calendar_type = "month";

    // Отправляем данные (сейчас при добавлении страница перезагрузится в UpdateData, в дальнейшем вставка происходит на лету)
    $.ajax({
        type: "POST",
        url: "calendar.php",
        data: {
            csrf: csrf,
            id: calendar.id,
            put: 'new_line',
            data: {
              event_id: saveEventId, 
              calendar_type: calendar_type, 
              values: insertObj
            }
        },
        success: function (input) {
            var data = JSON.parse(input);
            if (!data) {
                jalert("JSON parse error!");
                $(".save_event_button").fadeIn(300);
                return;
            }
            if (data.error) {
                jalert(data.error)
                $(".save_event_button").fadeIn(300);
                return;
            }
            var new_line = data.new_line;

            var js_event = all_calendar_events[new_line.event_id];

            var newEventData = "<div style='display: none' event_length='" + new_line.length + "' style='margin-top: 0px;border: 1px solid " + js_event.color + "' ";
            newEventData += "class='calendar_event_line calendar_event_line_" + js_event.date_field + "_" + new_line.line_id + "_" + new_line.event_id + "' ";
            newEventData += "id='calendar_event_line_" + js_event.date_field + "_" + new_line.line_id + "_" + new_line.event_id + "_0_0'>";
            newEventData += "<div onclick='viewOneEvent(" + new_line.event_id + ", " + new_line.line_id + ", event, 0, 0); event.cancelBubble = true;' ";
            newEventData += "onmouseover='highlightEvent(true, " + new_line.event_id + ", " + new_line.line_id + ")' onmouseout='highlightEvent(false, " + new_line.event_id + ", " + new_line.line_id + ")' ";
            newEventData += "id='c_event_layout" + js_event.date_field + "_" + new_line.line_id + "_" + new_line.event_id + "_0_0' ";
            newEventData += " class='c_event_layout c_event_layout" + new_line.event_id + "_" + new_line.line_id + "'><span style='display: inline-block; width: 10px; height: 10px; background: " + js_event.color + "; margin: 0px 4px 0px 2px;'></span>" + new_line.mount + "</div></div>";

            document.getElementById("hidden_add_events").innerHTML += newEventData;

            if (js_events == undefined)
                js_events = new Object;
            if (!js_events[new_line.event_id])
                js_events[new_line.event_id] = new Object;
            js_events[new_line.event_id][new_line.line_id] = {
                begin: new_line.begin,
                end: new_line.end,
                offset: 0,
                length: new_line.length
            };

            var resp_arr = data.changes.toString().split("\r\n");
            UpdateData(resp_arr);
        }
    });
}

// Вывод справки по событию (блок слева)
var sysHelpInit = false;
function viewSystemHelp(event_id, action) {
    if (action) {
        if (window.sysHelpInit || hiddenEvents[event_id])
            return;
        else
            sysHelpInit = setTimeout("document.getElementById('system_help_event" + event_id + "').style.display = 'block'", 1000);
    }
    else {
        if (window.sysHelpInit)
            clearTimeout(sysHelpInit);
        sysHelpInit = false;
        document.getElementById("system_help_event" + event_id).style.display = "none";
    }
}
// Фиксация таблицы после загрузки
$(document).ready(function () {
    fixCalendarLayout();
    setTimeout("$('#calendar_event_list .select_border_btn_hover').css({width: '100px', paddingRight: '50px'})", 500);
    // Скрытые события
    // смотрим тип календаря
    var type = '0';
    if ($.cookie('default_calendar_view')) {
        switch($.cookie('default_calendar_view')) {
            case 'week':
                type = 1;
                break;
            case 'day':
                type = 2;
                break;
            default:
                type = 0;
        }
    }
    // ищем их в куках
    if (hiddenEvents) {
        for (oneEventId in all_calendar_events)
            if ($.cookie("hce_" + oneEventId + "_" + type)) {
                viewHideEvents(oneEventId, type, true);
            }
    }
    calendar_combobox($('.combobox'), false);
    init_calendar_fast_edit();
    if (($.cookie("mini_calendar_hide" + calendar.id) === 'null') || ($.cookie("mini_calendar_hide" + calendar.id) === undefined)) {
        $("#mini_calendar_layout").animate({width: "show", opacity: "show", marginLeft: "30px"}, 200);
        document.getElementById("view_calendar_arrow").innerHTML = "◀";
    }else { // Скрываем календарь
        $("#mini_calendar_layout").animate({width: "hide", opacity: "hide", marginLeft: "0px"}, 200);
        document.getElementById("view_calendar_arrow").innerHTML = "▶";
    }
    setTimeout("fixCalendarLayout()", 300);
});

$(window).resize(function () {
    if (window.fixInit)
        clearTimeout(fixInit);
    fixInit = setTimeout("fixCalendarLayout()", 200);
});

// Фиксация таблицы при загрузке страницы и ресайзе

/**
 * @fixme Работает весьма странно, делит на столбцы даже тогда когда 
 * следующее событие календаря начинается когда заканчивается предыдущее
 * меняет верстку с задержкой когда пользователь не ожидает
 */
function fixCalendarLayout() {
    if (document.getElementById("mini_calendar_layout").style.display != "none") {
      document.getElementById("calendar_pad").style.width = $("#calendar_pad").width() + "px";
      $("#calendar_scrollable-content").removeClass('mini_calendar_layout__hide');
      $("#calendar_scrollable-content").addClass('mini_calendar_layout__show');
    }
    else {
      document.getElementById("calendar_pad").style.width = "0px";
      $("#calendar_scrollable-content").addClass('mini_calendar_layout__hide');
      $("#calendar_scrollable-content").removeClass('mini_calendar_layout__show');
    }

    if (!calendar.day && !calendar.week && !isGrouped) { // Месячный вид
        var tableWidth = $(".calendar_month_type").width() || $(".calendar_week_type").width();
        var tdWidth = parseInt(((tableWidth - 6) / 7) - 2);
        $(".calendar_month_type td").width(tdWidth)|| $(".calendar_week_type td").width(tdWidth);

        $(".calendar_event_line").each(function () {
            var eventLength = parseInt($(this).attr("event_length"));
            var isBackground = parseInt($(this).attr("is_background"));
            var borderMargin = 0
            if (isBackground)
                borderMargin = 2
            var addWidth = 0;
            if (eventLength == 1)
                addWidth = 1;
            if (eventLength == 2)
                addWidth = 3;
            if (eventLength == 3)
                addWidth = 7;
            if (eventLength == 4)
                addWidth = 11;
            if (eventLength == 5)
                addWidth = 13;
            if (eventLength == 6)
                addWidth = 17;
            if (eventLength == 7)
                addWidth = 19;

            fullCalWidth = (eventLength * tdWidth) + addWidth + borderMargin;
            $(this).width(fullCalWidth);
            var arrowMargin = fullCalWidth + 1 - isBackground;
            $(this).find(".next_event_arrow").css("margin-left", arrowMargin + "px");
        });
        $(".hidden_items_events").width(tdWidth);
    }
    if (calendar.day) { // Дневной вид
        var maxTableWidth = parseInt($(window).width() - $("#calendar_pad").width()) - 67 - 74;
        var maxTdWidth = parseInt(maxTableWidth / calendar.day_td_count);
        $(".day_top").width(maxTdWidth);

        if (document.getElementById("cur_time_line")) {
            var timeLineWidth = maxTableWidth + 1;
            document.getElementById("cur_time_line").style.width = timeLineWidth + "px";
            var timeHeight = parseInt($(".day_time").height());

            var pOffset = parseInt($("#cur_time_line").attr("offset"));
            var timeLineOffset = parseInt(timeHeight - ((timeHeight / 100) * (100 - pOffset))) - 9;
            if (pOffset > 50) timeLineOffset += 2;
            document.getElementById("cur_time_line").style.marginTop = timeLineOffset + "px";
        }

        resizeRow(true);

        resizeRow(false);

        $(".calendar_day_type .calendar_event_line").each(function () {
            var oneMargin = '';
            var oneHeight = '';
            var element_width = '';
            var eventLength = parseFloat($(this).attr("event_length"));
            var eventOffset = $(this).attr("event_offset");
            var isBackground = $(this).attr("is_background");

            backgroundInLayout = 1;
            if (eventOffset.indexOf("back") < 0)
                backgroundInLayout = 0;
            eventOffset = parseInt(eventOffset);

            if (isBackground == 1) {
                oneHeight = parseInt(eventLength * BLOCK_HEIGHT - 2); // 2 - размер бордера, его отнимаем от высоты
                if (navigator.userAgent.search("Firefox") !== -1) {
                    oneHeight = parseInt(eventLength * (BLOCK_HEIGHT + 0.45) - 1);
                }
                $(this).css({
                    height: oneHeight + 'px',
                    'min-height': oneHeight + 'px'
                });
            } else {
                oneHeight = parseInt(eventLength * BLOCK_HEIGHT - 2); // 2 - размер бордера, его отнимаем от высоты
                if (navigator.userAgent.search("Firefox") !== -1) { // лиса присовывает полпикселя к ряду
                    oneHeight = parseInt(eventLength * (BLOCK_HEIGHT + 0.45) - 1);
                }

                if (eventOffset <= 0 ) eventOffset = 1;

                let elemWidth = maxTdWidth - 1;
                let marginLeft = $(this).css('margin-left');

                marginLeft =  parseInt(marginLeft.substring(0, marginLeft.length - 2));
                if (marginLeft !== 0) elemWidth -= marginLeft;

                $(this).css({
                    height: oneHeight + 'px',
                    'min-height': oneHeight + 'px',
                    'max-width': elemWidth + 'px',
                });
                $(this).find($('.c_event_layout')).css('min-height', oneHeight + 'px');
                if (config['display_calendar_events'] == '0') {
                    $(this).hover(function () {
                        $(this).css('height', 'auto');
                        if (!$(this).parent().is(':last-child')) {
                            $(this).css('min-width', '150px');
                        }
                    }, function () {
                        let defaulWidth = $(this).attr('data-default-width') || $(this).attr('c_default-width');
                        $(this).css('height', oneHeight + 'px');
                        $(this).css('min-width', defaulWidth);
                        $(this).css('z-index', parseInt($(this).attr('event_offset')) + 13);
                    });
                }
            }
        });

        var arrowMargin = $(".calendar_table").width();
        $(".view_overperiod_events .calendar_event_line").each(function () {
            if (parseInt($(this).attr("is_background")))
                aMargin = arrowMargin;
            else
                aMargin = arrowMargin - 2;
            $(this).find(".next_event_arrow").css("margin-left", aMargin + "px");
        });
    }
    if (!isGrouped && calendar.week) { // Недельный вид
        var tableWidth = $(window).width() - $("#calendar_pad").width() - 140;
        var oneTdWidth = parseInt((tableWidth - 7) / 7);
        $("td.overperiod_cells").width(oneTdWidth);

        if (oneTdWidth < 100) oneTdWidth = 100;

        if (document.getElementById("cur_time_line")) {
            var timeLineWidth = oneTdWidth;
            document.getElementById("cur_time_line").style.width = timeLineWidth + "px";
            var timeHeight = parseInt($(".week_time").height());

            var pOffset = parseInt($("#cur_time_line").attr("offset"));
            var timeLineOffset = parseInt(timeHeight - ((timeHeight / 100) * (100 - pOffset)));
            document.getElementById("cur_time_line").style.marginTop = timeLineOffset + "px";
            var leftArrowOffset = $("#calendar_pad").width() + 32;
            var topArrowOffset = timeLineOffset - 6;
            $("#cur_time_line_arrow").css({left: "-"+((oneTdWidth+1)*(calendar.cur_week_day-1) + 75)+"px", marginTop: topArrowOffset + "px"});
        }

        $(".view_overperiod_events .calendar_event_line").each(function () {
            var eventLength = parseInt($(this).attr("event_length"));
            fullCalWidth = parseInt((eventLength * oneTdWidth) + eventLength) - 2; // Задаем ширину, учитывая границы (1px)
            fullCalHeight = Math.ceil($(this).height());
            $(this).width(fullCalWidth);
            $(this).height(fullCalHeight);
            var arrowMargin = fullCalWidth;

            $(this).find(".next_event_arrow").css("margin-left", arrowMargin + "px");
        });

        var backgroundEvents = resizeRow(true);
        resizeRow(false);

        $(".week_top .calendar_event_line, .week_bottom .calendar_event_line").each(function (i) {
            var isBackground = parseInt($(this).attr("is_background"));
            var eventOffset = parseInt($(this).attr("event_offset"));
            var eventLength = parseFloat($(this).attr("event_length"));
            var oneHeight = parseInt(eventLength * BLOCK_HEIGHT - 2); // 2 - размер бордера, его отнимаем от высоты
            let defaulWidth = parseInt($(this).attr('data-default-width')),
                count_events = $(this).attr('all_event'),
                count_event,
                is_far_event,
                margin_left_event = 0;
            if( count_events < $(this).parent().children().last().attr('event_offset')){
                is_far_event = true;
            }
            if (is_far_event==true) {
                count_event = (eventOffset-2);
            } else {
                count_event = (eventOffset-1);
            }
            if (count_event!=0) {
                margin_left_event = count_event*100/count_events;
                if (count_event > 0 && count_event != count_events && count_event < count_events) {
                    margin_left_event = count_event*100/count_events;
                } else {
                    margin_left_event = 0;
                }
            }
            if (defaulWidth < 100) defaulWidth = defaulWidth * 3 + 7; // Ширина блока для недельного отображения почему-то задается в 3 раза меньше (data-default-width)
            $(this).css('width', (100/$(this).attr('all_event') - 0.4) + '%');
            $(this).css('margin-left', margin_left_event + '%');
            if (config['display_calendar_events'] == '0') {
                $(this).hover(function () {
                    $(this).css('min-width', '150px' );
                    if (parseInt($(this).attr('event_length')) < 2) {
                        $(this).css('height', '38px');
                    } else {
                        $(this).css('height', $(this).attr('event_length') * 19 + 'px');
                    }
                    // if ($(this).attr('is_background') == '0') {
                    //     if ($($(this).children()[0]).height() < $(this).height()) {
                    //         $($(this).children()[0]).height($(this).height());
                    //     } else {
                    //         $(this).css('height', 'auto');
                    //     }

                    // } else {
                        $(this).css('opacity', '0.9');
                        $(this).css('zIndex', '900');
                    // }
                }, function () {
                    let defaulWidth = $(this).attr('data-default-width');
                    if (defaulWidth == undefined ) defaulWidth = $(this).attr('c_default-width');
                    $(this).css('min-width', defaulWidth );
                    if ($(this).attr('is_background') == '0') {
                        $(this).css('height', oneHeight + 'px');
                    } else {
                        $(this).css('opacity', '0.5');
                    }
                    if (parseInt($(this).attr('event_offset')) == 1){
                        $(this).css('z-index', 16);
                    } else {
                        $(this).css('z-index', parseInt($(this).attr('event_offset')) + 13);
                    }

                });
            }
        });
    }

    var calendar_table_width = $(".calendar_table").width();
    $(".view_overperiod_events").width(calendar_table_width + 'px');

    if(isGrouped && (calendar.week || calendar.month)){
    $('td[class^="month_cell"]').each(function() {
        var eventLines = $(this).find('div.calendar_event_line');
        eventLines.each(function(index) {
        var eventLength = parseInt($(this).attr("event_length")) || 1;
        var topValue = index * 21;
        var heightValue = eventLength > 1 ? 151 * eventLength - topValue : eventLength * 21;
        $(this).css({
            'top': topValue + 'px',
            'height': heightValue + 'px',
            'position': 'absolute',
            'z-index': 35 - eventLength,
        });
        highOneEventGroup(eventLines, eventLength);
        if (config['display_calendar_events'] == '0') {
            $(this).hover(function () {
                $(this).css("z-index", 401);
                if (!$(this).parent().is(':last-child')) {
                    $(this).css('min-width', '150px');
                }
                if($(this).height() < 148){
                    $(this).css('height', 'auto');
                }
            }, function () {
                $(this).css('height', heightValue);
                $(this).css('min-width', 100 + '%');
                $(this).css('z-index', 35 - eventLength);
                highOneEventGroup(eventLines, eventLength);
        });
        }
        $("div.c_event_layout").css('white-space', 'normal')
        });
    });
    }

    setTimeout("hidePreloader()", 400);
}

/**
 * Указание высоты события 100% если он один в ячейке
 * @param {object} eventLines      //jQuery html элементы событий
 * @param {number} eventLength     //сколько длиться событие дней
 * @returns {void}
 */
function highOneEventGroup(eventLines, eventLength) {
    if(eventLines.length === 1 && eventLength === 1){
        $(eventLines[0]).css('height', '100%');
    }
}

function highlightEvent(set, event_id, line_id) { // Подсветка событий при наведении
    var eventData = all_calendar_events[event_id];
    var backgroundColor = eventData.color;
    var colorR = eventData.color_r;
    var colorG = eventData.color_g;
    var colorB = eventData.color_b;

    if (set) {
        if ((!isGrouped && calendar.week) || calendar.day)
            $(".calendar_offset_" + line_id + "_" + event_id).css("z-index", 400);

        $(".c_event_layout" + event_id + "_" + line_id).css("background-color", "rgba(" + colorR + ", " + colorG + ", " + colorB + ", 0.4)");
    }
    else if (document.getElementById("record_card_add" + event_id).style.display == "none" || recordView != line_id) {
        $(".c_event_layout" + event_id + "_" + line_id).css("background-color", "rgba(" + colorR + ", " + colorG + ", " + colorB + ", 0.2)");
    }
}

// Подсветка столбца с маленьким календарём при наведении на стрелку
function highlightMinicalendar(set) {
    if (set)
        setColor = "#f0f0f0";
    else
        setColor = "transparent";
    document.getElementById("calendar_act_button").style.background = setColor;
    document.getElementById("calendar_pad").style.background = setColor;
}


// Показать/скрыть список всех событий на кнопке "Добавить запись"
var hiddenHandler = function () {
    if ($(event.target).closest("#hidden_add_events").length) return;
    viewAllEvents();
    event.stopPropagation();
};

function viewAllEvents() {
    if (document.getElementById("hidden_add_events").style.display == "none") {
        $('#hidden_add_events').animate({width: 'show', height: 'show', opacity: 'show'}, 200);
        $(document).bind("click", hiddenHandler);
    }
    else {
        $('#hidden_add_events').animate({width: 'hide', height: 'hide', opacity: 'hide'}, 200);
        $(document).unbind("click", hiddenHandler);
    }
}

// Функция перестроения календаря при изменениях, пока только перезагружает страницу
function changeOffsets() {
    window.location.reload();
    return;
    var lengthOrder = new Object;
    var maxLength = 0;
    max_offset = 0;
    max_overperiods_offset = 0;
    var busyDates = new Object;
    var busyWeekdays = new Object;

    for (oneEventId in js_events) {
        var oneEvent = js_events[oneEventId];
        for (oneLineId in oneEvent) {
            var oneLine = oneEvent[oneLineId];
            if (oneLine == undefined) continue;
            if (calendar.day && (parseInt(calendar.js_begin) > parseInt(oneLine.begin) || parseInt(calendar.js_end) < parseInt(oneLine.end))) continue;

            var lengthValue = parseInt(oneLine.length);
            if (lengthValue > maxLength)
                maxLength = lengthValue;
            if (!lengthOrder[lengthValue])
                lengthOrder[lengthValue] = new Object;
            if (!lengthOrder[lengthValue][oneEventId])
                lengthOrder[lengthValue][oneEventId] = new Object;
            lengthOrder[lengthValue][oneEventId][oneLineId] = 1;
        }
    }

    for (m = maxLength + 1; m >= 0; m--) {
        var oneData = lengthOrder[m];
        if (!oneData) continue;
        for (oneEventId in oneData) {
            var oneEvent = oneData[oneEventId];
            for (oneLineId in oneEvent) {
                var oneLine = js_events[oneEventId][oneLineId];
                if (calendar.day || calendar.week)
                    var startLine = 1;
                else
                    var startLine = 0;

                var overperiodOffset = 0;

                var currentOffset = oneLine.offset;
                var addedLine = false;

                while (!addedLine) {
                    if (calendar.day || (calendar.week && (oneLine.begin.substr(0, 8) == oneLine.end.substr(0, 8) || oneLine.end == "0"))) {
                        var startHour = oneLine.begin.substr(8, 2);
                        if (startHour[0] == "0") startHour = startHour[1];
                        var startMin = oneLine.begin.substr(10, 2);
                        if (startMin < "30") startMin = 0;
                        else startMin = 0.5;
                        var startTime = parseInt(startHour) + startMin;

                        if (oneLine.end == "0")
                            var endTime = startTime + 1;
                        else {
                            var endHour = oneLine.end.substr(8, 2);
                            if (endHour[0] == "0") endHour = endHour[1];
                            var endMin = oneLine.end.substr(10, 2);
                            if (endMin < "30") endMin = 0;
                            else endMin = 0.5;
                            var endTime = parseInt(endHour) + endMin;
                        }

                        if (calendar.week) {
                            for (pos in week_days)
                                if (oneLine.begin.substr(0, 8) == week_days[pos])
                                    lineWeekDay = pos;
                        }
                        else
                            lineWeekDay = "";

                        for (h = startTime; h <= endTime; h = h + 0.5) {
                            var busyDate = h + "" + lineWeekDay;
                            if (!busyDates[busyDate])
                                busyDates[busyDate] = new Object;
                            if (!busyDates[busyDate][startLine]) {
                                busyDates[busyDate][startLine] = {event_id: oneEventId, line_id: oneLineId};
                                addedLine = true;
                            }
                            else {
                                addedLine = false;
                                startLine += 1;

                                for (oneDateStr in busyDates) {
                                    var oneDate = busyDates[oneDateStr];
                                    if (oneDate[startLine])
                                        if (oneDate[startLine]['event_id'] == oneEventId && oneDate[startLine]['line_id'] == oneLineId)
                                            busyDates[oneDateStr][startLine] = false;
                                }
                                break;
                            }
                        }
                    }
                    else if (calendar.week && oneLine.begin.substr(0, 8) != oneLine.end.substr(0, 8) && oneLine.end != "0") {
                        if (calendar.js_begin > oneLine.begin)
                            var startCell = calendar.js_begin.substr(0, 8);
                        else
                            var startCell = oneLine.begin.substr(0, 8);

                        if (calendar.js_end < oneLine.end)
                            var endCell = calendar.js_end.substr(0, 8);
                        else
                            var endCell = oneLine.end.substr(0, 8);

                        for (pos in week_days) {
                            if (startCell <= week_days[pos] && endCell >= week_days[pos]) {
                                if (!busyWeekdays[pos])
                                    busyWeekdays[pos] = new Object;
                                if (!busyWeekdays[pos][overperiodOffset]) {
                                    busyWeekdays[pos][overperiodOffset] = {event_id: oneEventId, line_id: oneLineId};
                                    addedLine = true;
                                }
                                else {
                                    for (oneWeekStr in busyWeekdays) {
                                        var oneWeek = busyWeekdays[oneWeekStr];
                                        if (oneWeek[overperiodOffset])
                                            if (oneWeek[overperiodOffset]['event_id'] == oneEventId && oneWeek[overperiodOffset]['line_id'] == oneLineId)
                                                busyWeekdays[oneWeekStr][overperiodOffset] = false;
                                    }
                                    overperiodOffset += 1;
                                    addedLine = false;
                                    break;
                                }
                            }
                        }
                    }
                    else {
                        for (oneStrId in month_days) {
                            var oneStr = month_days[oneStrId];
                            for (oneCellId in oneStr) {
                                var oneCell = oneStr[oneCellId];
                                var busyDate = parseInt(oneCell.str);
                                if (parseInt(oneLine.end) == 0) oneLine.end = oneLine.begin;

                                if (busyDate >= parseInt(oneLine.begin) && busyDate <= parseInt(oneLine.end)) {
                                    if (!busyDates[busyDate])
                                        busyDates[busyDate] = new Object;

                                    if (!busyDates[busyDate][startLine]) {
                                        busyDates[busyDate][startLine] = {event_id: oneEventId, line_id: oneLineId};
                                        addedLine = true;
                                    }
                                    else {
                                        for (oneDateStr in busyDates) {
                                            var oneDate = busyDates[oneDateStr];
                                            if (oneDate[startLine])
                                                if (oneDate[startLine]['event_id'] == oneEventId && oneDate[startLine]['line_id'] == oneLineId)
                                                    busyDates[oneDateStr][startLine] = false;
                                        }
                                        addedLine = false;
                                        startLine += 1
                                    }
                                }
                            }
                        }
                    }
                }

                if (max_offset < parseInt(startLine)) max_offset = parseInt(startLine) - 1;
                if (max_overperiods_offset < parseInt(overperiodOffset)) max_overperiods_offset = parseInt(overperiodOffset);

                $(".overperiods_heights").height((max_overperiods_offset + 1) * 17)

                if (calendar.week && oneLine.begin.substr(0, 8) != oneLine.end.substr(0, 8) && oneLine.end != "0" && parseInt(currentOffset) != parseInt(overperiodOffset)) {
                    var date_field = all_calendar_events[oneEventId]['date_field'];
                    js_events[oneEventId][oneLineId]['offset'] = overperiodOffset;

                    var cOffset = parseInt(overperiodOffset) * 17;
                    $(".calendar_event_line_" + date_field + "_" + oneLineId + "_" + oneEventId).css("margin-top", cOffset + "px");
                }
                else if (parseInt(currentOffset) != parseInt(startLine)) {
                    if (calendar.week && oneLine.begin.substr(0, 8) != oneLine.end.substr(0, 8) && oneLine.end != "0") continue;
                    var date_field = all_calendar_events[oneEventId]['date_field'];
                    js_events[oneEventId][oneLineId]['offset'] = startLine;

                    if (calendar.day || calendar.week) {
                        $(".calendar_event_line_" + date_field + "_" + oneLineId + "_" + oneEventId).attr("event_offset", startLine);
                    }
                    else {
                        var cOffset = parseInt(startLine) * 17;
                        $(".calendar_event_line_" + date_field + "_" + oneLineId + "_" + oneEventId).css("margin-top", cOffset + "px");
                    }
                }
            }
        }
    }
}
////////////////////////////////////
///////////////////////////////////

// сформировать значение мулти-селекта
function form_value_multi(field_id, line_id, pos, subtable_id, val) {
    fast_edit_old = field_id + '|' + line_id + '|' + pos + '|' + val;
    var mult_id = "fast_edit_span_" + field_id + "_" + line_id + "_" + subtable_id + pos;
    var mult_obj = document.getElementById(mult_id);
    var part = mult_obj.getAttribute('part');
    var selWidth = $(mult_obj).width();

    field = all_fields[field_id];
    type_field = field['type_field'];

    // формируем полное значение
    var full_value = "";
    var all_values = {};
    $("#sub_cell_" + field_id + "_" + line_id).find('select[multi_select_group=' + field_id + '_' + line_id + ']').each(function (i) {
        if (this.value && all_values[this.value] == undefined) {
            if (((type_field == 7 || type_field == 11) && this.value != 0) || (type_field == 14 && this.value != ""))
                full_value += "-" + this.value;
            else if (type_field == 4)
                full_value = full_value + this.value + "\r\n";
        }
        // Также составляем список элемент, которые нельзя выбрать
        if (((type_field == 7 || type_field == 11) && this.value != 0) || ((type_field == 14 || type_field == 4) && this.value != ""))
            all_values[this.value] = this.value;
    });
    if (full_value) {
        if (type_field == 4)
            full_value = full_value.substr(0, full_value.length - 2);
        else
            full_value += "-";
    }


    // измеянем количество селектов
    if (((type_field == 4 || type_field == 14) && val == "") || ((type_field == 7 || type_field == 11) && val == 0)) { // выбрано пустое значение
        var t_span = mult_obj.parentNode;
        t_span.parentNode.removeChild(t_span);
    }
    else { // выбрано непустое значение
        if (mult_obj.getAttribute("is_last") == 1) {
            mult_obj.setAttribute("is_last", 0);
            // клонируем текущий элемент
            var obj_tag_name = mult_obj.tagName;
            var new_pos = intval(pos) + 1;
            var new_id = "fast_edit_span_" + field_id + "_" + line_id + "_" + subtable_id + new_pos;
            var newEL = document.createElement(obj_tag_name);
            newEL.id = new_id;
            newEL.className = mult_obj.className;
            newEL.setAttribute("subtable_id", subtable_id);
            $(newEL).removeClass('cust_select');
            // события
            addHandler_mult_select(newEL);

            // атрибуты
            newEL.setAttribute("multi_select_group", field_id + "_" + line_id);
            newEL.setAttribute("style", mult_obj.getAttribute("style"));
            newEL.style.background = '';
            newEL.setAttribute("field_id", field_id);
            newEL.setAttribute("line_id", line_id);
            newEL.setAttribute("pos", new_pos);
            newEL.setAttribute("is_last", 1);
            newEL.setAttribute("tabindex", last_tabindex_fast_edit);
            newEL.setAttribute("part", part);
            $(newEL).html(mult_obj.innerHTML);
            newEL.selectedIndex = -1;
            // вставляем после текущего
            var parent = mult_obj.parentNode;
            parent.appendChild(newEL);
        }
    }

    $("#sub_cell_" + field_id + "_" + line_id).find('select[multi_select_group=' + field_id + '_' + line_id + ']').each(function (t) {
        var select_obj = this;
        var options_count = 0;
        $(this).children().each(function (y) {
            if (this.value != select_obj.value) { // не выбранный елемент
                if (in_array(this.value, all_values)) {
                    this.style.display = 'none';
                    this.setAttribute('disabled', 'disabled');
                }
                else {
                    this.style.display = '';
                    this.setAttribute('disabled', '');
                    this.removeAttribute('disabled');
                    if (((type_field == 4 || type_field == 14) && this.value != '') || ((type_field == 7 || type_field == 11) && this.value != 0)) options_count++;
                }
            }
            else {
                this.style.display = '';
                this.setAttribute('disabled', '');
                this.removeAttribute('disabled');
                if (((type_field == 4 || type_field == 14) && this.value != '') || ((type_field == 7 || type_field == 11) && this.value != 0)) options_count++;
            }
        });
        if (options_count == 0)
        // Не одного видимого элемента, скрываем котрол
        {
            select_obj.parentNode.style.display = 'none';
            select_obj.style.display = 'none';
        }
        else {
            select_obj.parentNode.style.display = 'block';
            select_obj.style.display = '';
        }
    });

    if (document.getElementById('fast_edit_span_' + field_id + '_' + line_id + "_" + subtable_id).value != full_value) { // Если изменилось сохранияем
        $('input[id^=fast_edit_span_' + field_id + '_' + line_id + ']').val(full_value);
        if (part == 'add_link_field') return; // Ничего не сохраняем
        save_value_multi(field_id, line_id, full_value);
    }
    ;
}

/// ----------------- Обработчкики событий ----------------
//
//  Одинарный селект
//

function addHandler_select(obj) {
    // Поскольку объект скрыт, ширина равна нулю, проходимся по всему списку значений и находим самое длинное, устанавливаем ширину элемента по нему
    var field_id = obj.getAttribute('field_id');
    if (obj.id.indexOf("fast_edit_span_" + field_id + "_e") >= 0) {
        $(obj).width(150); // В быстром поиске принудительная ширина
        $(obj).removeClass("nwidth");
        // setTimeout("substr_select_span('" + obj.id + "')", 500);
    }
    else {
        var charsCount = 0
        $("#" + obj.id + " option").each(function (element, index) {
            if (charsCount < $(this).html().length)
                charsCount = $(this).html().length;
        });
        $(obj).width((charsCount * 7) + 20);
    }
    addHandler(obj, "onkeypress", onkeypress_select);
    addHandler(obj, "onkeydown", onkeydown_select);
    addHandler(obj, "onmousedown", onmousedown_select);
    addHandler(obj, "onfocus", onfocus_select);
    addHandler(obj, "onchange", onchange_select);
};

function onkeypress_select(event) {
    var obj = event.target;
    if (!obj) obj = event.srcElement;

    if ((event.keyCode == 0xA) || (event.keyCode == 0xD)) {
        if (window.event) {
            window.event.cancelBubble = true;
            window.event.returnValue = false;
        }
        else {
            event.stopPropagation();
            event.cancelBubble = true;
            event.returnValue = false;
        }
        obj.blur();
        dont_open_view = 1;
        return false;
    }
};

function onkeydown_select(event) {
    var obj = event.target;
    if (!obj) obj = event.srcElement;
    if ((event.keyCode == 0xA) || (event.keyCode == 0xD)) {
        if (window.event) {
            window.event.cancelBubble = true;
            window.event.returnValue = false;
        }
        else {
            event.stopPropagation();
            event.cancelBubble = true;
            event.returnValue = false;
        }
        obj.blur();
        dont_open_view = 1;
        return false;
    }
};

function onmousedown_select(event) {
    dont_open_view = 2;
};


function onfocus_select(event) {
    var obj = event.target;
    if (!obj) obj = event.srcElement;
    fast_edit_old = obj.getAttribute('field_id') + '|' + obj.getAttribute('line_id') + '|' + obj.value;
}

function onchange_select(event) {
    var obj = event.target;
    if (!obj) obj = event.srcElement;
    var part = obj.getAttribute('part');
    var field_id = obj.getAttribute('field_id');
    /*if (obj.id.indexOf("fast_edit_span_" + field_id + "_e") >= 0)
        setTimeout("substr_select_span('" + obj.id + "')", 200);*/

    if (part == 'add_link_field') return; // Ничего не сохраняем
    if ($(".save_event_button:visible").length == 0) {
        $(".go_event_link").fadeOut(200, function () {
            setTimeout("$('.save_event_button').fadeIn(200)", 200);
        });
    }
    if (fast_edit_old != obj.getAttribute('field_id') + '|' + obj.getAttribute('line_id') + '|' + obj.value) {
        obj.style.backgroundColor = '#fff6ad';
        obj.parentNode.setAttribute('yellow_color', '1');
        save_value(field_id, obj.getAttribute('line_id'), obj.value);
        obj.parentNode.setAttribute('yellow_color', '0');
        parentObj= obj.parentNode;
        $(parentObj).animate({
            backgroundColor: '#fff'
        }, 1000);
    }
    else {
        obj.parentNode.style.background = '';
        obj.parentNode.style.backgroundColor = '';
    }
}


//
//  МНОЖЕСТВЕННЫЙ СЕЛЕКТ
//
function addHandler_mult_select(obj) {
    // Поскольку объект скрыт, ширина равна нулю, проходимся по всему списку значений и находим самое длинное, устанавливаем ширину элемента по нему
    var field_id = obj.getAttribute('field_id');
    var line_id = obj.getAttribute('line_id');
    if (obj.id.indexOf("fast_edit_span_" + field_id + "_e") >= 0) {
        $(obj).width(150); // В быстром поиске принудительная ширина
        $(obj).removeClass("nwidth");
    }
    else {
        var charsCount = 0
        $("#" + obj.id + " option").each(function (element, index) {
            if (charsCount < $(this).html().length)
                charsCount = $(this).html().length;
        });
        $(obj).width((charsCount * 7) + 20);
    }

    addHandler(obj, "onkeypress", onkeypress_mult_select);
    addHandler(obj, "onkeydown", onkeydown_mult_select);
    addHandler(obj, "onfocus", onfocus_mult_select);
    addHandler(obj, "onchange", onchange_mult_select);
};

function onkeypress_mult_select(event) {
    var obj = event.target;
    if (!obj) obj = event.srcElement;

    if ((event.keyCode == 0xA) || (event.keyCode == 0xD)) {
        if (window.event) {
            window.event.cancelBubble = true;
            window.event.returnValue = false;
        }
        else {
            event.stopPropagation();
            event.cancelBubble = true;
            event.returnValue = false;
        }
        obj.blur();
        dont_open_view = 1;
        return false;
    }
};

function onkeydown_mult_select(event) {
    var obj = event.target;
    if (!obj) obj = event.srcElement;
    if ((event.keyCode == 0xA) || (event.keyCode == 0xD)) {
        if (window.event) {
            window.event.cancelBubble = true;
            window.event.returnValue = false;
        }
        else {
            event.stopPropagation();
            event.cancelBubble = true;
            event.returnValue = false;
        }
        obj.blur();
        dont_open_view = 1;
        return false;
    }
};

function onfocus_mult_select(event) {
    var obj = event.target;
    if (!obj) obj = event.srcElement;
    fast_edit_old = obj.getAttribute('field_id') + '|' + obj.getAttribute('line_id') + '|' + obj.getAttribute('pos') + '|' + obj.value;
}

function onchange_mult_select(event) {
    var obj = event.target;
    if (!obj) obj = event.srcElement;
    if (fast_edit_old != obj.getAttribute('field_id') + '|' + obj.getAttribute('line_id') + '|' + obj.getAttribute('pos') + '|' + obj.value) {
        var part = obj.getAttribute('part');
        if (part != 'add_link_field') {
            obj.parentNode.style.background = '#fff6ad';
            obj.parentNode.setAttribute('yellow_color', '1');
            if ($(".save_event_button:visible").size() == 0) {
                $(".go_event_link").fadeOut(200, function () {
                    setTimeout("$('.save_event_button').fadeIn(200)", 200);
                });
            }
        }
        form_value_multi(obj.getAttribute('field_id'), obj.getAttribute('line_id'), obj.getAttribute('pos'), 0, obj.value);
    }
    else {
        obj.parentNode.style.background = '';
        obj.parentNode.style.backgroundColor = '';
    }
}

//
//  Поле дата
//
function addHandler_date(obj) {
    addHandler(obj, "onkeydown", onkeydown_date);
    $(obj).bind("mousedown", onmousedown_date);
    addHandler(obj, "onfocus", onfocus_date);
    addHandler(obj, "onblur", onblur_date);
    $(obj).bind('change', onchange_date); // необходимо для обмена событиями
};

function onkeydown_date(event) {
    var obj = event.target;
    if (!obj) obj = event.srcElement;
    var part = obj.getAttribute('part');
    if (part != 'add_link_field') {
        $(".go_event_link").fadeOut(200, function () {
            setTimeout("$('.save_event_button').fadeIn(200)", 200);
        });
    }
    if ((event.keyCode == 0xA) || (event.keyCode == 0xD)) {
        if (window.event) {
            window.event.cancelBubble = true;
            window.event.returnValue = false;
        }
        else {
            event.stopPropagation();
            event.cancelBubble = true;
            event.returnValue = false;
        }
        // Если нажат enter отменяем событие, и сохраняем значение
        this.blur();
        return false;
    }
}

function onmousedown_date(event) {
    var obj = event.target;
    if (!obj) obj = event.srcElement;
    dont_open_view = 1;
}

function onfocus_date(event) {
    var obj = event.target;
    if (!obj) obj = event.srcElement;
    if (skip_fast_focus) {
        skip_fast_focus = 0;
        return;
    }
    if (fast_edit_old === '') {
        fast_edit_old = obj.getAttribute('field_id') + '|' + obj.getAttribute('line_id') + '|' + obj.value;
    }
}

function onblur_date(event) {
    var obj = event.target;
    if (!obj) obj = event.srcElement;
    if (skip_fast_blur) {
        skip_fast_blur = 0;
        return;
    }
    ;
    // $(obj).datepicker('hide');
}

function onchange_date(event) {
    var obj = event.target;
    if (!obj) obj = event.srcElement;
    if ($('#ui-datepicker-div').css("overflow") != 'hidden' && $(obj).hasClass("datetimepicker") && $('#ui-datepicker-div').css("display") != 'none') return false; //отключаем быстрое редактирование в режиме календаря c часами и минутами до тех пор, пока открыт сам календарик
    var part = obj.getAttribute('part');
    if (part == 'add_link_field') return; // Ничего не сохраняем
    if (fast_edit_old != obj.getAttribute('field_id') + '|' + obj.getAttribute('line_id') + '|' + this.value) {
        if (!isDateValid($(obj).val()) && $(obj).val() != '') {
            incorrectDateFieldFormat($(obj));
            obj.blur();
            return;
        }
        obj.style.background = '#fff6ad';
        $(obj).attr('yellow_color', '1');
        save_value(obj.getAttribute('field_id'), obj.getAttribute('line_id'), obj.value);
    }
    else
        obj.style.background = '';
    obj.blur();
    if ($(".save_event_button:visible").size() == 0) {
        $(".go_event_link").fadeOut(200, function () {
            setTimeout("$('.save_event_button').fadeIn(200)", 200);
        });
    }
}

//
//  ПОЛЕ ТЕКСТ
//
function addHandler_text(obj, part) {
    //addHandler(obj,"onkeypress",   onkeypress_text);
    addHandler(obj, "onkeydown", onkeydown_text);
    //addHandler(obj,"onmousedown",  onmousedown_text);
    //addHandler(obj,"onfocus",      onfocus_text);
    //addHandler(obj,"onblur",       onblur_text);
    addHandler(obj, "onchange", onchange_text);
    addHandler(obj, "onfocus", onfocus_text_new);
    addHandler(obj, "onblur", onblur_text_new);
};

function onfocus_text_new(event) {
    var obj = event.target;

    var part = obj.getAttribute('part');
    if (part == 'add_link_field') return; // Ничего не сохраняем

    obj.style.border = '1px dotted #a0a0a0';
    obj.style.background = 'white';
}

function onblur_text_new(event) {
    var obj = event.target;

    var part = obj.getAttribute('part');
    if (part == 'add_link_field') return; // Ничего не сохраняем

    obj.style.border = '1px dotted #a0a0a0';
}

function onchange_text(event) {
    var obj = event.target;

    var part = obj.getAttribute('part');
    if (part == 'add_link_field') return; // Ничего не сохраняем

    obj.style.border = '1px dotted #a0a0a0';

    if (fast_edit_old != obj.getAttribute('field_id') + '|' + obj.getAttribute('line_id') + '|' + obj.value) {
        obj.style.background = '#fff6ad';
        $(obj).attr('yellow_color', '1');
        save_value(obj.getAttribute('field_id'), obj.getAttribute('line_id'), obj.value);
    }
    else
        obj.style.background = '';
}

function onkeypress_text(event) {
    if ((navigator.appVersion.indexOf("MSIE") != -1) || (navigator.userAgent.indexOf("Opera") != -1)) { // тк. они некорректно вставляют перенос строки
        if ((event.keyCode == 0xA) || (event.keyCode == 0xD)) {
            if (window.event) {
                window.event.cancelBubble = true;
                window.event.returnValue = false;
            }
            else {
                event.stopPropagation();
                event.cancelBubble = true;
                event.returnValue = false;
            }
            return false;
        }
    }
}

function onkeydown_text(event) {
    var obj = event.target;
    if (!obj) {
        obj = event.srcElement;
        // IE как всегда в ударе, подписываем на событие div а приходит событие о вложенном элементе, получить родительский div через свойства невозможно
        // поэтому если не div то всплываем
        while (obj.tagName != 'DIV') {
            obj = obj.parentNode;
        }
    }
    var part = obj.getAttribute('part');
    if (part != 'add_link_field') {
        $(".go_event_link").fadeOut(200, function () {
            setTimeout("$('.save_event_button').fadeIn(200)", 200);
        });
    }

    return; // Дальнейшая обработка не требуется

    if (event.shiftKey) // Для правильной установки фокуса на span - чтобы появлялся курсор
    {
        var t = obj.previousSibling;
        if (typeof(t.onfocus) !== 'undefined') t.tabIndex = '';
    }
    else {
        var t = obj.previousSibling;
        if (typeof(t.onfocus) !== 'undefined') t.tabIndex = '800';
    }
    if ((event.keyCode == 0xA) || (event.keyCode == 0xD)) {
        if (event.ctrlKey) { // если нажат контрол, полюбому теряем фокус
            obj.blur();
            return false;
        }
        if (obj.getAttribute("mult_value") == 1) {
            if ((navigator.appVersion.indexOf("MSIE") != -1) || (navigator.userAgent.indexOf("Opera") != -1)) { // тк. они некорректно вставляют перенос строки
                // IE и OPERA явно вставляем br
                if (window.event) {
                    window.event.cancelBubble = true;
                    window.event.returnValue = false;
                }
                else {
                    event.stopPropagation();
                    event.cancelBubble = true;
                    event.returnValue = false;
                }
                insert_next_line(obj);
                return false;
            }
            else {
                return true;
            }
        }
        else {
            obj.blur();
            return false;
        }
    }
    else if (event.keyCode == 32 && navigator.userAgent.indexOf('Chrome')) { // Если текстовое поле не ограничено по ширине, то при вставке стандартного пробела происходит баг с сокращением последнего пробела
        var space_span = document.createTextNode('\u2009');
        insertTextAtCursor(" ", space_span);
        if (window.event) {
            window.event.cancelBubble = true;
            window.event.returnValue = false;
        }
        else {
            event.stopPropagation();
            event.cancelBubble = true;
            event.returnValue = false;
        }
        return false;
    }
}

function onmousedown_text(event) {
    var obj = event.currentTarget;
    if (!obj) {
        obj = event.srcElement;
        // IE как всегда в ударе, подписываем на событие div а приходит событие о вложенном элементе, получить родительский div через свойства невозможно
        // поэтому если не div то всплываем
        while (obj.tagName != 'DIV') {
            obj = obj.parentNode;
        }
    }
    if (event.ctrlKey) { // если нажат контрол, то срабатывает как обычная таблица
    }
    else {
        obj.contentEditable = true;
        dont_open_view = 2;
    }
}

function onfocus_text(event) {
    var obj = event.target;
    if (!obj) {
        obj = event.srcElement;
        // IE как всегда в ударе, подписываем на событие div а приходит событие о вложенном элементе, получить родительский div через свойства невозможно
        // поэтому если не div то всплываем
        while (obj.tagName != 'DIV') {
            obj = obj.parentNode;
        }
    }
    obj.previousSibling.tabIndex = obj.getAttribute('tabindex');
    obj.style.border = '1px dotted #a0a0a0';
    fast_edit_old = obj.getAttribute('field_id') + '|' + obj.getAttribute('line_id') + '|' + obj.innerHTML;
}

function onblur_text(event) {
    var obj = event.target;
    if (!obj) {
        obj = event.srcElement;
        // IE как всегда в ударе, подписываем на событие div а приходит событие о вложенном элементе, получить родительский div через свойства невозможно
        // поэтому если не div то всплываем
        while (obj.tagName != 'DIV') {
            obj = obj.parentNode;
        }
    }
    var part = obj.getAttribute('part');
    obj.previousSibling.tabIndex = obj.getAttribute('tabindex');
    obj.contentEditable = false;
    obj.style.border = '';
    if (part == 'add_link_field') return; // Ничего не сохраняем
    if (fast_edit_old != obj.getAttribute('field_id') + '|' + obj.getAttribute('line_id') + '|' + obj.innerHTML) {
        obj.style.background = '#fff6ad';
        $(obj).attr('yellow_color', '1');
        save_value(obj.getAttribute('field_id'), obj.getAttribute('line_id'), obj.innerHTML);
    }
    else
        obj.style.background = '';
}

//
//  ПОЛЕ ФАЙЛ
//
function addHandler_file(obj) {
    addHandler(obj, "onmouseover", onmouseover_file);
    addHandler(obj, "onmouseout", onmouseout_file);
    addHandler(obj.nextSibling, "onclick", ondrop_file);
};

function onmouseover_file(event) {
    var obj = event.target;
    if (!obj) obj = event.srcElement;
    if (obj.tagName != "A") obj = obj.parentNode; // Разворачиваем вверх, т.к. это изображение
    obj.nextSibling.className = "b_drop";
    var field_id = obj.getAttribute('field_id');
    var line_id = obj.getAttribute('line_id');
    document.getElementById("add_file_url_" + field_id + "_" + line_id).className = "sub_fast_edit_file_url_hover";
}

function onmouseout_file(event) {
    var obj = event.target;
    if (!obj) obj = event.srcElement;
    if (obj.tagName != "A") obj = obj.parentNode; // Разворачиваем вверх, т.к. это изображение
    obj.nextSibling.className = "b_drop_hoverpopup";
    var field_id = obj.getAttribute('field_id');
    var line_id = obj.getAttribute('line_id');
    document.getElementById("add_file_url_" + field_id + "_" + line_id).className = "sub_fast_edit_file_url";
}

function ondrop_file(event) {
    $(".go_event_link").fadeOut(200, function () {
        setTimeout("$('.save_event_button').fadeIn(200)", 200);
    });
    var obj = event.target;
    if (!obj) obj = event.srcElement;
    var prev = obj.previousSibling; // A href
    var part = obj.getAttribute('part');
    if (part == 'add_link_field') return; // Ничего не сохраняем
    jconfirm(lang.Delete_file + " " + obj.previousSibling.title + " ?",
        function () {
            var field_id = prev.getAttribute('field_id');
            var line_id = prev.getAttribute('line_id');
            var f_name = prev.title;

            prev.style.background = "#fff6ad";
            obj.style.backgroundColor = "#fff6ad";
            obj.style.borderLeft = "7px solid #fff6ad";
            ajax_update.format = 0;
            ajax_update.method = "POST";
            ajax_update.call("sel=drop_file&field=" + field_id + "&line=" + line_id + "&fname=" + encodeURIComponent(f_name) + "&csrf=" + csrf, function (resp) {
                var res_arr = resp.toString().split("|");
                var field_id = res_arr[2];
                var line_id = res_arr[3];
                if (res_arr[0] == "deleted" || res_arr[0] == "message") { // удаляем из списка
                    obj.parentNode.parentNode.removeChild(obj.parentNode);
                }
                var resp_arr = str_replace("\n", "\r\n", resp.toString()).split("\r\n");
                UpdateData(resp_arr);
            });
        });
}

// Загрузка файла
var upload_in_progress = 0;

function check_upload_file(field_id, obj) {
    if (obj.files) {
        var f_pos;
        for (f_pos = 0; f_pos < obj.files.length; f_pos++) {
            var upl_files = obj.files[f_pos];
            name = upl_files.name;
            size = upl_files.size;
            ext_pos = name.lastIndexOf(".");
            ext = name.substr(ext_pos + 1).toLowerCase();
            f_t = all_fields[field_id]['file_types'];
            if (typeof f_t[0] != 'undefined') {
                var i = 0;
                var ext_str = '';
                for (var key in f_t) {
                    if (f_t[key] == ext) i++;
                    ext_str = ext_str + f_t[key] + ', ';
                }
                if (i == 0) {
                    len = ext_str.length;
                    ext_str = ext_str.substr(0, len - 2);
                    jalert(name + ' ' + lang.wrong_extension + ' ' + ext_str);
                    return false;
                }
            }
            if (all_fields[field_id]['max_size'] != 0 && all_fields[field_id]['max_size'] * 1024 < size) {
                jalert(name + ' ' + lang.File_max_size + ' ' + all_fields[field_id]['max_size']);
                return false;
            }
        }
    }
    return true;
}


function sub_add_file(field_id, line_id, obj) {
    if (check_upload_file(field_id, obj) === false) return false;
    var value = obj.value;
    var part = obj.getAttribute('part');
    var file_img = obj.getAttribute('file_img');
    var progress_span = "<span class='upload_progress'></span>";
    var bg = "style='background:#fff6ad'";
    if (part == 'add_link_field') {
        progress_span = "";
        bg = "";
    }
    if (obj.files) { // Новый режим многофайловость
        var i;
        for (i = 0; i < obj.files.length; i++) {
            value = obj.files[i].fileName;
            if (typeof(value) == 'undefined') value = obj.files[i].name;
            var new_line;
            if (file_img) new_line = $("<span style='white-space:nowrap;'><a href=\"open_file.php?field=" + field_id + "&line=" + line_id + "&file=" + encodeURIComponent(value) + "&show=1\" onclick='jalert(\"" + lang.file_wasnt_upload + "\");return false;' id='new_file_upload_" + field_id + "_" + line_id + "_" + i + "' " + bg + " file_img=1 title='" + value + "'>" + value + "</a>" + progress_span + "<span><br />");
            else new_line = $("<span style='white-space:nowrap;'><a href=\"open_file.php?field=" + field_id + "&line=" + line_id + "&file=" + encodeURIComponent(value) + "\" onclick='jalert(\"" + lang.file_wasnt_upload + "\");return false;' id='new_file_upload_" + field_id + "_" + line_id + "_" + i + "' " + bg + " title='" + value + "'>" + value + "</a>" + progress_span + "</span> ");
            $(document.getElementById("fast_edit_span_" + field_id + "_" + line_id + "_0")).append(new_line);
        }
    }
    else {  // Старый режим
        // Если указан полный путь оставляем только имя файла
        var last_slash = -1;
        var last_slash_p1 = 0;
        var last_slash_p2 = -1;
        while (1) {
            last_slash_p2 = value.indexOf('\\', last_slash_p1);
            if (last_slash_p2 == -1) break;
            last_slash_p1 = last_slash_p2 + 1;
            last_slash = last_slash_p2;
        }
        if (last_slash != -1) {
            value = value.substr(last_slash + 1, 1024 * 1024);
        }
        ;

        var new_line;
        if (file_img) new_line = $("<span style='white-space:nowrap;'><a class=\"href_post\" href=\"open_file.php?field=" + field_id + "&line=" + line_id + "&file=" + encodeURIComponent(value) + "&show=1\" onclick='jalert(\"" + lang.file_wasnt_upload + "\");return false;' id='new_file_upload_" + field_id + "_" + line_id + "_0' " + bg + " file_img=1 title='" + value + "'>" + value + "</a>" + progress_span + "</span><br />");
        else new_line = $("<span style='white-space:nowrap;'><a class=\"href_post\" href=\"open_file.php?field=" + field_id + "&line=" + line_id + "&file=" + encodeURIComponent(value) + "\" onclick='jalert(\"" + lang.file_wasnt_upload + "\");return false;' id='new_file_upload_" + field_id + "_" + line_id + "_0' " + bg + " title='" + value + "'>" + value + "</a>" + progress_span + "</span> ");
        $(document.getElementById("fast_edit_span_" + field_id + "_" + line_id + "_0")).append(new_line);
    }
    if (part == 'add_link_field') return; // Ничего не сохраняем
    upload_in_progress = 1;
    document.getElementById("sbmt_file_" + field_id + "_" + line_id).submit();
    obj.value = "";

}

function onupload_file_done(event) {
    $(".go_event_link").fadeOut(200, function () {
        setTimeout("$('.save_event_button').fadeIn(200)", 200);
    });
    var obj = event.target;
    if (!obj) obj = event.srcElement;
    var file_img;
    var fname;

    var resp = obj.contentDocument.body.innerHTML;
    var resp_arr = str_replace("\n", "\r\n", resp.toString()).split("\r\n");
    if (upload_in_progress) {
        upload_in_progress = 0;
        obj.src = "";
        // Проверка на ограничение размера
        var max_size_text = 'Warning: POST Content-Length of';
        var max_bytes_text = 'bytes exceeds the limit of ';
        var max_size_flag = 0;
        var pos = resp.indexOf(max_size_text);
        if (pos != -1) { // Превышен максиамальный размер файла
            var p2 = resp.indexOf(max_bytes_text, pos);
            var max_size_mb = 'unknown';
            if (p2 != -1) {
                var msz = resp.substr(p2 + max_bytes_text.length, 1024);
                max_size_mb = intval(msz) / 1024 / 1024;
                max_size_mb = intval(max_size_mb) + (intval(max_size_mb * 10) / 10 - intval(max_size_mb)); // Округляем до десятых
            }
            jalert(lang.max_filesize_exceeded + ' ' + max_size_mb + ' ' + lang.megabytes + '.');
            max_size_flag = 1;
            var p2 = resp.indexOf('saved|');
            resp = resp.substr(p2, 1024);
        }
        resp = trim(resp, "\r\n");
        var res_arr = resp.toString().split("|");
        var field_id = res_arr[2];
        let table_id = all_fields[field_id]['table_id'];
        var line_id = res_arr[3];
        var i;
        var n_el;
        var drop_cur_file;
        if (res_arr[0] == "saved" || res_arr[0] == "message") { // файл успешно обработан, убираем индикатор загрузки
            for (i = 0; i < 100000; i++) {
                if (!document.getElementById("new_file_upload_" + field_id + "_" + line_id + "_" + i)) break;
                n_el = document.getElementById("new_file_upload_" + field_id + "_" + line_id + "_" + i);
                fname = n_el.getAttribute('title');
                file_img = n_el.getAttribute('file_img');
                drop_cur_file = 0;
                if (max_size_flag) { // файл не сохранился, убираем его из списка
                    drop_cur_file = 1;
                }
                else {
                    // проверяем является ли файл дубликатом
                    $(n_el.parentNode.parentNode).find("a").each(function () {
                        if (this != n_el) {

                            if (this.innerHTML == n_el.innerHTML) {  // такой файл уже есть, убираем как дубликат
                                drop_cur_file = 1;
                            }
                        }
                    })
                }
                if (drop_cur_file) { // удаляем файл из списка
                    n_el.parentNode.parentNode.removeChild(n_el.parentNode);
                }
                else {
                    // Меняем стиль файла на обычный
                    n_el.id = "";
                    n_el.style.background = '';
                    n_el.setAttribute('field_id', field_id);
                    n_el.setAttribute('line_id', line_id);
                    if (file_img) {
                        n_el.onclick = "image_window=window.open('open_file.php?field=" + field_id + "&line=" + line_id + "&file=" + encodeURIComponent(fname) + "&show=1','','width=,height=,menubar=1,scrollbars=1,resizable=1,status=1');image_window.focus();return false;";
                        n_el.innerHTML = "<img src='cache/" + table_id + "_" + field_id + "_" + line_id + "_" + utf2eng(fname) + ".png' class='sub_fast_edit_img'>";
                    }
                    else
                        n_el.onclick = "";
                    n_el.nextSibling.className = "b_drop_hoverpopup";
                    addHandler_file(n_el);
                }
            }
            UpdateData(resp_arr);
        }
    }
    return;
};


//
//   ПОЛЕ СВЯЗЬ
//

function calendar_combobox(combobox, add_event) {
    if (combobox.length > 0) {
        combobox.each(function (i, item) {
          var f_id = $(this).attr('field_id');

          const current_field = fields ? fields.filter((item) => item.id == f_id)[0] || null : null;
          const parent_field = fields ? fields.filter((item) => item.id == (current_field?.parent_link_field || -1))[0] || null : null;

            var l_id = $(this).attr('line_id');
            var ac_val = $(item).attr('ac_link_val') || $(`#fast_edit_span_${current_field?.parent_link_field || -1}_new_0`).attr('ac_link_val');
            var url;
            if (typeof ac_val !== 'undefined' && ac_val != 0) {
                url = 'select_value.php?field=' + f_id + '&line=' + ac_val;
            } else {
                url = 'select_value.php?field=' + f_id + '&line=' + l_id;
            }
            autocomplete_ajax_request($(this), url, false,false,0,'',{}, true);
            var field_id = $(item).attr('field_id');
            var parent_link = $(item).attr('filter_field') || current_field?.parent_link_field || null;
            create_combobox($(item));
            var input = $(this).next().find($('.autocomplete__input'));
            var btn = input.next();
            var autocomplete = $(item).next().find('.autocomplete__input');
            var autocomplete_btn = autocomplete.next();
            install_autocomplete_value($(item));
            install_combobox_val(item);
            var timeout;

            input.on('keyup', function(e) {
                var word = '&q=';
                if (input.val() !== '') {
                    word += encodeURIComponent(input.val());
                }
                var url_by_word = 'select_value.php?field=' + f_id + '&line=' + l_id + word;
                if (e.keyCode === 17 || e.keyCode === 18 || e.keyCode === 16 || e.keyCode === 27 || e.keyCode === 40 || e.keyCode === 37 ||
                    e.keyCode === 38 || e.keyCode === 39 || e.keyCode === 9 || e.keyCode === 20 || e.keyCode === 13) {
                    return;
                }
                clearTimeout(timeout);
                timeout = setTimeout(function() {
                    autocomplete_ajax_request($(item), url_by_word, false, true);
                }, 100);
            });


            var onAutocompleteClick = function () {

            };

            if (parent_link && parent_link !== '') {
                onAutocompleteClick = function () {
                    var parent_obj =  $('select[field_id="' + parent_link + '"]');
                    var filter_id = $(parent_obj).attr('filter_field') || parent_field?.s_filter_id || 0;
                    var ac_data = {
                        table_id: 0,
                        filter_id: filter_id,
                        field_id: field_id,
                        line_id: 'new',
                        has_parent: true,
                        parent_obj: parent_obj
                    };
                    autocomplete_ajax_request($(item), url, false, true, 0, parent_obj.attr('ac_link_val'), ac_data);
                    autocomplete_btn.off('click', autocomplete_btn);
                }
                autocomplete.click(onAutocompleteClick);
                autocomplete_btn.click(onAutocompleteClick);
            } else {
                onAutocompleteClick = function () {
                    autocomplete_ajax_request($(item), url, false, true, 0);
                    autocomplete_btn.off('click', onAutocompleteClick);
                };
                autocomplete.on('click', onAutocompleteClick);
                autocomplete_btn.on('click', onAutocompleteClick);
            }
        });
    }
}

function sub_fix_lnk_result(event, data, formatted) {
    this.setAttribute('f_value', data[0]);
    $(this).change();
}
/// ================= Обработчкики событий ================


// сохранить значение
function save_value(field_id, line_id, val) {
    fast_edit_old = field_id + "|" + line_id + "|" + val;
    var new_val = encodeURIComponent(val);
    ajax_update.format = 0;
    ajax_update.method = "POST";
    ajax_update.call("calendar=" + my_event_id + "&field=" + field_id + "&line=" + line_id + "&value=" + new_val + "&csrf=" + csrf, ComRespSaveSub);
}

// сохранить значение мулти-селекта
function save_value_multi(field_id, line_id, full_value) {
    var new_val = encodeURIComponent(full_value);
    ajax_update.format = 0;
    ajax_update.method = "POST";
    ajax_update.call("calendar=1&field=" + field_id + "&line=" + line_id + "&value=" + new_val + "&csrf=" + csrf, ComRespSaveMultSub);
};

function sub_drop_line(table_id, line_id) {
    ajax_update.format = 0;
    ajax_update.method = "POST";
    ajax_update.call("calendar=1&sel=delete_line&table_id=" + table_id + "&line_id=" + line_id + "&csrf=" + csrf, ComRespDropSub);
};

function ComRespDropSub(resp) {
    resp = trim(resp, "\r\n")
    var resp_arr = resp.toString().split("\r\n");
    var res_arr = resp_arr[0].toString().split("|");
    var drop_table_id = res_arr[1];
    var drop_line_id = res_arr[2];
    if (res_arr[0] == "cancel_delete") {
        // var v_o=document.getElementById('subtable_'+cur_subtable_id+"_line_"+drop_line_id);
        // v_o.style.background='#ffadad';
        //  $(v_o).stop().animate({ backgroundColor: '#FFFFFF' },1500).css('background','');
        jalert(lang.Canceled);
        // Обновляем, если были вычисления
        if (typeof resp_arr != 'undefined') UpdateData(resp_arr);
    }
    if (res_arr[0] == "deleted" || res_arr[0] == "message") {
        window.location.reload(); // Временная функция
        return;                   // В этом месте необходимо вызывать перестроение календаря
        var v_o = document.getElementById('subtable_' + cur_subtable_id + "_line_" + drop_line_id);
        if ((v_o) && (typeof(v_o.parentNode) !== 'undefined')) // Защита от быстрого двойного щелчка удалить
            v_o.parentNode.removeChild(v_o);
        if ($("#subtable_" + cur_subtable_id + " [id ^= subtable_" + cur_subtable_id + "_line_]").size() == 0 && cur_mode == "view") $(".small_green_save").fadeOut();
        // Обновляем, если были вычисления
        if (typeof resp_arr != 'undefined') UpdateData(resp_arr);
    }
}

var pageReload = false;
// Обновить данные на странице
function UpdateData(resp_arr) {
    var field;
    var type_field;
    var n_tmp;
    var n;
    var n2;
    var last_select;
    var select_obj;
    var int_val;
    var dspl_val;
    var saved_field;
    var date_fields = new Object;
    var updatedLines = new Object;
    var messagesObj = new Object;
    var messagesNum = new Object;
    var pageReloadIns = false;
    for (keyVar in resp_arr) {  // меняем также значения которые изменились в результате вычислений
        res_arr = resp_arr[keyVar].toString().split("|");
        field_id = res_arr[1];
        line_id = res_arr[2];
        new_value_arr = res_arr[3].split(" ");
        new_value = Base64.decode(new_value_arr[0]);
        saved_field = false;

        if (res_arr[0] == "message") {
            var mStatus = field_id;
            var mNum = line_id;
            var mText = new_value
            if (!messagesObj[mStatus]) {
                messagesObj[mStatus] = new Object;
                messagesNum[mStatus] = mNum;
            }
            if (messagesNum[mStatus] < mNum)
                messagesNum[mStatus] = mNum;
            messagesObj[mStatus][mNum] = mText;
            continue;
        }
        if (res_arr[0] == "inserted") {
            inserted_table_id = field_id;
            inserted_line_id = line_id;
            pageReloadIns = true;
        }
        if (res_arr[0] == "saved"||res_arr[0] == "error") { // Приводим действие 'saved' к виду changed
            field_id = res_arr[2];
            line_id = res_arr[3];
            new_value = Base64.decode(res_arr[4]);
            if (res_arr[5])
                link_add_value = Base64.decode(res_arr[5]);
            else
                link_add_value = false;
            res_arr[0] = "changed";
            saved_field = true;
        }

        if (res_arr[0] == "changed") {
            field = all_fields[field_id];
            if (!field) continue;
            view_field = field['view'];
            type_field = field['type_field'];

            if (calendar.day)
                for (oneEventId in all_calendar_events)
                    if (all_calendar_events[oneEventId]['group_field'] == field_id)
                        pageReload = true;

            if ((type_field == 2 || type_field == 12) && new_value != "") {
                var new_date = new_value.substr(0, 10);
                var dateParts = new_date.split(".");
                var new_time = new_value.substr(11, 5);
                if (new_time)
                    var timeParts = new_time.split(":");
                else
                    var timeParts = ['00', '00'];

                for (oneEventId in all_calendar_events) {
                    oneEvent = all_calendar_events[oneEventId];
                    if (field_id != oneEvent.date_field && field_id != oneEvent.period_field) continue;
                    if (js_events[oneEventId] == undefined) continue;
                    if (js_events[oneEventId][line_id] == undefined) continue;

                    pageReload = true;
                    /*
                     updatedLines[oneEventId] = {};
                     updatedLines[oneEventId][line_id] = 1;

                     if (calendar.day||calendar.week)
                     jsDateValue = dateParts[2]+ "" + dateParts[1] + "" + dateParts[0]+timeParts[0]+timeParts[1];
                     else
                     jsDateValue = dateParts[2]+ "" + dateParts[1] + "" + dateParts[0];

                     if (field_id == oneEvent.date_field)
                     js_events[oneEventId][line_id]['begin'] = jsDateValue;
                     else
                     js_events[oneEventId][line_id]['end'] = jsDateValue;
                     */
                }
            }
            v_o = document.getElementById("fast_edit_span_" + field_id + "_" + line_id + "_0");
            if (!v_o) { // Нередактируемое поле
                v_o = document.getElementById("sub_cell_" + field_id + "_" + line_id);
                if (!v_o) continue;
                if (type_field == 5) {
                    n_tmp = new_value.split("|");
                    int_val = n_tmp[0];
                    dspl_val = n_tmp[1];
                    if (view_field) {
                        v_o.innerHTML = '<a href="view_line2.php?table=' + field['table_id'] + '&line=' + int_val + '&back_url={$base64_current_url}">' + dspl_val + '</a>';
                    }
                    else {
                        v_o.innerHTML = dspl_val;
                    }
                    $(".mount_cell_" + field_id + "_" + line_id).each(function (indx, element) {
                        if (link_add_value)
                            mountValue = link_add_value;
                        else
                            mountValue = dspl_val;
                        if (mountValue == "") mounValue = "&nbsp;";
                        this.innerHTML = strip_tags(mountValue);
                    });
                }
                else {
                    if (field['view_html'] == "0")
                        v_o.innerHTML = new_value;
                    $(".mount_cell_" + field_id + "_" + line_id).each(function (indx, element) {
                        mountValue = str_replace("<br>", " ", str_replace("<br/>", " ", str_replace("<br />", " ", new_value)));
                        if (mountValue == "") mountValue = "&nbsp;";
                        this.innerHTML = strip_tags(mountValue);
                    });
                }
                v_o.style.background = '#fff6ad';
                $(v_o).attr('yellow_color', '0').stop().animate({backgroundColor: '#FFFFFF'}, 1500).css('background', '')
                if (type_field == 3) {
                    // выполняем Javascript если он вписан и поле - отображать html
                    if (field['view_html']) {
                        var re = /<script>([\s\S]*?)<\/script>/gi;
                        js_result = new_value.match(re);
                        if (js_result) {
                            var o_str, s_len = String('<script>').length;
                            for (keyVar in js_result) {
                                if (intval(keyVar) != keyVar) continue; // Фикс для ie, т.к. могут быть не только числа
                                o_str = js_result[keyVar];
                                o_str = o_str.substr(s_len, o_str.length - s_len * 2 - 1);
                                eval(o_str);
                            }
                        }
                    }
                }
                continue;
            }

            // Формируем вывод
            if ((type_field == 1) || (type_field == 2) || (type_field == 12) || (type_field == 3) || (type_field == 10)) { // Число, строка, дата
                if ((type_field == 2) || (type_field == 12)) new_value = new_value.substr(0, 16);
                if (typeof(v_o.value) === 'undefined' && field['view_html'] == "0") {
                    v_o.innerHTML = new_value;
                }
                else if (field['view_html'] == "0")
                    v_o.value = new_value;

                $(".mount_cell_" + field_id + "_" + line_id).each(function (indx, element) {
                    mountValue = str_replace("<br>", " ", str_replace("<br/>", " ", str_replace("<br />", " ", new_value)));
                    if (mountValue == "") mountValue = "&nbsp;";
                    this.innerHTML = strip_tags(mountValue);
                });
                v_o.style.background = '#fff6ad';
                $(v_o).attr('yellow_color', '0').stop().animate({backgroundColor: '#FFFFFF'}, 1500).css('background', '')
            }
            if (type_field == 7 || type_field == 11 || type_field == 14) { // Пользователь, группа
                if (saved_field) {
                    if (field['mult_value'] > 0) {
                        real_value = "";
                        all_id_values = new_value.split('\r\n');
                        vals_count = all_id_values.length;
                        for (v = 0; v < vals_count; v++)
                            real_value += field['s_list_values'][all_id_values[v]] + " "

                    }
                    else real_value = field['s_list_values'][new_value];
                }
                else if (field['mult_value'] > 0)
                    real_value = str_replace("\r\n", " ", new_value);
                else
                    real_value = new_value;

                $(".mount_cell_" + field_id + "_" + line_id).each(function (indx, element) {
                    if (real_value == "") real_value = "&nbsp;";
                    this.innerHTML = real_value;
                });

                if (saved_field) continue;
                if (field['mult_value'] > 0) {
                    all_text_values = new_value.split('\r\n');
                    insVal = str_replace("\r\n", " ", new_value);
                    $(".mount_cell_" + field_id + "_" + line_id).each(function (indx, element) {
                        if (insVal == "") insVal = "&nbsp;"
                        this.innerHTML = insVal;
                    });

                    values_count = all_text_values.length;
                    var all_list_count;
                    all_values = [];
                    n = 0;

                    // Формируем значение в скрытом поле
                    if (new_value == "") {
                        document.getElementById("fast_edit_span_" + field_id + "_" + line_id + "_0").value = "";
                        values_count = 0;
                    }
                    else {
                        var hidden_val = "-";
                        for (l = 0; l < values_count; l++) {
                            all_list_count = 0;
                            for (v_id in field['s_list_values']) {
                                all_list_count += 1;
                                if (all_text_values[l] == field['s_list_values'][v_id]) {
                                    hidden_val += v_id + "-";
                                    all_values[l] = v_id;
                                }
                            }
                        }
                        document.getElementById("fast_edit_span_" + field_id + "_" + line_id + "_0").value = hidden_val;
                    }

                    // Устанавливаем значения
                    $('select[multi_select_group=' + field_id + '_' + line_id + ']').each(function (i) {
                        select_obj = this;
                        last_select = select_obj;
                        n_tmp = select_obj.nextSibling;
                        n_tmp.style.background = '#fff6ad';
                        $(n_tmp).attr('yellow_color', '0').stop().animate({backgroundColor: '#FFFFFF'}, 1500).css('background', '')
                        if (n < values_count) {  // устанавливаем значение
                            select_obj.value = all_values[n];
                            n_tmp.innerHTML = all_text_values[n];
                            select_obj.setAttribute("is_last", 0);
                            $(select_obj).children().each(function (y) {
                                if (this.value != select_obj.value) { // не выбранный елемент
                                    if (in_array(this.value, all_values)) {
                                        this.style.display = 'none';
                                        this.setAttribute('disabled', 'disabled');
                                    }
                                    else {
                                        this.style.display = '';
                                        this.setAttribute('disabled', '');
                                        this.removeAttribute('disabled');
                                    }
                                }
                                else {
                                    this.selected = true;
                                    this.style.display = '';
                                    this.setAttribute('disabled', '');
                                    this.removeAttribute('disabled');
                                }
                            });
                        }
                        else if (n == values_count) { // пустое значение в конце
                            if (type_field == 14)
                                select_obj.value = "";
                            else
                                select_obj.value = 0;
                            n_tmp.innerHTML = "";
                            select_obj.setAttribute("is_last", 1);
                            $(select_obj).children().each(function (y) {
                                if (in_array(this.value, all_values)) {
                                    this.style.display = 'none';
                                    this.setAttribute('disabled', 'disabled');
                                }
                                else {
                                    this.style.display = '';
                                    this.setAttribute('disabled', '');
                                    this.removeAttribute('disabled');
                                }
                            });
                        }
                        else { // лишние значения, удаляем
                            var t_span = select_obj.parentNode;
                            t_span.parentNode.removeChild(t_span);
                        }
                        n++;
                    });

                    // если новых элементов больше чем существующих - добавляем
                    for (n; n <= values_count; n++) {
                        // Создание элемента, на основе копирования последнего
                        var obj_tag_name = last_select.tagName;
                        var new_id = "fast_edit_span_" + field_id + "_" + line_id + "_" + n + "0";
                        var newEL = document.createElement(obj_tag_name);
                        newEL.id = new_id;
                        newEL.className = "";
                        // события
                        addHandler_mult_select(newEL);

                        // атрибуты
                        newEL.setAttribute("multi_select_group", field_id + "_" + line_id);
                        newEL.setAttribute("style", last_select.getAttribute("style"));
                        newEL.style.background = '';
                        newEL.setAttribute("field_id", field_id);
                        newEL.setAttribute("line_id", line_id);
                        newEL.setAttribute("pos", n);
                        newEL.setAttribute("is_last", 1);
                        newEL.setAttribute("tabindex", last_tabindex_fast_edit);
                        $(newEL).html(last_select.innerHTML);
                        newEL.selectedIndex = -1;

                        // вставляем после текущего
                        var t_span = last_select.parentNode;
                        var next_node = t_span.nextSibling;
                        //alert(next_node.innerHTML);
                        t_span.parentNode.insertBefore(newEL, next_node);
                        $(newEL).attr('add_width', '0');
                        form_fast_select_obj(newEL);

                        var select_obj = newEL;
                        var last_select = select_obj;
                        n_tmp = select_obj.nextSibling;

                        if (n < values_count) {  // устанавливаем значение
                            select_obj.value = all_values[n];
                            n_tmp.innerHTML = all_text_values[n];
                            select_obj.setAttribute("is_last", 0);
                            $(select_obj).children().each(function (y) {
                                if (this.value != select_obj.value) { // не выбранный елемент
                                    if (in_array(this.value, all_values)) {
                                        this.style.display = 'none';
                                        this.setAttribute('disabled', 'disabled');
                                    }
                                    else {
                                        //this.selected = true;
                                        this.style.display = '';
                                        this.setAttribute('disabled', '');
                                        this.removeAttribute('disabled');
                                    }
                                }
                                else {
                                    this.style.display = '';
                                    this.setAttribute('disabled', '');
                                    this.removeAttribute('disabled');
                                }
                            });
                        }
                        else if (n == values_count) { // пустое значение в конце
                            var options_count = 0;
                            if (type_field == 14)
                                select_obj.value = "";
                            else
                                select_obj.value = 0;
                            select_obj.setAttribute("is_last", 1);
                            $(select_obj).children().each(function (y) {
                                if (in_array(this.value, all_values)) {
                                    this.style.display = 'none';
                                    this.setAttribute('disabled', 'disabled');
                                }
                                else {
                                    this.style.display = '';
                                    this.setAttribute('disabled', '');
                                    this.removeAttribute('disabled');
                                    if ((type_field == 14 && this.value != '') || ((type_field == 7 || type_field == 11) && this.value != 0)) options_count++;
                                }
                            });

                            if (options_count == 0) // Не одного видимого элемента, скрываем котрол
                            {
                                select_obj.parentNode.style.display = 'none';
                                select_obj.style.display = 'none';
                            }
                        }
                    }
                }
                else { // Обычный список
                    // Ищем id
                    var new_value_id = 0;
                    for (v_id in field['s_list_values'])
                        if (new_value == field['s_list_values'][v_id]) new_value_id = v_id;

                    v_o.value = new_value_id;
                    n_tmp = v_o.nextSibling;
                    n_tmp.innerHTML = new_value;
                    $(n_tmp).attr('yellow_color', '1');
                    n_tmp.style.background = '#fff6ad';
                    $(n_tmp).attr('yellow_color', '0').stop().animate({backgroundColor: '#FFFFFF'}, 1500).css('background', '')
                    // substr_select_span(v_o.id)
                }
            }
            if (type_field == 4) { // Список
                if (field['mult_value'] > 0) { // Мультисписок
                    all_values = new_value.split('\r\n');
                    insVal = str_replace("\r\n", " ", new_value);
                    $(".mount_cell_" + field_id + "_" + line_id).each(function (indx, element) {
                        if (insVal == "") insVal = "&nbsp;";
                        this.innerHTML = insVal;
                    });
                    values_count = all_values.length;
                    n = 0;

                    // Формируем значение в скрытом поле
                    if (new_value == "") {
                        document.getElementById("fast_edit_span_" + field_id + "_" + line_id + "_0").value = "";
                        values_count = 0;
                    }
                    else
                        document.getElementById("fast_edit_span_" + field_id + "_" + line_id + "_0").value = new_value;

                    // Устанавливаем значения
                    $('select[multi_select_group=' + field_id + '_' + line_id + ']').each(function (i) {
                        select_obj = this;
                        last_select = select_obj;
                        n_tmp = select_obj.nextSibling;
                        n_tmp.style.background = '#fff6ad';
                        $(n_tmp).attr('yellow_color', '0').stop().animate({backgroundColor: '#FFFFFF'}, 1500).css('background', '')
                        if (n < values_count) {  // устанавливаем значение
                            select_obj.value = all_values[n];
                            n_tmp.innerHTML = all_values[n];
                            select_obj.setAttribute("is_last", 0);
                            $(select_obj).children().each(function (y) {
                                if (this.value != select_obj.value) { // не выбранный елемент
                                    if (in_array(this.value, all_values)) {
                                        this.style.display = 'none';
                                        this.setAttribute('disabled', 'disabled');
                                    }
                                    else {
                                        this.style.display = '';
                                        this.setAttribute('disabled', '');
                                        this.removeAttribute('disabled');
                                    }
                                }
                                else {
                                    this.style.display = '';
                                    this.setAttribute('disabled', '');
                                    this.removeAttribute('disabled');
                                }
                            });
                        }
                        else if (n == values_count) { // пустое значение в конце
                            select_obj.value = "";
                            n_tmp.innerHTML = "";
                            select_obj.setAttribute("is_last", 1);
                            $(select_obj).children().each(function (y) {
                                if (in_array(this.value, all_values)) {
                                    this.style.display = 'none';
                                    this.setAttribute('disabled', 'disabled');
                                }
                                else {
                                    this.style.display = '';
                                    this.setAttribute('disabled', '');
                                    this.removeAttribute('disabled');
                                }
                            });
                        }
                        else { // лишние значения, удаляем
                            var t_span = select_obj.parentNode;
                            t_span.parentNode.removeChild(t_span);
                        }
                        n++;
                    });

                    // если новых элементов больше чем существующих - добавляем
                    for (n; n <= values_count; n++) {
                        // Создание элемента, на основе копирования последнего
                        var obj_tag_name = last_select.tagName;
                        var new_id = "fast_edit_span_" + field_id + "_" + line_id + "_" + n + "0";
                        var newEL = document.createElement(obj_tag_name);
                        newEL.id = new_id;
                        newEL.className = "";
                        // события
                        addHandler_mult_select(newEL);

                        // атрибуты
                        newEL.setAttribute("multi_select_group", field_id + "_" + line_id);
                        newEL.setAttribute("style", last_select.getAttribute("style"));
                        newEL.style.background = '';
                        newEL.setAttribute("field_id", field_id);
                        newEL.setAttribute("line_id", line_id);
                        newEL.setAttribute("pos", n);
                        newEL.setAttribute("is_last", 1);
                        newEL.setAttribute("tabindex", last_tabindex_fast_edit);
                        $(newEL).html(last_select.innerHTML);
                        newEL.selectedIndex = -1;

                        // вставляем после текущего
                        var t_span = last_select.parentNode;
                        var next_node = t_span.nextSibling;
                        t_span.parentNode.insertBefore(newEL, next_node);
                        $(newEL).attr('add_width', '0');
                        form_fast_select_obj(newEL);

                        select_obj = newEL;
                        last_select = select_obj;
                        n_tmp = select_obj.nextSibling;

                        if (n < values_count) {  // устанавливаем значение
                            select_obj.value = all_values[n];
                            n_tmp.innerHTML = all_values[n];
                            select_obj.setAttribute("is_last", 0);
                            $(select_obj).children().each(function (y) {
                                if (this.value != select_obj.value) { // не выбранный елемент
                                    if (in_array(this.value, all_values)) {
                                        this.style.display = 'none';
                                        this.setAttribute('disabled', 'disabled');
                                    }
                                    else {
                                        this.style.display = '';
                                        this.setAttribute('disabled', '');
                                        this.removeAttribute('disabled');
                                    }
                                }
                                else {
                                    this.style.display = '';
                                    this.setAttribute('disabled', '');
                                    this.removeAttribute('disabled');
                                }
                            });
                        }
                        else if (n == values_count) { // пустое значение в конце
                            select_obj.value = "";
                            n_tmp.innerHTML = "";
                            select_obj.setAttribute("is_last", 1);
                            var options_count = 0;
                            $(select_obj).children().each(function (y) {
                                if (in_array(this.value, all_values)) {
                                    this.style.display = 'none';
                                    this.setAttribute('disabled', 'disabled');
                                }
                                else {
                                    this.style.display = '';
                                    this.setAttribute('disabled', '');
                                    this.removeAttribute('disabled');
                                    if (this.value != '') options_count++;
                                }
                            });

                            if (options_count == 0) // Не одного видимого элемента, скрываем котрол
                            {
                                select_obj.parentNode.style.display = 'none';
                                select_obj.style.display = 'none';
                            }
                        }

                    }
                }
                else { // Обычный список
                    v_o.value = new_value;
                    n_tmp = v_o.cloneNode([true]);
                    n_tmp.innerHTML = new_value;
                    n_tmp.style.background = '#fff6ad';
                    v_o.style.background = '';
                    $(".mount_cell_" + field_id + "_" + line_id).each(function (indx, element) {
                        if (new_value == "") new_value = "&nbsp;"
                        this.innerHTML = new_value;
                    });
                    $(n_tmp).attr('yellow_color', '0').stop().animate({backgroundColor: '#FFFFFF'}, 1500).css('background', '')
                    // substr_select_span(v_o.id)
                }
            }
            if (type_field == 5) { // Связь
                n_tmp = new_value.split("|");
                int_val = n_tmp[0];
                dspl_val = n_tmp[1];
                v_o.setAttribute('f_value', int_val);
                v_o.value = dspl_val;
                v_o.style.background = '#fff6ad';
                $(v_o).attr('yellow_color', '0').stop().animate({backgroundColor: '#FFFFFF'}, 1500).css('background', '')
                $(".mount_cell_" + field_id + "_" + line_id).each(function (indx, element) {
                    if (link_add_value)
                        mountValue = link_add_value;
                    else
                        mountValue = dspl_val;
                    if (mountValue == "") mountValue = "&nbsp;"
                    this.innerHTML = mountValue;
                });
            }
        }
    }

    var messagesCalc = false;
    for (statusId in messagesObj) {
        messagesCalc = true;
        var oneMsgGroup = messagesObj[statusId];
        var oneStatusText = "";
        for (m = 0; m <= messagesNum[statusId]; m++) {
            if (!oneMsgGroup[m]) continue;
            oneStatusText += oneMsgGroup[m] + "<br />";
        }
        if (oneStatusText != "")
            jalert(oneStatusText);
    }

    if (pageReloadIns) {
        if (messagesCalc)
            $("#jalert button").bind("click", saveHandler);
        else
            window.location.reload();
    }

    var stringChanges = false;
    for (oneEventId in updatedLines) {
        stringChanges = true; // Временная
        break;                // заглушка
    }

    if (stringChanges) {
        changeOffsets();
        fixCalendarLayout();
    }
}

// Обработка сохранения
var saveHandler = function () {
    $.ajax({
        type: "POST",
        url: "calendar.php",
        data: {
            csrf: csrf,
            id: calendar.id,
            get: 'check_line',
            data: {line_id: inserted_line_id, table_id: inserted_table_id}
        },
        success: function (input) {
            if (input == "1")
                window.location.reload();
            else {
                $("#jalert button").unbind("click", saveHandler);
                pageReload = false;
                $(".save_event_button").fadeIn(100);
            }
        }
    });
};

var calendar_error_status = 0;
var uniq_error_status = 0;
// Обработать отклик о сохранении
function ComRespSaveSub(resp) {

    if (resp == "Invalid access.") {
        jalert("Please press f5 button, for refresh login status.");
        return;
    }
    resp = trim(resp, "\r\n");
    var resp_arr = resp.toString().split("\r\n");
    for (i = 0; i < resp_arr.length; i++) {
        var res_arr = resp_arr[i].toString().split("|");
        var field_id = res_arr[2];
        var line_id = res_arr[3];
        var new_value = Base64.decode(res_arr[4]);
        var v_o;


        if (res_arr[0] == "saved") {
            calendar_error_status = 0;
            uniq_error_status = 0;
            $('[yellow_color=1]').attr('yellow_color', '0').stop().animate({backgroundColor: '#FFFFFF'}, 1500).css('background-color', '');
            // Обновляем остальные поля
            UpdateData(resp_arr);
        }
        else if (res_arr[0] == 'error') {
            // calendar_error_status = 1;
            $('[yellow_color=1]').attr('yellow_color', '0').stop().animate({backgroundColor: '#FFFFFF'}, 1500).css('background-color', '');
            jalert('Время начала события больше времени окончания!');
            UpdateData(resp_arr);
        }
        else if (resp_arr.indexOf('error1') != -1) {
            var mm = resp_arr.toString().split("|");
            var my_field = all_fields[mm[1]];

            uniq_error_status = 1;
            $('[yellow_color=1]').attr('yellow_color', '0').stop().animate({backgroundColor: '#FFFFFF'}, 1500).css('background-color', '');
            jalert(lang.fast_field_already_exists_p1 + ' ' + my_field.name_field + ' ' + lang.fast_field_already_exists_p2);
        }
    }
}


// Обработать отклик о сохранении мултивыбора
function ComRespSaveMultSub(resp) {
    var res_arr = resp.toString().split("|");
    var field_id = res_arr[2];
    var line_id = res_arr[3];
    var new_value = Base64.decode(res_arr[4]);
    if (res_arr[0] == "saved" || res_arr[0] == "message") {
        // Обратный отклик не реализован, просто меняем фон на белый
        $('[yellow_color=1]').attr('yellow_color', '0').stop().animate({backgroundColor: '#FFFFFF'}, 1500).css('background', '');
    }
}

function insertTextAtCursor(text, t_node) {
    var sel, range, html;
    if (window.getSelection) {
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            range = sel.getRangeAt(0);
            range.insertNode(t_node);
            range.setEndAfter(t_node);
            range.setStartAfter(t_node);
            sel.removeAllRanges();
            sel.addRange(range);
        }
    } else if (document.selection && document.selection.createRange) {
        range = document.selection.createRange();
        range.pasteHTML(text);
        range.moveEnd('character', 0);
        range.select();
    }
}

// Вставляем стандартный перевод строки
function insert_next_line(element) {
    insertTextAtCursor("<br>", document.createElement("br"));
}

function get_edit_html_value(obj_id) {
    var html = $('#edit_html_value_' + obj_id).html();
    var par_w = $('#edit_html_value_' + obj_id).width();
    $('#edit_html_value_' + obj_id).html('<textarea style="border:0px;min-height:17px; outline:none; background: #fff;padding:0px;width:' + par_w + 'px;">' + html + '</textarea>');

    /*$('#edit_html_value_' + obj_id + ' textarea:first-child').autosize({
        append: ""
    });*/
    $('#edit_html_value_' + obj_id + ' textarea:first-child').focus();
    $('#edit_html_value_' + obj_id + ' textarea:first-child').blur(function () {

        save_edit_html_value(obj_id)
    });

    $('#edit_html_value_' + obj_id).attr('onclick', '');

    $(".go_event_link").fadeOut(200, function () {
        setTimeout("$('.save_event_button').fadeIn(200)", 200);
    });
}

function save_edit_html_value(obj_id) {
    var html = $('#edit_html_value_' + obj_id + ' textarea:first-child').val();

    $('#edit_html_value_' + obj_id).css('background', '#fff6ad');
    $('#edit_html_value_' + obj_id).attr('yellow_color', '1');

    save_value($('#edit_html_value_' + obj_id).attr('field_id'), $('#edit_html_value_' + obj_id).attr('line_id'), html);

    $('#edit_html_value_' + obj_id).html(html);

    $('#edit_html_value_' + obj_id).attr('onclick', 'get_edit_html_value("' + obj_id + '")');
}

function get_edit_html_input_value(obj_id) {
    var html = $('#edit_html_input_value_' + obj_id).html();
    var par_w = $('#edit_html_input_value_' + obj_id).width();
    $('#edit_html_input_value_' + obj_id).html('<input type="text" value="' + html + '" style="border:0px;min-height:17px; outline:none; background: #fff;padding:0px;width:' + par_w + 'px;"/>');

    $('#edit_html_input_value_' + obj_id + ' input:first-child').focus();
    $('#edit_html_input_value_' + obj_id + ' input:first-child').blur(function () {
        save_edit_html_input_value(obj_id)
    });

    $('#edit_html_input_value_' + obj_id).attr('onclick', '');

    $(".go_event_link").fadeOut(200, function () {
        setTimeout("$('.save_event_button').fadeIn(200)", 200);
    });
}

function save_edit_html_input_value(obj_id) {
    var html = $('#edit_html_input_value_' + obj_id + ' input:first-child').val();

    $('#edit_html_input_value_' + obj_id).css('background', '#fff6ad');
    $('#edit_html_input_value_' + obj_id).attr('yellow_color', '1');

    save_value($('#edit_html_input_value_' + obj_id).attr('field_id'), $('#edit_html_input_value_' + obj_id).attr('line_id'), html);

    $('#edit_html_input_value_' + obj_id).html(html);

    $('#edit_html_input_value_' + obj_id).attr('onclick', 'get_edit_html_input_value("' + obj_id + '")');
}

var eventDurations = {};

  //Перетаскивание мышкой события в календаре
$(function() {
  let $draggedElementRef; // Переменная для хранения перетаскиваемого элемента

  const [getLocation, setLocation] = useState(null);
  const [getDraggableElementParams, setDraggableElementParams] = useState(null);
  /**
   * Обработчик события `dragstart` для элементов календаря.
   *
   * @param {DragEvent} event - Объект события браузера при перетаскивании.
   *
   * @description
   * - Сохраняет информацию о перетаскиваемом элементе в глобальные параметры.
   * - Использует атрибуты элемента для получения информации о событии календаря:
   *   - `id`: Уникальный идентификатор элемента.
   *   - `dateFieldId`: Идентификатор даты события.
   *   - `periodFieldId`: Идентификатор периода окончания события.
   *   - `eventId`: Уникальный идентификатор события.
   *   - `lineId`: Идентификатор строки в календаре.
   * - Обновляет локальное хранилище для отслеживания перетаскиваемых элементов.
   *
   * @example
   * $('.calendar-event').on('dragstart', onDragStartCalendar);
   */
  const onDragStartCalendar = (event) => {
    $draggedElementRef = $(event.target);

    setDraggableElementParams({
      id: $draggedElementRef.attr('id'),
      dateFieldId: $draggedElementRef.data('date_field_id'),
      periodFieldId: $draggedElementRef.data('period_field_id'),
      eventId: $draggedElementRef.data('event_id'),
      lineId: $draggedElementRef.data('line_id'),
    })
    setLocation($draggedElementRef.parent());
  }
  /**
   * Обработчик события `dragover` для элемента календаря.
   *
   * @param {Event} event - Объект события браузера.
   *
   * @description
   * - Предотвращает действие по умолчанию для события `dragover`.
   * - Скрывает элемент, который в данный момент перетаскивается, чтобы отобразить правильное взаимодействие.
   *
   * Используется для обеспечения корректного перетаскивания элементов в интерфейсе календаря.
   *
   * @example
   * // Пример использования при взаимодействии с ячейкой календаря:
   * $('.calendar-cell').on('dragover', onDragOver);
   */
  const onDragOver = (event) => {
    event.preventDefault();
    $draggedElementRef.hide();
  }
  /**
   * Перемещает элемент календарного события в целевую ячейку календаря.
   *
   * @param {Object} params - Параметры функции.
   * @param {jQuery} params.$targetCell - jQuery-объект целевой ячейки (`td`), куда необходимо переместить элемент.
   * @param {string} params.elementId - Уникальный идентификатор элемента календарного события.
   *
   * @returns {jQuery|null} Возвращает jQuery-объект перемещенного элемента или `null`, если целевая ячейка отсутствует.
   *
   * @description
   * - Находит элемент по его `elementId`.
   * - Если целевая ячейка пустая (не содержит текста), очищает её.
   * - Открепляет элемент от текущего родительского элемента и перемещает его в целевую ячейку.
   * - Возвращает перемещенный элемент для дальнейших действий.
   *
   * @example
   * const $targetCell = $('.mount_cell_03');
   * const elementId = 'calendarEvent123';
   * const movedElement = moveElement({ $targetCell, elementId });
   * console.log(movedElement);
   *
   * @throws {Error} Если целевая ячейка не найдена.
   */
  const moveElement = ({ $targetCell, elementId }) => {
    if ($targetCell.length === 0) {
      console.error('No target cell found');
      return;
    }

    if ($targetCell.text().trim() === "") {
      $targetCell.html("");
    }

    const currentCalendarEvent = $(`#${elementId}`);
    currentCalendarEvent.detach();
    currentCalendarEvent.appendTo($targetCell);
    return currentCalendarEvent;
  }

  /**
   * @fixme
   * Смешной кусок кода
   * Сначала создали трудности запихав все атрибуты в id элемента в calendar.tpl
   * А потом героически решаем с помощью регулярок...
   * Дата атрибуты по всей видимости придуманы напрасно
   * */ 
  const getTargetDateTime = ({ $targetCell, elementId }) => {
    let cellId = $targetCell.attr('id');
    // Для сохранения событий без времени на верхних ячейках
    if (!cellId.match(/cell_(\d+)_(\d+)_(\d+)(?:_(\d+)_(\d+))?/)) {
      let match = cellId.match(/week_overperiod(\d+)$/);
      if (match) {
        let number = match[1];
        let tdIndex = parseInt(number, 10);
        let tdElement = $('.calendar_table tr:nth-child(4) td').eq(tdIndex);
        cellId = tdElement.attr('id');
        if (cellId) {
          cellId = cellId.replace(/(_\d+_\d+)$/, '');
        }
      }
    }

    // новая логика для ячеек дневного календаря
    if($targetCell.data('calendar-type') === 'day') {
      const hour = $targetCell.data('hour');  
      const minute = $targetCell.data('minute');  
      return `${hour}.${minute}.0 ${hour}:${minute}` // Это не я придумал такой формат, вообще жесть конечно
    }
    
    const dateTimeParts = cellId.match(/cell_(\d+)_(\d+)_(\d+)(?:_(\d+)_(\d+))?/);
    
    if (dateTimeParts) {
      var day = dateTimeParts[1].padStart(2, '0');
      var month = dateTimeParts[2].padStart(2, '0');
      var year = dateTimeParts[3];
      var hour, minute;
      // Определяем тип календаря и обновляем время события соответственно
      if (dateTimeParts[4] && dateTimeParts[5]) { // Календарь на странице недели
        hour = dateTimeParts[4].padStart(2, '0');
        minute = dateTimeParts[5] === '0' ? '00' : '30';
      } else { // Календарь на странице месяца и дня
        var eventTime = $(`#${elementId}`).find('.event_datetime').text().split(' - ')[0];

        hour = eventTime.split(':')[0];
        minute = eventTime.split(':')[1];

        if ($(".calendar_type_switcher.sw_act").text() == "Месяц" && !isGrouped) {
          updateEventMargins($targetCell);
        }
      }
      var newDateTime = day + "." + month + "." + year + " " + hour + ":" + minute;
      return newDateTime;
    } else {
      throw new Error('Invalid cell ID format:', cellId)
    }
  }
  /**
   * Обработчик события переноса элемента календаря (drag-and-drop).
   *
   * @async
   * @param {Object} event - Объект события переноса элемента.
   * @property {Function} preventDefault - Предотвращает стандартное поведение браузера.
   *
   * @description
   * - Обрабатывает перенос элемента в ячейку календаря.
   * - Находит целевую ячейку (`td`) и перемещает элемент.
   * - Обновляет параметры события, включая новую дату и время.
   * - Выполняет запросы для обновления данных в бэкенде.
   * - Если обновление прошло успешно, отображает новую информацию в DOM.
   * - В случае ошибки возвращает элемент обратно в исходное место.
   * - Обновляет макет календаря после переноса элемента.
   *
   * @returns {Promise<void>} Возвращает промис.
   *
   * @example
   * // Пример обработки переноса элемента в ячейку календаря
   * $(element).on('drop', onDropCalendar);
   *
   * @throws {Error} Ошибки обновления данных в процессе AJAX-запроса.
   *
   * @property {Object} $targetCell - jQuery-объект, представляющий целевую ячейку.
   * @property {Object} $currentCalendarEvent - jQuery-объект для текущего события календаря.
   * @property {string} id - Уникальный идентификатор элемента.
   * @property {string} dateFieldId - Идентификатор для поля даты.
   * @property {string} periodFieldId - Идентификатор для конечной даты события.
   * @property {Object} csrf - Токен для защиты от CSRF.
   */
  const onDropCalendar = async (event) => {
    event.preventDefault();
    $draggedElementRef.show();
    const $targetCell = $(event.target).closest('td'); // Находим ближайшую ячейку td

    const {
      id,
      dateFieldId, // Дата события календаря
      periodFieldId, // Дата окончания, если есть
      lineId,
    } = getDraggableElementParams();
    const $currentCalendarEvent = moveElement({ $targetCell, elementId: id });
    const $currentDateTimeElement = $(`.mount_cell_${dateFieldId}_${lineId}.event_datetime`)
    const previousDateTime = $currentDateTimeElement.text();
    try {
      const newDateTime = getTargetDateTime({ $targetCell, elementId: id });
      const { newStartDateTime, newEndDateTime } = calculateNewPeriod(
        newDateTime, 
        $currentCalendarEvent,
      ); 
      $currentDateTimeElement.html(createLoader());
      const [startFieldResult, endFieldResult = null] = await fetchUpdatePeriod({
        lineId,
        startFieldId: dateFieldId,
        startValue: newStartDateTime,
        endFieldId: periodFieldId,
        endValue: newEndDateTime,
        csrf,
      })
      
      const resultPeriod = getNewPeriodStr({
        startDateTime: startFieldResult, 
        endDateTime: endFieldResult
      })
      
      $currentDateTimeElement.text(resultPeriod);
    } catch (error) {
      console.error(error)
      $currentCalendarEvent.appendTo(getLocation());
      $currentDateTimeElement.text(previousDateTime);
    } finally {
      const childrenElements = $(`.calendar_event_line`)
      applicationChildrenParams(childrenElements)
      fixCalendarLayout();
      setCalendarEventGroupField($currentCalendarEvent, lineId);
      
      if(pageReload) window.location.reload()
      // Запрещаем логику перезагрузки страницы после изменений
      /**
       * @fix Перезагрузка страницы после изменений вообще не должна существовать
       */
      pageReload = false;
    }
    
  }

  // Обработчик начала перетаскивания
  $('.calendar_table td div[draggable="true"]').on("dragstart", onDragStartCalendar);

  // Обработчики событий для ячеек календаря
  $('.calendar_table td')
    .on("dragenter dragover", onDragOver)
    .on("drop", onDropCalendar);
});
/**
 * Формирует строку для рендера нового периода события.
 *
 * @param {Object} params - Объект с параметрами периода.
 * @param {string} params.startDateTime - Дата и время начала события в формате "YYYY-MM-DD HH:MM".
 * @param {string} params.endDateTime - Дата и время окончания события в формате "YYYY-MM-DD HH:MM".
 *
 * @returns {string} - Строка, представляющая период события.
 *                    Если время окончания отсутствует, возвращается только время начала.
 *
 * @description
 * - Разбивает `startDateTime` и `endDateTime` на дату и время.
 * - Если конечное время присутствует и отличается от начального времени, возвращает строку в формате "HH:MM - HH:MM".
 * - Если конечное время отсутствует или совпадает с начальным, возвращает только время начала.
 *
 * @example
 * const periodStr = getNewPeriodStr({
 *   startDateTime: '2024-06-01 09:00',
 *   endDateTime: '2024-06-01 10:30'
 * });
 * console.log(periodStr); // Вывод: "09:00 - 10:30"
 */
const getNewPeriodStr = ({
  startDateTime,
  endDateTime,
}) => {
  const [startDate, startTime] = startDateTime.split(' ');
  const [endDate, endTime] = endDateTime?.split(' ') || [];
  // Убираем дефис, если нет конечного времени
  const result = endTime && startTime !== endTime 
    ? `${startTime} - ${endTime}` 
    : String(startTime);
  return result;
}

// Временное решение для очистки ответа от случайных символов
const cleanServerResponse = (response) => {
  // Используем регулярное выражение для извлечения корректной части
  const match = response.match(/^\d{2}\.\d{2}\.\d{4} \d{2}:\d{2}/);
  return match ? match[0] : null; // Возвращаем найденное, если есть
};

/**
 * Обновляет период события календаря, отправляя запросы на сервер
 *
 * @param {Object} params - Параметры для обновления.
 * @param {number|string} params.lineId - Идентификатор строки в таблице.
 * @param {string} params.startFieldId - Идентификатор даты начала события календаря.
 * @param {string} params.startValue - Значение даты начала события календаря.
 * @param {string} [params.endFieldId] - (Опционально) Идентификатор даты конца события календаря.
 * @param {string} [params.endValue] - (Опционально) Значение даты конца события календаря.
 * @param {string} params.csrf - Токен CSRF для проверки безопасности запросов.
 *
 * @returns {Promise<Array>} - Возвращает массив результатов обновлений для всех успешных запросов.
 *
 * @throws {Error} - Если при обновлении данных возникла ошибка.
 *
 * @description
 * - Для каждого поля отправляется POST-запрос на обновление данных.
 * - Используется метод `fetch` для отправки запросов на сервер.
 * - Обрабатывается ответ от сервера, разделяя данные по символу '|'.
 * - Если ответ сервера имеет неожиданный формат, выбрасывается ошибка.
 * - Если есть как `startFieldId`, так и `endFieldId`, оба запроса выполняются параллельно.
 *
 * @example
 * const params = {
 *   lineId: 123,
 *   startFieldId: 'field1',
 *   startValue: '2024-06-01',
 *   endFieldId: 'field2',
 *   endValue: '2024-06-02',
 *   csrf: 'someCsrfToken'
 * };
 *
 * fetchUpdatePeriod(params)
 *   .then(results => console.log('Updated fields:', results))
 *   .catch(err => console.error('Update error:', err));
 */
const fetchUpdatePeriod = async ({
  lineId,
  startFieldId,
  startValue,
  endFieldId,
  endValue,
  csrf,
}) => {
  const updateField = async (fieldId, value) => {
    try {
      const response = await fetch('update_value.php', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams({
          field: fieldId,
          line: lineId,
          value: value,
          csrf: csrf,
        }).toString(),
      });
  
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      const data = await response.text();
      const [
        responseAction, 
        responseTableId, 
        responseFieldId, 
        responseLineId, 
        result
      ] = data.split('|');
      if (responseAction.trim() !== 'saved')
        throw new Error(`Unexpected response: ${data}`);
      return cleanServerResponse(Base64.decode(result));
    } catch (error) {
      console.error('Error updating field:', error);
    }
  };

  try {
    const reqs = [
      updateField(startFieldId, startValue),
      ...(endFieldId && endValue // Если есть конечный период вызываем запрос и на него
        ? [updateField(endFieldId, endValue)]
        : []),
    ];
    return await Promise.all(reqs);
  } catch (error) {
    console.error('Error in fetch updating period:', error);
  }
};

/**
 * Возвращает новое начальное время события календаря на основе текущего URL и переданного времени.
 *
 * @param {string} newDateTime - Время события в формате "HH.mm" (например, "01.00" или "01.30").
 * @returns {string} - Полное время события в формате "YYYY-MM-DD HH:MM", если в URL есть параметр 'day',
 *                     иначе возвращает исходное `newDateTime`.
 *
 * @throws {Error} Если формат времени неверный.
 *
 * @description
 * Функция проверяет наличие параметра `day` в URL.
 * Если параметр присутствует, функция:
 * 1. Получает дату события из URL с помощью `getEventDateFromURL`.
 * 2. Преобразует время из формата "HH.mm" в формат "HH:MM", где минуты округляются до "00" или "30".
 * 3. Возвращает полное время в формате "YYYY-MM-DD HH:MM".
 *
 * Если параметр `day` отсутствует, возвращает переданное время `newDateTime` без изменений.
 *
 * @example
 * // Пример с параметром day в URL
 * window.location.search = "?day=2024-06-18";
 * const startTime = getNewStartDateTime("01.00");
 * console.log(startTime);
 * // Output: "2024-06-18 01:00"
 *
 * @example
 * // Пример без параметра day в URL
 * window.location.search = "?month=06";
 * const startTime = getNewStartDateTime("01.00");
 * console.log(startTime);
 * // Output: "01.00"
 */
const getNewStartDateTime = (newDateTime) => {
  if (window.location.search.indexOf('day=') !== -1) {
    // Получаем дату события из URL
    const eventDate = getEventDateFromURL();

    // Преобразуем время из формата "01.00" в "01:00" или "01:30"
    const timeParts = newDateTime.match(/(\d{2}).(\d{2})/);
    if (!timeParts) {
      throw new Error('Invalid time format:', newDateTime);
      return;
    }
    const hours = timeParts[1];
    const minutes = timeParts[2] === '00' ? '00' : '30';

    // Составляем полную дату и время начала события
    return eventDate + ' ' + hours + ':' + minutes;   
  } else {
    return newDateTime;
  }
}

/**
 * Рассчитывает новый временной период для календарного события.
 *
 * @param {string} newDateTime - Новая дата и время в формате строки (например, "YYYY-MM-DD HH:mm").
 * @param {jQuery} currentEvent - jQuery объект текущего события календаря, содержащий атрибуты события.
 * @returns {{ newStartDateTime: string, newEndDateTime: string }} - Объект с новыми значениями начала и окончания события.
 *
 * @throws {Error} Если невозможно рассчитать новое время окончания события.
 *
 * @description
 * Функция рассчитывает новый временной период на основе переданных параметров:
 * - `newDateTime` задает новое начальное время.
 * - Длительность события вычисляется с помощью `getEventDuration`.
 * - Длина события (`event_length`) извлекается из атрибута текущего события.
 *
 * Особая обработка выполняется для событий, начинающихся в 00:00: они сдвигаются на 00:01, чтобы избежать проблем с сохранением.
 *
 * @example
 * const newPeriod = calculateNewPeriod("2024-06-18 08:00", $('.calendar-event'));
 * console.log(newPeriod);
 * // Output: { newStartDateTime: "2024-06-18 08:00", newEndDateTime: "2024-06-18 10:00" }
 */
const calculateNewPeriod = (newDateTime, currentEvent) => {
  let calendarEventDuration = getEventDuration(currentEvent);
  let calendarEventLength = parseInt(currentEvent.attr('event_length'), 10) || 0;

  const newStartDateTime = getNewStartDateTime(newDateTime);
  const newEndDateTime = getNewEndDateTime(newStartDateTime, calendarEventDuration, calendarEventLength);
  
  // Для сохранения событий на ячейке времени 00:00
  if (newStartDateTime.endsWith("00:00"))
    return {
      newStartDateTime: newStartDateTime.replace("00:00", "00:01"), 
      newEndDateTime 
    };
  return { newStartDateTime, newEndDateTime }
}

/**
 * Устанавливает значение поля группы для календарного события.
 * @param {jQuery} $currentCalendarEvent - Текущий элемент календарного события (jQuery объект).
 * 
 * @description
 * Функция извлекает информацию о поле группы из родительского элемента события на основе его класса.
 * Если поле группы (`calendar.group_field`) не равно `0`, то происходит:
 * 1. Извлечение номера ячейки месяца из класса родительского элемента.
 * 2. Получение имени группы и ключа группы из заголовка соответствующего столбца календаря.
 * 3. Проверка имени группы и ключа на пустоту и валидность.
 * 4. Сохранение имени или ключа группы с помощью функции `save_value`.
 * 
 * @example
 * // Пример использования:
 * const $event = $('.calendar-event'); // Пример jQuery-элемента события
 * setCalendarEventGroupField($event);
 */
const setCalendarEventGroupField = ($currentCalendarEvent, lineId) => {
  let group_field = calendar.group_field;
  if (group_field != 0) {
    const groupName = $currentCalendarEvent.parent().data('group-name');
    const groupKey = $currentCalendarEvent.parent().data('group-key');
    
    groupName?.includes(groupKey) && isNaN(groupKey) 
      ? save_value(group_field, lineId, groupName) 
      : save_value(group_field, lineId, groupKey);
  }
}

/**
 * Создаёт DOM-элемент индикатора загрузки (loader) с текстом.
 * 
 * @param {string} [text="Загрузка..."] - Текст, который будет отображаться рядом с лоадером.
 * @returns {HTMLDivElement} - Элемент `<div>`, содержащий лоадер и текст.
 */
const createLoader = (text = "Загрузка...") => {
  const loaderWrapper = document.createElement("div");
  loaderWrapper.style.cssText = `
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 11px;
    color: #555;
  `;
  // Создаём элемент лоадера
  const loader = document.createElement("div");
  loader.style.cssText = `
    border: 2px solid #f3f3f3;
    border-top: 2px solid #3498db;
    border-radius: 50%;
    width: 8px;
    height: 8px;
    animation: spin 1s linear infinite;
  `;
  const loaderText = document.createElement("span");
  loaderText.textContent = text;
  // Добавляем анимацию через <style>, если она ещё не определена
  if (!document.getElementById("loader-animation-style")) {
    const style = document.createElement("style");
    style.id = "loader-animation-style";
    style.textContent = `
      @keyframes spin {
        0% { transform: rotate(0deg); }
        100% { transform: rotate(360deg); }
      }
    `;
    document.head.appendChild(style);
  }
  // Собираем обёртку с лоадером и текстом
  loaderWrapper.appendChild(loader);
  loaderWrapper.appendChild(loaderText);
  return loaderWrapper;
}

/**
 * Берет дату из ссылки страницы одного дня
 * @returns {string}    Дата
*/
function getEventDateFromURL() {
    var urlParams = new URLSearchParams(window.location.search);
    var day = urlParams.get('day');
    var month = urlParams.get('month');
    var year = urlParams.get('year');
    return day.padStart(2, '0') + '.' + month.padStart(2, '0') + '.' + year;
  }

/**
 * Функция для получения новой конечной даты и времени события
 * @param {string} startDateTime     Новая дата/время начала события
 * @param {number} duration          Разница между началом и концом события в минутах
 * @returns {string}                 Новая дата/время конца события
 */
function getNewEndDateTime(startDateTime, duration, eventLength) {
  let [datePart, timePart] = startDateTime.split(" ");
  let [day, month, year] = datePart.split(".").map(Number);
  let [hours = 0, minutes = 0] = timePart ? timePart.split(":").map(Number) : [];

  let startDate = new Date(year, month - 1, day, hours, minutes);

  if (isNaN(startDate.getTime())) {
    console.error("Invalid Date:", startDateTime);
    throw new Error("Invalid Date:", startDateTime)
  }

  startDate.setMinutes(startDate.getMinutes() + duration);

  if (eventLength > 1 && !isGroup && isMonth) {
    pageReload = true;
    return null
  }
  if (eventLength > 1 && isGroup && (isWeek || isMonth)) {
      startDate.setDate(startDate.getDate() + eventLength - 1);
  }

  return startDate.toLocaleString("ru-RU", {
      day: "2-digit",
      month: "2-digit",
      year: "numeric",
      hour: timePart ? "2-digit" : undefined,
      minute: timePart ? "2-digit" : undefined,
      hour12: false
  }).replace(",", "");
}


/**
 * Функция для получения продолжительности события
 * @param {jQuery} currentEvent     Объект jQuery, перетаскиваемого события
 * @returns {number}                Продолжительность события в минутах
 */
function getEventDuration(currentEvent) {
    var eventElementId = currentEvent.attr('id');
    
    // Проверяем, есть ли сохранённая продолжительность для текущего события, во избежании не корректного вычисления при быстром и частом перетаскивании
    if (eventDurations[eventElementId]) {
        return eventDurations[eventElementId];
    }
    
    var eventTimeElement = currentEvent.find('span.event_datetime');
    var times = eventTimeElement.text().split(' - ');
    var startTime = times[0].split(':');
    var startDate = new Date(0, 0, 0, parseInt(startTime[0], 10), parseInt(startTime[1], 10), 0);
    var duration = 0;
    // Если есть конечное время, вычисляем продолжительность
    if (times.length > 1 && times[1]) {
      var endTime = times[1].split(':');
      var endDate = new Date(0, 0, 0, parseInt(endTime[0], 10), parseInt(endTime[1], 10), 0);
      duration = (endDate - startDate) / (1000 * 60); // Продолжительность в минутах
    }

    if (startTime.join('') === "0001") duration++;

    eventDurations[eventElementId] = duration;

    return duration;
}

/**
 * Обновление атрибутов событий для регулирования их ширины и отступов
 * @param {object} childrenElements    html элементы событий
 * @returns {void}
 */
function applicationChildrenParams(childrenElements){
  for(var i = 0; i < childrenElements.length; i++){
    var parentElement = $(childrenElements[i]).parent();
    var childrenElementss = parentElement.children();
    var childCount = childrenElementss.length;

    childrenElementss.each(function(index) {
      var paramsEvent = {
        event_width: childCount - 1,
        event_offset: index + 1, // Позиция элемента в родителе, начиная с 1
        all_event: childCount
      };

      // Обновляем атрибуты элемента
      $(this).attr('event_width', paramsEvent.event_width);
      $(this).attr('event_offset', paramsEvent.event_offset);
      $(this).attr('all_event', paramsEvent.all_event);
    });
  }
}

/**
 * Вертиткальное позиционирование событий на странице месяца и группированной недели
 * @param {object} cell    html элемент ячейки
 * @returns {void}
 */
function updateEventMargins(cell) {
  var events = $(cell).find('.calendar_event_line');
  if(isGrouped){
    var eventMargin = 0;

    events.each(function(index) {
      // Устанавливаем вертикальный отступ для каждого события
      $(this).css('top', index * eventMargin + 'px');
      eventMargin += 20;
    });
  }else{
    var eventMargin = 17;

    events.each(function(index) {
      $(this).css('margin-top', index * eventMargin + 'px');
    });
}
}
