function initGraffiti(event) {
    let canvas = null;
    const msgbox = new CMessageBox({
        title: tr("draw_graffiti"),
        body: "<div id='ovkDraw'></div>",
        close_on_buttons: false,
        warn_on_exit: true,
        buttons: [tr("save"), tr("cancel")],
        callbacks: [function() {
            canvas.getImage({includeWatermark: false}).toBlob(blob => {
                let fName = "Graffiti-" + Math.ceil(performance.now()).toString() + ".jpeg";
                let image = new File([blob], fName, {type: "image/jpeg", lastModified: new Date().getTime()});
                
                __uploadToTextarea(image, u(event.target).closest('#write'))
            }, "image/jpeg", 0.92);
            
            canvas.teardown();
            msgbox.close()
        }, async function() {
            const res = await msgbox.__showCloseConfirmationDialog()
            if(res === true) {
                canvas.teardown()
                msgbox.close()
            }
        }]
    })
    
    let watermarkImage = new Image();
    watermarkImage.src = "/assets/packages/static/openvk/img/logo_watermark.gif";
    
    msgbox.getNode().attr("style", "width: 750px;");
    canvas = LC.init(document.querySelector("#ovkDraw"), {
        backgroundColor: "#fff",
        imageURLPrefix: "/assets/packages/static/openvk/js/node_modules/literallycanvas/lib/img",
        watermarkImage: watermarkImage,
        imageSize: {
            width: 640,
            height: 480
        }
    });
}

u(document).on('click', '.menu_toggler', (e) => {
    const post_buttons = $(e.target).closest('.post-buttons')
    const wall_attachment_menu = post_buttons.find('#wallAttachmentMenu')
    if(wall_attachment_menu.is('.hidden')) {
        wall_attachment_menu.css({ opacity: 0 });
        wall_attachment_menu.toggleClass('hidden').fadeTo(250, 1);
    } else {
        wall_attachment_menu.fadeTo(250, 0, function () {
            $(this).toggleClass('hidden');
        });
    }
})

u(document).on("click", ".post-like-button", function(e) {
    e.preventDefault();
    e.stopPropagation()
    
    var thisBtn = u(this).first();
    var link    = u(this).attr("href");
    var heart   = u(".heart", thisBtn);
    var counter = u(".likeCnt", thisBtn);
    var likes   = counter.text() === "" ? 0 : counter.text();
    var isLiked = heart.attr("id") === 'liked';
    
    ky.post(link)
    heart.attr("id", isLiked ? '' : 'liked');
    counter.text(parseInt(likes) + (isLiked ? -1 : 1));
    if (counter.text() === "0") {
        counter.text("");
    }
    
    return false;
});

u(document).on("input", "textarea", function(e) {
    var boost             = 5;
    var textArea          = e.target;
    textArea.style.height = "5px";
    var newHeight = textArea.scrollHeight;
    textArea.style.height = newHeight + boost + "px";
    return;
    
    // revert to original size if it is larger (possibly changed by user)
    // textArea.style.height = (newHeight > originalHeight ? (newHeight + boost) : originalHeight) + "px";
});

async function OpenMiniature(e, photo, post, photo_id, type = "post") {
    /*
    костыли но смешные однако
    */
    e.preventDefault();
    e.stopPropagation()

    // Значения для переключения фоток

    const albums_per_page = 20
    let json;
    let offset = type == 'album' ? (Number((new URL(location.href)).searchParams.get('p') ?? 1) - 1) * albums_per_page : 0
    let shown_offset = 0

    let imagesCount = 0;
    let currentImageid = '0_0';

    const photo_viewer = new CMessageBox({
        title: '',
        custom_template: u(`
        <div class="ovk-photo-view-dimmer">
            <div class="ovk-photo-view">
                <div class="photo_com_title">
                    <text id="photo_com_title_photos">
                        <img src="/assets/packages/static/openvk/img/loading_mini.gif">
                    </text>
                    <div>
                        <a id="ovk-photo-close">${tr("close")}</a>
                    </div>
                </div>
                <div class='photo_viewer_wrapper'>
                    <div class="ovk-photo-slide-left"></div>
                    <div class="ovk-photo-slide-right"></div>
                    <img src="${photo}" id="ovk-photo-img">
                </div>
                <div class="ovk-photo-details">
                    <img src="/assets/packages/static/openvk/img/loading_mini.gif">
                </div>
            </div>
        </div>`)
    })

    photo_viewer.getNode().find("#ovk-photo-close").on("click", function(e) {
        photo_viewer.close()
    });

    function __getIndex(photo_id = null) {
        return Object.keys(json.body).findIndex(item => item == (photo_id ?? currentImageid)) + 1
    }

    function __getByIndex(id) {
        const ids = Object.keys(json.body)
        const _id  = ids[id - 1]

        return json.body[_id]
    }

    function __reloadTitleBar() {
        photo_viewer.getNode().find("#photo_com_title_photos").last().innerHTML = imagesCount > 1 ? tr("photo_x_from_y", shown_offset, imagesCount) : tr("photo");
    }

    async function __loadDetails(photo_id) {
        if(json.body[photo_id].cached == null) {
            photo_viewer.getNode().find(".ovk-photo-details").last().innerHTML = '<img src="/assets/packages/static/openvk/img/loading_mini.gif">';
            const photo_url     = `/photo${photo_id}`
            const photo_page    = await fetch(photo_url)
            const photo_text    = await photo_page.text()
            const parser        = new DOMParser
            const body          = parser.parseFromString(photo_text, "text/html")
            const details       = body.querySelector('.ovk-photo-details')
            json.body[photo_id].cached = details ? details.innerHTML : ''
            if(photo_id == currentImageid) {
                photo_viewer.getNode().find(".ovk-photo-details").last().innerHTML = details ? details.innerHTML : ''
            }

            photo_viewer.getNode().find(".ovk-photo-details .bsdn").nodes.forEach(bsdnInitElement)
        } else {
            photo_viewer.getNode().find(".ovk-photo-details").last().innerHTML = json.body[photo_id].cached
        }
    }

    async function __slidePhoto(direction) {
        /* direction = 1 - right
           direction = 0 - left */
        if(json == undefined) {
            console.log("Да подожди ты. Куда торопишься?");
        } else {
            let current_index = __getIndex()
            if(current_index >= imagesCount && direction == 1) {
                shown_offset   = 1
                current_index  = 1
            } else if(current_index <= 1 && direction == 0) {
                shown_offset   += imagesCount - 1
                current_index  = imagesCount
            } else if(direction == 1) {
                shown_offset  += 1
                current_index += 1
            } else if(direction == 0) {
                shown_offset -= 1
                current_index -= 1
            }

            currentImageid = __getByIndex(current_index)
            if(!currentImageid) {
                if(type == 'album') {
                    if(direction == 1) {
                        offset += albums_per_page
                    } else {
                        offset -= albums_per_page
                    }
                    
                    await __loadContext(type, post, true, direction == 0)
                } else {
                    return
                }
            }
            currentImageid = currentImageid.id
            let photoURL = json.body[currentImageid].url;

            photo_viewer.getNode().find("#ovk-photo-img").last().src = ''
            photo_viewer.getNode().find("#ovk-photo-img").last().src = photoURL;
            __reloadTitleBar();
            __loadDetails(json.body[currentImageid].id);
        }
    }

    async function __loadContext(type, id, ref = false, inverse = false) {
        if(type == 'post' || type == 'comment') {
            const form_data = new FormData()
            form_data.append('parentType', type);
    
            const endpoint_url = `/iapi/getPhotosFromPost/${type == "post" ? id : "1_"+id}`
            const fetcher = await fetch(endpoint_url, {
                method: 'POST',
                body: form_data,
            })
            json = await fetcher.json()
            imagesCount = Object.entries(json.body).length
        } else {
            const params = {
                'offset': offset,
                'count': albums_per_page,
                'owner_id': id.split('_')[0],
                'album_id': id.split('_')[1],
                'photo_sizes': 1
            }

            const result = await window.OVKAPI.call('photos.get', params)
            const converted_items = {}

            result.items.forEach(item => {
                const id = item.owner_id + '_' + item.id
                converted_items[id] = {
                    'url': item.src_xbig,
                    'id': id,
                }
            })
            imagesCount = result.count

            if(!json)
                json = {'body': []}
            
            if(!inverse) {
                json.body = Object.assign(converted_items, json.body)
            } else {
                json.body = Object.assign(json.body, converted_items)
            }
        }

        currentImageid = photo_id
    }

    photo_viewer.getNode().find(".ovk-photo-slide-left").on("click", (e) => {
        __slidePhoto(0);
    })
    photo_viewer.getNode().find(".ovk-photo-slide-right").on("click", (e) => {
        __slidePhoto(1);
    })
    
    if(!type) {
        imagesCount = 1
        json = {
            'body': {}
        }

        json.body[photo_id] = {
            'id': photo_id,
            'url': photo
        }
        currentImageid = photo_id
        
        __reloadTitleBar()
        __loadDetails(photo_id)
    } else {
        await __loadContext(type, post)
        shown_offset = offset + __getIndex()

        __reloadTitleBar();
        __loadDetails(json.body[currentImageid].id);
    }

    return photo_viewer.getNode()
}

async function OpenVideo(video_arr = [], init_player = true)
{
    CMessageBox.toggleLoader()
    const video_owner = video_arr[0]
    const video_id    = video_arr[1]
    let video_api     = null
    try {
        video_api   = await window.OVKAPI.call('video.get', {'videos': `${video_owner}_${video_id}`, 'extended': 1})
    
        if(!video_api.items || !video_api.items[0]) {
            throw new Error('Not found')
        }
    } catch(e) {
        CMessageBox.toggleLoader()
        fastError(e.message)

        return
    }

    // TODO: video lists
    const video_object = video_api.items[0]
    const pretty_id    = `${video_object.owner_id}_${video_object.id}`
    const author       = find_author(video_object.owner_id, video_api.profiles, video_api.groups)
    let player_html = ''
    if(init_player) {
        if(video_object.platform == 'youtube') {
            const video_url = new URL(video_object.player)
            const video_id = video_url.pathname.replace('/', '')
            player_html = `
            <iframe
               width="600"
               height="340"
               src="https://www.youtube-nocookie.com/embed/${video_id}"
               frameborder="0"
               sandbox="allow-same-origin allow-scripts allow-popups"
               allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
               allowfullscreen></iframe>
            `
        } else {
            if(!video_object.is_processed) {
                player_html = `<span class='gray'>${tr('video_processing')}</span>`
            } else {
                const author_name = `${author.first_name} ${author.last_name}`
                player_html = `
                    <div class='bsdn media' data-name="${escapeHtml(video_object.title)}" data-author="${escapeHtml(author_name)}">
                        <video class='media' src='${video_object.player}'></video>
                    </div>
                `
            }
        }
    }

    const msgbox = new CMessageBox({
        title: '...',
        close_on_buttons: false,
        warn_on_exit: true,
        custom_template: u(`
        <div class="ovk-photo-view-dimmer">
            <div class="ovk-modal-player-window">
                <div id="ovk-player-part">
                    <div class='top-part'>
                        <b>${escapeHtml(video_object.title)}</b>

                        <div class='top-part-buttons'>
                            <a id='__modal_player_minimize' class='hoverable_color'>${tr('hide_player')}</a>
                            |
                            <a id='__modal_player_close' class='hoverable_color'>${tr('close')}</a>
                        </div>
                    </div>
                    <div class='center-part'>
                        ${player_html}
                    </div>
                    <div class='bottom-part'>
                        <a id='__toggle_comments' class='hoverable_color'>${tr('show_comments')}</a>
                        |
                        <a href='/video${pretty_id}' class='hoverable_color'>${tr('to_page')}</a>
                    </div>
                </div>
                <div id="ovk-player-info"></div>
            </div>
        </div>
        `)
    })

    if(video_object.platform != 'youtube' && video_object.is_processed) {
        bsdnInitElement(msgbox.getNode().find('.bsdn').nodes[0])
    }

    msgbox.getNode().find('#ovk-player-part #__modal_player_close').on('click', (e) => {
        msgbox.close()
    })

    msgbox.getNode().find('#__toggle_comments').on('click', async (e) => {
        if(msgbox.getNode().find('#ovk-player-info').hasClass('shown')) {
            msgbox.getNode().find('#__toggle_comments').html(tr('show_comments'))
        } else {
            msgbox.getNode().find('#__toggle_comments').html(tr('close_comments'))
        }

        msgbox.getNode().find('#ovk-player-info').toggleClass('shown')
        if(msgbox.getNode().find('#ovk-player-info').html().length < 1) {
            u('#ovk-player-info').html(`<div id='gif_loader'></div>`)

            const fetcher = await fetch(`/video${pretty_id}`)
            const fetch_r = await fetcher.text()
            const dom_parser = new DOMParser
            const results =  u(dom_parser.parseFromString(fetch_r, 'text/html'))
            const details = results.find('.ovk-vid-details')
            details.find('.media-page-wrapper-description b').remove()

            u('#ovk-player-info').html(details.html())
            bsdnHydrate()
        }
    })

    msgbox.getNode().find('#__modal_player_minimize').on('click', (e) => {
        e.preventDefault()

        const miniplayer = u(`
            <div class='miniplayer'>
                <div class='miniplayer-head'>
                    <b>${escapeHtml(video_object.title)}</b>
                    <div class='miniplayer-head-buttons'>
                        <div id='__miniplayer_return'></div>
                        <div id='__miniplayer_close'></div>
                    </div>
                </div>
                <div class='miniplayer-body'></div>
            </div>
        `)
        msgbox.hide()

        u('body').append(miniplayer)
        miniplayer.find('.miniplayer-body').nodes[0].append(msgbox.getNode().find('.center-part > *').nodes[0])
        miniplayer.attr('style', `left:100px;top:0px;`)
        miniplayer.find('#__miniplayer_return').on('click', (e) => {
            msgbox.reveal()
            msgbox.getNode().find('.center-part').nodes[0].append(miniplayer.find('.miniplayer-body > *').nodes[0])
            u('.miniplayer').remove()
        })

        miniplayer.find('#__miniplayer_close').on('click', (e) => {
            msgbox.close()
            u('.miniplayer').remove()
        })

        $('.miniplayer').draggable({cursor: 'grabbing', containment: 'window', cancel: '.miniplayer-body'})
        $('.miniplayer').resizable({
            maxHeight: 2000,
            maxWidth: 3000,
            minHeight: 150,
            minWidth: 200
        })
    })

    CMessageBox.toggleLoader()
}

u(document).on('click', '#videoOpen', (e) => {
    e.preventDefault()
    e.stopPropagation()

    try {
        const target = e.target.closest('#videoOpen')
        const vid = target.dataset.id
        const split = vid.split('_')

        OpenVideo(split)
    } catch(ec) {
        return
    }
})

u(document).on("keydown", "#write > form", function(event) {
    if(event.ctrlKey && event.keyCode === 13)
        u(event.target).closest('form').find(`input[type='submit']`).nodes[0].click()
});

u(document).on('keydown', '.edit_menu #write', (e) => {
    if(e.ctrlKey && e.keyCode === 13)
        e.target.closest('.edit_menu').querySelector('#__edit_save').click()
})

// Migrated from inline start
function reportPhoto(photo_id) {
    uReportMsgTxt  = tr("going_to_report_photo");
    uReportMsgTxt += "<br/>"+tr("report_question_text");
    uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"

    MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
        (function() {
            res = document.querySelector("#uReportMsgInput").value;
            xhr = new XMLHttpRequest();
            xhr.open("GET", "/report/" + photo_id + "?reason=" + res + "&type=photo", true);
            xhr.onload = (function() {
            if(xhr.responseText.indexOf("reason") === -1)
                MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
            else
                MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
            });
            xhr.send(null);
            }),
        Function.noop
    ]);
}

function reportVideo(video_id) {
    uReportMsgTxt  = tr("going_to_report_video");
    uReportMsgTxt += "<br/>"+tr("report_question_text");
    uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"

    MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
        (function() {
            res = document.querySelector("#uReportMsgInput").value;
            xhr = new XMLHttpRequest();
            xhr.open("GET", "/report/" + video_id + "?reason=" + res + "&type=video", true);
            xhr.onload = (function() {
            if(xhr.responseText.indexOf("reason") === -1)
                MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
            else
            MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
            });
            xhr.send(null);
        }),
        Function.noop
    ]);
}

function reportUser(user_id) {
    uReportMsgTxt  = tr("going_to_report_user");
    uReportMsgTxt += "<br/>"+tr("report_question_text");
    uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"

    MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
        (function() {
            res = document.querySelector("#uReportMsgInput").value;
            xhr = new XMLHttpRequest();
            xhr.open("GET", "/report/" + user_id + "?reason=" + res + "&type=user", true);
            xhr.onload = (function() {
                if(xhr.responseText.indexOf("reason") === -1)
                    MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
                else
                    MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
            });
            xhr.send(null);
        }),
        Function.noop
    ]);
}

function reportComment(comment_id) {
    uReportMsgTxt  = tr("going_to_report_comment");
    uReportMsgTxt += "<br/>"+tr("report_question_text");
    uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"

    MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
        (function() {
            res = document.querySelector("#uReportMsgInput").value;
            xhr = new XMLHttpRequest();
            xhr.open("GET", "/report/" + comment_id + "?reason=" + res + "&type=comment", true);
            xhr.onload = (function() {
                if(xhr.responseText.indexOf("reason") === -1)
                    MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
                else
                    MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
                });
            xhr.send(null);
        }),
        Function.noop
    ]);
}

function reportApp(id) {
    uReportMsgTxt  = tr('going_to_report_app');
    uReportMsgTxt += "<br/>"+tr("report_question_text");
    uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"

    MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
        (function() {
            res = document.querySelector("#uReportMsgInput").value;
            xhr = new XMLHttpRequest();
            xhr.open("GET", "/report/" + id + "?reason=" + res + "&type=app", true);
            xhr.onload = (function() {
            if(xhr.responseText.indexOf("reason") === -1)
                MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
            else
                MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
            });
            xhr.send(null);
        }),
        Function.noop
    ]);
}

function reportClub(club_id) {
    uReportMsgTxt  = tr("going_to_report_club");
    uReportMsgTxt += "<br/>"+tr("report_question_text");
    uReportMsgTxt += "<br/><br/><b>"+tr("report_reason")+"</b>: <input type='text' id='uReportMsgInput' placeholder='" + tr("reason") + "' />"

    MessageBox(tr("report_question"), uReportMsgTxt, [tr("confirm_m"), tr("cancel")], [
        (function() {
            res = document.querySelector("#uReportMsgInput").value;
            xhr = new XMLHttpRequest();
            xhr.open("GET", "/report/" + club_id + "?reason=" + res + "&type=group", true);
            xhr.onload = (function() {
            if(xhr.responseText.indexOf("reason") === -1)
                MessageBox(tr("error"), tr("error_sending_report"), ["OK"], [Function.noop]);
            else
                MessageBox(tr("action_successfully"), tr("will_be_watched"), ["OK"], [Function.noop]);
            });
            xhr.send(null);
            }),
        Function.noop
    ]);
}

$(document).on("click", "#_photoDelete, #_videoDelete", function(e) {
    var formHtml = "<form id='tmpPhDelF' action='" + u(this).attr("href") + "' >";
    formHtml    += "<input type='hidden' name='hash' value='" + u("meta[name=csrf]").attr("value") + "' />";
    formHtml    += "</form>";
    u("body").append(formHtml);
    
    MessageBox(tr('warning'), tr('question_confirm'), [
        tr('yes'),
        tr('no')
    ], [
        (function() {
            u("#tmpPhDelF").nodes[0].submit();
        }),
        (function() {
            u("#tmpPhDelF").remove();
        }),
    ]);
    
    e.stopPropagation()
    return e.preventDefault();
});
/* @rem-pai why this func wasn't named as "#_deleteDialog"? It looks universal IMO */

u(document).on("click", "#_noteDelete", function(e) {
    var formHtml = "<form id='tmpPhDelF' action='" + u(this).attr("href") + "' >";
    formHtml    += "<input type='hidden' name='hash' value='" + u("meta[name=csrf]").attr("value") + "' />";
    formHtml    += "</form>";
    u("body").append(formHtml);
    
    MessageBox(tr('warning'), tr('question_confirm'), [
        tr('yes'),
        tr('no')
    ], [
        (function() {
            u("#tmpPhDelF").nodes[0].submit();
        }),
        (function() {
            u("#tmpPhDelF").remove();
        }),
    ]);
    
    e.stopPropagation()
    return e.preventDefault();
});

// TODO REWRITE cuz its a little broken
u(document).on("click", "#_pinGroup", async function(e) {
    e.preventDefault();
    e.stopPropagation()

    let link = u(this).attr("href");
    let thisButton = u(this);
    let groupName = u(this).attr("data-group-name");
    let groupUrl = u(this).attr("data-group-url");
    let list = u('#_groupListPinnedGroups');
    
    thisButton.nodes[0].classList.add('loading');
    thisButton.nodes[0].classList.add('disable');

    let req = await ky(link);
    if(req.ok == false) {
        NewNotification(tr('error'), tr('error_1'), null);
        thisButton.nodes[0].classList.remove('loading');
        thisButton.nodes[0].classList.remove('disable');
        return;
    }

    if(!parseAjaxResponse(await req.text())) {
        thisButton.nodes[0].classList.remove('loading');
        thisButton.nodes[0].classList.remove('disable');
        return;
    }

    // Adding a divider if not already there
    if(list.nodes[0].children.length == 0) {
        list.nodes[0].append(u('<div class="menu_divider"></div>').first());
    }

    // Changing the button name
    if(thisButton.html().trim() == tr('remove_from_left_menu')) {
        thisButton.html(tr('add_to_left_menu'));
        for(let i = 0; i < list.nodes[0].children.length; i++) {
            let element = list.nodes[0].children[i];
            if(element.pathname == groupUrl) {
                element.remove();
            }
        }
    }else{
        thisButton.html(tr('remove_from_left_menu'));
        list.nodes[0].append(u('<a href="' + groupUrl + '" class="link group_link">' + groupName + '</a>').first());
    }

    // Adding the group to the left group list
    if(list.nodes[0].children[0].className != "menu_divider" || list.nodes[0].children.length == 1) {
        list.nodes[0].children[0].remove();
    }
    
    thisButton.nodes[0].classList.remove('loading');
    thisButton.nodes[0].classList.remove('disable');

    return false;
});

u(document).handle("submit", "#_submitUserSubscriptionAction", async function(e) {
    e.preventDefault()
    e.stopPropagation()

    u(this).nodes[0].parentElement.classList.add('loading');
    u(this).nodes[0].parentElement.classList.add('disable');
    console.log(e.target);
    const data = await fetch(u(this).attr('action'), { method: 'POST', body: new FormData(e.target) });
    if (data.ok) {
        u(this).nodes[0].parentElement.classList.remove('loading');
        u(this).nodes[0].parentElement.classList.remove('disable');
        if (e.target[0].value == "add") {
            u(this).nodes[0].parentElement.innerHTML = tr("friends_add_msg");
        } else if (e.target[0].value == "rej") {
            u(this).nodes[0].parentElement.innerHTML = tr("friends_rej_msg");
        } else if (e.target[0].value == "rem") {
            u(this).nodes[0].parentElement.innerHTML = tr("friends_rem_msg");
        }
    }
})

function changeOwner(club, newOwner, newOwnerName) {
    const action = "/groups/" + club + "/setNewOwner/" + newOwner;

    MessageBox(tr('group_changeowner_modal_title'), `
        ${tr("group_changeowner_modal_text", escapeHtml(newOwnerName))}
        <br/><br/>
        <form id="transfer-owner-permissions-form" method="post">
            <label for="password">${tr('password')}</label>
            <input type="password" id="password" name="password" required />
            <input type="hidden" name="hash" value='${window.router.csrf}' />
        </form>
    `, [tr('transfer'), tr('cancel')], [
        () => {
            $("#transfer-owner-permissions-form").attr("action", action);
            document.querySelector("#transfer-owner-permissions-form").submit();
        }, Function.noop
    ]);
}

async function withdraw(id) {
    let coins = await API.Apps.withdrawFunds(id);
    if(coins == 0)
        MessageBox(tr('app_withdrawal'), tr('app_withdrawal_empty'), ["OK"], [Function.noop]);
    else
        MessageBox(tr('app_withdrawal'), tr("app_withdrawal_created", window.coins), ["OK"], [Function.noop]);
}

function toggleMaritalStatus(e) {
    let elem = $("#maritalstatus-user");
    $("#maritalstatus-user-select").empty();
    if ([0, 1, 8].includes(Number(e.value))) {
        elem.hide();
    } else {
        elem.show();
    }
}

u(document).on("paste", ".vouncher_input", function(event) {
    const vouncher = event.clipboardData.getData("text");

    let segments;
    if(vouncher.length === 27) {
        segments = vouncher.split("-");
        if(segments.length !== 4)
            segments = undefined;
    } else if(vouncher.length === 24) {
        segments = chunkSubstr(vouncher, 6);
    }

    if(segments !== undefined) {
        document.vouncher_form.key0.value = segments[0];
        document.vouncher_form.key1.value = segments[1];
        document.vouncher_form.key2.value = segments[2];
        document.vouncher_form.key3.value = segments[3];
        document.vouncher_form.key3.focus();
    }

    event.preventDefault();
});

// Migrated from inline end

var tooltipClientTemplate = Handlebars.compile(`
    <table>
        <tr>
            <td width="54" valign="top">
                <img src="{{img}}" width="54" />
            </td>
            <td width="1"></td>
            <td width="150" valign="top">
                <text>
                    {{app_tr}}: <b>{{name}}</b>
                </text><br/>
                <a href="{{url}}">${tr("learn_more")}</a>
            </td>
        </tr>
    </table>
`);

var tooltipClientNoInfoTemplate = Handlebars.compile(`
    <table>
        <tr>
            <td width="150" valign="top">
                <text>
                    {{app_tr}}: <b>{{name}}</b>
                </text><br/>
            </td>
        </tr>
    </table>
`);

tippy.delegate("body", {
    target: '.client_app',
    theme: "light vk",
    content: "⌛",
    allowHTML: true,
    interactive: true,
    interactiveDebounce: 500,

    onCreate: async function(that) {
        that._resolvedClient = null;
    },

    onShow: async function(that) {
        let client_tag = that.reference.dataset.appTag;
        let client_name = that.reference.dataset.appName;
        let client_url = that.reference.dataset.appUrl;
        let client_img = that.reference.dataset.appImg;
        
        if(client_name != "") {
            let res = {
                'name':   client_name,
                'url':    client_url,
                'img':    client_img,
                'app_tr': tr("app") 
            };
    
            that.setContent(tooltipClientTemplate(res));
        } else {
            let res = {
                'name': client_tag,
                'app_tr': tr("app") 
            };
    
            that.setContent(tooltipClientNoInfoTemplate(res));
        }
    }
});

tippy.delegate('body', {
    animation: 'up_down',
    target: `.post-like-button[data-type]:not([data-likes="0"])`,
    theme: "special vk",
    content: "⌛",
    allowHTML: true,
    interactive: true,
    interactiveDebounce: 500,

    onCreate: async function(that) {
        that._likesList = null;
    },

    onShow: async function(that) {
        const id  = that.reference.dataset.id
        const type = that.reference.dataset.type
        let final_type = type
        if(type == 'post') {
            final_type = 'wall'
        }

        if(!that._likesList) {
            that._likesList = await window.OVKAPI.call('likes.getList', {'extended': 1, 'count': 6, 'type': type, 'owner_id': id.split('_')[0], 'item_id': id.split('_')[1]})
        }

        const final_template = u(`
            <div style='margin: -6px -10px;'>
                <div class='like_tooltip_wrapper'>
                    <a href="/${final_type}/${id}/likes" class='like_tooltip_head'>
                        <span>${tr('liked_by_x_people', that._likesList.count)}</span>
                    </a>

                    <div class='like_tooltip_body'>
                        <div class='like_tooltip_body_grid'></div>
                    </div>
                </div>
            </div>
        `)

        that._likesList.items.forEach(item => {
            final_template.find('.like_tooltip_body .like_tooltip_body_grid').append(`
                <a href='/id${item.id}'><img src='${item.photo_50}' alt='.'></a>
            `)
        })
        that.setContent(final_template.nodes[0].outerHTML)
    }
})

async function showArticle(note_id) {
    u("body").addClass("dimmed");
    let note = await API.Notes.getNote(note_id);
    u("#articleAuthorAva").attr("src", note.author.ava);
    u("#articleAuthorName").text(note.author.name);
    u("#articleAuthorName").attr("href", note.author.link);
    u("#articleTime").text(note.created);
    u("#articleLink").attr("href", note.link);
    u("#articleText").html(`<h1 class="articleView_nameHeading">${note.title}</h1>` + note.html);
    u("body").removeClass("dimmed");
    u("body").addClass("article");
}

u(document).on("click", "#editPost", async (e) => {
    const target = u(e.target)
    const post = target.closest("table")
    const content = post.find(".post-content")
    const edit_place = post.find('.post-edit')
    const id = post.attr('data-id').split('_')

    let type = 'post'
    if(post.hasClass('comment')) {
        type = 'comment'
    }

    if(post.hasClass('editing')) {
        post.removeClass('editing')
        return
    }

    if(edit_place.html() == '') {
        target.addClass('lagged')
        const params = {}
        if(type == 'post') {
            params['posts'] = post.attr('data-id')
        } else {
            params['owner_id'] = 1
            params['comment_id'] = id[1]
        }

        const api_req = await window.OVKAPI.call(`wall.${type == 'post' ? 'getById' : 'getComment'}`, params)
        const api_post = api_req.items[0]
        
        edit_place.html(`
            <div class='edit_menu'>
                <form id="write">
                    <textarea placeholder="${tr('edit')}" name="text" style="width: 100%;resize: none;" class="expanded-textarea small-textarea">${api_post.text}</textarea>
                    
                    <div class='post-buttons'>
                        <div class="post-horizontal"></div>
                        <div class="post-vertical"></div>
                        <div class="post-repost"></div>
                        <div class="post-source"></div>

                        <div class='post-opts'>
                            ${type == 'post' ? `<label>
                                <input type="checkbox" name="nsfw" ${api_post.is_explicit ? 'checked' : ''} /> ${tr('contains_nsfw')}
                            </label>` : ''}

                            ${api_post.owner_id < 0 && api_post.can_pin ? `<label>
                                <input type="checkbox" name="as_group" ${api_post.from_id < 0 ? 'checked' : ''} /> ${tr('post_as_group')}
                            </label>` : ''}
                        </div>

                        <input type="hidden" id="source" name="source" value="none" />

                        <div class='edit_menu_buttons'>
                            <input class='button' type='button' id='__edit_save' value='${tr('save')}'>
                            <input class='button' type='button' id='__edit_cancel' value='${tr('cancel')}'>

                            <div style="float: right; display: flex; flex-direction: column;">
                                <a class='menu_toggler'>
                                    ${tr('attach')}
                                </a>
                                
                                <div id="wallAttachmentMenu" class="hidden">
                                    <a class="header menu_toggler">
                                        ${tr('attach')}
                                    </a>
                                    <a id="__photoAttachment">
                                        <img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-x-egon.png" />
                                        ${tr('photo')}
                                    </a>
                                    <a id="__videoAttachment">
                                        <img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-vnd.rn-realmedia.png" />
                                        ${tr('video')}
                                    </a>
                                    <a id="__audioAttachment">
                                        <img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/audio-ac3.png" />
                                        ${tr('audio')}
                                    </a>
                                    ${type == 'post' ? `<a id="__notesAttachment">
                                        <img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/mimetypes/application-x-srt.png" />
                                        ${tr('note')}
                                    </a>
                                    <a id='__sourceAttacher'>
                                        <img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/actions/insert-link.png" />
                                        ${tr('source')}
                                    </a>` : ''}
                                </div>
                            </div>
                        </div>
                    </div>
                </form>
            </div>`)

        if(api_post.copyright) {
            edit_place.find('.post-source').html(`
                <span>${tr('source')}: <a>${escapeHtml(api_post.copyright.link)}</a></span>
                <div id='remove_source_button'></div>
            `)

            edit_place.find('.post-source #remove_source_button').on('click', (e) => {
                edit_place.find('.post-source').html('')
                edit_place.find(`input[name='source']`).attr('value', 'remove')
            })
        }

        if(api_post.copy_history && api_post.copy_history.length > 0) {
            edit_place.find('.post-repost').html(`
                <span>${tr('has_repost')}.</span>
            `)
        }

        // horizontal attachments
        api_post.attachments.forEach(att => {
            const type = att.type
            const aid = att[type].owner_id + '_' + att[type].id

            if(type == 'video' || type == 'photo') {
                let preview = ''

                if(type == 'photo') {
                    preview = att[type].sizes[1].url
                } else {
                    preview = att[type].image[0].url
                }

                __appendToTextarea({
                    'type': type,
                    'preview': preview,
                    'id': aid
                }, edit_place)
            } else if(type == 'poll') {
                __appendToTextarea({
                    'type': type,
                    'alignment': 'vertical',
                    'html': tr('poll'),
                    'id': att[type].id,
                    'undeletable': true,
                }, edit_place) 
            } else {
                const found_block = post.find(`div[data-att_type='${type}'][data-att_id='${aid}']`)
                __appendToTextarea({
                    'type': type,
                    'alignment': 'vertical',
                    'html': found_block.html(),
                    'id': aid,
                }, edit_place)
            }
        })

        target.removeClass('lagged')

        edit_place.find('.edit_menu #__edit_save').on('click', async (ev) => {
            const text_node = edit_place.find('.edit_menu textarea')
            const nsfw_mark = edit_place.find(`.edit_menu input[name='nsfw']`)
            const as_group = edit_place.find(`.edit_menu input[name='as_group']`)
            const copyright = edit_place.find(`.edit_menu input[name='source']`)
            const collected_attachments = collect_attachments(edit_place.find('.post-buttons')).join(',')
            const params = {}
            
            params['owner_id'] = id[0]
            params['post_id'] = id[1]
            params['message'] = text_node.nodes[0].value

            if(nsfw_mark.length > 0) {
                params['explicit'] = Number(nsfw_mark.nodes[0].checked)
            }
            
            params['attachments'] = collected_attachments
            if(collected_attachments.length < 1) {
                params['attachments'] = 'remove'
            }

            if(as_group.length > 0 && as_group.nodes[0].checked) {
                params['from_group'] = 1
            }

            if(copyright.nodes[0].value != 'none') {
                params['copyright'] = copyright.nodes[0].value
            }

            u(ev.target).addClass('lagged')
            // больше двух запросов !
            try {
                if(type == 'post') {
                    await window.OVKAPI.call('wall.edit', params)
                } else {
                    params['comment_id'] = id[1]
                    await window.OVKAPI.call('wall.editComment', params)
                }
            } catch(e) {
                fastError(e.message)
                u(ev.target).removeClass('lagged')
                return
            }
            
            const new_post_html = await (await fetch(`/iapi/getPostTemplate/${id[0]}_${id[1]}?type=${type}`, {
                'method': 'POST'
            })).text()
            u(ev.target).removeClass('lagged')
            post.removeClass('editing')
            post.nodes[0].outerHTML = u(new_post_html).last().outerHTML

            bsdnHydrate()
        })
    
        edit_place.find('.edit_menu #__edit_cancel').on('click', (e) => {
            post.removeClass('editing')
        })
    }

    post.addClass('editing')
})

async function __uploadToTextarea(file, textareaNode) {
    const MAX_FILESIZE = window.openvk.max_filesize_mb*1024*1024
    let filetype = 'photo'
    if(file.type.startsWith('video/')) {
        filetype = 'video'
    }

    if(!file.type.startsWith('image/') && !file.type.startsWith('video/')) {
        fastError(tr("only_images_accepted", escapeHtml(file.name)))
        throw new Error('Only images accepted')
    }

    if(file.size > MAX_FILESIZE) {
        fastError(tr("max_filesize", window.openvk.max_filesize_mb))
        throw new Error('Big file')
    }

    const horizontal_count = textareaNode.find('.post-horizontal > a').length
    if(horizontal_count > window.openvk.max_attachments) {
        fastError(tr("too_many_photos"))
        throw new Error('Too many attachments')
    }

    const form_data = new FormData
    form_data.append('photo_0', file)
    form_data.append('count', 1)
    form_data.append("hash", u("meta[name=csrf]").attr("value"))
    
    if(filetype == 'photo') {
        const temp_url = URL.createObjectURL(file)
        const rand = random_int(0, 1000)
        textareaNode.find('.post-horizontal').append(`<a id='temp_filler${rand}' class="upload-item lagged"><img src='${temp_url}'></a>`)
        
        const res = await fetch(`/photos/upload`, {
            method: 'POST',
            body: form_data
        })
        const json_response = await res.json()
        if(!json_response.success) {
            u(`#temp_filler${rand}`).remove()
            fastError((tr("error_uploading_photo") + json_response.flash.message))
            return
        }

        json_response.photos.forEach(photo => {
            __appendToTextarea({
                'type': 'photo',
                'preview': photo.url,
                'id': photo.pretty_id,
                'fullsize_url': photo.link,
            }, textareaNode)
        })
        u(`#temp_filler${rand}`).remove()
        URL.revokeObjectURL(temp_url)
    } else {
        return
    }
}

async function __appendToTextarea(attachment_obj, textareaNode) {
    const form = textareaNode.find('.post-buttons')
    const indicator = textareaNode.find('.post-horizontal')

    if(attachment_obj.alignment == 'vertical') {
        textareaNode.find('.post-vertical').append(`
            <div class="vertical-attachment upload-item" draggable="true" data-type='${attachment_obj.type}' data-id="${attachment_obj.id}">
                <div class='vertical-attachment-content' draggable="false">
                    ${attachment_obj.html}
                </div>
                <div class='${attachment_obj.undeletable ? 'lagged' : ''} vertical-attachment-remove'>
                    <div id='small_remove_button'></div>
                </div>
            </div>
        `)

        return
    }
    
    indicator.append(`
        <a draggable="true" href='/${attachment_obj.type}${attachment_obj.id}' class="upload-item" data-type='${attachment_obj.type}' data-id="${attachment_obj.id}">
            <span class="upload-delete">×</span>
            ${attachment_obj.type == 'video' ? `<div class='play-button'><div class='play-button-ico'></div></div>` : ''}
            <img draggable="false" src="${attachment_obj.preview}" alt='...'>
        </a>      
    `)
}

u(document).on('paste', '#write .small-textarea', (e) => {
    if(e.clipboardData.files.length === 1) {
        __uploadToTextarea(e.clipboardData.files[0], u(e.target).closest('#write'))
        return;
    }
})

u(document).on('dragstart', '#write .post-horizontal .upload-item, .post-vertical .upload-item, .PE_audios .vertical-attachment', (e) => {
    //e.preventDefault()
    //console.log(e)
    u(e.target).closest('.upload-item').addClass('currently_dragging')
    return
})

u(document).on('dragover', '#write .post-horizontal .upload-item, .post-vertical .upload-item, .PE_audios .vertical-attachment', (e) => {
    e.preventDefault()

    const target = u(e.target).closest('.upload-item')
    const current = u('.upload-item.currently_dragging')

    if(current.length < 1) {
        return
    }

    if(target.nodes[0].dataset.id != current.nodes[0].dataset.id) {
        target.addClass('dragged')
    }
    
    return
})

u(document).on("dragover drop", async (e) => {
    e.preventDefault()
    return false;
})

u(document).on('dragleave dragend', '#write .post-horizontal .upload-item, .post-vertical .upload-item, .PE_audios .vertical-attachment', (e) => {
    //console.log(e)
    u(e.target).closest('.upload-item').removeClass('dragged')
    return
})

u(document).on("drop", '#write', function(e) {
    const current = u('.upload-item.currently_dragging')
    //console.log(e)
    if(e.dataTransfer.types.includes('Files')) {
        e.preventDefault()

        e.dataTransfer.dropEffect = 'move'
        __uploadToTextarea(e.dataTransfer.files[0], u(e.target).closest('#write'))
    } else if(e.dataTransfer.types.length < 1 || e.dataTransfer.types.includes('text/uri-list')) { 
        e.preventDefault()

        const target = u(e.target).closest('.upload-item')
        u('.dragged').removeClass('dragged')
        current.removeClass('currently_dragging')
        //console.log(target)
        if(!current.closest('.vertical-attachment').length < 1 && target.closest('.vertical-attachment').length < 1
         || current.closest('.vertical-attachment').length < 1 && !target.closest('.vertical-attachment').length < 1) {
            return
        }

        const first_html = target.nodes[0].outerHTML
        const second_html = current.nodes[0].outerHTML

        current.nodes[0].outerHTML = first_html
        target.nodes[0].outerHTML = second_html
    }
})

// !!! PHOTO PICKER !!!
u(document).on("click", "#__photoAttachment", async (e) => {
    const photos_per_page = 23
    const form = u(e.target).closest('form') 
    const club = Number(e.currentTarget.dataset.club ?? 0)
    const msg = new CMessageBox({
        title: tr('select_photo'),
        body: `
        <div class='attachment_selector'>
            <div class="topGrayBlock display_flex_row">
                <label>
                    <input type="file" multiple accept="image/*" id="__pickerQuickUpload" style="display:none">
                    <input type="button" class="button" value="${tr("upload_button")}" onclick="__pickerQuickUpload.click()">
                </label>
                
                <select id="albumSelect">
                    <option value="0">${tr("all_photos")}</option>
                </select>
            </div>
            <div id='attachment_insert'>
                <div id='attachment_insert_count'>
                    <h4>${tr("is_x_photos", 0)}</h4>
                </div>
                <div class="photosList album-flex"></div>
            </div>
        </div>
        `,
        buttons: [tr('close')],
        callbacks: [Function.noop]
    })

    msg.getNode().attr('style', 'width: 630px;')
    msg.getNode().find('.ovk-diag-body').attr('style', 'height:335px;padding:0px;')

    async function __recievePhotos(page, album = 0) {
        u('#gif_loader').remove()
        u('#attachment_insert').append(`<div id='gif_loader'></div>`)
        const insert_place = u('#attachment_insert .photosList')
        let photos = null

        try {
            if(album == 0) {
                photos = await window.OVKAPI.call('photos.getAll', {'owner_id': window.openvk.current_id, 'photo_sizes': 1, 'count': photos_per_page, 'offset': page * photos_per_page})
            } else {
                photos = await window.OVKAPI.call('photos.get', {'owner_id': window.openvk.current_id, 'album_id': album, 'photo_sizes': 1, 'count': photos_per_page, 'offset': page * photos_per_page})
            }
        } catch(e) {
            u("#attachment_insert_count h4").html(tr("is_x_photos", -1))
            u("#gif_loader").remove()
            insert_place.html("Invalid album")
            return
        }

        u("#attachment_insert_count h4").html(tr("is_x_photos", photos.count))
        u("#gif_loader").remove()
        const pages_count = Math.ceil(Number(photos.count) / photos_per_page)
        photos.items.forEach(photo => {
            const is_attached = (form.find(`.upload-item[data-type='photo'][data-id='${photo.owner_id}_${photo.id}']`)).length > 0
            insert_place.append(`
                <a class="album-photo${is_attached ? ' selected' : ''}" data-attachmentdata="${photo.owner_id}_${photo.id}" data-preview="${photo.photo_130}" href="/photo${photo.owner_id}_${photo.id}">
                    <img class="album-photo--image" src="${photo.photo_130}" alt="...">
                </a>
            `)
        })

        if(page < pages_count - 1) {
            insert_place.append(`
            <div id="show_more" data-pagesCount="${pages_count}" data-page="${page + 1}">
                <span>${tr('show_more')}</span>
            </div>`)
        }
    }

    // change album
    u('.ovk-diag-body .attachment_selector').on("change", ".topGrayBlock #albumSelect", (ev) => {
        u("#attachment_insert .photosList").html('')

        __recievePhotos(0, ev.target.value)
    })

    // next page
    u(".ovk-diag-body .attachment_selector").on("click", "#show_more", async (ev) => {
        const target = u(ev.target).closest('#show_more')
        target.addClass('lagged')
        await __recievePhotos(Number(target.nodes[0].dataset.page), u(".topGrayBlock #albumSelect").nodes[0].value)
        target.remove()
    })

    // add photo
    u(".ovk-diag-body .attachment_selector").on("click", ".album-photo", async (ev) => {
        ev.preventDefault()
        ev.stopPropagation()
        
        const target = u(ev.target).closest('.album-photo')
        const dataset = target.nodes[0].dataset
        const is_attached = (form.find(`.upload-item[data-type='photo'][data-id='${dataset.attachmentdata}']`)).length > 0
        if(is_attached) {
            (form.find(`.upload-item[data-type='photo'][data-id='${dataset.attachmentdata}']`)).remove()
            target.removeClass('selected')
        } else {
            if(form.find(`.upload-item`).length + 1 > window.openvk.max_attachments) {
                makeError(tr('too_many_attachments'), 'Red', 10000, 1)
                return
            }

            target.addClass('selected')
            __appendToTextarea({
                'type': 'photo',
                'preview': dataset.preview,
                'id': dataset.attachmentdata,
                'fullsize_url': dataset.preview,
            }, form)
        }
    })

    // "upload" button
    u(".ovk-diag-body #__pickerQuickUpload").on('change', (ev) => {
        for(file of ev.target.files) {
            try {
                __uploadToTextarea(file, form)
            } catch(e) {
                makeError(e.message)
                return
            }
        }

        msg.close()
    })

    __recievePhotos(0)
    if(!window.openvk.photoalbums) {
        window.openvk.photoalbums = await window.OVKAPI.call('photos.getAlbums', {'owner_id': club != 0 ? Math.abs(club) * -1 : window.openvk.current_id})
    }
    window.openvk.photoalbums.items.forEach(item => {
        u('.ovk-diag-body #albumSelect').append(`<option value="${item.vid}">${ovk_proc_strtr(escapeHtml(item.title), 20)}</option>`)
    })
})

u(document).on('click', '#__videoAttachment', async (e) => {
    const per_page = 10
    const form = u(e.target).closest('form') 
    const msg = new CMessageBox({
        title: tr('selecting_video'),
        body: `
        <div class='attachment_selector'>
            <div class="topGrayBlock display_flex_row">
                <a id='__fast_video_upload' href="/videos/upload"><input class='button' type='button' value='${tr("upload_button")}'></a>
                
                <input type="search" id="video_query" maxlength="20" placeholder="${tr("header_search")}">
            </div>
            <div id='attachment_insert'>
                <div class="videosInsert"></div>
            </div>
        </div>
        `,
        buttons: [tr('close')],
        callbacks: [Function.noop]
    })

    msg.getNode().attr('style', 'width: 630px;')
    msg.getNode().find('.ovk-diag-body').attr('style', 'height:335px;padding:0px;')
    
    async function __recieveVideos(page, query = '') {
        u('#gif_loader').remove()
        u('#attachment_insert').append(`<div id='gif_loader'></div>`)
        const insert_place = u('#attachment_insert .videosInsert')
        let videos = null

        try {
            if(query == '') {
                videos = await window.OVKAPI.call('video.get', {'owner_id': window.openvk.current_id, 'extended': 1, 'count': per_page, 'offset': page * per_page})
            } else {
                videos = await window.OVKAPI.call('video.search', {'q': escapeHtml(query), 'extended': 1, 'count': per_page, 'offset': page * per_page})
            }
        } catch(e) {
            u("#gif_loader").remove()
            insert_place.html("Err")
            return
        }

        u("#gif_loader").remove()
        const pages_count = Math.ceil(Number(videos.count) / per_page)
        
        if(pages_count < 1) {
            insert_place.append(query == '' ? tr('no_videos') : tr('no_videos_results'))
        }

        videos.items.forEach(video => {
            const pretty_id = `${video.owner_id}_${video.id}`
            const is_attached = (form.find(`.upload-item[data-type='video'][data-id='${video.owner_id}_${video.id}']`)).length > 0
            let author_name = ''

            const profiles = videos.profiles
            const groups = videos.groups

            if(video['owner_id'] > 0) {
                const profile = profiles.find(prof => prof.id == video['owner_id'])
                if(profile) {  
                    author_name = profile['first_name'] + ' ' + profile['last_name']
                }
            } else {
                const group = groups.find(grou => grou.id == Math.abs(video['owner_id']))
                if(group) {
                    author_name = group['name']
                }
            }

            insert_place.append(`
            <div class="content video_list" style="padding: unset;" data-preview='${video.image[0].url}' data-attachmentdata="${pretty_id}">
                <table>
                    <tbody>
                        <tr>
                            <td valign="top">
                                <a href="/video${pretty_id}">
                                    <div class="video-preview">
                                        <img src="${video.image[0].url}" alt="${escapeHtml(video.title)}">
                                    </div>
                                </a>
                            </td>
                            <td valign="top" style="width: 100%">
                                <a href="/video${pretty_id}">
                                    <b class='video-name'>
                                        ${ovk_proc_strtr(escapeHtml(video.title), 50)}
                                    </b>
                                </a>
                                <br>
                                <p>
                                    <span class='video-desc'>${ovk_proc_strtr(escapeHtml(video.description ?? ""), 140)}</span>
                                </p>
                                <span><a href="/id${video.owner_id}" target="_blank">${ovk_proc_strtr(escapeHtml(author_name ?? ""), 100)}</a></span>
                            </td>
                            <td valign="top" class="action_links">
                                <a class="profile_link" id="__attach_vid">${!is_attached ? tr("attach") : tr("detach")}</a>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </div>
            `)
        })

        if(page < pages_count - 1) {
            insert_place.append(`
            <div id="show_more" data-pagesCount="${pages_count}" data-page="${page + 1}">
                <span>${tr('show_more')}</span>
            </div>`)
        }

        if(query != '') {
            highlightText(query, '.videosInsert', ['.video-name', '.video-desc'])
        }
    }

    u(".ovk-diag-body #video_query").on('change', (ev) => {
        if(ev.target.value == u(".ovk-diag-body #video_query").nodes[0].value) {
            u('#attachment_insert .videosInsert').html('')
            __recieveVideos(0, u(".ovk-diag-body #video_query").nodes[0].value)
        }
    })

    // next page
    u(".ovk-diag-body .attachment_selector").on("click", "#show_more", async (ev) => {
        const target = u(ev.target).closest('#show_more')
        target.addClass('lagged')
        await __recieveVideos(Number(target.nodes[0].dataset.page), u(".topGrayBlock #video_query").nodes[0].value)
        target.remove()
    })

    // add video
    u(".ovk-diag-body .attachment_selector").on("click", "#__attach_vid", async (ev) => {
        ev.preventDefault()
        
        const target = u(ev.target).closest('.content')
        const button = target.find('#__attach_vid')
        const dataset = target.nodes[0].dataset
        const is_attached = (form.find(`.upload-item[data-type='video'][data-id='${dataset.attachmentdata}']`)).length > 0
        if(is_attached) {
            (form.find(`.upload-item[data-type='video'][data-id='${dataset.attachmentdata}']`)).remove()
            button.html(tr('attach'))
        } else {
            if(form.find(`.upload-item`).length + 1 > window.openvk.max_attachments) {
                makeError(tr('too_many_attachments'), 'Red', 10000, 1)
                return
            }

            button.html(tr('detach'))
            __appendToTextarea({
                'type': 'video',
                'preview': dataset.preview,
                'id': dataset.attachmentdata,
                'fullsize_url': dataset.preview,
            }, form)
        }
    })

    u(".ovk-diag-body .attachment_selector").on('click', '#__fast_video_upload', (ev) => {
        ev.preventDefault()
        showFastVideoUpload(form)
    })

    __recieveVideos(0)
})

// __audioAttachment -> al_music.js, 1318

u(document).on('click', '#__notesAttachment', async (e) => {
    const per_page = 10
    const form = u(e.target).closest('form') 
    const msg = new CMessageBox({
        title: tr('select_note'),
        body: `
        <div class='attachment_selector'>
            <div id='attachment_insert' style='height: 325px;'>
                <div class="notesInsert"></div>
            </div>
        </div>
        `,
        buttons: [tr("create_note"), tr('close')],
        callbacks: [() => {
            window.location.assign('/notes/create')
        }, Function.noop]
    })

    msg.getNode().attr('style', 'width: 340px;')
    msg.getNode().find('.ovk-diag-body').attr('style', 'height:335px;padding:0px;')
    
    async function __recieveNotes(page) {
        u('#gif_loader').remove()
        u('#attachment_insert').append(`<div id='gif_loader'></div>`)
        const insert_place = u('#attachment_insert .notesInsert')
        let notes = null

        try {
            notes = await window.OVKAPI.call('notes.get', {'user_id': window.openvk.current_id, 'count': per_page, 'offset': per_page * page})
        } catch(e) {
            u("#gif_loader").remove()
            insert_place.html("Err")
            return
        }

        u("#gif_loader").remove()
        const pages_count = Math.ceil(Number(notes.count) / per_page)

        if(notes.count < 1) {
            insert_place.append(tr('no_notes'))    
        }

        notes.notes.forEach(note => {
            is_attached = (form.find(`.upload-item[data-type='note'][data-id='${note.owner_id}_${note.id}']`)).length > 0
            insert_place.append(`
                <div class='display_flex_row _content' data-attachmentdata="${note.owner_id}_${note.id}" data-name='${escapeHtml(note.title)}'>
                    <div class="notes_titles" style='width: 73%;'>
                        <div class="written">
                            <a href="${note.view_url}">${escapeHtml(note.title)}</a>

                            <small>
                                <span>${ovk_proc_strtr(escapeHtml(strip_tags(note.text)), 100)}</span>
                            </small>
                        </div>
                    </div>
                    <div class="attachAudio" id='__attach_note'>
                        <span>${is_attached ? tr("detach") : tr("attach")}</span>
                    </div>
                </div>
            `)
        })

        if(page < pages_count - 1) {
            insert_place.append(`
            <div id="show_more" data-pagesCount="${pages_count}" data-page="${page + 1}">
                <span>${tr('show_more')}</span>
            </div>`)
        }
    }

    // next page
    u(".ovk-diag-body .attachment_selector").on("click", "#show_more", async (ev) => {
        const target = u(ev.target).closest('#show_more')
        target.addClass('lagged')
        await __recieveNotes(Number(target.nodes[0].dataset.page))
        target.remove()
    })

    // add note
    u(".ovk-diag-body .attachment_selector").on("click", "#__attach_note", async (ev) => {
        if(u(form).find(`.upload-item`).length > window.openvk.max_attachments) {
            makeError(tr('too_many_attachments'), 'Red', 10000, 1)
            return    
        }

        const target = u(ev.target).closest('._content')
        const button = target.find('#__attach_note')
        const dataset = target.nodes[0].dataset
        const is_attached = (form.find(`.upload-item[data-type='note'][data-id='${dataset.attachmentdata}']`)).length > 0
        if(is_attached) {
            (form.find(`.upload-item[data-type='note'][data-id='${dataset.attachmentdata}']`)).remove()
            button.html(tr('attach'))
        } else {
            if(form.find(`.upload-item`).length + 1 > window.openvk.max_attachments) {
                makeError(tr('too_many_attachments'), 'Red', 10000, 1)
                return
            }

            button.html(tr('detach'))
            form.find('.post-vertical').append(`
                <div class="vertical-attachment upload-item" draggable="true" data-type='note' data-id="${dataset.attachmentdata}">
                    <div class='vertical-attachment-content' draggable="false">
                        <div class="attachment_note">
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 10"><polygon points="0 0 0 10 8 10 8 4 4 4 4 0 0 0"/><polygon points="5 0 5 3 8 3 5 0"/></svg>
                            
                            <div class='attachment_note_content'>
                                <span class="attachment_note_text">${tr('note')}</span>
                                <span class="attachment_note_name">${ovk_proc_strtr(escapeHtml(dataset.name), 66)}</span>
                            </div>
                        </div>
                    </div>
                    <div class='vertical-attachment-remove'>
                        <div id='small_remove_button'></div>
                    </div>
                </div>
            `)
        }
    })

    __recieveNotes(0)
})

function showFastVideoUpload(node) {
    let current_tab = 'file'
    const msg = new CMessageBox({
        title: tr('upload_video'),
        close_on_buttons: false,
        unique_name: 'video_uploader',
        body: `
        <div id='_fast_video_upload'>
            <div id='_tabs'>
                <div class="mb_tabs">
                    <div class="mb_tab" data-name='file'>
                        <a>
                            ${tr('video_file_upload')}
                        </a>
                    </div>
                    <div class="mb_tab" data-name='youtube'>
                        <a>
                            ${tr('video_youtube_upload')}
                        </a>
                    </div>
                </div>
            </div>
            <div id='__content'></div>
        </div>
        `,
        buttons: [tr('close'), tr('upload_button')],
        callbacks: [() => {msg.close()}, async () => {
            const video_name = u(`#_fast_video_upload input[name='name']`).nodes[0].value
            const video_desc = u(`#_fast_video_upload textarea[name='desc']`).nodes[0].value
            let   append_result = null

            if(video_name.length < 1) {
                u(`#_fast_video_upload input[name='name']`).nodes[0].focus()
                return
            }

            const form_data  = new FormData
            switch(current_tab) {
                default:
                case 'file':
                    const video_file = u(`#_fast_video_upload input[name='blob']`).nodes[0]
                    if(video_file.files.length < 1) {
                        return
                    }

                    const video_blob = video_file.files[0]
                    form_data.append('ajax', '1')
                    form_data.append('name', video_name)
                    form_data.append('desc', video_desc)
                    form_data.append('blob', video_blob)
                    form_data.append('unlisted', 1)
                    form_data.append("hash", u("meta[name=csrf]").attr("value"))

                    window.messagebox_stack[1].getNode().find('.ovk-diag-action button').nodes[1].classList.add('lagged')
                    const fetcher = await fetch(`/videos/upload`, {
                        method: 'POST',
                        body: form_data
                    })
                    const fetcher_results = await fetcher.json()
                    append_result = fetcher_results

                    break
                case 'youtube':
                    const video_url = u(`#_fast_video_upload input[name='link']`).nodes[0]
                    const video_link = video_url.value
                    if(video_link.length < 1) {
                        u(`#_fast_video_upload input[name='link']`).nodes[0].focus()
                        return
                    }

                    form_data.append('ajax', '1')
                    form_data.append('name', video_name)
                    form_data.append('desc', video_desc)
                    form_data.append('link', video_link)
                    form_data.append('unlisted', 1)
                    form_data.append("hash", u("meta[name=csrf]").attr("value"))

                    window.messagebox_stack[1].getNode().find('.ovk-diag-action button').nodes[1].classList.add('lagged')
                    const fetcher_yt = await fetch(`/videos/upload`, {
                        method: 'POST',
                        body: form_data
                    })
                    const fetcher_yt_results = await fetcher_yt.json()
                    append_result = fetcher_yt_results

                    break
            }

            if(append_result.payload) {
                append_result = append_result.payload
                const preview = append_result.image[0]
                __appendToTextarea({
                    'type': 'video',
                    'preview': preview.url,
                    'id': append_result.owner_id + '_' + append_result.id,
                    'fullsize_preview': preview.url,
                }, node)

                window.messagebox_stack.forEach(msg_ => {
                    msg_.close()
                })
            } else {
                fastError(append_result.flash.message)
                msg.close()
            }
        }]
    })

    msg.getNode().find('.ovk-diag-body').attr('style', 'padding:0px;height: 161px;')
    async function __switchTab(tab_name) {
        current_tab = tab_name
        u(`#_fast_video_upload .mb_tab`).attr('id', 'ki')
        u(`#_fast_video_upload .mb_tab[data-name='${current_tab}']`).attr('id', 'active')
        
        switch(current_tab) {
            case 'file':
                msg.getNode().find('#__content').html(`
                    <table cellspacing="7" cellpadding="0" width="80%" border="0" align="center">
                        <tbody>
                            <tr>
                                <td width="120" valign="top"><span class="nobold">${tr('info_name')}:</span></td>
                                <td><input type="text" name="name" /></td>
                            </tr>
                            <tr>
                                <td width="120" valign="top"><span class="nobold">${tr('description')}:</span></td>
                                <td><textarea name="desc"></textarea></td>
                            </tr>
                            <tr>
                                <td width="120" valign="top"><span class="nobold">${tr('video')}:</span></td>
                                <td>
                                    <label class="button" style="">
                                        ${tr('browse')}
                                        <input type="file" id="blob" name="blob" style="display: none;" accept="video/*" />
                                    </label>
                                    <span id="filename" style="margin-left: 7px;"></span>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                `)
                break
            case 'youtube':
                msg.getNode().find('#__content').html(`
                    <table cellspacing="7" cellpadding="0" width="80%" border="0" align="center">
                        <tbody>
                            <tr>
                                <td width="120" valign="top"><span class="nobold">${tr('info_name')}:</span></td>
                                <td><input type="text" name="name" /></td>
                            </tr>
                            <tr>
                                <td width="120" valign="top"><span class="nobold">${tr('description')}:</span></td>
                                <td><textarea name="desc"></textarea></td>
                            </tr>
                            <tr>
                                <td width="120" valign="top"><span class="nobold">${tr('video_link_to_yt')}:</span></td>
                                <td>
                                    <input type="text" name="link" placeholder="https://www.youtube.com/watch?v=9FWSRQEqhKE" />
                                </td>
                            </tr>
                        </tbody>
                    </table>
                `)
                break
        }
    }

    u('#_fast_video_upload').on('click', '.mb_tab', (e) => {
        __switchTab(u(e.target).closest('.mb_tab').nodes[0].dataset.name)
    })

    u('#_fast_video_upload').on('change', '#blob', (e) => {
        u('#_fast_video_upload #filename').html(escapeHtml(e.target.files[0].name))
        u(`#_fast_video_upload input[name='name']`).nodes[0].value = escapeHtml(e.target.files[0].name)
    })

    __switchTab('file')
}

u(document).on('click', `.post-horizontal .upload-item .upload-delete`, (e) => {
    e.preventDefault()
    u(e.target).closest('.upload-item').remove()
})

u(document).on('click', `.vertical-attachment #small_remove_button`, (e) => {
    e.preventDefault()
    u(e.target).closest('.vertical-attachment').remove()
})

u(document).on('click', '.post-buttons .upload-item', (e) => {
    e.preventDefault()
    e.stopPropagation()
})

u(document).on('click', '.post.post-nsfw .post-content', (e) => {
    e.preventDefault()
    e.stopPropagation()
    
    if(window.openvk.current_id == 0) {
        return
    }
    
    u(e.target).closest('.post-nsfw').removeClass('post-nsfw')
})

u(document).on('focusin', '#write', (e) => {
    const target = u(e.target).closest('#write')
    target.find('.post-buttons').attr('style', 'display:block')
    target.find('.small-textarea').addClass('expanded-textarea')
})

async function repost(id, repost_type = 'post') {
    const repostsCount = u(`#repostsCount${id}`)
    const previousVal  = repostsCount.length > 0 ? Number(repostsCount.html()) : 0;

    const msg = new CMessageBox({
        title: tr('share'),
        unique_name: 'repost_modal',
        body: `
            <div class='display_flex_column' style='gap: 1px;'>
                <b>${tr('auditory')}</b>
                
                <div class='display_flex_column'>
                    <label>
                        <input type="radio" name="repost_type" value="wall" checked>
                        ${tr("in_wall")}
                    </label>
                    
                    <label>
                        <input type="radio" name="repost_type" value="group">
                        ${tr("in_group")}
                    </label>

                    <select name="selected_repost_club" style='display:none;'></select>
                </div>

                <b>${tr('your_comment')}</b>

                <input type='hidden' id='repost_attachments'>
                <textarea id='repostMsgInput' placeholder='...'></textarea>

                <div id="repost_signs" class='display_flex_column' style='display:none;'>
                    <label><input type='checkbox' name="asGroup">${tr('post_as_group')}</label>
                    <label><input type='checkbox' name="signed">${tr('add_signature')}</label>
                </div>
            </div>
        `,
        buttons: [tr('send'), tr('cancel')],
        callbacks: [
            async () => {
                const message  = u('#repostMsgInput').nodes[0].value
                const type     = u(`input[name='repost_type']:checked`).nodes[0].value
                let club_id = 0
                try {
                    club_id = parseInt(u(`select[name='selected_repost_club']`).nodes[0].selectedOptions[0].value)
                } catch(e) {}
    
                const as_group = u(`input[name='asGroup']`).nodes[0].checked
                const signed   = u(`input[name='signed']`).nodes[0].checked
                const attachments = u(`#repost_attachments`).nodes[0].value
    
                const params = {}
                switch(repost_type) {
                    case 'post':
                        params.object = `wall${id}`
                        break
                    case 'photo':
                        params.object = `photo${id}`
                        break
                    case 'video':
                        params.object = `video${id}`
                        break
                }
    
                params.message = message
                if(type == 'group' && club_id != 0) {
                    params.group_id = club_id
                }
    
                if(as_group) {
                    params.as_group = Number(as_group)
                }
                
                if(signed) {
                    params.signed = Number(signed)
                }
    
                if(attachments != '') {
                    params.attachments = attachments
                }
    
                try {
                    res = await window.OVKAPI.call('wall.repost', params)
    
                    if(u('#reposts' + id).length > 0) {
                        if(repostsCount.length > 0) {
                            repostsCount.html(previousVal + 1)
                        } else {
                            u('#reposts' + id).nodes[0].insertAdjacentHTML('beforeend', `(<b id='repostsCount${id}'>1</b>)`)
                        }
                    }
    
                    NewNotification(tr('information_-1'), tr('shared_succ'), null, () => {window.router.route(`/wall${res.pretty_id}`)});
                } catch(e) {
                    console.error(e)
                    fastError(e.message)
                }
            },
            Function.noop
        ]
    });
    
    u('.ovk-diag-body').attr('style', 'padding: 14px;')
    u('.ovk-diag-body').on('change', `input[name='repost_type']`, (e) => {
        const value = e.target.value

        switch(value) {
            case 'wall':
                u('#repost_signs').attr('style', 'display:none')
                u(`select[name='selected_repost_club']`).attr('style', 'display:none')
                break
            case 'group':
                u('#repost_signs').attr('style', 'display:flex')
                u(`select[name='selected_repost_club']`).attr('style', 'display:block')
                break
        }
    })
    
    if(!window.openvk.writeableClubs) {
        window.openvk.writeableClubs = await window.OVKAPI.call('groups.get', {'filter': 'admin', 'count': 100})
    }

    window.openvk.writeableClubs.items.forEach(club => {
        u(`select[name='selected_repost_club']`).append(`<option value='${club.id}'>${ovk_proc_strtr(escapeHtml(club.name), 100)}</option>`)
    })

    if(window.openvk.writeableClubs.items.length < 1) {
        u(`input[name='repost_type'][value='group']`).attr('disabled', 'disabled')
    }
}

$(document).on("click", "#add_image", (e) => {
    let isGroup = e.currentTarget.closest(".avatar_block").dataset.club != null
    let group = isGroup ? e.currentTarget.closest(".avatar_block").dataset.club : 0

    let body = `
    <div id="avatarUpload">
        <p>${isGroup == true ? tr('groups_avatar') : tr('friends_avatar')}</p>
        <p>${tr('formats_avatar')}</p><br>

        <label class="button" style="margin-left:45%;user-select:none" id="uploadbtn">
            ${tr("browse")}
            <input accept="image/*" type="file" id="_avaInput" name="blob" hidden style="display: none;">
        </label>

        <br><br>

        <p>${tr('troubles_avatar')}</p>
        <p>${tr('webcam_avatar')}</p>
    </div>
    `

    let msg = MessageBox(tr('uploading_new_image'), body, [
        tr('cancel')
    ], [
        (function() {
            u("#tmpPhDelF").remove();
        }),
    ]);

    msg.attr("style", "width: 600px;");
    document.querySelector(".ovk-diag-body").style.padding = "13px"

    $("#avatarUpload input").on("change", (ev) => {
        let image = URL.createObjectURL(ev.currentTarget.files[0])
        $(".ovk-diag-body")[0].innerHTML = `
            <span>${!isGroup ? tr("selected_area_user") : tr("selected_area_club")}</span>

            <p style="margin-bottom: 10px;">${tr("selected_area_rotate")}</p>

            <div class="cropper-image-cont" style="max-height: 274px;">
                <img src="${image}" id="temp_uploadPic">

                <div class="rotateButtons">
                    <div class="_rotateLeft hoverable"></div>
                    <div class="_rotateRight hoverable"></div>
                </div>
            </div>

            <label style="margin-top: 14px;display: block;">
                <input id="publish_on_wall" type="checkbox" checked>${tr("publish_on_wall")}
            </label>
        `

        document.querySelector(".ovk-diag-action").insertAdjacentHTML("beforeend", `
            <button class="button" style="margin-left: 4px;" id="_uploadImg">${tr("upload_button")}</button>
        `)
        
        const image_div = document.getElementById('temp_uploadPic');
        const cropper = new Cropper(image_div, {
            aspectRatio: NaN,
            zoomable: true,
            minCropBoxWidth: 150,
            minCropBoxHeight: 150,
            dragMode: 'move',
            background: false,
            center: false,
            guides: false,
            modal: true,
            viewMode: 2,
            cropstart(event) {
                document.querySelector(".cropper-container").classList.add("moving")
            },
            cropend(event) {
                document.querySelector(".cropper-container").classList.remove("moving")
            },
        });

        msg.attr("style", "width: 487px;");

        document.querySelector("#_uploadImg").onclick = (evv) => {
            cropper.getCroppedCanvas({
                fillColor: '#fff',
                imageSmoothingEnabled: false,
                imageSmoothingQuality: 'high',
            }).toBlob((blob) => {
                document.querySelector("#_uploadImg").classList.add("lagged")
                let formdata = new FormData()
                formdata.append("blob", blob)
                formdata.append("ajax", 1)
                formdata.append("on_wall", Number(document.querySelector("#publish_on_wall").checked))
                formdata.append("hash", u("meta[name=csrf]").attr("value"))
        
                $.ajax({
                    type: "POST",
                    url: isGroup ? "/club" + group + "/al_avatar" : "/al_avatars",
                    data: formdata,
                    processData: false,
                    contentType: false,
                    error: (response) => {
                        fastError(response.flash.message)
                    },
                    success: (response) => {
                        document.querySelector("#_uploadImg").classList.remove("lagged")
                        u("body").removeClass("dimmed");
                        document.querySelector("html").style.overflowY = "scroll"
                        u(".ovk-diag-cont").remove();

                        if(!response.success) {
                            fastError(response.flash.message)
                            return
                        }
                        
                        document.querySelector("#bigAvatar").src = response.url
                        document.querySelector("#bigAvatar").parentNode.href = "/photo" + response.new_photo
                    
                        document.querySelector(".add_image_text").style.display = "none"
                        document.querySelector(".avatar_controls").style.display = "block"
                    }
                })
            })
        }

        $(".ovk-diag-body ._rotateLeft").on("click", (e) => {
            cropper.rotate(90)
        })

        $(".ovk-diag-body ._rotateRight").on("click", (e) => {
            cropper.rotate(-90)
        })
    })

    $(".ovk-diag-body #_takeSelfie").on("click", (e) => {
        $("#avatarUpload")[0].style.display = "none"

        $(".ovk-diag-body")[0].insertAdjacentHTML("beforeend", `
            <div id="_takeSelfieFrame" style="text-align: center;">
                <video style="max-width: 100%;max-height: 479px;"></video>
                <canvas id="_tempCanvas" style="position: absolute;">
            </div>
        `)

        let video = document.querySelector("#_takeSelfieFrame video")

        if(!navigator.mediaDevices) {
            u("body").removeClass("dimmed");
            document.querySelector("html").style.overflowY = "scroll"
            u(".ovk-diag-cont").remove();

            fastError(tr("your_browser_doesnt_support_webcam"))

            return
        }

        navigator.mediaDevices
        .getUserMedia({ video: true, audio: false })
        .then((stream) => {
            video.srcObject = stream;
            video.play()

            window._cameraStream = stream
        })
        .catch((err) => {
            u("body").removeClass("dimmed");
            document.querySelector("html").style.overflowY = "scroll"
            u(".ovk-diag-cont").remove();

            fastError(err)
        });
        
        function __closeConnection() {
            window._cameraStream.getTracks().forEach(track => track.stop())
        }

        document.querySelector(".ovk-diag-action").insertAdjacentHTML("beforeend", `
            <button class="button" style="margin-left: 4px;" id="_takeSnap">${tr("take_snapshot")}</button>
        `)

        document.querySelector(".ovk-diag-action button").onclick = (evv) => {
            __closeConnection()
        }

        document.querySelector("#_takeSnap").onclick = (evv) => {
            let canvas = document.getElementById('_tempCanvas')
            let context = canvas.getContext('2d')

            canvas.setAttribute("width", video.clientWidth)
            canvas.setAttribute("height", video.clientHeight)
            context.drawImage(video, 0, 0, video.clientWidth, video.clientHeight);
            canvas.toBlob((blob) => {
                $("#_takeSnap").remove()
                
                let file = new File([blob], "snapshot.jpg", {type: "image/jpeg", lastModified: new Date().getTime()})
                let dt = new DataTransfer();
                dt.items.add(file);

                $("#_avaInput")[0].files = dt.files
                $("#_avaInput").trigger("change")
                $("#_takeSelfieFrame").remove()

                __closeConnection()
            })
        }
    })
})

$(document).on("click", ".avatarDelete", (e) => {
    let isGroup = e.currentTarget.closest(".avatar_block").dataset.club != null
    let group = isGroup ? e.currentTarget.closest(".avatar_block").dataset.club : 0

    let body = `
        <span>${tr("deleting_avatar_sure")}</span>
    `

    let msg = MessageBox(tr('deleting_avatar'), body, [
        tr('yes'),
        tr('no')
    ], [
        (function() {
            let formdata = new FormData()
            formdata.append("hash", u("meta[name=csrf]").attr("value"))

            $.ajax({
                type: "POST",
                url: isGroup ? "/club" + group + "/delete_avatar" : "/delete_avatar",
                data: formdata,
                processData: false,
                contentType: false,
                beforeSend: () => {
                    document.querySelector(".avatarDelete").classList.add("lagged")
                },
                error: (response) => {
                    fastError(response.flash.message)
                },
                success: (response) => {
                    if(!response.success) {
                        fastError(response.flash.message)
                        return
                    }

                    document.querySelector(".avatarDelete").classList.remove("lagged")
                    
                    u("body").removeClass("dimmed");
                    document.querySelector("html").style.overflowY = "scroll"
                    u(".ovk-diag-cont").remove()

                    document.querySelector("#bigAvatar").src = response.url
                    document.querySelector("#bigAvatar").parentNode.href = response.new_photo ? ("/photo" + response.new_photo) : "javascript:void(0)"
                    
                    if(!response.has_new_photo) {
                        document.querySelector(".avatar_controls").style.display = "none"
                        document.querySelector(".add_image_text").style.display = "block"
                    }
                }
            })
        }),
        (function() {
            u("#tmpPhDelF").remove();
        }),
    ]);
})

async function __processPaginatorNextPage(page)
{
    const container = u('.scroll_container')
    const container_node = '.scroll_node'
    const parser = new DOMParser

    const replace_url = new URL(location.href)
    replace_url.searchParams.set('p', page)
    /*replace_url.searchParams.set('al', 1)
    replace_url.searchParams.set('hash', u("meta[name=csrf]").attr("value"))*/

    const new_content = await fetch(replace_url.href)
    const new_content_response = await new_content.text()
    const parsed_content = parser.parseFromString(new_content_response, 'text/html')

    const nodes = parsed_content.querySelectorAll(container_node)
    nodes.forEach(node => {
        const unique_id = node.dataset.uniqueid
        if(unique_id) {
            const elements_unique = u(`.scroll_node[data-uniqueid='${unique_id}']`).length
            if(elements_unique > 0) {
                console.info('AJAX | Found duplicates')
                return
            }
        }

        container.append(node)
    })

    u(`.paginator:not(.paginator-at-top)`).html(parsed_content.querySelector('.paginator:not(.paginator-at-top)').innerHTML)
    if(u(`.paginator:not(.paginator-at-top)`).nodes[0].closest('.scroll_container')) {
        container.nodes[0].append(u(`.paginator:not(.paginator-at-top)`).nodes[0].parentNode)
    }
    
    if(window.player && window.player.isAtAudiosPage() && window.player.isAtCurrentContextPage()) {
        window.player.loadContext(page)
        window.player.__highlightActiveTrack()
    }

    /*if(window.router) {
        window.router.savePreviousPage()
    }*/

    const new_url = new URL(location.href)
    new_url.hash = page
    history.replaceState(null, null, new_url)

    if(typeof __scrollHook != 'undefined') {
        __scrollHook(page)
    }
}

const showMoreObserver = new IntersectionObserver(entries => {
    entries.forEach(async x => {
        if(x.isIntersecting) {
            if(Number(localStorage.getItem('ux.auto_scroll') ?? 1) == 0) {
                return
            }

            if(u('.scroll_container').length < 1) {
                return
            }

            /*if(window.player && window.player.isAtAudiosPage() && !window.player.isAtCurrentContextPage()) {
                return
            }*/
            
            const target = u(x.target)
            if(target.length < 1 || target.hasClass('paginator-at-top')) {
                return
            }

            const current_url = new URL(location.href)
            if(current_url.searchParams && !isNaN(parseInt(current_url.searchParams.get('p')))) {
                return
            }

            target.addClass('lagged')
            const active_tab = target.find('.active')
            const next_page  = u(active_tab.nodes[0] ? active_tab.nodes[0].nextElementSibling : null)
            if(next_page.length < 1) {
                u('.paginator:not(.paginator-at-top)').removeClass('lagged')
                return
            }

            const page_number = Number(next_page.html())

            try {
                await __processPaginatorNextPage(page_number)
            } catch(e) {
                console.error(e)
            }
            
            bsdnHydrate()
            u('.paginator:not(.paginator-at-top)').removeClass('lagged')
        }
    })
}, {
    root: null,
    rootMargin: '0px',
    threshold: 0,
})

if(u('.paginator:not(.paginator-at-top)').length > 0) {
    showMoreObserver.observe(u('.paginator:not(.paginator-at-top)').nodes[0])
}

u(document).on('click', '#__sourceAttacher', (e) => {
    MessageBox(tr('add_source'), `
        <div id='source_flex_kunteynir'>
            <span>${tr('set_source_tip')}</span>
            <!-- давай, копируй ссылку и переходи по ней -->
            <input type='text' maxlength='400' placeholder='https://www.youtube.com/watch?v=lkWuk_nzzVA'>
        </div>
    `, [tr('cancel')], [
        () => {Function.noop}
    ])

    __removeDialog = () => {
        u("body").removeClass("dimmed");
        document.querySelector("html").style.overflowY = "scroll"
        u(".ovk-diag-cont").remove()
    }

    u('.ovk-diag-action').append(`
        <button class='button' id='__setsrcbutton'>${tr('set_source')}</button>
    `)

    u('.ovk-diag-action #__setsrcbutton').on('click', async (ev) => {
        // Consts
        const _u_target        = u(e.target)
        const nearest_textarea = _u_target.closest('#write')
        const source_output    = nearest_textarea.find(`input[name='source']`)
        const source_input     = u(`#source_flex_kunteynir input[type='text']`)
        const source_value     = source_input.nodes[0].value ?? ''
        if(source_value.length < 1) {
            return
        }

        ev.target.classList.add('lagged')

        // Checking link
        const __checkCopyrightLinkRes = await fetch(`/method/wall.checkCopyrightLink?auth_mechanism=roaming&link=${encodeURIComponent(source_value)}`)
        const checkCopyrightLink = await __checkCopyrightLinkRes.json()
        
        // todo переписать блять мессенджбоксы чтоб они классами были
        if(checkCopyrightLink.error_code) {
            __removeDialog()
            switch(checkCopyrightLink.error_code) {
                default:
                case 3102:
                    fastError(tr('error_adding_source_regex'))
                    return
                case 3103:
                    fastError(tr('error_adding_source_long'))
                    return
                case 3104:
                    fastError(tr('error_adding_source_sus'))
                    return
            }
        }

        // Making indicator
        __removeDialog()
        source_output.attr('value', source_value)
        nearest_textarea.find('.post-source').html(`
            <span>${tr('source')}: <a target='_blank' href='${source_value.escapeHtml()}'>${ovk_proc_strtr(source_value.escapeHtml(), 50)}</a></span>
            <div id='remove_source_button'></div>
        `)

        nearest_textarea.find('.post-source #remove_source_button').on('click', () => {
            nearest_textarea.find('.post-source').html('')
            source_output.attr('value', 'none')
        })
    })

    u('.ovk-diag-body').attr('style', `padding:8px;`)
    u('.ovk-diag-cont').attr('style', 'width: 325px;')
    u('#source_flex_kunteynir input').nodes[0].focus()
})

u(document).on('keyup', async (e) => {
    if(u('#ovk-player-part .bsdn').length > 0) {
        switch(e.keyCode) {
            case 32:
                u('#ovk-player-part .bsdn .bsdn_playButton').trigger('click')
                break
            case 39:
                u('#ovk-player-part video').nodes[0].currentTime = u('#ovk-player-part video').nodes[0].currentTime + 2
                break
            case 37:
                u('#ovk-player-part video').nodes[0].currentTime = u('#ovk-player-part video').nodes[0].currentTime - 2
                break
        }
    }
})

u(document).on('mouseover mousemove mouseout', `div[data-tip='simple']`, (e) => {
    if(e.target.dataset.allow_mousemove != '1' && e.type == 'mousemove') {
        return
    }

    if(e.type == 'mouseout') {
        u(`.tip_result`).remove()
        return
    }

    const target = u(e.target).closest(`div[data-tip='simple']`)
    const title  = target.attr('data-title')
    if(title == '') {
        return
    }

    target.nodes[0].parentNode.insertAdjacentHTML('afterbegin', `
        <div class='tip_result' style='left:${e.layerX}px;'>
            ${escapeHtml(title)}
        </div>    
    `)
})

function setStatusEditorShown(shown) {
    document.getElementById("status_editor").style.display = shown ? "block" : "none";
}

u(document).on('click', (event) => {
    u('#ctx_menu').remove()
    if(u('#status_editor').length < 1) {
        return
    }

    if(!event.target.closest("#status_editor") && !event.target.closest("#page_status_text"))
        setStatusEditorShown(false);
})

u(document).on('click', '#page_status_text', (e) => {
    setStatusEditorShown(true)
})

async function changeStatus() {
    const status = document.status_popup_form.status.value;
    const broadcast = document.status_popup_form.broadcast.checked;

    document.status_popup_form.submit.innerHTML = "<div class=\"button-loading\"></div>";
    document.status_popup_form.submit.disabled = true;

    const formData = new FormData();
    formData.append("status", status);
    formData.append("broadcast", Number(broadcast));
    formData.append("hash", document.status_popup_form.hash.value);
    const response = await ky.post("/edit?act=status", {body: formData});

    if(!parseAjaxResponse(await response.text())) {
        document.status_popup_form.submit.innerHTML = tr("send");
        document.status_popup_form.submit.disabled = false;
        return;
    }

    if(document.status_popup_form.status.value === "") {
        document.querySelector("#page_status_text").innerHTML = `[ ${tr("change_status")} ]`;
        document.querySelector("#page_status_text").className = "edit_link page_status_edit_button";
    } else {
        document.querySelector("#page_status_text").innerHTML = escapeHtml(status);
        document.querySelector("#page_status_text").className = "page_status page_status_edit_button";
    }

    setStatusEditorShown(false);
    document.status_popup_form.submit.innerHTML = tr("send");
    document.status_popup_form.submit.disabled = false;
}

const tplMapIcon = `<svg class="map_svg_icon" width="13" height="12" viewBox="0 0 3.4395833 3.175">
<g><path d="M 1.7197917 0.0025838216 C 1.1850116 0.0049444593 0.72280427 0.4971031 0.71520182 1.0190592 C 0.70756921 1.5430869 1.7223755 3.1739665 1.7223755 3.1739665 C 1.7223755 3.1739665 2.7249195 1.5439189 2.7243815 0.99632161 C 2.7238745 0.48024825 2.2492929 0.00024648357 1.7197917 0.0025838216 z M 1.7197917 0.52606608 A 0.48526123 0.48526123 0 0 1 2.2050334 1.0113078 A 0.48526123 0.48526123 0 0 1 1.7197917 1.4965495 A 0.48526123 0.48526123 0 0 1 1.23455 1.0113078 A 0.48526123 0.48526123 0 0 1 1.7197917 0.52606608 z " /></g>
</svg>`

u(document).on('click', "#__geoAttacher", async (e) => {
    const form = u(e.target).closest('#write')
    const buttons = form.find('.post-buttons')

    let current_coords = [54.51331, 36.2732]
    let currentMarker  = null
    const getCoords = async () => {
        const pos = await new Promise((resolve, reject) => {
            navigator.geolocation.getCurrentPosition((position) => {
                resolve([position.coords.latitude, position.coords.longitude])
            }, () => {
                resolve([54.51331, 36.2732])
            },
            {
                enableHighAccuracy: true,
                timeout: 5000,
                maximumAge: 0,
            })
        })

        return pos
    }

    current_coords = await getCoords()

    const geo_msg = new CMessageBox({
        title: tr('attach_geotag'),
        body: `<div id=\"osm-map\" style='height:75vh;'></div>`,
        buttons: [tr('attach'), tr('cancel')],
        callbacks: [() => {
            if(!currentMarker) {
                return
            }
            
            const geo_name = $(`#geo-name`).html()
            if(geo_name == '') {
                return
            }

            const marker = {
                lat: currentMarker._latlng.lat,
                lng: currentMarker._latlng.lng,
                name: geo_name
            }
            buttons.find(`input[name='geo']`).nodes[0].value = JSON.stringify(marker)
            buttons.find(`.post-has-geo`).html(`
                ${tplMapIcon}
                <span>${escapeHtml(geo_name)}</span>
                <div id="small_remove_button"></div>
            `)
        }, () => {}]
    })

    // by n1rwana
    const markerLayers = L.layerGroup()
    const map = L.map(u('#osm-map').nodes[0], {
        center: current_coords,
        zoom: 10,
        attributionControl: false,
        width: 800
    })
    markerLayers.addTo(map)

    map.on('click', async (e) => {
        const lat = e.latlng.lat
        const lng = e.latlng.lng

        if(currentMarker) map.removeLayer(currentMarker);

        const marker_fetch_req = await fetch(`https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lng}&format=jsonv2`)
        const marker_fetch = await marker_fetch_req.json()

        markerLayers.clearLayers()
        currentMarker = L.marker([lat, lng]).addTo(map)

        let marker_name = marker_fetch && marker_fetch.display_name ? short_geo_name(marker_fetch.address) : tr('geotag')
        const content = `<span id="geo-name">${marker_name}</span>`;

        currentMarker.bindPopup(content).openPopup()
        markerLayers.addLayer(currentMarker)
    })

    const geocoderControl = L.Control.geocoder({
        defaultMarkGeocode: false,
    }).addTo(map)

    geocoderControl.on('markgeocode', function (e) {
        console.log(e.geocode.properties)
        const lat = e.geocode.properties.lat
        const lng = e.geocode.properties.lon
        const name = e.geocode.properties?.display_name ? short_geo_name(e.geocode.properties?.address) : tr('geotag')

        if(currentMarker) map.removeLayer(currentMarker)

        currentMarker = L.marker([lat, lng]).addTo(map)
        currentMarker.bindPopup(`<span id="geo-name">${escapeHtml(name)}</span>`).openPopup()

        marker = {
            lat: lat,
            lng: lng,
            name: name
        };
        map.setView([lat, lng], 15);
    })

    L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
    }).addTo(map)

    geo_msg.getNode().nodes[0].style = 'width:90%'
    setTimeout(function(){ map.invalidateSize()}, 100)
})

u(document).on('click', '.post-has-geo #small_remove_button', (e) => {
    const form = u(e.target).closest('#write')
    const geo  = form.find('.post-has-geo')
    geo.remove()
    form.find(`input[name='geo']`).nodes[0].value = ''
})

u(document).on('click', '#geo-name', (e) => {
    const current_value = escapeHtml(e.target.innerHTML)
    const msg = new CMessageBox({
        title: tr('change_geo_name'),
        unique_name: 'geo_change_name_menu',
        body: `
            <div>
                <input type="text" maxlength="255" name="final_value" placeholder="${tr('change_geo_name_new')}" value="${current_value}">
            </div>
        `,
        buttons: [tr('save'), tr('cancel')],
        callbacks: [() => {
            const new_value = u(`input[name='final_value']`).nodes[0].value
            u('#geo-name').html(escapeHtml(new_value))
        }, Function.noop]
    })
    u(`input[name='final_value']`).nodes[0].focus()
})

function openGeo(data, owner_id, virtual_id) {
    MessageBox(tr("geotag"), "<div id=\"osm-map\"></div>", [tr("nearest_posts"), tr("close")], [async () => {
        const posts = await OVKAPI.call('wall.getNearby', {owner_id: owner_id, post_id: virtual_id})
        openNearPosts(posts)
    }, Function.noop]);

    let element = document.getElementById('osm-map');
    element.style = 'height: 80vh;';

    let map = L.map(element, {attributionControl: false});
    let target = L.latLng(data.lat, data.lng);
    map.setView(target, 15);

    let marker = L.marker(target).addTo(map);
    marker.bindPopup(escapeHtml(data.name ?? tr("geotag"))).openPopup();

    L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
    }).addTo(map);

    $(".ovk-diag-cont").width('80%');
    setTimeout(function(){ map.invalidateSize()}, 100);
}

function tplPost(post) {
    return `<a style="color: inherit; display: block; margin-bottom: 8px;" href="${post.url}">
            <table border="0" style="font-size: 11px;" class="post">
                <tbody>
                    <tr>
                        <td width="54" valign="top">
                            <a href="${post.owner.domain}">
                                <img src="${post.owner.photo_50}" width="50">
                            </a>
                        </td>
                        <td width="100%" valign="top">
                            <div class="post-author">
                                <a href="${post.owner.domain}"><b>${escapeHtml(post.owner.name)}</b></a>
                                ${post.owner.verified ? `<img class="name-checkmark" src="/assets/packages/static/openvk/img/checkmark.png">` : ""}
                                <br>
                                <a href="${post.url}" class="date">
                                    ${post.created}
                                </a>
                            </div>
                            <div class="post-content">
                                <div class="text">
                                    ${escapeHtml(post.message)}
                                </div>
                                <div style="padding: 4px;">
                                    <div style="border-bottom: #ECECEC solid 1px;"></div>
                                    <div style="cursor: pointer; padding: 4px;">
                                        ${tplMapIcon}
                                        ${escapeHtml(post.geo.name)}
                                    </div>
                                </div>
                            </div>
                        </td>
                    </tr>
                </tbody>
            </table>
       </a>`;
}

function openNearPosts(posts) {
    if (posts.length > 0) {
        let MsgTxt = "<div id=\"osm-map\"></div>";
        MsgTxt += `<br /><br /><center style='color: grey;'>${tr('shown_last_nearest_posts', 25)}</center>`;

        MessageBox(tr('nearest_posts'), MsgTxt, ["OK"], [Function.noop]);

        let element = document.getElementById('osm-map');
        element.style = 'height: 80vh;';

        let markerLayers = L.layerGroup();
        let map = L.map(element, {attributionControl: false});

        markerLayers.addTo(map);

        let markersBounds = [];
        let coords = [];

        posts.forEach((post) => {
            if (coords.includes(`${post.geo.lat} ${post.geo.lng}`)) {
                markerLayers.getLayers().forEach((marker) => {
                    if (marker.getLatLng().lat === post.geo.lat && marker.getLatLng().lng === post.geo.lng) {
                        let content = marker.getPopup()._content += tplPost(post);
                        if (!content.startsWith(`<div style="max-height: 300px; overflow-y: auto;">`))
                            content = `<div style="max-height: 300px; overflow-y: auto;">${content}`;

                        marker.getPopup().setContent(content);
                    }
                });
            } else {
                let marker = L.marker(L.latLng(post.geo.lat, post.geo.lng)).addTo(map);
                marker.bindPopup(tplPost(post));
                markerLayers.addLayer(marker);
                markersBounds.push(marker.getLatLng());
            }

            coords.push(`${post.geo.lat} ${post.geo.lng}`);
        })

        let bounds = L.latLngBounds(markersBounds);
        map.fitBounds(bounds);

        L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
            attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        }).addTo(map);

        $(".ovk-diag-cont").width('80%');
        setTimeout(function () {
            map.invalidateSize()
        }, 100);
    } else {
        MessageBox(tr('nearest_posts'), `<center style='color: grey;'>${tr('no_nearest_posts')}</center>`, ["OK"], [Function.noop]);
    }
}

u(document).on('click', '#_bl_toggler', async (e) => {
    e.preventDefault()

    const target = u(e.target)
    const val = Number(target.attr('data-val'))
    const id  = Number(target.attr('data-id'))
    const name = target.attr('data-name')

    const fallback = (e) => {
        fastError(e.message)
        target.removeClass('lagged')
    }

    if(val == 1) {
        const msg = new CMessageBox({
            title: tr('addition_to_bl'),
            body: `<span>${escapeHtml(tr('adding_to_bl_sure', name))}</span>`,
            buttons: [tr('yes'), tr('no')],
            callbacks: [async () => {
                try {
                    target.addClass('lagged')
                    await window.OVKAPI.call('account.ban', {'owner_id': id})
                    window.router.route(location.href)
                } catch(e) {
                    fallback(e)
                }
            }, () => Function.noop]
        })
    } else {
        try {
            target.addClass('lagged')
            await window.OVKAPI.call('account.unban', {'owner_id': id})
            window.router.route(location.href)
        } catch(e) {
            fallback(e)
        }
    }
})
