/**
    * @file form_editor.js
    * @brief Основной файл form_editor, удля создании form
*/



/* окно создания */

/** @brief Выбранная кнопка формы */
let selectedBtnId = null;
/** @brief Отложенный источник изображения */
window.pendingImageSrc = null;

document.querySelectorAll('#formEditorButtons .fe-btn').forEach(btn => {
    btn.addEventListener('click', () => {
        const isActive = btn.style.backgroundColor === 'darkgray';
        document.querySelectorAll('#formEditorButtons .fe-btn').forEach(b => b.style.backgroundColor = '');
        if(isActive){
            selectedBtnId = null;
            window.pendingImageSrc = null;
            closeImageChooser();
        } else {
            btn.style.backgroundColor = 'darkgray';
            selectedBtnId = btn.id;
            openImageChooser(btn);
        }
    });
});

document.querySelector('.formEditorArea').addEventListener('click', function(e){
    if(!selectedBtnId) return;
    const area = this;
    const areaRect = area.getBoundingClientRect();
    let el;

    if(selectedBtnId === 'addDiv') {
        el = document.createElement('div');
        el.style.width = '300px';
        el.style.height = '200px';
        el.style.border = '1px solid #000';
        el.style.display = 'flex';
        el.style.justifyContent = 'center';
        el.style.alignContent = 'center';
        el.style.flexWrap = 'wrap';
    } else if(selectedBtnId === 'addText') {
        el = document.createElement('span');
        el.textContent = 'Text';
        el.style.textAlign = 'center';
    } else if(selectedBtnId === 'addInput') {
        el = document.createElement('input');
        el.type = 'text';
        el.placeholder = 'Input';
        el.style.width = '150px';
    } else if(selectedBtnId === 'addButton') {
        el = document.createElement('button');
        el.type = 'button';
        el.textContent = 'Button';
        el.style.width = '100px';
    } else if(selectedBtnId === 'addImg'){
        if(!window.pendingImageSrc) return;
        el = document.createElement('img');
        el.src = window.pendingImageSrc;
        el.style.width = '300px';
    }

    if(el){
        el.style.position = 'absolute';
        el.style.padding = '2px';
        el.style.margin = '2px';
        const w = parseInt(el.style.width || 50);
        const h = parseInt(el.style.height || 30);
        el.style.left = (e.clientX - areaRect.left + area.scrollLeft) / currentZoom - w / 2 + 'px';
        el.style.top = (e.clientY - areaRect.top + area.scrollTop) / currentZoom - h / 2 + 'px';
        makeDraggable(el);
        area.appendChild(el);
    }
});

/**
    * @brief Открывает окно выбора изображения
    * @param btn кнопка, для которой открывается окно
*/
function openImageChooser(btn){
    const box = document.querySelector('.image-chooser');
    const rect = btn.getBoundingClientRect();
    box.style.left = (rect.right + 6 + window.scrollX) + 'px';
    box.style.top = (rect.top + window.scrollY) + 'px';
    box.style.display = 'flex';
}

/** @brief Закрывает окно выбора изображения */
function closeImageChooser(){
    document.querySelector('.image-chooser').style.display='none'
}

document.getElementById('imgByUrl').onclick = async function() {
    messageQueue.push("Enter image URL");
    const url = await messageCreateInput();
    if (url) {
        window.pendingImageSrc = url;
        selectedBtnId = 'addImg';
        closeImageChooser();
        document.querySelectorAll('#formEditorButtons .fe-btn').forEach(b => b.style.backgroundColor = '');
        document.getElementById('addImg').style.backgroundColor = 'darkgray';
    }
};
document.getElementById('imgUpload').onclick = function() {
    const input = document.createElement("input");
    document.getElementById('imgUpload').type = 'button';
    input.type = "file";
    input.accept = "image/*";
    input.style.display = "none";
    document.body.appendChild(input);

    input.addEventListener("change", function() {
        if (input.files.length > 0) {
            const reader = new FileReader();
            reader.onload = function(e) {
                const base64Data = e.target.result.split(',')[1];
                jsonrpcRequest('uploadImage', { userImgBase64: base64Data, userImgName: input.files[0].name })
                .then(response => {
                    if (response) {
                        window.pendingImageSrc = response;  
                        selectedBtnId = 'addImg';  
                        closeImageChooser();  
                        document.querySelectorAll('#formEditorButtons .fe-btn').forEach(b => b.style.backgroundColor = '');
                        document.getElementById('addImg').style.backgroundColor = 'darkgray';
                    } else {
                        messageFunction("{{img_upload_error}}");
                    }
                })
                .catch(() => messageFunction("{{img_upload_error}}"));
            };
            reader.readAsDataURL(input.files[0]);
        }
        document.body.removeChild(input);
    });

    input.click();
};
document.getElementById('imgData').onclick = async function() {
    window.managerDataAction = "selectImgFormToForm";
    let userName = getCookie('User');
    if (userName) {
        await includePlugin("manager");
        managerData("/img/users_img/" + userName);
        managerDiv.style.visibility = "visible";
        document.getElementById("settingsMain_d")?.style && (document.getElementById("settingsMain_d").style.visibility = "hidden");
    }
};


document.addEventListener('click',function(e){
    const chooser=document.querySelector('.image-chooser')
    if(chooser.style.display==='flex' && !chooser.contains(e.target) && !e.target.closest('#addImg')){
        closeImageChooser()
    }
})

/* сетка и привязка к ней */
let GRID = 20;
let currentSnapX = 0;
let currentSnapY = 0;

function createGrid(){
    const area = document.querySelector('.formEditorArea');
    if(!area) return;
    area.style.position = area.style.position || 'relative';
    if(GRID > 1){
        area.style.backgroundImage = 'radial-gradient(circle at 0 0, rgba(0,0,0,0.6) 1px, transparent 2px)';
        area.style.backgroundSize = GRID + 'px ' + GRID + 'px';
        area.style.backgroundRepeat = 'repeat';
        area.style.backgroundPosition = '0 0';
    } else {
        area.style.backgroundImage = '';
    }
}

/* передвижение */

/** @brief Флаг перетаскивания */
let isDragging = false;
/** @brief Смещение по X при перетаскивании */
let offsetX = 0;
/** @brief Смещение по Y при перетаскивании */
let offsetY = 0;
/** @brief Перетаскиваемый элемент */
let draggedElement = null;

/**
 * @brief Делаем элемент перетаскиваемым
 * @param el элемент для перетаскивания
 */
function makeDraggable(el){
    el.style.touchAction = 'none';
    if(el.tagName.toLowerCase() !== 'form') el.isDraggable = true;
    el.addEventListener('mousedown', function(e){ startDrag(e, el); });
}

/**
 * @brief Начало перетаскивания элемента
 * @param e событие мыши
 * @param el элемент для перетаскивания
 */
function startDrag(e, el){
    if(e.button !== 0) return;
    if(el.tagName.toLowerCase() !== 'form' && !el.isDraggable) return;
    isDragging = true;
    draggedElement = el;
    const rect = el.getBoundingClientRect();
    offsetX = e.clientX - rect.left;
    offsetY = e.clientY - rect.top;
    document.addEventListener('mousemove', dragMove);
    document.addEventListener('mouseup', stopDrag);
    e.preventDefault();
}

/**
 * @brief Обработка движения мыши при перетаскивании
 * @param e событие мыши
 */
function dragMove(e){
    if(!isDragging || !draggedElement) return;
    const el = draggedElement;
    const area = document.querySelector('.formEditorArea');
    if(!area) return;

    const areaRect = area.getBoundingClientRect();

    let rawLeft = (e.clientX - areaRect.left - offsetX + area.scrollLeft) / currentZoom;
    let rawTop  = (e.clientY - areaRect.top  - offsetY + area.scrollTop) / currentZoom;

    let newLeft = Math.round(rawLeft / GRID) * GRID;
    let newTop  = Math.round(rawTop  / GRID) * GRID;

    const maxLeft = Math.max(0, (area.scrollWidth) - el.offsetWidth);
    const maxTop  = Math.max(0, (area.scrollHeight) - el.offsetHeight);
    newLeft = Math.max(0, Math.min(newLeft, maxLeft));
    newTop  = Math.max(0, Math.min(newTop, maxTop));

    currentSnapX = newLeft;
    currentSnapY = newTop;

    el.style.position = 'absolute';
    el.style.left = newLeft + 'px';
    el.style.top  = newTop + 'px';

    if(draggedElement === activeElement && activeResizer){
        const rect = activeElement.getBoundingClientRect();
        const areaRect = document.querySelector('.formEditorArea').getBoundingClientRect();
        activeResizer.style.left = (rect.right - areaRect.left - 5 + area.scrollLeft) / currentZoom + 'px';
        activeResizer.style.top  = (rect.bottom - areaRect.top - 5 + area.scrollTop) / currentZoom + 'px';
    }
}

/** @brief Завершение перетаскивания элемента */
function stopDrag(e){
    if(!isDragging || !draggedElement) return;
    const el = draggedElement;
    isDragging = false;
    document.removeEventListener('mousemove', dragMove);
    document.removeEventListener('mouseup', stopDrag);

    const area = document.querySelector('.formEditorArea');
    if(!area){
        draggedElement = null;
        return;
    }

    const areaRect = area.getBoundingClientRect();
    let rawLeft = (e.clientX - areaRect.left - offsetX + area.scrollLeft) / currentZoom;
    let rawTop  = (e.clientY - areaRect.top  - offsetY + area.scrollTop) / currentZoom;

    let snappedLeft = Math.round(rawLeft / GRID) * GRID;
    let snappedTop  = Math.round(rawTop  / GRID) * GRID;

    const maxLeft = Math.max(0, (area.scrollWidth) - el.offsetWidth);
    const maxTop  = Math.max(0, (area.scrollHeight) - el.offsetHeight);
    snappedLeft = Math.max(0, Math.min(snappedLeft, maxLeft));
    snappedTop  = Math.max(0, Math.min(snappedTop, maxTop));

    el.style.position = 'absolute';
    el.style.left = snappedLeft + 'px';
    el.style.top  = snappedTop + 'px';

    currentSnapX = snappedLeft;
    currentSnapY = snappedTop;

    const divs = Array.from(area.querySelectorAll('div')).filter(d => d !== el);

    let candidate = null;
    for(let d of divs){
        if(el.contains(d)) continue;

        const rect = d.getBoundingClientRect();
        const elRect = el.getBoundingClientRect();
        const elCenterX = elRect.left + elRect.width / 2;
        const elCenterY = elRect.top  + elRect.height / 2;

        if(elCenterX >= rect.left &&
           elCenterY >= rect.top &&
           elCenterX <= rect.right &&
           elCenterY <= rect.bottom){
            if(!candidate || candidate.contains(d)){
                candidate = d;
            }
        }
    }

    if(candidate){
        const candStyle = window.getComputedStyle(candidate);
        if(candStyle.position === 'static'){
            candidate.style.position = 'relative';
        }

        const candRect = candidate.getBoundingClientRect();
        const candOffsetX = candRect.left - areaRect.left + area.scrollLeft;
        const candOffsetY = candRect.top  - areaRect.top  + area.scrollTop;

        let leftInCandidate = currentSnapX - candOffsetX;
        let topInCandidate  = currentSnapY - candOffsetY;

        leftInCandidate = Math.round(leftInCandidate / GRID) * GRID;
        topInCandidate  = Math.round(topInCandidate  / GRID) * GRID;

        const maxLeftInCand = Math.max(0, candidate.clientWidth - el.offsetWidth);
        const maxTopInCand  = Math.max(0, candidate.clientHeight - el.offsetHeight);
        leftInCandidate = Math.max(0, Math.min(leftInCandidate, maxLeftInCand));
        topInCandidate  = Math.max(0, Math.min(topInCandidate, maxTopInCand));

        if(el.offsetWidth > candidate.clientWidth){
            el.style.width = (candidate.clientWidth - 16) + 'px';
        }
        if(el.offsetHeight > candidate.clientHeight){
            el.style.height = Math.max(10, Math.floor(candidate.clientHeight / 4)) + 'px';
        }

        el.style.position = '';
        el.style.left = '';
        el.style.top  = '';
        candidate.appendChild(el);
    } else {
        el.style.position = 'absolute';
        el.style.left = currentSnapX + 'px';
        el.style.top  = currentSnapY + 'px';
    }

    draggedElement = null;
}

// инициализация сетки один раз
createGrid();

/* Подсветка границ */

document.querySelector('.formEditorArea').addEventListener('mouseover', function(e){
    if(e.target !== this){
        e.target.style.outline = '1px dashed gray';
    }
});

document.querySelector('.formEditorArea').addEventListener('mouseout', function(e){
    if(e.target !== this){
        e.target.style.outline = '';
    }
});

/* изменение размеров */

/** @brief Активный ресайзер */
let activeResizer = null;
/** @brief Активный элемент для изменения размера */
let activeElement = null;

/**
    * @brief Показывает ресайзер для элемента
    * @param el элемент для изменения размера
*/
function showResizer(el){
    removeExistingResizers();
    activeElement = el;
    const area = document.querySelector('.formEditorArea');
    
    if(!activeResizer){
        activeResizer = document.createElement('div');
        activeResizer.style.width = '10px';
        activeResizer.style.height = '10px';
        activeResizer.style.background = '#000';
        activeResizer.style.position = 'absolute';
        activeResizer.style.cursor = 'se-resize';
        area.appendChild(activeResizer);

        let isResizing = false;
        let startX, startY, startWidth, startHeight;

        activeResizer.addEventListener('mousedown', function(e){
            e.stopPropagation();
            e.preventDefault();
            isResizing = true;
            startX = e.clientX;
            startY = e.clientY;
            startWidth = parseInt(window.getComputedStyle(activeElement).width,10);
            startHeight = parseInt(window.getComputedStyle(activeElement).height,10);
            document.addEventListener('mousemove', resizeElement);
            document.addEventListener('mouseup', stopResize);
        });

        function resizeElement(e){
            if(!isResizing) return;
            let newWidth = (startWidth + (e.clientX - startX) / currentZoom);
            let newHeight = (startHeight + (e.clientY - startY) / currentZoom);
                    
            newWidth = Math.round(newWidth / GRID) * GRID;
            newHeight = Math.round(newHeight / GRID) * GRID;
                    
            const areaRect = area.getBoundingClientRect();
            if(newWidth + activeElement.offsetLeft > areaRect.width / currentZoom) newWidth = areaRect.width / currentZoom - activeElement.offsetLeft;
            if(newHeight + activeElement.offsetTop > areaRect.height / currentZoom) newHeight = areaRect.height / currentZoom - activeElement.offsetTop;
        
            activeElement.style.width = newWidth + 'px';
            activeElement.style.height = newHeight + 'px';
            positionResizer();
        }

        function stopResize(){
            isResizing = false;
            document.removeEventListener('mousemove', resizeElement);
            document.removeEventListener('mouseup', stopResize);
        }
    }

    function positionResizer(){
        const rect = activeElement.getBoundingClientRect();
        const areaRect = area.getBoundingClientRect();
        activeResizer.style.left = (rect.right - areaRect.left - 5 + area.scrollLeft) / currentZoom + 'px';
        activeResizer.style.top  = (rect.bottom - areaRect.top - 5 + area.scrollTop) / currentZoom + 'px';
    }

    positionResizer();
}

/** @brief Удаляет существующие ресайзеры */
function removeExistingResizers(){
    if(activeResizer){
        activeResizer.remove();
        activeResizer = null;
        activeElement = null;
    }
}

document.querySelector('.formEditorArea').addEventListener('mousedown', function(e){
    removeExistingResizers();
    if(e.target !== this && (e.target.tagName.toLowerCase() === 'div' || e.target.tagName.toLowerCase() === 'img')){
        showResizer(e.target);
    }
});

/* окно свойств */

document.querySelector('.formEditorArea').addEventListener('click', function(e){
    if(e.target === this){
        /* showActions(e.target); */
        document.getElementById("panelProperties").style.display = "none";
    } else {
        showProperties(e.target);
    }
});
document.querySelector('.formEditorArea').click();

/** @brief Показывает свойства элемента */
function showProperties(el){
    var props = document.querySelector('.formEditorProperties');
    props.innerHTML = '';

    var tag = el.tagName.toLowerCase();

    addRow('<hr>', 'width: -webkit-fill-available; margin-block-start: 0em;');
/*     addRow('{{id}}', el.id || '', null, el);
    addRow('{{class}}', el.className || '', null, el); */
    selectedElement = el
    const propsContainer = document.querySelector('.formEditorProperties');
    let idClassEditor = document.getElementById('idClassEditor');
    if (!idClassEditor) {
        idClassEditor = document.createElement('div');
        idClassEditor.id = 'idClassEditor';
        propsContainer.appendChild(idClassEditor);
    }
    createIdEditor(idClassEditor);
    createClassEditor(idClassEditor);

    addRow('<hr>', 'width: -webkit-fill-available;');
    addRow('{{width}}', el.style.width || el.offsetWidth + 'px', function(val){ el.style.width = val; }, el);
    addRow('{{height}}', el.style.height || el.offsetHeight + 'px', function(val){ el.style.height = val; }, el);
    addRow('{{padding}}', el.style.padding || window.getComputedStyle(el).padding, function(val){ el.style.padding = val; }, el);
    addRow('{{margin}}', el.style.margin || window.getComputedStyle(el).margin, function(val){ el.style.margin = val; }, el);
    addRow('{{background-color}}', el.style.backgroundColor || '', function(val){ el.style.backgroundColor = val; }, el);
    addRow('{{color}}', el.style.color || '', function(val){ el.style.color = val; }, el);
    addRow('{{border}}', el.style.border || window.getComputedStyle(el).border, function(val){ el.style.border = val; }, el);
    addRow('{{border-radius}}', el.style.borderRadius || window.getComputedStyle(el).borderRadius, function(val){ el.style.borderRadius = val; }, el);
    addRow('{{cursor}}', el.style.cursor || '', function(val){ el.style.cursor = val; }, el);
    addRow('{{box-shadow}}', el.style.boxShadow || '', function(val){ el.style.boxShadow = val; }, el);
    addRow('{{opacity}}', el.style.opacity || '', function(val){ el.style.opacity = val; }, el);

    addRow('<hr>', 'width: -webkit-fill-available;');
    if(tag === 'div'){
        addRow('{{justifyContent}}', el.style.justifyContent || 'flex-start', function(val){ el.style.justifyContent = val; }, el);
        addRow('{{alignContent}}', el.style.alignContent || 'flex-start', function(val){ el.style.alignContent = val; }, el);
    }
    if(tag === 'span'){
        addRow('{{text}}', el.textContent, function(val){ el.textContent = val; }, el);
        addRow('{{font-size}}', el.style.fontSize || '', function(val){ el.style.fontSize = val; }, el);
        addRow('{{font-family}}', el.style.fontFamily || '', function(val){ el.style.fontFamily = val; }, el);
        addRow('{{font-weight}}', el.style.fontWeight || '', function(val){ el.style.fontWeight = val; }, el);
        addRow('{{line-height}}', el.style.lineHeight || '', function(val){ el.style.lineHeight = val; }, el);
        addRow('{{text-decoration}}', el.style.textDecoration || '', function(val){ el.style.textDecoration = val; }, el);
        addRow('{{text-align}}', el.style.textAlign || '', function(val){ el.style.textAlign = val; }, el);
        addRow('{{white-space}}', el.style.whiteSpace || '', function(val){ el.style.whiteSpace = val; }, el);
    }
    if(tag === 'input'){
        addRow('{{placeholder}}', el.placeholder || '', function(val){ el.placeholder = val; }, el);
        addRow('{{type}}', el.type || '', function(val){ el.type = val; }, el);
        addRow('{{maxLength}}', el.maxLength || '', function(val){ el.maxLength = val; }, el);
        addRow('{{min}}', el.min || '', function(val){ el.min = val; }, el);
        addRow('{{max}}', el.max || '', function(val){ el.max = val; }, el);
    }
    if(tag === 'button'){
        addRow('{{text}}', el.textContent, function(val){ el.textContent = val; }, el);
        addRow('{{type}}', el.type || '', function(val){ el.type = val; }, el);
    }
    if(tag === 'img'){
        addRow('{{src}}', el.src || '', function(val){ el.src = val; }, el);
    }

    document.getElementById('panelProperties').style.display = 'flex';
}

/**
    * @brief Добавляет строку в окно свойств
    * @param label название свойства
    * @param value значение свойства
    * @param propHandler функция обработки изменения свойства
    * @param el элемент, к которому применяется свойство
*/
function addRow(label, value = "", propHandler = "", el = null){
    var props = document.querySelector('.formEditorProperties');

    // предопределённые списки для сложных свойств
    var predefined = {
        'justifyContent': ['flex-start','flex-end','center','space-between','space-around'],
        'alignContent': ['flex-start','flex-end','center','space-between','space-around'],
        'text-decoration': ['none','underline','line-through','underline line-through'],
        'text-align': ['left','right','center','justify'],
        'white-space': ['normal','nowrap'],
        'cursor': ['auto','default','pointer','text','move','help'],
        'font-weight': ['normal', '100','300','500','700','800','900']
    };

    var inputIsNumber = function(v){
        if(typeof v !== 'string') v = String(v);
        return /^-?\d+(\.\d+)?$/.test(v.trim());
    };

    if(label === '{{id}}'){
        var row = document.createElement('div');
        row.style.display = 'flex';
        row.style.flexDirection = 'column';
        row.style.marginBottom = '5px';

        var nameDiv = document.createElement('div');
        nameDiv.textContent = label;
        nameDiv.style.marginBottom = '2px';

        var input = document.createElement('input');
        input.type = 'text';
        input.value = value || '';
        input.style.width = '100%';

        var warn = document.createElement('div');
        warn.style.color = 'red';
        warn.style.fontSize = '12px';
        warn.style.height = '16px';
        warn.style.marginTop = '4px';
        warn.textContent = '';
        warn.style.display = 'none';

        input.addEventListener('input', function(){
            var newId = input.value.trim();
            if(!newId){
                warn.textContent = '';
                warn.style.display = 'none';
                return;
            }
            var exists = document.getElementById(newId);
            if(exists && exists !== el){
                warn.textContent = '{{id_already_exists}}';
                warn.style.display = '';
            } else {
                warn.textContent = '';
                warn.style.display = 'none';
            }
        });

        input.addEventListener('blur', function(){
            if(!el) return;
            var newId = input.value.trim();
            if(!newId){
                el.id = '';
                input.value = '';
                warn.textContent = '';
                warn.style.display = 'none';
                return;
            }
            var exists = document.getElementById(newId);
            if(exists && exists !== el){
                var base = newId.replace(/\d+$/, '');
                var numbers = [];
                document.querySelectorAll('[id^="' + base + '"]').forEach(function(item){
                    var match = item.id.match(new RegExp('^' + base + '(\\d*)$'));
                    if(match){
                        numbers.push(match[1] ? parseInt(match[1], 10) : 0);
                    }
                });
                numbers.sort(function(a,b){ return a-b; });
                var next = 0;
                for(var i=0;i<numbers.length;i++){
                    if(numbers[i] !== i){
                        next = i;
                        break;
                    }
                    next = i+1;
                }
                el.id = base + (next === 0 ? '' : next);
                input.value = el.id;
                warn.textContent = '';
                warn.style.display = 'none';
            } else {
                el.id = newId;
                warn.textContent = '';
                warn.style.display = 'none';
            }
        });

        input.addEventListener('keydown', function(ev){
            if(ev.key === 'Enter') input.blur();
        });

        row.appendChild(nameDiv);
        row.appendChild(input);
        row.appendChild(warn);
        props.appendChild(row);

    } else if (label === '{{class}}'){
        var row = document.createElement('div');
        row.style.display = 'flex';
        row.style.flexDirection = 'column';
        row.style.marginBottom = '5px';

        var nameDiv = document.createElement('div');
        nameDiv.textContent = label;
        nameDiv.style.marginBottom = '2px';

        var input = document.createElement('input');
        input.type = 'text';
        input.value = value || '';
        input.style.width = '100%';

        input.addEventListener('blur', function(){
            if(!el) return;
            el.className = input.value.trim();
        });

        input.addEventListener('keydown', function(ev){
            if(ev.key === 'Enter') input.blur();
        });

        row.appendChild(nameDiv);
        row.appendChild(input);
        props.appendChild(row);

    } else if (label.startsWith('{{')) {
        var row = document.createElement('div');
        row.style.display = 'flex';
        row.style.flexDirection = 'column';
        row.style.marginBottom = '5px';

        var nameDiv = document.createElement('div');
        nameDiv.textContent = label;
        nameDiv.style.marginBottom = '2px';

        // ключ без {{ }}
        var key = label.replace(/^\{\{/, '').replace(/\}\}$/, '');

        var options = null;
        if(key === 'type' && el){
            var t = (el.tagName || '').toLowerCase();
            if(t === 'input'){
                options = ['text','number','password','email','url','tel','search','date','datetime-local','month','week','time','checkbox','radio','range','color','file','hidden'];
            } else if(t === 'button'){
                options = ['button','submit','reset'];
            }
        } else {
            if(predefined.hasOwnProperty(key)) options = predefined[key];
            else {
                var keb = key.replace(/([A-Z])/g, '-$1').toLowerCase();
                if(predefined.hasOwnProperty(keb)) options = predefined[keb];
            }
        }

        if(Array.isArray(options) && options.length > 0){
            var select = document.createElement('select');
            select.style.width = '100%';
            if(!value){
                var emptyOpt = document.createElement('option');
                emptyOpt.value = '';
                emptyOpt.textContent = '';
                select.appendChild(emptyOpt);
            }
            options.forEach(function(opt){
                var o = document.createElement('option');
                o.value = opt;
                o.textContent = opt;
                select.appendChild(o);
            });
            try{ select.value = value; }catch(e){}
            select.addEventListener('change', function(){
                if(propHandler) propHandler(select.value);
            });
            select.addEventListener('blur', function(){
                if(propHandler) propHandler(select.value);
            });

            row.appendChild(nameDiv);
            row.appendChild(select);
            props.appendChild(row);
            return;
        }

        if(inputIsNumber(value)){
            var input = document.createElement('input');
            input.type = 'number';
            input.step = 'any';
            input.value = value;
            input.style.width = '100%';
            input.addEventListener('input', function(){
                if(propHandler) propHandler(input.value);
            });
            input.addEventListener('blur', function(){
                if(propHandler) propHandler(input.value);
            });
            input.addEventListener('keydown', function(ev){
                if(ev.key === 'Enter') input.blur();
            });

            row.appendChild(nameDiv);
            row.appendChild(input);
            props.appendChild(row);
            return;
        }

        var input = document.createElement('input');
        input.type = 'text';
        input.value = value || '';
        input.style.width = '100%';
        input.addEventListener('input', function(){
            if(propHandler) propHandler(input.value);
        });
        input.addEventListener('blur', function(){
            if(propHandler) propHandler(input.value);
        });
        input.addEventListener('keydown', function(ev){
            if(ev.key === 'Enter') input.blur();
        });

        row.appendChild(nameDiv);
        row.appendChild(input);
        props.appendChild(row);

    } else if (label.startsWith('<')) {
        var tagName = label.slice(1, -1);
        var elTag = document.createElement(tagName);
        elTag.style = value;
        props.appendChild(elTag);
    }
}

/* сохранение и загрузка */

/**
    * @brief Показывает действия для области формы
    * @param area область формы
*/
function showActions(area){
    const props = document.querySelector('.formEditorProperties')
    props.innerHTML = ''

    const hr1 = document.createElement('hr')
    hr1.style = "width: -webkit-fill-available; margin-block-start: 0em;"
    props.appendChild(hr1)

    createActionButton(props, '{{download_html}}', () => downloadFormattedHtml(area))
    createActionButton(props, '{{load_html}}', () => loadHtml(area))
    createActionButton(props, '{{download_html_and_auto_css}}', () => downloadFormattedHtmlAndCss(area))

    const hr2 = document.createElement('hr')
    hr2.style = "width: -webkit-fill-available;"
    props.appendChild(hr2)

    createGridControl(props)

    const hr3 = document.createElement('hr')
    hr3.style = "width: -webkit-fill-available;"
    props.appendChild(hr3)

    createIdEditor(props)
    createClassEditor(props)
}

/**
 * @brief Показывает действия для области формы
 * @param area область формы
 */
function updateGrid(val){
    const GRID = parseInt(val) || 20;
    const area = document.querySelector('.formEditorArea');
    if(!area) return;

    if(GRID > 1){
        area.style.backgroundImage = 'radial-gradient(circle at 0 0, rgba(0,0,0,0.6) 1px, transparent 2px)';
        area.style.backgroundSize = GRID + 'px ' + GRID + 'px';
        area.style.backgroundRepeat = 'repeat';
        area.style.backgroundPosition = '0 0';
    } else {
        area.style.backgroundImage = '';
    }
}

function createIdEditor(container){
	const row = document.createElement('div')
	row.style.marginBottom = '5px'

	const label = document.createElement('label')
	label.textContent = 'ID:'
	label.style.marginRight = '5px'

	const input = document.createElement('input')
	input.type = 'text'
	input.style.width = '100%'

	input.value = selectedElement ? selectedElement.id || '' : ''
	input.placeholder = selectedElement ? '' : 'не найдено'

    input.addEventListener('input', () => {
        if(selectedElement) selectedElement.id = input.value;
        showIdClassProperties(container, input.value, null);
    });

	row.appendChild(label)
	row.appendChild(input)
	container.appendChild(row)

	showIdClassProperties(container, input.value, null)
}

function createClassEditor(container){
	const row = document.createElement('div')
	row.style.marginBottom = '5px'

	const label = document.createElement('label')
	label.textContent = 'Class:'
	label.style.marginRight = '5px'

	const wrapper = document.createElement('div')
	wrapper.style.display = 'flex'
	wrapper.style.gap = '6px'
	wrapper.style.alignItems = 'center'

	const input = document.createElement('input')
	input.type = 'text'
	input.style.flex = '1'
	input.style.width = '100%'

	const dotsBtn = document.createElement('button')
	dotsBtn.type = 'button'
	dotsBtn.textContent = '⋯'
	dotsBtn.style.width = '28px'
	dotsBtn.style.height = '24px'
	dotsBtn.style.padding = '0'
	dotsBtn.style.cursor = 'pointer'

	wrapper.appendChild(input)
	wrapper.appendChild(dotsBtn)

	input.value = selectedElement ? selectedElement.className || '' : ''
	input.placeholder = selectedElement ? '' : 'не найдено'

	row.appendChild(label)
	row.appendChild(wrapper)
	container.appendChild(row)

	const manager = document.createElement('div')
	manager.style.marginTop = '6px'
	manager.style.display = 'none'
	container.appendChild(manager)

	var activeClass = null

	function getClasses(){
		return selectedElement ? (selectedElement.className || '').trim().split(/\s+/).filter(Boolean) : []
	}

	function setClasses(arr){
		if(!selectedElement) return
		selectedElement.className = arr.join(' ')
		input.value = selectedElement.className
	}

	function addClass(name){
		if(!selectedElement) return
		name = name.trim()
		if(!name) return
		var arr = getClasses()
		if(arr.indexOf(name) === -1) arr.push(name)
		setClasses(arr)
		activeClass = name
		renderManager()
		showIdClassProperties(container, null, activeClass)
        input.value = name;
	}

	function removeClass(name){
		if(!selectedElement) return
		var arr = getClasses().filter(x => x !== name)
		setClasses(arr)
		if(activeClass === name) activeClass = null
		renderManager()
		showIdClassProperties(container, null, activeClass)
        input.value = name;
	}

	function renderManager(){
		manager.innerHTML = ''
		if(!selectedElement){
			var no = document.createElement('div')
			no.textContent = 'Элемент не выбран'
			manager.appendChild(no)
			return
		}

		var titleRow = document.createElement('div')
		titleRow.style.display = 'flex'
		titleRow.style.justifyContent = 'space-between'
		titleRow.style.alignItems = 'center'
		titleRow.style.marginBottom = '6px'

		var title = document.createElement('div')
		title.textContent = 'Управление классами'
		title.style.fontWeight = '600'

		var closeBtn = document.createElement('button')
		closeBtn.type = 'button'
		closeBtn.textContent = 'x'
		closeBtn.style.width = '28px'
		closeBtn.style.cursor = 'pointer'
		closeBtn.addEventListener('click', function(){ manager.style.display = 'none' })

		titleRow.appendChild(title)
		titleRow.appendChild(closeBtn)
		manager.appendChild(titleRow)

		var list = document.createElement('div')
		list.style.display = 'flex'
		list.style.flexDirection = 'column'
		list.style.gap = '6px'

		var classes = getClasses()
		if(classes.length === 0){
			var empty = document.createElement('div')
			empty.textContent = 'Классы не найдены'
			list.appendChild(empty)
		} else {
			classes.forEach(function(cn){
				var item = document.createElement('div')
				item.style.display = 'flex'
				item.style.alignItems = 'center'
				item.style.gap = '6px'

				var name = document.createElement('div')
				name.textContent = cn
				name.style.flex = '1'
				name.style.cursor = 'pointer'
				if(activeClass === cn){
					name.style.fontWeight = '700'
				}
				name.addEventListener('click', function(){
					activeClass = cn
					input.value = cn
					renderManager()
					showIdClassProperties(container, null, activeClass)
				})

				var editBtn = document.createElement('button')
				editBtn.id = 'editBtnForm'
				editBtn.type = 'button'
				editBtn.textContent = '✎'
				editBtn.style.width = '28px'
				editBtn.style.cursor = 'pointer'
				editBtn.addEventListener('click', function(){
					activeClass = cn
					input.value = cn
					input.focus()
					renderManager()
					showIdClassProperties(container, null, activeClass)
				})

				var del = document.createElement('button')
				del.type = 'button'
				del.textContent = '-'
				del.style.width = '28px'
				del.style.cursor = 'pointer'
				del.addEventListener('click', function(){ removeClass(cn) })

				item.appendChild(name)
				item.appendChild(editBtn)
				item.appendChild(del)
				list.appendChild(item)
			})
		}

		manager.appendChild(list)

		var addRow = document.createElement('div')
		addRow.style.display = 'flex'
		addRow.style.gap = '6px'
		addRow.style.marginTop = '6px'
		addRow.style.alignItems = 'center'

		var newInput = document.createElement('input')
		newInput.type = 'text'
		newInput.placeholder = 'new_class'
		newInput.style.width = '150px'
		newInput.style.flex = '1'

		var addBtn = document.createElement('button')
		addBtn.type = 'button'
		addBtn.textContent = '+'
		addBtn.style.width = '28px'
		addBtn.style.cursor = 'pointer'
		addBtn.addEventListener('click', function(){
			addClass(newInput.value)
			newInput.value = ''
		})

		addRow.appendChild(newInput)
		addRow.appendChild(addBtn)
		manager.appendChild(addRow)
	}

	dotsBtn.addEventListener('click', function(e){
		e.stopPropagation()
		manager.style.display = manager.style.display === 'none' ? 'block' : 'none'
		renderManager()
	})

    input.addEventListener('input', function(){
        if(!selectedElement) return;
        var val = input.value.trim();
        if(activeClass){
            var arr = getClasses();
            var idx = arr.indexOf(activeClass);
            if(idx !== -1){
                if(val) arr[idx] = val;
                else arr.splice(idx, 1);
                setClasses(arr);
                activeClass = val || null;
                renderManager();
                showIdClassProperties(container, null, activeClass);
            } else {
                activeClass = null;
                renderManager();
            }
        } else {
            setClasses(val ? val.split(/\s+/) : []);
            renderManager();
            showIdClassProperties(container, null, val || null);
        }
    });

	container.appendChild(manager)
	renderManager()
    showIdClassProperties(container, null, null)
}

var formEditorStyles = {};

function updateDynamicStyles(){
    var css = '';
    for(var selector in formEditorStyles){
        var props = formEditorStyles[selector];
        var decls = Object.keys(props).map(function(k){ return k + ': ' + props[k] + ';'; }).join(' ');
        if(decls) css += selector + ' { ' + decls + ' }\n';
    }
    var container = document.querySelector('#formEditorStyles style');
    if(container){
        container.textContent = css;
    }
}

function renderProps(propsContainer, propsMap, selector){
    propsContainer.innerHTML = '';
    var keys = Object.keys(propsMap);
    if(keys.length === 0){
        var empty = document.createElement('div');
        empty.textContent = 'Свойства не найдены';
        propsContainer.appendChild(empty);
    } else {
        keys.forEach(function(key){
            (function(propKey){
                var wrapper = document.createElement('div');
                wrapper.style.display = 'flex';
                wrapper.style.flexDirection = 'column';
                wrapper.style.marginBottom = '5px';

                var labelDiv = document.createElement('div');
                labelDiv.style.marginBottom = '2px';
                labelDiv.textContent = propKey;

                var valueInput = document.createElement('input');
                valueInput.type = 'text';
                valueInput.value = propsMap[propKey];
                valueInput.style.width = '100%';

                valueInput.addEventListener('change', function(){
                    propsMap[propKey] = valueInput.value;
                    formEditorStyles[selector] = Object.assign({}, propsMap);
                    updateDynamicStyles();
                });

                wrapper.appendChild(labelDiv);
                wrapper.appendChild(valueInput);
                propsContainer.appendChild(wrapper);
            })(key);
        });
    }

    var addRow = document.createElement('div');
    addRow.style.display = 'flex';
    addRow.style.alignItems = 'center';
    addRow.style.marginTop = '6px';
    addRow.style.gap = '4px';

    var newPropInput = document.createElement('input');
    newPropInput.type = 'text';
    newPropInput.placeholder = 'property_name';
    newPropInput.style.width = '90%';

    var addBtn = document.createElement('button');
    addBtn.type = 'button';
    addBtn.textContent = '+';

    addBtn.addEventListener('click', function(){
        var name = newPropInput.value.trim();
        if(!name) return;
        if(!propsMap[name]) propsMap[name] = '';
        formEditorStyles[selector] = Object.assign({}, propsMap);
        updateDynamicStyles();
        renderProps(propsContainer, propsMap, selector);
    });

    addRow.appendChild(newPropInput);
    addRow.appendChild(addBtn);
    propsContainer.appendChild(addRow);
}

function showIdClassProperties(container, idValue, classValue){
    var old = container.querySelector('.id-class-props');
    if(old) old.remove();

    var area = document.createElement('div');
    area.className = 'id-class-props';
    container.appendChild(area);

    var selectors = [];
    if(idValue && idValue.trim()) selectors.push('#' + idValue.trim());
    if(classValue && classValue.trim()) selectors.push('.' + classValue.trim());

    if(selectors.length === 0){
        var note = document.createElement('div');
        note.textContent = 'ID/Class не указаны';
        area.appendChild(note);
        return;
    }

    selectors.forEach(function(selector){
        var title = document.createElement('div');
        title.textContent = selector;
        title.style.fontWeight = '600';
        title.style.marginTop = '6px';
        area.appendChild(title);

        var propsMap = {};

        try{
            for(var si = 0; si < document.styleSheets.length; si++){
                var ss = document.styleSheets[si];
                var rules;
                try{ rules = ss.cssRules; }catch(e){ continue; }
                if(!rules) continue;
                for(var ri = 0; ri < rules.length; ri++){
                    var rule = rules[ri];
                    if(rule.type === 1 && rule.selectorText){
                        var parts = rule.selectorText.split(',').map(s => s.trim());
                        if(parts.includes(selector)){
                            var style = rule.style;
                            for(var i = 0; i < style.length; i++){
                                var name = style[i];
                                propsMap[name] = style.getPropertyValue(name).trim();
                            }
                        }
                    }
                }
            }
        }catch(e){}

        if(formEditorStyles[selector]){
            Object.assign(propsMap, formEditorStyles[selector]);
        }

        var propsContainer = document.createElement('div');
        propsContainer.style.marginTop = '4px';
        area.appendChild(propsContainer);

        renderProps(propsContainer, propsMap, selector);
    });
}

const area = document.querySelector('.formEditorArea');
document.getElementById('downloadBtn').addEventListener('click', () => {
    downloadFormattedHtml(area);
});
document.getElementById('loadBtn').addEventListener('click', () => {
    loadHtml(area);
});
document.getElementById('downloadCssBtn').addEventListener('click', () => {
    downloadFormattedHtmlAndCss(area);
});
document.getElementById('gridInput').addEventListener('change', (e) => {
    updateGrid(e.target.value);
});

document.getElementById('menuToggle').addEventListener('click', function() {
    const menu = document.getElementById('menuPanel');
    menu.style.display = menu.style.display === 'block' ? 'none' : 'block';
});

document.addEventListener('click', function(e){
    const menu = document.getElementById('menuPanel');
    const toggle = document.getElementById('menuToggle');
    if(!menu.contains(e.target) && e.target !== toggle){
        menu.style.display = 'none';
    }
});

function updateGrid(val){
    const GRID = parseInt(val) || 20;
    const area = document.querySelector('.formEditorArea');
    if(!area) return;

    if(GRID > 1){
        area.style.backgroundImage = 'radial-gradient(circle at 0 0, rgba(0,0,0,0.6) 1px, transparent 2px)';
        area.style.backgroundSize = GRID + 'px ' + GRID + 'px';
        area.style.backgroundRepeat = 'repeat';
        area.style.backgroundPosition = '0 0';
    } else {
        area.style.backgroundImage = '';
    }
}

/**
    * @brief Создает кнопку действия в панели свойств
    * @param container контейнер для кнопки
    * @param text текст кнопки
    * @param callback функция, вызываемая при нажатии
*/
function createActionButton(container, text, callback){
    const btn = document.createElement('button')
    btn.textContent = text
    btn.style.padding = '4px'
    btn.style.marginTop = '5px'
    btn.style.cursor = 'pointer'
    container.appendChild(btn)
    btn.addEventListener('click', callback)
}

/**
    * @brief Сохраняет HTML формы в формате с отформатированным кодом
    * @param area область формы
*/
function downloadFormattedHtml(area){
    const clone = area.cloneNode(true)
    clone.removeAttribute('class')
    clone.querySelectorAll('[resizer]').forEach(r => r.remove())

    Array.from(clone.children).forEach(el => {
        el.style.position = 'relative'
        el.style.left = ''
        el.style.top = ''
        el.style.outline = ''
    })

    clone.querySelectorAll('*').forEach(el => el.style.outline = '')

    const formattedHtml = Array.from(clone.children).map(child => formatHtml(child)).join('')

    const blob = new Blob([formattedHtml], {type: 'text/html'})
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = 'form.html'
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    URL.revokeObjectURL(url)
}


/**
    * @brief Сохраняет HTML формы с отдельными стилями CSS
    * @param area область формы
*/
function downloadFormattedHtmlAndCss(area){
    const clone = area.cloneNode(true)
    clone.removeAttribute('class')
    clone.querySelectorAll('[resizer]').forEach(r => r.remove())
    clone.querySelectorAll('*').forEach(el => el.style.outline = '')

    Array.from(clone.children).forEach(el => {
        el.style.position = 'relative'
        el.style.left = ''
        el.style.top = ''
        el.style.outline = ''
    })

    const groups = {}
    const tagCounts = {}
    const elements = Array.from(clone.querySelectorAll('*'))
    elements.forEach(el => {
        const tag = el.tagName.toLowerCase()
        const style = el.getAttribute('style') || ''
        const key = tag + '|' + style
        if(!groups[key]){
            tagCounts[tag] = (tagCounts[tag] || 0) + 1
            const gen = tag + tagCounts[tag]
            groups[key] = { className: gen, style: style }
        }
        const existingClass = el.getAttribute('class') || ''
        const newClass = (existingClass ? existingClass + ' ' : '') + groups[key].className
        el.setAttribute('class', newClass.trim())
        if(el.hasAttribute('style')) el.removeAttribute('style')
    })

    const styleContainer = document.querySelector('#formEditorStyles style')
    if(styleContainer && styleContainer.textContent.trim()){
        const styleEl = document.createElement('style')
        styleEl.textContent = styleContainer.textContent
        clone.prepend(styleEl)
    }

    const formattedHtml = Array.from(clone.children).map(child => formatHtml(child)).join('')

    let styleBlock = ''
    const groupList = Object.values(groups)
    if(groupList.length){
        styleBlock += '<style>\n'
        groupList.forEach(g => {
            if(!g.style.trim()) return
            styleBlock += '    .' + g.className + ' {\n'
            g.style.split(';').map(s => s.trim()).filter(Boolean).forEach(prop => {
                styleBlock += '        ' + prop + ';\n'
            })
            styleBlock += '    }\n'
        })
        styleBlock += '</style>\n'
    }

    const output = formattedHtml + '\n' + styleBlock

    const blob = new Blob([output], { type: 'text/html' })
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = 'form_with_css.html'
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    URL.revokeObjectURL(url)
}

/**
    * @brief Форматирует HTML элемент в строку с отступами
    * @param el элемент
    * @param indent уровень отступа
*/
function formatHtml(el, indent = 0){
    let tab = '    '.repeat(indent)
    let html = ''
    if(el.nodeType === 3) return tab + el.textContent.trim() + '\n'

    let tag = el.tagName.toLowerCase()
    let attrs = Array.from(el.attributes).map(a => `${a.name}="${a.value}"`).join(' ')
    if(attrs) attrs = ' ' + attrs

    let selfClosing = ['input','img','br','hr','meta','link'].includes(tag)
    if(selfClosing){
        html += `${tab}<${tag}${attrs}>\n`
    } else {
        html += `${tab}<${tag}${attrs}>\n`
        Array.from(el.childNodes).forEach(child => {
            html += formatHtml(child, indent + 1)
        })
        html += `${tab}</${tag}>\n`
    }
    return html
}

/**
    * @brief Загружает HTML файл и вставляет его в форму
    * @param area область формы
*/
function loadHtml(area){
    const input = document.createElement('input')
    input.type = 'file'
    input.accept = '.html'
    input.addEventListener('change', function(){
        const file = input.files[0]
        if(!file) return
        const reader = new FileReader()
        reader.onload = function(evt){
            const parser = new DOMParser()
            const doc = parser.parseFromString(evt.target.result, 'text/html')
            const styleTags = doc.querySelectorAll('style')
            const styleContainer = document.querySelector('.formEditorAreaStyle') || (() => {
                const div = document.createElement('div')
                div.className = 'formEditorAreaStyle'
                div.style.display = 'none'
                document.body.appendChild(div)
                return div
            })()
            styleTags.forEach(style => {
                const newStyle = document.createElement('style')
                let content = style.textContent
                content = content.replace(/\.(\w+)/g, function(match, className){
                    let uniqueClass = className
                    let counter = 1
                    while(document.querySelector('.' + uniqueClass)){
                        uniqueClass = className + '_' + counter
                        counter++
                    }
                    return '.' + uniqueClass
                })
                newStyle.textContent = content
                styleContainer.appendChild(newStyle)
            })
            const bodyChildren = Array.from(doc.body.children).filter(el => el.tagName.toLowerCase() !== 'style')
            const existingChildren = area.children.length
            bodyChildren.forEach(el => area.appendChild(el))
            const newChildren = Array.from(area.children).slice(existingChildren)
            newChildren.forEach(el => {
                el.style.position = 'absolute'
                el.style.left = '0px'
                el.style.top = '0px'
                el.style.outline = ''
                makeDraggable(el)
            })
            /* showActions(area) */
        }
        reader.readAsText(file)
    })
    input.click()
}

/* окно действий */

/** @brief Контейнер для контекстного меню действий редактора */
var actions = document.getElementById('formEditorActions');
actions.style.display = 'none';
/** @brief Последняя координата X мыши при открытии контекстного меню */
var mouseX = 0;
/** @brief Последняя координата Y мыши при открытии контекстного меню */
var mouseY = 0;
/** @brief Текущий выбранный элемент в форме */
var selectedElement = null;

/**
    * @brief Копирует элемент в буфер редактора
    * @param el элемент
*/
function copyElement(el){
    if(el) window.formEditorClipboard = el.cloneNode(true);
}

/**
    * @brief Вставляет скопированный элемент в заданные координаты
    * @param x координата X
    * @param y координата Y
*/
function pasteElement(x, y){
    if(window.formEditorClipboard){
        var area = document.querySelector('.formEditorArea');
        var clone = window.formEditorClipboard.cloneNode(true);
        clone.style.position = 'absolute';
        clone.style.left = x + 'px';
        clone.style.top = y + 'px';
        makeDraggable(clone);
        area.appendChild(clone);
    }
}

/**
    * @brief Удаляет элемент из формы
    * @param el элемент
*/
function deleteElement(el){
    if(el) el.remove();
}

document.querySelector('.formEditorArea').addEventListener('contextmenu', function(e){
    e.preventDefault();
    var area = this;
    var el = e.target !== area ? e.target : null;
    selectedElement = el;
    mouseX = e.clientX;
    mouseY = e.clientY;

    if(el){
        actions.style.display = 'block';
        actions.style.left = mouseX + 3 + 'px';
        actions.style.top = mouseY + 3 + 'px';
        document.getElementById('copyAction').style.display = 'block';
        document.getElementById('deleteAction').style.display = 'block';
        document.getElementById('pasteAction').style.display = window.formEditorClipboard ? 'block' : 'none';
    } else if(window.formEditorClipboard){
        actions.style.display = 'block';
        actions.style.left = mouseX + 3 + 'px';
        actions.style.top = mouseY + 3 + 'px';
        document.getElementById('copyAction').style.display = 'none';
        document.getElementById('deleteAction').style.display = 'none';
        document.getElementById('pasteAction').style.display = 'block';
    } else {
        actions.style.display = 'none';
    }
});

document.addEventListener('click', function(e){
    if(!actions.contains(e.target)){
        actions.style.display = 'none';
    }
});

document.getElementById('copyAction').addEventListener('click', function(){
    copyElement(selectedElement);
    actions.style.display = 'none';
});

document.getElementById('pasteAction').addEventListener('click', function(e){
    var area = document.querySelector('.formEditorArea');
    var rect = area.getBoundingClientRect();
    pasteElement((e.clientX - rect.left) / currentZoom, (e.clientY - rect.top) / currentZoom);
    actions.style.display = 'none';
});

document.getElementById('deleteAction').addEventListener('click', function(){
    deleteElement(selectedElement);
    actions.style.display = 'none';
});

let currentZoom = 1;

document.addEventListener('wheel', function(e){
    if(e.ctrlKey){
        e.preventDefault();
        const area = document.querySelector('.formEditorArea');
        if(!area) return;

        currentZoom += e.deltaY < 0 ? 0.2 : -0.2;
        currentZoom = Math.min(Math.max(currentZoom, 0.2), 4);

        area.style.transform = 'scale(' + currentZoom + ')';
        area.style.transformOrigin = '0 0';

        const grid = 20 * currentZoom;
        area.style.backgroundImage = 'radial-gradient(circle at 0px 0px, rgba(0, 0, 0, 0.6) 1px, transparent 2px)';
        area.style.backgroundSize = grid + 'px ' + grid + 'px';
        area.style.width = 100 / currentZoom + "%";
        area.style.height = 100 / currentZoom + "%";
    }
}, { passive: false });
